* [PATCH 2/2] pwm: stm32: initialize raw local variables
From: Thierry Reding @ 2018-05-18 22:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526657044-14879-3-git-send-email-fabrice.gasnier@st.com>
On Fri, May 18, 2018 at 05:24:04PM +0200, Fabrice Gasnier wrote:
> This removes build warning when COMPILE_TEST=y and MFD_STM32_TIMERS=n
> in drivers/pwm/pwm-stm32.c. In function 'stm32_pwm_capture' 'raw_prd' and
> 'raw_dty' may be used uninitialized in this function
> [-Wmaybe-uninitialized]
>
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> ---
> drivers/pwm/pwm-stm32.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
Hi Lee,
I assume you'll pick this up into your branch where you applied the
initial patches along with 1/2 in this series?
Acked-by: Thierry Reding <thierry.reding@gmail.com>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180519/a4364ea1/attachment-0001.sig>
^ permalink raw reply
* [PATCH 3/3] mmc: sdhci-iproc: add SDHCI_QUIRK2_HOST_OFF_CARD_ON for cygnus
From: Scott Branden @ 2018-05-18 22:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526681037-2074-1-git-send-email-scott.branden@broadcom.com>
From: Corneliu Doban <corneliu.doban@broadcom.com>
The SDHCI_QUIRK2_HOST_OFF_CARD_ON is needed for the driver to
properly reset the host controller (reset all) on initialization
after exiting deep sleep.
Signed-off-by: Corneliu Doban <corneliu.doban@broadcom.com>
Signed-off-by: Scott Branden <scott.branden@broadcom.com>
Reviewed-by: Ray Jui <ray.jui@broadcom.com>
Reviewed-by: Srinath Mannam <srinath.mannam@broadcom.com>
---
drivers/mmc/host/sdhci-iproc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c
index 1f0ab08..d0e83db 100644
--- a/drivers/mmc/host/sdhci-iproc.c
+++ b/drivers/mmc/host/sdhci-iproc.c
@@ -186,7 +186,7 @@ static const struct sdhci_ops sdhci_iproc_32only_ops = {
static const struct sdhci_pltfm_data sdhci_iproc_cygnus_pltfm_data = {
.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
- .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN | SDHCI_QUIRK2_HOST_OFF_CARD_ON,
.ops = &sdhci_iproc_32only_ops,
};
--
2.5.0
^ permalink raw reply related
* [PATCH 2/3] mmc: sdhci-iproc: fix 32bit writes for TRANSFER_MODE register
From: Scott Branden @ 2018-05-18 22:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526681037-2074-1-git-send-email-scott.branden@broadcom.com>
From: Corneliu Doban <corneliu.doban@broadcom.com>
When the host controller accepts only 32bit writes, the value of the
16bit TRANSFER_MODE register, that has the same 32bit address as the
16bit COMMAND register, needs to be saved and it will be written
in a 32bit write together with the command as this will trigger the
host to send the command on the SD interface.
When sending the tuning command, TRANSFER_MODE is written and then
sdhci_set_transfer_mode reads it back to clear AUTO_CMD12 bit and
write it again resulting in wrong value to be written because the
initial write value was saved in a shadow and the read-back returned
a wrong value, from the register.
Fix sdhci_iproc_readw to return the saved value of TRANSFER_MODE
when a saved value exist.
Same fix for read of BLOCK_SIZE and BLOCK_COUNT registers, that are
saved for a different reason, although a scenario that will cause the
mentioned problem on this registers is not probable.
Fixes: b580c52d58d9 ("mmc: sdhci-iproc: add IPROC SDHCI driver")
Signed-off-by: Corneliu Doban <corneliu.doban@broadcom.com>
Signed-off-by: Scott Branden <scott.branden@broadcom.com>
---
drivers/mmc/host/sdhci-iproc.c | 30 +++++++++++++++++++++++++-----
1 file changed, 25 insertions(+), 5 deletions(-)
diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c
index 6f430da..1f0ab08 100644
--- a/drivers/mmc/host/sdhci-iproc.c
+++ b/drivers/mmc/host/sdhci-iproc.c
@@ -33,6 +33,8 @@ struct sdhci_iproc_host {
const struct sdhci_iproc_data *data;
u32 shadow_cmd;
u32 shadow_blk;
+ bool is_cmd_shadowed;
+ bool is_blk_shadowed;
};
#define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18)
@@ -48,8 +50,22 @@ static inline u32 sdhci_iproc_readl(struct sdhci_host *host, int reg)
static u16 sdhci_iproc_readw(struct sdhci_host *host, int reg)
{
- u32 val = sdhci_iproc_readl(host, (reg & ~3));
- u16 word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_iproc_host *iproc_host = sdhci_pltfm_priv(pltfm_host);
+ u32 val;
+ u16 word;
+
+ if ((reg == SDHCI_TRANSFER_MODE) && iproc_host->is_cmd_shadowed) {
+ /* Get the saved transfer mode */
+ val = iproc_host->shadow_cmd;
+ } else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) &&
+ iproc_host->is_blk_shadowed) {
+ /* Get the saved block info */
+ val = iproc_host->shadow_blk;
+ } else {
+ val = sdhci_iproc_readl(host, (reg & ~3));
+ }
+ word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff;
return word;
}
@@ -105,13 +121,15 @@ static void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg)
if (reg == SDHCI_COMMAND) {
/* Write the block now as we are issuing a command */
- if (iproc_host->shadow_blk != 0) {
+ if (iproc_host->is_blk_shadowed) {
sdhci_iproc_writel(host, iproc_host->shadow_blk,
SDHCI_BLOCK_SIZE);
- iproc_host->shadow_blk = 0;
+ iproc_host->is_blk_shadowed = false;
}
oldval = iproc_host->shadow_cmd;
- } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
+ iproc_host->is_cmd_shadowed = false;
+ } else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) &&
+ iproc_host->is_blk_shadowed) {
/* Block size and count are stored in shadow reg */
oldval = iproc_host->shadow_blk;
} else {
@@ -123,9 +141,11 @@ static void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg)
if (reg == SDHCI_TRANSFER_MODE) {
/* Save the transfer mode until the command is issued */
iproc_host->shadow_cmd = newval;
+ iproc_host->is_cmd_shadowed = true;
} else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
/* Save the block info until the command is issued */
iproc_host->shadow_blk = newval;
+ iproc_host->is_blk_shadowed = true;
} else {
/* Command or other regular 32-bit write */
sdhci_iproc_writel(host, newval, reg & ~3);
--
2.5.0
^ permalink raw reply related
* [PATCH 1/3] mmc: sdhci-iproc: remove hard coded mmc cap 1.8v
From: Scott Branden @ 2018-05-18 22:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526681037-2074-1-git-send-email-scott.branden@broadcom.com>
From: Srinath Mannam <srinath.mannam@broadcom.com>
Remove hard coded mmc cap 1.8v from platform data as it is board specific.
The 1.8v DDR mmc caps can be enabled using DTS property for those
boards that support it.
Fixes: b17b4ab8ce38 ("mmc: sdhci-iproc: define MMC caps in platform data")
Signed-off-by: Srinath Mannam <srinath.mannam@broadcom.com>
Signed-off-by: Scott Branden <scott.branden@broadcom.com>
Reviewed-by: Ray Jui <ray.jui@broadcom.com>
---
drivers/mmc/host/sdhci-iproc.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c
index 0ef741b..6f430da 100644
--- a/drivers/mmc/host/sdhci-iproc.c
+++ b/drivers/mmc/host/sdhci-iproc.c
@@ -206,7 +206,6 @@ static const struct sdhci_iproc_data iproc_data = {
.caps1 = SDHCI_DRIVER_TYPE_C |
SDHCI_DRIVER_TYPE_D |
SDHCI_SUPPORT_DDR50,
- .mmc_caps = MMC_CAP_1_8V_DDR,
};
static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = {
--
2.5.0
^ permalink raw reply related
* [PATCH 0/3] mmc: sdhci-iproc: UHS and 32bit access fixes
From: Scott Branden @ 2018-05-18 22:03 UTC (permalink / raw)
To: linux-arm-kernel
Collection of bug fixes for sdhci-iproc driver.
- fix for 32bit writes for TRANSFER_MODE register by correcting shadow
register logic
- fix for deep sleep mode by adding SDHCI_QUIRK2_HOST_OFF_CARD_ON
- remove hard coded mmc capability of 1.8V to allow boards to be supported
that do support 1.8V.
Corneliu Doban (2):
mmc: sdhci-iproc: fix 32bit writes for TRANSFER_MODE register
mmc: sdhci-iproc: add SDHCI_QUIRK2_HOST_OFF_CARD_ON for cygnus
Srinath Mannam (1):
mmc: sdhci-iproc: remove hard coded mmc cap 1.8v
drivers/mmc/host/sdhci-iproc.c | 33 ++++++++++++++++++++++++++-------
1 file changed, 26 insertions(+), 7 deletions(-)
--
2.5.0
^ permalink raw reply
* [GIT PULL v2 2/5] memory: tegra: Changes for v4.18-rc1
From: Thierry Reding @ 2018-05-18 21:58 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518142245.20242-2-thierry.reding@gmail.com>
Hi ARM SoC maintainers,
The following changes since commit 60cc43fc888428bb2f18f08997432d426a243338:
Linux 4.17-rc1 (2018-04-15 18:24:20 -0700)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux.git tags/tegra-for-4.18-memory-v2
for you to fetch changes up to a1be3cfdfb81cc55c1b2feb73aca6945f61acddb:
dt-bindings: memory: tegra: Remove Tegra114 SATA and AFI reset definitions (2018-05-18 22:45:01 +0200)
This contains the same patches as the previous pull request with the exception
that the final two are reordered to keep the set bisectible.
Thanks,
Thierry
----------------------------------------------------------------
memory: tegra: Changes for v4.18-rc1
This contains some cleanup of the memory controller driver as well as
unification work to share more code between Tegra20 and later SoC
generations. Also included are an implementation for the hot resets
functionality by the memory controller which is required to properly
reset busy hardware.
----------------------------------------------------------------
Dmitry Osipenko (14):
dt-bindings: memory: tegra: Add hot resets definitions
memory: tegra: Do not handle spurious interrupts
memory: tegra: Setup interrupts mask before requesting IRQ
memory: tegra: Apply interrupts mask per SoC
memory: tegra: Remove unused headers inclusions
memory: tegra: Squash tegra20-mc into common tegra-mc driver
memory: tegra: Introduce memory client hot reset
memory: tegra: Add Tegra20 memory controller hot resets
memory: tegra: Add Tegra30 memory controller hot resets
memory: tegra: Add Tegra114 memory controller hot resets
memory: tegra: Add Tegra124 memory controller hot resets
memory: tegra: Register SMMU after MC driver became ready
memory: tegra: Remove Tegra114 SATA and AFI reset definitions
dt-bindings: memory: tegra: Remove Tegra114 SATA and AFI reset definitions
Thierry Reding (1):
memory: tegra: Add Tegra210 memory controller hot resets
drivers/memory/Kconfig | 10 -
drivers/memory/Makefile | 1 -
drivers/memory/tegra/Makefile | 1 +
drivers/memory/tegra/mc.c | 362 +++++++++++++++++++++++++++----
drivers/memory/tegra/mc.h | 22 ++
drivers/memory/tegra/tegra114.c | 33 +++
drivers/memory/tegra/tegra124.c | 48 ++++
drivers/memory/tegra/tegra20.c | 296 +++++++++++++++++++++++++
drivers/memory/tegra/tegra210.c | 53 ++++-
drivers/memory/tegra/tegra30.c | 35 +++
drivers/memory/tegra20-mc.c | 254 ----------------------
include/dt-bindings/memory/tegra114-mc.h | 17 ++
include/dt-bindings/memory/tegra124-mc.h | 25 +++
include/dt-bindings/memory/tegra20-mc.h | 21 ++
include/dt-bindings/memory/tegra210-mc.h | 31 +++
include/dt-bindings/memory/tegra30-mc.h | 19 ++
include/soc/tegra/mc.h | 37 +++-
17 files changed, 956 insertions(+), 309 deletions(-)
create mode 100644 drivers/memory/tegra/tegra20.c
delete mode 100644 drivers/memory/tegra20-mc.c
create mode 100644 include/dt-bindings/memory/tegra20-mc.h
^ permalink raw reply
* dma_sync_*_for_cpu and direction=TO_DEVICE (was Re: [PATCH 02/20] dma-mapping: provide a generic dma-noncoherent implementation)
From: Russell King - ARM Linux @ 2018-05-18 21:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cecfe6bd-ef1f-1e25-bfcf-992d1f828efb@synopsys.com>
On Fri, May 18, 2018 at 01:35:08PM -0700, Vineet Gupta wrote:
> On 05/18/2018 10:50 AM, Russell King - ARM Linux wrote:
> >On Fri, May 18, 2018 at 10:20:02AM -0700, Vineet Gupta wrote:
> >>I never understood the need for this direction. And if memory serves me
> >>right, at that time I was seeing twice the amount of cache flushing !
> >It's necessary. Take a moment to think carefully about this:
> >
> > dma_map_single(, dir)
> >
> > dma_sync_single_for_cpu(, dir)
> >
> > dma_sync_single_for_device(, dir)
> >
> > dma_unmap_single(, dir)
>
> As an aside, do these imply a state machine of sorts - does a driver needs
> to always call map_single first ?
Kind-of, but some drivers do omit some of the dma_sync_*() calls.
For example, if a buffer is written to, then mapped with TO_DEVICE,
and then the CPU wishes to write to it, it's fairly common that a
driver omits the dma_sync_single_for_cpu() call. If you think about
the cases I gave and what cache operations happen, such a scenario
practically turns out to be safe.
> My original point of contention/confusion is the specific combinations of
> API and direction, specifically for_cpu(TO_DEV) and for_device(TO_CPU)
Remember that it is expected that all calls for a mapping use the
same direction argument while that mapping exists. In other words,
if you call dma_map_single(TO_DEVICE) and then use any of the other
functions, the other functions will also use TO_DEVICE. The DMA
direction argument describes the direction of the DMA operation
being performed on the buffer, not on the individual dma_* operation.
What isn't expected at arch level is for drivers to do:
dma_map_single(TO_DEVICE)
dma_sync_single_for_cpu(FROM_DEVICE)
or vice versa.
> Semantically what does dma_sync_single_for_cpu(TO_DEV) even imply for a non
> dma coherent arch.
>
> Your tables below have "none" for both, implying it is unlikely to be a real
> combination (for ARM and ARC atleast).
Very little for the cases that I've stated (and as I mentioned
above, some drivers do omit the call in that case.)
> The other case, actually @dir TO_CPU, independent of for_{cpu, device}?
> implies driver intends to touch it after the call, so it would invalidate
> any stray lines, unconditionally (and not just for speculative prefetch
> case).
If you don't have a CPU that speculatively prefetches, and you've
already had to invalidate the cache lines (to avoid write-backs
corrupting DMA'd data) then there's no need for the architecture
to do any work at the for_cpu(TO_CPU) case - the CPU shouldn't
be touching cache lines that are part of the buffer while it is
mapped, which means a non-speculating CPU won't pull in any
cache lines without an explicit access.
Speculating CPUs are different. The action of the speculation is
to try and guess what data the program wants to access ahead of
the program flow. That causes the CPU to prefetch data into the
cache. The point in the program flow that this happens is not
really determinant to the programmer. This means that if you try
to read from the DMA buffer after the DMA operation has complete
without invalidating the cache between the DMA completing and the
CPU reading, you have no guarantee that you're reading the data
that the DMA operation has been written. The cache may have
loaded itself with data before the DMA operation completed, and
the CPU may see that stale data.
The difference between non-speculating CPUs and speculating CPUs
is that for non-speculating CPUs, caches work according to explicit
accesses by the program, and the program is stalled while the data
is fetched from external memory. Speculating CPUs try to predict
ahead of time what data the program will require in the future,
and attempt to load that data into the caches _before_ the program
requires it - which means that the program suffers fewer stalls.
> >In the case of a DMA-incoherent architecture, the operations done at each
> >stage depend on the direction argument:
> >
> > map for_cpu for_device unmap
> >TO_DEV writeback none writeback none
> >TO_CPU invalidate invalidate* invalidate invalidate*
> >BIDIR writeback invalidate writeback invalidate
> >
> >* - only necessary if the CPU speculatively prefetches.
> >
> >The multiple invalidations for the TO_CPU case handles different
> >conditions that can result in data corruption, and for some CPUs, all
> >four are necessary.
>
> Can you please explain in some more detail, TO_CPU row, why invalidate is
> conditional sometimes.
See above - I hope my explanation above is sufficient.
--
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 8.8Mbps down 630kbps up
According to speedtest.net: 8.21Mbps down 510kbps up
^ permalink raw reply
* [PATCH v9 02/12] drivers: base: cacheinfo: setup DT cache properties early
From: Andy Shevchenko @ 2018-05-18 21:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180517154701.GA20281@e107155-lin>
On Thu, May 17, 2018 at 6:47 PM, Sudeep Holla <sudeep.holla@arm.com> wrote:
> Is below patch does what you were looking for ?
Somewhat.
See below for some minors.
> of_property_read_u64 searches for a property in a device node and read
> a 64-bit value from it. Instead of using of_get_property to get the
> property and then read 64-bit value using of_read_number, we can make
> use of of_property_read_u64.
Suggested-by?
> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
> - cache_size = of_get_property(np, propname, NULL);
> - if (cache_size)
> - this_leaf->size = of_read_number(cache_size, 1);
> + if (!of_property_read_u64(np, propname, &cache_size))
> + this_leaf->size = cache_size;
I suppose it's something like this
ret = of_property_...(..., &this_leaf->VAR);
if (ret)
warning / set default / etc
> propname = cache_type_info[ct_idx].line_size_props[i];
> - line_size = of_get_property(np, propname, NULL);
> - if (line_size)
> + line_size = of_property_read_u64(np, propname, &line_size);
> + if (line_size) {
ret = ...
if (ret) {
> + this_leaf->coherency_line_size = line_size;
> break;
> + }
> + if (!of_property_read_u64(np, propname, &nr_sets))
> + this_leaf->number_of_sets = nr_sets;
As in first case.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* [PATCH 4/5] pinctrl: actions: Add gpio support for Actions S900 SoC
From: Andy Shevchenko @ 2018-05-18 21:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518023056.7869-5-manivannan.sadhasivam@linaro.org>
On Fri, May 18, 2018 at 5:30 AM, Manivannan Sadhasivam
<manivannan.sadhasivam@linaro.org> wrote:
> Add gpio support to pinctrl driver for Actions Semi S900 SoC.
>
LGTM,
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> ---
> drivers/pinctrl/actions/Kconfig | 1 +
> drivers/pinctrl/actions/pinctrl-owl.c | 206 +++++++++++++++++++++++++++++++++
> drivers/pinctrl/actions/pinctrl-owl.h | 20 ++++
> drivers/pinctrl/actions/pinctrl-s900.c | 29 ++++-
> 4 files changed, 255 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/pinctrl/actions/Kconfig b/drivers/pinctrl/actions/Kconfig
> index ede97cdbbc12..490927b4ea76 100644
> --- a/drivers/pinctrl/actions/Kconfig
> +++ b/drivers/pinctrl/actions/Kconfig
> @@ -4,6 +4,7 @@ config PINCTRL_OWL
> select PINMUX
> select PINCONF
> select GENERIC_PINCONF
> + select GPIOLIB
> help
> Say Y here to enable Actions Semi OWL pinctrl driver
>
> diff --git a/drivers/pinctrl/actions/pinctrl-owl.c b/drivers/pinctrl/actions/pinctrl-owl.c
> index ee090697b1e9..4942e34c8b76 100644
> --- a/drivers/pinctrl/actions/pinctrl-owl.c
> +++ b/drivers/pinctrl/actions/pinctrl-owl.c
> @@ -11,6 +11,7 @@
>
> #include <linux/clk.h>
> #include <linux/err.h>
> +#include <linux/gpio/driver.h>
> #include <linux/io.h>
> #include <linux/module.h>
> #include <linux/of.h>
> @@ -31,6 +32,7 @@
> * struct owl_pinctrl - pinctrl state of the device
> * @dev: device handle
> * @pctrldev: pinctrl handle
> + * @chip: gpio chip
> * @lock: spinlock to protect registers
> * @soc: reference to soc_data
> * @base: pinctrl register base address
> @@ -38,6 +40,7 @@
> struct owl_pinctrl {
> struct device *dev;
> struct pinctrl_dev *pctrldev;
> + struct gpio_chip chip;
> raw_spinlock_t lock;
> struct clk *clk;
> const struct owl_pinctrl_soc_data *soc;
> @@ -536,6 +539,198 @@ static struct pinctrl_desc owl_pinctrl_desc = {
> .owner = THIS_MODULE,
> };
>
> +static const struct owl_gpio_port *
> +owl_gpio_get_port(struct owl_pinctrl *pctrl, unsigned int *pin)
> +{
> + unsigned int start = 0, i;
> +
> + for (i = 0; i < pctrl->soc->nports; i++) {
> + const struct owl_gpio_port *port = &pctrl->soc->ports[i];
> +
> + if (*pin >= start && *pin < start + port->pins) {
> + *pin -= start;
> + return port;
> + }
> +
> + start += port->pins;
> + }
> +
> + return NULL;
> +}
> +
> +static void owl_gpio_update_reg(void __iomem *base, unsigned int pin, int flag)
> +{
> + u32 val;
> +
> + val = readl_relaxed(base);
> +
> + if (flag)
> + val |= BIT(pin);
> + else
> + val &= ~BIT(pin);
> +
> + writel_relaxed(val, base);
> +}
> +
> +static int owl_gpio_request(struct gpio_chip *chip, unsigned int offset)
> +{
> + struct owl_pinctrl *pctrl = gpiochip_get_data(chip);
> + const struct owl_gpio_port *port;
> + void __iomem *gpio_base;
> + unsigned long flags;
> +
> + port = owl_gpio_get_port(pctrl, &offset);
> + if (WARN_ON(port == NULL))
> + return -ENODEV;
> +
> + gpio_base = pctrl->base + port->offset;
> +
> + /*
> + * GPIOs have higher priority over other modules, so either setting
> + * them as OUT or IN is sufficient
> + */
> + raw_spin_lock_irqsave(&pctrl->lock, flags);
> + owl_gpio_update_reg(gpio_base + port->outen, offset, true);
> + raw_spin_unlock_irqrestore(&pctrl->lock, flags);
> +
> + return 0;
> +}
> +
> +static void owl_gpio_free(struct gpio_chip *chip, unsigned int offset)
> +{
> + struct owl_pinctrl *pctrl = gpiochip_get_data(chip);
> + const struct owl_gpio_port *port;
> + void __iomem *gpio_base;
> + unsigned long flags;
> +
> + port = owl_gpio_get_port(pctrl, &offset);
> + if (WARN_ON(port == NULL))
> + return;
> +
> + gpio_base = pctrl->base + port->offset;
> +
> + raw_spin_lock_irqsave(&pctrl->lock, flags);
> + /* disable gpio output */
> + owl_gpio_update_reg(gpio_base + port->outen, offset, false);
> +
> + /* disable gpio input */
> + owl_gpio_update_reg(gpio_base + port->inen, offset, false);
> + raw_spin_unlock_irqrestore(&pctrl->lock, flags);
> +}
> +
> +static int owl_gpio_get(struct gpio_chip *chip, unsigned int offset)
> +{
> + struct owl_pinctrl *pctrl = gpiochip_get_data(chip);
> + const struct owl_gpio_port *port;
> + void __iomem *gpio_base;
> + unsigned long flags;
> + u32 val;
> +
> + port = owl_gpio_get_port(pctrl, &offset);
> + if (WARN_ON(port == NULL))
> + return -ENODEV;
> +
> + gpio_base = pctrl->base + port->offset;
> +
> + raw_spin_lock_irqsave(&pctrl->lock, flags);
> + val = readl_relaxed(gpio_base + port->dat);
> + raw_spin_unlock_irqrestore(&pctrl->lock, flags);
> +
> + return !!(val & BIT(offset));
> +}
> +
> +static void owl_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
> +{
> + struct owl_pinctrl *pctrl = gpiochip_get_data(chip);
> + const struct owl_gpio_port *port;
> + void __iomem *gpio_base;
> + unsigned long flags;
> +
> + port = owl_gpio_get_port(pctrl, &offset);
> + if (WARN_ON(port == NULL))
> + return;
> +
> + gpio_base = pctrl->base + port->offset;
> +
> + raw_spin_lock_irqsave(&pctrl->lock, flags);
> + owl_gpio_update_reg(gpio_base + port->dat, offset, value);
> + raw_spin_unlock_irqrestore(&pctrl->lock, flags);
> +}
> +
> +static int owl_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
> +{
> + struct owl_pinctrl *pctrl = gpiochip_get_data(chip);
> + const struct owl_gpio_port *port;
> + void __iomem *gpio_base;
> + unsigned long flags;
> +
> + port = owl_gpio_get_port(pctrl, &offset);
> + if (WARN_ON(port == NULL))
> + return -ENODEV;
> +
> + gpio_base = pctrl->base + port->offset;
> +
> + raw_spin_lock_irqsave(&pctrl->lock, flags);
> + owl_gpio_update_reg(gpio_base + port->outen, offset, false);
> + owl_gpio_update_reg(gpio_base + port->inen, offset, true);
> + raw_spin_unlock_irqrestore(&pctrl->lock, flags);
> +
> + return 0;
> +}
> +
> +static int owl_gpio_direction_output(struct gpio_chip *chip,
> + unsigned int offset, int value)
> +{
> + struct owl_pinctrl *pctrl = gpiochip_get_data(chip);
> + const struct owl_gpio_port *port;
> + void __iomem *gpio_base;
> + unsigned long flags;
> +
> + port = owl_gpio_get_port(pctrl, &offset);
> + if (WARN_ON(port == NULL))
> + return -ENODEV;
> +
> + gpio_base = pctrl->base + port->offset;
> +
> + raw_spin_lock_irqsave(&pctrl->lock, flags);
> + owl_gpio_update_reg(gpio_base + port->inen, offset, false);
> + owl_gpio_update_reg(gpio_base + port->outen, offset, true);
> + owl_gpio_update_reg(gpio_base + port->dat, offset, value);
> + raw_spin_unlock_irqrestore(&pctrl->lock, flags);
> +
> + return 0;
> +}
> +
> +static int owl_gpio_init(struct owl_pinctrl *pctrl)
> +{
> + struct gpio_chip *chip;
> + int ret;
> +
> + chip = &pctrl->chip;
> + chip->base = -1;
> + chip->ngpio = pctrl->soc->ngpios;
> + chip->label = dev_name(pctrl->dev);
> + chip->parent = pctrl->dev;
> + chip->owner = THIS_MODULE;
> + chip->of_node = pctrl->dev->of_node;
> +
> + ret = gpiochip_add_data(&pctrl->chip, pctrl);
> + if (ret) {
> + dev_err(pctrl->dev, "failed to register gpiochip\n");
> + return ret;
> + }
> +
> + ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev),
> + 0, 0, chip->ngpio);
> + if (ret) {
> + dev_err(pctrl->dev, "failed to add pin range\n");
> + gpiochip_remove(&pctrl->chip);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> int owl_pinctrl_probe(struct platform_device *pdev,
> struct owl_pinctrl_soc_data *soc_data)
> {
> @@ -571,6 +766,13 @@ int owl_pinctrl_probe(struct platform_device *pdev,
> owl_pinctrl_desc.pins = soc_data->pins;
> owl_pinctrl_desc.npins = soc_data->npins;
>
> + pctrl->chip.direction_input = owl_gpio_direction_input;
> + pctrl->chip.direction_output = owl_gpio_direction_output;
> + pctrl->chip.get = owl_gpio_get;
> + pctrl->chip.set = owl_gpio_set;
> + pctrl->chip.request = owl_gpio_request;
> + pctrl->chip.free = owl_gpio_free;
> +
> pctrl->soc = soc_data;
> pctrl->dev = &pdev->dev;
>
> @@ -581,6 +783,10 @@ int owl_pinctrl_probe(struct platform_device *pdev,
> return PTR_ERR(pctrl->pctrldev);
> }
>
> + ret = owl_gpio_init(pctrl);
> + if (ret)
> + return ret;
> +
> platform_set_drvdata(pdev, pctrl);
>
> return 0;
> diff --git a/drivers/pinctrl/actions/pinctrl-owl.h b/drivers/pinctrl/actions/pinctrl-owl.h
> index 448f81a6db3b..74342378937c 100644
> --- a/drivers/pinctrl/actions/pinctrl-owl.h
> +++ b/drivers/pinctrl/actions/pinctrl-owl.h
> @@ -114,6 +114,22 @@ struct owl_pinmux_func {
> unsigned int ngroups;
> };
>
> +/**
> + * struct owl_gpio_port - Actions GPIO port info
> + * @offset: offset of the GPIO port.
> + * @pins: number of pins belongs to the GPIO port.
> + * @outen: offset of the output enable register.
> + * @inen: offset of the input enable register.
> + * @dat: offset of the data register.
> + */
> +struct owl_gpio_port {
> + unsigned int offset;
> + unsigned int pins;
> + unsigned int outen;
> + unsigned int inen;
> + unsigned int dat;
> +};
> +
> /**
> * struct owl_pinctrl_soc_data - Actions pin controller driver configuration
> * @pins: array describing all pins of the pin controller.
> @@ -124,6 +140,8 @@ struct owl_pinmux_func {
> * @ngroups: number of entries in @groups.
> * @padinfo: array describing the pad info of this SoC.
> * @ngpios: number of pingroups the driver should expose as GPIOs.
> + * @port: array describing all GPIO ports of this SoC.
> + * @nports: number of GPIO ports in this SoC.
> */
> struct owl_pinctrl_soc_data {
> const struct pinctrl_pin_desc *pins;
> @@ -134,6 +152,8 @@ struct owl_pinctrl_soc_data {
> unsigned int ngroups;
> const struct owl_padinfo *padinfo;
> unsigned int ngpios;
> + const struct owl_gpio_port *ports;
> + unsigned int nports;
> };
>
> int owl_pinctrl_probe(struct platform_device *pdev,
> diff --git a/drivers/pinctrl/actions/pinctrl-s900.c b/drivers/pinctrl/actions/pinctrl-s900.c
> index 08d93f8fc086..5503c7945764 100644
> --- a/drivers/pinctrl/actions/pinctrl-s900.c
> +++ b/drivers/pinctrl/actions/pinctrl-s900.c
> @@ -33,6 +33,13 @@
> #define PAD_SR1 (0x0274)
> #define PAD_SR2 (0x0278)
>
> +#define OWL_GPIO_PORT_A 0
> +#define OWL_GPIO_PORT_B 1
> +#define OWL_GPIO_PORT_C 2
> +#define OWL_GPIO_PORT_D 3
> +#define OWL_GPIO_PORT_E 4
> +#define OWL_GPIO_PORT_F 5
> +
> #define _GPIOA(offset) (offset)
> #define _GPIOB(offset) (32 + (offset))
> #define _GPIOC(offset) (64 + (offset))
> @@ -1814,6 +1821,24 @@ static struct owl_padinfo s900_padinfo[NUM_PADS] = {
> [SGPIO3] = PAD_INFO_PULLCTL_ST(SGPIO3)
> };
>
> +#define OWL_GPIO_PORT(port, base, count, _outen, _inen, _dat) \
> + [OWL_GPIO_PORT_##port] = { \
> + .offset = base, \
> + .pins = count, \
> + .outen = _outen, \
> + .inen = _inen, \
> + .dat = _dat, \
> + }
> +
> +static const struct owl_gpio_port s900_gpio_ports[] = {
> + OWL_GPIO_PORT(A, 0x0000, 32, 0x0, 0x4, 0x8),
> + OWL_GPIO_PORT(B, 0x000C, 32, 0x0, 0x4, 0x8),
> + OWL_GPIO_PORT(C, 0x0018, 12, 0x0, 0x4, 0x8),
> + OWL_GPIO_PORT(D, 0x0024, 30, 0x0, 0x4, 0x8),
> + OWL_GPIO_PORT(E, 0x0030, 32, 0x0, 0x4, 0x8),
> + OWL_GPIO_PORT(F, 0x00F0, 8, 0x0, 0x4, 0x8)
> +};
> +
> static struct owl_pinctrl_soc_data s900_pinctrl_data = {
> .padinfo = s900_padinfo,
> .pins = (const struct pinctrl_pin_desc *)s900_pads,
> @@ -1822,7 +1847,9 @@ static struct owl_pinctrl_soc_data s900_pinctrl_data = {
> .nfunctions = ARRAY_SIZE(s900_functions),
> .groups = s900_groups,
> .ngroups = ARRAY_SIZE(s900_groups),
> - .ngpios = NUM_GPIOS
> + .ngpios = NUM_GPIOS,
> + .ports = s900_gpio_ports,
> + .nports = ARRAY_SIZE(s900_gpio_ports)
> };
>
> static int s900_pinctrl_probe(struct platform_device *pdev)
> --
> 2.14.1
>
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* [PATCH v3 4/6] dt-bindings: add binding for at91-usart in spi mode
From: Rob Herring @ 2018-05-18 21:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180511103822.31698-5-radu.pirea@microchip.com>
On Fri, May 11, 2018 at 01:38:20PM +0300, Radu Pirea wrote:
> These are bindings for at91-usart IP in spi spi mode. There is no support for
s/spi spi/SPI/
> internal chip select. Only kind of chip selects available are gpio chip
GPIO
> selects.
>
> Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
> ---
> .../bindings/spi/microchip,at91-usart-spi.txt | 28 +++++++++++++++++++
> 1 file changed, 28 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/spi/microchip,at91-usart-spi.txt
>
> diff --git a/Documentation/devicetree/bindings/spi/microchip,at91-usart-spi.txt b/Documentation/devicetree/bindings/spi/microchip,at91-usart-spi.txt
> new file mode 100644
> index 000000000000..b68a3bec4121
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/spi/microchip,at91-usart-spi.txt
So now we have 2 copies of the same thing that varies by 2 properties?
Please make this one doc.
> @@ -0,0 +1,28 @@
> +* Universal Synchronous Asynchronous Receiver/Transmitter (USART) in SPI mode
> +
> +Required properties:
> +- #size-cells : Must be <0>
> +- #address-cells : Must be <1>
> +- compatible: Should be "atmel,at91rm9200-usart" or "atmel,at91sam9260-usart"
> +- reg: Should contain registers location and length
> +- interrupts: Should contain interrupt
> +- clocks: phandles to input clocks.
> +- clock-names: tuple listing input clock names.
> + Required elements: "usart"
> +- cs-gpios: chipselects (internal cs not supported)
> +- at91,usart-mode: AT91_USART_MODE_SPI (found in dt-bindings/mfd/at91-usart.h)
at91 is not a vendor.
> +
> +Example:
> + #include <dt-bindings/mfd/at91-usart.h>
> +
> + spi0: spi at f001c000 {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + compatible = "atmel,at91rm9200-usart", "atmel,at91sam9260-usart";
> + at91,usart-mode = <AT91_USART_MODE_SPI>;
> + reg = <0xf001c000 0x100>;
> + interrupts = <12 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&usart0_clk>;
> + clock-names = "usart";
> + cs-gpios = <&pioB 3 0>;
> + };
> --
> 2.17.0
>
^ permalink raw reply
* [PATCH 16/16] iommu/arm-smmu: Add list of devices to opt out of DMA domains
From: Jordan Crouse @ 2018-05-18 21:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518213500.31595-1-jcrouse@codeaurora.org>
Add a list of compatible strings for devices that wish to opt out
of attaching to a DMA domain. This is for devices that prefer to
manage their own IOMMU space for any number of reasons. Returning
-ENOTSUPP for attach device will filter down and force
arch_setup_dma_ops() to not set up the iommu DMA ops. Later
the client device in question can set up and attach their own
domain.
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
---
drivers/iommu/arm-smmu.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 100797a07be0..df6e4eacf727 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1080,6 +1080,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
goto out_unlock;
cfg->cbndx = ret;
+
if (smmu->version < ARM_SMMU_V2) {
cfg->irptndx = atomic_inc_return(&smmu->irptndx);
cfg->irptndx %= smmu->num_context_irqs;
@@ -1450,6 +1451,15 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
return 0;
}
+/*
+ * This is a list of compatible strings for devices that wish to manage their
+ * own IOMMU space instead of the DMA IOMMU ops. Devices on this list will not
+ * allow themselves to be attached to a IOMMU_DOMAIN_DMA domain
+ */
+static const char *arm_smmu_dma_blacklist[] = {
+ "qcom,adreno",
+};
+
static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
{
int ret;
@@ -1472,6 +1482,20 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
if (!fwspec->iommu_priv)
return -ENODEV;
+ /*
+ * If this is the dfeault DMA domain, check to see if the device is on
+ * the blacklist and reject if so
+ */
+ if (domain->type == IOMMU_DOMAIN_DMA && dev->of_node) {
+ int i;
+
+ for(i = 0; i < ARRAY_SIZE(arm_smmu_dma_blacklist); i++) {
+ if (of_device_is_compatible(dev->of_node,
+ arm_smmu_dma_blacklist[i]))
+ return -ENOTSUPP;
+ }
+ }
+
smmu = fwspec_smmu(fwspec);
/* Ensure that the domain is finalised */
ret = arm_smmu_init_domain_context(domain, smmu);
--
2.17.0
^ permalink raw reply related
* [PATCH 15/16] iommu: Gracefully allow drivers to not attach to a default domain
From: Jordan Crouse @ 2018-05-18 21:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518213500.31595-1-jcrouse@codeaurora.org>
Provide individual device drivers the chance to gracefully refuse
to attach a device to the default domain. If the attach_device
op returns -ENOTSUPP don't print a error message and don't set
group->domain but still return success from iommu_group_add_dev().
This allows all the usual APIs to work and the next domain to try
to attach will take group->domain for itself and everything will
proceed as normal.
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
---
drivers/iommu/iommu.c | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 0ba3d27f2300..a255b5d6c495 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -599,7 +599,7 @@ int iommu_group_add_device(struct iommu_group *group, struct device *dev)
if (group->domain)
ret = __iommu_attach_device(group->domain, dev);
mutex_unlock(&group->mutex);
- if (ret)
+ if (ret && ret != -ENOTSUPP)
goto err_put_group;
/* Notify any listeners about change to group. */
@@ -625,7 +625,8 @@ int iommu_group_add_device(struct iommu_group *group, struct device *dev)
sysfs_remove_link(&dev->kobj, "iommu_group");
err_free_device:
kfree(device);
- pr_err("Failed to add device %s to group %d: %d\n", dev_name(dev), group->id, ret);
+ if (ret != -ENOTSUPP)
+ pr_err("Failed to add device %s to group %d: %d\n", dev_name(dev), group->id, ret);
return ret;
}
EXPORT_SYMBOL_GPL(iommu_group_add_device);
@@ -1238,8 +1239,16 @@ struct iommu_group *iommu_group_get_for_dev(struct device *dev)
ret = iommu_group_add_device(group, dev);
if (ret) {
- iommu_group_put(group);
- return ERR_PTR(ret);
+ /*
+ * If the driver chooses not to bind the device, reset
+ * group->domain so a new domain can be added later
+ */
+ if (ret == -ENOTSUPP)
+ group->domain = NULL;
+ else {
+ iommu_group_put(group);
+ return ERR_PTR(ret);
+ }
}
return group;
--
2.17.0
^ permalink raw reply related
* [PATCH 14/16] drm/msm: Support per-instance address spaces
From: Jordan Crouse @ 2018-05-18 21:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518213500.31595-1-jcrouse@codeaurora.org>
Create a per-instance address spaces when a new DRM file instance is
opened assuming the target supports it and the underlying
infrastructure exists. If the operation is unsupported fall back
quietly to use the global pagetable.
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
---
drivers/gpu/drm/msm/msm_drv.c | 31 ++++++++++++++++++++++++++++---
1 file changed, 28 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 2b663435a3f7..31d1e7589892 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -22,6 +22,7 @@
#include "msm_fence.h"
#include "msm_gpu.h"
#include "msm_kms.h"
+#include "msm_gem.h"
/*
@@ -511,7 +512,27 @@ static int context_init(struct drm_device *dev, struct drm_file *file)
msm_submitqueue_init(dev, ctx);
- ctx->aspace = priv->gpu->aspace;
+ /* FIXME: Do we want a dynamic name of some sort? */
+ /* FIXME: We need a smarter way to set the range based on target */
+
+ ctx->aspace = msm_gem_address_space_create_instance(
+ priv->gpu->aspace->mmu, "gpu", 0x100000000, 0x1ffffffff);
+
+ if (IS_ERR(ctx->aspace)) {
+ int ret = PTR_ERR(ctx->aspace);
+
+ /*
+ * if per-instance pagetables are not supported, fall back to
+ * using the generic address space
+ */
+ if (ret == -EOPNOTSUPP)
+ ctx->aspace = priv->gpu->aspace;
+ else {
+ kfree(ctx);
+ return ret;
+ }
+ }
+
file->driver_priv = ctx;
return 0;
@@ -527,8 +548,12 @@ static int msm_open(struct drm_device *dev, struct drm_file *file)
return context_init(dev, file);
}
-static void context_close(struct msm_file_private *ctx)
+static void context_close(struct msm_drm_private *priv,
+ struct msm_file_private *ctx)
{
+ if (ctx && ctx->aspace != priv->gpu->aspace)
+ msm_gem_address_space_put(ctx->aspace);
+
msm_submitqueue_close(ctx);
kfree(ctx);
}
@@ -543,7 +568,7 @@ static void msm_postclose(struct drm_device *dev, struct drm_file *file)
priv->lastctx = NULL;
mutex_unlock(&dev->struct_mutex);
- context_close(ctx);
+ context_close(priv, ctx);
}
static irqreturn_t msm_irq(int irq, void *arg)
--
2.17.0
^ permalink raw reply related
* [PATCH 13/16] drm/msm/a5xx: Support per-instance pagetables
From: Jordan Crouse @ 2018-05-18 21:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518213500.31595-1-jcrouse@codeaurora.org>
Add support for per-instance pagetables for 5XX targets. Create a support
buffer for preemption to hold the SMMU pagetable information for a preempted
ring, enable TTBR1 to support split pagetables and add the necessary PM4
commands to trigger a pagetable switch at the beginning of a user command.
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
---
drivers/gpu/drm/msm/Kconfig | 1 +
drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 55 +++++++++++++++++
drivers/gpu/drm/msm/adreno/a5xx_gpu.h | 17 ++++++
drivers/gpu/drm/msm/adreno/a5xx_preempt.c | 74 +++++++++++++++++++----
drivers/gpu/drm/msm/adreno/adreno_gpu.c | 11 ++++
drivers/gpu/drm/msm/adreno/adreno_gpu.h | 5 ++
drivers/gpu/drm/msm/msm_ringbuffer.h | 1 +
7 files changed, 152 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index 38cbde971b48..e69cbf88bb3d 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -15,6 +15,7 @@ config DRM_MSM
select SND_SOC_HDMI_CODEC if SND_SOC
select SYNC_FILE
select PM_OPP
+ select IOMMU_SVA
default y
help
DRM/KMS driver for MSM/snapdragon.
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
index b2c0370072dd..f4be2536441b 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
@@ -199,6 +199,59 @@ static void a5xx_submit_in_rb(struct msm_gpu *gpu, struct msm_gem_submit *submit
msm_gpu_retire(gpu);
}
+static void a5xx_set_pagetable(struct msm_gpu *gpu, struct msm_ringbuffer *ring,
+ struct msm_file_private *ctx)
+{
+ u64 ttbr;
+ u32 asid;
+
+ if (msm_iommu_pasid_info(ctx->aspace->mmu, &ttbr, &asid))
+ return;
+
+ ttbr = ttbr | ((u64) asid) << 48;
+
+ /* Turn off protected mode */
+ OUT_PKT7(ring, CP_SET_PROTECTED_MODE, 1);
+ OUT_RING(ring, 0);
+
+ /* Turn on APIV mode to access critical regions */
+ OUT_PKT4(ring, REG_A5XX_CP_CNTL, 1);
+ OUT_RING(ring, 1);
+
+ /* Make sure the ME is synchronized before staring the update */
+ OUT_PKT7(ring, CP_WAIT_FOR_ME, 0);
+
+ /* Execute the table update */
+ OUT_PKT7(ring, CP_SMMU_TABLE_UPDATE, 3);
+ OUT_RING(ring, lower_32_bits(ttbr));
+ OUT_RING(ring, upper_32_bits(ttbr));
+ OUT_RING(ring, 0);
+
+ /*
+ * Write the new TTBR0 to the preemption records - this will be used to
+ * reload the pagetable if the current ring gets preempted out.
+ */
+ OUT_PKT7(ring, CP_MEM_WRITE, 4);
+ OUT_RING(ring, lower_32_bits(rbmemptr(ring, ttbr0)));
+ OUT_RING(ring, upper_32_bits(rbmemptr(ring, ttbr0)));
+ OUT_RING(ring, lower_32_bits(ttbr));
+ OUT_RING(ring, upper_32_bits(ttbr));
+
+ /* Invalidate the draw state so we start off fresh */
+ OUT_PKT7(ring, CP_SET_DRAW_STATE, 3);
+ OUT_RING(ring, 0x40000);
+ OUT_RING(ring, 1);
+ OUT_RING(ring, 0);
+
+ /* Turn off APRIV */
+ OUT_PKT4(ring, REG_A5XX_CP_CNTL, 1);
+ OUT_RING(ring, 0);
+
+ /* Turn off protected mode */
+ OUT_PKT7(ring, CP_SET_PROTECTED_MODE, 1);
+ OUT_RING(ring, 1);
+}
+
static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
struct msm_file_private *ctx)
{
@@ -214,6 +267,8 @@ static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
return;
}
+ a5xx_set_pagetable(gpu, ring, ctx);
+
OUT_PKT7(ring, CP_PREEMPT_ENABLE_GLOBAL, 1);
OUT_RING(ring, 0x02);
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
index 7d71860c4bee..9387d6085576 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
@@ -45,6 +45,9 @@ struct a5xx_gpu {
atomic_t preempt_state;
struct timer_list preempt_timer;
+ struct a5xx_smmu_info *smmu_info;
+ struct drm_gem_object *smmu_info_bo;
+ uint64_t smmu_info_iova;
};
#define to_a5xx_gpu(x) container_of(x, struct a5xx_gpu, base)
@@ -132,6 +135,20 @@ struct a5xx_preempt_record {
*/
#define A5XX_PREEMPT_COUNTER_SIZE (16 * 4)
+/*
+ * This is a global structure that the preemption code uses to switch in the
+ * pagetable for the preempted process - the code switches in whatever we
+ * after preempting in a new ring.
+ */
+struct a5xx_smmu_info {
+ uint32_t magic;
+ uint32_t _pad4;
+ uint64_t ttbr0;
+ uint32_t asid;
+ uint32_t contextidr;
+};
+
+#define A5XX_SMMU_INFO_MAGIC 0x3618CDA3UL
int a5xx_power_init(struct msm_gpu *gpu);
void a5xx_gpmu_ucode_init(struct msm_gpu *gpu);
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_preempt.c b/drivers/gpu/drm/msm/adreno/a5xx_preempt.c
index 970c7963ae29..d5dbcbd494f3 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_preempt.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_preempt.c
@@ -12,6 +12,7 @@
*/
#include "msm_gem.h"
+#include "msm_mmu.h"
#include "a5xx_gpu.h"
/*
@@ -145,6 +146,15 @@ void a5xx_preempt_trigger(struct msm_gpu *gpu)
a5xx_gpu->preempt[ring->id]->wptr = get_wptr(ring);
spin_unlock_irqrestore(&ring->lock, flags);
+ /* Do read barrier to make sure we have updated pagetable info */
+ rmb();
+
+ /* Set the SMMU info for the preemption */
+ if (a5xx_gpu->smmu_info) {
+ a5xx_gpu->smmu_info->ttbr0 = ring->memptrs->ttbr0;
+ a5xx_gpu->smmu_info->contextidr = 0;
+ }
+
/* Set the address of the incoming preemption record */
gpu_write64(gpu, REG_A5XX_CP_CONTEXT_SWITCH_RESTORE_ADDR_LO,
REG_A5XX_CP_CONTEXT_SWITCH_RESTORE_ADDR_HI,
@@ -214,9 +224,10 @@ void a5xx_preempt_hw_init(struct msm_gpu *gpu)
a5xx_gpu->preempt[i]->rbase = gpu->rb[i]->iova;
}
- /* Write a 0 to signal that we aren't switching pagetables */
+ /* Tell the CP where to find the smmu_info buffer*/
gpu_write64(gpu, REG_A5XX_CP_CONTEXT_SWITCH_SMMU_INFO_LO,
- REG_A5XX_CP_CONTEXT_SWITCH_SMMU_INFO_HI, 0);
+ REG_A5XX_CP_CONTEXT_SWITCH_SMMU_INFO_HI,
+ a5xx_gpu->smmu_info_iova);
/* Reset the preemption state */
set_preempt_state(a5xx_gpu, PREEMPT_NONE);
@@ -275,8 +286,43 @@ void a5xx_preempt_fini(struct msm_gpu *gpu)
drm_gem_object_unreference(a5xx_gpu->preempt_bo[i]);
a5xx_gpu->preempt_bo[i] = NULL;
}
+
+ if (a5xx_gpu->smmu_info_bo) {
+ if (a5xx_gpu->smmu_info_iova)
+ msm_gem_put_iova(a5xx_gpu->smmu_info_bo, gpu->aspace);
+ drm_gem_object_unreference_unlocked(a5xx_gpu->smmu_info_bo);
+ a5xx_gpu->smmu_info_bo = NULL;
+ }
}
+static int a5xx_smmu_info_init(struct msm_gpu *gpu)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
+ struct a5xx_smmu_info *ptr;
+ struct drm_gem_object *bo;
+ u64 iova;
+
+ if (!msm_mmu_has_feature(gpu->aspace->mmu,
+ MMU_FEATURE_PER_INSTANCE_TABLES))
+ return 0;
+
+ ptr = msm_gem_kernel_new(gpu->dev, sizeof(struct a5xx_smmu_info),
+ MSM_BO_UNCACHED, gpu->aspace, &bo, &iova);
+
+ if (IS_ERR(ptr))
+ return PTR_ERR(ptr);
+
+ ptr->magic = A5XX_SMMU_INFO_MAGIC;
+
+ a5xx_gpu->smmu_info_bo = bo;
+ a5xx_gpu->smmu_info_iova = iova;
+ a5xx_gpu->smmu_info = ptr;
+
+ return 0;
+}
+
+
void a5xx_preempt_init(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
@@ -288,17 +334,21 @@ void a5xx_preempt_init(struct msm_gpu *gpu)
return;
for (i = 0; i < gpu->nr_rings; i++) {
- if (preempt_init_ring(a5xx_gpu, gpu->rb[i])) {
- /*
- * On any failure our adventure is over. Clean up and
- * set nr_rings to 1 to force preemption off
- */
- a5xx_preempt_fini(gpu);
- gpu->nr_rings = 1;
-
- return;
- }
+ if (preempt_init_ring(a5xx_gpu, gpu->rb[i]))
+ goto fail;
}
+ if (a5xx_smmu_info_init(gpu))
+ goto fail;
+
timer_setup(&a5xx_gpu->preempt_timer, a5xx_preempt_timer, 0);
+
+ return;
+fail:
+ /*
+ * On any failure our adventure is over. Clean up and
+ * set nr_rings to 1 to force preemption off
+ */
+ a5xx_preempt_fini(gpu);
+ gpu->nr_rings = 1;
}
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
index 17d0506d058c..b681edec4560 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
@@ -558,6 +558,17 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
adreno_gpu_config.ioname = "kgsl_3d0_reg_memory";
adreno_gpu_config.irqname = "kgsl_3d0_irq";
+ if (adreno_is_a5xx(adreno_gpu)) {
+ /*
+ * If possible use the TTBR1 virtual address space for all the
+ * "global" buffer objects which are shared between processes.
+ * This leaves the lower virtual address space open for
+ * per-instance pagables if they are available
+ */
+ adreno_gpu_config.va_start_global = 0xfffffff800000000ULL;
+ adreno_gpu_config.va_end_global = 0xfffffff8ffffffffULL;
+ }
+
adreno_gpu_config.va_start = SZ_16M;
adreno_gpu_config.va_end = 0xffffffff;
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
index d6b0e7b813f4..dc4b21ea3e65 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
@@ -203,6 +203,11 @@ static inline int adreno_is_a530(struct adreno_gpu *gpu)
return gpu->revn == 530;
}
+static inline bool adreno_is_a5xx(struct adreno_gpu *gpu)
+{
+ return ((gpu->revn >= 500) & (gpu->revn < 600));
+}
+
int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value);
const struct firmware *adreno_request_fw(struct adreno_gpu *adreno_gpu,
const char *fwname);
diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.h b/drivers/gpu/drm/msm/msm_ringbuffer.h
index cffce094aecb..fd71484d5894 100644
--- a/drivers/gpu/drm/msm/msm_ringbuffer.h
+++ b/drivers/gpu/drm/msm/msm_ringbuffer.h
@@ -26,6 +26,7 @@
struct msm_rbmemptrs {
volatile uint32_t rptr;
volatile uint32_t fence;
+ volatile uint64_t ttbr0;
};
struct msm_ringbuffer {
--
2.17.0
^ permalink raw reply related
* [PATCH 12/16] drm/msm: Add support for per-instance address spaces
From: Jordan Crouse @ 2018-05-18 21:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518213500.31595-1-jcrouse@codeaurora.org>
Add a function to allocate a new pasid from a existing
MMU domain and create a per-instance address space.
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
---
drivers/gpu/drm/msm/msm_drv.h | 3 +++
drivers/gpu/drm/msm/msm_gem_vma.c | 37 +++++++++++++++++++++++++------
2 files changed, 33 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 897b08135927..d92b009dfef4 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -177,6 +177,9 @@ void msm_gem_address_space_put(struct msm_gem_address_space *aspace);
struct msm_gem_address_space *
msm_gem_address_space_create(struct device *dev, struct iommu_domain *domain,
const char *name);
+struct msm_gem_address_space *
+msm_gem_address_space_create_instance(struct msm_mmu *parent, const char *name,
+ u64 start, u64 end);
void msm_gem_submit_free(struct msm_gem_submit *submit);
int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c
index ffbec224551b..d75b56119752 100644
--- a/drivers/gpu/drm/msm/msm_gem_vma.c
+++ b/drivers/gpu/drm/msm/msm_gem_vma.c
@@ -92,12 +92,11 @@ msm_gem_map_vma(struct msm_gem_address_space *aspace,
}
struct msm_gem_address_space *
-msm_gem_address_space_create(struct device *dev, struct iommu_domain *domain,
- const char *name)
+msm_gem_address_space_new(struct msm_mmu *mmu, const char *name,
+ u64 start, u64 end)
{
struct msm_gem_address_space *aspace;
- u64 size = domain->geometry.aperture_end -
- domain->geometry.aperture_start;
+ u64 size = end - start;
aspace = kzalloc(sizeof(*aspace), GFP_KERNEL);
if (!aspace)
@@ -105,12 +104,36 @@ msm_gem_address_space_create(struct device *dev, struct iommu_domain *domain,
spin_lock_init(&aspace->lock);
aspace->name = name;
- aspace->mmu = msm_iommu_new(dev, domain);
+ aspace->mmu = mmu;
- drm_mm_init(&aspace->mm, (domain->geometry.aperture_start >> PAGE_SHIFT),
- size >> PAGE_SHIFT);
+ drm_mm_init(&aspace->mm, (start >> PAGE_SHIFT), size >> PAGE_SHIFT);
kref_init(&aspace->kref);
return aspace;
}
+
+struct msm_gem_address_space *
+msm_gem_address_space_create(struct device *dev, struct iommu_domain *domain,
+ const char *name)
+{
+ struct msm_mmu *mmu = msm_iommu_new(dev, domain);
+
+ if (IS_ERR(mmu))
+ return ERR_CAST(mmu);
+
+ return msm_gem_address_space_new(mmu, name,
+ domain->geometry.aperture_start,
+ domain->geometry.aperture_end);
+}
+
+struct msm_gem_address_space *
+msm_gem_address_space_create_instance(struct msm_mmu *parent, const char *name,
+ u64 start, u64 end)
+{
+ struct msm_mmu *instance = msm_iommu_pasid_new(parent);
+ if (IS_ERR(instance))
+ return ERR_CAST(instance);
+
+ return msm_gem_address_space_new(instance, name, start, end);
+}
--
2.17.0
^ permalink raw reply related
* [PATCH 11/16] drm/msm: Add support for iommu-sva PASIDs
From: Jordan Crouse @ 2018-05-18 21:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518213500.31595-1-jcrouse@codeaurora.org>
The IOMMU core can support creating multiple pagetables
for a specific domai and making them available to a client
driver that has the means to manage the pagetable itself.
PASIDs are unique indexes to a software created pagetable with
the same format and characteristics as the parent IOMMU device.
The IOMMU driver allocates the pagetable and tracks it with a
unique token (PASID) - it does not touch the actual hardware.
The client driver is expected to be able to manage the pagetables
and do something interesting with them.
Some flavors of the MSM GPU are able to allow each DRM instance
to have its own pagetable (and virtual memory space) and switch them
asynchronously at the beginning of a command. This protects against
accidental or malicious corruption or copying of buffers from other
instances.
The first step is to add a MMU implementation that can allocate a
PASID and set up a msm_mmu struct to abstract (most) of the details
from the rest of the system.
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
---
drivers/gpu/drm/msm/msm_iommu.c | 190 ++++++++++++++++++++++++++++++++
drivers/gpu/drm/msm/msm_mmu.h | 6 +
2 files changed, 196 insertions(+)
diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c
index fdbe1a8372f0..99e6611969d4 100644
--- a/drivers/gpu/drm/msm/msm_iommu.c
+++ b/drivers/gpu/drm/msm/msm_iommu.c
@@ -15,6 +15,9 @@
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/hashtable.h>
+#include <linux/arm-smmu.h>
+
#include "msm_drv.h"
#include "msm_mmu.h"
@@ -34,12 +37,32 @@ static int msm_fault_handler(struct iommu_domain *domain, struct device *dev,
return 0;
}
+static bool msm_iommu_check_per_instance(struct msm_iommu *iommu)
+{
+ int val;
+
+ if (!IS_ENABLED(CONFIG_IOMMU_SVA))
+ return false;
+
+ if (iommu_domain_get_attr(iommu->domain, DOMAIN_ATTR_SPLIT_TABLES,
+ &val))
+ return false;
+
+ return val ? true : false;
+}
+
static int msm_iommu_attach(struct msm_mmu *mmu, const char * const *names,
int cnt)
{
struct msm_iommu *iommu = to_msm_iommu(mmu);
int ret;
+ if (msm_iommu_check_per_instance(iommu)) {
+ if (!iommu_sva_device_init(mmu->dev, 0, (1 << 31), NULL))
+ msm_mmu_set_feature(mmu,
+ MMU_FEATURE_PER_INSTANCE_TABLES);
+ }
+
pm_runtime_get_sync(mmu->dev);
ret = iommu_attach_device(iommu->domain, mmu->dev);
pm_runtime_put_sync(mmu->dev);
@@ -112,3 +135,170 @@ struct msm_mmu *msm_iommu_new(struct device *dev, struct iommu_domain *domain)
return &iommu->base;
}
+
+struct pasid_entry {
+ int pasid;
+ u64 ttbr;
+ u32 asid;
+ struct hlist_node node;
+};
+
+DECLARE_HASHTABLE(pasid_table, 4);
+
+static int install_pasid_cb(int pasid, u64 ttbr, u32 asid, void *data)
+{
+ struct pasid_entry *entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+
+ if (!entry)
+ return -ENOMEM;
+
+ entry->pasid = pasid;
+ entry->ttbr = ttbr;
+ entry->asid = asid;
+
+ /* FIXME: Assume that we'll never have a pasid conflict? */
+ /* FIXME: locks? RCU? */
+ hash_add(pasid_table, &entry->node, pasid);
+ return 0;
+}
+
+static void remove_pasid_cb(int pasid, void *data)
+{
+ struct pasid_entry *entry;
+
+ hash_for_each_possible(pasid_table, entry, node, pasid) {
+ if (pasid == entry->pasid) {
+ hash_del(&entry->node);
+ kfree(entry);
+ return;
+ }
+ }
+}
+
+struct msm_iommu_pasid {
+ struct msm_mmu base;
+ struct device *dev;
+ int pasid;
+ u64 ttbr;
+ u32 asid;
+};
+#define to_msm_iommu_pasid(x) container_of(x, struct msm_iommu_pasid, base)
+
+static int msm_iommu_pasid_attach(struct msm_mmu *mmu,
+ const char * const *names, int cnt)
+{
+ return 0;
+}
+
+static int msm_iommu_pasid_map(struct msm_mmu *mmu, uint64_t iova,
+ struct sg_table *sgt, unsigned len, int prot)
+{
+ struct msm_iommu_pasid *pasid = to_msm_iommu_pasid(mmu);
+ int ret;
+
+ ret = iommu_sva_map_sg(pasid->pasid, iova, sgt->sgl, sgt->nents, prot);
+ WARN_ON(ret < 0);
+
+ return (ret == len) ? 0 : -EINVAL;
+}
+
+static int msm_iommu_pasid_unmap(struct msm_mmu *mmu, uint64_t iova,
+ struct sg_table *sgt, unsigned len)
+{
+ struct msm_iommu_pasid *pasid = to_msm_iommu_pasid(mmu);
+
+ iommu_sva_unmap(pasid->pasid, iova, len);
+
+ return 0;
+}
+
+static void msm_iommu_pasid_detach(struct msm_mmu *mmu,
+ const char * const *names, int cnt)
+{
+}
+
+static void msm_iommu_pasid_destroy(struct msm_mmu *mmu)
+{
+ struct msm_iommu_pasid *pasid = to_msm_iommu_pasid(mmu);
+
+ iommu_sva_free_pasid(pasid->pasid, pasid->dev);
+ kfree(pasid);
+}
+
+static const struct msm_mmu_funcs pasid_funcs = {
+ .attach = msm_iommu_pasid_attach,
+ .detach = msm_iommu_pasid_detach,
+ .map = msm_iommu_pasid_map,
+ .unmap = msm_iommu_pasid_unmap,
+ .destroy = msm_iommu_pasid_destroy,
+};
+
+static const struct arm_smmu_pasid_ops msm_iommu_pasid_ops = {
+ .install_pasid = install_pasid_cb,
+ .remove_pasid = remove_pasid_cb,
+};
+
+struct msm_mmu *msm_iommu_pasid_new(struct msm_mmu *parent)
+{
+ struct msm_iommu *parent_iommu = to_msm_iommu(parent);
+ struct msm_iommu_pasid *pasid;
+ int id;
+
+ if (!msm_mmu_has_feature(parent, MMU_FEATURE_PER_INSTANCE_TABLES))
+ return ERR_PTR(-EOPNOTSUPP);
+
+ pasid = kzalloc(sizeof(*pasid), GFP_KERNEL);
+ if (!pasid)
+ return ERR_PTR(-ENOMEM);
+
+ arm_smmu_add_pasid_ops(parent_iommu->domain, &msm_iommu_pasid_ops,
+ NULL);
+
+ id = iommu_sva_alloc_pasid(parent_iommu->domain, parent->dev);
+ if (id < 0) {
+ kfree(pasid);
+ return ERR_PTR(id);
+ }
+
+ pasid->pasid = id;
+ pasid->dev = parent->dev;
+
+ msm_mmu_init(&pasid->base, parent->dev, &pasid_funcs);
+
+ return &pasid->base;
+}
+
+/* Given a pasid return the TTBR and ASID associated with it */
+int msm_iommu_pasid_info(struct msm_mmu *mmu, u64 *ttbr, u32 *asid)
+{
+ struct msm_iommu_pasid *pasid;
+ struct pasid_entry *entry;
+
+ if (mmu->funcs->map != msm_iommu_pasid_map)
+ return -ENODEV;
+
+ pasid = to_msm_iommu_pasid(mmu);
+
+ if (!pasid->ttbr) {
+ /* Find the pasid entry in the hash */
+ hash_for_each_possible(pasid_table, entry, node, pasid->pasid) {
+ if (pasid->pasid == entry->pasid) {
+ pasid->ttbr = entry->ttbr;
+ pasid->asid = entry->asid;
+ goto out;
+ }
+ }
+
+ WARN(1, "Couldn't find the entry for pasid %d\n", pasid->pasid);
+ return -EINVAL;
+ }
+
+out:
+ if (*ttbr)
+ *ttbr = pasid->ttbr;
+
+ if (*asid)
+ *asid = pasid->asid;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h
index 85df78d71398..29436b9daa73 100644
--- a/drivers/gpu/drm/msm/msm_mmu.h
+++ b/drivers/gpu/drm/msm/msm_mmu.h
@@ -30,6 +30,9 @@ struct msm_mmu_funcs {
void (*destroy)(struct msm_mmu *mmu);
};
+/* MMU features */
+#define MMU_FEATURE_PER_INSTANCE_TABLES (1 << 0)
+
struct msm_mmu {
const struct msm_mmu_funcs *funcs;
struct device *dev;
@@ -48,6 +51,9 @@ static inline void msm_mmu_init(struct msm_mmu *mmu, struct device *dev,
struct msm_mmu *msm_iommu_new(struct device *dev, struct iommu_domain *domain);
struct msm_mmu *msm_gpummu_new(struct device *dev, struct msm_gpu *gpu);
+struct msm_mmu *msm_iommu_pasid_new(struct msm_mmu *parent);
+int msm_iommu_pasid_info(struct msm_mmu *mmu, u64 *ttbr, u32 *asid);
+
static inline void msm_mmu_set_fault_handler(struct msm_mmu *mmu, void *arg,
int (*handler)(void *arg, unsigned long iova, int flags))
{
--
2.17.0
^ permalink raw reply related
* [PATCH 10/16] drm/msm: Add msm_mmu features
From: Jordan Crouse @ 2018-05-18 21:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518213500.31595-1-jcrouse@codeaurora.org>
Add a few simple support functions to support a bitmask of
features that a specific MMU implementation supports. The
first feature will be per-instance pagetables coming in the
following patch.
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
---
drivers/gpu/drm/msm/msm_mmu.h | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h
index aa2c5d4580c8..85df78d71398 100644
--- a/drivers/gpu/drm/msm/msm_mmu.h
+++ b/drivers/gpu/drm/msm/msm_mmu.h
@@ -35,6 +35,7 @@ struct msm_mmu {
struct device *dev;
int (*handler)(void *arg, unsigned long iova, int flags);
void *arg;
+ unsigned long features;
};
static inline void msm_mmu_init(struct msm_mmu *mmu, struct device *dev,
@@ -54,4 +55,16 @@ static inline void msm_mmu_set_fault_handler(struct msm_mmu *mmu, void *arg,
mmu->handler = handler;
}
+static inline void msm_mmu_set_feature(struct msm_mmu *mmu,
+ unsigned long feature)
+{
+ mmu->features |= feature;
+}
+
+static inline bool msm_mmu_has_feature(struct msm_mmu *mmu,
+ unsigned long feature)
+{
+ return (mmu->features & feature) ? true : false;
+}
+
#endif /* __MSM_MMU_H__ */
--
2.17.0
^ permalink raw reply related
* [PATCH 09/16] drm/msm/gpu: Support using split page tables for kernel buffer objects
From: Jordan Crouse @ 2018-05-18 21:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518213500.31595-1-jcrouse@codeaurora.org>
arm-smmu based targets can support split pagetables (TTBR0/TTBR1).
This is most useful for implementing per-instance pagetables so that
the "user" pagetable can be swapped out while the "kernel" or
"global" pagetable remains entact.
if the target specifies a global virtual memory range then try to
enable TTBR1 (the "global" pagetable) on the domain and if
successful use the global virtual memory range for allocations
on the default GPU address space - this ensures that the global
allocations make it into the right space. Per-instance pagetables
still need additional support to be enabled but even if they
aren't set up it isn't harmful to just use TTBR1 for all
virtual memory regions and leave the other pagetable unused.
If TTBR1 support isn't enabled then fall back to the "legacy"
virtual address space both kernel and user.
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
---
drivers/gpu/drm/msm/msm_gpu.c | 19 +++++++++++++++++--
drivers/gpu/drm/msm/msm_gpu.h | 4 ++--
2 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 2f45bea04221..78e8e56d2499 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -703,7 +703,8 @@ static int get_clocks(struct platform_device *pdev, struct msm_gpu *gpu)
static struct msm_gem_address_space *
msm_gpu_create_address_space(struct msm_gpu *gpu, struct platform_device *pdev,
- uint64_t va_start, uint64_t va_end)
+ u64 va_start, u64 va_end,
+ u64 va_global_start, u64 va_global_end)
{
struct iommu_domain *iommu;
struct msm_gem_address_space *aspace;
@@ -721,6 +722,19 @@ msm_gpu_create_address_space(struct msm_gpu *gpu, struct platform_device *pdev,
iommu->geometry.aperture_start = va_start;
iommu->geometry.aperture_end = va_end;
+ /* If a va_global range was specified then try to set up split tables */
+ if (va_global_start && va_global_end) {
+ int val = 1;
+
+ ret = iommu_domain_set_attr(iommu, DOMAIN_ATTR_SPLIT_TABLES,
+ &val);
+
+ if (!WARN(ret, "Unable to enable split pagetables for the IOMMU\n")) {
+ iommu->geometry.aperture_start = va_global_start;
+ iommu->geometry.aperture_end = va_global_end;
+ }
+ }
+
dev_info(gpu->dev->dev, "%s: using IOMMU\n", gpu->name);
aspace = msm_gem_address_space_create(&pdev->dev, iommu, "gpu");
@@ -813,7 +827,8 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
msm_devfreq_init(gpu);
gpu->aspace = msm_gpu_create_address_space(gpu, pdev,
- config->va_start, config->va_end);
+ config->va_start, config->va_end, config->va_start_global,
+ config->va_end_global);
if (gpu->aspace == NULL)
dev_info(drm->dev, "%s: no IOMMU, fallback to VRAM carveout!\n", name);
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index b8241179175a..da58aa6c12c8 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -31,8 +31,8 @@ struct msm_gpu_perfcntr;
struct msm_gpu_config {
const char *ioname;
const char *irqname;
- uint64_t va_start;
- uint64_t va_end;
+ uint64_t va_start, va_end;
+ uint64_t va_start_global, va_end_global;
unsigned int nr_rings;
};
--
2.17.0
^ permalink raw reply related
* [PATCH 08/16] drm/msm: Pass the MMU domain index in struct msm_file_private
From: Jordan Crouse @ 2018-05-18 21:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518213500.31595-1-jcrouse@codeaurora.org>
Pass the index of the MMU domain in struct msm_file_private instead
of assuming gpu->id throughout the submit path. This clears the way
to change ctx->aspace to a per-instance pagetable.
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
---
drivers/gpu/drm/msm/msm_drv.c | 16 ++++------------
drivers/gpu/drm/msm/msm_drv.h | 1 +
drivers/gpu/drm/msm/msm_gem.h | 1 +
drivers/gpu/drm/msm/msm_gem_submit.c | 11 ++++++-----
drivers/gpu/drm/msm/msm_gpu.c | 5 ++---
5 files changed, 14 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 30cd514d8f7c..2b663435a3f7 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -502,6 +502,7 @@ static void load_gpu(struct drm_device *dev)
static int context_init(struct drm_device *dev, struct drm_file *file)
{
+ struct msm_drm_private *priv = dev->dev_private;
struct msm_file_private *ctx;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
@@ -510,6 +511,7 @@ static int context_init(struct drm_device *dev, struct drm_file *file)
msm_submitqueue_init(dev, ctx);
+ ctx->aspace = priv->gpu->aspace;
file->driver_priv = ctx;
return 0;
@@ -683,17 +685,6 @@ static int msm_ioctl_gem_cpu_fini(struct drm_device *dev, void *data,
return ret;
}
-static int msm_ioctl_gem_info_iova(struct drm_device *dev,
- struct drm_gem_object *obj, uint64_t *iova)
-{
- struct msm_drm_private *priv = dev->dev_private;
-
- if (!priv->gpu)
- return -EINVAL;
-
- return msm_gem_get_iova(obj, priv->gpu->aspace, iova);
-}
-
static int msm_ioctl_gem_info(struct drm_device *dev, void *data,
struct drm_file *file)
{
@@ -709,9 +700,10 @@ static int msm_ioctl_gem_info(struct drm_device *dev, void *data,
return -ENOENT;
if (args->flags & MSM_INFO_IOVA) {
+ struct msm_file_private *ctx = file->driver_priv;
uint64_t iova;
- ret = msm_ioctl_gem_info_iova(dev, obj, &iova);
+ ret = msm_gem_get_iova(obj, ctx->aspace, &iova);
if (!ret)
args->offset = iova;
} else {
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 48ed5b9a8580..897b08135927 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -58,6 +58,7 @@ struct msm_file_private {
rwlock_t queuelock;
struct list_head submitqueues;
int queueid;
+ struct msm_gem_address_space *aspace;
};
enum msm_mdp_plane_property {
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h
index c5d9bd3e47a8..fe8b3aa7d76f 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -138,6 +138,7 @@ void msm_gem_vunmap(struct drm_gem_object *obj, enum msm_gem_lock subclass);
struct msm_gem_submit {
struct drm_device *dev;
struct msm_gpu *gpu;
+ struct msm_gem_address_space *aspace;
struct list_head node; /* node in ring submit list */
struct list_head bo_list;
struct ww_acquire_ctx ticket;
diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c
index 7bd83e0afa97..d5dffcba9919 100644
--- a/drivers/gpu/drm/msm/msm_gem_submit.c
+++ b/drivers/gpu/drm/msm/msm_gem_submit.c
@@ -31,8 +31,8 @@
#define BO_PINNED 0x2000
static struct msm_gem_submit *submit_create(struct drm_device *dev,
- struct msm_gpu *gpu, struct msm_gpu_submitqueue *queue,
- uint32_t nr_bos, uint32_t nr_cmds)
+ struct msm_gpu *gpu, struct msm_gem_address_space *aspace,
+ struct msm_gpu_submitqueue *queue, uint32_t nr_bos, uint32_t nr_cmds)
{
struct msm_gem_submit *submit;
uint64_t sz = sizeof(*submit) + ((u64)nr_bos * sizeof(submit->bos[0])) +
@@ -46,6 +46,7 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev,
return NULL;
submit->dev = dev;
+ submit->aspace = aspace;
submit->gpu = gpu;
submit->fence = NULL;
submit->pid = get_pid(task_pid(current));
@@ -167,7 +168,7 @@ static void submit_unlock_unpin_bo(struct msm_gem_submit *submit,
struct msm_gem_object *msm_obj = submit->bos[i].obj;
if (submit->bos[i].flags & BO_PINNED)
- msm_gem_put_iova(&msm_obj->base, submit->gpu->aspace);
+ msm_gem_put_iova(&msm_obj->base, submit->aspace);
if (submit->bos[i].flags & BO_LOCKED)
ww_mutex_unlock(&msm_obj->resv->lock);
@@ -270,7 +271,7 @@ static int submit_pin_objects(struct msm_gem_submit *submit)
/* if locking succeeded, pin bo: */
ret = msm_gem_get_iova(&msm_obj->base,
- submit->gpu->aspace, &iova);
+ submit->aspace, &iova);
if (ret)
break;
@@ -471,7 +472,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
}
}
- submit = submit_create(dev, gpu, queue, args->nr_bos, args->nr_cmds);
+ submit = submit_create(dev, gpu, ctx->aspace, queue, args->nr_bos, args->nr_cmds);
if (!submit) {
ret = -ENOMEM;
goto out_unlock;
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 1c09acfb4028..2f45bea04221 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -551,7 +551,7 @@ static void retire_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
struct msm_gem_object *msm_obj = submit->bos[i].obj;
/* move to inactive: */
msm_gem_move_to_inactive(&msm_obj->base);
- msm_gem_put_iova(&msm_obj->base, gpu->aspace);
+ msm_gem_put_iova(&msm_obj->base, submit->aspace);
drm_gem_object_put(&msm_obj->base);
}
@@ -635,8 +635,7 @@ void msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
/* submit takes a reference to the bo and iova until retired: */
drm_gem_object_get(&msm_obj->base);
- msm_gem_get_iova(&msm_obj->base,
- submit->gpu->aspace, &iova);
+ msm_gem_get_iova(&msm_obj->base, submit->aspace, &iova);
if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE)
msm_gem_move_to_active(&msm_obj->base, gpu, true, submit->fence);
--
2.17.0
^ permalink raw reply related
* [PATCH 07/16] drm/msm/gpu: Enable 64 bit mode by default
From: Jordan Crouse @ 2018-05-18 21:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518213500.31595-1-jcrouse@codeaurora.org>
A5XX GPUs can be run in either 32 or 64 bit mode. The GPU registers
and the microcode use 64 bit virtual addressing in either case but the
upper 32 bits are ignored if the GPU is in 32 bit mode. There is no
performance disadvantage to remaining in 64 bit mode even if we are
only generating 32 bit addresses so switch over now to prepare for
using addresses above 4G for targets that support them.
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
---
drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 14 ++++++++++++++
drivers/gpu/drm/msm/msm_iommu.c | 2 +-
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
index d39400e5bc42..b2c0370072dd 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
@@ -741,6 +741,20 @@ static int a5xx_hw_init(struct msm_gpu *gpu)
REG_A5XX_RBBM_SECVID_TSB_TRUSTED_BASE_HI, 0x00000000);
gpu_write(gpu, REG_A5XX_RBBM_SECVID_TSB_TRUSTED_SIZE, 0x00000000);
+ /* Put the GPU into 64 bit by default */
+ gpu_write(gpu, REG_A5XX_CP_ADDR_MODE_CNTL, 0x1);
+ gpu_write(gpu, REG_A5XX_VSC_ADDR_MODE_CNTL, 0x1);
+ gpu_write(gpu, REG_A5XX_GRAS_ADDR_MODE_CNTL, 0x1);
+ gpu_write(gpu, REG_A5XX_RB_ADDR_MODE_CNTL, 0x1);
+ gpu_write(gpu, REG_A5XX_PC_ADDR_MODE_CNTL, 0x1);
+ gpu_write(gpu, REG_A5XX_HLSQ_ADDR_MODE_CNTL, 0x1);
+ gpu_write(gpu, REG_A5XX_VFD_ADDR_MODE_CNTL, 0x1);
+ gpu_write(gpu, REG_A5XX_VPC_ADDR_MODE_CNTL, 0x1);
+ gpu_write(gpu, REG_A5XX_UCHE_ADDR_MODE_CNTL, 0x1);
+ gpu_write(gpu, REG_A5XX_SP_ADDR_MODE_CNTL, 0x1);
+ gpu_write(gpu, REG_A5XX_TPL1_ADDR_MODE_CNTL, 0x1);
+ gpu_write(gpu, REG_A5XX_RBBM_SECVID_TSB_ADDR_MODE_CNTL, 0x1);
+
ret = adreno_hw_init(gpu);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c
index b23d33622f37..fdbe1a8372f0 100644
--- a/drivers/gpu/drm/msm/msm_iommu.c
+++ b/drivers/gpu/drm/msm/msm_iommu.c
@@ -30,7 +30,7 @@ static int msm_fault_handler(struct iommu_domain *domain, struct device *dev,
struct msm_iommu *iommu = arg;
if (iommu->base.handler)
return iommu->base.handler(iommu->base.arg, iova, flags);
- pr_warn_ratelimited("*** fault: iova=%08lx, flags=%d\n", iova, flags);
+ pr_warn_ratelimited("*** fault: iova=%16lx, flags=%d\n", iova, flags);
return 0;
}
--
2.17.0
^ permalink raw reply related
* [PATCH 06/16] iommu: arm-smmu: Add side-band function for specific PASID callbacks
From: Jordan Crouse @ 2018-05-18 21:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518213500.31595-1-jcrouse@codeaurora.org>
Just allowing a client driver to create and manage a
a private PASID isn't interesting if the client driver doesn't have
enough information about the pagetable to be able to use it. Add a
side band function for arm-smmu that lets the client device register
pasid operations to pass the relevant pagetable information to the
client driver whenever a new PASID is created or destroyed
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
---
drivers/iommu/arm-smmu.c | 40 ++++++++++++++++++++++++++++++++++++++++
include/linux/arm-smmu.h | 18 ++++++++++++++++++
2 files changed, 58 insertions(+)
create mode 100644 include/linux/arm-smmu.h
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 5c7c135bbb44..100797a07be0 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -50,6 +50,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/arm-smmu.h>
#include <linux/amba/bus.h>
@@ -254,6 +255,8 @@ struct arm_smmu_domain {
spinlock_t pasid_lock;
struct list_head pasid_list;
+ const struct arm_smmu_pasid_ops *pasid_ops;
+ void *pasid_data;
};
struct arm_smmu_option_prop {
@@ -296,6 +299,10 @@ static void arm_smmu_mm_detach(struct iommu_domain *domain, struct device *dev,
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct arm_smmu_pasid *node, *obj = NULL;
+ if (smmu_domain->pasid_ops && smmu_domain->pasid_ops->remove_pasid)
+ smmu_domain->pasid_ops->remove_pasid(io_mm->pasid,
+ smmu_domain->pasid_data);
+
spin_lock(&smmu_domain->pasid_lock);
list_for_each_entry(node, &smmu_domain->pasid_list, node) {
if (node->pasid == io_mm->pasid) {
@@ -392,6 +399,26 @@ static int arm_smmu_mm_attach(struct iommu_domain *domain, struct device *dev,
obj->domain = domain;
obj->pasid = io_mm->pasid;
+ if (smmu_domain->pasid_ops && smmu_domain->pasid_ops->install_pasid) {
+ int ret;
+ u64 ttbr;
+
+ if (smmu_domain->cfg.fmt == ARM_SMMU_CTX_FMT_AARCH32_S)
+ ttbr = pgtbl_cfg.arm_v7s_cfg.ttbr;
+ else
+ ttbr = pgtbl_cfg.arm_lpae_s1_cfg.ttbr;
+
+ ret = smmu_domain->pasid_ops->install_pasid(io_mm->pasid, ttbr,
+ smmu_domain->cfg.asid, smmu_domain->pasid_data);
+
+ if (ret) {
+ free_io_pgtable_ops(obj->pgtbl_ops);
+ kfree(obj);
+
+ return ret;
+ }
+ }
+
spin_lock(&smmu_domain->pasid_lock);
list_add_tail(&obj->node, &smmu_domain->pasid_list);
spin_unlock(&smmu_domain->pasid_lock);
@@ -2156,6 +2183,19 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
return 0;
}
+void arm_smmu_add_pasid_ops(struct iommu_domain *domain,
+ const struct arm_smmu_pasid_ops *ops, void *data)
+{
+ struct arm_smmu_domain *smmu_domain;
+
+ if (domain) {
+ smmu_domain = to_smmu_domain(domain);
+ smmu_domain->pasid_ops = ops;
+ smmu_domain->pasid_data = data;
+ }
+}
+EXPORT_SYMBOL_GPL(arm_smmu_add_pasid_ops);
+
struct arm_smmu_match_data {
enum arm_smmu_arch_version version;
enum arm_smmu_implementation model;
diff --git a/include/linux/arm-smmu.h b/include/linux/arm-smmu.h
new file mode 100644
index 000000000000..c14ca52231bf
--- /dev/null
+++ b/include/linux/arm-smmu.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved. */
+
+#ifndef ARM_SMMU_H_
+#define ARM_SMMU_H_
+
+struct iommu_domain;
+
+struct arm_smmu_pasid_ops {
+ int (*install_pasid)(int pasid, u64 ttbr, u32 asid, void *data);
+ void (*remove_pasid)(int pasid, void *data);
+};
+
+
+void arm_smmu_add_pasid_ops(struct iommu_domain *domain,
+ const struct arm_smmu_pasid_ops *ops, void *data);
+
+#endif
--
2.17.0
^ permalink raw reply related
* [PATCH 05/16] iommu: arm-smmu: Add support for private PASIDs
From: Jordan Crouse @ 2018-05-18 21:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518213500.31595-1-jcrouse@codeaurora.org>
Add support for allocating and populating pagetables
indexed by private PASIDs. Each new PASID is allocated a pagetable
with the same parameters and format as the parent domain.
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
---
drivers/iommu/arm-smmu.c | 154 +++++++++++++++++++++++++++++++++++--
drivers/iommu/io-pgtable.h | 10 ++-
2 files changed, 155 insertions(+), 9 deletions(-)
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index d459909877c3..5c7c135bbb44 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -201,7 +201,6 @@ struct arm_smmu_device {
unsigned long va_size;
unsigned long ipa_size;
unsigned long pa_size;
- unsigned long ubs_size;
unsigned long pgsize_bitmap;
u32 num_global_irqs;
@@ -252,6 +251,9 @@ struct arm_smmu_domain {
spinlock_t cb_lock; /* Serialises ATS1* ops and TLB syncs */
u32 attributes;
struct iommu_domain domain;
+
+ spinlock_t pasid_lock;
+ struct list_head pasid_list;
};
struct arm_smmu_option_prop {
@@ -259,6 +261,144 @@ struct arm_smmu_option_prop {
const char *prop;
};
+static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom)
+{
+ return container_of(dom, struct arm_smmu_domain, domain);
+}
+
+struct arm_smmu_pasid {
+ struct iommu_domain *domain;
+ struct io_pgtable_ops *pgtbl_ops;
+ struct list_head node;
+ int pasid;
+};
+
+struct arm_smmu_pasid *arm_smmu_get_pasid(struct arm_smmu_domain *smmu_domain,
+ int pasid)
+{
+ struct arm_smmu_pasid *node, *obj = NULL;
+
+ spin_lock(&smmu_domain->pasid_lock);
+ list_for_each_entry(node, &smmu_domain->pasid_list, node) {
+ if (node->pasid == pasid) {
+ obj = node;
+ break;
+ }
+ }
+ spin_unlock(&smmu_domain->pasid_lock);
+
+ return obj;
+}
+
+static void arm_smmu_mm_detach(struct iommu_domain *domain, struct device *dev,
+ struct io_mm *io_mm, bool unused)
+{
+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+ struct arm_smmu_pasid *node, *obj = NULL;
+
+ spin_lock(&smmu_domain->pasid_lock);
+ list_for_each_entry(node, &smmu_domain->pasid_list, node) {
+ if (node->pasid == io_mm->pasid) {
+ obj = node;
+ list_del(&obj->node);
+ break;
+ }
+ }
+ spin_unlock(&smmu_domain->pasid_lock);
+
+ if (obj)
+ free_io_pgtable_ops(obj->pgtbl_ops);
+
+ kfree(obj);
+}
+
+static size_t arm_smmu_sva_unmap(struct iommu_domain *domain, int pasid,
+ unsigned long iova, size_t size)
+{
+ struct arm_smmu_pasid *obj =
+ arm_smmu_get_pasid(to_smmu_domain(domain), pasid);
+
+ if (!obj)
+ return -ENODEV;
+
+ return obj->pgtbl_ops->unmap(obj->pgtbl_ops, iova, size);
+}
+
+
+static int arm_smmu_sva_map(struct iommu_domain *domain, int pasid,
+ unsigned long iova, phys_addr_t paddr, size_t size, int prot)
+{
+ struct arm_smmu_pasid *obj =
+ arm_smmu_get_pasid(to_smmu_domain(domain), pasid);
+
+ if (!obj)
+ return -ENODEV;
+
+ return obj->pgtbl_ops->map(obj->pgtbl_ops, iova, paddr, size, prot);
+}
+
+static int arm_smmu_mm_attach(struct iommu_domain *domain, struct device *dev,
+ struct io_mm *io_mm, bool unused)
+{
+ struct arm_smmu_pasid *obj;
+ struct io_pgtable_cfg pgtbl_cfg;
+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+ struct arm_smmu_device *smmu = smmu_domain->smmu;
+ enum io_pgtable_fmt fmt;
+ unsigned long ias, oas;
+
+ /* Only allow private pasids */
+ if (io_mm->type != IO_TYPE_PRIVATE || io_mm->mm)
+ return -ENODEV;
+
+ /* Only allow pasid backed tables to be created on S1 domains */
+ if (smmu_domain->stage != ARM_SMMU_DOMAIN_S1)
+ return -ENODEV;
+
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (!obj)
+ return -ENOMEM;
+
+ /* Get the same exact format as the parent domain */
+ ias = smmu->va_size;
+ oas = smmu->ipa_size;
+
+ if (smmu_domain->cfg.fmt == ARM_SMMU_CTX_FMT_AARCH64)
+ fmt = ARM_64_LPAE_S1;
+ else if (smmu_domain->cfg.fmt == ARM_SMMU_CTX_FMT_AARCH32_L) {
+ fmt = ARM_32_LPAE_S1;
+ ias = min(ias, 32UL);
+ oas = min(oas, 40UL);
+ } else {
+ fmt = ARM_V7S;
+ ias = min(ias, 32UL);
+ oas = min(oas, 32UL);
+ }
+
+ pgtbl_cfg = (struct io_pgtable_cfg) {
+ .pgsize_bitmap = smmu->pgsize_bitmap,
+ .ias = ias,
+ .oas = oas,
+ .tlb = NULL,
+ .iommu_dev = smmu->dev
+ };
+
+ obj->pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain);
+ if (!obj->pgtbl_ops) {
+ kfree(obj);
+ return -ENOMEM;
+ }
+
+ obj->domain = domain;
+ obj->pasid = io_mm->pasid;
+
+ spin_lock(&smmu_domain->pasid_lock);
+ list_add_tail(&obj->node, &smmu_domain->pasid_list);
+ spin_unlock(&smmu_domain->pasid_lock);
+
+ return 0;
+}
+
static atomic_t cavium_smmu_context_count = ATOMIC_INIT(0);
static bool using_legacy_binding, using_generic_binding;
@@ -268,11 +408,6 @@ static struct arm_smmu_option_prop arm_smmu_options[] = {
{ 0, NULL},
};
-static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom)
-{
- return container_of(dom, struct arm_smmu_domain, domain);
-}
-
static void parse_driver_options(struct arm_smmu_device *smmu)
{
int i = 0;
@@ -1055,6 +1190,9 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
mutex_init(&smmu_domain->init_mutex);
spin_lock_init(&smmu_domain->cb_lock);
+ spin_lock_init(&smmu_domain->pasid_lock);
+ INIT_LIST_HEAD(&smmu_domain->pasid_list);
+
return &smmu_domain->domain;
}
@@ -1694,6 +1832,10 @@ static struct iommu_ops arm_smmu_ops = {
.of_xlate = arm_smmu_of_xlate,
.get_resv_regions = arm_smmu_get_resv_regions,
.put_resv_regions = arm_smmu_put_resv_regions,
+ .mm_attach = arm_smmu_mm_attach,
+ .sva_map = arm_smmu_sva_map,
+ .sva_unmap = arm_smmu_sva_unmap,
+ .mm_detach = arm_smmu_mm_detach,
.pgsize_bitmap = -1UL, /* Restricted during device attach */
};
diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h
index fd9f0fc4eb60..69fcee763446 100644
--- a/drivers/iommu/io-pgtable.h
+++ b/drivers/iommu/io-pgtable.h
@@ -173,18 +173,22 @@ struct io_pgtable {
static inline void io_pgtable_tlb_flush_all(struct io_pgtable *iop)
{
- iop->cfg.tlb->tlb_flush_all(iop->cookie);
+ if (iop->cfg.tlb)
+ iop->cfg.tlb->tlb_flush_all(iop->cookie);
}
static inline void io_pgtable_tlb_add_flush(struct io_pgtable *iop,
unsigned long iova, size_t size, size_t granule, bool leaf)
{
- iop->cfg.tlb->tlb_add_flush(iova, size, granule, leaf, iop->cookie);
+ if (iop->cfg.tlb)
+ iop->cfg.tlb->tlb_add_flush(iova, size, granule, leaf,
+ iop->cookie);
}
static inline void io_pgtable_tlb_sync(struct io_pgtable *iop)
{
- iop->cfg.tlb->tlb_sync(iop->cookie);
+ if (iop->cfg.tlb)
+ iop->cfg.tlb->tlb_sync(iop->cookie);
}
/**
--
2.17.0
^ permalink raw reply related
* [PATCH 04/16] iommu: sva: Add support for private PASIDs
From: Jordan Crouse @ 2018-05-18 21:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518213500.31595-1-jcrouse@codeaurora.org>
Some older SMMU implementations that do not have a fully featured
hardware PASID features have alternate workarounds for using multiple
pagetables. For example, MSM GPUs have logic to automatically switch the
user pagetable from hardware by writing the context bank directly.
Support private PASIDs by creating a new io-pgtable instance map it
to a PASID and provide the APIs for drivers to populate it manually.
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
---
drivers/iommu/iommu-sva.c | 139 ++++++++++++++++++++++++++++++++++++--
drivers/iommu/iommu.c | 66 +++++++++++++-----
include/linux/iommu.h | 74 ++++++++++++++++++--
3 files changed, 250 insertions(+), 29 deletions(-)
diff --git a/drivers/iommu/iommu-sva.c b/drivers/iommu/iommu-sva.c
index e98b994c15f1..26f0da9692d4 100644
--- a/drivers/iommu/iommu-sva.c
+++ b/drivers/iommu/iommu-sva.c
@@ -156,6 +156,7 @@ io_mm_alloc(struct iommu_domain *domain, struct device *dev,
mmgrab(mm);
io_mm->flags = flags;
+ io_mm->type = IO_TYPE_SHARED;
io_mm->mm = mm;
io_mm->notifier.ops = &iommu_mmu_notifier;
io_mm->release = domain->ops->mm_free;
@@ -544,13 +545,10 @@ int iommu_sva_device_init(struct device *dev, unsigned long features,
unsigned int max_pasid,
iommu_mm_exit_handler_t mm_exit)
{
- int ret;
+ int ret = 0;
struct iommu_sva_param *param;
struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
- if (!domain || !domain->ops->sva_device_init)
- return -ENODEV;
-
if (features & ~IOMMU_SVA_FEAT_IOPF)
return -EINVAL;
@@ -576,9 +574,12 @@ int iommu_sva_device_init(struct device *dev, unsigned long features,
* IOMMU driver updates the limits depending on the IOMMU and device
* capabilities.
*/
- ret = domain->ops->sva_device_init(dev, param);
- if (ret)
- goto err_free_param;
+
+ if (domain && domain->ops->sva_device_init) {
+ ret = domain->ops->sva_device_init(dev, param);
+ if (ret)
+ goto err_free_param;
+ }
mutex_lock(&dev->iommu_param->lock);
if (dev->iommu_param->sva_param)
@@ -790,3 +791,127 @@ struct mm_struct *iommu_sva_find(int pasid)
return mm;
}
EXPORT_SYMBOL_GPL(iommu_sva_find);
+
+int iommu_sva_alloc_pasid(struct iommu_domain *domain, struct device *dev)
+{
+ int ret, pasid;
+ struct io_mm *io_mm;
+ struct iommu_sva_param *param = dev->iommu_param->sva_param;
+
+ if (!domain->ops->mm_attach || !domain->ops->mm_detach)
+ return -ENODEV;
+
+ if (domain->ops->mm_alloc)
+ io_mm = domain->ops->mm_alloc(domain, NULL, 0);
+ else
+ io_mm = kzalloc(sizeof(*io_mm), GFP_KERNEL);
+
+ if (IS_ERR(io_mm))
+ return PTR_ERR(io_mm);
+ if (!io_mm)
+ return -ENOMEM;
+
+ io_mm->domain = domain;
+ io_mm->type = IO_TYPE_PRIVATE;
+
+ idr_preload(GFP_KERNEL);
+ spin_lock(&iommu_sva_lock);
+ pasid = idr_alloc_cyclic(&iommu_pasid_idr, io_mm, param->min_pasid,
+ param->max_pasid + 1, GFP_ATOMIC);
+ io_mm->pasid = pasid;
+ spin_unlock(&iommu_sva_lock);
+ idr_preload_end();
+
+ if (pasid < 0) {
+ kfree(io_mm);
+ return pasid;
+ }
+
+ ret = domain->ops->mm_attach(domain, dev, io_mm, false);
+ if (!ret)
+ return pasid;
+
+ spin_lock(&iommu_sva_lock);
+ idr_remove(&iommu_pasid_idr, io_mm->pasid);
+ spin_unlock(&iommu_sva_lock);
+
+ if (domain->ops->mm_free)
+ domain->ops->mm_free(io_mm);
+ else
+ kfree(io_mm);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iommu_sva_alloc_pasid);
+
+static struct io_mm *get_io_mm(int pasid)
+{
+ struct io_mm *io_mm;
+
+ spin_lock(&iommu_sva_lock);
+ io_mm = idr_find(&iommu_pasid_idr, pasid);
+ spin_unlock(&iommu_sva_lock);
+
+ return io_mm;
+}
+
+int iommu_sva_map(int pasid, unsigned long iova,
+ phys_addr_t paddr, size_t size, int prot)
+{
+ struct io_mm *io_mm = get_io_mm(pasid);
+
+ if (!io_mm || io_mm->type != IO_TYPE_PRIVATE)
+ return -ENODEV;
+
+ return __iommu_map(io_mm->domain, &pasid, iova, paddr, size, prot);
+}
+EXPORT_SYMBOL_GPL(iommu_sva_map);
+
+size_t iommu_sva_map_sg(int pasid, unsigned long iova, struct scatterlist *sg,
+ unsigned int nents, int prot)
+{
+ struct io_mm *io_mm = get_io_mm(pasid);
+ struct iommu_domain *domain;
+
+ if (!io_mm || io_mm->type != IO_TYPE_PRIVATE)
+ return -ENODEV;
+
+ domain = io_mm->domain;
+
+ return domain->ops->map_sg(domain, &pasid, iova, sg, nents, prot);
+}
+EXPORT_SYMBOL_GPL(iommu_sva_map_sg);
+
+size_t iommu_sva_unmap(int pasid, unsigned long iova, size_t size)
+{
+ struct io_mm *io_mm = get_io_mm(pasid);
+
+ if (!io_mm || io_mm->type != IO_TYPE_PRIVATE)
+ return -ENODEV;
+
+ return __iommu_unmap(io_mm->domain, &pasid, iova, size, false);
+}
+EXPORT_SYMBOL_GPL(iommu_sva_unmap);
+
+void iommu_sva_free_pasid(int pasid, struct device *dev)
+{
+ struct io_mm *io_mm = get_io_mm(pasid);
+ struct iommu_domain *domain;
+
+ if (!io_mm || io_mm->type != IO_TYPE_PRIVATE)
+ return;
+
+ domain = io_mm->domain;
+
+ domain->ops->mm_detach(domain, dev, io_mm, false);
+
+ spin_lock(&iommu_sva_lock);
+ idr_remove(&iommu_pasid_idr, io_mm->pasid);
+ spin_unlock(&iommu_sva_lock);
+
+ if (domain->ops->mm_free)
+ domain->ops->mm_free(io_mm);
+ else
+ kfree(io_mm);
+}
+EXPORT_SYMBOL_GPL(iommu_sva_free_pasid);
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 13f705df0725..0ba3d27f2300 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -1792,7 +1792,7 @@ static size_t iommu_pgsize(struct iommu_domain *domain,
return pgsize;
}
-int iommu_map(struct iommu_domain *domain, unsigned long iova,
+int __iommu_map(struct iommu_domain *domain, int *pasid, unsigned long iova,
phys_addr_t paddr, size_t size, int prot)
{
unsigned long orig_iova = iova;
@@ -1801,10 +1801,17 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t orig_paddr = paddr;
int ret = 0;
- if (unlikely(domain->ops->map == NULL ||
- domain->pgsize_bitmap == 0UL))
+ if (unlikely(domain->pgsize_bitmap == 0UL))
return -ENODEV;
+ if (pasid) {
+ if (unlikely(domain->ops->sva_map == NULL))
+ return -ENODEV;
+ } else {
+ if (unlikely(domain->ops->map == NULL))
+ return -ENODEV;
+ }
+
if (unlikely(!(domain->type & __IOMMU_DOMAIN_PAGING)))
return -EINVAL;
@@ -1830,7 +1837,13 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova,
pr_debug("mapping: iova 0x%lx pa %pa pgsize 0x%zx\n",
iova, &paddr, pgsize);
- ret = domain->ops->map(domain, iova, paddr, pgsize, prot);
+ if (pasid)
+ ret = domain->ops->sva_map(domain, *pasid, iova, paddr,
+ pgsize, prot);
+ else
+ ret = domain->ops->map(domain, iova, paddr, pgsize,
+ prot);
+
if (ret)
break;
@@ -1841,16 +1854,23 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova,
/* unroll mapping in case something went wrong */
if (ret)
- iommu_unmap(domain, orig_iova, orig_size - size);
+ __iommu_unmap(domain, pasid, orig_iova, orig_size - size,
+ pasid ? false : true);
else
trace_map(orig_iova, orig_paddr, orig_size);
return ret;
}
+
+int iommu_map(struct iommu_domain *domain, unsigned long iova,
+ phys_addr_t paddr, size_t size, int prot)
+{
+ return __iommu_map(domain, NULL, iova, paddr, size, prot);
+}
EXPORT_SYMBOL_GPL(iommu_map);
-static size_t __iommu_unmap(struct iommu_domain *domain,
- unsigned long iova, size_t size,
+size_t __iommu_unmap(struct iommu_domain *domain,
+ int *pasid, unsigned long iova, size_t size,
bool sync)
{
const struct iommu_ops *ops = domain->ops;
@@ -1858,9 +1878,16 @@ static size_t __iommu_unmap(struct iommu_domain *domain,
unsigned long orig_iova = iova;
unsigned int min_pagesz;
- if (unlikely(ops->unmap == NULL ||
- domain->pgsize_bitmap == 0UL))
- return 0;
+ if (unlikely(domain->pgsize_bitmap == 0UL))
+ return -0;
+
+ if (pasid) {
+ if (unlikely(domain->ops->sva_unmap == NULL))
+ return 0;
+ } else {
+ if (unlikely(domain->ops->unmap == NULL))
+ return 0;
+ }
if (unlikely(!(domain->type & __IOMMU_DOMAIN_PAGING)))
return 0;
@@ -1888,7 +1915,12 @@ static size_t __iommu_unmap(struct iommu_domain *domain,
while (unmapped < size) {
size_t pgsize = iommu_pgsize(domain, iova, size - unmapped);
- unmapped_page = ops->unmap(domain, iova, pgsize);
+ if (pasid)
+ unmapped_page = ops->sva_unmap(domain, *pasid, iova,
+ pgsize);
+ else
+ unmapped_page = ops->unmap(domain, iova, pgsize);
+
if (!unmapped_page)
break;
@@ -1912,19 +1944,20 @@ static size_t __iommu_unmap(struct iommu_domain *domain,
size_t iommu_unmap(struct iommu_domain *domain,
unsigned long iova, size_t size)
{
- return __iommu_unmap(domain, iova, size, true);
+ return __iommu_unmap(domain, NULL, iova, size, true);
}
EXPORT_SYMBOL_GPL(iommu_unmap);
size_t iommu_unmap_fast(struct iommu_domain *domain,
unsigned long iova, size_t size)
{
- return __iommu_unmap(domain, iova, size, false);
+ return __iommu_unmap(domain, NULL, iova, size, false);
}
EXPORT_SYMBOL_GPL(iommu_unmap_fast);
-size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
- struct scatterlist *sg, unsigned int nents, int prot)
+size_t default_iommu_map_sg(struct iommu_domain *domain, int *pasid,
+ unsigned long iova, struct scatterlist *sg,
+ unsigned int nents, int prot)
{
struct scatterlist *s;
size_t mapped = 0;
@@ -1948,7 +1981,8 @@ size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
if (!IS_ALIGNED(s->offset, min_pagesz))
goto out_err;
- ret = iommu_map(domain, iova + mapped, phys, s->length, prot);
+ ret = __iommu_map(domain, pasid, iova + mapped, phys, s->length,
+ prot);
if (ret)
goto out_err;
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 366254e4b07f..3d72d636c13d 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -108,7 +108,13 @@ struct iommu_domain {
struct list_head mm_list;
};
+enum iommu_io_type {
+ IO_TYPE_SHARED,
+ IO_TYPE_PRIVATE,
+};
+
struct io_mm {
+ enum iommu_io_type type;
int pasid;
/* IOMMU_SVA_FEAT_* */
unsigned long flags;
@@ -123,6 +129,9 @@ struct io_mm {
void (*release)(struct io_mm *io_mm);
/* For postponed release */
struct rcu_head rcu;
+
+ /* This is used by private entries */
+ struct iommu_domain *domain;
};
enum iommu_cap {
@@ -315,8 +324,9 @@ struct iommu_ops {
phys_addr_t paddr, size_t size, int prot);
size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,
size_t size);
- size_t (*map_sg)(struct iommu_domain *domain, unsigned long iova,
- struct scatterlist *sg, unsigned int nents, int prot);
+ size_t (*map_sg)(struct iommu_domain *domain, int *pasid,
+ unsigned long iova, struct scatterlist *sg,
+ unsigned int nents, int prot);
void (*flush_iotlb_all)(struct iommu_domain *domain);
void (*iotlb_range_add)(struct iommu_domain *domain,
unsigned long iova, size_t size);
@@ -358,6 +368,12 @@ struct iommu_ops {
struct device *dev, struct tlb_invalidate_info *inv_info);
int (*page_response)(struct device *dev, struct page_response_msg *msg);
+ int (*sva_map)(struct iommu_domain *domain, int pasid,
+ unsigned long iova, phys_addr_t paddr, size_t size,
+ int prot);
+ size_t (*sva_unmap)(struct iommu_domain *domain, int pasid,
+ unsigned long iova, size_t size);
+
unsigned long pgsize_bitmap;
};
@@ -548,9 +564,9 @@ extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova,
size_t size);
extern size_t iommu_unmap_fast(struct iommu_domain *domain,
unsigned long iova, size_t size);
-extern size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
- struct scatterlist *sg,unsigned int nents,
- int prot);
+extern size_t default_iommu_map_sg(struct iommu_domain *domain, int *pasid,
+ unsigned long iova, struct scatterlist *sg,
+ unsigned int nents, int prot);
extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova);
extern void iommu_set_fault_handler(struct iommu_domain *domain,
iommu_fault_handler_t handler, void *token);
@@ -636,7 +652,7 @@ static inline size_t iommu_map_sg(struct iommu_domain *domain,
unsigned long iova, struct scatterlist *sg,
unsigned int nents, int prot)
{
- return domain->ops->map_sg(domain, iova, sg, nents, prot);
+ return domain->ops->map_sg(domain, NULL, iova, sg, nents, prot);
}
/* PCI device grouping function */
@@ -676,6 +692,14 @@ extern int iommu_sva_bind_device(struct device *dev, struct mm_struct *mm,
int *pasid, unsigned long flags, void *drvdata);
extern int iommu_sva_unbind_device(struct device *dev, int pasid);
+/* Common map and unmap functions */
+extern int __iommu_map(struct iommu_domain *domain, int *pasid,
+ unsigned long iova, phys_addr_t paddr, size_t size, int prot);
+
+extern size_t __iommu_unmap(struct iommu_domain *domain,
+ int *pasid, unsigned long iova, size_t size,
+ bool sync);
+
#else /* CONFIG_IOMMU_API */
struct iommu_ops {};
@@ -1027,6 +1051,16 @@ extern int __iommu_sva_unbind_device(struct device *dev, int pasid);
extern void __iommu_sva_unbind_dev_all(struct device *dev);
extern struct mm_struct *iommu_sva_find(int pasid);
+
+extern int iommu_sva_alloc_pasid(struct iommu_domain *domain,
+ struct device *dev);
+extern int iommu_sva_map(int pasid, unsigned long iova, phys_addr_t physaddr,
+ size_t size, int prot);
+extern size_t iommu_sva_map_sg(int pasid, unsigned long iova,
+ struct scatterlist *sg, unsigned int nents, int prot);
+extern size_t iommu_sva_unmap(int pasid, unsigned long iova, size_t size);
+extern void iommu_sva_free_pasid(int pasid, struct device *dev);
+
#else /* CONFIG_IOMMU_SVA */
static inline int iommu_sva_device_init(struct device *dev,
unsigned long features,
@@ -1061,6 +1095,34 @@ static inline struct mm_struct *iommu_sva_find(int pasid)
{
return NULL;
}
+
+static inline int iommu_sva_alloc_pasid(struct iommu_domain *domain,
+ struct device *dev)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int iommu_sva_map(int pasid, unsigned long iova,
+ phys_addr_t physaddr, size_t size, int prot)
+{
+ return -ENODEV;
+}
+
+
+static inline size_t iommu_sva_map_sg(int pasid, unsigned long iova,
+ struct scatterlist *sg, unsigned int nents, int prot)
+{
+ return 0;
+}
+
+static inline size_t iommu_sva_unmap(int pasid, unsigned long iova, size_t size)
+{
+ return size;
+}
+
+static inline void iommu_sva_free_pasid(int pasid, struct device *dev) { }
+
+
#endif /* CONFIG_IOMMU_SVA */
#ifdef CONFIG_IOMMU_PAGE_FAULT
--
2.17.0
^ permalink raw reply related
* [PATCH 03/16] iommu/io-pgtable-arm: Remove ttbr[1] from io_pgtbl_cfg
From: Jordan Crouse @ 2018-05-18 21:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518213500.31595-1-jcrouse@codeaurora.org>
Now that we have a working example of an ARM driver that implements
split pagetables completely in the client driver it is apparent that
we don't need to store an extra ttbr value in the io_pgtbl_cfg struct
that will never get used.
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
---
drivers/iommu/arm-smmu-v3-context.c | 2 +-
drivers/iommu/arm-smmu.c | 8 ++++----
drivers/iommu/io-pgtable-arm-v7s.c | 3 +--
drivers/iommu/io-pgtable-arm.c | 5 ++---
drivers/iommu/io-pgtable.h | 4 ++--
drivers/iommu/ipmmu-vmsa.c | 2 +-
drivers/iommu/msm_iommu.c | 4 ++--
drivers/iommu/mtk_iommu.c | 4 ++--
drivers/iommu/qcom_iommu.c | 3 +--
9 files changed, 16 insertions(+), 19 deletions(-)
diff --git a/drivers/iommu/arm-smmu-v3-context.c b/drivers/iommu/arm-smmu-v3-context.c
index 22e7b80a7682..d23e0092f917 100644
--- a/drivers/iommu/arm-smmu-v3-context.c
+++ b/drivers/iommu/arm-smmu-v3-context.c
@@ -522,7 +522,7 @@ arm_smmu_alloc_priv_cd(struct iommu_pasid_table_ops *ops,
switch (fmt) {
case ARM_64_LPAE_S1:
- cd->ttbr = cfg->arm_lpae_s1_cfg.ttbr[0];
+ cd->ttbr = cfg->arm_lpae_s1_cfg.ttbr;
cd->tcr = cfg->arm_lpae_s1_cfg.tcr;
cd->mair = cfg->arm_lpae_s1_cfg.mair[0];
break;
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 3568e8b073ec..d459909877c3 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -644,7 +644,7 @@ static void arm_smmu_init_ttbr1(struct arm_smmu_domain *smmu_domain,
cb->split_table_mask = (1ULL << 48);
}
- cb->ttbr[1] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
+ cb->ttbr[1] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr;
cb->ttbr[1] |= (u64)cfg->asid << TTBRn_ASID_SHIFT;
}
@@ -675,10 +675,10 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
/* TTBRs */
if (stage1) {
if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
- cb->ttbr[0] = pgtbl_cfg->arm_v7s_cfg.ttbr[0];
- cb->ttbr[1] = pgtbl_cfg->arm_v7s_cfg.ttbr[1];
+ cb->ttbr[0] = pgtbl_cfg->arm_v7s_cfg.ttbr;
+ cb->ttbr[1] = 0;
} else {
- cb->ttbr[0] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
+ cb->ttbr[0] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr;
cb->ttbr[0] |= (u64)cfg->asid << TTBRn_ASID_SHIFT;
/*
diff --git a/drivers/iommu/io-pgtable-arm-v7s.c b/drivers/iommu/io-pgtable-arm-v7s.c
index 10e4a3d11c02..37d607ac5153 100644
--- a/drivers/iommu/io-pgtable-arm-v7s.c
+++ b/drivers/iommu/io-pgtable-arm-v7s.c
@@ -767,11 +767,10 @@ static struct io_pgtable *arm_v7s_alloc_pgtable(struct io_pgtable_cfg *cfg,
wmb();
/* TTBRs */
- cfg->arm_v7s_cfg.ttbr[0] = virt_to_phys(data->pgd) |
+ cfg->arm_v7s_cfg.ttbr = virt_to_phys(data->pgd) |
ARM_V7S_TTBR_S | ARM_V7S_TTBR_NOS |
ARM_V7S_TTBR_IRGN_ATTR(ARM_V7S_RGN_WBWA) |
ARM_V7S_TTBR_ORGN_ATTR(ARM_V7S_RGN_WBWA);
- cfg->arm_v7s_cfg.ttbr[1] = 0;
return &data->iop;
out_free_data:
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 920d9faa2a76..5bba30d901b4 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -793,9 +793,8 @@ arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie)
/* Ensure the empty pgd is visible before any actual TTBR write */
wmb();
- /* TTBRs */
- cfg->arm_lpae_s1_cfg.ttbr[0] = virt_to_phys(data->pgd);
- cfg->arm_lpae_s1_cfg.ttbr[1] = 0;
+ /* TTBR */
+ cfg->arm_lpae_s1_cfg.ttbr = virt_to_phys(data->pgd);
return &data->iop;
out_free_data:
diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h
index 2df79093cad9..fd9f0fc4eb60 100644
--- a/drivers/iommu/io-pgtable.h
+++ b/drivers/iommu/io-pgtable.h
@@ -87,7 +87,7 @@ struct io_pgtable_cfg {
/* Low-level data specific to the table format */
union {
struct {
- u64 ttbr[2];
+ u64 ttbr;
u64 tcr;
u64 mair[2];
} arm_lpae_s1_cfg;
@@ -98,7 +98,7 @@ struct io_pgtable_cfg {
} arm_lpae_s2_cfg;
struct {
- u32 ttbr[2];
+ u32 ttbr;
u32 tcr;
u32 nmrr;
u32 prrr;
diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c
index 40ae6e87cb88..7cdaa0fef85a 100644
--- a/drivers/iommu/ipmmu-vmsa.c
+++ b/drivers/iommu/ipmmu-vmsa.c
@@ -447,7 +447,7 @@ static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain)
}
/* TTBR0 */
- ttbr = domain->cfg.arm_lpae_s1_cfg.ttbr[0];
+ ttbr = domain->cfg.arm_lpae_s1_cfg.ttbr;
ipmmu_ctx_write_root(domain, IMTTLBR0, ttbr);
ipmmu_ctx_write_root(domain, IMTTUBR0, ttbr >> 32);
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index 0d3350463a3f..ef323301c574 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -281,8 +281,8 @@ static void __program_context(void __iomem *base, int ctx,
SET_V2PCFG(base, ctx, 0x3);
SET_TTBCR(base, ctx, priv->cfg.arm_v7s_cfg.tcr);
- SET_TTBR0(base, ctx, priv->cfg.arm_v7s_cfg.ttbr[0]);
- SET_TTBR1(base, ctx, priv->cfg.arm_v7s_cfg.ttbr[1]);
+ SET_TTBR0(base, ctx, priv->cfg.arm_v7s_cfg.ttbr);
+ SET_TTBR1(base, ctx, 0);
/* Set prrr and nmrr */
SET_PRRR(base, ctx, priv->cfg.arm_v7s_cfg.prrr);
diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
index f2832a10fcea..db710d99fc5f 100644
--- a/drivers/iommu/mtk_iommu.c
+++ b/drivers/iommu/mtk_iommu.c
@@ -344,7 +344,7 @@ static int mtk_iommu_attach_device(struct iommu_domain *domain,
/* Update the pgtable base address register of the M4U HW */
if (!data->m4u_dom) {
data->m4u_dom = dom;
- writel(dom->cfg.arm_v7s_cfg.ttbr[0],
+ writel(dom->cfg.arm_v7s_cfg.ttbr,
data->base + REG_MMU_PT_BASE_ADDR);
}
@@ -725,7 +725,7 @@ static int __maybe_unused mtk_iommu_resume(struct device *dev)
writel_relaxed(reg->int_main_control, base + REG_MMU_INT_MAIN_CONTROL);
writel_relaxed(reg->ivrp_paddr, base + REG_MMU_IVRP_PADDR);
if (data->m4u_dom)
- writel(data->m4u_dom->cfg.arm_v7s_cfg.ttbr[0],
+ writel(data->m4u_dom->cfg.arm_v7s_cfg.ttbr,
base + REG_MMU_PT_BASE_ADDR);
return 0;
}
diff --git a/drivers/iommu/qcom_iommu.c b/drivers/iommu/qcom_iommu.c
index 65b9c99707f8..b0f5fd5b33c0 100644
--- a/drivers/iommu/qcom_iommu.c
+++ b/drivers/iommu/qcom_iommu.c
@@ -257,10 +257,9 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain,
/* TTBRs */
iommu_writeq(ctx, ARM_SMMU_CB_TTBR0,
- pgtbl_cfg.arm_lpae_s1_cfg.ttbr[0] |
+ pgtbl_cfg.arm_lpae_s1_cfg.ttbr |
((u64)ctx->asid << TTBRn_ASID_SHIFT));
iommu_writeq(ctx, ARM_SMMU_CB_TTBR1,
- pgtbl_cfg.arm_lpae_s1_cfg.ttbr[1] |
((u64)ctx->asid << TTBRn_ASID_SHIFT));
/* TTBCR */
--
2.17.0
^ permalink raw reply related
* [PATCH 02/16] iommu/arm-smmu: Add split pagetable support for arm-smmu-v2
From: Jordan Crouse @ 2018-05-18 21:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518213500.31595-1-jcrouse@codeaurora.org>
Add support for a split pagetable (TTBR0/TTBR1) scheme for
arm-smmu-v2. If split pagetables are enabled, create a
pagetable for TTBR1 and set up the sign extension bit so
that all IOVAs with that bit set are mapped and translated
from the TTBR1 pagetable.
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
---
drivers/iommu/arm-smmu-regs.h | 18 ++++
drivers/iommu/arm-smmu.c | 148 +++++++++++++++++++++++++++++----
drivers/iommu/io-pgtable-arm.c | 3 +-
3 files changed, 153 insertions(+), 16 deletions(-)
diff --git a/drivers/iommu/arm-smmu-regs.h b/drivers/iommu/arm-smmu-regs.h
index a1226e4ab5f8..56f97093f46a 100644
--- a/drivers/iommu/arm-smmu-regs.h
+++ b/drivers/iommu/arm-smmu-regs.h
@@ -193,7 +193,25 @@ enum arm_smmu_s2cr_privcfg {
#define RESUME_RETRY (0 << 0)
#define RESUME_TERMINATE (1 << 0)
+#define TTBCR_EPD1 (1 << 23)
+#define TTBCR_T1SZ_SHIFT 16
+#define TTBCR_IRGN1_SHIFT 24
+#define TTBCR_ORGN1_SHIFT 26
+#define TTBCR_RGN_WBWA 1
+#define TTBCR_SH1_SHIFT 28
+#define TTBCR_SH_IS 3
+
+#define TTBCR_TG1_16K (1 << 30)
+#define TTBCR_TG1_4K (2 << 30)
+#define TTBCR_TG1_64K (3 << 30)
+
#define TTBCR2_SEP_SHIFT 15
+#define TTBCR2_SEP_31 (0x0 << TTBCR2_SEP_SHIFT)
+#define TTBCR2_SEP_35 (0x1 << TTBCR2_SEP_SHIFT)
+#define TTBCR2_SEP_39 (0x2 << TTBCR2_SEP_SHIFT)
+#define TTBCR2_SEP_41 (0x3 << TTBCR2_SEP_SHIFT)
+#define TTBCR2_SEP_43 (0x4 << TTBCR2_SEP_SHIFT)
+#define TTBCR2_SEP_47 (0x5 << TTBCR2_SEP_SHIFT)
#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT)
#define TTBCR2_AS (1 << 4)
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 69e7c60792a8..3568e8b073ec 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -143,6 +143,7 @@ struct arm_smmu_cb {
u32 tcr[2];
u32 mair[2];
struct arm_smmu_cfg *cfg;
+ u64 split_table_mask;
};
struct arm_smmu_master_cfg {
@@ -200,6 +201,7 @@ struct arm_smmu_device {
unsigned long va_size;
unsigned long ipa_size;
unsigned long pa_size;
+ unsigned long ubs_size;
unsigned long pgsize_bitmap;
u32 num_global_irqs;
@@ -242,12 +244,13 @@ enum arm_smmu_domain_stage {
struct arm_smmu_domain {
struct arm_smmu_device *smmu;
- struct io_pgtable_ops *pgtbl_ops;
+ struct io_pgtable_ops *pgtbl_ops[2];
const struct iommu_gather_ops *tlb_ops;
struct arm_smmu_cfg cfg;
enum arm_smmu_domain_stage stage;
struct mutex init_mutex; /* Protects smmu pointer */
spinlock_t cb_lock; /* Serialises ATS1* ops and TLB syncs */
+ u32 attributes;
struct iommu_domain domain;
};
@@ -582,6 +585,69 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev)
return IRQ_HANDLED;
}
+static void arm_smmu_init_ttbr1(struct arm_smmu_domain *smmu_domain,
+ struct io_pgtable_cfg *pgtbl_cfg)
+{
+ struct arm_smmu_device *smmu = smmu_domain->smmu;
+ struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
+ struct arm_smmu_cb *cb = &smmu_domain->smmu->cbs[cfg->cbndx];
+ int pgsize = 1 << __ffs(pgtbl_cfg->pgsize_bitmap);
+
+ /* Enable speculative walks through the TTBR1 */
+ cb->tcr[0] &= ~TTBCR_EPD1;
+
+ cb->tcr[0] |= TTBCR_SH_IS << TTBCR_SH1_SHIFT;
+ cb->tcr[0] |= TTBCR_RGN_WBWA << TTBCR_IRGN1_SHIFT;
+ cb->tcr[0] |= TTBCR_RGN_WBWA << TTBCR_ORGN1_SHIFT;
+
+ switch (pgsize) {
+ case SZ_4K:
+ cb->tcr[0] |= TTBCR_TG1_4K;
+ break;
+ case SZ_16K:
+ cb->tcr[0] |= TTBCR_TG1_16K;
+ break;
+ case SZ_64K:
+ cb->tcr[0] |= TTBCR_TG1_64K;
+ break;
+ }
+
+ cb->tcr[0] |= (64ULL - smmu->va_size) << TTBCR_T1SZ_SHIFT;
+
+ /* Clear the existing SEP configuration */
+ cb->tcr[1] &= ~TTBCR2_SEP_UPSTREAM;
+
+ /* Set up the sign extend bit */
+ switch (smmu->va_size) {
+ case 32:
+ cb->tcr[1] |= TTBCR2_SEP_31;
+ cb->split_table_mask = (1ULL << 31);
+ break;
+ case 36:
+ cb->tcr[1] |= TTBCR2_SEP_35;
+ cb->split_table_mask = (1ULL << 35);
+ break;
+ case 40:
+ cb->tcr[1] |= TTBCR2_SEP_39;
+ cb->split_table_mask = (1ULL << 39);
+ break;
+ case 42:
+ cb->tcr[1] |= TTBCR2_SEP_41;
+ cb->split_table_mask = (1ULL << 41);
+ break;
+ case 44:
+ cb->tcr[1] |= TTBCR2_SEP_43;
+ cb->split_table_mask = (1ULL << 43);
+ break;
+ case 48:
+ cb->tcr[1] |= TTBCR2_SEP_UPSTREAM;
+ cb->split_table_mask = (1ULL << 48);
+ }
+
+ cb->ttbr[1] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
+ cb->ttbr[1] |= (u64)cfg->asid << TTBRn_ASID_SHIFT;
+}
+
static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
struct io_pgtable_cfg *pgtbl_cfg)
{
@@ -614,8 +680,12 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
} else {
cb->ttbr[0] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
cb->ttbr[0] |= (u64)cfg->asid << TTBRn_ASID_SHIFT;
- cb->ttbr[1] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1];
- cb->ttbr[1] |= (u64)cfg->asid << TTBRn_ASID_SHIFT;
+
+ /*
+ * Set TTBR1 to empty by default - it will get
+ * programmed later if it is enabled
+ */
+ cb->ttbr[1] = (u64)cfg->asid << TTBRn_ASID_SHIFT;
}
} else {
cb->ttbr[0] = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
@@ -724,11 +794,13 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
{
int irq, start, ret = 0;
unsigned long ias, oas;
- struct io_pgtable_ops *pgtbl_ops;
+ struct io_pgtable_ops *pgtbl_ops[2];
struct io_pgtable_cfg pgtbl_cfg;
enum io_pgtable_fmt fmt;
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
+ bool split_tables =
+ (smmu_domain->attributes & (1 << DOMAIN_ATTR_SPLIT_TABLES));
mutex_lock(&smmu_domain->init_mutex);
if (smmu_domain->smmu)
@@ -758,8 +830,11 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
*
* Note that you can't actually request stage-2 mappings.
*/
- if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1))
+ if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1)) {
smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
+ /* FIXME: fail instead? */
+ split_tables = false;
+ }
if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2))
smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
@@ -776,8 +851,11 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
if (IS_ENABLED(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) &&
!IS_ENABLED(CONFIG_64BIT) && !IS_ENABLED(CONFIG_ARM_LPAE) &&
(smmu->features & ARM_SMMU_FEAT_FMT_AARCH32_S) &&
- (smmu_domain->stage == ARM_SMMU_DOMAIN_S1))
+ (smmu_domain->stage == ARM_SMMU_DOMAIN_S1)) {
+ /* FIXME: fail instead? */
+ split_tables = false;
cfg->fmt = ARM_SMMU_CTX_FMT_AARCH32_S;
+ }
if ((IS_ENABLED(CONFIG_64BIT) || cfg->fmt == ARM_SMMU_CTX_FMT_NONE) &&
(smmu->features & (ARM_SMMU_FEAT_FMT_AARCH64_64K |
ARM_SMMU_FEAT_FMT_AARCH64_16K |
@@ -864,8 +942,8 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
pgtbl_cfg.quirks = IO_PGTABLE_QUIRK_NO_DMA;
smmu_domain->smmu = smmu;
- pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain);
- if (!pgtbl_ops) {
+ pgtbl_ops[0] = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain);
+ if (!pgtbl_ops[0]) {
ret = -ENOMEM;
goto out_clear_smmu;
}
@@ -877,6 +955,22 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
/* Initialise the context bank with our page table cfg */
arm_smmu_init_context_bank(smmu_domain, &pgtbl_cfg);
+
+ pgtbl_ops[1] = NULL;
+
+ if (split_tables) {
+ /* FIXME: I think it is safe to reuse pgtbl_cfg here */
+ pgtbl_ops[1] = alloc_io_pgtable_ops(fmt, &pgtbl_cfg,
+ smmu_domain);
+ if (!pgtbl_ops[1]) {
+ free_io_pgtable_ops(pgtbl_ops[0]);
+ ret = -ENOMEM;
+ goto out_clear_smmu;
+ }
+
+ arm_smmu_init_ttbr1(smmu_domain, &pgtbl_cfg);
+ }
+
arm_smmu_write_context_bank(smmu, cfg->cbndx);
/*
@@ -895,7 +989,9 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
mutex_unlock(&smmu_domain->init_mutex);
/* Publish page table ops for map/unmap */
- smmu_domain->pgtbl_ops = pgtbl_ops;
+ smmu_domain->pgtbl_ops[0] = pgtbl_ops[0];
+ smmu_domain->pgtbl_ops[1] = pgtbl_ops[1];
+
return 0;
out_clear_smmu:
@@ -927,7 +1023,9 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
devm_free_irq(smmu->dev, irq, domain);
}
- free_io_pgtable_ops(smmu_domain->pgtbl_ops);
+ free_io_pgtable_ops(smmu_domain->pgtbl_ops[0]);
+ free_io_pgtable_ops(smmu_domain->pgtbl_ops[1]);
+
__arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx);
}
@@ -1230,10 +1328,23 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
return arm_smmu_domain_add_master(smmu_domain, fwspec);
}
+static struct io_pgtable_ops *
+arm_smmu_get_pgtbl_ops(struct iommu_domain *domain, unsigned long iova)
+{
+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+ struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
+ struct arm_smmu_cb *cb = &smmu_domain->smmu->cbs[cfg->cbndx];
+
+ if (iova & cb->split_table_mask)
+ return smmu_domain->pgtbl_ops[1];
+
+ return smmu_domain->pgtbl_ops[0];
+}
+
static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot)
{
- struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
+ struct io_pgtable_ops *ops = arm_smmu_get_pgtbl_ops(domain, iova);
if (!ops)
return -ENODEV;
@@ -1244,7 +1355,7 @@ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
size_t size)
{
- struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
+ struct io_pgtable_ops *ops = arm_smmu_get_pgtbl_ops(domain, iova);
if (!ops)
return 0;
@@ -1266,7 +1377,7 @@ static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain,
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct arm_smmu_device *smmu = smmu_domain->smmu;
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
- struct io_pgtable_ops *ops= smmu_domain->pgtbl_ops;
+ struct io_pgtable_ops *ops = arm_smmu_get_pgtbl_ops(domain, iova);
struct device *dev = smmu->dev;
void __iomem *cb_base;
u32 tmp;
@@ -1307,7 +1418,7 @@ static phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain,
dma_addr_t iova)
{
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
- struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
+ struct io_pgtable_ops *ops = arm_smmu_get_pgtbl_ops(domain, iova);
if (domain->type == IOMMU_DOMAIN_IDENTITY)
return iova;
@@ -1477,6 +1588,10 @@ static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
case DOMAIN_ATTR_NESTING:
*(int *)data = (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED);
return 0;
+ case DOMAIN_ATTR_SPLIT_TABLES:
+ *((int *)data) = !!(smmu_domain->attributes
+ & (1 << DOMAIN_ATTR_SPLIT_TABLES));
+ return 0;
default:
return -ENODEV;
}
@@ -1506,6 +1621,11 @@ static int arm_smmu_domain_set_attr(struct iommu_domain *domain,
smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
break;
+ case DOMAIN_ATTR_SPLIT_TABLES:
+ if (*((int *)data))
+ smmu_domain->attributes |=
+ 1 << DOMAIN_ATTR_SPLIT_TABLES;
+ break;
default:
ret = -ENODEV;
}
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index fe851eae9057..920d9faa2a76 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -422,8 +422,7 @@ static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova,
if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE)))
return 0;
- if (WARN_ON(iova >= (1ULL << data->iop.cfg.ias) ||
- paddr >= (1ULL << data->iop.cfg.oas)))
+ if (WARN_ON(paddr >= (1ULL << data->iop.cfg.oas)))
return -ERANGE;
prot = arm_lpae_prot_to_pte(data, iommu_prot);
--
2.17.0
^ permalink raw reply related
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