F: drivers/hwmon/fam15h_power.c
AMD GEODE CS5536 USB DEVICE CONTROLLER DRIVER
-M: Thomas Dahlmann <dahlmann.thomas@arcor.de>
L: linux-geode@lists.infradead.org (moderated for non-subscribers)
-S: Supported
+S: Orphan
F: drivers/usb/gadget/udc/amd5536udc.*
AMD GEODE PROCESSOR/CHIPSET SUPPORT
F: drivers/video/fbdev/arcfb.c
F: drivers/video/fbdev/core/fb_defio.c
+ARCNET NETWORK LAYER
+M: Michael Grzeschik <m.grzeschik@pengutronix.de>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/arcnet/
+F: include/uapi/linux/if_arcnet.h
+
ARM MFM AND FLOPPY DRIVERS
M: Ian Molton <spyro@f2s.com>
S: Maintained
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
------ARM/Allwinner A1X SoC support
++++++ARM/Allwinner sunXi SoC support
M: Maxime Ripard <maxime.ripard@free-electrons.com>
++++++M: Chen-Yu Tsai <wens@csie.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
------N: sun[x4567]i
++++++N: sun[x456789]i
ARM/Allwinner SoC Clock Support
M: Emilio López <emilio@elopez.com.ar>
DIGI EPCA PCI PRODUCTS
M: Lidza Louina <lidza.louina@gmail.com>
-M: Mark Hounschell <markh@compro.net>
M: Daeseok Youn <daeseok.youn@gmail.com>
L: driverdev-devel@linuxdriverproject.org
S: Maintained
F: include/drm/i915*
F: include/uapi/drm/i915*
++++++DRM DRIVERS FOR ATMEL HLCDC
++++++M: Boris Brezillon <boris.brezillon@free-electrons.com>
++++++L: dri-devel@lists.freedesktop.org
++++++S: Supported
++++++F: drivers/gpu/drm/atmel-hlcdc/
++++++F: Documentation/devicetree/bindings/drm/atmel/
++++++
DRM DRIVERS FOR EXYNOS
M: Inki Dae <inki.dae@samsung.com>
M: Joonyoung Shim <jy0922.shim@samsung.com>
F: drivers/gpu/drm/imx/
F: Documentation/devicetree/bindings/drm/imx/
++++++DRM DRIVERS FOR GMA500 (Poulsbo, Moorestown and derivative chipsets)
++++++M: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
++++++L: dri-devel@lists.freedesktop.org
++++++T: git git://github.com/patjak/drm-gma500
++++++S: Maintained
++++++F: drivers/gpu/drm/gma500
++++++F: include/drm/gma500*
++++++
DRM DRIVERS FOR NVIDIA TEGRA
M: Thierry Reding <thierry.reding@gmail.com>
M: Terje Bergström <tbergstrom@nvidia.com>
F: sound/usb/misc/ua101.c
EXTENSIBLE FIRMWARE INTERFACE (EFI)
- - -M: Matt Fleming <matt.fleming@intel.com>
+ + +M: Matt Fleming <matt@codeblueprint.co.uk>
L: linux-efi@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mfleming/efi.git
S: Maintained
EFI VARIABLE FILESYSTEM
M: Matthew Garrett <matthew.garrett@nebula.com>
M: Jeremy Kerr <jk@ozlabs.org>
- - -M: Matt Fleming <matt.fleming@intel.com>
+ + +M: Matt Fleming <matt@codeblueprint.co.uk>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mfleming/efi.git
L: linux-efi@vger.kernel.org
S: Maintained
S: Maintained
F: drivers/net/ethernet/freescale/ucc_geth*
++++++FREESCALE eTSEC ETHERNET DRIVER (GIANFAR)
++++++M: Claudiu Manoil <claudiu.manoil@freescale.com>
++++++L: netdev@vger.kernel.org
++++++S: Maintained
++++++F: drivers/net/ethernet/freescale/gianfar*
++++++X: drivers/net/ethernet/freescale/gianfar_ptp.c
++++++F: Documentation/devicetree/bindings/net/fsl-tsec-phy.txt
++++++
FREESCALE QUICC ENGINE UCC UART DRIVER
M: Timur Tabi <timur@tabi.org>
L: linuxppc-dev@lists.ozlabs.org
KERNEL VIRTUAL MACHINE (KVM) FOR AMD-V
M: Joerg Roedel <joro@8bytes.org>
L: kvm@vger.kernel.org
- -W: http://kvm.qumranet.com
+ +W: http://www.linux-kvm.org/
S: Maintained
F: arch/x86/include/asm/svm.h
F: arch/x86/kvm/svm.c
KERNEL VIRTUAL MACHINE (KVM) FOR POWERPC
M: Alexander Graf <agraf@suse.com>
L: kvm-ppc@vger.kernel.org
- -W: http://kvm.qumranet.com
+ +W: http://www.linux-kvm.org/
T: git git://github.com/agraf/linux-2.6.git
S: Supported
F: arch/powerpc/include/asm/kvm*
MELLANOX ETHERNET DRIVER (mlx4_en)
M: Amir Vadai <amirv@mellanox.com>
------M: Ido Shamay <idos@mellanox.com>
L: netdev@vger.kernel.org
S: Supported
W: http://www.mellanox.com
F: drivers/net/ethernet/qlogic/qla3xxx.*
QLOGIC QLCNIC (1/10)Gb ETHERNET DRIVER
-M: Shahed Shaikh <shahed.shaikh@qlogic.com>
M: Dept-GELinuxNICDev@qlogic.com
L: netdev@vger.kernel.org
S: Supported
F: include/net/iucv/
F: net/iucv/
++++ ++S390 IOMMU (PCI)
++++ ++M: Gerald Schaefer <gerald.schaefer@de.ibm.com>
++++ ++L: linux-s390@vger.kernel.org
++++ ++W: http://www.ibm.com/developerworks/linux/linux390/
++++ ++S: Supported
++++ ++F: drivers/iommu/s390-iommu.c
++++ ++
S3C24XX SD/MMC Driver
M: Ben Dooks <ben-linux@fluff.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
F: Documentation/devicetree/bindings/net/snps,dwc-qos-ethernet.txt
F: drivers/net/ethernet/synopsys/dwc_eth_qos.c
++++++SYNOPSYS DESIGNWARE I2C DRIVER
++++++M: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
++++++M: Jarkko Nikula <jarkko.nikula@linux.intel.com>
++++++M: Mika Westerberg <mika.westerberg@linux.intel.com>
++++++L: linux-i2c@vger.kernel.org
++++++S: Maintained
++++++F: drivers/i2c/busses/i2c-designware-*
++++++F: include/linux/platform_data/i2c-designware.h
++++++
SYNOPSYS DESIGNWARE MMC/SD/SDIO DRIVER
M: Seungwon Jeon <tgih.jun@samsung.com>
M: Jaehoon Chung <jh80.chung@samsung.com>
STAGING - LUSTRE PARALLEL FILESYSTEM
M: Oleg Drokin <oleg.drokin@intel.com>
M: Andreas Dilger <andreas.dilger@intel.com>
-L: HPDD-discuss@lists.01.org (moderated for non-subscribers)
-W: http://lustre.opensfs.org/
+L: lustre-devel@lists.lustre.org (moderated for non-subscribers)
+W: http://wiki.lustre.org/
S: Maintained
F: drivers/staging/lustre
STAGING - NVIDIA COMPLIANT EMBEDDED CONTROLLER INTERFACE (nvec)
- - -M: Julian Andres Klode <jak@jak-linux.org>
M: Marc Dietrich <marvin24@gmx.de>
L: ac100@lists.launchpad.net (moderated for non-subscribers)
L: linux-tegra@vger.kernel.org
F: include/linux/cpu_cooling.h
F: Documentation/devicetree/bindings/thermal/
+THERMAL/CPU_COOLING
+M: Amit Daniel Kachhap <amit.kachhap@gmail.com>
+M: Viresh Kumar <viresh.kumar@linaro.org>
+M: Javi Merino <javi.merino@arm.com>
+L: linux-pm@vger.kernel.org
+S: Supported
+F: Documentation/thermal/cpu-cooling-api.txt
+F: drivers/thermal/cpu_cooling.c
+F: include/linux/cpu_cooling.h
+
THINGM BLINK(1) USB RGB LED DRIVER
M: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
S: Maintained
F: include/linux/vlynq.h
VME SUBSYSTEM
-M: Martyn Welch <martyn.welch@ge.com>
+M: Martyn Welch <martyn@welchs.me.uk>
M: Manohar Vanga <manohar.vanga@gmail.com>
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
L: devel@driverdev.osuosl.org
M: Liam Girdwood <lgirdwood@gmail.com>
M: Mark Brown <broonie@kernel.org>
L: linux-kernel@vger.kernel.org
-W: http://opensource.wolfsonmicro.com/node/15
W: http://www.slimlogic.co.uk/?p=48
T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git
S: Supported
S: Maintained
F: drivers/net/vrf.c
F: include/net/vrf.h
+F: Documentation/networking/vrf.txt
VT1211 HARDWARE MONITOR DRIVER
M: Juerg Haefliger <juergh@gmail.com>
S: Maintained
F: drivers/net/wireless/wl3501*
- - -WM97XX TOUCHSCREEN DRIVERS
- - -M: Mark Brown <broonie@kernel.org>
- - -M: Liam Girdwood <lrg@slimlogic.co.uk>
- - -L: linux-input@vger.kernel.org
- - W: https://github.com/CirrusLogic/linux-drivers/wiki
-T: git git://opensource.wolfsonmicro.com/linux-2.6-touch
-W: http://opensource.wolfsonmicro.com/node/7
- - -S: Supported
- - -F: drivers/input/touchscreen/*wm97*
- - -F: include/linux/wm97xx.h
- - -
WOLFSON MICROELECTRONICS DRIVERS
L: patches@opensource.wolfsonmicro.com
-T: git git://opensource.wolfsonmicro.com/linux-2.6-asoc
-T: git git://opensource.wolfsonmicro.com/linux-2.6-audioplus
-W: http://opensource.wolfsonmicro.com/content/linux-drivers-wolfson-devices
+T: git https://github.com/CirrusLogic/linux-drivers.git
+W: https://github.com/CirrusLogic/linux-drivers/wiki
S: Supported
F: Documentation/hwmon/wm83??
F: arch/arm/mach-s3c64xx/mach-crag6410*
ZSMALLOC COMPRESSED SLAB MEMORY ALLOCATOR
M: Minchan Kim <minchan@kernel.org>
M: Nitin Gupta <ngupta@vflare.org>
++++++R: Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com>
L: linux-mm@kvack.org
S: Maintained
F: mm/zsmalloc.c
config IOMMU_IO_PGTABLE_LPAE
bool "ARMv7/v8 Long Descriptor Format"
select IOMMU_IO_PGTABLE
------ # SWIOTLB guarantees a dma_to_phys() implementation
------ depends on ARM || ARM64 || (COMPILE_TEST && SWIOTLB)
++++++ depends on HAS_DMA && (ARM || ARM64 || COMPILE_TEST)
help
Enable support for the ARM long descriptor pagetable format.
This allocator supports 4K/2M/1G, 16K/32M and 64K/512M page
endmenu
config IOMMU_IOVA
- - bool
+ + tristate
config OF_IOMMU
def_bool y
depends on OF && IOMMU_API
+++++ +# IOMMU-agnostic DMA-mapping layer
+++++ +config IOMMU_DMA
+++++ + bool
+++++ + depends on NEED_SG_DMA_LENGTH
+++++ + select IOMMU_API
+++++ + select IOMMU_IOVA
+++++ +
config FSL_PAMU
bool "Freescale IOMMU support"
depends on PPC32
depends on ARM64 && PCI
select IOMMU_API
select IOMMU_IO_PGTABLE_LPAE
+++ +++ select GENERIC_MSI_IRQ_DOMAIN
help
Support for implementations of the ARM System MMU architecture
version 3 providing translation support to a PCIe root complex.
Say Y here if your system includes an IOMMU device implementing
the ARM SMMUv3 architecture.
++++ ++config S390_IOMMU
++++ ++ def_bool y if S390 && PCI
++++ ++ depends on S390 && PCI
++++ ++ select IOMMU_API
++++ ++ help
++++ ++ Support for the IOMMU API for s390 PCI devices.
++++ ++
endif # IOMMU_SUPPORT
obj-$(CONFIG_IOMMU_API) += iommu.o
obj-$(CONFIG_IOMMU_API) += iommu-traces.o
obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o
+++++ +obj-$(CONFIG_IOMMU_DMA) += dma-iommu.o
obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o
obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o
obj-$(CONFIG_IOMMU_IOVA) += iova.o
obj-$(CONFIG_SHMOBILE_IOMMU) += shmobile-iommu.o
obj-$(CONFIG_SHMOBILE_IPMMU) += shmobile-ipmmu.o
obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o
++++ ++obj-$(CONFIG_S390_IOMMU) += s390-iommu.o
struct iommu_dev_data {
struct list_head list; /* For domain->dev_list */
struct list_head dev_data_list; /* For global dev_data_list */
------ struct list_head alias_list; /* Link alias-groups together */
------ struct iommu_dev_data *alias_data;/* The alias dev_data */
struct protection_domain *domain; /* Domain the device is bound to */
u16 devid; /* PCI Device ID */
bool iommu_v2; /* Device can make use of IOMMUv2 */
if (!dev_data)
return NULL;
------ INIT_LIST_HEAD(&dev_data->alias_list);
------
dev_data->devid = devid;
spin_lock_irqsave(&dev_data_list_lock, flags);
return dev_data;
}
------ static void free_dev_data(struct iommu_dev_data *dev_data)
------ {
------ unsigned long flags;
------
------ spin_lock_irqsave(&dev_data_list_lock, flags);
------ list_del(&dev_data->dev_data_list);
------ spin_unlock_irqrestore(&dev_data_list_lock, flags);
------
------ kfree(dev_data);
------ }
------
static struct iommu_dev_data *search_dev_data(u16 devid)
{
struct iommu_dev_data *dev_data;
iommu_group_put(group);
}
------ static int __last_alias(struct pci_dev *pdev, u16 alias, void *data)
------ {
------ *(u16 *)data = alias;
------ return 0;
------ }
------
------ static u16 get_alias(struct device *dev)
------ {
------ struct pci_dev *pdev = to_pci_dev(dev);
------ u16 devid, ivrs_alias, pci_alias;
------
------ devid = get_device_id(dev);
------ ivrs_alias = amd_iommu_alias_table[devid];
------ pci_for_each_dma_alias(pdev, __last_alias, &pci_alias);
------
------ if (ivrs_alias == pci_alias)
------ return ivrs_alias;
------
------ /*
------ * DMA alias showdown
------ *
------ * The IVRS is fairly reliable in telling us about aliases, but it
------ * can't know about every screwy device. If we don't have an IVRS
------ * reported alias, use the PCI reported alias. In that case we may
------ * still need to initialize the rlookup and dev_table entries if the
------ * alias is to a non-existent device.
------ */
------ if (ivrs_alias == devid) {
------ if (!amd_iommu_rlookup_table[pci_alias]) {
------ amd_iommu_rlookup_table[pci_alias] =
------ amd_iommu_rlookup_table[devid];
------ memcpy(amd_iommu_dev_table[pci_alias].data,
------ amd_iommu_dev_table[devid].data,
------ sizeof(amd_iommu_dev_table[pci_alias].data));
------ }
------
------ return pci_alias;
------ }
------
------ pr_info("AMD-Vi: Using IVRS reported alias %02x:%02x.%d "
------ "for device %s[%04x:%04x], kernel reported alias "
------ "%02x:%02x.%d\n", PCI_BUS_NUM(ivrs_alias), PCI_SLOT(ivrs_alias),
------ PCI_FUNC(ivrs_alias), dev_name(dev), pdev->vendor, pdev->device,
------ PCI_BUS_NUM(pci_alias), PCI_SLOT(pci_alias),
------ PCI_FUNC(pci_alias));
------
------ /*
------ * If we don't have a PCI DMA alias and the IVRS alias is on the same
------ * bus, then the IVRS table may know about a quirk that we don't.
------ */
------ if (pci_alias == devid &&
------ PCI_BUS_NUM(ivrs_alias) == pdev->bus->number) {
------ pdev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
------ pdev->dma_alias_devfn = ivrs_alias & 0xff;
------ pr_info("AMD-Vi: Added PCI DMA alias %02x.%d for %s\n",
------ PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias),
------ dev_name(dev));
------ }
------
------ return ivrs_alias;
------ }
------
static int iommu_init_device(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct iommu_dev_data *dev_data;
------ u16 alias;
if (dev->archdata.iommu)
return 0;
if (!dev_data)
return -ENOMEM;
------ alias = get_alias(dev);
------
------ if (alias != dev_data->devid) {
------ struct iommu_dev_data *alias_data;
------
------ alias_data = find_dev_data(alias);
------ if (alias_data == NULL) {
------ pr_err("AMD-Vi: Warning: Unhandled device %s\n",
------ dev_name(dev));
------ free_dev_data(dev_data);
------ return -ENOTSUPP;
------ }
------ dev_data->alias_data = alias_data;
------
------ /* Add device to the alias_list */
------ list_add(&dev_data->alias_list, &alias_data->alias_list);
------ }
------
if (pci_iommuv2_capable(pdev)) {
struct amd_iommu *iommu;
iommu_group_remove_device(dev);
------ /* Unlink from alias, it may change if another device is re-plugged */
------ dev_data->alias_data = NULL;
------
/* Remove dma-ops */
dev->archdata.dma_ops = NULL;
while (head != tail) {
iommu_print_event(iommu, iommu->evt_buf + head);
------ head = (head + EVENT_ENTRY_SIZE) % iommu->evt_buf_size;
++++++ head = (head + EVENT_ENTRY_SIZE) % EVT_BUFFER_SIZE;
}
writel(head, iommu->mmio_base + MMIO_EVT_HEAD_OFFSET);
u8 *target;
target = iommu->cmd_buf + tail;
------ tail = (tail + sizeof(*cmd)) % iommu->cmd_buf_size;
++++++ tail = (tail + sizeof(*cmd)) % CMD_BUFFER_SIZE;
/* Copy command to buffer */
memcpy(target, cmd, sizeof(*cmd));
u32 left, tail, head, next_tail;
unsigned long flags;
------ WARN_ON(iommu->cmd_buf_size & CMD_BUFFER_UNINITIALIZED);
------
again:
spin_lock_irqsave(&iommu->lock, flags);
head = readl(iommu->mmio_base + MMIO_CMD_HEAD_OFFSET);
tail = readl(iommu->mmio_base + MMIO_CMD_TAIL_OFFSET);
------ next_tail = (tail + sizeof(*cmd)) % iommu->cmd_buf_size;
------ left = (head - next_tail) % iommu->cmd_buf_size;
++++++ next_tail = (tail + sizeof(*cmd)) % CMD_BUFFER_SIZE;
++++++ left = (head - next_tail) % CMD_BUFFER_SIZE;
if (left <= 2) {
struct iommu_cmd sync_cmd;
static int device_flush_dte(struct iommu_dev_data *dev_data)
{
struct amd_iommu *iommu;
++++++ u16 alias;
int ret;
iommu = amd_iommu_rlookup_table[dev_data->devid];
++++++ alias = amd_iommu_alias_table[dev_data->devid];
ret = iommu_flush_dte(iommu, dev_data->devid);
++++++ if (!ret && alias != dev_data->devid)
++++++ ret = iommu_flush_dte(iommu, alias);
if (ret)
return ret;
static void clear_dte_entry(u16 devid)
{
/* remove entry from the device table seen by the hardware */
------ amd_iommu_dev_table[devid].data[0] = IOMMU_PTE_P | IOMMU_PTE_TV;
------ amd_iommu_dev_table[devid].data[1] = 0;
++++++ amd_iommu_dev_table[devid].data[0] = IOMMU_PTE_P | IOMMU_PTE_TV;
++++++ amd_iommu_dev_table[devid].data[1] &= DTE_FLAG_MASK;
amd_iommu_apply_erratum_63(devid);
}
struct protection_domain *domain)
{
struct amd_iommu *iommu;
++++++ u16 alias;
bool ats;
iommu = amd_iommu_rlookup_table[dev_data->devid];
++++++ alias = amd_iommu_alias_table[dev_data->devid];
ats = dev_data->ats.enabled;
/* Update data structures */
dev_data->domain = domain;
list_add(&dev_data->list, &domain->dev_list);
------ set_dte_entry(dev_data->devid, domain, ats);
/* Do reference counting */
domain->dev_iommu[iommu->index] += 1;
domain->dev_cnt += 1;
------ /* Flush the DTE entry */
++++++ /* Update device table */
++++++ set_dte_entry(dev_data->devid, domain, ats);
++++++ if (alias != dev_data->devid)
++++++ set_dte_entry(dev_data->devid, domain, ats);
++++++
device_flush_dte(dev_data);
}
static void do_detach(struct iommu_dev_data *dev_data)
{
struct amd_iommu *iommu;
++++++ u16 alias;
+++++
++++++ /*
++++++ * First check if the device is still attached. It might already
++++++ * be detached from its domain because the generic
++++++ * iommu_detach_group code detached it and we try again here in
++++++ * our alias handling.
++++++ */
++++++ if (!dev_data->domain)
++++++ return;
+
iommu = amd_iommu_rlookup_table[dev_data->devid];
++++++ alias = amd_iommu_alias_table[dev_data->devid];
/* decrease reference counters */
dev_data->domain->dev_iommu[iommu->index] -= 1;
dev_data->domain = NULL;
list_del(&dev_data->list);
clear_dte_entry(dev_data->devid);
++++++ if (alias != dev_data->devid)
++++++ clear_dte_entry(alias);
/* Flush the DTE entry */
device_flush_dte(dev_data);
static int __attach_device(struct iommu_dev_data *dev_data,
struct protection_domain *domain)
{
------ struct iommu_dev_data *head, *entry;
int ret;
++++++ /*
++++++ * Must be called with IRQs disabled. Warn here to detect early
++++++ * when its not.
++++++ */
++++++ WARN_ON(!irqs_disabled());
++++++
/* lock domain */
spin_lock(&domain->lock);
------ head = dev_data;
------
------ if (head->alias_data != NULL)
------ head = head->alias_data;
------
------ /* Now we have the root of the alias group, if any */
------
ret = -EBUSY;
------ if (head->domain != NULL)
++++++ if (dev_data->domain != NULL)
goto out_unlock;
/* Attach alias group root */
------ do_attach(head, domain);
------
------ /* Attach other devices in the alias group */
------ list_for_each_entry(entry, &head->alias_list, alias_list)
------ do_attach(entry, domain);
++++++ do_attach(dev_data, domain);
ret = 0;
*/
static void __detach_device(struct iommu_dev_data *dev_data)
{
------ struct iommu_dev_data *head, *entry;
struct protection_domain *domain;
------ unsigned long flags;
------ BUG_ON(!dev_data->domain);
------
------ domain = dev_data->domain;
++++++ /*
++++++ * Must be called with IRQs disabled. Warn here to detect early
++++++ * when its not.
++++++ */
++++++ WARN_ON(!irqs_disabled());
------ spin_lock_irqsave(&domain->lock, flags);
++++++ if (WARN_ON(!dev_data->domain))
++++++ return;
------ head = dev_data;
------ if (head->alias_data != NULL)
------ head = head->alias_data;
++++++ domain = dev_data->domain;
------ list_for_each_entry(entry, &head->alias_list, alias_list)
------ do_detach(entry);
++++++ spin_lock(&domain->lock);
------ do_detach(head);
++++++ do_detach(dev_data);
------ spin_unlock_irqrestore(&domain->lock, flags);
++++++ spin_unlock(&domain->lock);
}
/*
.iova_to_phys = amd_iommu_iova_to_phys,
.add_device = amd_iommu_add_device,
.remove_device = amd_iommu_remove_device,
+++++ + .device_group = pci_device_group,
.get_dm_regions = amd_iommu_get_dm_regions,
.put_dm_regions = amd_iommu_put_dm_regions,
.pgsize_bitmap = AMD_IOMMU_PGSIZES,
return 0x04 << (*ivhd >> 6);
}
------ /*
------ * This function reads the last device id the IOMMU has to handle from the PCI
------ * capability header for this IOMMU
------ */
------ static int __init find_last_devid_on_pci(int bus, int dev, int fn, int cap_ptr)
------ {
------ u32 cap;
------
------ cap = read_pci_config(bus, dev, fn, cap_ptr+MMIO_RANGE_OFFSET);
------ update_last_devid(PCI_DEVID(MMIO_GET_BUS(cap), MMIO_GET_LD(cap)));
------
------ return 0;
------ }
------
/*
* After reading the highest device id from the IOMMU PCI capability header
* this function looks if there is a higher device id defined in the ACPI table
p += sizeof(*h);
end += h->length;
------ find_last_devid_on_pci(PCI_BUS_NUM(h->devid),
------ PCI_SLOT(h->devid),
------ PCI_FUNC(h->devid),
------ h->cap_ptr);
------
while (p < end) {
dev = (struct ivhd_entry *)p;
switch (dev->type) {
++++++ case IVHD_DEV_ALL:
++++++ /* Use maximum BDF value for DEV_ALL */
++++++ update_last_devid(0xffff);
++++++ break;
case IVHD_DEV_SELECT:
case IVHD_DEV_RANGE_END:
case IVHD_DEV_ALIAS:
* write commands to that buffer later and the IOMMU will execute them
* asynchronously
*/
------ static u8 * __init alloc_command_buffer(struct amd_iommu *iommu)
++++++ static int __init alloc_command_buffer(struct amd_iommu *iommu)
{
------ u8 *cmd_buf = (u8 *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
------ get_order(CMD_BUFFER_SIZE));
------
------ if (cmd_buf == NULL)
------ return NULL;
------
------ iommu->cmd_buf_size = CMD_BUFFER_SIZE | CMD_BUFFER_UNINITIALIZED;
++++++ iommu->cmd_buf = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
++++++ get_order(CMD_BUFFER_SIZE));
------ return cmd_buf;
++++++ return iommu->cmd_buf ? 0 : -ENOMEM;
}
/*
&entry, sizeof(entry));
amd_iommu_reset_cmd_buffer(iommu);
------ iommu->cmd_buf_size &= ~(CMD_BUFFER_UNINITIALIZED);
}
static void __init free_command_buffer(struct amd_iommu *iommu)
{
------ free_pages((unsigned long)iommu->cmd_buf,
------ get_order(iommu->cmd_buf_size & ~(CMD_BUFFER_UNINITIALIZED)));
++++++ free_pages((unsigned long)iommu->cmd_buf, get_order(CMD_BUFFER_SIZE));
}
/* allocates the memory where the IOMMU will log its events to */
------ static u8 * __init alloc_event_buffer(struct amd_iommu *iommu)
++++++ static int __init alloc_event_buffer(struct amd_iommu *iommu)
{
------ iommu->evt_buf = (u8 *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
------ get_order(EVT_BUFFER_SIZE));
++++++ iommu->evt_buf = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
++++++ get_order(EVT_BUFFER_SIZE));
------ if (iommu->evt_buf == NULL)
------ return NULL;
------
------ iommu->evt_buf_size = EVT_BUFFER_SIZE;
------
------ return iommu->evt_buf;
++++++ return iommu->evt_buf ? 0 : -ENOMEM;
}
static void iommu_enable_event_buffer(struct amd_iommu *iommu)
}
/* allocates the memory where the IOMMU will log its events to */
------ static u8 * __init alloc_ppr_log(struct amd_iommu *iommu)
++++++ static int __init alloc_ppr_log(struct amd_iommu *iommu)
{
------ iommu->ppr_log = (u8 *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
------ get_order(PPR_LOG_SIZE));
-----
----- if (iommu->ppr_log == NULL)
----- return NULL;
++++++ iommu->ppr_log = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
++++++ get_order(PPR_LOG_SIZE));
- if (iommu->ppr_log == NULL)
- return NULL;
-
------ return iommu->ppr_log;
++++++ return iommu->ppr_log ? 0 : -ENOMEM;
}
static void iommu_enable_ppr_log(struct amd_iommu *iommu)
switch (e->type) {
case IVHD_DEV_ALL:
------ DUMP_printk(" DEV_ALL\t\t\t first devid: %02x:%02x.%x"
------ " last device %02x:%02x.%x flags: %02x\n",
------ PCI_BUS_NUM(iommu->first_device),
------ PCI_SLOT(iommu->first_device),
------ PCI_FUNC(iommu->first_device),
------ PCI_BUS_NUM(iommu->last_device),
------ PCI_SLOT(iommu->last_device),
------ PCI_FUNC(iommu->last_device),
------ e->flags);
++++++ DUMP_printk(" DEV_ALL\t\t\tflags: %02x\n", e->flags);
------ for (dev_i = iommu->first_device;
------ dev_i <= iommu->last_device; ++dev_i)
------ set_dev_entry_from_acpi(iommu, dev_i,
------ e->flags, 0);
++++++ for (dev_i = 0; dev_i <= amd_iommu_last_bdf; ++dev_i)
++++++ set_dev_entry_from_acpi(iommu, dev_i, e->flags, 0);
break;
case IVHD_DEV_SELECT:
return 0;
}
------ /* Initializes the device->iommu mapping for the driver */
------ static int __init init_iommu_devices(struct amd_iommu *iommu)
------ {
------ u32 i;
------
------ for (i = iommu->first_device; i <= iommu->last_device; ++i)
------ set_iommu_for_device(iommu, i);
------
------ return 0;
------ }
------
static void __init free_iommu_one(struct amd_iommu *iommu)
{
free_command_buffer(iommu);
if (!iommu->mmio_base)
return -ENOMEM;
------ iommu->cmd_buf = alloc_command_buffer(iommu);
------ if (!iommu->cmd_buf)
++++++ if (alloc_command_buffer(iommu))
return -ENOMEM;
------ iommu->evt_buf = alloc_event_buffer(iommu);
------ if (!iommu->evt_buf)
++++++ if (alloc_event_buffer(iommu))
return -ENOMEM;
iommu->int_enabled = false;
*/
amd_iommu_rlookup_table[iommu->devid] = NULL;
------ init_iommu_devices(iommu);
------
return 0;
}
if (!iommu->dev)
return -ENODEV;
++++++ /* Prevent binding other PCI device drivers to IOMMU devices */
++++++ iommu->dev->match_driver = false;
++++++
pci_read_config_dword(iommu->dev, cap_ptr + MMIO_CAP_HDR_OFFSET,
&iommu->cap);
pci_read_config_dword(iommu->dev, cap_ptr + MMIO_RANGE_OFFSET,
pci_read_config_dword(iommu->dev, cap_ptr + MMIO_MISC_OFFSET,
&misc);
------ iommu->first_device = PCI_DEVID(MMIO_GET_BUS(range),
------ MMIO_GET_FD(range));
------ iommu->last_device = PCI_DEVID(MMIO_GET_BUS(range),
------ MMIO_GET_LD(range));
------
if (!(iommu->cap & (1 << IOMMU_CAP_IOTLB)))
amd_iommu_iotlb_sup = false;
amd_iommu_v2_present = true;
}
------ if (iommu_feature(iommu, FEATURE_PPR)) {
------ iommu->ppr_log = alloc_ppr_log(iommu);
------ if (!iommu->ppr_log)
------ return -ENOMEM;
------ }
++++++ if (iommu_feature(iommu, FEATURE_PPR) && alloc_ppr_log(iommu))
++++++ return -ENOMEM;
if (iommu->cap & (1UL << IOMMU_CAP_NPCACHE))
amd_iommu_np_cache = true;
free_pages((unsigned long)irq_lookup_table,
get_order(rlookup_table_size));
------ if (amd_iommu_irq_cache) {
------ kmem_cache_destroy(amd_iommu_irq_cache);
------ amd_iommu_irq_cache = NULL;
------
------ }
++++++ kmem_cache_destroy(amd_iommu_irq_cache);
++++++ amd_iommu_irq_cache = NULL;
free_pages((unsigned long)amd_iommu_rlookup_table,
get_order(rlookup_table_size));
iommu_detected = 1;
x86_init.iommu.iommu_init = amd_iommu_init;
------ return 0;
++++++ return 1;
}
/****************************************************************************
#define IOMMU_PTE_IR (1ULL << 61)
#define IOMMU_PTE_IW (1ULL << 62)
----- #define DTE_FLAG_IOTLB (0x01UL << 32)
----- #define DTE_FLAG_GV (0x01ULL << 55)
++++++ #define DTE_FLAG_IOTLB (1ULL << 32)
++++++ #define DTE_FLAG_GV (1ULL << 55)
++++++#define DTE_FLAG_MASK (0x3ffULL << 32)
- #define DTE_FLAG_IOTLB (0x01UL << 32)
- #define DTE_FLAG_GV (0x01ULL << 55)
#define DTE_GLX_SHIFT (56)
#define DTE_GLX_MASK (3)
/* pci domain of this IOMMU */
u16 pci_seg;
------ /* first device this IOMMU handles. read from PCI */
------ u16 first_device;
------ /* last device this IOMMU handles. read from PCI */
------ u16 last_device;
------
/* start of exclusion range of that IOMMU */
u64 exclusion_start;
/* length of exclusion range of that IOMMU */
/* command buffer virtual address */
u8 *cmd_buf;
------ /* size of command buffer */
------ u32 cmd_buf_size;
------ /* size of event buffer */
------ u32 evt_buf_size;
/* event buffer virtual address */
u8 *evt_buf;
#include <linux/iommu.h>
#include <linux/iopoll.h>
#include <linux/module.h>
+++ +++#include <linux/msi.h>
#include <linux/of.h>
#include <linux/of_address.h>
+++ +++#include <linux/of_platform.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#define IDR0_TTF_SHIFT 2
#define IDR0_TTF_MASK 0x3
#define IDR0_TTF_AARCH64 (2 << IDR0_TTF_SHIFT)
++++++#define IDR0_TTF_AARCH32_64 (3 << IDR0_TTF_SHIFT)
#define IDR0_S1P (1 << 1)
#define IDR0_S2P (1 << 0)
#define CMDQ_TLBI_0_VMID_SHIFT 32
#define CMDQ_TLBI_0_ASID_SHIFT 48
#define CMDQ_TLBI_1_LEAF (1UL << 0)
------#define CMDQ_TLBI_1_ADDR_MASK ~0xfffUL
++++++#define CMDQ_TLBI_1_VA_MASK ~0xfffUL
++++++#define CMDQ_TLBI_1_IPA_MASK 0xfffffffff000UL
#define CMDQ_PRI_0_SSID_SHIFT 12
#define CMDQ_PRI_0_SSID_MASK 0xfffffUL
PRI_RESP_SUCC,
};
+++ +++enum arm_smmu_msi_index {
+++ +++ EVTQ_MSI_INDEX,
+++ +++ GERROR_MSI_INDEX,
+++ +++ PRIQ_MSI_INDEX,
+++ +++ ARM_SMMU_MAX_MSIS,
+++ +++};
+++ +++
+++ +++static phys_addr_t arm_smmu_msi_cfg[ARM_SMMU_MAX_MSIS][3] = {
+++ +++ [EVTQ_MSI_INDEX] = {
+++ +++ ARM_SMMU_EVTQ_IRQ_CFG0,
+++ +++ ARM_SMMU_EVTQ_IRQ_CFG1,
+++ +++ ARM_SMMU_EVTQ_IRQ_CFG2,
+++ +++ },
+++ +++ [GERROR_MSI_INDEX] = {
+++ +++ ARM_SMMU_GERROR_IRQ_CFG0,
+++ +++ ARM_SMMU_GERROR_IRQ_CFG1,
+++ +++ ARM_SMMU_GERROR_IRQ_CFG2,
+++ +++ },
+++ +++ [PRIQ_MSI_INDEX] = {
+++ +++ ARM_SMMU_PRIQ_IRQ_CFG0,
+++ +++ ARM_SMMU_PRIQ_IRQ_CFG1,
+++ +++ ARM_SMMU_PRIQ_IRQ_CFG2,
+++ +++ },
+++ +++};
+++ +++
struct arm_smmu_cmdq_ent {
/* Common fields */
u8 opcode;
unsigned int sid_bits;
struct arm_smmu_strtab_cfg strtab_cfg;
--- --- struct list_head list;
};
/* SMMU private data for an IOMMU group */
struct iommu_domain domain;
};
--- ---/* Our list of SMMU instances */
--- ---static DEFINE_SPINLOCK(arm_smmu_devices_lock);
--- ---static LIST_HEAD(arm_smmu_devices);
--- ---
struct arm_smmu_option_prop {
u32 opt;
const char *prop;
break;
case CMDQ_OP_TLBI_NH_VA:
cmd[0] |= (u64)ent->tlbi.asid << CMDQ_TLBI_0_ASID_SHIFT;
------ /* Fallthrough */
++++++ cmd[1] |= ent->tlbi.leaf ? CMDQ_TLBI_1_LEAF : 0;
++++++ cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_VA_MASK;
++++++ break;
case CMDQ_OP_TLBI_S2_IPA:
cmd[0] |= (u64)ent->tlbi.vmid << CMDQ_TLBI_0_VMID_SHIFT;
cmd[1] |= ent->tlbi.leaf ? CMDQ_TLBI_1_LEAF : 0;
------ cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_ADDR_MASK;
++++++ cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_IPA_MASK;
break;
case CMDQ_OP_TLBI_NH_ASID:
cmd[0] |= (u64)ent->tlbi.asid << CMDQ_TLBI_0_ASID_SHIFT;
struct io_pgtable_cfg *pgtbl_cfg)
{
int ret;
--- --- u16 asid;
+++ +++ int asid;
struct arm_smmu_device *smmu = smmu_domain->smmu;
struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
&cfg->cdptr_dma, GFP_KERNEL);
if (!cfg->cdptr) {
dev_warn(smmu->dev, "failed to allocate context descriptor\n");
+++ +++ ret = -ENOMEM;
goto out_free_asid;
}
--- --- cfg->cd.asid = asid;
+++ +++ cfg->cd.asid = (u16)asid;
cfg->cd.ttbr = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
cfg->cd.tcr = pgtbl_cfg->arm_lpae_s1_cfg.tcr;
cfg->cd.mair = pgtbl_cfg->arm_lpae_s1_cfg.mair[0];
static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain,
struct io_pgtable_cfg *pgtbl_cfg)
{
--- --- u16 vmid;
+++ +++ int vmid;
struct arm_smmu_device *smmu = smmu_domain->smmu;
struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
if (IS_ERR_VALUE(vmid))
return vmid;
--- --- cfg->vmid = vmid;
+++ +++ cfg->vmid = (u16)vmid;
cfg->vttbr = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
cfg->vtcr = pgtbl_cfg->arm_lpae_s2_cfg.vtcr;
return 0;
static struct arm_smmu_device *arm_smmu_get_for_pci_dev(struct pci_dev *pdev)
{
struct device_node *of_node;
--- --- struct arm_smmu_device *curr, *smmu = NULL;
+++ +++ struct platform_device *smmu_pdev;
+++ +++ struct arm_smmu_device *smmu = NULL;
struct pci_bus *bus = pdev->bus;
/* Walk up to the root bus */
return NULL;
/* See if we can find an SMMU corresponding to the phandle */
--- --- spin_lock(&arm_smmu_devices_lock);
--- --- list_for_each_entry(curr, &arm_smmu_devices, list) {
--- --- if (curr->dev->of_node == of_node) {
--- --- smmu = curr;
--- --- break;
--- --- }
--- --- }
--- --- spin_unlock(&arm_smmu_devices_lock);
+++ +++ smmu_pdev = of_find_device_by_node(of_node);
+++ +++ if (smmu_pdev)
+++ +++ smmu = platform_get_drvdata(smmu_pdev);
+++ +++
of_node_put(of_node);
return smmu;
}
.iova_to_phys = arm_smmu_iova_to_phys,
.add_device = arm_smmu_add_device,
.remove_device = arm_smmu_remove_device,
+++++ + .device_group = pci_device_group,
.domain_get_attr = arm_smmu_domain_get_attr,
.domain_set_attr = arm_smmu_domain_set_attr,
.pgsize_bitmap = -1UL, /* Restricted during device attach */
1, ARM_SMMU_POLL_TIMEOUT_US);
}
+++ +++static void arm_smmu_free_msis(void *data)
+++ +++{
+++ +++ struct device *dev = data;
+++ +++ platform_msi_domain_free_irqs(dev);
+++ +++}
+++ +++
+++ +++static void arm_smmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
+++ +++{
+++ +++ phys_addr_t doorbell;
+++ +++ struct device *dev = msi_desc_to_dev(desc);
+++ +++ struct arm_smmu_device *smmu = dev_get_drvdata(dev);
+++ +++ phys_addr_t *cfg = arm_smmu_msi_cfg[desc->platform.msi_index];
+++ +++
+++ +++ doorbell = (((u64)msg->address_hi) << 32) | msg->address_lo;
+++ +++ doorbell &= MSI_CFG0_ADDR_MASK << MSI_CFG0_ADDR_SHIFT;
+++ +++
+++ +++ writeq_relaxed(doorbell, smmu->base + cfg[0]);
+++ +++ writel_relaxed(msg->data, smmu->base + cfg[1]);
+++ +++ writel_relaxed(MSI_CFG2_MEMATTR_DEVICE_nGnRE, smmu->base + cfg[2]);
+++ +++}
+++ +++
+++ +++static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
+++ +++{
+++ +++ struct msi_desc *desc;
+++ +++ int ret, nvec = ARM_SMMU_MAX_MSIS;
+++ +++ struct device *dev = smmu->dev;
+++ +++
+++ +++ /* Clear the MSI address regs */
+++ +++ writeq_relaxed(0, smmu->base + ARM_SMMU_GERROR_IRQ_CFG0);
+++ +++ writeq_relaxed(0, smmu->base + ARM_SMMU_EVTQ_IRQ_CFG0);
+++ +++
+++ +++ if (smmu->features & ARM_SMMU_FEAT_PRI)
+++ +++ writeq_relaxed(0, smmu->base + ARM_SMMU_PRIQ_IRQ_CFG0);
+++ +++ else
+++ +++ nvec--;
+++ +++
+++ +++ if (!(smmu->features & ARM_SMMU_FEAT_MSI))
+++ +++ return;
+++ +++
+++ +++ /* Allocate MSIs for evtq, gerror and priq. Ignore cmdq */
+++ +++ ret = platform_msi_domain_alloc_irqs(dev, nvec, arm_smmu_write_msi_msg);
+++ +++ if (ret) {
+++ +++ dev_warn(dev, "failed to allocate MSIs\n");
+++ +++ return;
+++ +++ }
+++ +++
+++ +++ for_each_msi_entry(desc, dev) {
+++ +++ switch (desc->platform.msi_index) {
+++ +++ case EVTQ_MSI_INDEX:
+++ +++ smmu->evtq.q.irq = desc->irq;
+++ +++ break;
+++ +++ case GERROR_MSI_INDEX:
+++ +++ smmu->gerr_irq = desc->irq;
+++ +++ break;
+++ +++ case PRIQ_MSI_INDEX:
+++ +++ smmu->priq.q.irq = desc->irq;
+++ +++ break;
+++ +++ default: /* Unknown */
+++ +++ continue;
+++ +++ }
+++ +++ }
+++ +++
+++ +++ /* Add callback to free MSIs on teardown */
+++ +++ devm_add_action(dev, arm_smmu_free_msis, dev);
+++ +++}
+++ +++
static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
{
int ret, irq;
return ret;
}
--- --- /* Clear the MSI address regs */
--- --- writeq_relaxed(0, smmu->base + ARM_SMMU_GERROR_IRQ_CFG0);
--- --- writeq_relaxed(0, smmu->base + ARM_SMMU_EVTQ_IRQ_CFG0);
+++ +++ arm_smmu_setup_msis(smmu);
--- --- /* Request wired interrupt lines */
+++ +++ /* Request interrupt lines */
irq = smmu->evtq.q.irq;
if (irq) {
ret = devm_request_threaded_irq(smmu->dev, irq,
}
if (smmu->features & ARM_SMMU_FEAT_PRI) {
--- --- writeq_relaxed(0, smmu->base + ARM_SMMU_PRIQ_IRQ_CFG0);
--- ---
irq = smmu->priq.q.irq;
if (irq) {
ret = devm_request_threaded_irq(smmu->dev, irq,
}
/* We only support the AArch64 table format at present */
------ if ((reg & IDR0_TTF_MASK << IDR0_TTF_SHIFT) < IDR0_TTF_AARCH64) {
++++++ switch (reg & IDR0_TTF_MASK << IDR0_TTF_SHIFT) {
++++++ case IDR0_TTF_AARCH32_64:
++++++ smmu->ias = 40;
++++++ /* Fallthrough */
++++++ case IDR0_TTF_AARCH64:
++++++ break;
++++++ default:
dev_err(smmu->dev, "AArch64 table format not supported!\n");
return -ENXIO;
}
dev_warn(smmu->dev,
"failed to set DMA mask for table walker\n");
------ if (!smmu->ias)
------ smmu->ias = smmu->oas;
++++++ smmu->ias = max(smmu->ias, smmu->oas);
dev_info(smmu->dev, "ias %lu-bit, oas %lu-bit (features 0x%08x)\n",
smmu->ias, smmu->oas, smmu->features);
if (ret)
return ret;
+++ +++ /* Record our private device structure */
+++ +++ platform_set_drvdata(pdev, smmu);
+++ +++
/* Reset the device */
ret = arm_smmu_device_reset(smmu);
if (ret)
goto out_free_structures;
--- --- /* Record our private device structure */
--- --- INIT_LIST_HEAD(&smmu->list);
--- --- spin_lock(&arm_smmu_devices_lock);
--- --- list_add(&smmu->list, &arm_smmu_devices);
--- --- spin_unlock(&arm_smmu_devices_lock);
return 0;
out_free_structures:
static int arm_smmu_device_remove(struct platform_device *pdev)
{
--- --- struct arm_smmu_device *curr, *smmu = NULL;
--- --- struct device *dev = &pdev->dev;
--- ---
--- --- spin_lock(&arm_smmu_devices_lock);
--- --- list_for_each_entry(curr, &arm_smmu_devices, list) {
--- --- if (curr->dev == dev) {
--- --- smmu = curr;
--- --- list_del(&smmu->list);
--- --- break;
--- --- }
--- --- }
--- --- spin_unlock(&arm_smmu_devices_lock);
--- ---
--- --- if (!smmu)
--- --- return -ENODEV;
+++ +++ struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
arm_smmu_device_disable(smmu);
arm_smmu_free_structures(smmu);
((smmu->options & ARM_SMMU_OPT_SECURE_CFG_ACCESS) \
? 0x400 : 0))
+++ +++#ifdef CONFIG_64BIT
+++ +++#define smmu_writeq writeq_relaxed
+++ +++#else
+++ +++#define smmu_writeq(reg64, addr) \
+++ +++ do { \
+++ +++ u64 __val = (reg64); \
+++ +++ void __iomem *__addr = (addr); \
+++ +++ writel_relaxed(__val >> 32, __addr + 4); \
+++ +++ writel_relaxed(__val, __addr); \
+++ +++ } while (0)
+++ +++#endif
+++ +++
/* Configuration registers */
#define ARM_SMMU_GR0_sCR0 0x0
#define sCR0_CLIENTPD (1 << 0)
#define ARM_SMMU_CB_SCTLR 0x0
#define ARM_SMMU_CB_RESUME 0x8
#define ARM_SMMU_CB_TTBCR2 0x10
--- ---#define ARM_SMMU_CB_TTBR0_LO 0x20
--- ---#define ARM_SMMU_CB_TTBR0_HI 0x24
--- ---#define ARM_SMMU_CB_TTBR1_LO 0x28
--- ---#define ARM_SMMU_CB_TTBR1_HI 0x2c
+++ +++#define ARM_SMMU_CB_TTBR0 0x20
+++ +++#define ARM_SMMU_CB_TTBR1 0x28
#define ARM_SMMU_CB_TTBCR 0x30
#define ARM_SMMU_CB_S1_MAIR0 0x38
#define ARM_SMMU_CB_S1_MAIR1 0x3c
#define TTBCR2_SEP_SHIFT 15
#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT)
--- ---#define TTBRn_HI_ASID_SHIFT 16
+++ +++#define TTBRn_ASID_SHIFT 48
#define FSR_MULTI (1 << 31)
#define FSR_SS (1 << 30)
struct io_pgtable_cfg *pgtbl_cfg)
{
u32 reg;
+++ +++ u64 reg64;
bool stage1;
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
struct arm_smmu_device *smmu = smmu_domain->smmu;
--- --- void __iomem *cb_base, *gr0_base, *gr1_base;
+++ +++ void __iomem *cb_base, *gr1_base;
--- --- gr0_base = ARM_SMMU_GR0(smmu);
gr1_base = ARM_SMMU_GR1(smmu);
stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS;
cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
/* TTBRs */
if (stage1) {
--- --- reg = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
--- --- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_LO);
--- --- reg = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0] >> 32;
--- --- reg |= ARM_SMMU_CB_ASID(cfg) << TTBRn_HI_ASID_SHIFT;
--- --- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_HI);
--- ---
--- --- reg = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1];
--- --- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR1_LO);
--- --- reg = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1] >> 32;
--- --- reg |= ARM_SMMU_CB_ASID(cfg) << TTBRn_HI_ASID_SHIFT;
--- --- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR1_HI);
+++ +++ reg64 = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
+++ +++
+++ +++ reg64 |= ((u64)ARM_SMMU_CB_ASID(cfg)) << TTBRn_ASID_SHIFT;
+++ +++ smmu_writeq(reg64, cb_base + ARM_SMMU_CB_TTBR0);
+++ +++
+++ +++ reg64 = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1];
+++ +++ reg64 |= ((u64)ARM_SMMU_CB_ASID(cfg)) << TTBRn_ASID_SHIFT;
+++ +++ smmu_writeq(reg64, cb_base + ARM_SMMU_CB_TTBR1);
} else {
--- --- reg = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
--- --- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_LO);
--- --- reg = pgtbl_cfg->arm_lpae_s2_cfg.vttbr >> 32;
--- --- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_HI);
+++ +++ reg64 = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
+++ +++ smmu_writeq(reg64, cb_base + ARM_SMMU_CB_TTBR0);
}
/* TTBCR */
/* ATS1 registers can only be written atomically */
va = iova & ~0xfffUL;
--- ---#ifdef CONFIG_64BIT
if (smmu->version == ARM_SMMU_V2)
--- --- writeq_relaxed(va, cb_base + ARM_SMMU_CB_ATS1PR);
+++ +++ smmu_writeq(va, cb_base + ARM_SMMU_CB_ATS1PR);
else
--- ---#endif
writel_relaxed(va, cb_base + ARM_SMMU_CB_ATS1PR);
if (readl_poll_timeout_atomic(cb_base + ARM_SMMU_CB_ATSR, tmp,
!(tmp & ATSR_ACTIVE), 5, 50)) {
dev_err(dev,
--- --- "iova to phys timed out on 0x%pad. Falling back to software table walk.\n",
+++ +++ "iova to phys timed out on %pad. Falling back to software table walk.\n",
&iova);
return ops->iova_to_phys(ops, iova);
}
kfree(data);
}
----- -static int arm_smmu_add_pci_device(struct pci_dev *pdev)
+++++ +static int arm_smmu_init_pci_device(struct pci_dev *pdev,
+++++ + struct iommu_group *group)
{
----- - int i, ret;
----- - u16 sid;
----- - struct iommu_group *group;
struct arm_smmu_master_cfg *cfg;
----- -
----- - group = iommu_group_get_for_dev(&pdev->dev);
----- - if (IS_ERR(group))
----- - return PTR_ERR(group);
+++++ + u16 sid;
+++++ + int i;
cfg = iommu_group_get_iommudata(group);
if (!cfg) {
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
----- - if (!cfg) {
----- - ret = -ENOMEM;
----- - goto out_put_group;
----- - }
+++++ + if (!cfg)
+++++ + return -ENOMEM;
iommu_group_set_iommudata(group, cfg,
__arm_smmu_release_pci_iommudata);
}
----- - if (cfg->num_streamids >= MAX_MASTER_STREAMIDS) {
----- - ret = -ENOSPC;
----- - goto out_put_group;
----- - }
+++++ + if (cfg->num_streamids >= MAX_MASTER_STREAMIDS)
+++++ + return -ENOSPC;
/*
* Assume Stream ID == Requester ID for now.
cfg->streamids[cfg->num_streamids++] = sid;
return 0;
----- -out_put_group:
----- - iommu_group_put(group);
----- - return ret;
}
----- -static int arm_smmu_add_platform_device(struct device *dev)
+++++ +static int arm_smmu_init_platform_device(struct device *dev,
+++++ + struct iommu_group *group)
{
----- - struct iommu_group *group;
----- - struct arm_smmu_master *master;
struct arm_smmu_device *smmu = find_smmu_for_device(dev);
+++++ + struct arm_smmu_master *master;
if (!smmu)
return -ENODEV;
if (!master)
return -ENODEV;
----- - /* No automatic group creation for platform devices */
----- - group = iommu_group_alloc();
----- - if (IS_ERR(group))
----- - return PTR_ERR(group);
----- -
iommu_group_set_iommudata(group, &master->cfg, NULL);
----- - return iommu_group_add_device(group, dev);
+++++ +
+++++ + return 0;
}
static int arm_smmu_add_device(struct device *dev)
{
----- - if (dev_is_pci(dev))
----- - return arm_smmu_add_pci_device(to_pci_dev(dev));
+++++ + struct iommu_group *group;
+++ + +
- return arm_smmu_add_platform_device(dev);
+++++ + group = iommu_group_get_for_dev(dev);
+++++ + if (IS_ERR(group))
+++++ + return PTR_ERR(group);
+
--- - - return arm_smmu_add_platform_device(dev);
+++++ + return 0;
}
static void arm_smmu_remove_device(struct device *dev)
iommu_group_remove_device(dev);
}
+++++ +static struct iommu_group *arm_smmu_device_group(struct device *dev)
+++++ +{
+++++ + struct iommu_group *group;
+++++ + int ret;
+++++ +
+++++ + if (dev_is_pci(dev))
+++++ + group = pci_device_group(dev);
+++++ + else
+++++ + group = generic_device_group(dev);
+++++ +
+++++ + if (IS_ERR(group))
+++++ + return group;
+++++ +
+++++ + if (dev_is_pci(dev))
+++++ + ret = arm_smmu_init_pci_device(to_pci_dev(dev), group);
+++++ + else
+++++ + ret = arm_smmu_init_platform_device(dev, group);
+++++ +
+++++ + if (ret) {
+++++ + iommu_group_put(group);
+++++ + group = ERR_PTR(ret);
+++++ + }
+++++ +
+++++ + return group;
+++++ +}
+++++ +
static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
enum iommu_attr attr, void *data)
{
.iova_to_phys = arm_smmu_iova_to_phys,
.add_device = arm_smmu_add_device,
.remove_device = arm_smmu_remove_device,
+++++ + .device_group = arm_smmu_device_group,
.domain_get_attr = arm_smmu_domain_get_attr,
.domain_set_attr = arm_smmu_domain_set_attr,
.pgsize_bitmap = -1UL, /* Restricted during device attach */
#include <linux/mempool.h>
#include <linux/memory.h>
#include <linux/timer.h>
+ +++++#include <linux/io.h>
#include <linux/iova.h>
#include <linux/iommu.h>
#include <linux/intel-iommu.h>
return -ENOMEM;
/* It is large page*/
if (largepage_lvl > 1) {
++++++ unsigned long nr_superpages, end_pfn;
++++++
pteval |= DMA_PTE_LARGE_PAGE;
lvl_pages = lvl_to_nr_pages(largepage_lvl);
++++++
++++++ nr_superpages = sg_res / lvl_pages;
++++++ end_pfn = iov_pfn + nr_superpages * lvl_pages - 1;
++++++
/*
* Ensure that old small page tables are
------ * removed to make room for superpage,
------ * if they exist.
++++++ * removed to make room for superpage(s).
*/
------ dma_pte_free_pagetable(domain, iov_pfn,
------ iov_pfn + lvl_pages - 1);
++++++ dma_pte_free_pagetable(domain, iov_pfn, end_pfn);
} else {
pteval &= ~(uint64_t)DMA_PTE_LARGE_PAGE;
}
if (ret) {
spin_unlock_irqrestore(&device_domain_lock, flags);
++++++ free_devinfo_mem(info);
return NULL;
}
DMA_PTE_READ|DMA_PTE_WRITE);
}
- -----static int iommu_prepare_identity_map(struct device *dev,
- ----- unsigned long long start,
- ----- unsigned long long end)
+ +++++static int domain_prepare_identity_map(struct device *dev,
+ +++++ struct dmar_domain *domain,
+ +++++ unsigned long long start,
+ +++++ unsigned long long end)
{
- ----- struct dmar_domain *domain;
- ----- int ret;
- -----
- ----- domain = get_domain_for_dev(dev, DEFAULT_DOMAIN_ADDRESS_WIDTH);
- ----- if (!domain)
- ----- return -ENOMEM;
- -----
/* For _hardware_ passthrough, don't bother. But for software
passthrough, we do it anyway -- it may indicate a memory
range which is reserved in E820, so which didn't get set
dmi_get_system_info(DMI_BIOS_VENDOR),
dmi_get_system_info(DMI_BIOS_VERSION),
dmi_get_system_info(DMI_PRODUCT_VERSION));
- ----- ret = -EIO;
- ----- goto error;
+ +++++ return -EIO;
}
if (end >> agaw_to_width(domain->agaw)) {
dmi_get_system_info(DMI_BIOS_VENDOR),
dmi_get_system_info(DMI_BIOS_VERSION),
dmi_get_system_info(DMI_PRODUCT_VERSION));
- ----- ret = -EIO;
- ----- goto error;
+ +++++ return -EIO;
}
- ----- ret = iommu_domain_identity_map(domain, start, end);
- ----- if (ret)
- ----- goto error;
+ +++++ return iommu_domain_identity_map(domain, start, end);
+ +++++}
- ----- return 0;
+ +++++static int iommu_prepare_identity_map(struct device *dev,
+ +++++ unsigned long long start,
+ +++++ unsigned long long end)
+ +++++{
+ +++++ struct dmar_domain *domain;
+ +++++ int ret;
+ +++++
+ +++++ domain = get_domain_for_dev(dev, DEFAULT_DOMAIN_ADDRESS_WIDTH);
+ +++++ if (!domain)
+ +++++ return -ENOMEM;
+ +++++
+ +++++ ret = domain_prepare_identity_map(dev, domain, start, end);
+ +++++ if (ret)
+ +++++ domain_exit(domain);
- ----- error:
- ----- domain_exit(domain);
return ret;
}
}
static int copy_context_table(struct intel_iommu *iommu,
- ----- struct root_entry __iomem *old_re,
+ +++++ struct root_entry *old_re,
struct context_entry **tbl,
int bus, bool ext)
{
int tbl_idx, pos = 0, idx, devfn, ret = 0, did;
- ----- struct context_entry __iomem *old_ce = NULL;
struct context_entry *new_ce = NULL, ce;
+ +++++ struct context_entry *old_ce = NULL;
struct root_entry re;
phys_addr_t old_ce_phys;
tbl_idx = ext ? bus * 2 : bus;
- ----- memcpy_fromio(&re, old_re, sizeof(re));
+ +++++ memcpy(&re, old_re, sizeof(re));
for (devfn = 0; devfn < 256; devfn++) {
/* First calculate the correct index */
}
ret = -ENOMEM;
- ----- old_ce = ioremap_cache(old_ce_phys, PAGE_SIZE);
+ +++++ old_ce = memremap(old_ce_phys, PAGE_SIZE,
+ +++++ MEMREMAP_WB);
if (!old_ce)
goto out;
}
/* Now copy the context entry */
- ----- memcpy_fromio(&ce, old_ce + idx, sizeof(ce));
+ +++++ memcpy(&ce, old_ce + idx, sizeof(ce));
if (!__context_present(&ce))
continue;
__iommu_flush_cache(iommu, new_ce, VTD_PAGE_SIZE);
out_unmap:
- ----- iounmap(old_ce);
+ +++++ memunmap(old_ce);
out:
return ret;
static int copy_translation_tables(struct intel_iommu *iommu)
{
- ----- struct root_entry __iomem *old_rt;
struct context_entry **ctxt_tbls;
+ +++++ struct root_entry *old_rt;
phys_addr_t old_rt_phys;
int ctxt_table_entries;
unsigned long flags;
if (!old_rt_phys)
return -EINVAL;
- ----- old_rt = ioremap_cache(old_rt_phys, PAGE_SIZE);
+ +++++ old_rt = memremap(old_rt_phys, PAGE_SIZE, MEMREMAP_WB);
if (!old_rt)
return -ENOMEM;
ret = 0;
out_unmap:
- ----- iounmap(old_rt);
+ +++++ memunmap(old_rt);
return ret;
}
/* Restrict dma_mask to the width that the iommu can handle */
dma_mask = min_t(uint64_t, DOMAIN_MAX_ADDR(domain->gaw), dma_mask);
+ + /* Ensure we reserve the whole size-aligned region */
+ + nrpages = __roundup_pow_of_two(nrpages);
if (!dmar_forcedac && dma_mask > DMA_BIT_MASK(32)) {
/*
static struct dmar_domain *__get_valid_domain_for_dev(struct device *dev)
{
+ +++++ struct dmar_rmrr_unit *rmrr;
struct dmar_domain *domain;
+ +++++ struct device *i_dev;
+ +++++ int i, ret;
domain = get_domain_for_dev(dev, DEFAULT_DOMAIN_ADDRESS_WIDTH);
if (!domain) {
return NULL;
}
+ +++++ /* We have a new domain - setup possible RMRRs for the device */
+ +++++ rcu_read_lock();
+ +++++ for_each_rmrr_units(rmrr) {
+ +++++ for_each_active_dev_scope(rmrr->devices, rmrr->devices_cnt,
+ +++++ i, i_dev) {
+ +++++ if (i_dev != dev)
+ +++++ continue;
+ +++++
+ +++++ ret = domain_prepare_identity_map(dev, domain,
+ +++++ rmrr->base_address,
+ +++++ rmrr->end_address);
+ +++++ if (ret)
+ +++++ dev_err(dev, "Mapping reserved region failed\n");
+ +++++ }
+ +++++ }
+ +++++ rcu_read_unlock();
+ +++++
return domain;
}
static int __init iommu_init_mempool(void)
{
int ret;
- - ret = iommu_iova_cache_init();
+ + ret = iova_cache_get();
if (ret)
return ret;
kmem_cache_destroy(iommu_domain_cache);
domain_error:
- - iommu_iova_cache_destroy();
+ + iova_cache_put();
return -ENOMEM;
}
{
kmem_cache_destroy(iommu_devinfo_cache);
kmem_cache_destroy(iommu_domain_cache);
- - iommu_iova_cache_destroy();
+ + iova_cache_put();
}
static void quirk_ioat_snb_local_iommu(struct pci_dev *pdev)
.iova_to_phys = intel_iommu_iova_to_phys,
.add_device = intel_iommu_add_device,
.remove_device = intel_iommu_remove_device,
+++++ + .device_group = pci_device_group,
.pgsize_bitmap = INTEL_IOMMU_PGSIZES,
};