* [RFC PATCH 10/15] spi: use new `spi_transfer_delay` helper where straightforward
From: Alexandru Ardelean @ 2019-09-13 11:45 UTC (permalink / raw)
To: linux-spi, linux-iio, linux-arm-kernel, linux-tegra, linux-kernel,
bcm-kernel-feedback-list
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
In-Reply-To: <20190913114550.956-1-alexandru.ardelean@analog.com>
For many places in the spi drivers, using the new `spi_transfer_delay`
helper is straightforward.
It's just replacing:
```
if (t->delay_usecs)
udelay(t->delay_usecs);
```
with `spi_transfer_delay(t)` which handles both `delay_usecs` and the new
`delay` field.
This change replaces in all places (in the spi drivers) where this change
is simple.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/spi/spi-atmel.c | 3 +--
drivers/spi/spi-bcm63xx-hsspi.c | 3 +--
drivers/spi/spi-cavium.c | 3 +--
drivers/spi/spi-fsl-dspi.c | 3 +--
drivers/spi/spi-fsl-espi.c | 3 +--
drivers/spi/spi-fsl-spi.c | 3 +--
drivers/spi/spi-mpc512x-psc.c | 3 +--
drivers/spi/spi-mpc52xx-psc.c | 3 +--
drivers/spi/spi-omap-100k.c | 3 +--
drivers/spi/spi-pl022.c | 25 +++++++++++--------------
drivers/spi/spi-sc18is602.c | 3 +--
drivers/spi/spi-sh-hspi.c | 3 +--
drivers/spi/spi-tegra20-sflash.c | 2 +-
drivers/spi/spi-topcliff-pch.c | 7 +------
drivers/spi/spi-txx9.c | 3 +--
drivers/spi/spi-xcomm.c | 3 +--
16 files changed, 26 insertions(+), 47 deletions(-)
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 009bb4fb5e5f..f51af930c7a4 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -1389,8 +1389,7 @@ static int atmel_spi_one_transfer(struct spi_master *master,
&& as->use_pdc)
atmel_spi_dma_unmap_xfer(master, xfer);
- if (xfer->delay_usecs)
- udelay(xfer->delay_usecs);
+ spi_transfer_delay(xfer);
if (xfer->cs_change) {
if (list_is_last(&xfer->transfer_list,
diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c
index 9a06ffdb73b8..d9f5c241e2aa 100644
--- a/drivers/spi/spi-bcm63xx-hsspi.c
+++ b/drivers/spi/spi-bcm63xx-hsspi.c
@@ -291,8 +291,7 @@ static int bcm63xx_hsspi_transfer_one(struct spi_master *master,
msg->actual_length += t->len;
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ spi_transfer_delay(t);
if (t->cs_change)
bcm63xx_hsspi_set_cs(bs, spi->chip_select, false);
diff --git a/drivers/spi/spi-cavium.c b/drivers/spi/spi-cavium.c
index 5aaf21582cb5..db7f5f94c525 100644
--- a/drivers/spi/spi-cavium.c
+++ b/drivers/spi/spi-cavium.c
@@ -119,8 +119,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p,
*rx_buf++ = (u8)v;
}
- if (xfer->delay_usecs)
- udelay(xfer->delay_usecs);
+ spi_transfer_delay(xfer);
return xfer->len;
}
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
index 53335ccc98f6..0b761352c16a 100644
--- a/drivers/spi/spi-fsl-dspi.c
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -752,8 +752,7 @@ static int dspi_transfer_one_message(struct spi_master *master,
dspi->waitflags = 0;
}
- if (transfer->delay_usecs)
- udelay(transfer->delay_usecs);
+ spi_transfer_delay(transfer);
}
out:
diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index f20326714b9d..a3af12127c40 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -427,8 +427,7 @@ static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans)
ret = fsl_espi_bufs(spi, trans);
- if (trans->delay_usecs)
- udelay(trans->delay_usecs);
+ spi_transfer_delay(trans);
return ret;
}
diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c
index 1d9b33aa1a3b..9352b127d5b8 100644
--- a/drivers/spi/spi-fsl-spi.c
+++ b/drivers/spi/spi-fsl-spi.c
@@ -417,8 +417,7 @@ static int fsl_spi_do_one_msg(struct spi_master *master,
}
m->actual_length += t->len;
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ spi_transfer_delay(t);
if (cs_change) {
ndelay(nsecs);
diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c
index a337b842ae8c..329bbdd1f15f 100644
--- a/drivers/spi/spi-mpc512x-psc.c
+++ b/drivers/spi/spi-mpc512x-psc.c
@@ -311,8 +311,7 @@ static int mpc512x_psc_spi_msg_xfer(struct spi_master *master,
break;
m->actual_length += t->len;
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ spi_transfer_delay(t);
if (cs_change)
mpc512x_psc_spi_deactivate_cs(spi);
diff --git a/drivers/spi/spi-mpc52xx-psc.c b/drivers/spi/spi-mpc52xx-psc.c
index c7e478b9b586..dd79d7fd3687 100644
--- a/drivers/spi/spi-mpc52xx-psc.c
+++ b/drivers/spi/spi-mpc52xx-psc.c
@@ -234,8 +234,7 @@ static void mpc52xx_psc_spi_work(struct work_struct *work)
break;
m->actual_length += t->len;
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ spi_transfer_delay(t);
if (cs_change)
mpc52xx_psc_spi_deactivate_cs(spi);
diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c
index b955ca8796d2..eb5b51bd5126 100644
--- a/drivers/spi/spi-omap-100k.c
+++ b/drivers/spi/spi-omap-100k.c
@@ -321,8 +321,7 @@ static int omap1_spi100k_transfer_one_message(struct spi_master *master,
}
}
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ spi_transfer_delay(t);
/* ignore the "leave it on after last xfer" hint */
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index 7fedea67159c..42cf800073e4 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -485,12 +485,11 @@ static void giveback(struct pl022 *pl022)
struct spi_transfer, transfer_list);
/* Delay if requested before any change in chip select */
- if (last_transfer->delay_usecs)
- /*
- * FIXME: This runs in interrupt context.
- * Is this really smart?
- */
- udelay(last_transfer->delay_usecs);
+ /*
+ * FIXME: This runs in interrupt context.
+ * Is this really smart?
+ */
+ spi_transfer_delay(last_transfer);
if (!last_transfer->cs_change) {
struct spi_message *next_msg;
@@ -1401,12 +1400,11 @@ static void pump_transfers(unsigned long data)
previous = list_entry(transfer->transfer_list.prev,
struct spi_transfer,
transfer_list);
- if (previous->delay_usecs)
- /*
- * FIXME: This runs in interrupt context.
- * Is this really smart?
- */
- udelay(previous->delay_usecs);
+ /*
+ * FIXME: This runs in interrupt context.
+ * Is this really smart?
+ */
+ spi_transfer_delay(previous);
/* Reselect chip select only if cs_change was requested */
if (previous->cs_change)
@@ -1520,8 +1518,7 @@ static void do_polling_transfer(struct pl022 *pl022)
previous =
list_entry(transfer->transfer_list.prev,
struct spi_transfer, transfer_list);
- if (previous->delay_usecs)
- udelay(previous->delay_usecs);
+ spi_transfer_delay(previous);
if (previous->cs_change)
pl022_cs_control(pl022, SSP_CHIP_SELECT);
} else {
diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c
index 11acddc83304..13cb992df712 100644
--- a/drivers/spi/spi-sc18is602.c
+++ b/drivers/spi/spi-sc18is602.c
@@ -211,8 +211,7 @@ static int sc18is602_transfer_one(struct spi_master *master,
}
status = 0;
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ spi_transfer_delay(t);
}
m->status = status;
spi_finalize_current_message(master);
diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c
index 7f73f91d412a..4edad22c222e 100644
--- a/drivers/spi/spi-sh-hspi.c
+++ b/drivers/spi/spi-sh-hspi.c
@@ -190,8 +190,7 @@ static int hspi_transfer_one_message(struct spi_controller *ctlr,
msg->actual_length += t->len;
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ spi_transfer_delay(t);
if (cs_change) {
ndelay(nsecs);
diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c
index cd714a4f52c6..00593096cfed 100644
--- a/drivers/spi/spi-tegra20-sflash.c
+++ b/drivers/spi/spi-tegra20-sflash.c
@@ -344,7 +344,7 @@ static int tegra_sflash_transfer_one_message(struct spi_master *master,
if (xfer->cs_change && xfer->delay_usecs) {
tegra_sflash_writel(tsd, tsd->def_command_reg,
SPI_COMMAND);
- udelay(xfer->delay_usecs);
+ spi_transfer_delay(xfer);
}
}
ret = 0;
diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c
index f88cbb94ce12..645b086eece8 100644
--- a/drivers/spi/spi-topcliff-pch.c
+++ b/drivers/spi/spi-topcliff-pch.c
@@ -1229,12 +1229,7 @@ static void pch_spi_process_messages(struct work_struct *pwork)
"%s:data->current_msg->actual_length=%d\n",
__func__, data->current_msg->actual_length);
- /* check for delay */
- if (data->cur_trans->delay_usecs) {
- dev_dbg(&data->master->dev, "%s:delay in usec=%d\n",
- __func__, data->cur_trans->delay_usecs);
- udelay(data->cur_trans->delay_usecs);
- }
+ spi_transfer_delay(data->cur_trans);
spin_lock(&data->lock);
diff --git a/drivers/spi/spi-txx9.c b/drivers/spi/spi-txx9.c
index 51759d3fd45f..65aa736783ae 100644
--- a/drivers/spi/spi-txx9.c
+++ b/drivers/spi/spi-txx9.c
@@ -248,8 +248,7 @@ static void txx9spi_work_one(struct txx9spi *c, struct spi_message *m)
len -= count * wsize;
}
m->actual_length += t->len;
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ spi_transfer_delay(t);
if (!cs_change)
continue;
diff --git a/drivers/spi/spi-xcomm.c b/drivers/spi/spi-xcomm.c
index a3496c46cc1b..3aaae32b0384 100644
--- a/drivers/spi/spi-xcomm.c
+++ b/drivers/spi/spi-xcomm.c
@@ -188,8 +188,7 @@ static int spi_xcomm_transfer_one(struct spi_master *master,
}
status = 0;
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ spi_transfer_delay(t);
is_first = false;
}
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [RFC PATCH 11/15] spi: tegra114: use `spi_transfer_delay` helper
From: Alexandru Ardelean @ 2019-09-13 11:45 UTC (permalink / raw)
To: linux-spi, linux-iio, linux-arm-kernel, linux-tegra, linux-kernel,
bcm-kernel-feedback-list
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
In-Reply-To: <20190913114550.956-1-alexandru.ardelean@analog.com>
The tegra114 driver has a weird/separate `tegra_spi_transfer_delay()`
function that does 2 delays: one mdelay() and one udelay().
This was introduced via commit f4fade12d506e14867a2b0a5e2f7aaf227297d8b
("spi/tegra114: Correct support for cs_change").
There doesn't seem to be a mention in that commit message to suggest a
specific need/use-case for having the 2 delay calls.
For the most part, udelay() should be sufficient.
This change replaces it with the new `spi_transfer_delay()`, which should
do the same thing.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/spi/spi-tegra114.c | 17 +++--------------
1 file changed, 3 insertions(+), 14 deletions(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 39374c2edcf3..5ac2ceb234fc 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -984,17 +984,6 @@ static int tegra_spi_setup(struct spi_device *spi)
return 0;
}
-static void tegra_spi_transfer_delay(int delay)
-{
- if (!delay)
- return;
-
- if (delay >= 1000)
- mdelay(delay / 1000);
-
- udelay(delay % 1000);
-}
-
static void tegra_spi_transfer_end(struct spi_device *spi)
{
struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master);
@@ -1098,7 +1087,7 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
complete_xfer:
if (ret < 0 || skip) {
tegra_spi_transfer_end(spi);
- tegra_spi_transfer_delay(xfer->delay_usecs);
+ spi_transfer_delay(xfer);
goto exit;
} else if (list_is_last(&xfer->transfer_list,
&msg->transfers)) {
@@ -1106,11 +1095,11 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
tspi->cs_control = spi;
else {
tegra_spi_transfer_end(spi);
- tegra_spi_transfer_delay(xfer->delay_usecs);
+ spi_transfer_delay(xfer);
}
} else if (xfer->cs_change) {
tegra_spi_transfer_end(spi);
- tegra_spi_transfer_delay(xfer->delay_usecs);
+ spi_transfer_delay(xfer);
}
}
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [RFC PATCH 12/15] spi: spi-loopback-test: use new `delay` field
From: Alexandru Ardelean @ 2019-09-13 11:45 UTC (permalink / raw)
To: linux-spi, linux-iio, linux-arm-kernel, linux-tegra, linux-kernel,
bcm-kernel-feedback-list
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
In-Reply-To: <20190913114550.956-1-alexandru.ardelean@analog.com>
This change replaces the use of the `delay_usecs` field with the new
`delay` field. The code/test still uses micro-seconds, but they are now
configured and used via the `struct spi_delay` format of the `delay` field.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/spi/spi-loopback-test.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c
index 6f18d4952767..b6d79cd156fb 100644
--- a/drivers/spi/spi-loopback-test.c
+++ b/drivers/spi/spi-loopback-test.c
@@ -298,12 +298,18 @@ static struct spi_test spi_tests[] = {
{
.tx_buf = TX(0),
.rx_buf = RX(0),
- .delay_usecs = 1000,
+ .delay = {
+ .value = 1000,
+ .unit = SPI_DELAY_UNIT_USECS,
+ },
},
{
.tx_buf = TX(0),
.rx_buf = RX(0),
- .delay_usecs = 1000,
+ .delay = {
+ .value = 1000,
+ .unit = SPI_DELAY_UNIT_USECS,
+ },
},
},
},
@@ -537,7 +543,7 @@ static int spi_test_check_elapsed_time(struct spi_device *spi,
unsigned long long nbits = (unsigned long long)BITS_PER_BYTE *
xfer->len;
- delay_usecs += xfer->delay_usecs;
+ delay_usecs += xfer->delay.value;
if (!xfer->speed_hz)
continue;
estimated_time += div_u64(nbits * NSEC_PER_SEC, xfer->speed_hz);
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [RFC PATCH 13/15] spi: spidev: use new `delay` field for spi transfers
From: Alexandru Ardelean @ 2019-09-13 11:45 UTC (permalink / raw)
To: linux-spi, linux-iio, linux-arm-kernel, linux-tegra, linux-kernel,
bcm-kernel-feedback-list
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
In-Reply-To: <20190913114550.956-1-alexandru.ardelean@analog.com>
The `delay` field has type `struct spi_delay`.
This allows users to specify nano-second or clock-cycle delays (if needed).
Converting to use `delay` is straightforward: it's just assigning the
value to `delay.value` and hard-coding the `delay.unit` to
`SPI_DELAY_UNIT_USECS`.
This keeps the uapi for spidev un-changed. Changing it can be part of
another changeset and discussion.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/spi/spidev.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 495319c2fa11..6b6afd27f4a1 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -265,7 +265,8 @@ static int spidev_message(struct spidev_data *spidev,
k_tmp->tx_nbits = u_tmp->tx_nbits;
k_tmp->rx_nbits = u_tmp->rx_nbits;
k_tmp->bits_per_word = u_tmp->bits_per_word;
- k_tmp->delay_usecs = u_tmp->delay_usecs;
+ k_tmp->delay.value = u_tmp->delay_usecs;
+ k_tmp->delay.unit = SPI_DELAY_UNIT_USECS;
k_tmp->speed_hz = u_tmp->speed_hz;
k_tmp->word_delay.value = u_tmp->word_delay_usecs;
k_tmp->word_delay.unit = SPI_DELAY_UNIT_USECS;
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [RFC PATCH 14/15] spi: tegra114: change format for `spi_set_cs_timing()` function
From: Alexandru Ardelean @ 2019-09-13 11:45 UTC (permalink / raw)
To: linux-spi, linux-iio, linux-arm-kernel, linux-tegra, linux-kernel,
bcm-kernel-feedback-list
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
In-Reply-To: <20190913114550.956-1-alexandru.ardelean@analog.com>
The initial version of `spi_set_cs_timing()` was implemented with
consideration only for clock-cycles as delay.
For cases like `CS setup` time, it's sometimes needed that micro-seconds
(or nano-seconds) are required, or sometimes even longer delays, for cases
where the device needs a little longer to start transferring that after CS
is asserted.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/spi/spi-tegra114.c | 22 ++++++++++++++++++++--
drivers/spi/spi.c | 16 ++++++++++------
include/linux/spi/spi.h | 9 ++++++---
3 files changed, 36 insertions(+), 11 deletions(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 5ac2ceb234fc..d8db4af08fff 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -723,15 +723,31 @@ static void tegra_spi_deinit_dma_param(struct tegra_spi_data *tspi,
dma_release_channel(dma_chan);
}
-static void tegra_spi_set_hw_cs_timing(struct spi_device *spi, u8 setup_dly,
- u8 hold_dly, u8 inactive_dly)
+static int tegra_spi_set_hw_cs_timing(struct spi_device *spi,
+ struct spi_delay *setup,
+ struct spi_delay *hold,
+ struct spi_delay *inactive)
{
struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master);
+ u8 setup_dly, hold_dly, inactive_dly;
u32 setup_hold;
u32 spi_cs_timing;
u32 inactive_cycles;
u8 cs_state;
+ if ((setup && setup->unit != SPI_DELAY_UNIT_SCK) ||
+ (hold && hold->unit != SPI_DELAY_UNIT_SCK) ||
+ (inactive && inactive != SPI_DELAY_UNIT_SCK)) {
+ dev_err(&spi->dev,
+ "Invalid delay unit %d, should be SPI_DELAY_UNIT_SCK\n",
+ SPI_DELAY_UNIT_SCK);
+ return -EINVAL;
+ }
+
+ setup_dly = setup ? setup->value : 0;
+ hold_dly = hold ? hold->value : 0;
+ inactive_dly = inactive ? inactive->value : 0;
+
setup_dly = min_t(u8, setup_dly, MAX_SETUP_HOLD_CYCLES);
hold_dly = min_t(u8, hold_dly, MAX_SETUP_HOLD_CYCLES);
if (setup_dly && hold_dly) {
@@ -758,6 +774,8 @@ static void tegra_spi_set_hw_cs_timing(struct spi_device *spi, u8 setup_dly,
tspi->spi_cs_timing2 = spi_cs_timing;
tegra_spi_writel(tspi, spi_cs_timing, SPI_CS_TIMING2);
}
+
+ return 0;
}
static u32 tegra_spi_setup_transfer_one(struct spi_device *spi,
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index c55d55acbbb0..54d00c0a26d2 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -3147,15 +3147,19 @@ EXPORT_SYMBOL_GPL(spi_setup);
/**
* spi_set_cs_timing - configure CS setup, hold, and inactive delays
* @spi: the device that requires specific CS timing configuration
- * @setup: CS setup time in terms of clock count
- * @hold: CS hold time in terms of clock count
- * @inactive_dly: CS inactive delay between transfers in terms of clock count
+ * @setup: CS setup time specified via @spi_delay
+ * @hold: CS hold time specified via @spi_delay
+ * @inactive: CS inactive delay between transfers specified via @spi_delay
+ *
+ * Return: zero on success, else a negative error code.
*/
-void spi_set_cs_timing(struct spi_device *spi, u8 setup, u8 hold,
- u8 inactive_dly)
+int spi_set_cs_timing(struct spi_device *spi, struct spi_delay *setup,
+ struct spi_delay *hold, struct spi_delay *inactive)
{
if (spi->controller->set_cs_timing)
- spi->controller->set_cs_timing(spi, setup, hold, inactive_dly);
+ return spi->controller->set_cs_timing(spi, setup, hold,
+ inactive);
+ return -ENOTSUPP;
}
EXPORT_SYMBOL_GPL(spi_set_cs_timing);
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index c7f90a0129be..292e752ce34a 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -517,8 +517,8 @@ struct spi_controller {
* to configure specific CS timing through spi_set_cs_timing() after
* spi_setup().
*/
- void (*set_cs_timing)(struct spi_device *spi, u8 setup_clk_cycles,
- u8 hold_clk_cycles, u8 inactive_clk_cycles);
+ int (*set_cs_timing)(struct spi_device *spi, struct spi_delay *setup,
+ struct spi_delay *hold, struct spi_delay *inactive);
/* bidirectional bulk transfers
*
@@ -1007,7 +1007,10 @@ static inline void spi_message_free(struct spi_message *m)
kfree(m);
}
-extern void spi_set_cs_timing(struct spi_device *spi, u8 setup, u8 hold, u8 inactive_dly);
+extern int spi_set_cs_timing(struct spi_device *spi,
+ struct spi_delay *setup,
+ struct spi_delay *hold,
+ struct spi_delay *inactive);
extern int spi_setup(struct spi_device *spi);
extern int spi_async(struct spi_device *spi, struct spi_message *message);
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [RFC PATCH 15/15] spi: implement SW control for CS times
From: Alexandru Ardelean @ 2019-09-13 11:45 UTC (permalink / raw)
To: linux-spi, linux-iio, linux-arm-kernel, linux-tegra, linux-kernel,
bcm-kernel-feedback-list
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
In-Reply-To: <20190913114550.956-1-alexandru.ardelean@analog.com>
This change implements CS control for setup, hold & inactive delays.
The `cs_setup` delay is completely new, and can help with cases where
asserting the CS, also brings the device out of power-sleep, where there
needs to be a longer (than usual), before transferring data.
The `cs_hold` time can overlap with the `delay` (or `delay_usecs`) from an
SPI transfer. The main difference is that `cs_hold` implies that CS will be
de-asserted.
The `cs_inactive` delay does not have a clear use-case yet. It has been
implemented mostly because the `spi_set_cs_timing()` function implements
it. To some degree, this could overlap or replace `cs_change_delay`, but
this will require more consideration/investigation in the future.
All these delays have been added to the `spi_controller` struct, as they
would typically be configured by calling `spi_set_cs_timing()` after an
`spi_setup()` call.
Software-mode for CS control, implies that the `set_cs_timing()` hook has
not been provided for the `spi_controller` object.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/spi/spi.c | 50 ++++++++++++++++++++++++++++++++++++++++-
include/linux/spi/spi.h | 5 +++++
2 files changed, 54 insertions(+), 1 deletion(-)
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 54d00c0a26d2..acbbfee837ed 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -775,6 +775,15 @@ int spi_register_board_info(struct spi_board_info const *info, unsigned n)
static void spi_set_cs(struct spi_device *spi, bool enable)
{
+ bool enable1 = enable;
+
+ if (!spi->controller->set_cs_timing) {
+ if (enable1)
+ spi_delay_exec(&spi->controller->cs_setup, NULL);
+ else
+ spi_delay_exec(&spi->controller->cs_hold, NULL);
+ }
+
if (spi->mode & SPI_CS_HIGH)
enable = !enable;
@@ -800,6 +809,11 @@ static void spi_set_cs(struct spi_device *spi, bool enable)
} else if (spi->controller->set_cs) {
spi->controller->set_cs(spi, !enable);
}
+
+ if (!spi->controller->set_cs_timing) {
+ if (!enable1)
+ spi_delay_exec(&spi->controller->cs_inactive, NULL);
+ }
}
#ifdef CONFIG_HAS_DMA
@@ -3144,6 +3158,11 @@ int spi_setup(struct spi_device *spi)
}
EXPORT_SYMBOL_GPL(spi_setup);
+static inline bool _spi_delay_clock_cycles(struct spi_delay *d)
+{
+ return d && d->unit == SPI_DELAY_UNIT_SCK;
+}
+
/**
* spi_set_cs_timing - configure CS setup, hold, and inactive delays
* @spi: the device that requires specific CS timing configuration
@@ -3156,10 +3175,39 @@ EXPORT_SYMBOL_GPL(spi_setup);
int spi_set_cs_timing(struct spi_device *spi, struct spi_delay *setup,
struct spi_delay *hold, struct spi_delay *inactive)
{
+ size_t len;
+
if (spi->controller->set_cs_timing)
return spi->controller->set_cs_timing(spi, setup, hold,
inactive);
- return -ENOTSUPP;
+
+ if (_spi_delay_clock_cycles(setup) ||
+ _spi_delay_clock_cycles(hold) ||
+ _spi_delay_clock_cycles(inactive)) {
+ dev_err(&spi->dev,
+ "Clock-cycle delays for CS not supported in SW mode\n");
+ return -ENOTSUPP;
+ }
+
+ len = sizeof(struct spi_delay);
+
+ /* copy delays to controller */
+ if (setup)
+ memcpy(&spi->controller->cs_setup, setup, len);
+ else
+ memset(&spi->controller->cs_setup, 0, len);
+
+ if (hold)
+ memcpy(&spi->controller->cs_hold, hold, len);
+ else
+ memset(&spi->controller->cs_hold, 0, len);
+
+ if (inactive)
+ memcpy(&spi->controller->cs_inactive, inactive, len);
+ else
+ memset(&spi->controller->cs_inactive, 0, len);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(spi_set_cs_timing);
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 292e752ce34a..def49a76299f 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -602,6 +602,11 @@ struct spi_controller {
/* Optimized handlers for SPI memory-like operations. */
const struct spi_controller_mem_ops *mem_ops;
+ /* CS delays */
+ struct spi_delay cs_setup;
+ struct spi_delay cs_hold;
+ struct spi_delay cs_inactive;
+
/* gpio chip select */
int *cs_gpios;
struct gpio_desc **cs_gpiods;
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH v5 1/3] coresight: etm4x: save/restore state across CPU low power states
From: Andrew Murray @ 2019-09-13 8:50 UTC (permalink / raw)
To: Mathieu Poirier
Cc: Al.Grant, Suzuki K Poulose, Alexander Shishkin, coresight,
Sudeep Holla, Leo Yan, linux-arm-kernel, Mike Leach
In-Reply-To: <20190820215537.GA12159@xps15>
On Tue, Aug 20, 2019 at 03:55:37PM -0600, Mathieu Poirier wrote:
> Hi Andrew,
>
> On Fri, Aug 16, 2019 at 04:46:13PM +0100, Andrew Murray wrote:
> > Some hardware will ignore bit TRCPDCR.PU which is used to signal
> > to hardware that power should not be removed from the trace unit.
> > Let's mitigate against this by conditionally saving and restoring
> > the trace unit state when the CPU enters low power states.
> >
> > This patchset introduces a firmware property named
> > 'arm,coresight-loses-context-with-cpu' - when this is present the
> > hardware state will be conditionally saved and restored.
> >
> > A module parameter 'pm_save_enable' is also introduced which can
> > be configured to override the firmware property. This can be set
> > to never allow save/restore or to conditionally allow it (only for
> > self-hosted). The default value is determined by firmware.
> >
> > We avoid saving the hardware state when self-hosted coresight isn't
> > in use to reduce PM latency - we can't determine this by reading the
> > claim tags (TRCCLAIMCLR) as these are 'trace' registers which need
> > power and clocking, something we can't easily provide in the PM
> > context. Therefore we rely on the existing drvdata->mode internal
> > state that is set when self-hosted coresight is used (and powered).
> >
> > Signed-off-by: Andrew Murray <andrew.murray@arm.com>
> > ---
> > drivers/hwtracing/coresight/coresight-etm4x.c | 318 ++++++++++++++++++
> > drivers/hwtracing/coresight/coresight-etm4x.h | 64 ++++
> > drivers/hwtracing/coresight/coresight.c | 6 +
> > include/linux/coresight.h | 6 +
> > 4 files changed, 394 insertions(+)
> >
> > diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
> > index a128b5063f46..35a524eec36d 100644
> > --- a/drivers/hwtracing/coresight/coresight-etm4x.c
> > +++ b/drivers/hwtracing/coresight/coresight-etm4x.c
> > @@ -18,6 +18,7 @@
> > #include <linux/stat.h>
> > #include <linux/clk.h>
> > #include <linux/cpu.h>
> > +#include <linux/cpu_pm.h>
> > #include <linux/coresight.h>
> > #include <linux/coresight-pmu.h>
> > #include <linux/pm_wakeup.h>
> > @@ -26,6 +27,7 @@
> > #include <linux/uaccess.h>
> > #include <linux/perf_event.h>
> > #include <linux/pm_runtime.h>
> > +#include <linux/property.h>
> > #include <asm/sections.h>
> > #include <asm/local.h>
> > #include <asm/virt.h>
> > @@ -37,6 +39,15 @@ static int boot_enable;
> > module_param(boot_enable, int, 0444);
> > MODULE_PARM_DESC(boot_enable, "Enable tracing on boot");
> >
> > +#define PARAM_PM_SAVE_FIRMWARE 0 /* save self-hosted state as per firmware */
> > +#define PARAM_PM_SAVE_NEVER 1 /* never save any state */
> > +#define PARAM_PM_SAVE_SELF_HOSTED 2 /* save self-hosted state only */
> > +
> > +static int pm_save_enable = PARAM_PM_SAVE_FIRMWARE;
> > +module_param(pm_save_enable, int, 0444);
> > +MODULE_PARM_DESC(pm_save_enable,
> > + "Save/restore state on power down: 1 = never, 2 = self-hosted");
> > +
> > /* The number of ETMv4 currently registered */
> > static int etm4_count;
> > static struct etmv4_drvdata *etmdrvdata[NR_CPUS];
> > @@ -54,6 +65,14 @@ static void etm4_os_unlock(struct etmv4_drvdata *drvdata)
> > isb();
> > }
> >
> > +static void etm4_os_lock(struct etmv4_drvdata *drvdata)
> > +{
> > + /* Writing 0x1 to TRCOSLAR locks the trace registers */
> > + writel_relaxed(0x1, drvdata->base + TRCOSLAR);
> > + drvdata->os_unlock = false;
> > + isb();
> > +}
> > +
> > static bool etm4_arch_supported(u8 arch)
> > {
> > /* Mask out the minor version number */
> > @@ -1085,6 +1104,288 @@ static void etm4_init_trace_id(struct etmv4_drvdata *drvdata)
> > drvdata->trcid = coresight_get_trace_id(drvdata->cpu);
> > }
> >
> > +#ifdef CONFIG_CPU_PM
> > +static int etm4_cpu_save(struct etmv4_drvdata *drvdata)
> > +{
> > + int i, ret = 0;
> > + struct etmv4_save_state *state;
> > + struct device *etm_dev = &drvdata->csdev->dev;
> > +
> > + /*
> > + * As recommended by 3.4.1 ("The procedure when powering down the PE")
> > + * of ARM IHI 0064D
> > + */
> > + dsb(sy);
> > + isb();
> > +
> > + CS_UNLOCK(drvdata->base);
> > +
> > + /* Lock the OS lock to disable trace and external debugger access */
> > + etm4_os_lock(drvdata);
> > +
> > + /* wait for TRCSTATR.PMSTABLE to go up */
> > + if (coresight_timeout(drvdata->base, TRCSTATR,
> > + TRCSTATR_PMSTABLE_BIT, 1)) {
>
> Indentation problems
Thanks, I'll also improve the indentation of etm4_cpu_restore.
>
> > + dev_err(etm_dev,
> > + "timeout while waiting for PM Stable Status\n");
> > + etm4_os_unlock(drvdata);
> > + ret = -EBUSY;
> > + goto out;
> > + }
> > +
> > + state = drvdata->save_state;
> > +
> > + state->trcprgctlr = readl(drvdata->base + TRCPRGCTLR);
> > + state->trcprocselr = readl(drvdata->base + TRCPROCSELR);
> > + state->trcconfigr = readl(drvdata->base + TRCCONFIGR);
> > + state->trcauxctlr = readl(drvdata->base + TRCAUXCTLR);
> > + state->trceventctl0r = readl(drvdata->base + TRCEVENTCTL0R);
> > + state->trceventctl1r = readl(drvdata->base + TRCEVENTCTL1R);
> > + state->trcstallctlr = readl(drvdata->base + TRCSTALLCTLR);
> > + state->trctsctlr = readl(drvdata->base + TRCTSCTLR);
> > + state->trcsyncpr = readl(drvdata->base + TRCSYNCPR);
> > + state->trcccctlr = readl(drvdata->base + TRCCCCTLR);
> > + state->trcbbctlr = readl(drvdata->base + TRCBBCTLR);
> > + state->trctraceidr = readl(drvdata->base + TRCTRACEIDR);
> > + state->trcqctlr = readl(drvdata->base + TRCQCTLR);
> > +
> > + state->trcvictlr = readl(drvdata->base + TRCVICTLR);
> > + state->trcviiectlr = readl(drvdata->base + TRCVIIECTLR);
> > + state->trcvissctlr = readl(drvdata->base + TRCVISSCTLR);
> > + state->trcvipcssctlr = readl(drvdata->base + TRCVIPCSSCTLR);
> > + state->trcvdctlr = readl(drvdata->base + TRCVDCTLR);
> > + state->trcvdsacctlr = readl(drvdata->base + TRCVDSACCTLR);
> > + state->trcvdarcctlr = readl(drvdata->base + TRCVDARCCTLR);
> > +
> > + for (i = 0; i < drvdata->nrseqstate; i++)
> > + state->trcseqevr[i] = readl(drvdata->base + TRCSEQEVRn(i));
> > +
> > + state->trcseqrstevr = readl(drvdata->base + TRCSEQRSTEVR);
> > + state->trcseqstr = readl(drvdata->base + TRCSEQSTR);
> > + state->trcextinselr = readl(drvdata->base + TRCEXTINSELR);
> > +
> > + for (i = 0; i < drvdata->nr_cntr; i++) {
> > + state->trccntrldvr[i] = readl(drvdata->base + TRCCNTRLDVRn(i));
> > + state->trccntctlr[i] = readl(drvdata->base + TRCCNTCTLRn(i));
> > + state->trccntvr[i] = readl(drvdata->base + TRCCNTVRn(i));
> > + }
> > +
> > + for (i = 0; i < drvdata->nr_resource * 2; i++)
> > + state->trcrsctlr[i] = readl(drvdata->base + TRCRSCTLRn(i));
> > +
> > + for (i = 0; i < drvdata->nr_ss_cmp; i++) {
> > + state->trcssccr[i] = readl(drvdata->base + TRCSSCCRn(i));
> > + state->trcsscsr[i] = readl(drvdata->base + TRCSSCSRn(i));
> > + state->trcsspcicr[i] = readl(drvdata->base + TRCSSPCICRn(i));
> > + }
> > +
> > + for (i = 0; i < drvdata->nr_addr_cmp * 2; i++) {
> > + state->trcacvr[i] = readl(drvdata->base + TRCACVRn(i));
> > + state->trcacatr[i] = readl(drvdata->base + TRCACATRn(i));
> > + }
> > +
> > + /*
> > + * Data trace stream is architecturally prohibited for A profile cores
> > + * so we don't save (or later restore) trcdvcvr and trcdvcmr - As per
> > + * section 1.3.4 ("Possible functional configurations of an ETMv4 trace
> > + * unit") of ARM IHI 0064D.
> > + */
> > +
> > + for (i = 0; i < drvdata->numcidc; i++)
> > + state->trccidcvr[i] = readl(drvdata->base + TRCCIDCVRn(i));
> > +
> > + for (i = 0; i < drvdata->numvmidc; i++)
> > + state->trcvmidcvr[i] = readl(drvdata->base + TRCVMIDCVRn(i));
> > +
> > + state->trccidcctlr0 = readl(drvdata->base + TRCCIDCCTLR0);
> > + state->trccidcctlr1 = readl(drvdata->base + TRCCIDCCTLR1);
> > +
> > + state->trcvmidcctlr0 = readl(drvdata->base + TRCVMIDCCTLR0);
> > + state->trcvmidcctlr0 = readl(drvdata->base + TRCVMIDCCTLR1);
> > +
> > + state->trcclaimset = readl(drvdata->base + TRCCLAIMCLR);
> > +
> > + state->trcpdcr = readl(drvdata->base + TRCPDCR);
> > +
> > + /* wait for TRCSTATR.IDLE to go up */
> > + if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 1)) {
> > + dev_err(etm_dev,
> > + "timeout while waiting for Idle Trace Status\n");
> > + etm4_os_unlock(drvdata);
> > + ret = -EBUSY;
> > + goto out;
> > + }
> > +
> > + drvdata->state_needs_restore = true;
> > +
> > + /*
> > + * Power can be removed from the trace unit now. We do this to
> > + * potentially save power on systems that respect the TRCPDCR_PU
> > + * despite requesting software to save/restore state.
> > + */
> > + writel_relaxed((state->trcpdcr & ~TRCPDCR_PU),
> > + drvdata->base + TRCPDCR);
> > +
> > +out:
> > + CS_LOCK(drvdata->base);
> > + return ret;
> > +}
> > +
> > +static void etm4_cpu_restore(struct etmv4_drvdata *drvdata)
> > +{
> > + int i;
> > + struct etmv4_save_state *state = drvdata->save_state;
> > +
> > + CS_UNLOCK(drvdata->base);
> > +
> > + writel_relaxed(state->trcclaimset, drvdata->base + TRCCLAIMSET);
> > +
> > + writel_relaxed(state->trcprgctlr, drvdata->base + TRCPRGCTLR);
> > + writel_relaxed(state->trcprocselr, drvdata->base + TRCPROCSELR);
> > + writel_relaxed(state->trcconfigr, drvdata->base + TRCCONFIGR);
> > + writel_relaxed(state->trcauxctlr, drvdata->base + TRCAUXCTLR);
> > + writel_relaxed(state->trceventctl0r, drvdata->base + TRCEVENTCTL0R);
> > + writel_relaxed(state->trceventctl1r, drvdata->base + TRCEVENTCTL1R);
> > + writel_relaxed(state->trcstallctlr, drvdata->base + TRCSTALLCTLR);
> > + writel_relaxed(state->trctsctlr, drvdata->base + TRCTSCTLR);
> > + writel_relaxed(state->trcsyncpr, drvdata->base + TRCSYNCPR);
> > + writel_relaxed(state->trcccctlr, drvdata->base + TRCCCCTLR);
> > + writel_relaxed(state->trcbbctlr, drvdata->base + TRCBBCTLR);
> > + writel_relaxed(state->trctraceidr, drvdata->base + TRCTRACEIDR);
> > + writel_relaxed(state->trcqctlr, drvdata->base + TRCQCTLR);
> > +
> > + writel_relaxed(state->trcvictlr, drvdata->base + TRCVICTLR);
> > + writel_relaxed(state->trcviiectlr, drvdata->base + TRCVIIECTLR);
> > + writel_relaxed(state->trcvissctlr, drvdata->base + TRCVISSCTLR);
> > + writel_relaxed(state->trcvipcssctlr, drvdata->base + TRCVIPCSSCTLR);
> > + writel_relaxed(state->trcvdctlr, drvdata->base + TRCVDCTLR);
> > + writel_relaxed(state->trcvdsacctlr, drvdata->base + TRCVDSACCTLR);
> > + writel_relaxed(state->trcvdarcctlr, drvdata->base + TRCVDARCCTLR);
> > +
> > + for (i = 0; i < drvdata->nrseqstate; i++)
> > + writel_relaxed(state->trcseqevr[i],
> > + drvdata->base + TRCSEQEVRn(i));
> > +
> > + writel_relaxed(state->trcseqrstevr, drvdata->base + TRCSEQRSTEVR);
> > + writel_relaxed(state->trcseqstr, drvdata->base + TRCSEQSTR);
> > + writel_relaxed(state->trcextinselr, drvdata->base + TRCEXTINSELR);
> > +
> > + for (i = 0; i < drvdata->nr_cntr; i++) {
> > + writel_relaxed(state->trccntrldvr[i],
> > + drvdata->base + TRCCNTRLDVRn(i));
> > + writel_relaxed(state->trccntctlr[i],
> > + drvdata->base + TRCCNTCTLRn(i));
> > + writel_relaxed(state->trccntvr[i],
> > + drvdata->base + TRCCNTVRn(i));
> > + }
> > +
> > + for (i = 0; i < drvdata->nr_resource * 2; i++)
> > + writel_relaxed(state->trcrsctlr[i],
> > + drvdata->base + TRCRSCTLRn(i));
> > +
> > + for (i = 0; i < drvdata->nr_ss_cmp; i++) {
> > + writel_relaxed(state->trcssccr[i],
> > + drvdata->base + TRCSSCCRn(i));
> > + writel_relaxed(state->trcsscsr[i],
> > + drvdata->base + TRCSSCSRn(i));
> > + writel_relaxed(state->trcsspcicr[i],
> > + drvdata->base + TRCSSPCICRn(i));
> > + }
> > +
> > + for (i = 0; i < drvdata->nr_addr_cmp * 2; i++) {
> > + writel_relaxed(state->trcacvr[i],
> > + drvdata->base + TRCACVRn(i));
> > + writel_relaxed(state->trcacatr[i],
> > + drvdata->base + TRCACATRn(i));
> > + }
> > +
> > + for (i = 0; i < drvdata->numcidc; i++)
> > + writel_relaxed(state->trccidcvr[i],
> > + drvdata->base + TRCCIDCVRn(i));
> > +
> > + for (i = 0; i < drvdata->numvmidc; i++)
> > + writel_relaxed(state->trcvmidcvr[i],
> > + drvdata->base + TRCVMIDCVRn(i));
> > +
> > + writel_relaxed(state->trccidcctlr0, drvdata->base + TRCCIDCCTLR0);
> > + writel_relaxed(state->trccidcctlr1, drvdata->base + TRCCIDCCTLR1);
> > +
> > + writel_relaxed(state->trcvmidcctlr0, drvdata->base + TRCVMIDCCTLR0);
> > + writel_relaxed(state->trcvmidcctlr0, drvdata->base + TRCVMIDCCTLR1);
> > +
> > + writel_relaxed(state->trcclaimset, drvdata->base + TRCCLAIMSET);
> > +
> > + writel_relaxed(state->trcpdcr, drvdata->base + TRCPDCR);
> > +
> > + drvdata->state_needs_restore = false;
> > +
> > + /*
> > + * As recommended by section 4.3.7 ("Synchronization when using the
> > + * memory-mapped interface") of ARM IHI 0064D
> > + */
> > + dsb(sy);
> > + isb();
> > +
> > + /* Unlock the OS lock to re-enable trace and external debug access */
> > + etm4_os_unlock(drvdata);
> > + CS_LOCK(drvdata->base);
> > +}
> > +
> > +static int etm4_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd,
> > + void *v)
> > +{
> > + struct etmv4_drvdata *drvdata;
> > + unsigned int cpu = smp_processor_id();
> > +
> > + if (!etmdrvdata[cpu])
> > + return 0;
> > +
> > + drvdata = etmdrvdata[cpu];
> > +
> > + if (!drvdata->save_state)
> > + return NOTIFY_OK;
> > +
> > + if (WARN_ON_ONCE(drvdata->cpu != cpu))
> > + return NOTIFY_BAD;
> > +
> > + switch (cmd) {
> > + case CPU_PM_ENTER:
> > + /* save the state if self-hosted coresight is in use */
> > + if (local_read(&drvdata->mode))
> > + if (etm4_cpu_save(drvdata))
> > + return NOTIFY_BAD;
> > + break;
> > + case CPU_PM_EXIT:
>
> Implicit fallthroughs are coming to an end. Please add a
> /* fallthrough */
>
> > + case CPU_PM_ENTER_FAILED:
> > + /* trcclaimset is set when there is state to restore */
>
> As far as I can tell the above comment doesn't apply anymore.
I'll update it.
Thanks,
Andrew Murray
>
> > + if (drvdata->state_needs_restore)
> > + etm4_cpu_restore(drvdata);
> > + break;
> > + default:
> > + return NOTIFY_DONE;
> > + }
> > +
> > + return NOTIFY_OK;
> > +}
> > +
> > +static struct notifier_block etm4_cpu_pm_nb = {
> > + .notifier_call = etm4_cpu_pm_notify,
> > +};
> > +
> > +static int etm4_cpu_pm_register(void)
> > +{
> > + return cpu_pm_register_notifier(&etm4_cpu_pm_nb);
> > +}
> > +
> > +static void etm4_cpu_pm_unregister(void)
> > +{
> > + cpu_pm_unregister_notifier(&etm4_cpu_pm_nb);
> > +}
> > +#else
> > +static int etm4_cpu_pm_register(void) { return 0; }
> > +static void etm4_cpu_pm_unregister(void) { }
> > +#endif
> > +
> > static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
> > {
> > int ret;
> > @@ -1101,6 +1402,17 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
> >
> > dev_set_drvdata(dev, drvdata);
> >
> > + if (pm_save_enable == PARAM_PM_SAVE_FIRMWARE)
> > + pm_save_enable = coresight_loses_context_with_cpu(dev) ?
> > + PARAM_PM_SAVE_SELF_HOSTED : PARAM_PM_SAVE_NEVER;
> > +
> > + if (pm_save_enable != PARAM_PM_SAVE_NEVER) {
> > + drvdata->save_state = devm_kmalloc(dev,
> > + sizeof(struct etmv4_save_state), GFP_KERNEL);
> > + if (!drvdata->save_state)
> > + return -ENOMEM;
> > + }
> > +
> > /* Validity for the resource is already checked by the AMBA core */
> > base = devm_ioremap_resource(dev, res);
> > if (IS_ERR(base))
> > @@ -1135,6 +1447,10 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
> > if (ret < 0)
> > goto err_arch_supported;
> > hp_online = ret;
> > +
> > + ret = etm4_cpu_pm_register();
> > + if (ret)
> > + goto err_arch_supported;
> > }
> >
> > cpus_read_unlock();
> > @@ -1185,6 +1501,8 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
> >
> > err_arch_supported:
> > if (--etm4_count == 0) {
> > + etm4_cpu_pm_unregister();
> > +
> > cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
> > if (hp_online)
> > cpuhp_remove_state_nocalls(hp_online);
> > diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h
> > index 4523f10ddd0f..546d790cb01b 100644
> > --- a/drivers/hwtracing/coresight/coresight-etm4x.h
> > +++ b/drivers/hwtracing/coresight/coresight-etm4x.h
> > @@ -175,6 +175,7 @@
> > ETM_MODE_EXCL_USER)
> >
> > #define TRCSTATR_IDLE_BIT 0
> > +#define TRCSTATR_PMSTABLE_BIT 1
> > #define ETM_DEFAULT_ADDR_COMP 0
> >
> > /* PowerDown Control Register bits */
> > @@ -281,6 +282,65 @@ struct etmv4_config {
> > u32 ext_inp;
> > };
> >
> > +/**
> > + * struct etm4_save_state - state to be preserved when ETM is without power
> > + */
> > +struct etmv4_save_state {
> > + u32 trcprgctlr;
> > + u32 trcprocselr;
> > + u32 trcconfigr;
> > + u32 trcauxctlr;
> > + u32 trceventctl0r;
> > + u32 trceventctl1r;
> > + u32 trcstallctlr;
> > + u32 trctsctlr;
> > + u32 trcsyncpr;
> > + u32 trcccctlr;
> > + u32 trcbbctlr;
> > + u32 trctraceidr;
> > + u32 trcqctlr;
> > +
> > + u32 trcvictlr;
> > + u32 trcviiectlr;
> > + u32 trcvissctlr;
> > + u32 trcvipcssctlr;
> > + u32 trcvdctlr;
> > + u32 trcvdsacctlr;
> > + u32 trcvdarcctlr;
> > +
> > + u32 trcseqevr[ETM_MAX_SEQ_STATES];
> > + u32 trcseqrstevr;
> > + u32 trcseqstr;
> > + u32 trcextinselr;
> > + u32 trccntrldvr[ETMv4_MAX_CNTR];
> > + u32 trccntctlr[ETMv4_MAX_CNTR];
> > + u32 trccntvr[ETMv4_MAX_CNTR];
> > +
> > + u32 trcrsctlr[ETM_MAX_RES_SEL * 2];
> > +
> > + u32 trcssccr[ETM_MAX_SS_CMP];
> > + u32 trcsscsr[ETM_MAX_SS_CMP];
> > + u32 trcsspcicr[ETM_MAX_SS_CMP];
> > +
> > + u64 trcacvr[ETM_MAX_SINGLE_ADDR_CMP];
> > + u64 trcacatr[ETM_MAX_SINGLE_ADDR_CMP];
> > + u64 trccidcvr[ETMv4_MAX_CTXID_CMP];
> > + u32 trcvmidcvr[ETM_MAX_VMID_CMP];
> > + u32 trccidcctlr0;
> > + u32 trccidcctlr1;
> > + u32 trcvmidcctlr0;
> > + u32 trcvmidcctlr1;
> > +
> > + u32 trcclaimset;
> > +
> > + u32 cntr_val[ETMv4_MAX_CNTR];
> > + u32 seq_state;
> > + u32 vinst_ctrl;
> > + u32 ss_status[ETM_MAX_SS_CMP];
> > +
> > + u32 trcpdcr;
> > +};
> > +
> > /**
> > * struct etm4_drvdata - specifics associated to an ETM component
> > * @base: Memory mapped base address for this component.
> > @@ -336,6 +396,8 @@ struct etmv4_config {
> > * @atbtrig: If the implementation can support ATB triggers
> > * @lpoverride: If the implementation can support low-power state over.
> > * @config: structure holding configuration parameters.
> > + * @save_state: State to be preserved across power loss
> > + * @state_needs_restore: True when there is context to restore after PM exit
> > */
> > struct etmv4_drvdata {
> > void __iomem *base;
> > @@ -381,6 +443,8 @@ struct etmv4_drvdata {
> > bool atbtrig;
> > bool lpoverride;
> > struct etmv4_config config;
> > + struct etmv4_save_state *save_state;
> > + bool state_needs_restore;
> > };
> >
> > /* Address comparator access types */
> > diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
> > index 6453c67a4d01..e6ca899fea4e 100644
> > --- a/drivers/hwtracing/coresight/coresight.c
> > +++ b/drivers/hwtracing/coresight/coresight.c
> > @@ -1308,6 +1308,12 @@ static inline int coresight_search_device_idx(struct coresight_dev_list *dict,
> > return -ENOENT;
> > }
> >
> > +bool coresight_loses_context_with_cpu(struct device *dev)
> > +{
> > + return fwnode_property_present(dev_fwnode(dev),
> > + "arm,coresight-loses-context-with-cpu");
> > +}
> > +
> > /*
> > * coresight_alloc_device_name - Get an index for a given device in the
> > * device index list specific to a driver. An index is allocated for a
> > diff --git a/include/linux/coresight.h b/include/linux/coresight.h
> > index a2b68823717b..44e552de419c 100644
> > --- a/include/linux/coresight.h
> > +++ b/include/linux/coresight.h
> > @@ -285,6 +285,8 @@ extern void coresight_disclaim_device(void __iomem *base);
> > extern void coresight_disclaim_device_unlocked(void __iomem *base);
> > extern char *coresight_alloc_device_name(struct coresight_dev_list *devs,
> > struct device *dev);
> > +
> > +extern bool coresight_loses_context_with_cpu(struct device *dev);
> > #else
> > static inline struct coresight_device *
> > coresight_register(struct coresight_desc *desc) { return NULL; }
> > @@ -307,6 +309,10 @@ static inline int coresight_claim_device(void __iomem *base)
> > static inline void coresight_disclaim_device(void __iomem *base) {}
> > static inline void coresight_disclaim_device_unlocked(void __iomem *base) {}
> >
> > +static inline bool coresight_loses_context_with_cpu(struct device *dev)
> > +{
> > + return false;
> > +}
> > #endif
> >
> > extern int coresight_get_cpu(struct device *dev);
> > --
> > 2.21.0
> >
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v5 0/4] Raspberry Pi 4 DMA addressing support
From: Stefan Wahren @ 2019-09-13 8:50 UTC (permalink / raw)
To: Matthias Brugger, catalin.marinas, marc.zyngier, Matthias Brugger,
robh+dt, linux-mm, linux-arm-kernel, linux-riscv, hch,
Nicolas Saenz Julienne
Cc: f.fainelli, robin.murphy, linux-kernel, linux-rpi-kernel, phill,
will, m.szyprowski
In-Reply-To: <4a6f965b-c988-5839-169f-9f24a0e7a567@suse.com>
Am 13.09.19 um 10:09 schrieb Matthias Brugger:
>
> On 12/09/2019 21:32, Stefan Wahren wrote:
>> Am 12.09.19 um 19:18 schrieb Matthias Brugger:
>>> On 10/09/2019 11:27, Matthias Brugger wrote:
>>>> On 09/09/2019 21:33, Stefan Wahren wrote:
>>>>> Hi Nicolas,
>>>>>
>>>>> Am 09.09.19 um 11:58 schrieb Nicolas Saenz Julienne:
>>>>>> Hi all,
>>>>>> this series attempts to address some issues we found while bringing up
>>>>>> the new Raspberry Pi 4 in arm64 and it's intended to serve as a follow
>>>>>> up of these discussions:
>>>>>> v4: https://lkml.org/lkml/2019/9/6/352
>>>>>> v3: https://lkml.org/lkml/2019/9/2/589
>>>>>> v2: https://lkml.org/lkml/2019/8/20/767
>>>>>> v1: https://lkml.org/lkml/2019/7/31/922
>>>>>> RFC: https://lkml.org/lkml/2019/7/17/476
>>>>>>
>>>>>> The new Raspberry Pi 4 has up to 4GB of memory but most peripherals can
>>>>>> only address the first GB: their DMA address range is
>>>>>> 0xc0000000-0xfc000000 which is aliased to the first GB of physical
>>>>>> memory 0x00000000-0x3c000000. Note that only some peripherals have these
>>>>>> limitations: the PCIe, V3D, GENET, and 40-bit DMA channels have a wider
>>>>>> view of the address space by virtue of being hooked up trough a second
>>>>>> interconnect.
>>>>>>
>>>>>> Part of this is solved on arm32 by setting up the machine specific
>>>>>> '.dma_zone_size = SZ_1G', which takes care of reserving the coherent
>>>>>> memory area at the right spot. That said no buffer bouncing (needed for
>>>>>> dma streaming) is available at the moment, but that's a story for
>>>>>> another series.
>>>>>>
>>>>>> Unfortunately there is no such thing as 'dma_zone_size' in arm64. Only
>>>>>> ZONE_DMA32 is created which is interpreted by dma-direct and the arm64
>>>>>> arch code as if all peripherals where be able to address the first 4GB
>>>>>> of memory.
>>>>>>
>>>>>> In the light of this, the series implements the following changes:
>>>>>>
>>>>>> - Create both DMA zones in arm64, ZONE_DMA will contain the first 1G
>>>>>> area and ZONE_DMA32 the rest of the 32 bit addressable memory. So far
>>>>>> the RPi4 is the only arm64 device with such DMA addressing limitations
>>>>>> so this hardcoded solution was deemed preferable.
>>>>>>
>>>>>> - Properly set ARCH_ZONE_DMA_BITS.
>>>>>>
>>>>>> - Reserve the CMA area in a place suitable for all peripherals.
>>>>>>
>>>>>> This series has been tested on multiple devices both by checking the
>>>>>> zones setup matches the expectations and by double-checking physical
>>>>>> addresses on pages allocated on the three relevant areas GFP_DMA,
>>>>>> GFP_DMA32, GFP_KERNEL:
>>>>>>
>>>>>> - On an RPi4 with variations on the ram memory size. But also forcing
>>>>>> the situation where all three memory zones are nonempty by setting a 3G
>>>>>> ZONE_DMA32 ceiling on a 4G setup. Both with and without NUMA support.
>>>>>>
>>>>> i like to test this series on Raspberry Pi 4 and i have some questions
>>>>> to get arm64 running:
>>>>>
>>>>> Do you use U-Boot? Which tree?
>>>> If you want to use U-Boot, try v2019.10-rc4, it should have everything you need
>>>> to boot your kernel.
>>>>
>>> Ok, here is a thing. In the linux kernel we now use bcm2711 as SoC name, but the
>>> RPi4 devicetree provided by the FW uses mostly bcm2838.
>> Do you mean the DTB provided at runtime?
>>
>> You mean the merged U-Boot changes, doesn't work with my Raspberry Pi
>> series?
>>
>>> U-Boot in its default
>>> config uses the devicetree provided by the FW, mostly because this way you don't
>>> have to do anything to find out how many RAM you really have. Secondly because
>>> this will allow us, in the near future, to have one U-boot binary for both RPi3
>>> and RPi4 (and as a side effect one binary for RPi1 and RPi2).
>>>
>>> Anyway, I found at least, that the following compatibles need to be added:
>>>
>>> "brcm,bcm2838-cprman"
>>> "brcm,bcm2838-gpio"
>>>
>>> Without at least the cprman driver update, you won't see anything.
>>>
>>> "brcm,bcm2838-rng200" is also a candidate.
>>>
>>> I also suppose we will need to add "brcm,bcm2838" to
>>> arch/arm/mach-bcm/bcm2711.c, but I haven't verified this.
>> How about changing this in the downstream kernel? Which is much easier.
> I'm not sure I understand what you want to say. My goal is to use the upstream
> kernel with the device tree blob provided by the FW.
The device tree blob you are talking is defined in this repository:
https://github.com/raspberrypi/linux
So the word FW is misleading to me.
> If you talk about the
> downstream kernel, I suppose you mean we should change this in the FW DT blob
> and in the downstream kernel. That would work for me.
>
> Did I understand you correctly?
Yes
So i suggest to add the upstream compatibles into the repo mentioned above.
Sorry, but in case you decided as a U-Boot developer to be compatible
with a unreviewed DT, we also need to make U-Boot compatible with
upstream and downstream DT blobs.
>
>>> Regards,
>>> Matthias
>>>
>>>> Regards,
>>>> Matthias
>>>>
>>>>> Are there any config.txt tweaks necessary?
>>>>>
>>>>>
>>>> _______________________________________________
>>>> linux-arm-kernel mailing list
>>>> linux-arm-kernel@lists.infradead.org
>>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>>>
>>> _______________________________________________
>>> linux-arm-kernel mailing list
>>> linux-arm-kernel@lists.infradead.org
>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH] mm/pgtable/debug: Fix test validating architecture page table helpers
From: Kirill A. Shutemov @ 2019-09-13 8:51 UTC (permalink / raw)
To: Anshuman Khandual
Cc: Mark Rutland, linux-ia64, linux-sh, Peter Zijlstra, James Hogan,
Heiko Carstens, Michal Hocko, linux-mm, Paul Mackerras,
sparclinux, Thomas Gleixner, linux-s390, Jason Gunthorpe, x86,
Russell King - ARM Linux, Matthew Wilcox, Steven Price,
Tetsuo Handa, Gerald Schaefer, linux-snps-arc, linux-arm-kernel,
Kees Cook, Masahiro Yamada, Mark Brown, Dan Williams,
Vlastimil Babka, Christophe Leroy, Sri Krishna chowdary,
Dave Hansen, Greg Kroah-Hartman, Ard Biesheuvel, linux-mips,
Ralf Baechle, linux-kernel, Paul Burton, Mike Rapoport,
Vineet Gupta, Martin Schwidefsky, Andrew Morton, linuxppc-dev,
David S. Miller
In-Reply-To: <bdf7f152-d093-1691-4e96-77da7eb9e20a@arm.com>
On Fri, Sep 13, 2019 at 02:12:45PM +0530, Anshuman Khandual wrote:
>
>
> On 09/13/2019 12:41 PM, Christophe Leroy wrote:
> >
> >
> > Le 13/09/2019 à 09:03, Christophe Leroy a écrit :
> >>
> >>
> >> Le 13/09/2019 à 08:58, Anshuman Khandual a écrit :
> >>> On 09/13/2019 11:53 AM, Christophe Leroy wrote:
> >>>> Fix build failure on powerpc.
> >>>>
> >>>> Fix preemption imbalance.
> >>>>
> >>>> Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
> >>>> ---
> >>>> mm/arch_pgtable_test.c | 3 +++
> >>>> 1 file changed, 3 insertions(+)
> >>>>
> >>>> diff --git a/mm/arch_pgtable_test.c b/mm/arch_pgtable_test.c
> >>>> index 8b4a92756ad8..f2b3c9ec35fa 100644
> >>>> --- a/mm/arch_pgtable_test.c
> >>>> +++ b/mm/arch_pgtable_test.c
> >>>> @@ -24,6 +24,7 @@
> >>>> #include <linux/swap.h>
> >>>> #include <linux/swapops.h>
> >>>> #include <linux/sched/mm.h>
> >>>> +#include <linux/highmem.h>
> >>>
> >>> This is okay.
> >>>
> >>>> #include <asm/pgalloc.h>
> >>>> #include <asm/pgtable.h>
> >>>> @@ -400,6 +401,8 @@ static int __init arch_pgtable_tests_init(void)
> >>>> p4d_clear_tests(p4dp);
> >>>> pgd_clear_tests(mm, pgdp);
> >>>> + pte_unmap(ptep);
> >>>> +
> >>>
> >>> Now the preemption imbalance via pte_alloc_map() path i.e
> >>>
> >>> pte_alloc_map() -> pte_offset_map() -> kmap_atomic()
> >>>
> >>> Is not this very much powerpc 32 specific or this will be applicable
> >>> for all platform which uses kmap_XXX() to map high memory ?
> >>>
> >>
> >> See https://elixir.bootlin.com/linux/v5.3-rc8/source/include/linux/highmem.h#L91
> >>
> >> I think it applies at least to all arches using the generic implementation.
> >>
> >> Applies also to arm:
> >> https://elixir.bootlin.com/linux/v5.3-rc8/source/arch/arm/mm/highmem.c#L52
> >>
> >> Applies also to mips:
> >> https://elixir.bootlin.com/linux/v5.3-rc8/source/arch/mips/mm/highmem.c#L47
> >>
> >> Same on sparc:
> >> https://elixir.bootlin.com/linux/v5.3-rc8/source/arch/sparc/mm/highmem.c#L52
> >>
> >> Same on x86:
> >> https://elixir.bootlin.com/linux/v5.3-rc8/source/arch/x86/mm/highmem_32.c#L34
> >>
> >> I have not checked others, but I guess it is like that for all.
> >>
> >
> >
> > Seems like I answered too quickly. All kmap_atomic() do preempt_disable(), but not all pte_alloc_map() call kmap_atomic().
> >
> > However, for instance ARM does:
> >
> > https://elixir.bootlin.com/linux/v5.3-rc8/source/arch/arm/include/asm/pgtable.h#L200
> >
> > And X86 as well:
> >
> > https://elixir.bootlin.com/linux/v5.3-rc8/source/arch/x86/include/asm/pgtable_32.h#L51
> >
> > Microblaze also:
> >
> > https://elixir.bootlin.com/linux/v5.3-rc8/source/arch/microblaze/include/asm/pgtable.h#L495
>
> All the above platforms checks out to be using k[un]map_atomic(). I am wondering whether
> any of the intermediate levels will have similar problems on any these 32 bit platforms
> or any other platforms which might be using generic k[un]map_atomic().
No. Kernel only allocates pte page table from highmem. All other page
tables are always visible in kernel address space.
--
Kirill A. Shutemov
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2] dt-bindings: arm: renesas: Convert 'renesas,prr' to json-schema
From: Simon Horman @ 2019-09-13 8:59 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Mark Rutland, devicetree, Yoshihiro Kaneko, Magnus Damm,
linux-renesas-soc, Rob Herring, linux-arm-kernel
In-Reply-To: <20190908120528.9392-1-horms+renesas@verge.net.au>
On Sun, Sep 08, 2019 at 01:05:28PM +0100, Simon Horman wrote:
> Convert Renesas Product Register bindings documentation to json-schema.
>
> Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
Apologies Geert,
this patch was supposed to be addressed to you rather than myself.
> ---
> Based on v5.3-rc1
> Tested using:
> make dtbs_check DT_SCHEMA_FILES=Documentation/devicetree/bindings/arm/renesas,prr.yaml
>
> v2
> * Use simple enum for compat values
> * Drop "" from compat values
> * Only supply 'maxItems' property to 'reg'
> ---
> .../devicetree/bindings/arm/renesas,prr.txt | 20 -------------
> .../devicetree/bindings/arm/renesas,prr.yaml | 35 ++++++++++++++++++++++
> 2 files changed, 35 insertions(+), 20 deletions(-)
> delete mode 100644 Documentation/devicetree/bindings/arm/renesas,prr.txt
> create mode 100644 Documentation/devicetree/bindings/arm/renesas,prr.yaml
>
> diff --git a/Documentation/devicetree/bindings/arm/renesas,prr.txt b/Documentation/devicetree/bindings/arm/renesas,prr.txt
> deleted file mode 100644
> index 08e482e953ca..000000000000
> --- a/Documentation/devicetree/bindings/arm/renesas,prr.txt
> +++ /dev/null
> @@ -1,20 +0,0 @@
> -Renesas Product Register
> -
> -Most Renesas ARM SoCs have a Product Register or Boundary Scan ID Register that
> -allows to retrieve SoC product and revision information. If present, a device
> -node for this register should be added.
> -
> -Required properties:
> - - compatible: Must be one of:
> - "renesas,prr"
> - "renesas,bsid"
> - - reg: Base address and length of the register block.
> -
> -
> -Examples
> ---------
> -
> - prr: chipid@ff000044 {
> - compatible = "renesas,prr";
> - reg = <0 0xff000044 0 4>;
> - };
> diff --git a/Documentation/devicetree/bindings/arm/renesas,prr.yaml b/Documentation/devicetree/bindings/arm/renesas,prr.yaml
> new file mode 100644
> index 000000000000..7f8d17f33983
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/renesas,prr.yaml
> @@ -0,0 +1,35 @@
> +# SPDX-License-Identifier: GPL-2.0
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/arm/renesas,prr.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Renesas Product Register
> +
> +maintainers:
> + - Geert Uytterhoeven <geert+renesas@glider.be>
> + - Magnus Damm <magnus.damm@gmail.com>
> +
> +description: |
> + Most Renesas ARM SoCs have a Product Register or Boundary Scan ID
> + Register that allows to retrieve SoC product and revision information.
> + If present, a device node for this register should be added.
> +
> +properties:
> + compatible:
> + enum:
> + - renesas,prr
> + - renesas,bsid
> + reg:
> + maxItems: 1
> +
> +required:
> + - compatible
> + - reg
> +
> +examples:
> + - |
> + prr: chipid@ff000044 {
> + compatible = "renesas,prr";
> + reg = <0 0xff000044 0 4>;
> + };
> --
> 2.11.0
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH V2 2/2] mm/pgtable/debug: Add test validating architecture page table helpers
From: Anshuman Khandual @ 2019-09-13 9:02 UTC (permalink / raw)
To: Christophe Leroy, linux-mm
Cc: Mark Rutland, linux-ia64, linux-sh, Peter Zijlstra, James Hogan,
Heiko Carstens, Michal Hocko, Dave Hansen, Paul Mackerras,
sparclinux, Thomas Gleixner, linux-s390, Jason Gunthorpe,
Michael Ellerman, x86, Russell King - ARM Linux, Matthew Wilcox,
Steven Price, Tetsuo Handa, Gerald Schaefer, linux-snps-arc,
Kees Cook, Masahiro Yamada, Mark Brown, Kirill A . Shutemov,
Dan Williams, Vlastimil Babka, linux-arm-kernel,
Sri Krishna chowdary, Ard Biesheuvel, Greg Kroah-Hartman,
linux-mips, Ralf Baechle, linux-kernel, Paul Burton,
Mike Rapoport, Vineet Gupta, Martin Schwidefsky, Andrew Morton,
linuxppc-dev, David S. Miller
In-Reply-To: <ab0ca38b-1e4f-b636-f8b4-007a15903984@c-s.fr>
On 09/12/2019 10:44 PM, Christophe Leroy wrote:
>
>
> Le 12/09/2019 à 08:02, Anshuman Khandual a écrit :
>> This adds a test module which will validate architecture page table helpers
>> and accessors regarding compliance with generic MM semantics expectations.
>> This will help various architectures in validating changes to the existing
>> page table helpers or addition of new ones.
>>
>> Test page table and memory pages creating it's entries at various level are
>> all allocated from system memory with required alignments. If memory pages
>> with required size and alignment could not be allocated, then all depending
>> individual tests are skipped.
>>
>
> [...]
>
>>
>> Suggested-by: Catalin Marinas <catalin.marinas@arm.com>
>> Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
>> ---
>> arch/x86/include/asm/pgtable_64_types.h | 2 +
>> mm/Kconfig.debug | 14 +
>> mm/Makefile | 1 +
>> mm/arch_pgtable_test.c | 429 ++++++++++++++++++++++++
>> 4 files changed, 446 insertions(+)
>> create mode 100644 mm/arch_pgtable_test.c
>>
>> diff --git a/arch/x86/include/asm/pgtable_64_types.h b/arch/x86/include/asm/pgtable_64_types.h
>> index 52e5f5f2240d..b882792a3999 100644
>> --- a/arch/x86/include/asm/pgtable_64_types.h
>> +++ b/arch/x86/include/asm/pgtable_64_types.h
>> @@ -40,6 +40,8 @@ static inline bool pgtable_l5_enabled(void)
>> #define pgtable_l5_enabled() 0
>> #endif /* CONFIG_X86_5LEVEL */
>> +#define mm_p4d_folded(mm) (!pgtable_l5_enabled())
>> +
>
> This is specific to x86, should go in a separate patch.
Thought about it but its just a single line. Kirill suggested this in the
previous version. There is a generic fallback definition but s390 has it's
own. This change overrides the generic one for x86 probably as a fix or as
an improvement. Kirill should be able to help classify it in which case it
can be a separate patch.
>
>> extern unsigned int pgdir_shift;
>> extern unsigned int ptrs_per_p4d;
>> diff --git a/mm/Kconfig.debug b/mm/Kconfig.debug
>> index 327b3ebf23bf..ce9c397f7b07 100644
>> --- a/mm/Kconfig.debug
>> +++ b/mm/Kconfig.debug
>> @@ -117,3 +117,17 @@ config DEBUG_RODATA_TEST
>> depends on STRICT_KERNEL_RWX
>> ---help---
>> This option enables a testcase for the setting rodata read-only.
>> +
>> +config DEBUG_ARCH_PGTABLE_TEST
>> + bool "Test arch page table helpers for semantics compliance"
>> + depends on MMU
>> + depends on DEBUG_KERNEL
>> + help
>> + This options provides a kernel module which can be used to test
>> + architecture page table helper functions on various platform in
>> + verifying if they comply with expected generic MM semantics. This
>> + will help architectures code in making sure that any changes or
>> + new additions of these helpers will still conform to generic MM
>> + expected semantics.
>> +
>> + If unsure, say N.
>> diff --git a/mm/Makefile b/mm/Makefile
>> index d996846697ef..bb572c5aa8c5 100644
>> --- a/mm/Makefile
>> +++ b/mm/Makefile
>> @@ -86,6 +86,7 @@ obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o
>> obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o
>> obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
>> obj-$(CONFIG_DEBUG_RODATA_TEST) += rodata_test.o
>> +obj-$(CONFIG_DEBUG_ARCH_PGTABLE_TEST) += arch_pgtable_test.o
>> obj-$(CONFIG_PAGE_OWNER) += page_owner.o
>> obj-$(CONFIG_CLEANCACHE) += cleancache.o
>> obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
>> diff --git a/mm/arch_pgtable_test.c b/mm/arch_pgtable_test.c
>> new file mode 100644
>> index 000000000000..8b4a92756ad8
>> --- /dev/null
>> +++ b/mm/arch_pgtable_test.c
>> @@ -0,0 +1,429 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * This kernel module validates architecture page table helpers &
>> + * accessors and helps in verifying their continued compliance with
>> + * generic MM semantics.
>> + *
>> + * Copyright (C) 2019 ARM Ltd.
>> + *
>> + * Author: Anshuman Khandual <anshuman.khandual@arm.com>
>> + */
>> +#define pr_fmt(fmt) "arch_pgtable_test: %s " fmt, __func__
>> +
>> +#include <linux/gfp.h>
>> +#include <linux/hugetlb.h>
>> +#include <linux/kernel.h>
>> +#include <linux/mm.h>
>> +#include <linux/mman.h>
>> +#include <linux/mm_types.h>
>> +#include <linux/module.h>
>> +#include <linux/pfn_t.h>
>> +#include <linux/printk.h>
>> +#include <linux/random.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/swap.h>
>> +#include <linux/swapops.h>
>> +#include <linux/sched/mm.h>
>
> Add <linux/highmem.h> (see other mails, build failure on ppc book3s/32)
Okay.
>
>> +#include <asm/pgalloc.h>
>> +#include <asm/pgtable.h>
>> +
>> +/*
>> + * Basic operations
>> + *
>> + * mkold(entry) = An old and not a young entry
>> + * mkyoung(entry) = A young and not an old entry
>> + * mkdirty(entry) = A dirty and not a clean entry
>> + * mkclean(entry) = A clean and not a dirty entry
>> + * mkwrite(entry) = A write and not a write protected entry
>> + * wrprotect(entry) = A write protected and not a write entry
>> + * pxx_bad(entry) = A mapped and non-table entry
>> + * pxx_same(entry1, entry2) = Both entries hold the exact same value
>> + */
>> +#define VMFLAGS (VM_READ|VM_WRITE|VM_EXEC)
>> +
>> +/*
>> + * On s390 platform, the lower 12 bits are used to identify given page table
>> + * entry type and for other arch specific requirements. But these bits might
>> + * affect the ability to clear entries with pxx_clear(). So while loading up
>> + * the entries skip all lower 12 bits in order to accommodate s390 platform.
>> + * It does not have affect any other platform.
>> + */
>> +#define RANDOM_ORVALUE (0xfffffffffffff000UL)
>> +#define RANDOM_NZVALUE (0xff)
>> +
>> +static bool pud_aligned;
>> +static bool pmd_aligned;
>> +
>> +static void pte_basic_tests(struct page *page, pgprot_t prot)
>> +{
>> + pte_t pte = mk_pte(page, prot);
>> +
>> + WARN_ON(!pte_same(pte, pte));
>> + WARN_ON(!pte_young(pte_mkyoung(pte)));
>> + WARN_ON(!pte_dirty(pte_mkdirty(pte)));
>> + WARN_ON(!pte_write(pte_mkwrite(pte)));
>> + WARN_ON(pte_young(pte_mkold(pte)));
>> + WARN_ON(pte_dirty(pte_mkclean(pte)));
>> + WARN_ON(pte_write(pte_wrprotect(pte)));
>> +}
>> +
>> +#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE
>> +static void pmd_basic_tests(struct page *page, pgprot_t prot)
>> +{
>> + pmd_t pmd;
>> +
>> + /*
>> + * Memory block here must be PMD_SIZE aligned. Abort this
>> + * test in case we could not allocate such a memory block.
>> + */
>> + if (!pmd_aligned) {
>> + pr_warn("Could not proceed with PMD tests\n");
>> + return;
>> + }
>> +
>> + pmd = mk_pmd(page, prot);
>> + WARN_ON(!pmd_same(pmd, pmd));
>> + WARN_ON(!pmd_young(pmd_mkyoung(pmd)));
>> + WARN_ON(!pmd_dirty(pmd_mkdirty(pmd)));
>> + WARN_ON(!pmd_write(pmd_mkwrite(pmd)));
>> + WARN_ON(pmd_young(pmd_mkold(pmd)));
>> + WARN_ON(pmd_dirty(pmd_mkclean(pmd)));
>> + WARN_ON(pmd_write(pmd_wrprotect(pmd)));
>> + /*
>> + * A huge page does not point to next level page table
>> + * entry. Hence this must qualify as pmd_bad().
>> + */
>> + WARN_ON(!pmd_bad(pmd_mkhuge(pmd)));
>> +}
>> +#else
>> +static void pmd_basic_tests(struct page *page, pgprot_t prot) { }
>> +#endif
>> +
>> +#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
>> +static void pud_basic_tests(struct page *page, pgprot_t prot)
>> +{
>> + pud_t pud;
>> +
>> + /*
>> + * Memory block here must be PUD_SIZE aligned. Abort this
>> + * test in case we could not allocate such a memory block.
>> + */
>> + if (!pud_aligned) {
>> + pr_warn("Could not proceed with PUD tests\n");
>> + return;
>> + }
>> +
>> + pud = pfn_pud(page_to_pfn(page), prot);
>> + WARN_ON(!pud_same(pud, pud));
>> + WARN_ON(!pud_young(pud_mkyoung(pud)));
>> + WARN_ON(!pud_write(pud_mkwrite(pud)));
>> + WARN_ON(pud_write(pud_wrprotect(pud)));
>> + WARN_ON(pud_young(pud_mkold(pud)));
>> +
>> +#if !defined(__PAGETABLE_PMD_FOLDED) && !defined(__ARCH_HAS_4LEVEL_HACK)
>> + /*
>> + * A huge page does not point to next level page table
>> + * entry. Hence this must qualify as pud_bad().
>> + */
>> + WARN_ON(!pud_bad(pud_mkhuge(pud)));
>> +#endif
>> +}
>> +#else
>> +static void pud_basic_tests(struct page *page, pgprot_t prot) { }
>> +#endif
>> +
>> +static void p4d_basic_tests(struct page *page, pgprot_t prot)
>> +{
>> + p4d_t p4d;
>> +
>> + memset(&p4d, RANDOM_NZVALUE, sizeof(p4d_t));
>> + WARN_ON(!p4d_same(p4d, p4d));
>> +}
>> +
>> +static void pgd_basic_tests(struct page *page, pgprot_t prot)
>> +{
>> + pgd_t pgd;
>> +
>> + memset(&pgd, RANDOM_NZVALUE, sizeof(pgd_t));
>> + WARN_ON(!pgd_same(pgd, pgd));
>> +}
>> +
>> +#if !defined(__PAGETABLE_PMD_FOLDED) && !defined(__ARCH_HAS_4LEVEL_HACK)
>
> #ifdefs have to be avoided as much as possible, see below
Yeah but it has been bit difficult to avoid all these $ifdef because of the
availability (or lack of it) for all these pgtable helpers in various config
combinations on all platforms.
>
>> +static void pud_clear_tests(pud_t *pudp)
>> +{
>> + pud_t pud = READ_ONCE(*pudp);
> if (mm_pmd_folded() || __is_defined(__ARCH_HAS_4LEVEL_HACK))
> return;
>
>> +
>> + pud = __pud(pud_val(pud) | RANDOM_ORVALUE);
>> + WRITE_ONCE(*pudp, pud);
>> + pud_clear(pudp);
>> + pud = READ_ONCE(*pudp);
>> + WARN_ON(!pud_none(pud));
>> +}
>> +
>> +static void pud_populate_tests(struct mm_struct *mm, pud_t *pudp, pmd_t *pmdp)
>> +{
>> + pud_t pud;
>> +
> if (mm_pmd_folded() || __is_defined(__ARCH_HAS_4LEVEL_HACK))
> return;
>> + /*
>> + * This entry points to next level page table page.
>> + * Hence this must not qualify as pud_bad().
>> + */
>> + pmd_clear(pmdp);
>> + pud_clear(pudp);
>> + pud_populate(mm, pudp, pmdp);
>> + pud = READ_ONCE(*pudp);
>> + WARN_ON(pud_bad(pud));
>> +}
>> +#else
>
> Then the else branch goes away.
>
>> +static void pud_clear_tests(pud_t *pudp) { }
>> +static void pud_populate_tests(struct mm_struct *mm, pud_t *pudp, pmd_t *pmdp)
>> +{
>> +}
>> +#endif
>> +
>> +#if !defined(__PAGETABLE_PUD_FOLDED) && !defined(__ARCH_HAS_5LEVEL_HACK)
>
> The same can be done here.
IIRC not only the page table helpers but there are data types (pxx_t) which
were not present on various configs and these wrappers help prevent build
failures. Any ways will try and see if this can be improved further. But
meanwhile if you have some suggestions, please do let me know.
>
>> +static void p4d_clear_tests(p4d_t *p4dp)
>> +{
>> + p4d_t p4d = READ_ONCE(*p4dp);
>> +
>> + p4d = __p4d(p4d_val(p4d) | RANDOM_ORVALUE);
>> + WRITE_ONCE(*p4dp, p4d);
>> + p4d_clear(p4dp);
>> + p4d = READ_ONCE(*p4dp);
>> + WARN_ON(!p4d_none(p4d));
>> +}
>> +
>> +static void p4d_populate_tests(struct mm_struct *mm, p4d_t *p4dp, pud_t *pudp)
>> +{
>> + p4d_t p4d;
>> +
>> + /*
>> + * This entry points to next level page table page.
>> + * Hence this must not qualify as p4d_bad().
>> + */
>> + pud_clear(pudp);
>> + p4d_clear(p4dp);
>> + p4d_populate(mm, p4dp, pudp);
>> + p4d = READ_ONCE(*p4dp);
>> + WARN_ON(p4d_bad(p4d));
>> +}
>> +#else
>> +static void p4d_clear_tests(p4d_t *p4dp) { }
>> +static void p4d_populate_tests(struct mm_struct *mm, p4d_t *p4dp, pud_t *pudp)
>> +{
>> +}
>> +#endif
>> +
>> +#ifndef __ARCH_HAS_5LEVEL_HACK
>
> And the same here (you already did part of it with testing mm_p4d_folded(mm)
But it was not capturing all the build combinations which will break
otherwise e.g some configs on arm64 was failing to build.
>
>> +static void pgd_clear_tests(struct mm_struct *mm, pgd_t *pgdp)
>> +{
>> + pgd_t pgd = READ_ONCE(*pgdp);
>> +
>> + if (mm_p4d_folded(mm))
>> + return;
>> +
>> + pgd = __pgd(pgd_val(pgd) | RANDOM_ORVALUE);
>> + WRITE_ONCE(*pgdp, pgd);
>> + pgd_clear(pgdp);
>> + pgd = READ_ONCE(*pgdp);
>> + WARN_ON(!pgd_none(pgd));
>> +}
>> +
>> +static void pgd_populate_tests(struct mm_struct *mm, pgd_t *pgdp, p4d_t *p4dp)
>> +{
>> + pgd_t pgd;
>> +
>> + if (mm_p4d_folded(mm))
>> + return;
>> +
>> + /*
>> + * This entry points to next level page table page.
>> + * Hence this must not qualify as pgd_bad().
>> + */
>> + p4d_clear(p4dp);
>> + pgd_clear(pgdp);
>> + pgd_populate(mm, pgdp, p4dp);
>> + pgd = READ_ONCE(*pgdp);
>> + WARN_ON(pgd_bad(pgd));
>> +}
>> +#else
>> +static void pgd_clear_tests(struct mm_struct *mm, pgd_t *pgdp) { }
>> +static void pgd_populate_tests(struct mm_struct *mm, pgd_t *pgdp, p4d_t *p4dp)
>> +{
>> +}
>> +#endif
>> +
>> +static void pte_clear_tests(struct mm_struct *mm, pte_t *ptep)
>> +{
>> + pte_t pte = READ_ONCE(*ptep);
>> +
>> + pte = __pte(pte_val(pte) | RANDOM_ORVALUE);
>> + WRITE_ONCE(*ptep, pte);
>> + pte_clear(mm, 0, ptep);
>> + pte = READ_ONCE(*ptep);
>> + WARN_ON(!pte_none(pte));
>> +}
>> +
>> +static void pmd_clear_tests(pmd_t *pmdp)
>> +{
>> + pmd_t pmd = READ_ONCE(*pmdp);
>> +
>> + pmd = __pmd(pmd_val(pmd) | RANDOM_ORVALUE);
>> + WRITE_ONCE(*pmdp, pmd);
>> + pmd_clear(pmdp);
>> + pmd = READ_ONCE(*pmdp);
>> + WARN_ON(!pmd_none(pmd));
>> +}
>> +
>> +static void pmd_populate_tests(struct mm_struct *mm, pmd_t *pmdp,
>> + pgtable_t pgtable)
>> +{
>> + pmd_t pmd;
>> +
>> + /*
>> + * This entry points to next level page table page.
>> + * Hence this must not qualify as pmd_bad().
>> + */
>> + pmd_clear(pmdp);
>> + pmd_populate(mm, pmdp, pgtable);
>> + pmd = READ_ONCE(*pmdp);
>> + WARN_ON(pmd_bad(pmd));
>> +}
>> +
>> +static struct page *alloc_mapped_page(void)
>> +{
>> + struct page *page;
>> + gfp_t gfp_mask = GFP_KERNEL | __GFP_ZERO;
>> +
>> + page = alloc_gigantic_page_order(get_order(PUD_SIZE), gfp_mask,
>> + first_memory_node, &node_states[N_MEMORY]);
>> + if (page) {
>> + pud_aligned = true;
>> + pmd_aligned = true;
>> + return page;
>> + }
>> +
>> + page = alloc_pages(gfp_mask, get_order(PMD_SIZE));
>> + if (page) {
>> + pmd_aligned = true;
>> + return page;
>> + }
>> + return alloc_page(gfp_mask);
>> +}
>> +
>> +static void free_mapped_page(struct page *page)
>> +{
>> + if (pud_aligned) {
>> + unsigned long pfn = page_to_pfn(page);
>> +
>> + free_contig_range(pfn, 1ULL << get_order(PUD_SIZE));
>> + return;
>> + }
>> +
>> + if (pmd_aligned) {
>> + int order = get_order(PMD_SIZE);
>> +
>> + free_pages((unsigned long)page_address(page), order);
>> + return;
>> + }
>> + free_page((unsigned long)page_address(page));
>> +}
>> +
>> +static unsigned long get_random_vaddr(void)
>> +{
>> + unsigned long random_vaddr, random_pages, total_user_pages;
>> +
>> + total_user_pages = (TASK_SIZE - FIRST_USER_ADDRESS) / PAGE_SIZE;
>> +
>> + random_pages = get_random_long() % total_user_pages;
>> + random_vaddr = FIRST_USER_ADDRESS + random_pages * PAGE_SIZE;
>> +
>> + WARN_ON(random_vaddr > TASK_SIZE);
>> + WARN_ON(random_vaddr < FIRST_USER_ADDRESS);
>> + return random_vaddr;
>> +}
>> +
>> +static int __init arch_pgtable_tests_init(void)
>> +{
>> + struct mm_struct *mm;
>> + struct page *page;
>> + pgd_t *pgdp;
>> + p4d_t *p4dp, *saved_p4dp;
>> + pud_t *pudp, *saved_pudp;
>> + pmd_t *pmdp, *saved_pmdp, pmd;
>> + pte_t *ptep;
>> + pgtable_t saved_ptep;
>> + pgprot_t prot;
>> + unsigned long vaddr;
>> +
>> + prot = vm_get_page_prot(VMFLAGS);
>> + vaddr = get_random_vaddr();
>> + mm = mm_alloc();
>> + if (!mm) {
>> + pr_err("mm_struct allocation failed\n");
>> + return 1;
>> + }
>> +
>> + page = alloc_mapped_page();
>> + if (!page) {
>> + pr_err("memory allocation failed\n");
>> + return 1;
>> + }
>> +
>> + pgdp = pgd_offset(mm, vaddr);
>> + p4dp = p4d_alloc(mm, pgdp, vaddr);
>> + pudp = pud_alloc(mm, p4dp, vaddr);
>> + pmdp = pmd_alloc(mm, pudp, vaddr);
>> + ptep = pte_alloc_map(mm, pmdp, vaddr);
>> +
>> + /*
>> + * Save all the page table page addresses as the page table
>> + * entries will be used for testing with random or garbage
>> + * values. These saved addresses will be used for freeing
>> + * page table pages.
>> + */
>> + pmd = READ_ONCE(*pmdp);
>> + saved_p4dp = p4d_offset(pgdp, 0UL);
>> + saved_pudp = pud_offset(p4dp, 0UL);
>> + saved_pmdp = pmd_offset(pudp, 0UL);
>> + saved_ptep = pmd_pgtable(pmd);
>> +
>> + pte_basic_tests(page, prot);
>> + pmd_basic_tests(page, prot);
>> + pud_basic_tests(page, prot);
>> + p4d_basic_tests(page, prot);
>> + pgd_basic_tests(page, prot);
>> +
>> + pte_clear_tests(mm, ptep);
>> + pmd_clear_tests(pmdp);
>> + pud_clear_tests(pudp);
>> + p4d_clear_tests(p4dp);
>> + pgd_clear_tests(mm, pgdp);
>> +
>> + pmd_populate_tests(mm, pmdp, saved_ptep);
>> + pud_populate_tests(mm, pudp, saved_pmdp);
>> + p4d_populate_tests(mm, p4dp, saved_pudp);
>> + pgd_populate_tests(mm, pgdp, saved_p4dp);
>> +
>> + p4d_free(mm, saved_p4dp);
>> + pud_free(mm, saved_pudp);
>> + pmd_free(mm, saved_pmdp);
>> + pte_free(mm, saved_ptep);
>> +
>> + mm_dec_nr_puds(mm);
>> + mm_dec_nr_pmds(mm);
>> + mm_dec_nr_ptes(mm);
>> + __mmdrop(mm);
>> +
>> + free_mapped_page(page);
>> + return 0;
>
> Is there any benefit in keeping the module loaded once the tests are done ? Shouldn't the load fail instead ?
Will change this as late_init() sequence with all __init marked
functions as suggested by Kirill on the other thread.
>
>> +}
>> +
>> +static void __exit arch_pgtable_tests_exit(void) { }
>
> Is this function really needed ?
This will be gone as well.
>
>> +
>> +module_init(arch_pgtable_tests_init);
>> +module_exit(arch_pgtable_tests_exit);
>> +
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_AUTHOR("Anshuman Khandual <anshuman.khandual@arm.com>");
>> +MODULE_DESCRIPTION("Test architecture page table helpers");
>>
>
> Christophe
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 5/6] media: sun4i: Add H3 deinterlace driver
From: Maxime Ripard @ 2019-09-13 9:11 UTC (permalink / raw)
To: Jernej Škrabec
Cc: mark.rutland, devicetree, linux-sunxi, linux-kernel, hverkuil,
wens, robh+dt, mchehab, linux-arm-kernel, linux-media
In-Reply-To: <4613446.95M5L3lKvs@jernej-laptop>
[-- Attachment #1.1: Type: text/plain, Size: 417 bytes --]
Hi,
On Thu, Sep 12, 2019 at 10:43:28PM +0200, Jernej Škrabec wrote:
> Dne četrtek, 12. september 2019 ob 22:26:47 CEST je Maxime Ripard napisal(a):
> > > + clk_set_rate(dev->mod_clk, 300000000);
I just realized I missed this too. If you really need the rate to be
fixed, and if the controller cannot operate properly at any other
frequency, you probably want to use clk_set_rate_exclusive there.
Maxime
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
[-- Attachment #2: Type: text/plain, Size: 176 bytes --]
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH V2 2/2] mm/pgtable/debug: Add test validating architecture page table helpers
From: Kirill A. Shutemov @ 2019-09-13 9:13 UTC (permalink / raw)
To: Anshuman Khandual
Cc: Mark Rutland, linux-ia64, linux-sh, Peter Zijlstra, James Hogan,
Tetsuo Handa, Heiko Carstens, Michal Hocko, linux-mm,
Paul Mackerras, sparclinux, Thomas Gleixner, linux-s390,
Michael Ellerman, x86, Russell King - ARM Linux, Matthew Wilcox,
Steven Price, Jason Gunthorpe, Gerald Schaefer, linux-snps-arc,
linux-arm-kernel, Kees Cook, Masahiro Yamada, Mark Brown,
Dan Williams, Vlastimil Babka, Christophe Leroy,
Sri Krishna chowdary, Ard Biesheuvel, Greg Kroah-Hartman,
Dave Hansen, linux-mips, Ralf Baechle, linux-kernel, Paul Burton,
Mike Rapoport, Vineet Gupta, Martin Schwidefsky, Andrew Morton,
linuxppc-dev, David S. Miller
In-Reply-To: <502c497a-9bf1-7d2e-95f2-cfebcd9cf1d9@arm.com>
On Fri, Sep 13, 2019 at 02:32:04PM +0530, Anshuman Khandual wrote:
>
> On 09/12/2019 10:44 PM, Christophe Leroy wrote:
> >
> >
> > Le 12/09/2019 à 08:02, Anshuman Khandual a écrit :
> >> This adds a test module which will validate architecture page table helpers
> >> and accessors regarding compliance with generic MM semantics expectations.
> >> This will help various architectures in validating changes to the existing
> >> page table helpers or addition of new ones.
> >>
> >> Test page table and memory pages creating it's entries at various level are
> >> all allocated from system memory with required alignments. If memory pages
> >> with required size and alignment could not be allocated, then all depending
> >> individual tests are skipped.
> >>
> >
> > [...]
> >
> >>
> >> Suggested-by: Catalin Marinas <catalin.marinas@arm.com>
> >> Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
> >> ---
> >> arch/x86/include/asm/pgtable_64_types.h | 2 +
> >> mm/Kconfig.debug | 14 +
> >> mm/Makefile | 1 +
> >> mm/arch_pgtable_test.c | 429 ++++++++++++++++++++++++
> >> 4 files changed, 446 insertions(+)
> >> create mode 100644 mm/arch_pgtable_test.c
> >>
> >> diff --git a/arch/x86/include/asm/pgtable_64_types.h b/arch/x86/include/asm/pgtable_64_types.h
> >> index 52e5f5f2240d..b882792a3999 100644
> >> --- a/arch/x86/include/asm/pgtable_64_types.h
> >> +++ b/arch/x86/include/asm/pgtable_64_types.h
> >> @@ -40,6 +40,8 @@ static inline bool pgtable_l5_enabled(void)
> >> #define pgtable_l5_enabled() 0
> >> #endif /* CONFIG_X86_5LEVEL */
> >> +#define mm_p4d_folded(mm) (!pgtable_l5_enabled())
> >> +
> >
> > This is specific to x86, should go in a separate patch.
>
> Thought about it but its just a single line. Kirill suggested this in the
> previous version. There is a generic fallback definition but s390 has it's
> own. This change overrides the generic one for x86 probably as a fix or as
> an improvement. Kirill should be able to help classify it in which case it
> can be a separate patch.
I don't think it worth a separate patch.
--
Kirill A. Shutemov
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v5 1/3] coresight: etm4x: save/restore state across CPU low power states
From: Andrew Murray @ 2019-09-13 9:20 UTC (permalink / raw)
To: Suzuki K Poulose
Cc: Al.Grant, mathieu.poirier, alexander.shishkin, coresight, leo.yan,
sudeep.holla, linux-arm-kernel, mike.leach
In-Reply-To: <3e3f7e86-c62b-e9d6-9d42-f4897b47847d@arm.com>
On Thu, Sep 12, 2019 at 03:03:44PM +0100, Suzuki K Poulose wrote:
> Hi Andrew,
>
> On 08/16/2019 04:46 PM, Andrew Murray wrote:
> > Some hardware will ignore bit TRCPDCR.PU which is used to signal
> > to hardware that power should not be removed from the trace unit.
> > Let's mitigate against this by conditionally saving and restoring
> > the trace unit state when the CPU enters low power states.
> >
> > This patchset introduces a firmware property named
> > 'arm,coresight-loses-context-with-cpu' - when this is present the
> > hardware state will be conditionally saved and restored.
> >
> > A module parameter 'pm_save_enable' is also introduced which can
> > be configured to override the firmware property. This can be set
> > to never allow save/restore or to conditionally allow it (only for
> > self-hosted). The default value is determined by firmware.
> >
> > We avoid saving the hardware state when self-hosted coresight isn't
> > in use to reduce PM latency - we can't determine this by reading the
> > claim tags (TRCCLAIMCLR) as these are 'trace' registers which need
> > power and clocking, something we can't easily provide in the PM
> > context. Therefore we rely on the existing drvdata->mode internal
> > state that is set when self-hosted coresight is used (and powered).
>
> The patch looks good to me. Some very minor comments below.
>
> >
> > Signed-off-by: Andrew Murray <andrew.murray@arm.com>
> > ---
> > drivers/hwtracing/coresight/coresight-etm4x.c | 318 ++++++++++++++++++
> > drivers/hwtracing/coresight/coresight-etm4x.h | 64 ++++
> > drivers/hwtracing/coresight/coresight.c | 6 +
> > include/linux/coresight.h | 6 +
> > 4 files changed, 394 insertions(+)
> >
>
>
> > +static void etm4_cpu_restore(struct etmv4_drvdata *drvdata)
> > +{
> > + int i;
> > + struct etmv4_save_state *state = drvdata->save_state;
> > +
> > + CS_UNLOCK(drvdata->base);
> > +
> > + writel_relaxed(state->trcclaimset, drvdata->base + TRCCLAIMSET);
> > +
> > + writel_relaxed(state->trcprgctlr, drvdata->base + TRCPRGCTLR);
> > + writel_relaxed(state->trcprocselr, drvdata->base + TRCPROCSELR);
> > + writel_relaxed(state->trcconfigr, drvdata->base + TRCCONFIGR);
> > + writel_relaxed(state->trcauxctlr, drvdata->base + TRCAUXCTLR);
> > + writel_relaxed(state->trceventctl0r, drvdata->base + TRCEVENTCTL0R);
> > + writel_relaxed(state->trceventctl1r, drvdata->base + TRCEVENTCTL1R);
> > + writel_relaxed(state->trcstallctlr, drvdata->base + TRCSTALLCTLR);
> > + writel_relaxed(state->trctsctlr, drvdata->base + TRCTSCTLR);
> > + writel_relaxed(state->trcsyncpr, drvdata->base + TRCSYNCPR);
> > + writel_relaxed(state->trcccctlr, drvdata->base + TRCCCCTLR);
> > + writel_relaxed(state->trcbbctlr, drvdata->base + TRCBBCTLR);
> > + writel_relaxed(state->trctraceidr, drvdata->base + TRCTRACEIDR);
> > + writel_relaxed(state->trcqctlr, drvdata->base + TRCQCTLR);
> > +
> > + writel_relaxed(state->trcvictlr, drvdata->base + TRCVICTLR);
> > + writel_relaxed(state->trcviiectlr, drvdata->base + TRCVIIECTLR);
> > + writel_relaxed(state->trcvissctlr, drvdata->base + TRCVISSCTLR);
> > + writel_relaxed(state->trcvipcssctlr, drvdata->base + TRCVIPCSSCTLR);
> > + writel_relaxed(state->trcvdctlr, drvdata->base + TRCVDCTLR);
> > + writel_relaxed(state->trcvdsacctlr, drvdata->base + TRCVDSACCTLR);
> > + writel_relaxed(state->trcvdarcctlr, drvdata->base + TRCVDARCCTLR);
> > +
> > + for (i = 0; i < drvdata->nrseqstate; i++)
> > + writel_relaxed(state->trcseqevr[i],
> > + drvdata->base + TRCSEQEVRn(i));
>
> minor nit: alignment issues here and below for the multi-line
> write_relaxed() invocations.
> ...
OK.
>
>
> > +static int etm4_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd,
> > + void *v)
> > +{
> > + struct etmv4_drvdata *drvdata;
> > + unsigned int cpu = smp_processor_id();
> > +
> > + if (!etmdrvdata[cpu])
> > + return 0;
>
>
> Please could we be consistent with the return value. i.e, use something
> in line with NOTIFY_*. NOTIFY_OK ?
Yes I think NOTIFY_OK is the correct thing to do here.
> With the above fixed:
>
> Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Thanks for the review,
Andrew Murray
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v4 2/6] thermal: amlogic: Add thermal driver to support G12 SoCs
From: Amit Kucheria @ 2019-09-13 9:21 UTC (permalink / raw)
To: Guillaume La Roque
Cc: open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Linux PM list, Daniel Lezcano, LKML, Eduardo Valentin,
linux-amlogic, Zhang Rui, lakml
In-Reply-To: <20190821222421.30242-3-glaroque@baylibre.com>
On Thu, Aug 22, 2019 at 3:54 AM Guillaume La Roque
<glaroque@baylibre.com> wrote:
>
> Amlogic G12A and G12B SoCs integrate two thermal sensors
> with the same design.
> One is located close to the DDR controller and the other one is
> located close to the PLLs (between the CPU and GPU).
Merge into one paragraph?
> The calibration data for each of the thermal sensors instance is
> stored in a different location within the AO region.
>
> Implement reading the temperature from each thermal sensor.
>
> The IP block has more functionality, which may be added to this driver
> in the future:
> - chip reset when the temperature exceeds a configurable threshold
> - up to four interrupts when the temperature has risen above a
> configurable threshold
> - up to four interrupts when the temperature has fallen below a
> configurable threshold
>
> Signed-off-by: Guillaume La Roque <glaroque@baylibre.com>
> ---
> drivers/thermal/Kconfig | 11 +
> drivers/thermal/Makefile | 1 +
> drivers/thermal/amlogic_thermal.c | 335 ++++++++++++++++++++++++++++++
> 3 files changed, 347 insertions(+)
> create mode 100644 drivers/thermal/amlogic_thermal.c
>
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index 9966364a6deb..0f31bb4bc372 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -348,6 +348,17 @@ config MTK_THERMAL
> Enable this option if you want to have support for thermal management
> controller present in Mediatek SoCs
>
> +config AMLOGIC_THERMAL
> + tristate "Amlogic Thermal Support"
> + default ARCH_MESON
> + depends on OF && ARCH_MESON
> + help
> + If you say yes here you get support for Amlogic Thermal
> + for G12 SoC Family.
> +
> + This driver can also be built as a module. If so, the module will
> + be called amlogic_thermal.
> +
> menu "Intel thermal drivers"
> depends on X86 || X86_INTEL_QUARK || COMPILE_TEST
> source "drivers/thermal/intel/Kconfig"
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> index 74a37c7f847a..baeb70bf0568 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -54,3 +54,4 @@ obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o
> obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o
> obj-$(CONFIG_ZX2967_THERMAL) += zx2967_thermal.o
> obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o
> +obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o
> diff --git a/drivers/thermal/amlogic_thermal.c b/drivers/thermal/amlogic_thermal.c
> new file mode 100644
> index 000000000000..173d808048d2
> --- /dev/null
> +++ b/drivers/thermal/amlogic_thermal.c
> @@ -0,0 +1,335 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Amlogic Thermal Sensor Driver
> + *
> + * Copyright (C) 2017 Huan Biao <huan.biao@amlogic.com>
> + * Copyright (C) 2019 Guillaume La Roque <glaroque@baylibre.com>
> + *
> + * Register value to celsius temperature formulas:
> + * Read_Val m * U
> + * U = ---------, Uptat = ---------
> + * 2^16 1 + n * U
> + *
> + * Temperature = A * ( Uptat + u_efuse / 2^16 )- B
> + *
> + * A B m n : calibration parameters
> + * u_efuse : fused calibration value, it's a signed 16 bits value
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/thermal.h>
> +
> +#include "thermal_core.h"
> +
> +#define TSENSOR_CFG_REG1 0x4
> + #define TSENSOR_CFG_REG1_RSET_VBG BIT(12)
> + #define TSENSOR_CFG_REG1_RSET_ADC BIT(11)
> + #define TSENSOR_CFG_REG1_VCM_EN BIT(10)
> + #define TSENSOR_CFG_REG1_VBG_EN BIT(9)
> + #define TSENSOR_CFG_REG1_OUT_CTL BIT(6)
> + #define TSENSOR_CFG_REG1_FILTER_EN BIT(5)
> + #define TSENSOR_CFG_REG1_DEM_EN BIT(3)
> + #define TSENSOR_CFG_REG1_CH_SEL GENMASK(1, 0)
> + #define TSENSOR_CFG_REG1_ENABLE \
> + (TSENSOR_CFG_REG1_FILTER_EN | \
> + TSENSOR_CFG_REG1_VCM_EN | \
> + TSENSOR_CFG_REG1_VBG_EN | \
> + TSENSOR_CFG_REG1_DEM_EN | \
> + TSENSOR_CFG_REG1_CH_SEL)
> +
> +#define TSENSOR_STAT0 0x40
> +
> +#define TSENSOR_STAT9 0x64
> +
> +#define TSENSOR_READ_TEMP_MASK GENMASK(15, 0)
> +#define TSENSOR_TEMP_MASK GENMASK(11, 0)
> +
> +#define TSENSOR_TRIM_SIGN_MASK BIT(15)
> +#define TSENSOR_TRIM_TEMP_MASK GENMASK(14, 0)
> +#define TSENSOR_TRIM_VERSION_MASK GENMASK(31, 24)
> +
> +#define TSENSOR_TRIM_VERSION(_version) \
> + FIELD_GET(TSENSOR_TRIM_VERSION_MASK, _version)
> +
> +#define TSENSOR_TRIM_CALIB_VALID_MASK (GENMASK(3, 2) | BIT(7))
> +
> +#define TSENSOR_CALIB_OFFSET 1
> +#define TSENSOR_CALIB_SHIFT 4
> +
> +/**
> + * struct amlogic_thermal_soc_calib_data
> + * @A, B, m, n: calibration parameters
> + * This structure is required for configuration of amlogic thermal driver.
> + */
> +struct amlogic_thermal_soc_calib_data {
> + int A;
> + int B;
> + int m;
> + int n;
> +};
> +
> +/**
> + * struct amlogic_thermal_data
> + * @u_efuse_off: register offset to read fused calibration value
> + * @soc: calibration parameters structure pointer
This doesn't match the member below.
> + * @regmap_config: regmap config for the device
> + * This structure is required for configuration of amlogic thermal driver.
> + */
> +struct amlogic_thermal_data {
> + int u_efuse_off;
> + const struct amlogic_thermal_soc_calib_data *calibration_parameters;
> + const struct regmap_config *regmap_config;
> +};
> +
> +struct amlogic_thermal {
> + struct platform_device *pdev;
> + const struct amlogic_thermal_data *data;
> + struct regmap *regmap;
> + struct regmap *sec_ao_map;
> + struct clk *clk;
> + struct thermal_zone_device *tzd;
> + u32 trim_info;
> +};
> +
> +/*
> + * Calculate a temperature value from a temperature code.
> + * The unit of the temperature is degree milliCelsius.
> + */
> +static int amlogic_thermal_code_to_millicelsius(struct amlogic_thermal *pdata,
> + int temp_code)
> +{
> + const struct amlogic_thermal_soc_calib_data *param =
> + pdata->data->calibration_parameters;
> + int temp;
> + s64 factor, Uptat, uefuse;
> +
> + uefuse = pdata->trim_info & TSENSOR_TRIM_SIGN_MASK ?
> + ~(pdata->trim_info & TSENSOR_TRIM_TEMP_MASK) + 1 :
> + (pdata->trim_info & TSENSOR_TRIM_TEMP_MASK);
> +
> + factor = param->n * temp_code;
> + factor = div_s64(factor, 100);
> +
> + Uptat = temp_code * param->m;
> + Uptat = div_s64(Uptat, 100);
> + Uptat = Uptat * BIT(16);
> + Uptat = div_s64(Uptat, BIT(16) + factor);
> +
> + temp = (Uptat + uefuse) * param->A;
> + temp = div_s64(temp, BIT(16));
> + temp = (temp - param->B) * 100;
> +
> + return temp;
> +}
> +
> +static int amlogic_thermal_initialize(struct amlogic_thermal *pdata)
> +{
> + int ret = 0;
> + int ver;
> +
> + regmap_read(pdata->sec_ao_map, pdata->data->u_efuse_off,
> + &pdata->trim_info);
> +
> + ver = TSENSOR_TRIM_VERSION(pdata->trim_info);
> +
> + if ((ver & TSENSOR_TRIM_CALIB_VALID_MASK) == 0) {
> + ret = -EINVAL;
> + dev_err(&pdata->pdev->dev,
> + "tsensor thermal calibration not supported: 0x%x!\n",
> + ver);
> + }
> +
> + return ret;
> +}
> +
> +static int amlogic_thermal_enable(struct amlogic_thermal *data)
> +{
> + int ret;
> +
> + ret = clk_prepare_enable(data->clk);
> + if (ret)
> + return ret;
> +
> + regmap_update_bits(data->regmap, TSENSOR_CFG_REG1,
> + TSENSOR_CFG_REG1_ENABLE, TSENSOR_CFG_REG1_ENABLE);
> +
> + return 0;
> +}
> +
> +static int amlogic_thermal_disable(struct amlogic_thermal *data)
> +{
> + regmap_update_bits(data->regmap, TSENSOR_CFG_REG1,
> + TSENSOR_CFG_REG1_ENABLE, 0);
> + clk_disable_unprepare(data->clk);
> +
> + return 0;
> +}
> +
> +static int amlogic_thermal_get_temp(void *data, int *temp)
> +{
> + unsigned int tval;
> + struct amlogic_thermal *pdata = data;
> +
> + if (!data)
> + return -EINVAL;
> +
> + regmap_read(pdata->regmap, TSENSOR_STAT0, &tval);
> + *temp =
> + amlogic_thermal_code_to_millicelsius(pdata,
> + tval & TSENSOR_READ_TEMP_MASK);
> +
> + return 0;
> +}
> +
> +static const struct thermal_zone_of_device_ops amlogic_thermal_ops = {
> + .get_temp = amlogic_thermal_get_temp,
> +};
> +
> +static const struct regmap_config amlogic_thermal_regmap_config_g12a = {
> + .reg_bits = 8,
> + .val_bits = 32,
> + .reg_stride = 4,
> + .max_register = TSENSOR_STAT9,
> +};
> +
> +static const struct amlogic_thermal_soc_calib_data amlogic_thermal_g12a = {
> + .A = 9411,
> + .B = 3159,
> + .m = 424,
> + .n = 324,
> +};
> +
> +static const struct amlogic_thermal_data amlogic_thermal_g12a_cpu_param = {
> + .u_efuse_off = 0x128,
> + .calibration_parameters = &amlogic_thermal_g12a,
> + .regmap_config = &amlogic_thermal_regmap_config_g12a,
> +};
> +
> +static const struct amlogic_thermal_data amlogic_thermal_g12a_ddr_param = {
> + .u_efuse_off = 0xf0,
> + .calibration_parameters = &amlogic_thermal_g12a,
> + .regmap_config = &amlogic_thermal_regmap_config_g12a,
> +};
> +
> +static const struct of_device_id of_amlogic_thermal_match[] = {
> + {
> + .compatible = "amlogic,g12a-ddr-thermal",
> + .data = &amlogic_thermal_g12a_ddr_param,
> + },
> + {
> + .compatible = "amlogic,g12a-cpu-thermal",
> + .data = &amlogic_thermal_g12a_cpu_param,
> + },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, of_amlogic_thermal_match);
> +
> +static int amlogic_thermal_probe(struct platform_device *pdev)
> +{
> + struct amlogic_thermal *pdata;
> + struct device *dev = &pdev->dev;
> + struct resource *res;
> + void __iomem *base;
> + int ret;
> +
> + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
> + if (!pdata)
> + return -ENOMEM;
> +
> + pdata->data = of_device_get_match_data(dev);
> + pdata->pdev = pdev;
> + platform_set_drvdata(pdev, pdata);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Use devm_platform_ioremap_resource instead.
> + base = devm_ioremap_resource(dev, res);
> + if (IS_ERR(base)) {
> + dev_err(dev, "failed to get io address\n");
> + return PTR_ERR(base);
> + }
> +
> + pdata->regmap = devm_regmap_init_mmio(dev, base,
> + pdata->data->regmap_config);
> + if (IS_ERR(pdata->regmap))
> + return PTR_ERR(pdata->regmap);
> +
> + pdata->clk = devm_clk_get(dev, NULL);
> + if (IS_ERR(pdata->clk)) {
> + if (PTR_ERR(pdata->clk) != -EPROBE_DEFER)
> + dev_err(dev, "failed to get clock\n");
> + return PTR_ERR(pdata->clk);
> + }
> +
> + pdata->sec_ao_map = syscon_regmap_lookup_by_phandle
> + (pdev->dev.of_node, "amlogic,ao-secure");
> + if (IS_ERR(pdata->sec_ao_map)) {
> + dev_err(dev, "syscon regmap lookup failed.\n");
> + return PTR_ERR(pdata->sec_ao_map);
> + }
> +
> + pdata->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev,
> + 0,
> + pdata,
> + &amlogic_thermal_ops);
> + if (IS_ERR(pdata->tzd)) {
> + ret = PTR_ERR(pdata->tzd);
> + dev_err(dev, "Failed to register tsensor: %d\n", ret);
> + return PTR_ERR(pdata->tzd);
return ret;
> + }
> +
> + ret = amlogic_thermal_initialize(pdata);
> + if (ret)
> + return ret;
> +
> + ret = amlogic_thermal_enable(pdata);
> +
> + return ret;
> +}
> +
> +static int amlogic_thermal_remove(struct platform_device *pdev)
> +{
> + struct amlogic_thermal *data = platform_get_drvdata(pdev);
> +
> + return amlogic_thermal_disable(data);
> +}
> +
> +static int __maybe_unused amlogic_thermal_suspend(struct device *dev)
> +{
> + struct amlogic_thermal *data = dev_get_drvdata(dev);
> +
> + return amlogic_thermal_disable(data);
> +}
> +
> +static int __maybe_unused amlogic_thermal_resume(struct device *dev)
> +{
> + struct amlogic_thermal *data = dev_get_drvdata(dev);
> +
> + return amlogic_thermal_enable(data);
> +}
> +
> +static SIMPLE_DEV_PM_OPS(amlogic_thermal_pm_ops,
> + amlogic_thermal_suspend, amlogic_thermal_resume);
> +
> +static struct platform_driver amlogic_thermal_driver = {
> + .driver = {
> + .name = "amlogic_thermal",
> + .pm = &amlogic_thermal_pm_ops,
> + .of_match_table = of_amlogic_thermal_match,
> + },
> + .probe = amlogic_thermal_probe,
> + .remove = amlogic_thermal_remove,
> +};
> +
> +module_platform_driver(amlogic_thermal_driver);
> +
> +MODULE_AUTHOR("Guillaume La Roque <glaroque@baylibre.com>");
> +MODULE_DESCRIPTION("Amlogic thermal driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.17.1
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v5 2/3] dt-bindings: arm: coresight: Add support for coresight-loses-context-with-cpu
From: Andrew Murray @ 2019-09-13 9:22 UTC (permalink / raw)
To: Mathieu Poirier
Cc: Al.Grant, Suzuki K Poulose, Alexander Shishkin, coresight,
Sudeep Holla, Leo Yan, linux-arm-kernel, Mike Leach
In-Reply-To: <20190820215930.GB12159@xps15>
On Tue, Aug 20, 2019 at 03:59:30PM -0600, Mathieu Poirier wrote:
> On Fri, Aug 16, 2019 at 04:46:14PM +0100, Andrew Murray wrote:
> > Some coresight components, because of choices made during hardware
> > integration, require their state to be saved and restored across CPU low
> > power states.
> >
> > The software has no reliable method of detecting when save/restore is
> > required thus let's add a binding to inform the kernel.
> >
> > Signed-off-by: Andrew Murray <andrew.murray@arm.com>
> > ---
> > Documentation/devicetree/bindings/arm/coresight.txt | 9 +++++++++
> > 1 file changed, 9 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt
> > index fcc3bacfd8bc..d02c42d21f2f 100644
> > --- a/Documentation/devicetree/bindings/arm/coresight.txt
> > +++ b/Documentation/devicetree/bindings/arm/coresight.txt
> > @@ -87,6 +87,15 @@ its hardware characteristcs.
> >
> > * port or ports: see "Graph bindings for Coresight" below.
> >
> > +* Optional properties for all components:
> > +
> > + * arm,coresight-loses-context-with-cpu : boolean. Indicates that the
> > + hardware will lose register context on CPU power down (e.g. CPUIdle).
> > + An example of where this may be needed are systems which contain a
> > + coresight component and CPU in the same power domain. When the CPU
> > + powers down the coresight component also powers down and loses its
> > + context. This property is currently only used for the ETM 4.x driver.
> > +
>
> Reviewed-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>
> When you resend this set make sure to include the device tree mailing list as
> instructed by get_maintainer.pl. Since this set did not CC the DT list, none of
> the maintainers over there will look at your patches.
>
Sure I'll do that.
Thanks,
Andrew Murray
>
> > * Optional properties for ETM/PTMs:
> >
> > * arm,cp14: must be present if the system accesses ETM/PTM management
> > --
> > 2.21.0
> >
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v5 0/4] Raspberry Pi 4 DMA addressing support
From: Matthias Brugger @ 2019-09-13 9:25 UTC (permalink / raw)
To: Stefan Wahren, catalin.marinas, marc.zyngier, Matthias Brugger,
robh+dt, linux-mm, linux-arm-kernel, linux-riscv, hch,
Nicolas Saenz Julienne
Cc: f.fainelli, robin.murphy, linux-kernel, linux-rpi-kernel, phill,
will, m.szyprowski
In-Reply-To: <48a6b72d-d554-b563-5ed6-9a79db5fb4ab@gmx.net>
On 13/09/2019 10:50, Stefan Wahren wrote:
> Am 13.09.19 um 10:09 schrieb Matthias Brugger:
>>
>> On 12/09/2019 21:32, Stefan Wahren wrote:
>>> Am 12.09.19 um 19:18 schrieb Matthias Brugger:
>>>> On 10/09/2019 11:27, Matthias Brugger wrote:
>>>>> On 09/09/2019 21:33, Stefan Wahren wrote:
>>>>>> Hi Nicolas,
>>>>>>
>>>>>> Am 09.09.19 um 11:58 schrieb Nicolas Saenz Julienne:
>>>>>>> Hi all,
>>>>>>> this series attempts to address some issues we found while bringing up
>>>>>>> the new Raspberry Pi 4 in arm64 and it's intended to serve as a follow
>>>>>>> up of these discussions:
>>>>>>> v4: https://lkml.org/lkml/2019/9/6/352
>>>>>>> v3: https://lkml.org/lkml/2019/9/2/589
>>>>>>> v2: https://lkml.org/lkml/2019/8/20/767
>>>>>>> v1: https://lkml.org/lkml/2019/7/31/922
>>>>>>> RFC: https://lkml.org/lkml/2019/7/17/476
>>>>>>>
>>>>>>> The new Raspberry Pi 4 has up to 4GB of memory but most peripherals can
>>>>>>> only address the first GB: their DMA address range is
>>>>>>> 0xc0000000-0xfc000000 which is aliased to the first GB of physical
>>>>>>> memory 0x00000000-0x3c000000. Note that only some peripherals have these
>>>>>>> limitations: the PCIe, V3D, GENET, and 40-bit DMA channels have a wider
>>>>>>> view of the address space by virtue of being hooked up trough a second
>>>>>>> interconnect.
>>>>>>>
>>>>>>> Part of this is solved on arm32 by setting up the machine specific
>>>>>>> '.dma_zone_size = SZ_1G', which takes care of reserving the coherent
>>>>>>> memory area at the right spot. That said no buffer bouncing (needed for
>>>>>>> dma streaming) is available at the moment, but that's a story for
>>>>>>> another series.
>>>>>>>
>>>>>>> Unfortunately there is no such thing as 'dma_zone_size' in arm64. Only
>>>>>>> ZONE_DMA32 is created which is interpreted by dma-direct and the arm64
>>>>>>> arch code as if all peripherals where be able to address the first 4GB
>>>>>>> of memory.
>>>>>>>
>>>>>>> In the light of this, the series implements the following changes:
>>>>>>>
>>>>>>> - Create both DMA zones in arm64, ZONE_DMA will contain the first 1G
>>>>>>> area and ZONE_DMA32 the rest of the 32 bit addressable memory. So far
>>>>>>> the RPi4 is the only arm64 device with such DMA addressing limitations
>>>>>>> so this hardcoded solution was deemed preferable.
>>>>>>>
>>>>>>> - Properly set ARCH_ZONE_DMA_BITS.
>>>>>>>
>>>>>>> - Reserve the CMA area in a place suitable for all peripherals.
>>>>>>>
>>>>>>> This series has been tested on multiple devices both by checking the
>>>>>>> zones setup matches the expectations and by double-checking physical
>>>>>>> addresses on pages allocated on the three relevant areas GFP_DMA,
>>>>>>> GFP_DMA32, GFP_KERNEL:
>>>>>>>
>>>>>>> - On an RPi4 with variations on the ram memory size. But also forcing
>>>>>>> the situation where all three memory zones are nonempty by setting a 3G
>>>>>>> ZONE_DMA32 ceiling on a 4G setup. Both with and without NUMA support.
>>>>>>>
>>>>>> i like to test this series on Raspberry Pi 4 and i have some questions
>>>>>> to get arm64 running:
>>>>>>
>>>>>> Do you use U-Boot? Which tree?
>>>>> If you want to use U-Boot, try v2019.10-rc4, it should have everything you need
>>>>> to boot your kernel.
>>>>>
>>>> Ok, here is a thing. In the linux kernel we now use bcm2711 as SoC name, but the
>>>> RPi4 devicetree provided by the FW uses mostly bcm2838.
>>> Do you mean the DTB provided at runtime?
>>>
>>> You mean the merged U-Boot changes, doesn't work with my Raspberry Pi
>>> series?
>>>
>>>> U-Boot in its default
>>>> config uses the devicetree provided by the FW, mostly because this way you don't
>>>> have to do anything to find out how many RAM you really have. Secondly because
>>>> this will allow us, in the near future, to have one U-boot binary for both RPi3
>>>> and RPi4 (and as a side effect one binary for RPi1 and RPi2).
>>>>
>>>> Anyway, I found at least, that the following compatibles need to be added:
>>>>
>>>> "brcm,bcm2838-cprman"
>>>> "brcm,bcm2838-gpio"
>>>>
>>>> Without at least the cprman driver update, you won't see anything.
>>>>
>>>> "brcm,bcm2838-rng200" is also a candidate.
>>>>
>>>> I also suppose we will need to add "brcm,bcm2838" to
>>>> arch/arm/mach-bcm/bcm2711.c, but I haven't verified this.
>>> How about changing this in the downstream kernel? Which is much easier.
>> I'm not sure I understand what you want to say. My goal is to use the upstream
>> kernel with the device tree blob provided by the FW.
>
> The device tree blob you are talking is defined in this repository:
>
> https://github.com/raspberrypi/linux
>
> So the word FW is misleading to me.
>
No, it's part of
https://github.com/raspberrypi/firmware.git
file boot/bcm2711-rpi-4-b.dtb
>> If you talk about the
>> downstream kernel, I suppose you mean we should change this in the FW DT blob
>> and in the downstream kernel. That would work for me.
>>
>> Did I understand you correctly?
>
> Yes
>
> So i suggest to add the upstream compatibles into the repo mentioned above.
>
> Sorry, but in case you decided as a U-Boot developer to be compatible
> with a unreviewed DT, we also need to make U-Boot compatible with
> upstream and downstream DT blobs.
>
Well RPi3 is working with the DT blob provided by the FW, as I mentioned earlier
if we can use this DTB we can work towards one binary that can boot both RPi3
and RPi4. On the other hand we can rely on the FW to detect the amount of memory
our RPi4 has.
That said, I agree that we should make sure that U-Boot can boot with both DTBs,
the upstream one and the downstream. Now the question is how to get to this. I'm
a bit puzzled that by talking about "unreviewed DT" you insinuate that bcm2711
compatible is already reviewed and can't be changed. From what I can see none of
these compatibles got merged for now, so we are still at time to change them.
Apart from the point Florian made, to stay consistent with the RPi SoC naming,
it will save us work, both in the kernel and in U-Boot, as we would need to add
both compatibles to the code-base.
Regards,
Matthias
>>
>>>> Regards,
>>>> Matthias
>>>>
>>>>> Regards,
>>>>> Matthias
>>>>>
>>>>>> Are there any config.txt tweaks necessary?
>>>>>>
>>>>>>
>>>>> _______________________________________________
>>>>> linux-arm-kernel mailing list
>>>>> linux-arm-kernel@lists.infradead.org
>>>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>>>>
>>>> _______________________________________________
>>>> linux-arm-kernel mailing list
>>>> linux-arm-kernel@lists.infradead.org
>>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>>
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v5 2/2] drm/bridge: Add NWL MIPI DSI host controller support
From: Andrzej Hajda @ 2019-09-13 9:31 UTC (permalink / raw)
To: Guido Günther, David Airlie, Daniel Vetter, Rob Herring,
Mark Rutland, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, NXP Linux Team, Neil Armstrong, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Lee Jones, dri-devel, devicetree,
linux-arm-kernel, linux-kernel, Robert Chiras, Sam Ravnborg,
Arnd Bergmann
In-Reply-To: <3ce1891ea41249bf4a9985e2cee8640fb36de42e.1567995854.git.agx@sigxcpu.org>
On 09.09.2019 04:25, Guido Günther wrote:
> This adds initial support for the NWL MIPI DSI Host controller found on
> i.MX8 SoCs.
>
> It adds support for the i.MX8MQ but the same IP can be found on
> e.g. the i.MX8QXP.
>
> It has been tested on the Librem 5 devkit using mxsfb.
>
> Signed-off-by: Guido Günther <agx@sigxcpu.org>
> Co-developed-by: Robert Chiras <robert.chiras@nxp.com>
> Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
> Tested-by: Robert Chiras <robert.chiras@nxp.com>
> ---
> drivers/gpu/drm/bridge/Kconfig | 2 +
> drivers/gpu/drm/bridge/Makefile | 1 +
> drivers/gpu/drm/bridge/nwl-dsi/Kconfig | 16 +
> drivers/gpu/drm/bridge/nwl-dsi/Makefile | 4 +
> drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c | 499 ++++++++++++++++
> drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h | 65 +++
> drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c | 696 +++++++++++++++++++++++
> drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h | 112 ++++
Why do you need separate files nwl-drv.[ch] and nwl-dsi.[ch] ? I guess
you can merge all into one file, maybe with separate file for NWL
register definitions.
> 8 files changed, 1395 insertions(+)
> create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/Kconfig
> create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/Makefile
> create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c
> create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h
> create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c
> create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h
>
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index 1cc9f502c1f2..7980b5c2156f 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -154,6 +154,8 @@ source "drivers/gpu/drm/bridge/analogix/Kconfig"
>
> source "drivers/gpu/drm/bridge/adv7511/Kconfig"
>
> +source "drivers/gpu/drm/bridge/nwl-dsi/Kconfig"
> +
> source "drivers/gpu/drm/bridge/synopsys/Kconfig"
>
> endmenu
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index 4934fcf5a6f8..d9f6c0f77592 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -16,4 +16,5 @@ obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
> obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
> obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o
> obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o
> +obj-$(CONFIG_DRM_NWL_MIPI_DSI) += nwl-dsi/
> obj-y += synopsys/
> diff --git a/drivers/gpu/drm/bridge/nwl-dsi/Kconfig b/drivers/gpu/drm/bridge/nwl-dsi/Kconfig
> new file mode 100644
> index 000000000000..7fa678e3b5e2
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/nwl-dsi/Kconfig
> @@ -0,0 +1,16 @@
> +config DRM_NWL_MIPI_DSI
> + tristate "Northwest Logic MIPI DSI Host controller"
> + depends on DRM
> + depends on COMMON_CLK
> + depends on OF && HAS_IOMEM
> + select DRM_KMS_HELPER
> + select DRM_MIPI_DSI
> + select DRM_PANEL_BRIDGE
> + select GENERIC_PHY_MIPI_DPHY
> + select MFD_SYSCON
> + select MULTIPLEXER
> + select REGMAP_MMIO
> + help
> + This enables the Northwest Logic MIPI DSI Host controller as
> + for example found on NXP's i.MX8 Processors.
> +
> diff --git a/drivers/gpu/drm/bridge/nwl-dsi/Makefile b/drivers/gpu/drm/bridge/nwl-dsi/Makefile
> new file mode 100644
> index 000000000000..804baf2f1916
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/nwl-dsi/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0
> +nwl-mipi-dsi-y := nwl-drv.o nwl-dsi.o
> +obj-$(CONFIG_DRM_NWL_MIPI_DSI) += nwl-mipi-dsi.o
> +header-test-y += nwl-drv.h nwl-dsi.h
> diff --git a/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c b/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c
> new file mode 100644
> index 000000000000..9ff43d2de127
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c
> @@ -0,0 +1,499 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * i.MX8 NWL MIPI DSI host driver
> + *
> + * Copyright (C) 2017 NXP
> + * Copyright (C) 2019 Purism SPC
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/irq.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/mux/consumer.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy/phy.h>
> +#include <linux/reset.h>
> +#include <linux/regmap.h>
Alphabetic order
> +#include <linux/sys_soc.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +
> +#include "nwl-drv.h"
> +#include "nwl-dsi.h"
> +
> +#define DRV_NAME "nwl-dsi"
> +
> +/* Possible platform specific clocks */
> +#define NWL_DSI_CLK_CORE "core"
> +
> +static const struct regmap_config nwl_dsi_regmap_config = {
> + .reg_bits = 16,
> + .val_bits = 32,
> + .reg_stride = 4,
> + .max_register = NWL_DSI_IRQ_MASK2,
> + .name = DRV_NAME,
> +};
What is the point in using regmap here, why not simple writel/readl.
> +
> +struct nwl_dsi_platform_data {
> + int (*poweron)(struct nwl_dsi *dsi);
> + int (*poweroff)(struct nwl_dsi *dsi);
> + int (*select_input)(struct nwl_dsi *dsi);
> + int (*deselect_input)(struct nwl_dsi *dsi);
> + struct nwl_dsi_plat_clk_config clk_config[NWL_DSI_MAX_PLATFORM_CLOCKS];
> +};
Another construct which do not have justification, at least for now.
Please simplify the driver, remove callbacks/intermediate structs/quirks
- for now they are useless.
Unless there is a serious reason - in such case please describe it in
comments.
> +
> +static inline struct nwl_dsi *bridge_to_dsi(struct drm_bridge *bridge)
> +{
> + return container_of(bridge, struct nwl_dsi, bridge);
> +}
> +
> +static int nwl_dsi_set_platform_clocks(struct nwl_dsi *dsi, bool enable)
> +{
> + struct device *dev = dsi->dev;
> + const char *id;
> + struct clk *clk;
> + size_t i;
> + unsigned long rate;
> + int ret, result = 0;
> +
> + DRM_DEV_DEBUG_DRIVER(dev, "%s platform clocks\n",
> + enable ? "enabling" : "disabling");
> + for (i = 0; i < ARRAY_SIZE(dsi->pdata->clk_config); i++) {
> + if (!dsi->clk_config[i].present)
> + continue;
> + id = dsi->clk_config[i].id;
> + clk = dsi->clk_config[i].clk;
> +
> + if (enable) {
> + ret = clk_prepare_enable(clk);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dev,
> + "Failed to enable %s clk: %d\n",
> + id, ret);
> + result = result ?: ret;
> + }
> + rate = clk_get_rate(clk);
> + DRM_DEV_DEBUG_DRIVER(dev, "Enabled %s clk @%lu Hz\n",
> + id, rate);
> + } else {
> + clk_disable_unprepare(clk);
> + DRM_DEV_DEBUG_DRIVER(dev, "Disabled %s clk\n", id);
> + }
> + }
> +
> + return result;
> +}
> +
> +static int nwl_dsi_plat_enable(struct nwl_dsi *dsi)
> +{
> + struct device *dev = dsi->dev;
> + int ret;
> +
> + if (dsi->pdata->select_input)
> + dsi->pdata->select_input(dsi);
> +
> + ret = nwl_dsi_set_platform_clocks(dsi, true);
> + if (ret < 0)
> + return ret;
> +
> + ret = dsi->pdata->poweron(dsi);
> + if (ret < 0)
> + DRM_DEV_ERROR(dev, "Failed to power on DSI: %d\n", ret);
> + return ret;
> +}
> +
> +static void nwl_dsi_plat_disable(struct nwl_dsi *dsi)
> +{
> + dsi->pdata->poweroff(dsi);
> + nwl_dsi_set_platform_clocks(dsi, false);
> + if (dsi->pdata->deselect_input)
> + dsi->pdata->deselect_input(dsi);
> +}
> +
> +static void nwl_dsi_bridge_disable(struct drm_bridge *bridge)
> +{
> + struct nwl_dsi *dsi = bridge_to_dsi(bridge);
> +
> + nwl_dsi_disable(dsi);
> + nwl_dsi_plat_disable(dsi);
> + pm_runtime_put(dsi->dev);
> +}
> +
> +static int nwl_dsi_get_dphy_params(struct nwl_dsi *dsi,
> + const struct drm_display_mode *mode,
> + union phy_configure_opts *phy_opts)
> +{
> + unsigned long rate;
> + int ret;
> +
> + if (dsi->lanes < 1 || dsi->lanes > 4)
> + return -EINVAL;
> +
> + /*
> + * So far the DPHY spec minimal timings work for both mixel
> + * dphy and nwl dsi host
> + */
> + ret = phy_mipi_dphy_get_default_config(
> + mode->crtc_clock * 1000,
> + mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes,
> + &phy_opts->mipi_dphy);
> + if (ret < 0)
> + return ret;
> +
> + rate = clk_get_rate(dsi->tx_esc_clk);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "LP clk is @%lu Hz\n", rate);
> + phy_opts->mipi_dphy.lp_clk_rate = rate;
> +
> + return 0;
> +}
> +
> +static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
> + const struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode)
> +{
> + /* At least LCDIF + NWL needs active high sync */
> + adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
> + adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
> +
> + return true;
> +}
> +
> +static enum drm_mode_status
> +nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge,
> + const struct drm_display_mode *mode)
> +{
> + struct nwl_dsi *dsi = bridge_to_dsi(bridge);
> + int bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
> +
> + if (mode->clock * bpp > 15000000 * dsi->lanes)
> + return MODE_CLOCK_HIGH;
> +
> + if (mode->clock * bpp < 80000 * dsi->lanes)
> + return MODE_CLOCK_LOW;
> +
> + return MODE_OK;
> +}
> +
> +static void
> +nwl_dsi_bridge_mode_set(struct drm_bridge *bridge,
> + const struct drm_display_mode *mode,
> + const struct drm_display_mode *adjusted_mode)
> +{
> + struct nwl_dsi *dsi = bridge_to_dsi(bridge);
> + struct device *dev = dsi->dev;
> + union phy_configure_opts new_cfg;
> + unsigned long phy_ref_rate;
> + int ret;
> +
> + ret = nwl_dsi_get_dphy_params(dsi, adjusted_mode, &new_cfg);
> + if (ret < 0)
> + return;
> +
> + /*
> + * If hs clock is unchanged, we're all good - all parameters are
> + * derived from it atm.
> + */
> + if (new_cfg.mipi_dphy.hs_clk_rate == dsi->phy_cfg.mipi_dphy.hs_clk_rate)
> + return;
> +
> + phy_ref_rate = clk_get_rate(dsi->phy_ref_clk);
> + DRM_DEV_DEBUG_DRIVER(dev, "PHY at ref rate: %lu\n", phy_ref_rate);
> + /* Save the new desired phy config */
> + memcpy(&dsi->phy_cfg, &new_cfg, sizeof(new_cfg));
> +
> + memcpy(&dsi->mode, adjusted_mode, sizeof(dsi->mode));
> + drm_mode_debug_printmodeline(adjusted_mode);
> +}
> +
> +static void nwl_dsi_bridge_pre_enable(struct drm_bridge *bridge)
> +{
> + struct nwl_dsi *dsi = bridge_to_dsi(bridge);
> +
> + pm_runtime_get_sync(dsi->dev);
> + nwl_dsi_plat_enable(dsi);
> + nwl_dsi_enable(dsi);
> +}
> +
> +static int nwl_dsi_bridge_attach(struct drm_bridge *bridge)
> +{
> + struct nwl_dsi *dsi = bridge->driver_private;
> +
> + return drm_bridge_attach(bridge->encoder, dsi->panel_bridge, bridge);
> +}
> +
> +static const struct drm_bridge_funcs nwl_dsi_bridge_funcs = {
> + .pre_enable = nwl_dsi_bridge_pre_enable,
> + .disable = nwl_dsi_bridge_disable,
> + .mode_fixup = nwl_dsi_bridge_mode_fixup,
> + .mode_set = nwl_dsi_bridge_mode_set,
> + .mode_valid = nwl_dsi_bridge_mode_valid,
> + .attach = nwl_dsi_bridge_attach,
> +};
> +
> +static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
> +{
> + struct platform_device *pdev = to_platform_device(dsi->dev);
> + struct clk *clk;
> + const char *clk_id;
> + void __iomem *base;
> + int i, ret;
> +
> + dsi->phy = devm_phy_get(dsi->dev, "dphy");
> + if (IS_ERR(dsi->phy)) {
> + ret = PTR_ERR(dsi->phy);
> + if (ret != -EPROBE_DEFER)
> + DRM_DEV_ERROR(dsi->dev, "Could not get PHY: %d\n", ret);
> + return ret;
> + }
> +
> + /* Platform dependent clocks */
> + memcpy(dsi->clk_config, dsi->pdata->clk_config,
> + sizeof(dsi->pdata->clk_config));
> +
> + for (i = 0; i < ARRAY_SIZE(dsi->pdata->clk_config); i++) {
> + if (!dsi->clk_config[i].present)
> + continue;
> +
> + clk_id = dsi->clk_config[i].id;
> + clk = devm_clk_get(dsi->dev, clk_id);
> + if (IS_ERR(clk)) {
> + ret = PTR_ERR(clk);
> + DRM_DEV_ERROR(dsi->dev, "Failed to get %s clock: %d\n",
> + clk_id, ret);
> + return ret;
> + }
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "Setup clk %s (rate: %lu)\n",
> + clk_id, clk_get_rate(clk));
> + dsi->clk_config[i].clk = clk;
> + }
> +
> + /* DSI clocks */
> + clk = devm_clk_get(dsi->dev, "phy_ref");
> + if (IS_ERR(clk)) {
> + ret = PTR_ERR(clk);
> + DRM_DEV_ERROR(dsi->dev, "Failed to get phy_ref clock: %d\n",
> + ret);
> + return ret;
> + }
> + dsi->phy_ref_clk = clk;
> +
> + clk = devm_clk_get(dsi->dev, "rx_esc");
> + if (IS_ERR(clk)) {
> + ret = PTR_ERR(clk);
> + DRM_DEV_ERROR(dsi->dev, "Failed to get rx_esc clock: %d\n",
> + ret);
> + return ret;
> + }
> + dsi->rx_esc_clk = clk;
> +
> + clk = devm_clk_get(dsi->dev, "tx_esc");
> + if (IS_ERR(clk)) {
> + ret = PTR_ERR(clk);
> + DRM_DEV_ERROR(dsi->dev, "Failed to get tx_esc clock: %d\n",
> + ret);
> + return ret;
> + }
> + dsi->tx_esc_clk = clk;
> +
> + dsi->mux = devm_mux_control_get(dsi->dev, NULL);
> + if (IS_ERR(dsi->mux)) {
> + ret = PTR_ERR(dsi->mux);
> + if (ret != -EPROBE_DEFER)
> + DRM_DEV_ERROR(dsi->dev, "Failed to get mux: %d\n", ret);
> + return ret;
> + }
> +
> + base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + dsi->regmap =
> + devm_regmap_init_mmio(dsi->dev, base, &nwl_dsi_regmap_config);
> + if (IS_ERR(dsi->regmap)) {
> + ret = PTR_ERR(dsi->regmap);
> + DRM_DEV_ERROR(dsi->dev, "Failed to create NWL DSI regmap: %d\n",
> + ret);
> + return ret;
> + }
> +
> + dsi->irq = platform_get_irq(pdev, 0);
> + if (dsi->irq < 0) {
> + DRM_DEV_ERROR(dsi->dev, "Failed to get device IRQ: %d\n",
> + dsi->irq);
> + return dsi->irq;
> + }
> +
> + dsi->rstc = devm_reset_control_array_get(dsi->dev, false, true);
> + if (IS_ERR(dsi->rstc)) {
> + DRM_DEV_ERROR(dsi->dev, "Failed to get resets: %ld\n",
> + PTR_ERR(dsi->rstc));
> + return PTR_ERR(dsi->rstc);
> + }
> +
> + return 0;
> +}
> +
> +static int imx8mq_dsi_select_input(struct nwl_dsi *dsi)
> +{
> + struct device_node *remote;
> + u32 use_dcss = 1;
> + int ret;
> +
> + remote = of_graph_get_remote_node(dsi->dev->of_node, 0, 0);
> + if (strcmp(remote->name, "lcdif") == 0)
> + use_dcss = 0;
Relying on node name seems to me wrong. I am not sure if whole logic for
input select should be here.
My 1st impression is that selecting should be done rather in DCSS or
LCDIF driver, why do you want to put it here?
Regards
Andrzej
> +
> + DRM_DEV_INFO(dsi->dev, "Using %s as input source\n",
> + (use_dcss) ? "DCSS" : "LCDIF");
> +
> + ret = mux_control_try_select(dsi->mux, use_dcss);
> + if (ret < 0)
> + DRM_DEV_ERROR(dsi->dev, "Failed to select input: %d\n", ret);
> +
> + of_node_put(remote);
> + return ret;
> +}
> +
> +
> +static int imx8mq_dsi_deselect_input(struct nwl_dsi *dsi)
> +{
> + int ret;
> +
> + ret = mux_control_deselect(dsi->mux);
> + if (ret < 0)
> + DRM_DEV_ERROR(dsi->dev, "Failed to deselect input: %d\n", ret);
> +
> + return ret;
> +}
> +
> +
> +static int imx8mq_dsi_poweron(struct nwl_dsi *dsi)
> +{
> + int ret = 0;
> +
> + /* otherwise the display stays blank */
> + usleep_range(200, 300);
> +
> + if (dsi->rstc)
> + ret = reset_control_deassert(dsi->rstc);
> +
> + return ret;
> +}
> +
> +static int imx8mq_dsi_poweroff(struct nwl_dsi *dsi)
> +{
> + int ret = 0;
> +
> + if (dsi->quirks & SRC_RESET_QUIRK)
> + return 0;
> +
> + if (dsi->rstc)
> + ret = reset_control_assert(dsi->rstc);
> + return ret;
> +}
> +
> +static const struct drm_bridge_timings nwl_dsi_timings = {
> + .input_bus_flags = DRM_BUS_FLAG_DE_LOW,
> +};
> +
> +static const struct nwl_dsi_platform_data imx8mq_dev = {
> + .poweron = &imx8mq_dsi_poweron,
> + .poweroff = &imx8mq_dsi_poweroff,
> + .select_input = &imx8mq_dsi_select_input,
> + .deselect_input = &imx8mq_dsi_deselect_input,
> + .clk_config = {
> + { .id = NWL_DSI_CLK_CORE, .present = true },
> + },
> +};
> +
> +static const struct of_device_id nwl_dsi_dt_ids[] = {
> + { .compatible = "fsl,imx8mq-nwl-dsi", .data = &imx8mq_dev, },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, nwl_dsi_dt_ids);
> +
> +static const struct soc_device_attribute nwl_dsi_quirks_match[] = {
> + { .soc_id = "i.MX8MQ", .revision = "2.0",
> + .data = (void *)(E11418_HS_MODE_QUIRK | SRC_RESET_QUIRK) },
> + { /* sentinel. */ },
> +};
> +
> +static int nwl_dsi_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + const struct of_device_id *of_id = of_match_device(nwl_dsi_dt_ids, dev);
> + const struct nwl_dsi_platform_data *pdata = of_id->data;
> + const struct soc_device_attribute *attr;
> + struct nwl_dsi *dsi;
> + int ret;
> +
> + dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
> + if (!dsi)
> + return -ENOMEM;
> +
> + dsi->dev = dev;
> + dsi->pdata = pdata;
> +
> + ret = nwl_dsi_parse_dt(dsi);
> + if (ret)
> + return ret;
> +
> + ret = devm_request_irq(dev, dsi->irq, nwl_dsi_irq_handler, 0,
> + dev_name(dev), dsi);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dev, "Failed to request IRQ %d: %d\n", dsi->irq,
> + ret);
> + return ret;
> + }
> +
> + dsi->dsi_host.ops = &nwl_dsi_host_ops;
> + dsi->dsi_host.dev = dev;
> + ret = mipi_dsi_host_register(&dsi->dsi_host);
> + if (ret) {
> + DRM_DEV_ERROR(dev, "Failed to register MIPI host: %d\n", ret);
> + return ret;
> + }
> +
> + attr = soc_device_match(nwl_dsi_quirks_match);
> + if (attr)
> + dsi->quirks = (uintptr_t)attr->data;
> +
> + dsi->bridge.driver_private = dsi;
> + dsi->bridge.funcs = &nwl_dsi_bridge_funcs;
> + dsi->bridge.of_node = dev->of_node;
> + dsi->bridge.timings = &nwl_dsi_timings;
> +
> + dev_set_drvdata(dev, dsi);
> + pm_runtime_enable(dev);
> + return 0;
> +}
> +
> +static int nwl_dsi_remove(struct platform_device *pdev)
> +{
> + struct nwl_dsi *dsi = platform_get_drvdata(pdev);
> +
> + mipi_dsi_host_unregister(&dsi->dsi_host);
> + pm_runtime_disable(&pdev->dev);
> + return 0;
> +}
> +
> +static struct platform_driver nwl_dsi_driver = {
> + .probe = nwl_dsi_probe,
> + .remove = nwl_dsi_remove,
> + .driver = {
> + .of_match_table = nwl_dsi_dt_ids,
> + .name = DRV_NAME,
> + },
> +};
> +
> +module_platform_driver(nwl_dsi_driver);
> +
> +MODULE_AUTHOR("NXP Semiconductor");
> +MODULE_AUTHOR("Purism SPC");
> +MODULE_DESCRIPTION("Northwest Logic MIPI-DSI driver");
> +MODULE_LICENSE("GPL"); /* GPLv2 or later */
> diff --git a/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h b/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h
> new file mode 100644
> index 000000000000..1e72a9221401
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h
> @@ -0,0 +1,65 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * NWL MIPI DSI host driver
> + *
> + * Copyright (C) 2017 NXP
> + * Copyright (C) 2019 Purism SPC
> + */
> +
> +#ifndef __NWL_DRV_H__
> +#define __NWL_DRV_H__
> +
> +#include <linux/mux/consumer.h>
> +#include <linux/phy/phy.h>
> +
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_mipi_dsi.h>
> +
> +struct nwl_dsi_platform_data;
> +
> +/* i.MX8 NWL quirks */
> +/* i.MX8MQ errata E11418 */
> +#define E11418_HS_MODE_QUIRK BIT(0)
> +/* Skip DSI bits in SRC on disable to avoid blank display on enable */
> +#define SRC_RESET_QUIRK BIT(1)
> +
> +#define NWL_DSI_MAX_PLATFORM_CLOCKS 1
> +struct nwl_dsi_plat_clk_config {
> + const char *id;
> + struct clk *clk;
> + bool present;
> +};
> +
> +struct nwl_dsi {
> + struct drm_bridge bridge;
> + struct mipi_dsi_host dsi_host;
> + struct drm_bridge *panel_bridge;
> + struct device *dev;
> + struct phy *phy;
> + union phy_configure_opts phy_cfg;
> + unsigned int quirks;
> +
> + struct regmap *regmap;
> + int irq;
> + struct reset_control *rstc;
> + struct mux_control *mux;
> +
> + /* DSI clocks */
> + struct clk *phy_ref_clk;
> + struct clk *rx_esc_clk;
> + struct clk *tx_esc_clk;
> + /* Platform dependent clocks */
> + struct nwl_dsi_plat_clk_config clk_config[NWL_DSI_MAX_PLATFORM_CLOCKS];
> +
> + /* dsi lanes */
> + u32 lanes;
> + enum mipi_dsi_pixel_format format;
> + struct drm_display_mode mode;
> + unsigned long dsi_mode_flags;
> +
> + struct nwl_dsi_transfer *xfer;
> +
> + const struct nwl_dsi_platform_data *pdata;
> +};
> +
> +#endif /* __NWL_DRV_H__ */
> diff --git a/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c
> new file mode 100644
> index 000000000000..e6038cb4e849
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c
> @@ -0,0 +1,696 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * NWL MIPI DSI host driver
> + *
> + * Copyright (C) 2017 NXP
> + * Copyright (C) 2019 Purism SPC
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/irq.h>
> +#include <linux/math64.h>
> +#include <linux/regmap.h>
> +#include <linux/time64.h>
> +
> +#include <video/mipi_display.h>
> +#include <video/videomode.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +
> +#include "nwl-drv.h"
> +#include "nwl-dsi.h"
> +
> +#define NWL_DSI_MIPI_FIFO_TIMEOUT msecs_to_jiffies(500)
> +
> +/*
> + * PKT_CONTROL format:
> + * [15: 0] - word count
> + * [17:16] - virtual channel
> + * [23:18] - data type
> + * [24] - LP or HS select (0 - LP, 1 - HS)
> + * [25] - perform BTA after packet is sent
> + * [26] - perform BTA only, no packet tx
> + */
> +#define NWL_DSI_WC(x) FIELD_PREP(GENMASK(15, 0), (x))
> +#define NWL_DSI_TX_VC(x) FIELD_PREP(GENMASK(17, 16), (x))
> +#define NWL_DSI_TX_DT(x) FIELD_PREP(GENMASK(23, 18), (x))
> +#define NWL_DSI_HS_SEL(x) FIELD_PREP(GENMASK(24, 24), (x))
> +#define NWL_DSI_BTA_TX(x) FIELD_PREP(GENMASK(25, 25), (x))
> +#define NWL_DSI_BTA_NO_TX(x) FIELD_PREP(GENMASK(26, 26), (x))
> +
> +/*
> + * RX_PKT_HEADER format:
> + * [15: 0] - word count
> + * [21:16] - data type
> + * [23:22] - virtual channel
> + */
> +#define NWL_DSI_RX_DT(x) FIELD_GET(GENMASK(21, 16), (x))
> +#define NWL_DSI_RX_VC(x) FIELD_GET(GENMASK(23, 22), (x))
> +
> +/* DSI Video mode */
> +#define NWL_DSI_VM_BURST_MODE_WITH_SYNC_PULSES 0
> +#define NWL_DSI_VM_NON_BURST_MODE_WITH_SYNC_EVENTS BIT(0)
> +#define NWL_DSI_VM_BURST_MODE BIT(1)
> +
> +/* * DPI color coding */
> +#define NWL_DSI_DPI_16_BIT_565_PACKED 0
> +#define NWL_DSI_DPI_16_BIT_565_ALIGNED 1
> +#define NWL_DSI_DPI_16_BIT_565_SHIFTED 2
> +#define NWL_DSI_DPI_18_BIT_PACKED 3
> +#define NWL_DSI_DPI_18_BIT_ALIGNED 4
> +#define NWL_DSI_DPI_24_BIT 5
> +
> +/* * DPI Pixel format */
> +#define NWL_DSI_PIXEL_FORMAT_16 0
> +#define NWL_DSI_PIXEL_FORMAT_18 BIT(0)
> +#define NWL_DSI_PIXEL_FORMAT_18L BIT(1)
> +#define NWL_DSI_PIXEL_FORMAT_24 (BIT(0) | BIT(1))
> +
> +enum transfer_direction {
> + DSI_PACKET_SEND,
> + DSI_PACKET_RECEIVE,
> +};
> +
> +struct nwl_dsi_transfer {
> + const struct mipi_dsi_msg *msg;
> + struct mipi_dsi_packet packet;
> + struct completion completed;
> +
> + int status; /* status of transmission */
> + enum transfer_direction direction;
> + bool need_bta;
> + u8 cmd;
> + u16 rx_word_count;
> + size_t tx_len; /* in bytes */
> + size_t rx_len; /* in bytes */
> +};
> +
> +static int nwl_dsi_write(struct nwl_dsi *dsi, unsigned int reg, u32 val)
> +{
> + int ret;
> +
> + ret = regmap_write(dsi->regmap, reg, val);
> + if (ret < 0)
> + DRM_DEV_ERROR(dsi->dev,
> + "Failed to write NWL DSI reg 0x%x: %d\n", reg,
> + ret);
> + return ret;
> +}
> +
> +static u32 nwl_dsi_read(struct nwl_dsi *dsi, u32 reg)
> +{
> + unsigned int val;
> + int ret;
> +
> + ret = regmap_read(dsi->regmap, reg, &val);
> + if (ret < 0)
> + DRM_DEV_ERROR(dsi->dev, "Failed to read NWL DSI reg 0x%x: %d\n",
> + reg, ret);
> +
> + return val;
> +}
> +
> +static u32 nwl_dsi_get_dpi_pixel_format(enum mipi_dsi_pixel_format format)
> +{
> + switch (format) {
> + case MIPI_DSI_FMT_RGB565:
> + return NWL_DSI_PIXEL_FORMAT_16;
> + case MIPI_DSI_FMT_RGB666:
> + return NWL_DSI_PIXEL_FORMAT_18L;
> + case MIPI_DSI_FMT_RGB666_PACKED:
> + return NWL_DSI_PIXEL_FORMAT_18;
> + case MIPI_DSI_FMT_RGB888:
> + return NWL_DSI_PIXEL_FORMAT_24;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +/*
> + * ps2bc - Picoseconds to byte clock cycles
> + */
> +static u32 ps2bc(struct nwl_dsi *dsi, unsigned long long ps)
> +{
> + u32 bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
> +
> + return DIV64_U64_ROUND_UP(ps * dsi->mode.clock * bpp,
> + dsi->lanes * 8 * NSEC_PER_SEC);
> +}
> +
> +/*
> + * ui2bc - UI time periods to byte clock cycles
> + */
> +static u32 ui2bc(struct nwl_dsi *dsi, unsigned long long ui)
> +{
> + u32 bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
> +
> + return DIV64_U64_ROUND_UP(ui * dsi->lanes,
> + dsi->mode.clock * 1000 * bpp);
> +}
> +
> +/*
> + * us2bc - micro seconds to lp clock cycles
> + */
> +static u32 us2lp(u32 lp_clk_rate, unsigned long us)
> +{
> + return DIV_ROUND_UP(us * lp_clk_rate, USEC_PER_SEC);
> +}
> +
> +static int nwl_dsi_config_host(struct nwl_dsi *dsi)
> +{
> + u32 cycles;
> + struct phy_configure_opts_mipi_dphy *cfg = &dsi->phy_cfg.mipi_dphy;
> +
> + if (dsi->lanes < 1 || dsi->lanes > 4)
> + return -EINVAL;
> +
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "DSI Lanes %d\n", dsi->lanes);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_NUM_LANES, dsi->lanes - 1);
> +
> + if (dsi->dsi_mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
> + nwl_dsi_write(dsi, NWL_DSI_CFG_NONCONTINUOUS_CLK, 0x01);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_AUTOINSERT_EOTP, 0x01);
> + } else {
> + nwl_dsi_write(dsi, NWL_DSI_CFG_NONCONTINUOUS_CLK, 0x00);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_AUTOINSERT_EOTP, 0x00);
> + }
> +
> + /* values in byte clock cycles */
> + cycles = ui2bc(dsi, cfg->clk_pre);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_t_pre: 0x%x\n", cycles);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_T_PRE, cycles);
> + cycles = ps2bc(dsi, cfg->lpx + cfg->clk_prepare + cfg->clk_zero);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_tx_gap (pre): 0x%x\n", cycles);
> + cycles += ui2bc(dsi, cfg->clk_pre);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_t_post: 0x%x\n", cycles);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_T_POST, cycles);
> + cycles = ps2bc(dsi, cfg->hs_exit);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_tx_gap: 0x%x\n", cycles);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_TX_GAP, cycles);
> +
> + nwl_dsi_write(dsi, NWL_DSI_CFG_EXTRA_CMDS_AFTER_EOTP, 0x01);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_HTX_TO_COUNT, 0x00);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_LRX_H_TO_COUNT, 0x00);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_BTA_H_TO_COUNT, 0x00);
> + /* In LP clock cycles */
> + cycles = us2lp(cfg->lp_clk_rate, cfg->wakeup);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_twakeup: 0x%x\n", cycles);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_TWAKEUP, cycles);
> +
> + return 0;
> +}
> +
> +static int nwl_dsi_config_dpi(struct nwl_dsi *dsi)
> +{
> + u32 color_format, mode;
> + bool burst_mode;
> + int hfront_porch, hback_porch, vfront_porch, vback_porch;
> + int hsync_len, vsync_len;
> +
> + hfront_porch = dsi->mode.hsync_start - dsi->mode.hdisplay;
> + hsync_len = dsi->mode.hsync_end - dsi->mode.hsync_start;
> + hback_porch = dsi->mode.htotal - dsi->mode.hsync_end;
> +
> + vfront_porch = dsi->mode.vsync_start - dsi->mode.vdisplay;
> + vsync_len = dsi->mode.vsync_end - dsi->mode.vsync_start;
> + vback_porch = dsi->mode.vtotal - dsi->mode.vsync_end;
> +
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "hfront_porch = %d\n", hfront_porch);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "hback_porch = %d\n", hback_porch);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "hsync_len = %d\n", hsync_len);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "hdisplay = %d\n", dsi->mode.hdisplay);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "vfront_porch = %d\n", vfront_porch);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "vback_porch = %d\n", vback_porch);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "vsync_len = %d\n", vsync_len);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "vactive = %d\n", dsi->mode.vdisplay);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "clock = %d kHz\n", dsi->mode.clock);
> +
> + color_format = nwl_dsi_get_dpi_pixel_format(dsi->format);
> + if (color_format < 0) {
> + DRM_DEV_ERROR(dsi->dev, "Invalid color format 0x%x\n",
> + dsi->format);
> + return color_format;
> + }
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "pixel fmt = %d\n", dsi->format);
> +
> + nwl_dsi_write(dsi, NWL_DSI_INTERFACE_COLOR_CODING, NWL_DSI_DPI_24_BIT);
> + nwl_dsi_write(dsi, NWL_DSI_PIXEL_FORMAT, color_format);
> + /*
> + * Adjusting input polarity based on the video mode results in
> + * a black screen so always pick active low:
> + */
> + nwl_dsi_write(dsi, NWL_DSI_VSYNC_POLARITY,
> + NWL_DSI_VSYNC_POLARITY_ACTIVE_LOW);
> + nwl_dsi_write(dsi, NWL_DSI_HSYNC_POLARITY,
> + NWL_DSI_HSYNC_POLARITY_ACTIVE_LOW);
> +
> + burst_mode = (dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_BURST) &&
> + !(dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE);
> +
> + if (burst_mode) {
> + nwl_dsi_write(dsi, NWL_DSI_VIDEO_MODE, NWL_DSI_VM_BURST_MODE);
> + nwl_dsi_write(dsi, NWL_DSI_PIXEL_FIFO_SEND_LEVEL, 256);
> + } else {
> + mode = ((dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) ?
> + NWL_DSI_VM_BURST_MODE_WITH_SYNC_PULSES :
> + NWL_DSI_VM_NON_BURST_MODE_WITH_SYNC_EVENTS);
> + nwl_dsi_write(dsi, NWL_DSI_VIDEO_MODE, mode);
> + nwl_dsi_write(dsi, NWL_DSI_PIXEL_FIFO_SEND_LEVEL,
> + dsi->mode.hdisplay);
> + }
> +
> + nwl_dsi_write(dsi, NWL_DSI_HFP, hfront_porch);
> + nwl_dsi_write(dsi, NWL_DSI_HBP, hback_porch);
> + nwl_dsi_write(dsi, NWL_DSI_HSA, hsync_len);
> +
> + nwl_dsi_write(dsi, NWL_DSI_ENABLE_MULT_PKTS, 0x0);
> + nwl_dsi_write(dsi, NWL_DSI_BLLP_MODE, 0x1);
> + nwl_dsi_write(dsi, NWL_DSI_USE_NULL_PKT_BLLP, 0x0);
> + nwl_dsi_write(dsi, NWL_DSI_VC, 0x0);
> +
> + nwl_dsi_write(dsi, NWL_DSI_PIXEL_PAYLOAD_SIZE, dsi->mode.hdisplay);
> + nwl_dsi_write(dsi, NWL_DSI_VACTIVE, dsi->mode.vdisplay - 1);
> + nwl_dsi_write(dsi, NWL_DSI_VBP, vback_porch);
> + nwl_dsi_write(dsi, NWL_DSI_VFP, vfront_porch);
> +
> + return 0;
> +}
> +
> +static void nwl_dsi_init_interrupts(struct nwl_dsi *dsi)
> +{
> + u32 irq_enable;
> +
> + nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK, 0xffffffff);
> + nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK2, 0x7);
> +
> + irq_enable = ~(u32)(NWL_DSI_TX_PKT_DONE_MASK |
> + NWL_DSI_RX_PKT_HDR_RCVD_MASK |
> + NWL_DSI_TX_FIFO_OVFLW_MASK |
> + NWL_DSI_HS_TX_TIMEOUT_MASK);
> +
> + nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK, irq_enable);
> +}
> +
> +static int nwl_dsi_host_attach(struct mipi_dsi_host *dsi_host,
> + struct mipi_dsi_device *device)
> +{
> + struct nwl_dsi *dsi = container_of(dsi_host, struct nwl_dsi, dsi_host);
> + struct device *dev = dsi->dev;
> + struct drm_bridge *bridge;
> + struct drm_panel *panel;
> + int ret;
> +
> + DRM_DEV_INFO(dev, "lanes=%u, format=0x%x flags=0x%lx\n", device->lanes,
> + device->format, device->mode_flags);
> +
> + if (device->lanes < 1 || device->lanes > 4)
> + return -EINVAL;
> +
> + dsi->lanes = device->lanes;
> + dsi->format = device->format;
> + dsi->dsi_mode_flags = device->mode_flags;
> +
> + ret = drm_of_find_panel_or_bridge(dsi->dev->of_node, 1, 0, &panel,
> + &bridge);
> + if (ret)
> + return ret;
> +
> + if (panel) {
> + bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DSI);
> + if (IS_ERR(bridge))
> + return PTR_ERR(bridge);
> + }
> +
> + dsi->panel_bridge = bridge;
> + drm_bridge_add(&dsi->bridge);
> +
> + return 0;
> +}
> +
> +static int nwl_dsi_host_detach(struct mipi_dsi_host *dsi_host,
> + struct mipi_dsi_device *device)
> +{
> + struct nwl_dsi *dsi = container_of(dsi_host, struct nwl_dsi, dsi_host);
> +
> + drm_of_panel_bridge_remove(dsi->dev->of_node, 1, 0);
> + drm_bridge_remove(&dsi->bridge);
> +
> + return 0;
> +}
> +
> +static bool nwl_dsi_read_packet(struct nwl_dsi *dsi, u32 status)
> +{
> + struct device *dev = dsi->dev;
> + struct nwl_dsi_transfer *xfer = dsi->xfer;
> + u8 *payload = xfer->msg->rx_buf;
> + u32 val;
> + u16 word_count;
> + u8 channel;
> + u8 data_type;
> +
> + xfer->status = 0;
> +
> + if (xfer->rx_word_count == 0) {
> + if (!(status & NWL_DSI_RX_PKT_HDR_RCVD))
> + return false;
> + /* Get the RX header and parse it */
> + val = nwl_dsi_read(dsi, NWL_DSI_RX_PKT_HEADER);
> + word_count = NWL_DSI_WC(val);
> + channel = NWL_DSI_RX_VC(val);
> + data_type = NWL_DSI_RX_DT(val);
> +
> + if (channel != xfer->msg->channel) {
> + DRM_DEV_ERROR(dev,
> + "[%02X] Channel mismatch (%u != %u)\n",
> + xfer->cmd, channel, xfer->msg->channel);
> + return true;
> + }
> +
> + switch (data_type) {
> + case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
> + /* Fall through */
> + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
> + if (xfer->msg->rx_len > 1) {
> + /* read second byte */
> + payload[1] = word_count >> 8;
> + ++xfer->rx_len;
> + }
> + /* Fall through */
> + case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
> + /* Fall through */
> + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
> + if (xfer->msg->rx_len > 0) {
> + /* read first byte */
> + payload[0] = word_count & 0xff;
> + ++xfer->rx_len;
> + }
> + xfer->status = xfer->rx_len;
> + return true;
> + case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
> + word_count &= 0xff;
> + DRM_DEV_ERROR(dev, "[%02X] DSI error report: 0x%02x\n",
> + xfer->cmd, word_count);
> + xfer->status = -EPROTO;
> + return true;
> + }
> +
> + if (word_count > xfer->msg->rx_len) {
> + DRM_DEV_ERROR(
> + dev,
> + "[%02X] Receive buffer too small: %zu (< %u)\n",
> + xfer->cmd, xfer->msg->rx_len, word_count);
> + return true;
> + }
> +
> + xfer->rx_word_count = word_count;
> + } else {
> + /* Set word_count from previous header read */
> + word_count = xfer->rx_word_count;
> + }
> +
> + /* If RX payload is not yet received, wait for it */
> + if (!(status & NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD))
> + return false;
> +
> + /* Read the RX payload */
> + while (word_count >= 4) {
> + val = nwl_dsi_read(dsi, NWL_DSI_RX_PAYLOAD);
> + payload[0] = (val >> 0) & 0xff;
> + payload[1] = (val >> 8) & 0xff;
> + payload[2] = (val >> 16) & 0xff;
> + payload[3] = (val >> 24) & 0xff;
> + payload += 4;
> + xfer->rx_len += 4;
> + word_count -= 4;
> + }
> +
> + if (word_count > 0) {
> + val = nwl_dsi_read(dsi, NWL_DSI_RX_PAYLOAD);
> + switch (word_count) {
> + case 3:
> + payload[2] = (val >> 16) & 0xff;
> + ++xfer->rx_len;
> + /* Fall through */
> + case 2:
> + payload[1] = (val >> 8) & 0xff;
> + ++xfer->rx_len;
> + /* Fall through */
> + case 1:
> + payload[0] = (val >> 0) & 0xff;
> + ++xfer->rx_len;
> + break;
> + }
> + }
> +
> + xfer->status = xfer->rx_len;
> +
> + return true;
> +}
> +
> +static void nwl_dsi_finish_transmission(struct nwl_dsi *dsi, u32 status)
> +{
> + struct nwl_dsi_transfer *xfer = dsi->xfer;
> + bool end_packet = false;
> +
> + if (!xfer)
> + return;
> +
> + if (xfer->direction == DSI_PACKET_SEND &&
> + status & NWL_DSI_TX_PKT_DONE) {
> + xfer->status = xfer->tx_len;
> + end_packet = true;
> + } else if (status & NWL_DSI_DPHY_DIRECTION &&
> + ((status & (NWL_DSI_RX_PKT_HDR_RCVD |
> + NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD)))) {
> + end_packet = nwl_dsi_read_packet(dsi, status);
> + }
> +
> + if (end_packet)
> + complete(&xfer->completed);
> +}
> +
> +static void nwl_dsi_begin_transmission(struct nwl_dsi *dsi)
> +{
> + struct nwl_dsi_transfer *xfer = dsi->xfer;
> + struct mipi_dsi_packet *pkt = &xfer->packet;
> + const u8 *payload;
> + size_t length;
> + u16 word_count;
> + u8 hs_mode;
> + u32 val;
> + u32 hs_workaround = 0;
> +
> + /* Send the payload, if any */
> + length = pkt->payload_length;
> + payload = pkt->payload;
> +
> + while (length >= 4) {
> + val = *(u32 *)payload;
> + hs_workaround |= !(val & 0xFFFF00);
> + nwl_dsi_write(dsi, NWL_DSI_TX_PAYLOAD, val);
> + payload += 4;
> + length -= 4;
> + }
> + /* Send the rest of the payload */
> + val = 0;
> + switch (length) {
> + case 3:
> + val |= payload[2] << 16;
> + /* Fall through */
> + case 2:
> + val |= payload[1] << 8;
> + hs_workaround |= !(val & 0xFFFF00);
> + /* Fall through */
> + case 1:
> + val |= payload[0];
> + nwl_dsi_write(dsi, NWL_DSI_TX_PAYLOAD, val);
> + break;
> + }
> + xfer->tx_len = pkt->payload_length;
> +
> + /*
> + * Send the header
> + * header[0] = Virtual Channel + Data Type
> + * header[1] = Word Count LSB (LP) or first param (SP)
> + * header[2] = Word Count MSB (LP) or second param (SP)
> + */
> + word_count = pkt->header[1] | (pkt->header[2] << 8);
> + if (hs_workaround && (dsi->quirks & E11418_HS_MODE_QUIRK)) {
> + DRM_DEV_DEBUG_DRIVER(dsi->dev,
> + "Using hs mode workaround for cmd 0x%x\n",
> + xfer->cmd);
> + hs_mode = 1;
> + } else {
> + hs_mode = (xfer->msg->flags & MIPI_DSI_MSG_USE_LPM) ? 0 : 1;
> + }
> + val = NWL_DSI_WC(word_count) | NWL_DSI_TX_VC(xfer->msg->channel) |
> + NWL_DSI_TX_DT(xfer->msg->type) | NWL_DSI_HS_SEL(hs_mode) |
> + NWL_DSI_BTA_TX(xfer->need_bta);
> + nwl_dsi_write(dsi, NWL_DSI_PKT_CONTROL, val);
> +
> + /* Send packet command */
> + nwl_dsi_write(dsi, NWL_DSI_SEND_PACKET, 0x1);
> +}
> +
> +static ssize_t nwl_dsi_host_transfer(struct mipi_dsi_host *dsi_host,
> + const struct mipi_dsi_msg *msg)
> +{
> + struct nwl_dsi *dsi = container_of(dsi_host, struct nwl_dsi, dsi_host);
> + struct nwl_dsi_transfer xfer;
> + ssize_t ret = 0;
> +
> + /* Create packet to be sent */
> + dsi->xfer = &xfer;
> + ret = mipi_dsi_create_packet(&xfer.packet, msg);
> + if (ret < 0) {
> + dsi->xfer = NULL;
> + return ret;
> + }
> +
> + if ((msg->type & MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM ||
> + msg->type & MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM ||
> + msg->type & MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM ||
> + msg->type & MIPI_DSI_DCS_READ) &&
> + msg->rx_len > 0 && msg->rx_buf != NULL)
> + xfer.direction = DSI_PACKET_RECEIVE;
> + else
> + xfer.direction = DSI_PACKET_SEND;
> +
> + xfer.need_bta = (xfer.direction == DSI_PACKET_RECEIVE);
> + xfer.need_bta |= (msg->flags & MIPI_DSI_MSG_REQ_ACK) ? 1 : 0;
> + xfer.msg = msg;
> + xfer.status = -ETIMEDOUT;
> + xfer.rx_word_count = 0;
> + xfer.rx_len = 0;
> + xfer.cmd = 0x00;
> + if (msg->tx_len > 0)
> + xfer.cmd = ((u8 *)(msg->tx_buf))[0];
> + init_completion(&xfer.completed);
> +
> + ret = clk_prepare_enable(dsi->rx_esc_clk);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dsi->dev, "Failed to enable rx_esc clk: %zd\n",
> + ret);
> + return ret;
> + }
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "Enabled rx_esc clk @%lu Hz\n",
> + clk_get_rate(dsi->rx_esc_clk));
> +
> + /* Initiate the DSI packet transmision */
> + nwl_dsi_begin_transmission(dsi);
> +
> + if (!wait_for_completion_timeout(&xfer.completed,
> + NWL_DSI_MIPI_FIFO_TIMEOUT)) {
> + DRM_DEV_ERROR(dsi_host->dev, "[%02X] DSI transfer timed out\n",
> + xfer.cmd);
> + ret = -ETIMEDOUT;
> + } else {
> + ret = xfer.status;
> + }
> +
> + clk_disable_unprepare(dsi->rx_esc_clk);
> +
> + return ret;
> +}
> +
> +const struct mipi_dsi_host_ops nwl_dsi_host_ops = {
> + .attach = nwl_dsi_host_attach,
> + .detach = nwl_dsi_host_detach,
> + .transfer = nwl_dsi_host_transfer,
> +};
> +
> +irqreturn_t nwl_dsi_irq_handler(int irq, void *data)
> +{
> + u32 irq_status;
> + struct nwl_dsi *dsi = data;
> +
> + irq_status = nwl_dsi_read(dsi, NWL_DSI_IRQ_STATUS);
> +
> + if (irq_status & NWL_DSI_TX_FIFO_OVFLW)
> + DRM_DEV_ERROR_RATELIMITED(dsi->dev, "tx fifo overflow\n");
> +
> + if (irq_status & NWL_DSI_HS_TX_TIMEOUT)
> + DRM_DEV_ERROR_RATELIMITED(dsi->dev, "HS tx timeout\n");
> +
> + if (irq_status & NWL_DSI_TX_PKT_DONE ||
> + irq_status & NWL_DSI_RX_PKT_HDR_RCVD ||
> + irq_status & NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD)
> + nwl_dsi_finish_transmission(dsi, irq_status);
> +
> + return IRQ_HANDLED;
> +}
> +
> +int nwl_dsi_enable(struct nwl_dsi *dsi)
> +{
> + struct device *dev = dsi->dev;
> + union phy_configure_opts *phy_cfg = &dsi->phy_cfg;
> + int ret;
> +
> + if (!dsi->lanes) {
> + DRM_DEV_ERROR(dev, "Need DSI lanes: %d\n", dsi->lanes);
> + return -EINVAL;
> + }
> +
> + ret = phy_init(dsi->phy);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dev, "Failed to init DSI phy: %d\n", ret);
> + return ret;
> + }
> +
> + ret = phy_configure(dsi->phy, phy_cfg);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dev, "Failed to configure DSI phy: %d\n", ret);
> + return ret;
> + }
> +
> + ret = clk_prepare_enable(dsi->tx_esc_clk);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dsi->dev, "Failed to enable tx_esc clk: %d\n",
> + ret);
> + return ret;
> + }
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "Enabled tx_esc clk @%lu Hz\n",
> + clk_get_rate(dsi->tx_esc_clk));
> +
> + ret = nwl_dsi_config_host(dsi);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dev, "Failed to set up DSI: %d", ret);
> + return ret;
> + }
> +
> + ret = nwl_dsi_config_dpi(dsi);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dev, "Failed to set up DPI: %d", ret);
> + return ret;
> + }
> +
> + ret = phy_power_on(dsi->phy);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dev, "Failed to power on DPHY (%d)\n", ret);
> + return ret;
> + }
> +
> + nwl_dsi_init_interrupts(dsi);
> +
> + return 0;
> +}
> +
> +int nwl_dsi_disable(struct nwl_dsi *dsi)
> +{
> + struct device *dev = dsi->dev;
> +
> + DRM_DEV_DEBUG_DRIVER(dev, "Disabling clocks and phy\n");
> +
> + phy_power_off(dsi->phy);
> + phy_exit(dsi->phy);
> +
> + /* Disabling the clock before the phy breaks enabling dsi again */
> + clk_disable_unprepare(dsi->tx_esc_clk);
> +
> + return 0;
> +}
> diff --git a/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h b/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h
> new file mode 100644
> index 000000000000..579b366de652
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h
> @@ -0,0 +1,112 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * NWL MIPI DSI host driver
> + *
> + * Copyright (C) 2017 NXP
> + * Copyright (C) 2019 Purism SPC
> + */
> +#ifndef __NWL_DSI_H__
> +#define __NWL_DSI_H__
> +
> +#include <linux/irqreturn.h>
> +
> +#include <drm/drm_mipi_dsi.h>
> +
> +#include "nwl-drv.h"
> +
> +/* DSI HOST registers */
> +#define NWL_DSI_CFG_NUM_LANES 0x0
> +#define NWL_DSI_CFG_NONCONTINUOUS_CLK 0x4
> +#define NWL_DSI_CFG_T_PRE 0x8
> +#define NWL_DSI_CFG_T_POST 0xc
> +#define NWL_DSI_CFG_TX_GAP 0x10
> +#define NWL_DSI_CFG_AUTOINSERT_EOTP 0x14
> +#define NWL_DSI_CFG_EXTRA_CMDS_AFTER_EOTP 0x18
> +#define NWL_DSI_CFG_HTX_TO_COUNT 0x1c
> +#define NWL_DSI_CFG_LRX_H_TO_COUNT 0x20
> +#define NWL_DSI_CFG_BTA_H_TO_COUNT 0x24
> +#define NWL_DSI_CFG_TWAKEUP 0x28
> +#define NWL_DSI_CFG_STATUS_OUT 0x2c
> +#define NWL_DSI_RX_ERROR_STATUS 0x30
> +
> +/* DSI DPI registers */
> +#define NWL_DSI_PIXEL_PAYLOAD_SIZE 0x200
> +#define NWL_DSI_PIXEL_FIFO_SEND_LEVEL 0x204
> +#define NWL_DSI_INTERFACE_COLOR_CODING 0x208
> +#define NWL_DSI_PIXEL_FORMAT 0x20c
> +#define NWL_DSI_VSYNC_POLARITY 0x210
> +#define NWL_DSI_VSYNC_POLARITY_ACTIVE_LOW 0
> +#define NWL_DSI_VSYNC_POLARITY_ACTIVE_HIGH BIT(1)
> +
> +#define NWL_DSI_HSYNC_POLARITY 0x214
> +#define NWL_DSI_HSYNC_POLARITY_ACTIVE_LOW 0
> +#define NWL_DSI_HSYNC_POLARITY_ACTIVE_HIGH BIT(1)
> +
> +#define NWL_DSI_VIDEO_MODE 0x218
> +#define NWL_DSI_HFP 0x21c
> +#define NWL_DSI_HBP 0x220
> +#define NWL_DSI_HSA 0x224
> +#define NWL_DSI_ENABLE_MULT_PKTS 0x228
> +#define NWL_DSI_VBP 0x22c
> +#define NWL_DSI_VFP 0x230
> +#define NWL_DSI_BLLP_MODE 0x234
> +#define NWL_DSI_USE_NULL_PKT_BLLP 0x238
> +#define NWL_DSI_VACTIVE 0x23c
> +#define NWL_DSI_VC 0x240
> +
> +/* DSI APB PKT control */
> +#define NWL_DSI_TX_PAYLOAD 0x280
> +#define NWL_DSI_PKT_CONTROL 0x284
> +#define NWL_DSI_SEND_PACKET 0x288
> +#define NWL_DSI_PKT_STATUS 0x28c
> +#define NWL_DSI_PKT_FIFO_WR_LEVEL 0x290
> +#define NWL_DSI_PKT_FIFO_RD_LEVEL 0x294
> +#define NWL_DSI_RX_PAYLOAD 0x298
> +#define NWL_DSI_RX_PKT_HEADER 0x29c
> +
> +/* DSI IRQ handling */
> +#define NWL_DSI_IRQ_STATUS 0x2a0
> +#define NWL_DSI_SM_NOT_IDLE BIT(0)
> +#define NWL_DSI_TX_PKT_DONE BIT(1)
> +#define NWL_DSI_DPHY_DIRECTION BIT(2)
> +#define NWL_DSI_TX_FIFO_OVFLW BIT(3)
> +#define NWL_DSI_TX_FIFO_UDFLW BIT(4)
> +#define NWL_DSI_RX_FIFO_OVFLW BIT(5)
> +#define NWL_DSI_RX_FIFO_UDFLW BIT(6)
> +#define NWL_DSI_RX_PKT_HDR_RCVD BIT(7)
> +#define NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD BIT(8)
> +#define NWL_DSI_BTA_TIMEOUT BIT(29)
> +#define NWL_DSI_LP_RX_TIMEOUT BIT(30)
> +#define NWL_DSI_HS_TX_TIMEOUT BIT(31)
> +
> +#define NWL_DSI_IRQ_STATUS2 0x2a4
> +#define NWL_DSI_SINGLE_BIT_ECC_ERR BIT(0)
> +#define NWL_DSI_MULTI_BIT_ECC_ERR BIT(1)
> +#define NWL_DSI_CRC_ERR BIT(2)
> +
> +#define NWL_DSI_IRQ_MASK 0x2a8
> +#define NWL_DSI_SM_NOT_IDLE_MASK BIT(0)
> +#define NWL_DSI_TX_PKT_DONE_MASK BIT(1)
> +#define NWL_DSI_DPHY_DIRECTION_MASK BIT(2)
> +#define NWL_DSI_TX_FIFO_OVFLW_MASK BIT(3)
> +#define NWL_DSI_TX_FIFO_UDFLW_MASK BIT(4)
> +#define NWL_DSI_RX_FIFO_OVFLW_MASK BIT(5)
> +#define NWL_DSI_RX_FIFO_UDFLW_MASK BIT(6)
> +#define NWL_DSI_RX_PKT_HDR_RCVD_MASK BIT(7)
> +#define NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD_MASK BIT(8)
> +#define NWL_DSI_BTA_TIMEOUT_MASK BIT(29)
> +#define NWL_DSI_LP_RX_TIMEOUT_MASK BIT(30)
> +#define NWL_DSI_HS_TX_TIMEOUT_MASK BIT(31)
> +
> +#define NWL_DSI_IRQ_MASK2 0x2ac
> +#define NWL_DSI_SINGLE_BIT_ECC_ERR_MASK BIT(0)
> +#define NWL_DSI_MULTI_BIT_ECC_ERR_MASK BIT(1)
> +#define NWL_DSI_CRC_ERR_MASK BIT(2)
> +
> +extern const struct mipi_dsi_host_ops nwl_dsi_host_ops;
> +
> +irqreturn_t nwl_dsi_irq_handler(int irq, void *data);
> +int nwl_dsi_enable(struct nwl_dsi *dsi);
> +int nwl_dsi_disable(struct nwl_dsi *dsi);
> +
> +#endif /* __NWL_DSI_H__ */
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v5 0/5] Add HDMI jack support on RK3288
From: Neil Armstrong @ 2019-09-13 9:32 UTC (permalink / raw)
To: Mark Brown
Cc: ALSA development, Heiko Stuebner, Liam Girdwood, David Airlie,
dri-devel, Linux Kernel Mailing List, Hans Verkuil, Andrzej Hajda,
Laurent Pinchart, Cheng-yi Chiang, linux-rockchip, Takashi Iwai,
Tzung-Bi Shih, Dylan Reid, tzungbi, Jonas Karlman, Russell King,
Jaroslav Kysela, linux-arm-kernel, Jernej Skrabec,
Douglas Anderson, Daniel Vetter
In-Reply-To: <20190909135346.GG2036@sirena.org.uk>
[-- Attachment #1.1.1: Type: text/plain, Size: 819 bytes --]
Hi,
On 09/09/2019 15:53, Mark Brown wrote:
> On Mon, Sep 09, 2019 at 09:37:14AM +0200, Neil Armstrong wrote:
>
>> I'd like some review from ASoC people and other drm bridge reviewers,
>> Jernej, Jonas & Andrzej.
>
>> Jonas could have some comments on the overall patchset.
>
> The ASoC bits look basically fine, I've gone ahead and applied
> patch 1 as is since we're just before the merge window and that
> way we reduce potential cross tree issues. I know there's a lot
> of discussion on the DRM side about how they want to handle
> things with jacks, I'm not 100% sure what the latest thinking is
> there.
>
Thanks Mark.
Cheng-Yi can you resent this serie without the already applied
first patch with Jernej, Jonas, and Jerome Brunet <jbrunet@baylibre.com> CCed ?
Thanks,
Neil
[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
[-- Attachment #2: Type: text/plain, Size: 176 bytes --]
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Applied "spi: mediatek: add spi support for mt6765 IC" to the spi tree
From: Mark Brown @ 2019-09-13 9:42 UTC (permalink / raw)
To: luhua.xu
Cc: Mark Rutland, devicetree, wsd_upstream, linux-kernel, Rob Herring,
linux-spi, Mark Brown, linux-mediatek, Matthias Brugger,
linux-arm-kernel
In-Reply-To: <1568195731-3239-3-git-send-email-luhua.xu@mediatek.com>
The patch
spi: mediatek: add spi support for mt6765 IC
has been applied to the spi tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-5.4
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
From 2c231e0ab638519759c9a7203ecced0aa457c6c3 Mon Sep 17 00:00:00 2001
From: "luhua.xu" <luhua.xu@mediatek.com>
Date: Wed, 11 Sep 2019 05:55:30 -0400
Subject: [PATCH] spi: mediatek: add spi support for mt6765 IC
This patch add spi support for mt6765 IC.
Signed-off-by: luhua.xu <luhua.xu@mediatek.com>
Link: https://lore.kernel.org/r/1568195731-3239-3-git-send-email-luhua.xu@mediatek.com
Signed-off-by: Mark Brown <broonie@kernel.org>
---
drivers/spi/spi-mt65xx.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c
index 1f5f716016a2..c70911e2a6e1 100644
--- a/drivers/spi/spi-mt65xx.c
+++ b/drivers/spi/spi-mt65xx.c
@@ -102,6 +102,12 @@ static const struct mtk_spi_compatible mt2712_compat = {
.must_tx = true,
};
+static const struct mtk_spi_compatible mt6765_compat = {
+ .need_pad_sel = true,
+ .must_tx = true,
+ .enhance_timing = true,
+};
+
static const struct mtk_spi_compatible mt7622_compat = {
.must_tx = true,
.enhance_timing = true,
@@ -137,6 +143,9 @@ static const struct of_device_id mtk_spi_of_match[] = {
{ .compatible = "mediatek,mt6589-spi",
.data = (void *)&mtk_common_compat,
},
+ { .compatible = "mediatek,mt6765-spi",
+ .data = (void *)&mt6765_compat,
+ },
{ .compatible = "mediatek,mt7622-spi",
.data = (void *)&mt7622_compat,
},
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Applied "dt-bindings: spi: update bindings for MT6765 SoC" to the spi tree
From: Mark Brown @ 2019-09-13 9:42 UTC (permalink / raw)
To: luhua.xu
Cc: Mark Rutland, devicetree, wsd_upstream, linux-kernel, Rob Herring,
linux-spi, Mark Brown, linux-mediatek, Matthias Brugger,
linux-arm-kernel
In-Reply-To: <1568195731-3239-2-git-send-email-luhua.xu@mediatek.com>
The patch
dt-bindings: spi: update bindings for MT6765 SoC
has been applied to the spi tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-5.4
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
From 7359d108d44b823d5da049ee12302125a08f2216 Mon Sep 17 00:00:00 2001
From: "luhua.xu" <luhua.xu@mediatek.com>
Date: Wed, 11 Sep 2019 05:55:29 -0400
Subject: [PATCH] dt-bindings: spi: update bindings for MT6765 SoC
Add a DT binding documentation for the MT6765 soc.
Signed-off-by: luhua.xu <luhua.xu@mediatek.com>
Link: https://lore.kernel.org/r/1568195731-3239-2-git-send-email-luhua.xu@mediatek.com
Signed-off-by: Mark Brown <broonie@kernel.org>
---
Documentation/devicetree/bindings/spi/spi-mt65xx.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/spi/spi-mt65xx.txt b/Documentation/devicetree/bindings/spi/spi-mt65xx.txt
index c0f6c8ecfa2e..3a8079eb18c8 100644
--- a/Documentation/devicetree/bindings/spi/spi-mt65xx.txt
+++ b/Documentation/devicetree/bindings/spi/spi-mt65xx.txt
@@ -5,6 +5,7 @@ Required properties:
- mediatek,mt2701-spi: for mt2701 platforms
- mediatek,mt2712-spi: for mt2712 platforms
- mediatek,mt6589-spi: for mt6589 platforms
+ - mediatek,mt6765-spi: for mt6765 platforms
- mediatek,mt7622-spi: for mt7622 platforms
- "mediatek,mt7629-spi", "mediatek,mt7622-spi": for mt7629 platforms
- mediatek,mt8135-spi: for mt8135 platforms
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Applied "spi: mediatek: support large PA" to the spi tree
From: Mark Brown @ 2019-09-13 9:42 UTC (permalink / raw)
To: luhua.xu
Cc: Mark Rutland, devicetree, wsd_upstream, linux-kernel, Rob Herring,
linux-spi, Mark Brown, linux-mediatek, Matthias Brugger,
linux-arm-kernel
In-Reply-To: <1568195731-3239-4-git-send-email-luhua.xu@mediatek.com>
The patch
spi: mediatek: support large PA
has been applied to the spi tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-5.4
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
From fdeae8f5a2e5eb3fcc9c295bfb28503c3abd4d6e Mon Sep 17 00:00:00 2001
From: "luhua.xu" <luhua.xu@mediatek.com>
Date: Wed, 11 Sep 2019 05:55:31 -0400
Subject: [PATCH] spi: mediatek: support large PA
Add spi large PA(max=64G) support for DMA transfer.
Signed-off-by: luhua.xu <luhua.xu@mediatek.com>
Link: https://lore.kernel.org/r/1568195731-3239-4-git-send-email-luhua.xu@mediatek.com
Signed-off-by: Mark Brown <broonie@kernel.org>
---
drivers/spi/spi-mt65xx.c | 44 +++++++++++++++++++++++++++++++++++-----
1 file changed, 39 insertions(+), 5 deletions(-)
diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c
index c70911e2a6e1..6888a4dcff6d 100644
--- a/drivers/spi/spi-mt65xx.c
+++ b/drivers/spi/spi-mt65xx.c
@@ -17,6 +17,7 @@
#include <linux/platform_data/spi-mt65xx.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
+#include <linux/dma-mapping.h>
#define SPI_CFG0_REG 0x0000
#define SPI_CFG1_REG 0x0004
@@ -28,6 +29,8 @@
#define SPI_STATUS0_REG 0x001c
#define SPI_PAD_SEL_REG 0x0024
#define SPI_CFG2_REG 0x0028
+#define SPI_TX_SRC_REG_64 0x002c
+#define SPI_RX_DST_REG_64 0x0030
#define SPI_CFG0_SCK_HIGH_OFFSET 0
#define SPI_CFG0_SCK_LOW_OFFSET 8
@@ -73,6 +76,10 @@
#define MTK_SPI_MAX_FIFO_SIZE 32U
#define MTK_SPI_PACKET_SIZE 1024
+#define MTK_SPI_32BITS_MASK (0xffffffff)
+
+#define DMA_ADDR_EXT_BITS (36)
+#define DMA_ADDR_DEF_BITS (32)
struct mtk_spi_compatible {
bool need_pad_sel;
@@ -80,6 +87,8 @@ struct mtk_spi_compatible {
bool must_tx;
/* some IC design adjust cfg register to enhance time accuracy */
bool enhance_timing;
+ /* some IC support DMA addr extension */
+ bool dma_ext;
};
struct mtk_spi {
@@ -106,6 +115,7 @@ static const struct mtk_spi_compatible mt6765_compat = {
.need_pad_sel = true,
.must_tx = true,
.enhance_timing = true,
+ .dma_ext = true,
};
static const struct mtk_spi_compatible mt7622_compat = {
@@ -380,10 +390,25 @@ static void mtk_spi_setup_dma_addr(struct spi_master *master,
{
struct mtk_spi *mdata = spi_master_get_devdata(master);
- if (mdata->tx_sgl)
- writel(xfer->tx_dma, mdata->base + SPI_TX_SRC_REG);
- if (mdata->rx_sgl)
- writel(xfer->rx_dma, mdata->base + SPI_RX_DST_REG);
+ if (mdata->tx_sgl) {
+ writel((u32)(xfer->tx_dma & MTK_SPI_32BITS_MASK),
+ mdata->base + SPI_TX_SRC_REG);
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+ if (mdata->dev_comp->dma_ext)
+ writel((u32)(xfer->tx_dma >> 32),
+ mdata->base + SPI_TX_SRC_REG_64);
+#endif
+ }
+
+ if (mdata->rx_sgl) {
+ writel((u32)(xfer->rx_dma & MTK_SPI_32BITS_MASK),
+ mdata->base + SPI_RX_DST_REG);
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+ if (mdata->dev_comp->dma_ext)
+ writel((u32)(xfer->rx_dma >> 32),
+ mdata->base + SPI_RX_DST_REG_64);
+#endif
+ }
}
static int mtk_spi_fifo_transfer(struct spi_master *master,
@@ -595,7 +620,7 @@ static int mtk_spi_probe(struct platform_device *pdev)
struct mtk_spi *mdata;
const struct of_device_id *of_id;
struct resource *res;
- int i, irq, ret;
+ int i, irq, ret, addr_bits;
master = spi_alloc_master(&pdev->dev, sizeof(*mdata));
if (!master) {
@@ -761,6 +786,15 @@ static int mtk_spi_probe(struct platform_device *pdev)
}
}
+ if (mdata->dev_comp->dma_ext)
+ addr_bits = DMA_ADDR_EXT_BITS;
+ else
+ addr_bits = DMA_ADDR_DEF_BITS;
+ ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(addr_bits));
+ if (ret)
+ dev_notice(&pdev->dev, "SPI dma_set_mask(%d) failed, ret:%d\n",
+ addr_bits, ret);
+
return 0;
err_disable_runtime_pm:
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH net] net: stmmac: Hold rtnl lock in suspend/resume callbacks
From: Jose Abreu @ 2019-09-13 9:50 UTC (permalink / raw)
To: netdev
Cc: Jose Abreu, Joao Pinto, Alexandre Torgue, linux-kernel,
linux-stm32, Christophe ROULLIER, Maxime Coquelin,
Giuseppe Cavallaro, David S. Miller, linux-arm-kernel
We need to hold rnl lock in suspend and resume callbacks because phylink
requires it. Otherwise we will get a WARN() in suspend and resume.
Also, move phylink start and stop callbacks to inside device's internal
lock so that we prevent concurrent HW accesses.
Fixes: 74371272f97f ("net: stmmac: Convert to phylink and remove phylib logic")
Reported-by: Christophe ROULLIER <christophe.roullier@st.com>
Tested-by: Christophe ROULLIER <christophe.roullier@st.com>
Signed-off-by: Jose Abreu <joabreu@synopsys.com>
---
Cc: Giuseppe Cavallaro <peppe.cavallaro@st.com>
Cc: Alexandre Torgue <alexandre.torgue@st.com>
Cc: Jose Abreu <joabreu@synopsys.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com>
Cc: netdev@vger.kernel.org
Cc: linux-stm32@st-md-mailman.stormreply.com
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Cc: Christophe ROULLIER <christophe.roullier@st.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index fd54c7c87485..b19ab09cb18f 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -4451,10 +4451,12 @@ int stmmac_suspend(struct device *dev)
if (!ndev || !netif_running(ndev))
return 0;
- phylink_stop(priv->phylink);
-
mutex_lock(&priv->lock);
+ rtnl_lock();
+ phylink_stop(priv->phylink);
+ rtnl_unlock();
+
netif_device_detach(ndev);
stmmac_stop_all_queues(priv);
@@ -4558,9 +4560,11 @@ int stmmac_resume(struct device *dev)
stmmac_start_all_queues(priv);
- mutex_unlock(&priv->lock);
-
+ rtnl_lock();
phylink_start(priv->phylink);
+ rtnl_unlock();
+
+ mutex_unlock(&priv->lock);
return 0;
}
--
2.7.4
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH V2 2/2] mm/pgtable/debug: Add test validating architecture page table helpers
From: Christophe Leroy @ 2019-09-13 10:01 UTC (permalink / raw)
To: Anshuman Khandual, linux-mm
Cc: Mark Rutland, linux-ia64, linux-sh, Peter Zijlstra, James Hogan,
Heiko Carstens, Michal Hocko, Dave Hansen, Paul Mackerras,
sparclinux, Thomas Gleixner, linux-s390, Jason Gunthorpe,
Michael Ellerman, x86, Russell King - ARM Linux, Matthew Wilcox,
Steven Price, Tetsuo Handa, Gerald Schaefer, linux-snps-arc,
Kees Cook, Masahiro Yamada, Mark Brown, Kirill A . Shutemov,
Dan Williams, Vlastimil Babka, linux-arm-kernel,
Sri Krishna chowdary, Ard Biesheuvel, Greg Kroah-Hartman,
linux-mips, Ralf Baechle, linux-kernel, Paul Burton,
Mike Rapoport, Vineet Gupta, Martin Schwidefsky, Andrew Morton,
linuxppc-dev, David S. Miller
In-Reply-To: <502c497a-9bf1-7d2e-95f2-cfebcd9cf1d9@arm.com>
Le 13/09/2019 à 11:02, Anshuman Khandual a écrit :
>
>>> +#if !defined(__PAGETABLE_PMD_FOLDED) && !defined(__ARCH_HAS_4LEVEL_HACK)
>>
>> #ifdefs have to be avoided as much as possible, see below
>
> Yeah but it has been bit difficult to avoid all these $ifdef because of the
> availability (or lack of it) for all these pgtable helpers in various config
> combinations on all platforms.
As far as I can see these pgtable helpers should exist everywhere at
least via asm-generic/ files.
Can you spot a particular config which fails ?
>
>>
[...]
>>> +#if !defined(__PAGETABLE_PUD_FOLDED) && !defined(__ARCH_HAS_5LEVEL_HACK)
>>
>> The same can be done here.
>
> IIRC not only the page table helpers but there are data types (pxx_t) which
> were not present on various configs and these wrappers help prevent build
> failures. Any ways will try and see if this can be improved further. But
> meanwhile if you have some suggestions, please do let me know.
pgt_t and pmd_t are everywhere I guess.
then pud_t and p4d_t have fallbacks in asm-generic files.
So it shouldn't be an issue. Maybe if a couple of arches miss them, the
best would be to fix the arches, since that's the purpose of your
testsuite isn't it ?
Christophe
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v5 0/4] Raspberry Pi 4 DMA addressing support
From: Stefan Wahren @ 2019-09-13 10:08 UTC (permalink / raw)
To: Matthias Brugger, catalin.marinas, marc.zyngier, Matthias Brugger,
robh+dt, linux-mm, linux-arm-kernel, linux-riscv, hch,
Nicolas Saenz Julienne
Cc: f.fainelli, robin.murphy, phil, linux-kernel, linux-rpi-kernel,
will, m.szyprowski
In-Reply-To: <2fcc5ad6-fa90-6565-e75c-d20b46965733@suse.com>
Am 13.09.19 um 11:25 schrieb Matthias Brugger:
>
> On 13/09/2019 10:50, Stefan Wahren wrote:
>> Am 13.09.19 um 10:09 schrieb Matthias Brugger:
>>> On 12/09/2019 21:32, Stefan Wahren wrote:
>>>> Am 12.09.19 um 19:18 schrieb Matthias Brugger:
>>>>> On 10/09/2019 11:27, Matthias Brugger wrote:
>>>>>> On 09/09/2019 21:33, Stefan Wahren wrote:
>>>>>>> Hi Nicolas,
>>>>>>>
>>>>>>> Am 09.09.19 um 11:58 schrieb Nicolas Saenz Julienne:
>>>>>>>> Hi all,
>>>>>>>> this series attempts to address some issues we found while bringing up
>>>>>>>> the new Raspberry Pi 4 in arm64 and it's intended to serve as a follow
>>>>>>>> up of these discussions:
>>>>>>>> v4: https://lkml.org/lkml/2019/9/6/352
>>>>>>>> v3: https://lkml.org/lkml/2019/9/2/589
>>>>>>>> v2: https://lkml.org/lkml/2019/8/20/767
>>>>>>>> v1: https://lkml.org/lkml/2019/7/31/922
>>>>>>>> RFC: https://lkml.org/lkml/2019/7/17/476
>>>>>>>>
>>>>>>>> The new Raspberry Pi 4 has up to 4GB of memory but most peripherals can
>>>>>>>> only address the first GB: their DMA address range is
>>>>>>>> 0xc0000000-0xfc000000 which is aliased to the first GB of physical
>>>>>>>> memory 0x00000000-0x3c000000. Note that only some peripherals have these
>>>>>>>> limitations: the PCIe, V3D, GENET, and 40-bit DMA channels have a wider
>>>>>>>> view of the address space by virtue of being hooked up trough a second
>>>>>>>> interconnect.
>>>>>>>>
>>>>>>>> Part of this is solved on arm32 by setting up the machine specific
>>>>>>>> '.dma_zone_size = SZ_1G', which takes care of reserving the coherent
>>>>>>>> memory area at the right spot. That said no buffer bouncing (needed for
>>>>>>>> dma streaming) is available at the moment, but that's a story for
>>>>>>>> another series.
>>>>>>>>
>>>>>>>> Unfortunately there is no such thing as 'dma_zone_size' in arm64. Only
>>>>>>>> ZONE_DMA32 is created which is interpreted by dma-direct and the arm64
>>>>>>>> arch code as if all peripherals where be able to address the first 4GB
>>>>>>>> of memory.
>>>>>>>>
>>>>>>>> In the light of this, the series implements the following changes:
>>>>>>>>
>>>>>>>> - Create both DMA zones in arm64, ZONE_DMA will contain the first 1G
>>>>>>>> area and ZONE_DMA32 the rest of the 32 bit addressable memory. So far
>>>>>>>> the RPi4 is the only arm64 device with such DMA addressing limitations
>>>>>>>> so this hardcoded solution was deemed preferable.
>>>>>>>>
>>>>>>>> - Properly set ARCH_ZONE_DMA_BITS.
>>>>>>>>
>>>>>>>> - Reserve the CMA area in a place suitable for all peripherals.
>>>>>>>>
>>>>>>>> This series has been tested on multiple devices both by checking the
>>>>>>>> zones setup matches the expectations and by double-checking physical
>>>>>>>> addresses on pages allocated on the three relevant areas GFP_DMA,
>>>>>>>> GFP_DMA32, GFP_KERNEL:
>>>>>>>>
>>>>>>>> - On an RPi4 with variations on the ram memory size. But also forcing
>>>>>>>> the situation where all three memory zones are nonempty by setting a 3G
>>>>>>>> ZONE_DMA32 ceiling on a 4G setup. Both with and without NUMA support.
>>>>>>>>
>>>>>>> i like to test this series on Raspberry Pi 4 and i have some questions
>>>>>>> to get arm64 running:
>>>>>>>
>>>>>>> Do you use U-Boot? Which tree?
>>>>>> If you want to use U-Boot, try v2019.10-rc4, it should have everything you need
>>>>>> to boot your kernel.
>>>>>>
>>>>> Ok, here is a thing. In the linux kernel we now use bcm2711 as SoC name, but the
>>>>> RPi4 devicetree provided by the FW uses mostly bcm2838.
>>>> Do you mean the DTB provided at runtime?
>>>>
>>>> You mean the merged U-Boot changes, doesn't work with my Raspberry Pi
>>>> series?
>>>>
>>>>> U-Boot in its default
>>>>> config uses the devicetree provided by the FW, mostly because this way you don't
>>>>> have to do anything to find out how many RAM you really have. Secondly because
>>>>> this will allow us, in the near future, to have one U-boot binary for both RPi3
>>>>> and RPi4 (and as a side effect one binary for RPi1 and RPi2).
>>>>>
>>>>> Anyway, I found at least, that the following compatibles need to be added:
>>>>>
>>>>> "brcm,bcm2838-cprman"
>>>>> "brcm,bcm2838-gpio"
>>>>>
>>>>> Without at least the cprman driver update, you won't see anything.
>>>>>
>>>>> "brcm,bcm2838-rng200" is also a candidate.
>>>>>
>>>>> I also suppose we will need to add "brcm,bcm2838" to
>>>>> arch/arm/mach-bcm/bcm2711.c, but I haven't verified this.
>>>> How about changing this in the downstream kernel? Which is much easier.
>>> I'm not sure I understand what you want to say. My goal is to use the upstream
>>> kernel with the device tree blob provided by the FW.
>> The device tree blob you are talking is defined in this repository:
>>
>> https://github.com/raspberrypi/linux
>>
>> So the word FW is misleading to me.
>>
> No, it's part of
> https://github.com/raspberrypi/firmware.git
> file boot/bcm2711-rpi-4-b.dtb
The compiled DT blobs incl. the kernel image are stored in this artifact
repository. But the sources for the kernel and the DT are in the Linux
repo. This is necessary to be compliant to the GPL.
>
>>> If you talk about the
>>> downstream kernel, I suppose you mean we should change this in the FW DT blob
>>> and in the downstream kernel. That would work for me.
>>>
>>> Did I understand you correctly?
>> Yes
>>
>> So i suggest to add the upstream compatibles into the repo mentioned above.
>>
>> Sorry, but in case you decided as a U-Boot developer to be compatible
>> with a unreviewed DT, we also need to make U-Boot compatible with
>> upstream and downstream DT blobs.
>>
> Well RPi3 is working with the DT blob provided by the FW, as I mentioned earlier
> if we can use this DTB we can work towards one binary that can boot both RPi3
> and RPi4. On the other hand we can rely on the FW to detect the amount of memory
> our RPi4 has.
>
> That said, I agree that we should make sure that U-Boot can boot with both DTBs,
> the upstream one and the downstream. Now the question is how to get to this. I'm
> a bit puzzled that by talking about "unreviewed DT" you insinuate that bcm2711
> compatible is already reviewed and can't be changed. From what I can see none of
> these compatibles got merged for now, so we are still at time to change them.
Stephen Boyd was okay with clk changes except of a small nit. So i fixed
this is as he suggested in a separate series. Unfortunately this hasn't
be applied yet [1].
The i2c, pinctrl and the sdhci changes has been applied yet.
In my opinion it isn't the job of the mainline kernel to adapt to a
vendor device tree. It's the vendor device tree which needs to be fixed.
Sorry, but this is my holiday. I will back after the weekend.
Best regards
Stefan
[1] - https://www.spinics.net/lists/linux-clk/msg40534.html
>
> Apart from the point Florian made, to stay consistent with the RPi SoC naming,
> it will save us work, both in the kernel and in U-Boot, as we would need to add
> both compatibles to the code-base.
>
> Regards,
> Matthias
>
>>>>> Regards,
>>>>> Matthias
>>>>>
>>>>>> Regards,
>>>>>> Matthias
>>>>>>
>>>>>>> Are there any config.txt tweaks necessary?
>>>>>>>
>>>>>>>
>>>>>> _______________________________________________
>>>>>> linux-arm-kernel mailing list
>>>>>> linux-arm-kernel@lists.infradead.org
>>>>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>>>>>
>>>>> _______________________________________________
>>>>> linux-arm-kernel mailing list
>>>>> linux-arm-kernel@lists.infradead.org
>>>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox