diff options
60 files changed, 5892 insertions, 761 deletions
diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra124-mc.txt b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra124-mc.txt new file mode 100644 index 000000000000..4c922e839059 --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra124-mc.txt @@ -0,0 +1,12 @@ +NVIDIA Tegra124 Memory Controller device tree bindings +====================================================== + +Required properties: +- compatible: Should be "nvidia,tegra124-mc" +- reg: Physical base address and length of the controller's registers. +- interrupts: The interrupt outputs from the controller. +- #iommu-cells: Should be 1. The single cell of the IOMMU specifier defines + the SWGROUP of the master. + +This device implements an IOMMU that complies with the generic IOMMU binding. +See ../iommu/iommu.txt for details. diff --git a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt index 0823362548dc..bc0b09b0dfb5 100644 --- a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt +++ b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt @@ -1,7 +1,10 @@ NVIDIA Tegra PCIe controller Required properties: -- compatible: "nvidia,tegra20-pcie" or "nvidia,tegra30-pcie" +- compatible: Must be one of: + - "nvidia,tegra20-pcie" + - "nvidia,tegra30-pcie" + - "nvidia,tegra124-pcie" - device_type: Must be "pci" - reg: A list of physical base address and length for each set of controller registers. Must contain an entry for each entry in the reg-names property. @@ -57,6 +60,11 @@ Required properties: - afi - pcie_x +Required properties on Tegra124 and later: +- phys: Must contain an entry for each entry in phy-names. +- phy-names: Must include the following entries: + - pcie + Power supplies for Tegra20: - avdd-pex-supply: Power supply for analog PCIe logic. Must supply 1.05 V. - vdd-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V. @@ -84,6 +92,21 @@ Power supplies for Tegra30: - avdd-pexb-supply: Power supply for analog PCIe logic. Must supply 1.05 V. - vdd-pexb-supply: Power supply for digital PCIe I/O. Must supply 1.05 V. +Power supplies for Tegra124: +- Required: + - avddio-pex-supply: Power supply for analog PCIe logic. Must supply 1.05 V. + - vddio-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V. + - avdd-pex-pll-supply: Power supply for dedicated (internal) PCIe PLL. Must + supply 1.05 V. + - hvdd-pex-supply: High-voltage supply for PCIe I/O and PCIe output clocks. + Must supply 3.3 V. + - hvdd-pex-plle-supply: High-voltage supply for PLLE (shared with USB3). + Must supply 3.3 V. + - vddio-pex-ctl-supply: Power supply for PCIe control I/O partition. Must + supply 2.8-3.3 V. + - avdd-plle-supply: Power supply for PLLE (shared with USB3). Must + supply 1.05 V. + Root ports are defined as subnodes of the PCIe controller node. Required properties: diff --git a/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt b/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt index 764db86d441a..65e001a1733d 100644 --- a/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt +++ b/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt @@ -17,6 +17,7 @@ Optional properties: "pwms" property (see PWM binding[0]) - enable-gpios: contains a single GPIO specifier for the GPIO which enables and disables the backlight (see GPIO binding[1]) + - backlight-boot-off: keep the backlight disabled on boot [0]: Documentation/devicetree/bindings/pwm/pwm.txt [1]: Documentation/devicetree/bindings/gpio/gpio.txt diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index d14710b04439..befc3fe12ba6 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -264,8 +264,10 @@ IIO IO region devm_release_mem_region() devm_release_region() + devm_release_resource() devm_request_mem_region() devm_request_region() + devm_request_resource() IOMAP devm_ioport_map() diff --git a/arch/arc/include/asm/io.h b/arch/arc/include/asm/io.h index 334ce7017a18..cabd518cb253 100644 --- a/arch/arc/include/asm/io.h +++ b/arch/arc/include/asm/io.h @@ -13,8 +13,6 @@ #include <asm/byteorder.h> #include <asm/page.h> -#define PCI_IOBASE ((void __iomem *)0) - extern void __iomem *ioremap(unsigned long physaddr, unsigned long size); extern void __iomem *ioremap_prot(phys_addr_t offset, unsigned long size, unsigned long flags); diff --git a/arch/arm/boot/dts/tegra124-jetson-tk1.dts b/arch/arm/boot/dts/tegra124-jetson-tk1.dts index 624b0fba2d0a..a4221e24c4ea 100644 --- a/arch/arm/boot/dts/tegra124-jetson-tk1.dts +++ b/arch/arm/boot/dts/tegra124-jetson-tk1.dts @@ -1687,6 +1687,18 @@ vbus-supply = <&vdd_usb3_vbus>; }; + /* Serial ATA */ + sata@0,70020000 { + status = "okay"; + + hvdd-supply = <&vdd_3v3_lp0>; + vddio-supply = <&vdd_1v05_run>; + avdd-supply = <&vdd_1v05_run>; + + target-5v-supply = <&vdd_5v0_sata>; + target-12v-supply = <&vdd_12v0_sata>; + }; + clocks { compatible = "simple-bus"; #address-cells = <1>; @@ -1828,6 +1840,29 @@ enable-active-high; vin-supply = <&vdd_5v0_sys>; }; + + /* Molex power connector */ + vdd_5v0_sata: regulator@13 { + compatible = "regulator-fixed"; + reg = <13>; + regulator-name = "+5V_SATA"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio TEGRA_GPIO(EE, 2) GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_5v0_sys>; + }; + + vdd_12v0_sata: regulator@14 { + compatible = "regulator-fixed"; + reg = <14>; + regulator-name = "+12V_SATA"; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + gpio = <&gpio TEGRA_GPIO(EE, 2) GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_mux>; + }; }; sound { diff --git a/arch/arm/boot/dts/tegra124.dtsi b/arch/arm/boot/dts/tegra124.dtsi index 03916efd6fa9..1efcf6319242 100644 --- a/arch/arm/boot/dts/tegra124.dtsi +++ b/arch/arm/boot/dts/tegra124.dtsi @@ -3,6 +3,7 @@ #include <dt-bindings/pinctrl/pinctrl-tegra.h> #include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h> #include <dt-bindings/interrupt-controller/arm-gic.h> +#include <dt-bindings/memory/tegra124-mc.h> #include "skeleton.dtsi" @@ -37,6 +38,8 @@ reset-names = "dc"; nvidia,head = <0>; + + iommus = <&mc TEGRA_SWGROUP_DC>; }; dc@0,54240000 { @@ -50,6 +53,8 @@ reset-names = "dc"; nvidia,head = <1>; + + iommus = <&mc TEGRA_SWGROUP_DCB>; }; hdmi@0,54280000 { @@ -480,6 +485,15 @@ reset-names = "fuse"; }; + mc: memory-controller@0,70019000 { + compatible = "nvidia,tegra124-mc"; + reg = <0x0 0x70019000 0x0 0x1000>; + + interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>; + + #iommu-cells = <1>; + }; + hda@0,70030000 { compatible = "nvidia,tegra124-hda", "nvidia,tegra30-hda"; reg = <0x0 0x70030000 0x0 0x10000>; @@ -512,6 +526,7 @@ resets = <&tegra_car 14>; reset-names = "sdhci"; status = "disabled"; + iommus = <&mc TEGRA_SWGROUP_SDMMC1A>; }; sdhci@0,700b0200 { @@ -522,6 +537,7 @@ resets = <&tegra_car 9>; reset-names = "sdhci"; status = "disabled"; + iommus = <&mc TEGRA_SWGROUP_SDMMC2A>; }; sdhci@0,700b0400 { @@ -532,6 +548,7 @@ resets = <&tegra_car 69>; reset-names = "sdhci"; status = "disabled"; + iommus = <&mc TEGRA_SWGROUP_SDMMC3A>; }; sdhci@0,700b0600 { @@ -542,6 +559,7 @@ resets = <&tegra_car 15>; reset-names = "sdhci"; status = "disabled"; + iommus = <&mc TEGRA_SWGROUP_SDMMC4A>; }; ahub@0,70300000 { @@ -756,6 +774,31 @@ status = "disabled"; }; + sata@0,70020000 { + compatible = "nvidia,tegra124-ahci"; + + reg = <0x0 0x70027000 0x0 0x2000>, /* AHCI */ + <0x0 0x70020000 0x0 0x7000>; /* SATA */ + + interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>; + + clocks = <&tegra_car TEGRA124_CLK_SATA>, + <&tegra_car TEGRA124_CLK_SATA_OOB>, + <&tegra_car TEGRA124_CLK_CML1>, + <&tegra_car TEGRA124_CLK_PLL_E>; + clock-names = "sata", "sata-oob", "cml1", "pll_e"; + + resets = <&tegra_car 124>, + <&tegra_car 123>, + <&tegra_car 129>; + reset-names = "sata", "sata-oob", "sata-cold"; + + phys = <&padctl TEGRA_XUSB_PADCTL_SATA>; + phy-names = "sata-phy"; + + status = "disabled"; + }; + cpus { #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm/configs/tegra_defconfig b/arch/arm/configs/tegra_defconfig index 285c433a9aad..2b8be4850347 100644 --- a/arch/arm/configs/tegra_defconfig +++ b/arch/arm/configs/tegra_defconfig @@ -102,6 +102,9 @@ CONFIG_BLK_DEV_SD=y CONFIG_BLK_DEV_SR=y CONFIG_SCSI_MULTI_LUN=y # CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +CONFIG_SATA_AHCI=y +CONFIG_AHCI_TEGRA=y CONFIG_NETDEVICES=y CONFIG_DUMMY=y CONFIG_IGB=y diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h index 3d23418cbddd..6719c6be2234 100644 --- a/arch/arm/include/asm/io.h +++ b/arch/arm/include/asm/io.h @@ -69,6 +69,7 @@ extern void __raw_readsl(const void __iomem *addr, void *data, int longlen); * writeback addressing modes as these incur a significant performance * overhead (the address generation must be emulated in software). */ +#define __raw_writew __raw_writew static inline void __raw_writew(u16 val, volatile void __iomem *addr) { asm volatile("strh %1, %0" @@ -76,6 +77,7 @@ static inline void __raw_writew(u16 val, volatile void __iomem *addr) : "r" (val)); } +#define __raw_readw __raw_readw static inline u16 __raw_readw(const volatile void __iomem *addr) { u16 val; @@ -86,6 +88,7 @@ static inline u16 __raw_readw(const volatile void __iomem *addr) } #endif +#define __raw_writeb __raw_writeb static inline void __raw_writeb(u8 val, volatile void __iomem *addr) { asm volatile("strb %1, %0" @@ -93,6 +96,7 @@ static inline void __raw_writeb(u8 val, volatile void __iomem *addr) : "r" (val)); } +#define __raw_writel __raw_writel static inline void __raw_writel(u32 val, volatile void __iomem *addr) { asm volatile("str %1, %0" @@ -100,6 +104,7 @@ static inline void __raw_writel(u32 val, volatile void __iomem *addr) : "r" (val)); } +#define __raw_readb __raw_readb static inline u8 __raw_readb(const volatile void __iomem *addr) { u8 val; @@ -109,6 +114,7 @@ static inline u8 __raw_readb(const volatile void __iomem *addr) return val; } +#define __raw_readl __raw_readl static inline u32 __raw_readl(const volatile void __iomem *addr) { u32 val; @@ -186,6 +192,7 @@ static inline void pci_ioremap_set_mem_type(int mem_type) {} #endif extern int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr); +extern void pci_iounmap_io(unsigned int offset); /* * Now, pick up the machine-defined IO definitions @@ -266,20 +273,6 @@ extern int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr); #define insl(p,d,l) __raw_readsl(__io(p),d,l) #endif -#define outb_p(val,port) outb((val),(port)) -#define outw_p(val,port) outw((val),(port)) -#define outl_p(val,port) outl((val),(port)) -#define inb_p(port) inb((port)) -#define inw_p(port) inw((port)) -#define inl_p(port) inl((port)) - -#define outsb_p(port,from,len) outsb(port,from,len) -#define outsw_p(port,from,len) outsw(port,from,len) -#define outsl_p(port,from,len) outsl(port,from,len) -#define insb_p(port,to,len) insb(port,to,len) -#define insw_p(port,to,len) insw(port,to,len) -#define insl_p(port,to,len) insl(port,to,len) - /* * String version of IO memory access ops: */ @@ -346,40 +339,38 @@ extern void _memset_io(volatile void __iomem *, int, size_t); #define iounmap __arm_iounmap /* - * io{read,write}{8,16,32} macros + * io{read,write}{16,32}be() macros */ -#ifndef ioread8 -#define ioread8(p) ({ unsigned int __v = __raw_readb(p); __iormb(); __v; }) -#define ioread16(p) ({ unsigned int __v = le16_to_cpu((__force __le16)__raw_readw(p)); __iormb(); __v; }) -#define ioread32(p) ({ unsigned int __v = le32_to_cpu((__force __le32)__raw_readl(p)); __iormb(); __v; }) - -#define ioread16be(p) ({ unsigned int __v = be16_to_cpu((__force __be16)__raw_readw(p)); __iormb(); __v; }) -#define ioread32be(p) ({ unsigned int __v = be32_to_cpu((__force __be32)__raw_readl(p)); __iormb(); __v; }) - -#define iowrite8(v,p) ({ __iowmb(); __raw_writeb(v, p); }) -#define iowrite16(v,p) ({ __iowmb(); __raw_writew((__force __u16)cpu_to_le16(v), p); }) -#define iowrite32(v,p) ({ __iowmb(); __raw_writel((__force __u32)cpu_to_le32(v), p); }) +#define ioread16be(p) ({ __u16 __v = be16_to_cpu((__force __be16)__raw_readw(p)); __iormb(); __v; }) +#define ioread32be(p) ({ __u32 __v = be32_to_cpu((__force __be32)__raw_readl(p)); __iormb(); __v; }) -#define iowrite16be(v,p) ({ __iowmb(); __raw_writew((__force __u16)cpu_to_be16(v), p); }) -#define iowrite32be(v,p) ({ __iowmb(); __raw_writel((__force __u32)cpu_to_be32(v), p); }) - -#define ioread8_rep(p,d,c) __raw_readsb(p,d,c) -#define ioread16_rep(p,d,c) __raw_readsw(p,d,c) -#define ioread32_rep(p,d,c) __raw_readsl(p,d,c) - -#define iowrite8_rep(p,s,c) __raw_writesb(p,s,c) -#define iowrite16_rep(p,s,c) __raw_writesw(p,s,c) -#define iowrite32_rep(p,s,c) __raw_writesl(p,s,c) +#define iowrite16be(v,p) ({ __iowmb(); __raw_writew((__force __u16)cpu_to_be16(v), p); }) +#define iowrite32be(v,p) ({ __iowmb(); __raw_writew((__force __u32)cpu_to_be32(v), p); }) +#define ioport_map ioport_map extern void __iomem *ioport_map(unsigned long port, unsigned int nr); +#define ioport_unmap ioport_unmap extern void ioport_unmap(void __iomem *addr); -#endif struct pci_dev; +#define pci_iounmap pci_iounmap extern void pci_iounmap(struct pci_dev *dev, void __iomem *addr); /* + * Convert a physical pointer to a virtual kernel pointer for /dev/mem + * access + */ +#define xlate_dev_mem_ptr(p) __va(p) + +/* + * Convert a virtual cached pointer to an uncached pointer + */ +#define xlate_dev_kmem_ptr(p) p + +#include <asm-generic/io.h> + +/* * can the hardware map this into one segment or not, given no other * constraints. */ @@ -401,17 +392,6 @@ extern int devmem_is_allowed(unsigned long pfn); #endif /* - * Convert a physical pointer to a virtual kernel pointer for /dev/mem - * access - */ -#define xlate_dev_mem_ptr(p) __va(p) - -/* - * Convert a virtual cached pointer to an uncached pointer - */ -#define xlate_dev_kmem_ptr(p) p - -/* * Register ISA memory and port locations for glibc iopl/inb/outb * emulation. */ diff --git a/arch/arm/include/asm/mach/pci.h b/arch/arm/include/asm/mach/pci.h index 7fc42784becb..bf4ff74afd8a 100644 --- a/arch/arm/include/asm/mach/pci.h +++ b/arch/arm/include/asm/mach/pci.h @@ -25,6 +25,7 @@ struct hw_pci { struct pci_ops *ops; int nr_controllers; void **private_data; + struct list_head *sys; int (*setup)(int nr, struct pci_sys_data *); struct pci_bus *(*scan)(int nr, struct pci_sys_data *); void (*preinit)(void); @@ -38,6 +39,7 @@ struct hw_pci { resource_size_t align); void (*add_bus)(struct pci_bus *bus); void (*remove_bus)(struct pci_bus *bus); + void (*teardown)(int nr, struct pci_sys_data *); }; /* @@ -49,6 +51,7 @@ struct pci_sys_data { #endif struct list_head node; int busnr; /* primary bus number */ + int nr; /* controller number */ u64 mem_offset; /* bus->cpu memory mapping offset */ unsigned long io_offset; /* bus->cpu IO mapping offset */ struct pci_bus *bus; /* PCI bus */ @@ -67,6 +70,7 @@ struct pci_sys_data { resource_size_t align); void (*add_bus)(struct pci_bus *bus); void (*remove_bus)(struct pci_bus *bus); + void (*teardown)(int nr, struct pci_sys_data *); void *private_data; /* platform controller private data */ }; @@ -84,6 +88,8 @@ static inline void pci_common_init(struct hw_pci *hw) pci_common_init_dev(NULL, hw); } +void pci_common_exit(struct list_head *head); + /* * Setup early fixed I/O mapping. */ diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h index e731018869a7..1c187611d3c7 100644 --- a/arch/arm/include/asm/memory.h +++ b/arch/arm/include/asm/memory.h @@ -274,11 +274,13 @@ static inline unsigned long __phys_to_virt(phys_addr_t x) * translation for translating DMA addresses. Use the driver * DMA support - see dma-mapping.h. */ +#define virt_to_phys virt_to_phys static inline phys_addr_t virt_to_phys(const volatile void *x) { return __virt_to_phys((unsigned long)(x)); } +#define phys_to_virt phys_to_virt static inline void *phys_to_virt(phys_addr_t x) { return (void *)__phys_to_virt(x); diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 17a26c17f7f5..570a248e0737 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -472,11 +472,13 @@ static void pcibios_init_hw(struct device *parent, struct hw_pci *hw, sys->domain = hw->domain; #endif sys->busnr = busnr; + sys->nr = nr; sys->swizzle = hw->swizzle; sys->map_irq = hw->map_irq; sys->align_resource = hw->align_resource; sys->add_bus = hw->add_bus; sys->remove_bus = hw->remove_bus; + sys->teardown = hw->teardown; INIT_LIST_HEAD(&sys->resources); if (hw->private_data) @@ -514,18 +516,24 @@ static void pcibios_init_hw(struct device *parent, struct hw_pci *hw, void pci_common_init_dev(struct device *parent, struct hw_pci *hw) { struct pci_sys_data *sys; - LIST_HEAD(head); + struct list_head *head; + LIST_HEAD(list); + + if (hw->sys) + head = hw->sys; + else + head = &list; pci_add_flags(PCI_REASSIGN_ALL_RSRC); if (hw->preinit) hw->preinit(); - pcibios_init_hw(parent, hw, &head); + pcibios_init_hw(parent, hw, head); if (hw->postinit) hw->postinit(); pci_fixup_irqs(pcibios_swizzle, pcibios_map_irq); - list_for_each_entry(sys, &head, node) { + list_for_each_entry(sys, head, node) { struct pci_bus *bus = sys->bus; if (!pci_has_flag(PCI_PROBE_ONLY)) { @@ -546,7 +554,7 @@ void pci_common_init_dev(struct device *parent, struct hw_pci *hw) pci_bus_add_devices(bus); } - list_for_each_entry(sys, &head, node) { + list_for_each_entry(sys, head, node) { struct pci_bus *bus = sys->bus; /* Configure PCI Express settings */ @@ -558,6 +566,26 @@ void pci_common_init_dev(struct device *parent, struct hw_pci *hw) } } } +EXPORT_SYMBOL(pci_common_init_dev); + +void pci_common_exit(struct list_head *head) +{ + struct pci_sys_data *sys, *tmp; + + list_for_each_entry_safe(sys, tmp, head, node) { + pci_stop_root_bus(sys->bus); + pci_remove_root_bus(sys->bus); + list_del(&sys->node); + + release_resource(&sys->io_res); + + if (sys->teardown) + sys->teardown(sys->nr, sys); + + kfree(sys); + } +} +EXPORT_SYMBOL(pci_common_exit); #ifndef CONFIG_PCI_HOST_ITE8152 void pcibios_set_master(struct pci_dev *dev) diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index 095399618ca5..e7b9afc7afc2 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -2,6 +2,7 @@ menuconfig ARCH_TEGRA bool "NVIDIA Tegra" if ARCH_MULTI_V7 select ARCH_REQUIRE_GPIOLIB select ARCH_SUPPORTS_TRUSTED_FOUNDATIONS + select ARM_DMA_USE_IOMMU select ARM_GIC select CLKSRC_MMIO select HAVE_ARM_SCU if SMP diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c index 316563141add..7d7e6d3ce32d 100644 --- a/arch/arm/mach-tegra/cpuidle.c +++ b/arch/arm/mach-tegra/cpuidle.c @@ -57,3 +57,4 @@ void tegra_cpuidle_pcie_irqs_in_use(void) break; } } +EXPORT_SYMBOL(tegra_cpuidle_pcie_irqs_in_use); diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c index d1e5ad7ab3bc..f4685f18e5d1 100644 --- a/arch/arm/mm/ioremap.c +++ b/arch/arm/mm/ioremap.c @@ -455,4 +455,10 @@ int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr) __pgprot(get_mem_type(pci_ioremap_mem_type)->prot_pte)); } EXPORT_SYMBOL_GPL(pci_ioremap_io); + +void pci_iounmap_io(unsigned int offset) +{ + unmap_kernel_range(PCI_IO_VIRT_BASE + offset, SZ_64K); +} +EXPORT_SYMBOL_GPL(pci_iounmap_io); #endif diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 62b4ae1c5bbf..c06bd0b67865 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -23,7 +23,6 @@ config ARM64 select GENERIC_CLOCKEVENTS_BROADCAST if SMP select GENERIC_CPU_AUTOPROBE select GENERIC_EARLY_IOREMAP - select GENERIC_IOMAP select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW select GENERIC_SCHED_CLOCK diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h index e0ecdcf6632d..a8e7a717c360 100644 --- a/arch/arm64/include/asm/io.h +++ b/arch/arm64/include/asm/io.h @@ -34,26 +34,31 @@ /* * Generic IO read/write. These perform native-endian accesses. */ +#define __raw_writeb __raw_writeb static inline void __raw_writeb(u8 val, volatile void __iomem *addr) { asm volatile("strb %w0, [%1]" : : "r" (val), "r" (addr)); } +#define __raw_writew __raw_writew static inline void __raw_writew(u16 val, volatile void __iomem *addr) { asm volatile("strh %w0, [%1]" : : "r" (val), "r" (addr)); } +#define __raw_writel __raw_writel static inline void __raw_writel(u32 val, volatile void __iomem *addr) { asm volatile("str %w0, [%1]" : : "r" (val), "r" (addr)); } +#define __raw_writeq __raw_writeq static inline void __raw_writeq(u64 val, volatile void __iomem *addr) { asm volatile("str %0, [%1]" : : "r" (val), "r" (addr)); } +#define __raw_readb __raw_readb static inline u8 __raw_readb(const volatile void __iomem *addr) { u8 val; @@ -61,6 +66,7 @@ static inline u8 __raw_readb(const volatile void __iomem *addr) return val; } +#define __raw_readw __raw_readw static inline u16 __raw_readw(const volatile void __iomem *addr) { u16 val; @@ -68,6 +74,7 @@ static inline u16 __raw_readw(const volatile void __iomem *addr) return val; } +#define __raw_readl __raw_readl static inline u32 __raw_readl(const volatile void __iomem *addr) { u32 val; @@ -75,6 +82,7 @@ static inline u32 __raw_readl(const volatile void __iomem *addr) return val; } +#define __raw_readq __raw_readq static inline u64 __raw_readq(const volatile void __iomem *addr) { u64 val; @@ -124,94 +132,6 @@ static inline u64 __raw_readq(const volatile void __iomem *addr) #define IO_SPACE_LIMIT 0xffff #define PCI_IOBASE ((void __iomem *)(MODULES_VADDR - SZ_32M)) -static inline u8 inb(unsigned long addr) -{ - return readb(addr + PCI_IOBASE); -} - -static inline u16 inw(unsigned long addr) -{ - return readw(addr + PCI_IOBASE); -} - -static inline u32 inl(unsigned long addr) -{ - return readl(addr + PCI_IOBASE); -} - -static inline void outb(u8 b, unsigned long addr) -{ - writeb(b, addr + PCI_IOBASE); -} - -static inline void outw(u16 b, unsigned long addr) -{ - writew(b, addr + PCI_IOBASE); -} - -static inline void outl(u32 b, unsigned long addr) -{ - writel(b, addr + PCI_IOBASE); -} - -#define inb_p(addr) inb(addr) -#define inw_p(addr) inw(addr) -#define inl_p(addr) inl(addr) - -#define outb_p(x, addr) outb((x), (addr)) -#define outw_p(x, addr) outw((x), (addr)) -#define outl_p(x, addr) outl((x), (addr)) - -static inline void insb(unsigned long addr, void *buffer, int count) -{ - u8 *buf = buffer; - while (count--) - *buf++ = __raw_readb(addr + PCI_IOBASE); -} - -static inline void insw(unsigned long addr, void *buffer, int count) -{ - u16 *buf = buffer; - while (count--) - *buf++ = __raw_readw(addr + PCI_IOBASE); -} - -static inline void insl(unsigned long addr, void *buffer, int count) -{ - u32 *buf = buffer; - while (count--) - *buf++ = __raw_readl(addr + PCI_IOBASE); -} - -static inline void outsb(unsigned long addr, const void *buffer, int count) -{ - const u8 *buf = buffer; - while (count--) - __raw_writeb(*buf++, addr + PCI_IOBASE); -} - -static inline void outsw(unsigned long addr, const void *buffer, int count) -{ - const u16 *buf = buffer; - while (count--) - __raw_writew(*buf++, addr + PCI_IOBASE); -} - -static inline void outsl(unsigned long addr, const void *buffer, int count) -{ - const u32 *buf = buffer; - while (count--) - __raw_writel(*buf++, addr + PCI_IOBASE); -} - -#define insb_p(port,to,len) insb(port,to,len) -#define insw_p(port,to,len) insw(port,to,len) -#define insl_p(port,to,len) insl(port,to,len) - -#define outsb_p(port,from,len) outsb(port,from,len) -#define outsw_p(port,from,len) outsw(port,from,len) -#define outsl_p(port,from,len) outsl(port,from,len) - /* * String version of I/O memory access operations. */ @@ -235,18 +155,14 @@ extern void __iomem *ioremap_cache(phys_addr_t phys_addr, size_t size); #define ioremap_wc(addr, size) __ioremap((addr), (size), __pgprot(PROT_NORMAL_NC)) #define iounmap __iounmap -#define ARCH_HAS_IOREMAP_WC -#include <asm-generic/iomap.h> - /* - * More restrictive address range checking than the default implementation - * (PHYS_OFFSET and PHYS_MASK taken into account). + * io{read,write}{16,32}be() macros */ -#define ARCH_HAS_VALID_PHYS_ADDR_RANGE -extern int valid_phys_addr_range(unsigned long addr, size_t size); -extern int valid_mmap_phys_addr_range(unsigned long pfn, size_t size); +#define ioread16be(p) ({ __u16 __v = be16_to_cpu((__force __be16)__raw_readw(p)); __iormb(); __v; }) +#define ioread32be(p) ({ __u32 __v = be32_to_cpu((__force __be32)__raw_readl(p)); __iormb(); __v; }) -extern int devmem_is_allowed(unsigned long pfn); +#define iowrite16be(v,p) ({ __iowmb(); __raw_writew((__force __u16)cpu_to_be16(v), p); }) +#define iowrite32be(v,p) ({ __iowmb(); __raw_writew((__force __u32)cpu_to_be32(v), p); }) /* * Convert a physical pointer to a virtual kernel pointer for /dev/mem @@ -259,6 +175,18 @@ extern int devmem_is_allowed(unsigned long pfn); */ #define xlate_dev_kmem_ptr(p) p +#include <asm-generic/io.h> + +/* + * More restrictive address range checking than the default implementation + * (PHYS_OFFSET and PHYS_MASK taken into account). + */ +#define ARCH_HAS_VALID_PHYS_ADDR_RANGE +extern int valid_phys_addr_range(unsigned long addr, size_t size); +extern int valid_mmap_phys_addr_range(unsigned long pfn, size_t size); + +extern int devmem_is_allowed(unsigned long pfn); + struct bio_vec; extern bool xen_biovec_phys_mergeable(const struct bio_vec *vec1, const struct bio_vec *vec2); diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h index ccc7087d3c4e..a40a4d7e830f 100644 --- a/arch/arm64/include/asm/memory.h +++ b/arch/arm64/include/asm/memory.h @@ -120,11 +120,13 @@ extern phys_addr_t memstart_addr; * translation for translating DMA addresses. Use the driver * DMA support - see dma-mapping.h. */ +#define virt_to_phys virt_to_phys static inline phys_addr_t virt_to_phys(const volatile void *x) { return __virt_to_phys((unsigned long)(x)); } +#define phys_to_virt phys_to_virt static inline void *phys_to_virt(phys_addr_t x) { return (void *)(__phys_to_virt(x)); diff --git a/arch/ia64/include/asm/uaccess.h b/arch/ia64/include/asm/uaccess.h index 449c8c0fa2bd..103bedc59644 100644 --- a/arch/ia64/include/asm/uaccess.h +++ b/arch/ia64/include/asm/uaccess.h @@ -365,15 +365,15 @@ ia64_done_with_exception (struct pt_regs *regs) } #define ARCH_HAS_TRANSLATE_MEM_PTR 1 -static __inline__ char * -xlate_dev_mem_ptr (unsigned long p) +static __inline__ void * +xlate_dev_mem_ptr(phys_addr_t p) { struct page *page; - char * ptr; + void *ptr; page = pfn_to_page(p >> PAGE_SHIFT); if (PageUncached(page)) - ptr = (char *)p + __IA64_UNCACHED_OFFSET; + ptr = (void *)p + __IA64_UNCACHED_OFFSET; else ptr = __va(p); @@ -383,15 +383,15 @@ xlate_dev_mem_ptr (unsigned long p) /* * Convert a virtual cached kernel memory pointer to an uncached pointer */ -static __inline__ char * -xlate_dev_kmem_ptr (char * p) +static __inline__ void * +xlate_dev_kmem_ptr(void *p) { struct page *page; - char * ptr; + void *ptr; page = virt_to_page((unsigned long)p); if (PageUncached(page)) - ptr = (char *)__pa(p) + __IA64_UNCACHED_OFFSET; + ptr = (void *)__pa(p) + __IA64_UNCACHED_OFFSET; else ptr = p; diff --git a/arch/s390/include/asm/io.h b/arch/s390/include/asm/io.h index cd6b9ee7b69c..d22c2eeae78f 100644 --- a/arch/s390/include/asm/io.h +++ b/arch/s390/include/asm/io.h @@ -13,9 +13,10 @@ #include <asm/page.h> #include <asm/pci_io.h> -void *xlate_dev_mem_ptr(unsigned long phys); #define xlate_dev_mem_ptr xlate_dev_mem_ptr -void unxlate_dev_mem_ptr(unsigned long phys, void *addr); +void *xlate_dev_mem_ptr(phys_addr_t phys); +#define unxlate_dev_mem_ptr unxlate_dev_mem_ptr +void unxlate_dev_mem_ptr(phys_addr_t phys, void *addr); /* * Convert a virtual cached pointer to an uncached pointer diff --git a/arch/s390/mm/maccess.c b/arch/s390/mm/maccess.c index 2a2e35416d2f..2eb34bdfc613 100644 --- a/arch/s390/mm/maccess.c +++ b/arch/s390/mm/maccess.c @@ -176,7 +176,7 @@ static int is_swapped(unsigned long addr) * For swapped prefix pages a new buffer is returned that contains a copy of * the absolute memory. The buffer size is maximum one page large. */ -void *xlate_dev_mem_ptr(unsigned long addr) +void *xlate_dev_mem_ptr(phys_addr_t addr) { void *bounce = (void *) addr; unsigned long size; @@ -197,7 +197,7 @@ void *xlate_dev_mem_ptr(unsigned long addr) /* * Free converted buffer for /dev/mem access (if necessary) */ -void unxlate_dev_mem_ptr(unsigned long addr, void *buf) +void unxlate_dev_mem_ptr(phys_addr_t addr, void *buf) { if ((void *) addr != buf) free_page((unsigned long) buf); diff --git a/arch/sparc/include/asm/io_32.h b/arch/sparc/include/asm/io_32.h index 9f532902627c..49267f2575d7 100644 --- a/arch/sparc/include/asm/io_32.h +++ b/arch/sparc/include/asm/io_32.h @@ -16,18 +16,15 @@ #include <asm-generic/io.h> -static inline void _memset_io(volatile void __iomem *dst, - int c, __kernel_size_t n) +static inline void _memset_io(void __iomem *dst, int c, __kernel_size_t n) { - volatile void __iomem *d = dst; - while (n--) { - writeb(c, d); - d++; + writeb(c, dst); + dst++; } } -static inline void _memcpy_fromio(void *dst, const volatile void __iomem *src, +static inline void _memcpy_fromio(void *dst, const void __iomem *src, __kernel_size_t n) { char *d = dst; @@ -39,16 +36,15 @@ static inline void _memcpy_fromio(void *dst, const volatile void __iomem *src, } } -static inline void _memcpy_toio(volatile void __iomem *dst, const void *src, +static inline void _memcpy_toio(void __iomem *dst, const void *src, __kernel_size_t n) { const char *s = src; - volatile void __iomem *d = dst; while (n--) { char tmp = *s++; - writeb(tmp, d); - d++; + writeb(tmp, dst); + dst++; } } diff --git a/arch/x86/include/asm/io.h b/arch/x86/include/asm/io.h index b8237d8a1e0c..ae2b593e7c6e 100644 --- a/arch/x86/include/asm/io.h +++ b/arch/x86/include/asm/io.h @@ -310,8 +310,8 @@ BUILDIO(b, b, char) BUILDIO(w, w, short) BUILDIO(l, , int) -extern void *xlate_dev_mem_ptr(unsigned long phys); -extern void unxlate_dev_mem_ptr(unsigned long phys, void *addr); +extern void *xlate_dev_mem_ptr(phys_addr_t phys); +extern void unxlate_dev_mem_ptr(phys_addr_t phys, void *addr); extern int ioremap_change_attr(unsigned long vaddr, unsigned long size, unsigned long prot_val); diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c index baff1da354e0..6d9cc45123e5 100644 --- a/arch/x86/mm/ioremap.c +++ b/arch/x86/mm/ioremap.c @@ -315,7 +315,7 @@ EXPORT_SYMBOL(iounmap); * Convert a physical pointer to a virtual kernel pointer for /dev/mem * access */ -void *xlate_dev_mem_ptr(unsigned long phys) +void *xlate_dev_mem_ptr(phys_addr_t phys) { void *addr; unsigned long start = phys & PAGE_MASK; @@ -331,7 +331,7 @@ void *xlate_dev_mem_ptr(unsigned long phys) return addr; } -void unxlate_dev_mem_ptr(unsigned long phys, void *addr) +void unxlate_dev_mem_ptr(phys_addr_t phys, void *addr) { if (page_is_ram(phys >> PAGE_SHIFT)) return; diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 917403fe10da..a304e8a6ebf2 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -84,9 +84,12 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size) } #endif -void __weak unxlate_dev_mem_ptr(unsigned long phys, void *addr) +#ifndef unxlate_dev_mem_ptr +#define unxlate_dev_mem_ptr unxlate_dev_mem_ptr +void __weak unxlate_dev_mem_ptr(phys_addr_t phys, void *addr) { } +#endif /* * This funcion reads the *physical* memory. The f_pos points directly to the @@ -97,7 +100,7 @@ static ssize_t read_mem(struct file *file, char __user *buf, { phys_addr_t p = *ppos; ssize_t read, sz; - char *ptr; + void *ptr; if (p != *ppos) return 0; @@ -400,7 +403,7 @@ static ssize_t read_kmem(struct file *file, char __user *buf, * uncached, then it must also be accessed uncached * by the kernel or data corruption may occur */ - kbuf = xlate_dev_kmem_ptr((char *)p); + kbuf = xlate_dev_kmem_ptr((void *)p); if (copy_to_user(buf, kbuf, sz)) return -EFAULT; @@ -461,7 +464,7 @@ static ssize_t do_write_kmem(unsigned long p, const char __user *buf, #endif while (count > 0) { - char *ptr; + void *ptr; sz = size_inside_page(p, count); @@ -470,7 +473,7 @@ static ssize_t do_write_kmem(unsigned long p, const char __user *buf, * it must also be accessed uncached by the kernel or data * corruption may occur. */ - ptr = xlate_dev_kmem_ptr((char *)p); + ptr = xlate_dev_kmem_ptr((void *)p); copied = copy_from_user(ptr, buf, sz); if (copied) { diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 6553fd238685..80b224fa9c3a 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -9,6 +9,7 @@ #include <linux/clk.h> #include <linux/debugfs.h> +#include <linux/iommu.h> #include <linux/reset.h> #include "dc.h" @@ -1287,6 +1288,12 @@ static int tegra_dc_init(struct host1x_client *client) struct tegra_drm *tegra = drm->dev_private; int err; + if (tegra->domain) { + err = iommu_attach_device(tegra->domain, dc->dev); + if (!err) + dc->domain = tegra->domain; + } + drm_crtc_init(drm, &dc->base, &tegra_crtc_funcs); drm_mode_crtc_set_gamma_size(&dc->base, 256); drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs); @@ -1344,6 +1351,11 @@ static int tegra_dc_exit(struct host1x_client *client) return err; } + if (dc->domain) { + iommu_detach_device(dc->domain, dc->dev); + dc->domain = NULL; + } + return 0; } diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 59736bb810cd..1d2bbafad982 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -8,6 +8,7 @@ */ #include <linux/host1x.h> +#include <linux/iommu.h> #include "drm.h" #include "gem.h" @@ -33,6 +34,16 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) if (!tegra) return -ENOMEM; + if (iommu_present(&platform_bus_type)) { + tegra->domain = iommu_domain_alloc(&platform_bus_type); + if (IS_ERR(tegra->domain)) { + kfree(tegra); + return PTR_ERR(tegra->domain); + } + + drm_mm_init(&tegra->mm, 0, SZ_2G); + } + mutex_init(&tegra->clients_lock); INIT_LIST_HEAD(&tegra->clients); drm->dev_private = tegra; @@ -71,6 +82,7 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) static int tegra_drm_unload(struct drm_device *drm) { struct host1x_device *device = to_host1x_device(drm->dev); + struct tegra_drm *tegra = drm->dev_private; int err; drm_kms_helper_poll_fini(drm); @@ -82,6 +94,11 @@ static int tegra_drm_unload(struct drm_device *drm) if (err < 0) return err; + if (tegra->domain) { + iommu_domain_free(tegra->domain); + drm_mm_takedown(&tegra->mm); + } + return 0; } diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index e89c70fa82d5..27f87acb96a7 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -39,6 +39,9 @@ struct tegra_fbdev { struct tegra_drm { struct drm_device *drm; + struct iommu_domain *domain; + struct drm_mm mm; + struct mutex clients_lock; struct list_head clients; @@ -120,6 +123,8 @@ struct tegra_dc { struct drm_pending_vblank_event *event; const struct tegra_dc_soc_info *soc; + + struct iommu_domain *domain; }; static inline struct tegra_dc * diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 3513d12d5aa1..c5fa3c4b2ed5 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -65,8 +65,12 @@ static void tegra_fb_destroy(struct drm_framebuffer *framebuffer) for (i = 0; i < fb->num_planes; i++) { struct tegra_bo *bo = fb->planes[i]; - if (bo) + if (bo) { + if (bo->pages && bo->vaddr) + vunmap(bo->vaddr); + drm_gem_object_unreference_unlocked(&bo->gem); + } } drm_framebuffer_cleanup(framebuffer); @@ -254,6 +258,16 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper, offset = info->var.xoffset * bytes_per_pixel + info->var.yoffset * fb->pitches[0]; + if (bo->pages) { + bo->vaddr = vmap(bo->pages, bo->num_pages, VM_MAP, + pgprot_writecombine(PAGE_KERNEL)); + if (!bo->vaddr) { + dev_err(drm->dev, "failed to vmap() framebuffer\n"); + err = -ENOMEM; + goto destroy; + } + } + drm->mode_config.fb_base = (resource_size_t)bo->paddr; info->screen_base = (void __iomem *)bo->vaddr + offset; info->screen_size = size; diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index ce023fa3e8ae..48e567bf8899 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -14,6 +14,7 @@ */ #include <linux/dma-buf.h> +#include <linux/iommu.h> #include <drm/tegra_drm.h> #include "drm.h" @@ -91,14 +92,144 @@ static const struct host1x_bo_ops tegra_bo_ops = { .kunmap = tegra_bo_kunmap, }; +static int iommu_map_sg(struct iommu_domain *domain, struct sg_table *sgt, + dma_addr_t iova, int prot) +{ + unsigned long offset = 0; + struct scatterlist *sg; + unsigned int i, j; + int err; + + for_each_sg(sgt->sgl, sg, sgt->nents, i) { + dma_addr_t phys = sg_phys(sg); + size_t length = sg->offset; + + phys = sg_phys(sg) - sg->offset; + length = sg->length + sg->offset; + + err = iommu_map(domain, iova + offset, phys, length, prot); + if (err < 0) + goto unmap; + + offset += length; + } + + return 0; + +unmap: + offset = 0; + + for_each_sg(sgt->sgl, sg, i, j) { + size_t length = sg->length + sg->offset; + iommu_unmap(domain, iova + offset, length); + offset += length; + } + + return err; +} + +static int iommu_unmap_sg(struct iommu_domain *domain, struct sg_table *sgt, + dma_addr_t iova) +{ + unsigned long offset = 0; + struct scatterlist *sg; + unsigned int i; + + for_each_sg(sgt->sgl, sg, sgt->nents, i) { + dma_addr_t phys = sg_phys(sg); + size_t length = sg->offset; + + phys = sg_phys(sg) - sg->offset; + length = sg->length + sg->offset; + + iommu_unmap(domain, iova + offset, length); + offset += length; + } + + return 0; +} + +static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) +{ + int prot = IOMMU_READ | IOMMU_WRITE; + int err; + + if (bo->mm) + return -EBUSY; + + bo->mm = kzalloc(sizeof(*bo->mm), GFP_KERNEL); + if (!bo->mm) + return -ENOMEM; + + err = drm_mm_insert_node_generic(&tegra->mm, bo->mm, bo->gem.size, + PAGE_SIZE, 0, 0, 0); + if (err < 0) { + dev_err(tegra->drm->dev, "out of virtual memory: %d\n", err); + return err; + } + + bo->paddr = bo->mm->start; + + err = iommu_map_sg(tegra->domain, bo->sgt, bo->paddr, prot); + if (err < 0) { + dev_err(tegra->drm->dev, "failed to map buffer: %d\n", err); + return err; + } + + return 0; +} + +static int tegra_bo_iommu_unmap(struct tegra_drm *tegra, struct tegra_bo *bo) +{ + if (!bo->mm) + return 0; + + iommu_unmap_sg(tegra->domain, bo->sgt, bo->paddr); + drm_mm_remove_node(bo->mm); + + kfree(bo->mm); + return 0; +} + static void tegra_bo_destroy(struct drm_device *drm, struct tegra_bo *bo) { - dma_free_writecombine(drm->dev, bo->gem.size, bo->vaddr, bo->paddr); + if (!bo->pages) + dma_free_writecombine(drm->dev, bo->gem.size, bo->vaddr, + bo->paddr); + else + drm_gem_put_pages(&bo->gem, bo->pages, true, true); +} + +static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo, + size_t size) +{ + bo->pages = drm_gem_get_pages(&bo->gem); + if (!bo->pages) + return -ENOMEM; + + bo->num_pages = size >> PAGE_SHIFT; + + return 0; +} + +static int tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo, + size_t size) +{ + bo->vaddr = dma_alloc_writecombine(drm->dev, size, &bo->paddr, + GFP_KERNEL | __GFP_NOWARN); + if (!bo->vaddr) { + dev_err(drm->dev, "failed to allocate buffer of size %zu\n", + size); + return -ENOMEM; + } + + return 0; } struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size, unsigned long flags) { + struct tegra_drm *tegra = drm->dev_private; struct tegra_bo *bo; int err; @@ -109,22 +240,33 @@ struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size, host1x_bo_init(&bo->base, &tegra_bo_ops); size = round_up(size, PAGE_SIZE); - bo->vaddr = dma_alloc_writecombine(drm->dev, size, &bo->paddr, - GFP_KERNEL | __GFP_NOWARN); - if (!bo->vaddr) { - dev_err(drm->dev, "failed to allocate buffer with size %u\n", - size); - err = -ENOMEM; - goto err_dma; - } - err = drm_gem_object_init(drm, &bo->gem, size); if (err) - goto err_init; + goto free; err = drm_gem_create_mmap_offset(&bo->gem); if (err) - goto err_mmap; + goto release; + + if (tegra->domain) { + err = tegra_bo_get_pages(drm, bo, size); + if (err < 0) + goto release; + + bo->sgt = drm_prime_pages_to_sg(bo->pages, bo->num_pages); + if (IS_ERR(bo->sgt)) { + err = PTR_ERR(bo->sgt); + goto release; + } + + err = tegra_bo_iommu_map(tegra, bo); + if (err < 0) + goto release; + } else { + err = tegra_bo_alloc(drm, bo, size); + if (err < 0) + goto release; + } if (flags & DRM_TEGRA_GEM_CREATE_TILED) bo->tiling.mode = TEGRA_BO_TILING_MODE_TILED; @@ -134,11 +276,10 @@ struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size, return bo; -err_mmap: +release: drm_gem_object_release(&bo->gem); -err_init: tegra_bo_destroy(drm, bo); -err_dma: +free: kfree(bo); return ERR_PTR(err); @@ -173,6 +314,7 @@ err: static struct tegra_bo *tegra_bo_import(struct drm_device *drm, struct dma_buf *buf) { + struct tegra_drm *tegra = drm->dev_private; struct dma_buf_attachment *attach; struct tegra_bo *bo; ssize_t size; @@ -212,12 +354,19 @@ static struct tegra_bo *tegra_bo_import(struct drm_device *drm, goto detach; } - if (bo->sgt->nents > 1) { - err = -EINVAL; - goto detach; + if (tegra->domain) { + err = tegra_bo_iommu_map(tegra, bo); + if (err < 0) + goto detach; + } else { + if (bo->sgt->nents > 1) { + err = -EINVAL; + goto detach; + } + + bo->paddr = sg_dma_address(bo->sgt->sgl); } - bo->paddr = sg_dma_address(bo->sgt->sgl); bo->gem.import_attach = attach; return bo; @@ -240,8 +389,12 @@ free: void tegra_bo_free_object(struct drm_gem_object *gem) { + struct tegra_drm *tegra = gem->dev->dev_private; struct tegra_bo *bo = to_tegra_bo(gem); + if (tegra->domain) + tegra_bo_iommu_unmap(tegra, bo); + if (gem->import_attach) { dma_buf_unmap_attachment(gem->import_attach, bo->sgt, DMA_TO_DEVICE); @@ -304,7 +457,38 @@ int tegra_bo_dumb_map_offset(struct drm_file *file, struct drm_device *drm, return 0; } +static int tegra_bo_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct drm_gem_object *gem = vma->vm_private_data; + struct tegra_bo *bo = to_tegra_bo(gem); + struct page *page; + pgoff_t offset; + int err; + + if (!bo->pages) + return VM_FAULT_SIGBUS; + + offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >> PAGE_SHIFT; + page = bo->pages[offset]; + + err = vm_insert_page(vma, (unsigned long)vmf->virtual_address, page); + switch (err) { + case -EAGAIN: + case 0: + case -ERESTARTSYS: + case -EINTR: + case -EBUSY: + return VM_FAULT_NOPAGE; + + case -ENOMEM: + return VM_FAULT_OOM; + } + + return VM_FAULT_SIGBUS; +} + const struct vm_operations_struct tegra_bo_vm_ops = { + .fault = tegra_bo_fault, .open = drm_gem_vm_open, .close = drm_gem_vm_close, }; @@ -322,10 +506,18 @@ int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma) gem = vma->vm_private_data; bo = to_tegra_bo(gem); - ret = remap_pfn_range(vma, vma->vm_start, bo->paddr >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, vma->vm_page_prot); - if (ret) - drm_gem_vm_close(vma); + if (!bo->pages) { + unsigned long size = vma->vm_end - vma->vm_start; + unsigned long pfn = bo->paddr >> PAGE_SHIFT; + + ret = remap_pfn_range(vma, vma->vm_start, pfn, size, + vma->vm_page_prot); + if (ret) + drm_gem_vm_close(vma); + } else { + vma->vm_flags |= VM_MIXEDMAP; + vma->vm_flags &= ~VM_PFNMAP; + } return ret; } diff --git a/drivers/gpu/drm/tegra/gem.h b/drivers/gpu/drm/tegra/gem.h index 43a25c853357..c2e3f43e4b3f 100644 --- a/drivers/gpu/drm/tegra/gem.h +++ b/drivers/gpu/drm/tegra/gem.h @@ -37,6 +37,10 @@ struct tegra_bo { dma_addr_t paddr; void *vaddr; + struct drm_mm_node *mm; + unsigned long num_pages; + struct page **pages; + struct tegra_bo_tiling tiling; }; diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 169836020208..7d369591e934 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -29,9 +29,13 @@ #include <linux/idr.h> #include <linux/notifier.h> #include <linux/err.h> +#include <linux/of.h> #include <linux/pci.h> #include <trace/events/iommu.h> +static DEFINE_MUTEX(iommus_lock); +static LIST_HEAD(iommus); + static struct kset *iommu_group_kset; static struct ida iommu_group_ida; static struct mutex iommu_group_mutex; @@ -1186,3 +1190,92 @@ int iommu_domain_set_attr(struct iommu_domain *domain, return ret; } EXPORT_SYMBOL_GPL(iommu_domain_set_attr); + +int iommu_add(struct iommu *iommu) +{ + mutex_lock(&iommus_lock); + list_add_tail(&iommu->list, &iommus); + mutex_unlock(&iommus_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(iommu_add); + +void iommu_remove(struct iommu *iommu) +{ + mutex_lock(&iommus_lock); + list_del_init(&iommu->list); + mutex_unlock(&iommus_lock); +} +EXPORT_SYMBOL_GPL(iommu_remove); + +static int of_iommu_attach(struct device *dev) +{ + struct of_phandle_iter iter; + struct iommu *iommu; + + mutex_lock(&iommus_lock); + + of_property_for_each_phandle_with_args(iter, dev->of_node, "iommus", + "#iommu-cells", 0) { + bool found = false; + int err; + + /* skip disabled IOMMUs */ + if (!of_device_is_available(iter.out_args.np)) + continue; + + list_for_each_entry(iommu, &iommus, list) { + if (iommu->dev->of_node == iter.out_args.np) { + err = iommu->ops->attach(iommu, dev); + if (err < 0) { + } + + found = true; + } + } + + if (!found) { + mutex_unlock(&iommus_lock); + return -EPROBE_DEFER; + } + } + + mutex_unlock(&iommus_lock); + + return 0; +} + +static int of_iommu_detach(struct device *dev) +{ + /* TODO: implement */ + return -ENOSYS; +} + +int iommu_attach(struct device *dev) +{ + int err = 0; + + if (IS_ENABLED(CONFIG_OF) && dev->of_node) { + err = of_iommu_attach(dev); + if (!err) + return 0; + } + + return err; +} +EXPORT_SYMBOL_GPL(iommu_attach); + +int iommu_detach(struct device *dev) +{ + int err = 0; + + if (IS_ENABLED(CONFIG_OF) && dev->of_node) { + err = of_iommu_detach(dev); + if (!err) + return 0; + } + + return err; +} +EXPORT_SYMBOL_GPL(iommu_detach); diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index 4055c47f45ab..7e390b2826aa 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -11,4 +11,5 @@ obj-$(CONFIG_FSL_CORENET_CF) += fsl-corenet-cf.o obj-$(CONFIG_FSL_IFC) += fsl_ifc.o obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o -obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o + +obj-$(CONFIG_ARCH_TEGRA) += tegra/ diff --git a/drivers/memory/tegra/Makefile b/drivers/memory/tegra/Makefile new file mode 100644 index 000000000000..51b9e8fcde1b --- /dev/null +++ b/drivers/memory/tegra/Makefile @@ -0,0 +1,5 @@ +obj-y = tegra-mc.o +obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30-mc.o +obj-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114-mc.o +obj-$(CONFIG_ARCH_TEGRA_124_SOC) += tegra124-mc.o +obj-$(CONFIG_ARCH_TEGRA_132_SOC) += tegra124-mc.o diff --git a/drivers/memory/tegra/tegra-mc.c b/drivers/memory/tegra/tegra-mc.c new file mode 100644 index 000000000000..6edbb3f1e962 --- /dev/null +++ b/drivers/memory/tegra/tegra-mc.c @@ -0,0 +1,1064 @@ +/* + * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/iommu.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <soc/tegra/ahb.h> + +#ifndef CONFIG_ARM64 +#include <asm/dma-iommu.h> +#endif + +#include "tegra-mc.h" + +#define MC_INTSTATUS 0x000 +#define MC_INT_DECERR_MTS (1 << 16) +#define MC_INT_SECERR_SEC (1 << 13) +#define MC_INT_DECERR_VPR (1 << 12) +#define MC_INT_INVALID_APB_ASID_UPDATE (1 << 11) +#define MC_INT_INVALID_SMMU_PAGE (1 << 10) +#define MC_INT_ARBITRATION_EMEM (1 << 9) +#define MC_INT_SECURITY_VIOLATION (1 << 8) +#define MC_INT_DECERR_EMEM (1 << 6) + +#define MC_INTMASK 0x004 + +#define MC_ERR_STATUS 0x08 +#define MC_ERR_STATUS_TYPE_SHIFT 28 +#define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (6 << MC_ERR_STATUS_TYPE_SHIFT) +#define MC_ERR_STATUS_TYPE_MASK (0x7 << MC_ERR_STATUS_TYPE_SHIFT) +#define MC_ERR_STATUS_READABLE (1 << 27) +#define MC_ERR_STATUS_WRITABLE (1 << 26) +#define MC_ERR_STATUS_NONSECURE (1 << 25) +#define MC_ERR_STATUS_ADR_HI_SHIFT 20 +#define MC_ERR_STATUS_ADR_HI_MASK 0x3 +#define MC_ERR_STATUS_SECURITY (1 << 17) +#define MC_ERR_STATUS_RW (1 << 16) +#define MC_ERR_STATUS_CLIENT_MASK 0x7f + +#define MC_ERR_ADR 0x0c + +#define MC_EMEM_ARB_CFG 0x90 +#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x) (((x) & 0x1ff) << 0) +#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK 0x1ff +#define MC_EMEM_ARB_MISC0 0xd8 + +struct tegra_smmu; + +struct tegra_mc { + struct device *dev; + struct tegra_smmu *smmu; + void __iomem *regs; + struct clk *clk; + int irq; + + const struct tegra_mc_soc *soc; + unsigned long tick; +}; + +static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset) +{ + return readl(mc->regs + offset); +} + +static inline void mc_writel(struct tegra_mc *mc, u32 value, unsigned long offset) +{ + writel(value, mc->regs + offset); +} + +struct tegra_smmu_master { + struct list_head list; + struct device *dev; +}; + +struct tegra_smmu_group { + const char *name; + const struct of_device_id *matches; + unsigned int asid; + +#ifndef CONFIG_ARM64 + struct dma_iommu_mapping *mapping; +#endif + struct list_head masters; +}; + +static void tegra_smmu_group_release(void *data) +{ + kfree(data); +} + +struct tegra_smmu { + void __iomem *regs; + struct iommu iommu; + struct device *dev; + + struct tegra_mc *mc; + const struct tegra_smmu_soc *soc; + + struct iommu_group **groups; + unsigned int num_groups; + + unsigned long *asids; + struct mutex lock; +}; + +struct tegra_smmu_address_space { + struct iommu_domain *domain; + struct tegra_smmu *smmu; + struct page *pd; + unsigned id; + u32 attr; +}; + +static inline void smmu_writel(struct tegra_smmu *smmu, u32 value, + unsigned long offset) +{ + writel(value, smmu->regs + offset); +} + +static inline u32 smmu_readl(struct tegra_smmu *smmu, unsigned long offset) +{ + return readl(smmu->regs + offset); +} + +#define SMMU_CONFIG 0x010 +#define SMMU_CONFIG_ENABLE (1 << 0) + +#define SMMU_TLB_CONFIG 0x14 +#define SMMU_TLB_CONFIG_HIT_UNDER_MISS (1 << 29) +#define SMMU_TLB_CONFIG_ROUND_ROBIN_ARBITRATION (1 << 28) +#define SMMU_TLB_CONFIG_ACTIVE_LINES(x) ((x) & 0x3f) + +#define SMMU_PTC_CONFIG 0x18 +#define SMMU_PTC_CONFIG_ENABLE (1 << 29) +#define SMMU_PTC_CONFIG_REQ_LIMIT(x) (((x) & 0x0f) << 24) +#define SMMU_PTC_CONFIG_INDEX_MAP(x) ((x) & 0x3f) + +#define SMMU_PTB_ASID 0x01c +#define SMMU_PTB_ASID_VALUE(x) ((x) & 0x7f) + +#define SMMU_PTB_DATA 0x020 +#define SMMU_PTB_DATA_VALUE(page, attr) (page_to_phys(page) >> 12 | (attr)) + +#define SMMU_MK_PDE(page, attr) (page_to_phys(page) >> SMMU_PTE_SHIFT | (attr)) + +#define SMMU_TLB_FLUSH 0x030 +#define SMMU_TLB_FLUSH_VA_MATCH_ALL (0 << 0) +#define SMMU_TLB_FLUSH_VA_MATCH_SECTION (2 << 0) +#define SMMU_TLB_FLUSH_VA_MATCH_GROUP (3 << 0) +#define SMMU_TLB_FLUSH_ASID(x) (((x) & 0x7f) << 24) +#define SMMU_TLB_FLUSH_VA_SECTION(addr) ((((addr) & 0xffc00000) >> 12) | \ + SMMU_TLB_FLUSH_VA_MATCH_SECTION) +#define SMMU_TLB_FLUSH_VA_GROUP(addr) ((((addr) & 0xffffc000) >> 12) | \ + SMMU_TLB_FLUSH_VA_MATCH_GROUP) +#define SMMU_TLB_FLUSH_ASID_MATCH (1 << 31) + +#define SMMU_PTC_FLUSH 0x034 +#define SMMU_PTC_FLUSH_TYPE_ALL (0 << 0) +#define SMMU_PTC_FLUSH_TYPE_ADR (1 << 0) + +#define SMMU_PTC_FLUSH_HI 0x9b8 +#define SMMU_PTC_FLUSH_HI_MASK 0x3 + +/* per-SWGROUP SMMU_*_ASID register */ +#define SMMU_ASID_ENABLE (1 << 31) +#define SMMU_ASID_MASK 0x7f +#define SMMU_ASID_VALUE(x) ((x) & SMMU_ASID_MASK) + +/* page table definitions */ +#define SMMU_NUM_PDE 1024 +#define SMMU_NUM_PTE 1024 + +#define SMMU_SIZE_PD (SMMU_NUM_PDE * 4) +#define SMMU_SIZE_PT (SMMU_NUM_PTE * 4) + +#define SMMU_PDE_SHIFT 22 +#define SMMU_PTE_SHIFT 12 + +#define SMMU_PFN_MASK 0x000fffff + +#define SMMU_PD_READABLE (1 << 31) +#define SMMU_PD_WRITABLE (1 << 30) +#define SMMU_PD_NONSECURE (1 << 29) + +#define SMMU_PDE_READABLE (1 << 31) +#define SMMU_PDE_WRITABLE (1 << 30) +#define SMMU_PDE_NONSECURE (1 << 29) +#define SMMU_PDE_NEXT (1 << 28) + +#define SMMU_PTE_READABLE (1 << 31) +#define SMMU_PTE_WRITABLE (1 << 30) +#define SMMU_PTE_NONSECURE (1 << 29) + +#define SMMU_PDE_ATTR (SMMU_PDE_READABLE | SMMU_PDE_WRITABLE | \ + SMMU_PDE_NONSECURE) +#define SMMU_PTE_ATTR (SMMU_PTE_READABLE | SMMU_PTE_WRITABLE | \ + SMMU_PTE_NONSECURE) + +#define SMMU_PDE_VACANT(n) (((n) << 10) | SMMU_PDE_ATTR) +#define SMMU_PTE_VACANT(n) (((n) << 12) | SMMU_PTE_ATTR) + +static inline void smmu_flush_ptc(struct tegra_smmu *smmu, struct page *page, + unsigned long offset) +{ + phys_addr_t phys = page ? page_to_phys(page) : 0; + u32 value; + + if (page) { + offset &= ~(smmu->mc->soc->atom_size - 1); + + if (smmu->soc->num_address_bits > 32) { +#ifdef CONFIG_PHYS_ADDR_T_64BIT + value = (phys >> 32) & SMMU_PTC_FLUSH_HI_MASK; +#else + value = 0; +#endif + smmu_writel(smmu, value, SMMU_PTC_FLUSH_HI); + } + + value = (phys + offset) | SMMU_PTC_FLUSH_TYPE_ADR; + } else { + value = SMMU_PTC_FLUSH_TYPE_ALL; + } + + smmu_writel(smmu, value, SMMU_PTC_FLUSH); +} + +static inline void smmu_flush_tlb(struct tegra_smmu *smmu) +{ + smmu_writel(smmu, SMMU_TLB_FLUSH_VA_MATCH_ALL, SMMU_TLB_FLUSH); +} + +static inline void smmu_flush_tlb_asid(struct tegra_smmu *smmu, + unsigned long asid) +{ + u32 value; + + value = SMMU_TLB_FLUSH_ASID_MATCH | SMMU_TLB_FLUSH_ASID(asid) | + SMMU_TLB_FLUSH_VA_MATCH_ALL; + smmu_writel(smmu, value, SMMU_TLB_FLUSH); +} + +static inline void smmu_flush_tlb_section(struct tegra_smmu *smmu, + unsigned long asid, + unsigned long iova) +{ + u32 value; + + value = SMMU_TLB_FLUSH_ASID_MATCH | SMMU_TLB_FLUSH_ASID(asid) | + SMMU_TLB_FLUSH_VA_SECTION(iova); + smmu_writel(smmu, value, SMMU_TLB_FLUSH); +} + +static inline void smmu_flush_tlb_group(struct tegra_smmu *smmu, + unsigned long asid, + unsigned long iova) +{ + u32 value; + + value = SMMU_TLB_FLUSH_ASID_MATCH | SMMU_TLB_FLUSH_ASID(asid) | + SMMU_TLB_FLUSH_VA_GROUP(iova); + smmu_writel(smmu, value, SMMU_TLB_FLUSH); +} + +static inline void smmu_flush(struct tegra_smmu *smmu) +{ + smmu_readl(smmu, SMMU_CONFIG); +} + +static inline struct tegra_smmu *to_tegra_smmu(struct iommu *iommu) +{ + return container_of(iommu, struct tegra_smmu, iommu); +} + +static struct tegra_smmu *smmu_handle = NULL; + +static int tegra_smmu_alloc_asid(struct tegra_smmu *smmu, unsigned int *idp) +{ + unsigned long id; + + mutex_lock(&smmu->lock); + + id = find_first_zero_bit(smmu->asids, smmu->soc->num_asids); + if (id >= smmu->soc->num_asids) { + mutex_unlock(&smmu->lock); + return -ENOSPC; + } + + set_bit(id, smmu->asids); + *idp = id; + + mutex_unlock(&smmu->lock); + return 0; +} + +static void tegra_smmu_free_asid(struct tegra_smmu *smmu, unsigned int id) +{ + mutex_lock(&smmu->lock); + clear_bit(id, smmu->asids); + mutex_unlock(&smmu->lock); +} + +static int tegra_smmu_domain_init(struct iommu_domain *domain) +{ + struct tegra_smmu *smmu = smmu_handle; + struct tegra_smmu_address_space *as; + uint32_t *pd, value; + unsigned int i; + int err = 0; + + as = kzalloc(sizeof(*as), GFP_KERNEL); + if (!as) { + err = -ENOMEM; + goto out; + } + + as->attr = SMMU_PD_READABLE | SMMU_PD_WRITABLE | SMMU_PD_NONSECURE; + as->smmu = smmu_handle; + as->domain = domain; + + err = tegra_smmu_alloc_asid(smmu, &as->id); + if (err < 0) { + kfree(as); + goto out; + } + + as->pd = alloc_page(GFP_KERNEL | __GFP_DMA); + if (!as->pd) { + err = -ENOMEM; + goto out; + } + + pd = page_address(as->pd); + SetPageReserved(as->pd); + + for (i = 0; i < SMMU_NUM_PDE; i++) + pd[i] = SMMU_PDE_VACANT(i); + + smmu->soc->ops->flush_dcache(as->pd, 0, SMMU_SIZE_PD); + smmu_flush_ptc(smmu, as->pd, 0); + smmu_flush_tlb_asid(smmu, as->id); + + smmu_writel(smmu, as->id & 0x7f, SMMU_PTB_ASID); + value = SMMU_PTB_DATA_VALUE(as->pd, as->attr); + smmu_writel(smmu, value, SMMU_PTB_DATA); + smmu_flush(smmu); + + domain->priv = as; + + return 0; + +out: + return err; +} + +static void tegra_smmu_domain_destroy(struct iommu_domain *domain) +{ + struct tegra_smmu_address_space *as = domain->priv; + + /* TODO: free page directory and page tables */ + + tegra_smmu_free_asid(as->smmu, as->id); + kfree(as); +} + +static const struct tegra_smmu_swgroup * +tegra_smmu_find_swgroup(struct tegra_smmu *smmu, unsigned int swgroup) +{ + const struct tegra_smmu_swgroup *group = NULL; + unsigned int i; + + for (i = 0; i < smmu->soc->num_swgroups; i++) { + if (smmu->soc->swgroups[i].swgroup == swgroup) { + group = &smmu->soc->swgroups[i]; + break; + } + } + + return group; +} + +static int tegra_smmu_enable(struct tegra_smmu *smmu, unsigned int swgroup, + unsigned int asid) +{ + const struct tegra_smmu_swgroup *group; + unsigned int i; + u32 value; + + for (i = 0; i < smmu->soc->num_clients; i++) { + const struct tegra_mc_client *client = &smmu->soc->clients[i]; + + if (client->swgroup != swgroup) + continue; + + value = smmu_readl(smmu, client->smmu.reg); + value |= BIT(client->smmu.bit); + smmu_writel(smmu, value, client->smmu.reg); + } + + group = tegra_smmu_find_swgroup(smmu, swgroup); + if (group) { + value = smmu_readl(smmu, group->reg); + value &= ~SMMU_ASID_MASK; + value |= SMMU_ASID_VALUE(asid); + value |= SMMU_ASID_ENABLE; + smmu_writel(smmu, value, group->reg); + } + + return 0; +} + +static int tegra_smmu_disable(struct tegra_smmu *smmu, unsigned int swgroup, + unsigned int asid) +{ + const struct tegra_smmu_swgroup *group; + unsigned int i; + u32 value; + + group = tegra_smmu_find_swgroup(smmu, swgroup); + if (group) { + value = smmu_readl(smmu, group->reg); + value &= ~SMMU_ASID_MASK; + value |= SMMU_ASID_VALUE(asid); + value &= ~SMMU_ASID_ENABLE; + smmu_writel(smmu, value, group->reg); + } + + for (i = 0; i < smmu->soc->num_clients; i++) { + const struct tegra_mc_client *client = &smmu->soc->clients[i]; + + if (client->swgroup != swgroup) + continue; + + value = smmu_readl(smmu, client->smmu.reg); + value &= ~BIT(client->smmu.bit); + smmu_writel(smmu, value, client->smmu.reg); + } + + return 0; +} + +static int tegra_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) +{ + struct tegra_smmu_address_space *as = domain->priv; + struct tegra_smmu *smmu = as->smmu; + struct of_phandle_iter entry; + int err; + + of_property_for_each_phandle_with_args(entry, dev->of_node, "iommus", + "#iommu-cells", 0) { + unsigned int swgroup = entry.out_args.args[0]; + + if (entry.out_args.np != smmu->dev->of_node) + continue; + + err = tegra_smmu_enable(smmu, swgroup, as->id); + if (err < 0) + pr_err("failed to enable SWGROUP#%u\n", swgroup); + } + + return 0; +} + +static void tegra_smmu_detach_dev(struct iommu_domain *domain, struct device *dev) +{ + struct tegra_smmu_address_space *as = domain->priv; + struct tegra_smmu *smmu = as->smmu; + struct of_phandle_iter entry; + int err; + + of_property_for_each_phandle_with_args(entry, dev->of_node, "iommus", + "#iommu-cells", 0) { + unsigned int swgroup; + + if (entry.out_args.np != smmu->dev->of_node) + continue; + + swgroup = entry.out_args.args[0]; + + err = tegra_smmu_disable(smmu, swgroup, as->id); + if (err < 0) { + pr_err("failed to enable SWGROUP#%u\n", swgroup); + } + } +} + +static u32 *as_get_pte(struct tegra_smmu_address_space *as, dma_addr_t iova, + struct page **pagep) +{ + struct tegra_smmu *smmu = smmu_handle; + u32 *pd = page_address(as->pd), *pt; + u32 pde = (iova >> SMMU_PDE_SHIFT) & 0x3ff; + u32 pte = (iova >> SMMU_PTE_SHIFT) & 0x3ff; + struct page *page; + unsigned int i; + + if (pd[pde] != SMMU_PDE_VACANT(pde)) { + page = pfn_to_page(pd[pde] & SMMU_PFN_MASK); + pt = page_address(page); + } else { + page = alloc_page(GFP_KERNEL | __GFP_DMA); + if (!page) + return NULL; + + pt = page_address(page); + SetPageReserved(page); + + for (i = 0; i < SMMU_NUM_PTE; i++) + pt[i] = SMMU_PTE_VACANT(i); + + smmu->soc->ops->flush_dcache(page, 0, SMMU_SIZE_PT); + + pd[pde] = SMMU_MK_PDE(page, SMMU_PDE_ATTR | SMMU_PDE_NEXT); + + smmu->soc->ops->flush_dcache(as->pd, pde << 2, 4); + smmu_flush_ptc(smmu, as->pd, pde << 2); + smmu_flush_tlb_section(smmu, as->id, iova); + smmu_flush(smmu); + } + + *pagep = page; + + return &pt[pte]; +} + +static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, size_t size, int prot) +{ + struct tegra_smmu_address_space *as = domain->priv; + struct tegra_smmu *smmu = smmu_handle; + unsigned long offset; + struct page *page; + u32 *pte; + + pte = as_get_pte(as, iova, &page); + if (!pte) + return -ENOMEM; + + offset = offset_in_page(pte); + + *pte = __phys_to_pfn(paddr) | SMMU_PTE_ATTR; + + smmu->soc->ops->flush_dcache(page, offset, 4); + smmu_flush_ptc(smmu, page, offset); + smmu_flush_tlb_group(smmu, as->id, iova); + smmu_flush(smmu); + + return 0; +} + +static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova, + size_t size) +{ + struct tegra_smmu_address_space *as = domain->priv; + struct tegra_smmu *smmu = smmu_handle; + unsigned long offset; + struct page *page; + u32 *pte; + + pte = as_get_pte(as, iova, &page); + if (!pte) + return 0; + + offset = offset_in_page(pte); + *pte = 0; + + smmu->soc->ops->flush_dcache(page, offset, 4); + smmu_flush_ptc(smmu, page, offset); + smmu_flush_tlb_group(smmu, as->id, iova); + smmu_flush(smmu); + + return size; +} + +static phys_addr_t tegra_smmu_iova_to_phys(struct iommu_domain *domain, + dma_addr_t iova) +{ + struct tegra_smmu_address_space *as = domain->priv; + struct page *page; + unsigned long pfn; + u32 *pte; + + pte = as_get_pte(as, iova, &page); + pfn = *pte & SMMU_PFN_MASK; + + return PFN_PHYS(pfn); +} + +static int tegra_smmu_attach(struct iommu *iommu, struct device *dev) +{ + struct tegra_smmu *smmu = to_tegra_smmu(iommu); + struct tegra_smmu_group *group; + unsigned int i; + + for (i = 0; i < smmu->soc->num_groups; i++) { + group = iommu_group_get_iommudata(smmu->groups[i]); + + if (of_match_node(group->matches, dev->of_node)) { + pr_debug("adding device %s to group %s\n", + dev_name(dev), group->name); + iommu_group_add_device(smmu->groups[i], dev); + break; + } + } + + if (i == smmu->soc->num_groups) + return 0; + +#ifndef CONFIG_ARM64 + return arm_iommu_attach_device(dev, group->mapping); +#else + return 0; +#endif +} + +static int tegra_smmu_detach(struct iommu *iommu, struct device *dev) +{ + return 0; +} + +static const struct iommu_ops tegra_smmu_ops = { + .domain_init = tegra_smmu_domain_init, + .domain_destroy = tegra_smmu_domain_destroy, + .attach_dev = tegra_smmu_attach_dev, + .detach_dev = tegra_smmu_detach_dev, + .map = tegra_smmu_map, + .unmap = tegra_smmu_unmap, + .iova_to_phys = tegra_smmu_iova_to_phys, + .attach = tegra_smmu_attach, + .detach = tegra_smmu_detach, + + .pgsize_bitmap = SZ_4K, +}; + +static struct tegra_smmu *tegra_smmu_probe(struct device *dev, + const struct tegra_smmu_soc *soc, + struct tegra_mc *mc) +{ + struct tegra_smmu *smmu; + unsigned int i; + size_t size; + u32 value; + int err; + + /* This can happen on Tegra20 which doesn't have an SMMU */ + if (!soc) + return NULL; + + smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); + if (!smmu) + return ERR_PTR(-ENOMEM); + + size = BITS_TO_LONGS(soc->num_asids) * sizeof(long); + + smmu->asids = devm_kzalloc(dev, size, GFP_KERNEL); + if (!smmu->asids) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&smmu->iommu.list); + mutex_init(&smmu->lock); + + smmu->iommu.ops = &tegra_smmu_ops; + smmu->iommu.dev = dev; + + smmu->regs = mc->regs; + smmu->soc = soc; + smmu->dev = dev; + smmu->mc = mc; + + smmu_handle = smmu; + + err = bus_set_iommu(&platform_bus_type, &tegra_smmu_ops); + if (err < 0) + return ERR_PTR(err); + + smmu->num_groups = soc->num_groups; + + smmu->groups = devm_kcalloc(dev, smmu->num_groups, sizeof(*smmu->groups), + GFP_KERNEL); + if (!smmu->groups) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < smmu->num_groups; i++) { + struct tegra_smmu_group *group; + + smmu->groups[i] = iommu_group_alloc(); + if (IS_ERR(smmu->groups[i])) + return ERR_CAST(smmu->groups[i]); + + err = iommu_group_set_name(smmu->groups[i], soc->groups[i].name); + if (err < 0) { + } + + group = kzalloc(sizeof(*group), GFP_KERNEL); + if (!group) + return ERR_PTR(-ENOMEM); + + group->matches = soc->groups[i].matches; + group->asid = soc->groups[i].asid; + group->name = soc->groups[i].name; + + iommu_group_set_iommudata(smmu->groups[i], group, + tegra_smmu_group_release); + +#ifndef CONFIG_ARM64 + group->mapping = arm_iommu_create_mapping(&platform_bus_type, + 0, SZ_2G); + if (IS_ERR(group->mapping)) { + dev_err(dev, "failed to create mapping for group %s: %ld\n", + group->name, PTR_ERR(group->mapping)); + return ERR_CAST(group->mapping); + } +#endif + } + + value = SMMU_PTC_CONFIG_ENABLE | SMMU_PTC_CONFIG_INDEX_MAP(0x3f); + + if (soc->supports_request_limit) + value |= SMMU_PTC_CONFIG_REQ_LIMIT(8); + + smmu_writel(smmu, value, SMMU_PTC_CONFIG); + + value = SMMU_TLB_CONFIG_HIT_UNDER_MISS | + SMMU_TLB_CONFIG_ACTIVE_LINES(0x20); + + if (soc->supports_round_robin_arbitration) + value |= SMMU_TLB_CONFIG_ROUND_ROBIN_ARBITRATION; + + smmu_writel(smmu, value, SMMU_TLB_CONFIG); + + smmu_flush_ptc(smmu, NULL, 0); + smmu_flush_tlb(smmu); + smmu_writel(smmu, SMMU_CONFIG_ENABLE, SMMU_CONFIG); + smmu_flush(smmu); + + if (1) { + static const struct of_device_id ahb_match[] = { + { .compatible = "nvidia,tegra30-ahb", }, + { } + }; + struct device_node *ahb; + + ahb = of_find_matching_node(NULL, ahb_match); + if (ahb) { + tegra_ahb_enable_smmu(ahb); + } + } + + err = iommu_add(&smmu->iommu); + if (err < 0) + return ERR_PTR(err); + + return smmu; +} + +static int tegra_smmu_remove(struct tegra_smmu *smmu) +{ + iommu_remove(&smmu->iommu); + + return 0; +} + +static const struct of_device_id tegra_mc_of_match[] = { +#ifdef CONFIG_ARCH_TEGRA_3x_SOC + { .compatible = "nvidia,tegra30-mc", .data = &tegra30_mc_soc }, +#endif +#ifdef CONFIG_ARCH_TEGRA_114_SOC + { .compatible = "nvidia,tegra114-mc", .data = &tegra114_mc_soc }, +#endif +#ifdef CONFIG_ARCH_TEGRA_124_SOC + { .compatible = "nvidia,tegra124-mc", .data = &tegra124_mc_soc }, +#endif +#ifdef CONFIG_ARCH_TEGRA_132_SOC + { .compatible = "nvidia,tegra132-mc", .data = &tegra132_mc_soc }, +#endif + { } +}; + +/* XXX: provide prototype in public header */ +int tegra_mc_set_bandwidth(struct tegra_mc *mc, unsigned int client_id, + unsigned long bandwidth) +{ + unsigned int i; + + for (i = 0; i < mc->soc->num_clients; i++) { + const struct tegra_mc_client *client = &mc->soc->clients[i]; + + if (client->id == client_id) { + const struct latency_allowance *la = &client->latency; + unsigned long long ticks; + u32 value; + + ticks = client->fifo_size * mc->soc->atom_size * + (NSEC_PER_SEC / mc->tick); + do_div(ticks, bandwidth); + ticks -= 1000 / mc->tick; + + value = readl(mc->regs + la->reg); + value &= ~(la->mask << la->shift); + value |= (ticks & la->mask) << la->shift; + writel(value, mc->regs + la->reg); + + return 0; + } + } + + return -EINVAL; +} + +static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc) +{ + unsigned long long tick; + unsigned int i; + u32 value; + + /* compute the number of MC clock cycles per tick */ + tick = mc->tick * clk_get_rate(mc->clk); + do_div(tick, NSEC_PER_SEC); + + value = readl(mc->regs + MC_EMEM_ARB_CFG); + value &= ~MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK; + value |= MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(tick); + writel(value, mc->regs + MC_EMEM_ARB_CFG); + + /* write latency allowance defaults */ + for (i = 0; i < mc->soc->num_clients; i++) { + const struct latency_allowance *la; + u32 value; + + la = &mc->soc->clients[i].latency; + + value = readl(mc->regs + la->reg); + value &= ~(la->mask << la->shift); + value |= (la->def & la->mask) << la->shift; + writel(value, mc->regs + la->reg); + } + + return 0; +} + +static const char *const status_names[32] = { + [ 1] = "External interrupt", + [ 6] = "EMEM address decode error", + [ 8] = "Security violation", + [ 9] = "EMEM arbitration error", + [10] = "Page fault", + [11] = "Invalid APB ASID update", + [12] = "VPR violation", + [13] = "Secure carveout violation", + [16] = "MTS carveout violation", +}; + +static const char *const error_names[8] = { + [2] = "EMEM decode error", + [3] = "TrustZone violation", + [4] = "Carveout violation", + [6] = "SMMU translation error", +}; + +static irqreturn_t tegra_mc_irq(int irq, void *data) +{ + struct tegra_mc *mc = data; + unsigned long status, mask; + unsigned int bit; + + /* mask all interrupts to avoid flooding */ + status = mc_readl(mc, MC_INTSTATUS); + mask = mc_readl(mc, MC_INTMASK); + + for_each_set_bit(bit, &status, 32) { + const char *error = status_names[bit] ?: "unknown"; + const char *client = "unknown", *desc; + const char *direction, *secure; + phys_addr_t addr = 0; + unsigned int i; + char perm[7]; + u8 id, type; + u32 value; + + value = mc_readl(mc, MC_ERR_STATUS); + +#ifdef CONFIG_PHYS_ADDR_T_64BIT + if (mc->smmu && mc->smmu->soc->num_address_bits > 32) { + addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) & + MC_ERR_STATUS_ADR_HI_MASK); + addr <<= 32; + } +#endif + + if (value & MC_ERR_STATUS_RW) + direction = "write"; + else + direction = "read"; + + if (value & MC_ERR_STATUS_SECURITY) + secure = "secure "; + else + secure = ""; + + id = value & MC_ERR_STATUS_CLIENT_MASK; + + for (i = 0; i < mc->soc->num_clients; i++) { + if (mc->soc->clients[i].id == id) { + client = mc->soc->clients[i].name; + break; + } + } + + type = (value & MC_ERR_STATUS_TYPE_MASK) >> + MC_ERR_STATUS_TYPE_SHIFT; + desc = error_names[type]; + + switch (value & MC_ERR_STATUS_TYPE_MASK) { + case MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE: + perm[0] = ' '; + perm[1] = '['; + + if (value & MC_ERR_STATUS_READABLE) + perm[2] = 'R'; + else + perm[2] = '-'; + + if (value & MC_ERR_STATUS_WRITABLE) + perm[3] = 'W'; + else + perm[3] = '-'; + + if (value & MC_ERR_STATUS_NONSECURE) + perm[4] = '-'; + else + perm[4] = 'S'; + + perm[5] = ']'; + perm[6] = '\0'; + break; + + default: + perm[0] = '\0'; + break; + } + + value = mc_readl(mc, MC_ERR_ADR); + addr |= value; + + dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s%s)\n", + client, secure, direction, &addr, error, + desc, perm); + } + + /* clear interrupts */ + mc_writel(mc, status, MC_INTSTATUS); + + return IRQ_HANDLED; +} + +static int tegra_mc_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + struct resource *res; + struct tegra_mc *mc; + u32 value; + int err; + + match = of_match_node(tegra_mc_of_match, pdev->dev.of_node); + if (!match) + return -ENODEV; + + mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); + if (!mc) + return -ENOMEM; + + platform_set_drvdata(pdev, mc); + mc->soc = match->data; + mc->dev = &pdev->dev; + + /* length of MC tick in nanoseconds */ + mc->tick = 30; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mc->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mc->regs)) + return PTR_ERR(mc->regs); + + mc->clk = devm_clk_get(&pdev->dev, "mc"); + if (IS_ERR(mc->clk)) + return PTR_ERR(mc->clk); + + err = tegra_mc_setup_latency_allowance(mc); + if (err < 0) + return err; + + if (IS_ENABLED(CONFIG_IOMMU_API)) { + mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc); + if (IS_ERR(mc->smmu)) { + dev_err(&pdev->dev, "failed to probe SMMU: %ld\n", + PTR_ERR(mc->smmu)); + return PTR_ERR(mc->smmu); + } + } + + mc->irq = platform_get_irq(pdev, 0); + if (mc->irq < 0) { + dev_err(&pdev->dev, "interrupt not specified\n"); + return mc->irq; + } + + err = devm_request_irq(&pdev->dev, mc->irq, tegra_mc_irq, IRQF_SHARED, + dev_name(&pdev->dev), mc); + if (err < 0) { + dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", mc->irq, + err); + return err; + } + + value = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | + MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE | + MC_INT_ARBITRATION_EMEM | MC_INT_SECURITY_VIOLATION | + MC_INT_DECERR_EMEM; + mc_writel(mc, value, MC_INTMASK); + + return 0; +} + +static int tegra_mc_remove(struct platform_device *pdev) +{ + struct tegra_mc *mc = platform_get_drvdata(pdev); + int err; + + if (IS_ENABLED(CONFIG_IOMMU_API)) { + err = tegra_smmu_remove(mc->smmu); + if (err < 0) { + dev_err(&pdev->dev, "failed to remove SMMU: %d\n", + err); + } + } + + return 0; +} + +static struct platform_driver tegra_mc_driver = { + .driver = { + .name = "tegra-mc", + .of_match_table = tegra_mc_of_match, + }, + .probe = tegra_mc_probe, + .remove = tegra_mc_remove, +}; +module_platform_driver(tegra_mc_driver); + +MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); +MODULE_DESCRIPTION("NVIDIA Tegra124 Memory Controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/memory/tegra/tegra-mc.h b/drivers/memory/tegra/tegra-mc.h new file mode 100644 index 000000000000..ee08c41fdd64 --- /dev/null +++ b/drivers/memory/tegra/tegra-mc.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef MEMORY_TEGRA_MC_H +#define MEMORY_TEGRA_MC_H + +#include <linux/types.h> + +struct page; + +struct tegra_smmu_group_init { + unsigned int asid; + const char *name; + + const struct of_device_id *matches; +}; + +struct latency_allowance { + unsigned int reg; + unsigned int shift; + unsigned int mask; + unsigned int def; +}; + +struct smmu_enable { + unsigned int reg; + unsigned int bit; +}; + +struct tegra_mc_client { + unsigned int id; + const char *name; + unsigned int swgroup; + + unsigned int fifo_size; + + struct smmu_enable smmu; + struct latency_allowance latency; +}; + +struct tegra_smmu_swgroup { + unsigned int swgroup; + unsigned int reg; +}; + +struct tegra_smmu_ops { + void (*flush_dcache)(struct page *page, unsigned long offset, + size_t size); +}; + +struct tegra_smmu_soc { + const struct tegra_smmu_group_init *groups; + unsigned int num_groups; + + const struct tegra_mc_client *clients; + unsigned int num_clients; + + bool supports_round_robin_arbitration; + bool supports_request_limit; + + const struct tegra_smmu_swgroup *swgroups; + unsigned int num_swgroups; + + unsigned int num_address_bits; + unsigned int num_asids; + + const struct tegra_smmu_ops *ops; +}; + +struct tegra_mc_soc { + const struct tegra_mc_client *clients; + unsigned int num_clients; + + unsigned int atom_size; + + const struct tegra_smmu_soc *smmu; +}; + +#ifdef CONFIG_ARCH_TEGRA_3x_SOC +extern const struct tegra_mc_soc tegra30_mc_soc; +#endif + +#ifdef CONFIG_ARCH_TEGRA_114_SOC +extern const struct tegra_mc_soc tegra114_mc_soc; +#endif + +#ifdef CONFIG_ARCH_TEGRA_124_SOC +extern const struct tegra_mc_soc tegra124_mc_soc; +#endif + +#ifdef CONFIG_ARCH_TEGRA_132_SOC +extern const struct tegra_mc_soc tegra132_mc_soc; +#endif + +#endif /* MEMORY_TEGRA_MC_H */ diff --git a/drivers/memory/tegra/tegra114-mc.c b/drivers/memory/tegra/tegra114-mc.c new file mode 100644 index 000000000000..1c4b2495ffc0 --- /dev/null +++ b/drivers/memory/tegra/tegra114-mc.c @@ -0,0 +1,958 @@ +/* + * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/of.h> +#include <linux/mm.h> + +#include <asm/cacheflush.h> + +#include <dt-bindings/memory/tegra114-mc.h> + +#include "tegra-mc.h" + +static const struct of_device_id tegra114_periph_matches[] = { + { } +}; + +static const struct tegra_smmu_group_init tegra114_smmu_groups[] = { + { 0, "peripherals", tegra114_periph_matches }, +}; + +static const struct tegra_mc_client tegra114_mc_clients[] = { + { + .id = 0x00, + .name = "ptcr", + .swgroup = TEGRA_SWGROUP_PTC, + }, { + .id = 0x01, + .name = "display0a", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 1, + }, + .latency = { + .reg = 0x2e8, + .shift = 0, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x02, + .name = "display0ab", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 2, + }, + .latency = { + .reg = 0x2f4, + .shift = 0, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x03, + .name = "display0b", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 3, + }, + .latency = { + .reg = 0x2e8, + .shift = 16, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x04, + .name = "display0bb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 4, + }, + .latency = { + .reg = 0x2f4, + .shift = 16, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x05, + .name = "display0c", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 5, + }, + .latency = { + .reg = 0x2ec, + .shift = 0, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x06, + .name = "display0cb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 6, + }, + .latency = { + .reg = 0x2f8, + .shift = 0, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x09, + .name = "eppup", + .swgroup = TEGRA_SWGROUP_EPP, + .smmu = { + .reg = 0x228, + .bit = 9, + }, + .latency = { + .reg = 0x300, + .shift = 0, + .mask = 0xff, + .def = 0x33, + }, + }, { + .id = 0x0a, + .name = "g2pr", + .swgroup = TEGRA_SWGROUP_G2, + .smmu = { + .reg = 0x228, + .bit = 10, + }, + .latency = { + .reg = 0x308, + .shift = 0, + .mask = 0xff, + .def = 0x09, + }, + }, { + .id = 0x0b, + .name = "g2sr", + .swgroup = TEGRA_SWGROUP_G2, + .smmu = { + .reg = 0x228, + .bit = 11, + }, + .latency = { + .reg = 0x308, + .shift = 16, + .mask = 0xff, + .def = 0x09, + }, + }, { + .id = 0x0f, + .name = "avpcarm7r", + .swgroup = TEGRA_SWGROUP_AVPC, + .smmu = { + .reg = 0x228, + .bit = 15, + }, + .latency = { + .reg = 0x2e4, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x10, + .name = "displayhc", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 16, + }, + .latency = { + .reg = 0x2f0, + .shift = 0, + .mask = 0xff, + .def = 0x68, + }, + }, { + .id = 0x11, + .name = "displayhcb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 17, + }, + .latency = { + .reg = 0x2fc, + .shift = 0, + .mask = 0xff, + .def = 0x68, + }, + }, { + .id = 0x12, + .name = "fdcdrd", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x228, + .bit = 18, + }, + .latency = { + .reg = 0x334, + .shift = 0, + .mask = 0xff, + .def = 0x0c, + }, + }, { + .id = 0x13, + .name = "fdcdrd2", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x228, + .bit = 19, + }, + .latency = { + .reg = 0x33c, + .shift = 0, + .mask = 0xff, + .def = 0x0c, + }, + }, { + .id = 0x14, + .name = "g2dr", + .swgroup = TEGRA_SWGROUP_G2, + .smmu = { + .reg = 0x228, + .bit = 20, + }, + .latency = { + .reg = 0x30c, + .shift = 0, + .mask = 0xff, + .def = 0x0a, + }, + }, { + .id = 0x15, + .name = "hdar", + .swgroup = TEGRA_SWGROUP_HDA, + .smmu = { + .reg = 0x228, + .bit = 21, + }, + .latency = { + .reg = 0x318, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x16, + .name = "host1xdmar", + .swgroup = TEGRA_SWGROUP_HC, + .smmu = { + .reg = 0x228, + .bit = 22, + }, + .latency = { + .reg = 0x310, + .shift = 0, + .mask = 0xff, + .def = 0x10, + }, + }, { + .id = 0x17, + .name = "host1xr", + .swgroup = TEGRA_SWGROUP_HC, + .smmu = { + .reg = 0x228, + .bit = 23, + }, + .latency = { + .reg = 0x310, + .shift = 16, + .mask = 0xff, + .def = 0xa5, + }, + }, { + .id = 0x18, + .name = "idxsrd", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x228, + .bit = 24, + }, + .latency = { + .reg = 0x334, + .shift = 16, + .mask = 0xff, + .def = 0x0b, + }, + }, { + .id = 0x1c, + .name = "msencsrd", + .swgroup = TEGRA_SWGROUP_MSENC, + .smmu = { + .reg = 0x228, + .bit = 28, + }, + .latency = { + .reg = 0x328, + .shift = 0, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x1d, + .name = "ppcsahbdmar", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x228, + .bit = 29, + }, + .latency = { + .reg = 0x344, + .shift = 0, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x1e, + .name = "ppcsahbslvr", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x228, + .bit = 30, + }, + .latency = { + .reg = 0x344, + .shift = 16, + .mask = 0xff, + .def = 0xe8, + }, + }, { + .id = 0x20, + .name = "texl2srd", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x22c, + .bit = 0, + }, + .latency = { + .reg = 0x338, + .shift = 0, + .mask = 0xff, + .def = 0x0c, + }, + }, { + .id = 0x22, + .name = "vdebsevr", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 2, + }, + .latency = { + .reg = 0x354, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x23, + .name = "vdember", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 3, + }, + .latency = { + .reg = 0x354, + .shift = 16, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x24, + .name = "vdemcer", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 4, + }, + .latency = { + .reg = 0x358, + .shift = 0, + .mask = 0xff, + .def = 0xb8, + }, + }, { + .id = 0x25, + .name = "vdetper", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 5, + }, + .latency = { + .reg = 0x358, + .shift = 16, + .mask = 0xff, + .def = 0xee, + }, + }, { + .id = 0x26, + .name = "mpcorelpr", + .swgroup = TEGRA_SWGROUP_MPCORELP, + .latency = { + .reg = 0x324, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x27, + .name = "mpcorer", + .swgroup = TEGRA_SWGROUP_MPCORE, + .latency = { + .reg = 0x320, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x28, + .name = "eppu", + .swgroup = TEGRA_SWGROUP_EPP, + .smmu = { + .reg = 0x22c, + .bit = 8, + }, + .latency = { + .reg = 0x300, + .shift = 16, + .mask = 0xff, + .def = 0x33, + }, + }, { + .id = 0x29, + .name = "eppv", + .swgroup = TEGRA_SWGROUP_EPP, + .smmu = { + .reg = 0x22c, + .bit = 9, + }, + .latency = { + .reg = 0x304, + .shift = 0, + .mask = 0xff, + .def = 0x6c, + }, + }, { + .id = 0x2a, + .name = "eppy", + .swgroup = TEGRA_SWGROUP_EPP, + .smmu = { + .reg = 0x22c, + .bit = 10, + }, + .latency = { + .reg = 0x304, + .shift = 16, + .mask = 0xff, + .def = 0x6c, + }, + }, { + .id = 0x2b, + .name = "msencswr", + .swgroup = TEGRA_SWGROUP_MSENC, + .smmu = { + .reg = 0x22c, + .bit = 11, + }, + .latency = { + .reg = 0x328, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x2c, + .name = "viwsb", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x22c, + .bit = 12, + }, + .latency = { + .reg = 0x364, + .shift = 0, + .mask = 0xff, + .def = 0x47, + }, + }, { + .id = 0x2d, + .name = "viwu", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x22c, + .bit = 13, + }, + .latency = { + .reg = 0x368, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x2e, + .name = "viwv", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x22c, + .bit = 14, + }, + .latency = { + .reg = 0x368, + .shift = 16, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x2f, + .name = "viwy", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x22c, + .bit = 15, + }, + .latency = { + .reg = 0x36c, + .shift = 0, + .mask = 0xff, + .def = 0x47, + }, + }, { + .id = 0x30, + .name = "g2dw", + .swgroup = TEGRA_SWGROUP_G2, + .smmu = { + .reg = 0x22c, + .bit = 16, + }, + .latency = { + .reg = 0x30c, + .shift = 16, + .mask = 0xff, + .def = 0x9, + }, + }, { + .id = 0x32, + .name = "avpcarm7w", + .swgroup = TEGRA_SWGROUP_AVPC, + .smmu = { + .reg = 0x22c, + .bit = 18, + }, + .latency = { + .reg = 0x2e4, + .shift = 16, + .mask = 0xff, + .def = 0x0e, + }, + }, { + .id = 0x33, + .name = "fdcdwr", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x22c, + .bit = 19, + }, + .latency = { + .reg = 0x338, + .shift = 16, + .mask = 0xff, + .def = 0x10, + }, + }, { + .id = 0x34, + .name = "fdcwr2", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x22c, + .bit = 20, + }, + .latency = { + .reg = 0x340, + .shift = 0, + .mask = 0xff, + .def = 0x10, + }, + }, { + .id = 0x35, + .name = "hdaw", + .swgroup = TEGRA_SWGROUP_HDA, + .smmu = { + .reg = 0x22c, + .bit = 21, + }, + .latency = { + .reg = 0x318, + .shift = 16, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x36, + .name = "host1xw", + .swgroup = TEGRA_SWGROUP_HC, + .smmu = { + .reg = 0x22c, + .bit = 22, + }, + .latency = { + .reg = 0x314, + .shift = 0, + .mask = 0xff, + .def = 0x25, + }, + }, { + .id = 0x37, + .name = "ispw", + .swgroup = TEGRA_SWGROUP_ISP, + .smmu = { + .reg = 0x22c, + .bit = 23, + }, + .latency = { + .reg = 0x31c, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x38, + .name = "mpcorelpw", + .swgroup = TEGRA_SWGROUP_MPCORELP, + .latency = { + .reg = 0x324, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x39, + .name = "mpcorew", + .swgroup = TEGRA_SWGROUP_MPCORE, + .latency = { + .reg = 0x320, + .shift = 16, + .mask = 0xff, + .def = 0x0e, + }, + }, { + .id = 0x3b, + .name = "ppcsahbdmaw", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x22c, + .bit = 27, + }, + .latency = { + .reg = 0x348, + .shift = 0, + .mask = 0xff, + .def = 0xa5, + }, + }, { + .id = 0x3c, + .name = "ppcsahbslvw", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x22c, + .bit = 28, + }, + .latency = { + .reg = 0x348, + .shift = 16, + .mask = 0xff, + .def = 0xe8, + }, + }, { + .id = 0x3e, + .name = "vdebsevw", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 30, + }, + .latency = { + .reg = 0x35c, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x3f, + .name = "vdedbgw", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 31, + }, + .latency = { + .reg = 0x35c, + .shift = 16, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x40, + .name = "vdembew", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x230, + .bit = 0, + }, + .latency = { + .reg = 0x360, + .shift = 0, + .mask = 0xff, + .def = 0x89, + }, + }, { + .id = 0x41, + .name = "vdetpmw", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x230, + .bit = 1, + }, + .latency = { + .reg = 0x360, + .shift = 16, + .mask = 0xff, + .def = 0x59, + }, + }, { + .id = 0x4a, + .name = "xusb_hostr", + .swgroup = TEGRA_SWGROUP_XUSB_HOST, + .smmu = { + .reg = 0x230, + .bit = 10, + }, + .latency = { + .reg = 0x37c, + .shift = 0, + .mask = 0xff, + .def = 0xa5, + }, + }, { + .id = 0x4b, + .name = "xusb_hostw", + .swgroup = TEGRA_SWGROUP_XUSB_HOST, + .smmu = { + .reg = 0x230, + .bit = 11, + }, + .latency = { + .reg = 0x37c, + .shift = 16, + .mask = 0xff, + .def = 0xa5, + }, + }, { + .id = 0x4c, + .name = "xusb_devr", + .swgroup = TEGRA_SWGROUP_XUSB_DEV, + .smmu = { + .reg = 0x230, + .bit = 12, + }, + .latency = { + .reg = 0x380, + .shift = 0, + .mask = 0xff, + .def = 0xa5, + }, + }, { + .id = 0x4d, + .name = "xusb_devw", + .swgroup = TEGRA_SWGROUP_XUSB_DEV, + .smmu = { + .reg = 0x230, + .bit = 13, + }, + .latency = { + .reg = 0x380, + .shift = 16, + .mask = 0xff, + .def = 0xa5, + }, + }, { + .id = 0x4e, + .name = "fdcdwr3", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x230, + .bit = 14, + }, + .latency = { + .reg = 0x388, + .shift = 0, + .mask = 0xff, + .def = 0x10, + }, + }, { + .id = 0x4f, + .name = "fdcdrd3", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x230, + .bit = 15, + }, + .latency = { + .reg = 0x384, + .shift = 0, + .mask = 0xff, + .def = 0x0c, + }, + }, { + .id = 0x50, + .name = "fdcwr4", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x230, + .bit = 16, + }, + .latency = { + .reg = 0x388, + .shift = 16, + .mask = 0xff, + .def = 0x10, + }, + }, { + .id = 0x51, + .name = "fdcrd4", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x230, + .bit = 17, + }, + .latency = { + .reg = 0x384, + .shift = 16, + .mask = 0xff, + .def = 0x0c, + }, + }, { + .id = 0x52, + .name = "emucifr", + .swgroup = TEGRA_SWGROUP_EMUCIF, + .latency = { + .reg = 0x38c, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x53, + .name = "emucifw", + .swgroup = TEGRA_SWGROUP_EMUCIF, + .latency = { + .reg = 0x38c, + .shift = 16, + .mask = 0xff, + .def = 0x0e, + }, + }, { + .id = 0x54, + .name = "tsecsrd", + .swgroup = TEGRA_SWGROUP_TSEC, + .smmu = { + .reg = 0x230, + .bit = 20, + }, + .latency = { + .reg = 0x390, + .shift = 0, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x55, + .name = "tsecswr", + .swgroup = TEGRA_SWGROUP_TSEC, + .smmu = { + .reg = 0x230, + .bit = 21, + }, + .latency = { + .reg = 0x390, + .shift = 16, + .mask = 0xff, + .def = 0x50, + }, + }, +}; + +static const struct tegra_smmu_swgroup tegra114_swgroups[] = { + { .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 }, + { .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 }, + { .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 }, + { .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c }, + { .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c }, + { .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268 }, + { .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 }, + { .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 }, + { .swgroup = TEGRA_SWGROUP_MSENC, .reg = 0x264 }, + { .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 }, + { .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c }, + { .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 }, + { .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 }, + { .swgroup = TEGRA_SWGROUP_XUSB_HOST, .reg = 0x288 }, + { .swgroup = TEGRA_SWGROUP_XUSB_DEV, .reg = 0x28c }, + { .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 }, +}; + +static void tegra114_flush_dcache(struct page *page, unsigned long offset, + size_t size) +{ + phys_addr_t phys = page_to_phys(page) + offset; + void *virt = page_address(page) + offset; + + __cpuc_flush_dcache_area(virt, size); + outer_flush_range(phys, phys + size); +} + +static const struct tegra_smmu_ops tegra114_smmu_ops = { + .flush_dcache = tegra114_flush_dcache, +}; + +static const struct tegra_smmu_soc tegra114_smmu_soc = { + .groups = tegra114_smmu_groups, + .num_groups = ARRAY_SIZE(tegra114_smmu_groups), + .clients = tegra114_mc_clients, + .num_clients = ARRAY_SIZE(tegra114_mc_clients), + .swgroups = tegra114_swgroups, + .num_swgroups = ARRAY_SIZE(tegra114_swgroups), + .supports_round_robin_arbitration = false, + .supports_request_limit = false, + .num_address_bits = 32, + .num_asids = 4, + .ops = &tegra114_smmu_ops, +}; + +const struct tegra_mc_soc tegra114_mc_soc = { + .clients = tegra114_mc_clients, + .num_clients = ARRAY_SIZE(tegra114_mc_clients), + .atom_size = 32, + .smmu = &tegra114_smmu_soc, +}; diff --git a/drivers/memory/tegra/tegra124-mc.c b/drivers/memory/tegra/tegra124-mc.c new file mode 100644 index 000000000000..b4604be65392 --- /dev/null +++ b/drivers/memory/tegra/tegra124-mc.c @@ -0,0 +1,1039 @@ +/* + * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/of.h> +#include <linux/mm.h> + +#include <asm/cacheflush.h> + +#include <dt-bindings/memory/tegra124-mc.h> + +#include "tegra-mc.h" + +static const struct of_device_id tegra124_periph_matches[] = { + { .compatible = "nvidia,tegra124-sdhci", }, + { } +}; + +static const struct tegra_smmu_group_init tegra124_smmu_groups[] = { + { 0, "peripherals", tegra124_periph_matches }, +}; + +static const struct tegra_mc_client tegra124_mc_clients[] = { + { + .id = 0x00, + .name = "ptcr", + .swgroup = TEGRA_SWGROUP_PTC, + }, { + .id = 0x01, + .name = "display0a", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 1, + }, + .latency = { + .reg = 0x2e8, + .shift = 0, + .mask = 0xff, + .def = 0xc2, + }, + }, { + .id = 0x02, + .name = "display0ab", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 2, + }, + .latency = { + .reg = 0x2f4, + .shift = 0, + .mask = 0xff, + .def = 0xc6, + }, + }, { + .id = 0x03, + .name = "display0b", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 3, + }, + .latency = { + .reg = 0x2e8, + .shift = 16, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x04, + .name = "display0bb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 4, + }, + .latency = { + .reg = 0x2f4, + .shift = 16, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x05, + .name = "display0c", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 5, + }, + .latency = { + .reg = 0x2ec, + .shift = 0, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x06, + .name = "display0cb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 6, + }, + .latency = { + .reg = 0x2f8, + .shift = 0, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x0e, + .name = "afir", + .swgroup = TEGRA_SWGROUP_AFI, + .smmu = { + .reg = 0x228, + .bit = 14, + }, + .latency = { + .reg = 0x2e0, + .shift = 0, + .mask = 0xff, + .def = 0x13, + }, + }, { + .id = 0x0f, + .name = "avpcarm7r", + .swgroup = TEGRA_SWGROUP_AVPC, + .smmu = { + .reg = 0x228, + .bit = 15, + }, + .latency = { + .reg = 0x2e4, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x10, + .name = "displayhc", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 16, + }, + .latency = { + .reg = 0x2f0, + .shift = 0, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x11, + .name = "displayhcb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 17, + }, + .latency = { + .reg = 0x2fc, + .shift = 0, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x15, + .name = "hdar", + .swgroup = TEGRA_SWGROUP_HDA, + .smmu = { + .reg = 0x228, + .bit = 21, + }, + .latency = { + .reg = 0x318, + .shift = 0, + .mask = 0xff, + .def = 0x24, + }, + }, { + .id = 0x16, + .name = "host1xdmar", + .swgroup = TEGRA_SWGROUP_HC, + .smmu = { + .reg = 0x228, + .bit = 22, + }, + .latency = { + .reg = 0x310, + .shift = 0, + .mask = 0xff, + .def = 0x1e, + }, + }, { + .id = 0x17, + .name = "host1xr", + .swgroup = TEGRA_SWGROUP_HC, + .smmu = { + .reg = 0x228, + .bit = 23, + }, + .latency = { + .reg = 0x310, + .shift = 16, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x1c, + .name = "msencsrd", + .swgroup = TEGRA_SWGROUP_MSENC, + .smmu = { + .reg = 0x228, + .bit = 28, + }, + .latency = { + .reg = 0x328, + .shift = 0, + .mask = 0xff, + .def = 0x23, + }, + }, { + .id = 0x1d, + .name = "ppcsahbdmar", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x228, + .bit = 29, + }, + .latency = { + .reg = 0x344, + .shift = 0, + .mask = 0xff, + .def = 0x49, + }, + }, { + .id = 0x1e, + .name = "ppcsahbslvr", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x228, + .bit = 30, + }, + .latency = { + .reg = 0x344, + .shift = 16, + .mask = 0xff, + .def = 0x1a, + }, + }, { + .id = 0x1f, + .name = "satar", + .swgroup = TEGRA_SWGROUP_SATA, + .smmu = { + .reg = 0x228, + .bit = 31, + }, + .latency = { + .reg = 0x350, + .shift = 0, + .mask = 0xff, + .def = 0x65, + }, + }, { + .id = 0x22, + .name = "vdebsevr", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 2, + }, + .latency = { + .reg = 0x354, + .shift = 0, + .mask = 0xff, + .def = 0x4f, + }, + }, { + .id = 0x23, + .name = "vdember", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 3, + }, + .latency = { + .reg = 0x354, + .shift = 16, + .mask = 0xff, + .def = 0x3d, + }, + }, { + .id = 0x24, + .name = "vdemcer", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 4, + }, + .latency = { + .reg = 0x358, + .shift = 0, + .mask = 0xff, + .def = 0x66, + }, + }, { + .id = 0x25, + .name = "vdetper", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 5, + }, + .latency = { + .reg = 0x358, + .shift = 16, + .mask = 0xff, + .def = 0xa5, + }, + }, { + .id = 0x26, + .name = "mpcorelpr", + .swgroup = TEGRA_SWGROUP_MPCORELP, + .latency = { + .reg = 0x324, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x27, + .name = "mpcorer", + .swgroup = TEGRA_SWGROUP_MPCORE, + .latency = { + .reg = 0x320, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x2b, + .name = "msencswr", + .swgroup = TEGRA_SWGROUP_MSENC, + .smmu = { + .reg = 0x22c, + .bit = 11, + }, + .latency = { + .reg = 0x328, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x31, + .name = "afiw", + .swgroup = TEGRA_SWGROUP_AFI, + .smmu = { + .reg = 0x22c, + .bit = 17, + }, + .latency = { + .reg = 0x2e0, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x32, + .name = "avpcarm7w", + .swgroup = TEGRA_SWGROUP_AVPC, + .smmu = { + .reg = 0x22c, + .bit = 18, + }, + .latency = { + .reg = 0x2e4, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x35, + .name = "hdaw", + .swgroup = TEGRA_SWGROUP_HDA, + .smmu = { + .reg = 0x22c, + .bit = 21, + }, + .latency = { + .reg = 0x318, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x36, + .name = "host1xw", + .swgroup = TEGRA_SWGROUP_HC, + .smmu = { + .reg = 0x22c, + .bit = 22, + }, + .latency = { + .reg = 0x314, + .shift = 0, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x38, + .name = "mpcorelpw", + .swgroup = TEGRA_SWGROUP_MPCORELP, + .latency = { + .reg = 0x324, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x39, + .name = "mpcorew", + .swgroup = TEGRA_SWGROUP_MPCORE, + .latency = { + .reg = 0x320, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x3b, + .name = "ppcsahbdmaw", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x22c, + .bit = 27, + }, + .latency = { + .reg = 0x348, + .shift = 0, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x3c, + .name = "ppcsahbslvw", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x22c, + .bit = 28, + }, + .latency = { + .reg = 0x348, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x3d, + .name = "sataw", + .swgroup = TEGRA_SWGROUP_SATA, + .smmu = { + .reg = 0x22c, + .bit = 29, + }, + .latency = { + .reg = 0x350, + .shift = 16, + .mask = 0xff, + .def = 0x65, + }, + }, { + .id = 0x3e, + .name = "vdebsevw", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 30, + }, + .latency = { + .reg = 0x35c, + .shift = 0, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x3f, + .name = "vdedbgw", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 31, + }, + .latency = { + .reg = 0x35c, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x40, + .name = "vdembew", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x230, + .bit = 0, + }, + .latency = { + .reg = 0x360, + .shift = 0, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x41, + .name = "vdetpmw", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x230, + .bit = 1, + }, + .latency = { + .reg = 0x360, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x44, + .name = "ispra", + .swgroup = TEGRA_SWGROUP_ISP2, + .smmu = { + .reg = 0x230, + .bit = 4, + }, + .latency = { + .reg = 0x370, + .shift = 0, + .mask = 0xff, + .def = 0x18, + }, + }, { + .id = 0x46, + .name = "ispwa", + .swgroup = TEGRA_SWGROUP_ISP2, + .smmu = { + .reg = 0x230, + .bit = 6, + }, + .latency = { + .reg = 0x374, + .shift = 0, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x47, + .name = "ispwb", + .swgroup = TEGRA_SWGROUP_ISP2, + .smmu = { + .reg = 0x230, + .bit = 7, + }, + .latency = { + .reg = 0x374, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x4a, + .name = "xusb_hostr", + .swgroup = TEGRA_SWGROUP_XUSB_HOST, + .smmu = { + .reg = 0x230, + .bit = 10, + }, + .latency = { + .reg = 0x37c, + .shift = 0, + .mask = 0xff, + .def = 0x39, + }, + }, { + .id = 0x4b, + .name = "xusb_hostw", + .swgroup = TEGRA_SWGROUP_XUSB_HOST, + .smmu = { + .reg = 0x230, + .bit = 11, + }, + .latency = { + .reg = 0x37c, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x4c, + .name = "xusb_devr", + .swgroup = TEGRA_SWGROUP_XUSB_DEV, + .smmu = { + .reg = 0x230, + .bit = 12, + }, + .latency = { + .reg = 0x380, + .shift = 0, + .mask = 0xff, + .def = 0x39, + }, + }, { + .id = 0x4d, + .name = "xusb_devw", + .swgroup = TEGRA_SWGROUP_XUSB_DEV, + .smmu = { + .reg = 0x230, + .bit = 13, + }, + .latency = { + .reg = 0x380, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x4e, + .name = "isprab", + .swgroup = TEGRA_SWGROUP_ISP2B, + .smmu = { + .reg = 0x230, + .bit = 14, + }, + .latency = { + .reg = 0x384, + .shift = 0, + .mask = 0xff, + .def = 0x18, + }, + }, { + .id = 0x50, + .name = "ispwab", + .swgroup = TEGRA_SWGROUP_ISP2B, + .smmu = { + .reg = 0x230, + .bit = 16, + }, + .latency = { + .reg = 0x388, + .shift = 0, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x51, + .name = "ispwbb", + .swgroup = TEGRA_SWGROUP_ISP2B, + .smmu = { + .reg = 0x230, + .bit = 17, + }, + .latency = { + .reg = 0x388, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x54, + .name = "tsecsrd", + .swgroup = TEGRA_SWGROUP_TSEC, + .smmu = { + .reg = 0x230, + .bit = 20, + }, + .latency = { + .reg = 0x390, + .shift = 0, + .mask = 0xff, + .def = 0x9b, + }, + }, { + .id = 0x55, + .name = "tsecswr", + .swgroup = TEGRA_SWGROUP_TSEC, + .smmu = { + .reg = 0x230, + .bit = 21, + }, + .latency = { + .reg = 0x390, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x56, + .name = "a9avpscr", + .swgroup = TEGRA_SWGROUP_A9AVP, + .smmu = { + .reg = 0x230, + .bit = 22, + }, + .latency = { + .reg = 0x3a4, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x57, + .name = "a9avpscw", + .swgroup = TEGRA_SWGROUP_A9AVP, + .smmu = { + .reg = 0x230, + .bit = 23, + }, + .latency = { + .reg = 0x3a4, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x58, + .name = "gpusrd", + .swgroup = TEGRA_SWGROUP_GPU, + .smmu = { + /* read-only */ + .reg = 0x230, + .bit = 24, + }, + .latency = { + .reg = 0x3c8, + .shift = 0, + .mask = 0xff, + .def = 0x1a, + }, + }, { + .id = 0x59, + .name = "gpuswr", + .swgroup = TEGRA_SWGROUP_GPU, + .smmu = { + /* read-only */ + .reg = 0x230, + .bit = 25, + }, + .latency = { + .reg = 0x3c8, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x5a, + .name = "displayt", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x230, + .bit = 26, + }, + .latency = { + .reg = 0x2f0, + .shift = 16, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x60, + .name = "sdmmcra", + .swgroup = TEGRA_SWGROUP_SDMMC1A, + .smmu = { + .reg = 0x234, + .bit = 0, + }, + .latency = { + .reg = 0x3b8, + .shift = 0, + .mask = 0xff, + .def = 0x49, + }, + }, { + .id = 0x61, + .name = "sdmmcraa", + .swgroup = TEGRA_SWGROUP_SDMMC2A, + .smmu = { + .reg = 0x234, + .bit = 1, + }, + .latency = { + .reg = 0x3bc, + .shift = 0, + .mask = 0xff, + .def = 0x49, + }, + }, { + .id = 0x62, + .name = "sdmmcr", + .swgroup = TEGRA_SWGROUP_SDMMC3A, + .smmu = { + .reg = 0x234, + .bit = 2, + }, + .latency = { + .reg = 0x3c0, + .shift = 0, + .mask = 0xff, + .def = 0x49, + }, + }, { + .id = 0x63, + .swgroup = TEGRA_SWGROUP_SDMMC4A, + .name = "sdmmcrab", + .smmu = { + .reg = 0x234, + .bit = 3, + }, + .latency = { + .reg = 0x3c4, + .shift = 0, + .mask = 0xff, + .def = 0x49, + }, + }, { + .id = 0x64, + .name = "sdmmcwa", + .swgroup = TEGRA_SWGROUP_SDMMC1A, + .smmu = { + .reg = 0x234, + .bit = 4, + }, + .latency = { + .reg = 0x3b8, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x65, + .name = "sdmmcwaa", + .swgroup = TEGRA_SWGROUP_SDMMC2A, + .smmu = { + .reg = 0x234, + .bit = 5, + }, + .latency = { + .reg = 0x3bc, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x66, + .name = "sdmmcw", + .swgroup = TEGRA_SWGROUP_SDMMC3A, + .smmu = { + .reg = 0x234, + .bit = 6, + }, + .latency = { + .reg = 0x3c0, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x67, + .name = "sdmmcwab", + .swgroup = TEGRA_SWGROUP_SDMMC4A, + .smmu = { + .reg = 0x234, + .bit = 7, + }, + .latency = { + .reg = 0x3c4, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x6c, + .name = "vicsrd", + .swgroup = TEGRA_SWGROUP_VIC, + .smmu = { + .reg = 0x234, + .bit = 12, + }, + .latency = { + .reg = 0x394, + .shift = 0, + .mask = 0xff, + .def = 0x1a, + }, + }, { + .id = 0x6d, + .name = "vicswr", + .swgroup = TEGRA_SWGROUP_VIC, + .smmu = { + .reg = 0x234, + .bit = 13, + }, + .latency = { + .reg = 0x394, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x72, + .name = "viw", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x234, + .bit = 18, + }, + .latency = { + .reg = 0x398, + .shift = 0, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x73, + .name = "displayd", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x234, + .bit = 19, + }, + .latency = { + .reg = 0x3c8, + .shift = 0, + .mask = 0xff, + .def = 0x50, + }, + }, +}; + +static const struct tegra_smmu_swgroup tegra124_swgroups[] = { + { .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 }, + { .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 }, + { .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 }, + { .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c }, + { .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 }, + { .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 }, + { .swgroup = TEGRA_SWGROUP_MSENC, .reg = 0x264 }, + { .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 }, + { .swgroup = TEGRA_SWGROUP_SATA, .reg = 0x274 }, + { .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c }, + { .swgroup = TEGRA_SWGROUP_ISP2, .reg = 0x258 }, + { .swgroup = TEGRA_SWGROUP_XUSB_HOST, .reg = 0x288 }, + { .swgroup = TEGRA_SWGROUP_XUSB_DEV, .reg = 0x28c }, + { .swgroup = TEGRA_SWGROUP_ISP2B, .reg = 0xaa4 }, + { .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 }, + { .swgroup = TEGRA_SWGROUP_A9AVP, .reg = 0x290 }, + { .swgroup = TEGRA_SWGROUP_GPU, .reg = 0xaa8 }, + { .swgroup = TEGRA_SWGROUP_SDMMC1A, .reg = 0xa94 }, + { .swgroup = TEGRA_SWGROUP_SDMMC2A, .reg = 0xa98 }, + { .swgroup = TEGRA_SWGROUP_SDMMC3A, .reg = 0xa9c }, + { .swgroup = TEGRA_SWGROUP_SDMMC4A, .reg = 0xaa0 }, + { .swgroup = TEGRA_SWGROUP_VIC, .reg = 0x284 }, + { .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 }, +}; + +#ifdef CONFIG_ARCH_TEGRA_124_SOC +static void tegra124_flush_dcache(struct page *page, unsigned long offset, + size_t size) +{ + phys_addr_t phys = page_to_phys(page) + offset; + void *virt = page_address(page) + offset; + + __cpuc_flush_dcache_area(virt, size); + outer_flush_range(phys, phys + size); +} + +static const struct tegra_smmu_ops tegra124_smmu_ops = { + .flush_dcache = tegra124_flush_dcache, +}; + +static const struct tegra_smmu_soc tegra124_smmu_soc = { + .groups = tegra124_smmu_groups, + .num_groups = ARRAY_SIZE(tegra124_smmu_groups), + .clients = tegra124_mc_clients, + .num_clients = ARRAY_SIZE(tegra124_mc_clients), + .swgroups = tegra124_swgroups, + .num_swgroups = ARRAY_SIZE(tegra124_swgroups), + .num_address_bits = 34, + .num_asids = 128, + .ops = &tegra124_smmu_ops, +}; + +const struct tegra_mc_soc tegra124_mc_soc = { + .clients = tegra124_mc_clients, + .num_clients = ARRAY_SIZE(tegra124_mc_clients), + .atom_size = 32, + .smmu = &tegra124_smmu_soc, +}; +#endif /* CONFIG_ARCH_TEGRA_124_SOC */ + +#ifdef CONFIG_ARCH_TEGRA_132_SOC +static void tegra132_flush_dcache(struct page *page, unsigned long offset, + size_t size) +{ + void *virt = page_address(page) + offset; + + __flush_dcache_area(virt, size); +} + +static const struct tegra_smmu_ops tegra132_smmu_ops = { + .flush_dcache = tegra132_flush_dcache, +}; + +static const struct tegra_smmu_soc tegra132_smmu_soc = { + .groups = tegra124_smmu_groups, + .num_groups = ARRAY_SIZE(tegra124_smmu_groups), + .clients = tegra124_mc_clients, + .num_clients = ARRAY_SIZE(tegra124_mc_clients), + .swgroups = tegra124_swgroups, + .num_swgroups = ARRAY_SIZE(tegra124_swgroups), + .supports_round_robin_arbitration = true, + .supports_request_limit = true, + .num_address_bits = 34, + .num_asids = 128, + .ops = &tegra132_smmu_ops, +}; + +const struct tegra_mc_soc tegra132_mc_soc = { + .clients = tegra124_mc_clients, + .num_clients = ARRAY_SIZE(tegra124_mc_clients), + .atom_size = 32, + .smmu = &tegra132_smmu_soc, +}; +#endif /* CONFIG_ARCH_TEGRA_132_SOC */ diff --git a/drivers/memory/tegra/tegra30-mc.c b/drivers/memory/tegra/tegra30-mc.c new file mode 100644 index 000000000000..9a7ea212eded --- /dev/null +++ b/drivers/memory/tegra/tegra30-mc.c @@ -0,0 +1,980 @@ +/* + * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/of.h> +#include <linux/mm.h> + +#include <asm/cacheflush.h> + +#include <dt-bindings/memory/tegra30-mc.h> + +#include "tegra-mc.h" + +static const struct of_device_id tegra30_periph_matches[] = { + { } +}; + +static const struct tegra_smmu_group_init tegra30_smmu_groups[] = { + { 0, "peripherals", tegra30_periph_matches }, +}; + +static const struct tegra_mc_client tegra30_mc_clients[] = { + { + .id = 0x00, + .name = "ptcr", + .swgroup = TEGRA_SWGROUP_PTC, + }, { + .id = 0x01, + .name = "display0a", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 1, + }, + .latency = { + .reg = 0x2e8, + .shift = 0, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x02, + .name = "display0ab", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 2, + }, + .latency = { + .reg = 0x2f4, + .shift = 0, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x03, + .name = "display0b", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 3, + }, + .latency = { + .reg = 0x2e8, + .shift = 16, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x04, + .name = "display0bb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 4, + }, + .latency = { + .reg = 0x2f4, + .shift = 16, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x05, + .name = "display0c", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 5, + }, + .latency = { + .reg = 0x2ec, + .shift = 0, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x06, + .name = "display0cb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 6, + }, + .latency = { + .reg = 0x2f8, + .shift = 0, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x07, + .name = "display1b", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 7, + }, + .latency = { + .reg = 0x2ec, + .shift = 16, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x08, + .name = "display1bb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 8, + }, + .latency = { + .reg = 0x2f8, + .shift = 16, + .mask = 0xff, + .def = 0x4e, + }, + }, { + .id = 0x09, + .name = "eppup", + .swgroup = TEGRA_SWGROUP_EPP, + .smmu = { + .reg = 0x228, + .bit = 9, + }, + .latency = { + .reg = 0x300, + .shift = 0, + .mask = 0xff, + .def = 0x17, + }, + }, { + .id = 0x0a, + .name = "g2pr", + .swgroup = TEGRA_SWGROUP_G2, + .smmu = { + .reg = 0x228, + .bit = 10, + }, + .latency = { + .reg = 0x308, + .shift = 0, + .mask = 0xff, + .def = 0x09, + }, + }, { + .id = 0x0b, + .name = "g2sr", + .swgroup = TEGRA_SWGROUP_G2, + .smmu = { + .reg = 0x228, + .bit = 11, + }, + .latency = { + .reg = 0x308, + .shift = 16, + .mask = 0xff, + .def = 0x09, + }, + }, { + .id = 0x0c, + .name = "mpeunifbr", + .swgroup = TEGRA_SWGROUP_MPE, + .smmu = { + .reg = 0x228, + .bit = 12, + }, + .latency = { + .reg = 0x328, + .shift = 0, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x0d, + .name = "viruv", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x228, + .bit = 13, + }, + .latency = { + .reg = 0x364, + .shift = 0, + .mask = 0xff, + .def = 0x2c, + }, + }, { + .id = 0x0e, + .name = "afir", + .swgroup = TEGRA_SWGROUP_AFI, + .smmu = { + .reg = 0x228, + .bit = 14, + }, + .latency = { + .reg = 0x2e0, + .shift = 0, + .mask = 0xff, + .def = 0x10, + }, + }, { + .id = 0x0f, + .name = "avpcarm7r", + .swgroup = TEGRA_SWGROUP_AVPC, + .smmu = { + .reg = 0x228, + .bit = 15, + }, + .latency = { + .reg = 0x2e4, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x10, + .name = "displayhc", + .swgroup = TEGRA_SWGROUP_DC, + .smmu = { + .reg = 0x228, + .bit = 16, + }, + .latency = { + .reg = 0x2f0, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x11, + .name = "displayhcb", + .swgroup = TEGRA_SWGROUP_DCB, + .smmu = { + .reg = 0x228, + .bit = 17, + }, + .latency = { + .reg = 0x2fc, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x12, + .name = "fdcdrd", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x228, + .bit = 18, + }, + .latency = { + .reg = 0x334, + .shift = 0, + .mask = 0xff, + .def = 0x0a, + }, + }, { + .id = 0x13, + .name = "fdcdrd2", + .swgroup = TEGRA_SWGROUP_NV2, + .smmu = { + .reg = 0x228, + .bit = 19, + }, + .latency = { + .reg = 0x33c, + .shift = 0, + .mask = 0xff, + .def = 0x0a, + }, + }, { + .id = 0x14, + .name = "g2dr", + .swgroup = TEGRA_SWGROUP_G2, + .smmu = { + .reg = 0x228, + .bit = 20, + }, + .latency = { + .reg = 0x30c, + .shift = 0, + .mask = 0xff, + .def = 0x0a, + }, + }, { + .id = 0x15, + .name = "hdar", + .swgroup = TEGRA_SWGROUP_HDA, + .smmu = { + .reg = 0x228, + .bit = 21, + }, + .latency = { + .reg = 0x318, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x16, + .name = "host1xdmar", + .swgroup = TEGRA_SWGROUP_HC, + .smmu = { + .reg = 0x228, + .bit = 22, + }, + .latency = { + .reg = 0x310, + .shift = 0, + .mask = 0xff, + .def = 0x05, + }, + }, { + .id = 0x17, + .name = "host1xr", + .swgroup = TEGRA_SWGROUP_HC, + .smmu = { + .reg = 0x228, + .bit = 23, + }, + .latency = { + .reg = 0x310, + .shift = 16, + .mask = 0xff, + .def = 0x50, + }, + }, { + .id = 0x18, + .name = "idxsrd", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x228, + .bit = 24, + }, + .latency = { + .reg = 0x334, + .shift = 16, + .mask = 0xff, + .def = 0x13, + }, + }, { + .id = 0x19, + .name = "idxsrd2", + .swgroup = TEGRA_SWGROUP_NV2, + .smmu = { + .reg = 0x228, + .bit = 25, + }, + .latency = { + .reg = 0x33c, + .shift = 16, + .mask = 0xff, + .def = 0x13, + }, + }, { + .id = 0x1a, + .name = "mpe_ipred", + .swgroup = TEGRA_SWGROUP_MPE, + .smmu = { + .reg = 0x228, + .bit = 26, + }, + .latency = { + .reg = 0x328, + .shift = 16, + .mask = 0xff, + .def = 0x80, + }, + }, { + .id = 0x1b, + .name = "mpeamemrd", + .swgroup = TEGRA_SWGROUP_MPE, + .smmu = { + .reg = 0x228, + .bit = 27, + }, + .latency = { + .reg = 0x32c, + .shift = 0, + .mask = 0xff, + .def = 0x42, + }, + }, { + .id = 0x1c, + .name = "mpecsrd", + .swgroup = TEGRA_SWGROUP_MPE, + .smmu = { + .reg = 0x228, + .bit = 28, + }, + .latency = { + .reg = 0x32c, + .shift = 16, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x1d, + .name = "ppcsahbdmar", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x228, + .bit = 29, + }, + .latency = { + .reg = 0x344, + .shift = 0, + .mask = 0xff, + .def = 0x10, + }, + }, { + .id = 0x1e, + .name = "ppcsahbslvr", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x228, + .bit = 30, + }, + .latency = { + .reg = 0x344, + .shift = 16, + .mask = 0xff, + .def = 0x12, + }, + }, { + .id = 0x1f, + .name = "satar", + .swgroup = TEGRA_SWGROUP_SATA, + .smmu = { + .reg = 0x228, + .bit = 31, + }, + .latency = { + .reg = 0x350, + .shift = 0, + .mask = 0xff, + .def = 0x33, + }, + }, { + .id = 0x20, + .name = "texsrd", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x22c, + .bit = 0, + }, + .latency = { + .reg = 0x338, + .shift = 0, + .mask = 0xff, + .def = 0x13, + }, + }, { + .id = 0x21, + .name = "texsrd2", + .swgroup = TEGRA_SWGROUP_NV2, + .smmu = { + .reg = 0x22c, + .bit = 1, + }, + .latency = { + .reg = 0x340, + .shift = 0, + .mask = 0xff, + .def = 0x13, + }, + }, { + .id = 0x22, + .name = "vdebsevr", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 2, + }, + .latency = { + .reg = 0x354, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x23, + .name = "vdember", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 3, + }, + .latency = { + .reg = 0x354, + .shift = 16, + .mask = 0xff, + .def = 0xd0, + }, + }, { + .id = 0x24, + .name = "vdemcer", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 4, + }, + .latency = { + .reg = 0x358, + .shift = 0, + .mask = 0xff, + .def = 0x2a, + }, + }, { + .id = 0x25, + .name = "vdetper", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 5, + }, + .latency = { + .reg = 0x358, + .shift = 16, + .mask = 0xff, + .def = 0x74, + }, + }, { + .id = 0x26, + .name = "mpcorelpr", + .swgroup = TEGRA_SWGROUP_MPCORELP, + .latency = { + .reg = 0x324, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x27, + .name = "mpcorer", + .swgroup = TEGRA_SWGROUP_MPCORE, + .latency = { + .reg = 0x320, + .shift = 0, + .mask = 0xff, + .def = 0x04, + }, + }, { + .id = 0x28, + .name = "eppu", + .swgroup = TEGRA_SWGROUP_EPP, + .smmu = { + .reg = 0x22c, + .bit = 8, + }, + .latency = { + .reg = 0x300, + .shift = 16, + .mask = 0xff, + .def = 0x6c, + }, + }, { + .id = 0x29, + .name = "eppv", + .swgroup = TEGRA_SWGROUP_EPP, + .smmu = { + .reg = 0x22c, + .bit = 9, + }, + .latency = { + .reg = 0x304, + .shift = 0, + .mask = 0xff, + .def = 0x6c, + }, + }, { + .id = 0x2a, + .name = "eppy", + .swgroup = TEGRA_SWGROUP_EPP, + .smmu = { + .reg = 0x22c, + .bit = 10, + }, + .latency = { + .reg = 0x304, + .shift = 16, + .mask = 0xff, + .def = 0x6c, + }, + }, { + .id = 0x2b, + .name = "mpeunifbw", + .swgroup = TEGRA_SWGROUP_MPE, + .smmu = { + .reg = 0x22c, + .bit = 11, + }, + .latency = { + .reg = 0x330, + .shift = 0, + .mask = 0xff, + .def = 0x13, + }, + }, { + .id = 0x2c, + .name = "viwsb", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x22c, + .bit = 12, + }, + .latency = { + .reg = 0x364, + .shift = 16, + .mask = 0xff, + .def = 0x12, + }, + }, { + .id = 0x2d, + .name = "viwu", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x22c, + .bit = 13, + }, + .latency = { + .reg = 0x368, + .shift = 0, + .mask = 0xff, + .def = 0xb2, + }, + }, { + .id = 0x2e, + .name = "viwv", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x22c, + .bit = 14, + }, + .latency = { + .reg = 0x368, + .shift = 16, + .mask = 0xff, + .def = 0xb2, + }, + }, { + .id = 0x2f, + .name = "viwy", + .swgroup = TEGRA_SWGROUP_VI, + .smmu = { + .reg = 0x22c, + .bit = 15, + }, + .latency = { + .reg = 0x36c, + .shift = 0, + .mask = 0xff, + .def = 0x12, + }, + }, { + .id = 0x30, + .name = "g2dw", + .swgroup = TEGRA_SWGROUP_G2, + .smmu = { + .reg = 0x22c, + .bit = 16, + }, + .latency = { + .reg = 0x30c, + .shift = 16, + .mask = 0xff, + .def = 0x9, + }, + }, { + .id = 0x31, + .name = "afiw", + .swgroup = TEGRA_SWGROUP_AFI, + .smmu = { + .reg = 0x22c, + .bit = 17, + }, + .latency = { + .reg = 0x2e0, + .shift = 16, + .mask = 0xff, + .def = 0x0c, + }, + }, { + .id = 0x32, + .name = "avpcarm7w", + .swgroup = TEGRA_SWGROUP_AVPC, + .smmu = { + .reg = 0x22c, + .bit = 18, + }, + .latency = { + .reg = 0x2e4, + .shift = 16, + .mask = 0xff, + .def = 0x0e, + }, + }, { + .id = 0x33, + .name = "fdcdwr", + .swgroup = TEGRA_SWGROUP_NV, + .smmu = { + .reg = 0x22c, + .bit = 19, + }, + .latency = { + .reg = 0x338, + .shift = 16, + .mask = 0xff, + .def = 0x0a, + }, + }, { + .id = 0x34, + .name = "fdcwr2", + .swgroup = TEGRA_SWGROUP_NV2, + .smmu = { + .reg = 0x22c, + .bit = 20, + }, + .latency = { + .reg = 0x340, + .shift = 16, + .mask = 0xff, + .def = 0x0a, + }, + }, { + .id = 0x35, + .name = "hdaw", + .swgroup = TEGRA_SWGROUP_HDA, + .smmu = { + .reg = 0x22c, + .bit = 21, + }, + .latency = { + .reg = 0x318, + .shift = 16, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x36, + .name = "host1xw", + .swgroup = TEGRA_SWGROUP_HC, + .smmu = { + .reg = 0x22c, + .bit = 22, + }, + .latency = { + .reg = 0x314, + .shift = 0, + .mask = 0xff, + .def = 0x10, + }, + }, { + .id = 0x37, + .name = "ispw", + .swgroup = TEGRA_SWGROUP_ISP, + .smmu = { + .reg = 0x22c, + .bit = 23, + }, + .latency = { + .reg = 0x31c, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x38, + .name = "mpcorelpw", + .swgroup = TEGRA_SWGROUP_MPCORELP, + .latency = { + .reg = 0x324, + .shift = 16, + .mask = 0xff, + .def = 0x0e, + }, + }, { + .id = 0x39, + .name = "mpcorew", + .swgroup = TEGRA_SWGROUP_MPCORE, + .latency = { + .reg = 0x320, + .shift = 16, + .mask = 0xff, + .def = 0x0e, + }, + }, { + .id = 0x3a, + .name = "mpecswr", + .swgroup = TEGRA_SWGROUP_MPE, + .smmu = { + .reg = 0x22c, + .bit = 26, + }, + .latency = { + .reg = 0x330, + .shift = 16, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x3b, + .name = "ppcsahbdmaw", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x22c, + .bit = 27, + }, + .latency = { + .reg = 0x348, + .shift = 0, + .mask = 0xff, + .def = 0x10, + }, + }, { + .id = 0x3c, + .name = "ppcsahbslvw", + .swgroup = TEGRA_SWGROUP_PPCS, + .smmu = { + .reg = 0x22c, + .bit = 28, + }, + .latency = { + .reg = 0x348, + .shift = 16, + .mask = 0xff, + .def = 0x06, + }, + }, { + .id = 0x3d, + .name = "sataw", + .swgroup = TEGRA_SWGROUP_SATA, + .smmu = { + .reg = 0x22c, + .bit = 29, + }, + .latency = { + .reg = 0x350, + .shift = 16, + .mask = 0xff, + .def = 0x33, + }, + }, { + .id = 0x3e, + .name = "vdebsevw", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 30, + }, + .latency = { + .reg = 0x35c, + .shift = 0, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x3f, + .name = "vdedbgw", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x22c, + .bit = 31, + }, + .latency = { + .reg = 0x35c, + .shift = 16, + .mask = 0xff, + .def = 0xff, + }, + }, { + .id = 0x40, + .name = "vdembew", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x230, + .bit = 0, + }, + .latency = { + .reg = 0x360, + .shift = 0, + .mask = 0xff, + .def = 0x42, + }, + }, { + .id = 0x41, + .name = "vdetpmw", + .swgroup = TEGRA_SWGROUP_VDE, + .smmu = { + .reg = 0x230, + .bit = 1, + }, + .latency = { + .reg = 0x360, + .shift = 16, + .mask = 0xff, + .def = 0x2a, + }, + }, +}; + +static const struct tegra_smmu_swgroup tegra30_swgroups[] = { + { .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 }, + { .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 }, + { .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 }, + { .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c }, + { .swgroup = TEGRA_SWGROUP_MPE, .reg = 0x264 }, + { .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 }, + { .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 }, + { .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c }, + { .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268 }, + { .swgroup = TEGRA_SWGROUP_NV2, .reg = 0x26c }, + { .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 }, + { .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 }, + { .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 }, + { .swgroup = TEGRA_SWGROUP_SATA, .reg = 0x278 }, + { .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c }, + { .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 }, +}; + +static void tegra30_flush_dcache(struct page *page, unsigned long offset, + size_t size) +{ + phys_addr_t phys = page_to_phys(page) + offset; + void *virt = page_address(page) + offset; + + __cpuc_flush_dcache_area(virt, size); + outer_flush_range(phys, phys + size); +} + +static const struct tegra_smmu_ops tegra30_smmu_ops = { + .flush_dcache = tegra30_flush_dcache, +}; + +static const struct tegra_smmu_soc tegra30_smmu_soc = { + .groups = tegra30_smmu_groups, + .num_groups = ARRAY_SIZE(tegra30_smmu_groups), + .clients = tegra30_mc_clients, + .num_clients = ARRAY_SIZE(tegra30_mc_clients), + .swgroups = tegra30_swgroups, + .num_swgroups = ARRAY_SIZE(tegra30_swgroups), + .supports_round_robin_arbitration = false, + .supports_request_limit = false, + .num_address_bits = 32, + .num_asids = 4, + .ops = &tegra30_smmu_ops, +}; + +const struct tegra_mc_soc tegra30_mc_soc = { + .clients = tegra30_mc_clients, + .num_clients = ARRAY_SIZE(tegra30_mc_clients), + .atom_size = 16, + .smmu = &tegra30_smmu_soc, +}; diff --git a/drivers/memory/tegra30-mc.c b/drivers/memory/tegra30-mc.c deleted file mode 100644 index ef7934535fd1..000000000000 --- a/drivers/memory/tegra30-mc.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Tegra30 Memory Controller - * - * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include <linux/err.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/ratelimit.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/io.h> - -#define DRV_NAME "tegra30-mc" - -#define MC_INTSTATUS 0x0 -#define MC_INTMASK 0x4 - -#define MC_INT_ERR_SHIFT 6 -#define MC_INT_ERR_MASK (0x1f << MC_INT_ERR_SHIFT) -#define MC_INT_DECERR_EMEM BIT(MC_INT_ERR_SHIFT) -#define MC_INT_SECURITY_VIOLATION BIT(MC_INT_ERR_SHIFT + 2) -#define MC_INT_ARBITRATION_EMEM BIT(MC_INT_ERR_SHIFT + 3) -#define MC_INT_INVALID_SMMU_PAGE BIT(MC_INT_ERR_SHIFT + 4) - -#define MC_ERR_STATUS 0x8 -#define MC_ERR_ADR 0xc - -#define MC_ERR_TYPE_SHIFT 28 -#define MC_ERR_TYPE_MASK (7 << MC_ERR_TYPE_SHIFT) -#define MC_ERR_TYPE_DECERR_EMEM 2 -#define MC_ERR_TYPE_SECURITY_TRUSTZONE 3 -#define MC_ERR_TYPE_SECURITY_CARVEOUT 4 -#define MC_ERR_TYPE_INVALID_SMMU_PAGE 6 - -#define MC_ERR_INVALID_SMMU_PAGE_SHIFT 25 -#define MC_ERR_INVALID_SMMU_PAGE_MASK (7 << MC_ERR_INVALID_SMMU_PAGE_SHIFT) -#define MC_ERR_RW_SHIFT 16 -#define MC_ERR_RW BIT(MC_ERR_RW_SHIFT) -#define MC_ERR_SECURITY BIT(MC_ERR_RW_SHIFT + 1) - -#define SECURITY_VIOLATION_TYPE BIT(30) /* 0=TRUSTZONE, 1=CARVEOUT */ - -#define MC_EMEM_ARB_CFG 0x90 -#define MC_EMEM_ARB_OUTSTANDING_REQ 0x94 -#define MC_EMEM_ARB_TIMING_RCD 0x98 -#define MC_EMEM_ARB_TIMING_RP 0x9c -#define MC_EMEM_ARB_TIMING_RC 0xa0 -#define MC_EMEM_ARB_TIMING_RAS 0xa4 -#define MC_EMEM_ARB_TIMING_FAW 0xa8 -#define MC_EMEM_ARB_TIMING_RRD 0xac -#define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0 -#define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4 -#define MC_EMEM_ARB_TIMING_R2R 0xb8 -#define MC_EMEM_ARB_TIMING_W2W 0xbc -#define MC_EMEM_ARB_TIMING_R2W 0xc0 -#define MC_EMEM_ARB_TIMING_W2R 0xc4 - -#define MC_EMEM_ARB_DA_TURNS 0xd0 -#define MC_EMEM_ARB_DA_COVERS 0xd4 -#define MC_EMEM_ARB_MISC0 0xd8 -#define MC_EMEM_ARB_MISC1 0xdc - -#define MC_EMEM_ARB_RING3_THROTTLE 0xe4 -#define MC_EMEM_ARB_OVERRIDE 0xe8 - -#define MC_TIMING_CONTROL 0xfc - -#define MC_CLIENT_ID_MASK 0x7f - -#define NUM_MC_REG_BANKS 4 - -struct tegra30_mc { - void __iomem *regs[NUM_MC_REG_BANKS]; - struct device *dev; - u32 ctx[0]; -}; - -static inline u32 mc_readl(struct tegra30_mc *mc, u32 offs) -{ - u32 val = 0; - - if (offs < 0x10) - val = readl(mc->regs[0] + offs); - else if (offs < 0x1f0) - val = readl(mc->regs[1] + offs - 0x3c); - else if (offs < 0x228) - val = readl(mc->regs[2] + offs - 0x200); - else if (offs < 0x400) - val = readl(mc->regs[3] + offs - 0x284); - - return val; -} - -static inline void mc_writel(struct tegra30_mc *mc, u32 val, u32 offs) -{ - if (offs < 0x10) - writel(val, mc->regs[0] + offs); - else if (offs < 0x1f0) - writel(val, mc->regs[1] + offs - 0x3c); - else if (offs < 0x228) - writel(val, mc->regs[2] + offs - 0x200); - else if (offs < 0x400) - writel(val, mc->regs[3] + offs - 0x284); -} - -static const char * const tegra30_mc_client[] = { - "csr_ptcr", - "cbr_display0a", - "cbr_display0ab", - "cbr_display0b", - "cbr_display0bb", - "cbr_display0c", - "cbr_display0cb", - "cbr_display1b", - "cbr_display1bb", - "cbr_eppup", - "cbr_g2pr", - "cbr_g2sr", - "cbr_mpeunifbr", - "cbr_viruv", - "csr_afir", - "csr_avpcarm7r", - "csr_displayhc", - "csr_displayhcb", - "csr_fdcdrd", - "csr_fdcdrd2", - "csr_g2dr", - "csr_hdar", - "csr_host1xdmar", - "csr_host1xr", - "csr_idxsrd", - "csr_idxsrd2", - "csr_mpe_ipred", - "csr_mpeamemrd", - "csr_mpecsrd", - "csr_ppcsahbdmar", - "csr_ppcsahbslvr", - "csr_satar", - "csr_texsrd", - "csr_texsrd2", - "csr_vdebsevr", - "csr_vdember", - "csr_vdemcer", - "csr_vdetper", - "csr_mpcorelpr", - "csr_mpcorer", - "cbw_eppu", - "cbw_eppv", - "cbw_eppy", - "cbw_mpeunifbw", - "cbw_viwsb", - "cbw_viwu", - "cbw_viwv", - "cbw_viwy", - "ccw_g2dw", - "csw_afiw", - "csw_avpcarm7w", - "csw_fdcdwr", - "csw_fdcdwr2", - "csw_hdaw", - "csw_host1xw", - "csw_ispw", - "csw_mpcorelpw", - "csw_mpcorew", - "csw_mpecswr", - "csw_ppcsahbdmaw", - "csw_ppcsahbslvw", - "csw_sataw", - "csw_vdebsevw", - "csw_vdedbgw", - "csw_vdembew", - "csw_vdetpmw", -}; - -static void tegra30_mc_decode(struct tegra30_mc *mc, int n) -{ - u32 err, addr; - const char * const mc_int_err[] = { - "MC_DECERR", - "Unknown", - "MC_SECURITY_ERR", - "MC_ARBITRATION_EMEM", - "MC_SMMU_ERR", - }; - const char * const err_type[] = { - "Unknown", - "Unknown", - "DECERR_EMEM", - "SECURITY_TRUSTZONE", - "SECURITY_CARVEOUT", - "Unknown", - "INVALID_SMMU_PAGE", - "Unknown", - }; - char attr[6]; - int cid, perm, type, idx; - const char *client = "Unknown"; - - idx = n - MC_INT_ERR_SHIFT; - if ((idx < 0) || (idx >= ARRAY_SIZE(mc_int_err)) || (idx == 1)) { - dev_err_ratelimited(mc->dev, "Unknown interrupt status %08lx\n", - BIT(n)); - return; - } - - err = mc_readl(mc, MC_ERR_STATUS); - - type = (err & MC_ERR_TYPE_MASK) >> MC_ERR_TYPE_SHIFT; - perm = (err & MC_ERR_INVALID_SMMU_PAGE_MASK) >> - MC_ERR_INVALID_SMMU_PAGE_SHIFT; - if (type == MC_ERR_TYPE_INVALID_SMMU_PAGE) - sprintf(attr, "%c-%c-%c", - (perm & BIT(2)) ? 'R' : '-', - (perm & BIT(1)) ? 'W' : '-', - (perm & BIT(0)) ? 'S' : '-'); - else - attr[0] = '\0'; - - cid = err & MC_CLIENT_ID_MASK; - if (cid < ARRAY_SIZE(tegra30_mc_client)) - client = tegra30_mc_client[cid]; - - addr = mc_readl(mc, MC_ERR_ADR); - - dev_err_ratelimited(mc->dev, "%s (0x%08x): 0x%08x %s (%s %s %s %s)\n", - mc_int_err[idx], err, addr, client, - (err & MC_ERR_SECURITY) ? "secure" : "non-secure", - (err & MC_ERR_RW) ? "write" : "read", - err_type[type], attr); -} - -static const u32 tegra30_mc_ctx[] = { - MC_EMEM_ARB_CFG, - MC_EMEM_ARB_OUTSTANDING_REQ, - MC_EMEM_ARB_TIMING_RCD, - MC_EMEM_ARB_TIMING_RP, - MC_EMEM_ARB_TIMING_RC, - MC_EMEM_ARB_TIMING_RAS, - MC_EMEM_ARB_TIMING_FAW, - MC_EMEM_ARB_TIMING_RRD, - MC_EMEM_ARB_TIMING_RAP2PRE, - MC_EMEM_ARB_TIMING_WAP2PRE, - MC_EMEM_ARB_TIMING_R2R, - MC_EMEM_ARB_TIMING_W2W, - MC_EMEM_ARB_TIMING_R2W, - MC_EMEM_ARB_TIMING_W2R, - MC_EMEM_ARB_DA_TURNS, - MC_EMEM_ARB_DA_COVERS, - MC_EMEM_ARB_MISC0, - MC_EMEM_ARB_MISC1, - MC_EMEM_ARB_RING3_THROTTLE, - MC_EMEM_ARB_OVERRIDE, - MC_INTMASK, -}; - -#ifdef CONFIG_PM -static int tegra30_mc_suspend(struct device *dev) -{ - int i; - struct tegra30_mc *mc = dev_get_drvdata(dev); - - for (i = 0; i < ARRAY_SIZE(tegra30_mc_ctx); i++) - mc->ctx[i] = mc_readl(mc, tegra30_mc_ctx[i]); - return 0; -} - -static int tegra30_mc_resume(struct device *dev) -{ - int i; - struct tegra30_mc *mc = dev_get_drvdata(dev); - - for (i = 0; i < ARRAY_SIZE(tegra30_mc_ctx); i++) - mc_writel(mc, mc->ctx[i], tegra30_mc_ctx[i]); - - mc_writel(mc, 1, MC_TIMING_CONTROL); - /* Read-back to ensure that write reached */ - mc_readl(mc, MC_TIMING_CONTROL); - return 0; -} -#endif - -static UNIVERSAL_DEV_PM_OPS(tegra30_mc_pm, - tegra30_mc_suspend, - tegra30_mc_resume, NULL); - -static const struct of_device_id tegra30_mc_of_match[] = { - { .compatible = "nvidia,tegra30-mc", }, - {}, -}; - -static irqreturn_t tegra30_mc_isr(int irq, void *data) -{ - u32 stat, mask, bit; - struct tegra30_mc *mc = data; - - stat = mc_readl(mc, MC_INTSTATUS); - mask = mc_readl(mc, MC_INTMASK); - mask &= stat; - if (!mask) - return IRQ_NONE; - while ((bit = ffs(mask)) != 0) { - tegra30_mc_decode(mc, bit - 1); - mask &= ~BIT(bit - 1); - } - - mc_writel(mc, stat, MC_INTSTATUS); - return IRQ_HANDLED; -} - -static int tegra30_mc_probe(struct platform_device *pdev) -{ - struct resource *irq; - struct tegra30_mc *mc; - size_t bytes; - int err, i; - u32 intmask; - - bytes = sizeof(*mc) + sizeof(u32) * ARRAY_SIZE(tegra30_mc_ctx); - mc = devm_kzalloc(&pdev->dev, bytes, GFP_KERNEL); - if (!mc) - return -ENOMEM; - mc->dev = &pdev->dev; - - for (i = 0; i < ARRAY_SIZE(mc->regs); i++) { - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, i); - mc->regs[i] = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(mc->regs[i])) - return PTR_ERR(mc->regs[i]); - } - - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!irq) - return -ENODEV; - err = devm_request_irq(&pdev->dev, irq->start, tegra30_mc_isr, - IRQF_SHARED, dev_name(&pdev->dev), mc); - if (err) - return -ENODEV; - - platform_set_drvdata(pdev, mc); - - intmask = MC_INT_INVALID_SMMU_PAGE | - MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION; - mc_writel(mc, intmask, MC_INTMASK); - return 0; -} - -static struct platform_driver tegra30_mc_driver = { - .probe = tegra30_mc_probe, - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - .of_match_table = tegra30_mc_of_match, - .pm = &tegra30_mc_pm, - }, -}; -module_platform_driver(tegra30_mc_driver); - -MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>"); -MODULE_DESCRIPTION("Tegra30 MC driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 33100d10d176..b884614fa4e6 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -15,6 +15,7 @@ #include <linux/err.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/iommu.h> #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/io.h> @@ -237,6 +238,11 @@ static int sdhci_tegra_probe(struct platform_device *pdev) match = of_match_device(sdhci_tegra_dt_match, &pdev->dev); if (!match) return -EINVAL; + + rc = iommu_attach(&pdev->dev); + if (rc < 0) + return rc; + soc_data = match->data; host = sdhci_pltfm_init(pdev, soc_data->pdata, 0); @@ -310,6 +316,8 @@ static int sdhci_tegra_remove(struct platform_device *pdev) clk_disable_unprepare(pltfm_host->clk); clk_put(pltfm_host->clk); + iommu_detach(&pdev->dev); + sdhci_pltfm_free(pdev); return 0; diff --git a/drivers/of/base.c b/drivers/of/base.c index d8574adf0d62..4298f32d205d 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1397,6 +1397,56 @@ void of_print_phandle_args(const char *msg, const struct of_phandle_args *args) printk("\n"); } +void of_phandle_iter_next(struct of_phandle_iter *iter) +{ + struct device_node *dn; + int i, count; + + if (!iter->cur || (iter->cur >= iter->end)) + goto err_out; + + dn = of_find_node_by_phandle(be32_to_cpup(iter->cur++)); + if (!dn) + goto err_out; + + if (iter->cells_name) { + if (of_property_read_u32(dn, iter->cells_name, &count)) + goto err_out; + } else { + count = iter->cell_count; + } + + iter->out_args.np = dn; + iter->out_args.args_count = count; + for (i = 0; i < count; i++) + iter->out_args.args[i] = be32_to_cpup(iter->cur++); + + return; + +err_out: + iter->cur = NULL; +} +EXPORT_SYMBOL_GPL(of_phandle_iter_next); + +void of_phandle_iter_start(struct of_phandle_iter *iter, + const struct device_node *np, + const char *list_name, const char *cells_name, + int cell_count) +{ + int bytes; + + iter->cur = of_get_property(np, list_name, &bytes); + if (!iter->cur) + return; + iter->end = iter->cur; + if (bytes) + iter->end += bytes / sizeof(*iter->cur); + iter->cells_name = cells_name; + iter->cell_count = cell_count; + of_phandle_iter_next(iter); +} +EXPORT_SYMBOL_GPL(of_phandle_iter_start); + static int __of_parse_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name, diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 8922c376456a..dcd41ef25215 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -31,7 +31,7 @@ config PCI_IMX6 select PCIE_DW config PCI_TEGRA - bool "NVIDIA Tegra PCIe controller" + tristate "NVIDIA Tegra PCIe controller" depends on ARCH_TEGRA config PCI_RCAR_GEN2 diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 0fb0fdb223d5..2dc73f172343 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -38,6 +38,7 @@ #include <linux/of_pci.h> #include <linux/of_platform.h> #include <linux/pci.h> +#include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/reset.h> #include <linux/sizes.h> @@ -115,13 +116,20 @@ #define AFI_INTR_CODE 0xb8 #define AFI_INTR_CODE_MASK 0xf -#define AFI_INTR_AXI_SLAVE_ERROR 1 -#define AFI_INTR_AXI_DECODE_ERROR 2 +#define AFI_INTR_INI_SLAVE_ERROR 1 +#define AFI_INTR_INI_DECODE_ERROR 2 #define AFI_INTR_TARGET_ABORT 3 #define AFI_INTR_MASTER_ABORT 4 #define AFI_INTR_INVALID_WRITE 5 #define AFI_INTR_LEGACY 6 #define AFI_INTR_FPCI_DECODE_ERROR 7 +#define AFI_INTR_AXI_DECODE_ERROR 8 +#define AFI_INTR_FPCI_TIMEOUT 9 +#define AFI_INTR_PE_PRSNT_SENSE 10 +#define AFI_INTR_PE_CLKREQ_SENSE 11 +#define AFI_INTR_CLKCLAMP_SENSE 12 +#define AFI_INTR_RDY4PD_SENSE 13 +#define AFI_INTR_P2P_ERROR 14 #define AFI_INTR_SIGNATURE 0xbc #define AFI_UPPER_FPCI_ADDRESS 0xc0 @@ -152,8 +160,10 @@ #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK (0xf << 20) #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE (0x0 << 20) #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420 (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1 (0x0 << 20) #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL (0x1 << 20) #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222 (0x1 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1 (0x1 << 20) #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 20) #define AFI_FUSE 0x104 @@ -165,12 +175,21 @@ #define AFI_PEX_CTRL_RST (1 << 0) #define AFI_PEX_CTRL_CLKREQ_EN (1 << 1) #define AFI_PEX_CTRL_REFCLK_EN (1 << 3) +#define AFI_PEX_CTRL_OVERRIDE_EN (1 << 4) + +#define AFI_PLLE_CONTROL 0x160 +#define AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL (1 << 9) +#define AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN (1 << 1) #define AFI_PEXBIAS_CTRL_0 0x168 #define RP_VEND_XP 0x00000F00 #define RP_VEND_XP_DL_UP (1 << 30) +#define RP_PRIV_MISC 0x00000FE0 +#define RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xE << 0) +#define RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xF << 0) + #define RP_LINK_CONTROL_STATUS 0x00000090 #define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000 #define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000 @@ -197,6 +216,7 @@ #define PADS_REFCLK_CFG0 0x000000C8 #define PADS_REFCLK_CFG1 0x000000CC +#define PADS_REFCLK_BIAS 0x000000D0 /* * Fields in PADS_REFCLK_CFG*. Those registers form an array of 16-bit @@ -236,6 +256,7 @@ struct tegra_pcie_soc_data { bool has_pex_bias_ctrl; bool has_intr_prsnt_sense; bool has_cml_clk; + bool has_gen2; }; static inline struct tegra_msi *to_tegra_msi(struct msi_chip *chip) @@ -251,8 +272,10 @@ struct tegra_pcie { int irq; struct list_head buses; + struct list_head sys; struct resource *cs; + struct resource all; struct resource io; struct resource mem; struct resource prefetch; @@ -267,6 +290,8 @@ struct tegra_pcie { struct reset_control *afi_rst; struct reset_control *pcie_xrst; + struct phy *phy; + struct tegra_msi msi; struct list_head ports; @@ -382,7 +407,7 @@ static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie, for (i = 0; i < 16; i++) { unsigned long virt = (unsigned long)bus->area->addr + i * SZ_64K; - phys_addr_t phys = cs + i * SZ_1M + busnr * SZ_64K; + phys_addr_t phys = cs + i * SZ_16M + busnr * SZ_64K; err = ioremap_page_range(virt, virt + SZ_64K, phys, prot); if (err < 0) { @@ -561,6 +586,8 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port) if (soc->has_pex_clkreq_en) value |= AFI_PEX_CTRL_CLKREQ_EN; + value |= AFI_PEX_CTRL_OVERRIDE_EN; + afi_writel(port->pcie, value, ctrl); tegra_pcie_port_reset(port); @@ -568,6 +595,7 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port) static void tegra_pcie_port_disable(struct tegra_pcie_port *port) { + const struct tegra_pcie_soc_data *soc = port->pcie->soc_data; unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); unsigned long value; @@ -578,6 +606,10 @@ static void tegra_pcie_port_disable(struct tegra_pcie_port *port) /* disable reference clock */ value = afi_readl(port->pcie, ctrl); + + if (soc->has_pex_clkreq_en) + value &= ~AFI_PEX_CTRL_CLKREQ_EN; + value &= ~AFI_PEX_CTRL_REFCLK_EN; afi_writel(port->pcie, value, ctrl); } @@ -626,6 +658,15 @@ DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable); static int tegra_pcie_setup(int nr, struct pci_sys_data *sys) { struct tegra_pcie *pcie = sys_to_pcie(sys); + int err; + + err = devm_request_resource(pcie->dev, &pcie->all, &pcie->mem); + if (err < 0) + return err; + + err = devm_request_resource(pcie->dev, &pcie->all, &pcie->prefetch); + if (err) + return err; pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset); pci_add_resource_offset(&sys->resources, &pcie->prefetch, @@ -675,6 +716,11 @@ static struct pci_bus *tegra_pcie_scan_bus(int nr, struct pci_sys_data *sys) return bus; } +static void tegra_pcie_teardown(int nr, struct pci_sys_data *sys) +{ + pci_iounmap_io(nr * SZ_64K); +} + static irqreturn_t tegra_pcie_isr(int irq, void *arg) { const char *err_msg[] = { @@ -684,9 +730,15 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg) "Target abort", "Master abort", "Invalid write", + "Legacy interrupt", "Response decoding error", "AXI response decoding error", "Transaction timeout", + "Slot present pin change", + "Slot clock request change", + "TMS clock ramp change", + "TMS ready for power down", + "Peer2Peer error", }; struct tegra_pcie *pcie = arg; u32 code, signature; @@ -792,30 +844,27 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) afi_writel(pcie, 0, AFI_MSI_BAR_SZ); } -static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) +static int tegra_pcie_pll_wait(struct tegra_pcie *pcie, unsigned long timeout) { const struct tegra_pcie_soc_data *soc = pcie->soc_data; - struct tegra_pcie_port *port; - unsigned int timeout; - unsigned long value; - - /* power down PCIe slot clock bias pad */ - if (soc->has_pex_bias_ctrl) - afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0); + u32 value; - /* configure mode and disable all ports */ - value = afi_readl(pcie, AFI_PCIE_CONFIG); - value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK; - value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar_config; + timeout = jiffies + msecs_to_jiffies(timeout); - list_for_each_entry(port, &pcie->ports, list) - value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index); + while (time_before(jiffies, timeout)) { + value = pads_readl(pcie, soc->pads_pll_ctl); + if (value & PADS_PLL_CTL_LOCKDET) + return 0; + } - afi_writel(pcie, value, AFI_PCIE_CONFIG); + return -ETIMEDOUT; +} - value = afi_readl(pcie, AFI_FUSE); - value |= AFI_FUSE_PCIE_T0_GEN2_DIS; - afi_writel(pcie, value, AFI_FUSE); +static int tegra_pcie_phy_enable(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc_data *soc = pcie->soc_data; + u32 value; + int err; /* initialize internal PHY, enable up to 16 PCIE lanes */ pads_writel(pcie, 0x0, PADS_CTL_SEL); @@ -834,6 +883,13 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) value |= PADS_PLL_CTL_REFCLK_INTERNAL_CML | soc->tx_ref_sel; pads_writel(pcie, value, soc->pads_pll_ctl); + /* reset PLL */ + value = pads_readl(pcie, soc->pads_pll_ctl); + value &= ~PADS_PLL_CTL_RST_B4SM; + pads_writel(pcie, value, soc->pads_pll_ctl); + + usleep_range(20, 100); + /* take PLL out of reset */ value = pads_readl(pcie, soc->pads_pll_ctl); value |= PADS_PLL_CTL_RST_B4SM; @@ -846,15 +902,11 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) pads_writel(pcie, PADS_REFCLK_CFG_VALUE, PADS_REFCLK_CFG1); /* wait for the PLL to lock */ - timeout = 300; - do { - value = pads_readl(pcie, soc->pads_pll_ctl); - usleep_range(1000, 2000); - if (--timeout == 0) { - pr_err("Tegra PCIe error: timeout waiting for PLL\n"); - return -EBUSY; - } - } while (!(value & PADS_PLL_CTL_LOCKDET)); + err = tegra_pcie_pll_wait(pcie, 500); + if (err < 0) { + dev_err(pcie->dev, "PLL failed to lock: %d\n", err); + return err; + } /* turn off IDDQ override */ value = pads_readl(pcie, PADS_CTL); @@ -866,6 +918,58 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) value |= PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L; pads_writel(pcie, value, PADS_CTL); + return 0; +} + +static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc_data *soc = pcie->soc_data; + struct tegra_pcie_port *port; + unsigned long value; + int err; + + /* enable PLL power down */ + if (pcie->phy) { + value = afi_readl(pcie, AFI_PLLE_CONTROL); + value &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL; + value |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN; + afi_writel(pcie, value, AFI_PLLE_CONTROL); + } + + /* power down PCIe slot clock bias pad */ + if (soc->has_pex_bias_ctrl) + afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0); + + /* configure mode and disable all ports */ + value = afi_readl(pcie, AFI_PCIE_CONFIG); + value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK; + value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar_config; + + list_for_each_entry(port, &pcie->ports, list) + value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index); + + afi_writel(pcie, value, AFI_PCIE_CONFIG); + + if (soc->has_gen2) { + value = afi_readl(pcie, AFI_FUSE); + value &= ~AFI_FUSE_PCIE_T0_GEN2_DIS; + afi_writel(pcie, value, AFI_FUSE); + } else { + value = afi_readl(pcie, AFI_FUSE); + value |= AFI_FUSE_PCIE_T0_GEN2_DIS; + afi_writel(pcie, value, AFI_FUSE); + } + + if (!pcie->phy) + err = tegra_pcie_phy_enable(pcie); + else + err = phy_power_on(pcie->phy); + + if (err < 0) { + dev_err(pcie->dev, "failed to power on PHY: %d\n", err); + return err; + } + /* take the PCIe interface module out of reset */ reset_control_deassert(pcie->pcie_xrst); @@ -899,6 +1003,10 @@ static void tegra_pcie_power_off(struct tegra_pcie *pcie) /* TODO: disable and unprepare clocks? */ + err = phy_power_off(pcie->phy); + if (err < 0) + dev_warn(pcie->dev, "failed to power off PHY: %d\n", err); + reset_control_assert(pcie->pcie_xrst); reset_control_assert(pcie->afi_rst); reset_control_assert(pcie->pex_rst); @@ -1020,6 +1128,19 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) return err; } + pcie->phy = devm_phy_optional_get(pcie->dev, "pcie"); + if (IS_ERR(pcie->phy)) { + err = PTR_ERR(pcie->phy); + dev_err(&pdev->dev, "failed to get PHY: %d\n", err); + return err; + } + + err = phy_init(pcie->phy); + if (err < 0) { + dev_err(&pdev->dev, "failed to initialize PHY: %d\n", err); + return err; + } + err = tegra_pcie_power_on(pcie); if (err) { dev_err(&pdev->dev, "failed to power up: %d\n", err); @@ -1078,10 +1199,17 @@ poweroff: static int tegra_pcie_put_resources(struct tegra_pcie *pcie) { + int err; + if (pcie->irq > 0) free_irq(pcie->irq, pcie); tegra_pcie_power_off(pcie); + + err = phy_exit(pcie->phy); + if (err < 0) + dev_err(pcie->dev, "failed to teardown PHY: %d\n", err); + return 0; } @@ -1327,7 +1455,19 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes, { struct device_node *np = pcie->dev->of_node; - if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) { + if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) { + switch (lanes) { + case 0x0000104: + dev_info(pcie->dev, "4x1, 1x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1; + return 0; + + case 0x0000102: + dev_info(pcie->dev, "2x1, 1x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1; + return 0; + } + } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) { switch (lanes) { case 0x00000204: dev_info(pcie->dev, "4x1, 2x1 configuration\n"); @@ -1435,7 +1575,23 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask) struct device_node *np = pcie->dev->of_node; unsigned int i = 0; - if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) { + if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) { + pcie->num_supplies = 7; + + pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies, + sizeof(*pcie->supplies), + GFP_KERNEL); + if (!pcie->supplies) + return -ENOMEM; + + pcie->supplies[i++].supply = "avddio-pex"; + pcie->supplies[i++].supply = "vddio-pex"; + pcie->supplies[i++].supply = "avdd-pex-pll"; + pcie->supplies[i++].supply = "hvdd-pex"; + pcie->supplies[i++].supply = "hvdd-pex-plle"; + pcie->supplies[i++].supply = "vddio-pex-ctl"; + pcie->supplies[i++].supply = "avdd-plle"; + } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) { bool need_pexa = false, need_pexb = false; /* VDD_PEXA and AVDD_PEXA supply lanes 0 to 3 */ @@ -1514,6 +1670,12 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) struct resource res; int err; + memset(&pcie->all, 0, sizeof(pcie->all)); + pcie->all.flags = IORESOURCE_MEM; + pcie->all.name = np->full_name; + pcie->all.start = ~0; + pcie->all.end = 0; + if (of_pci_range_parser_init(&parser, np)) { dev_err(pcie->dev, "missing \"ranges\" property\n"); return -EINVAL; @@ -1525,21 +1687,31 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) switch (res.flags & IORESOURCE_TYPE_BITS) { case IORESOURCE_IO: memcpy(&pcie->io, &res, sizeof(res)); - pcie->io.name = "I/O"; + pcie->io.name = np->full_name; break; case IORESOURCE_MEM: if (res.flags & IORESOURCE_PREFETCH) { memcpy(&pcie->prefetch, &res, sizeof(res)); - pcie->prefetch.name = "PREFETCH"; + pcie->prefetch.name = "prefetchable"; } else { memcpy(&pcie->mem, &res, sizeof(res)); - pcie->mem.name = "MEM"; + pcie->mem.name = "non-prefetchable"; } break; } + + if (res.start <= pcie->all.start) + pcie->all.start = res.start; + + if (res.end >= pcie->all.end) + pcie->all.end = res.end; } + err = devm_request_resource(pcie->dev, &iomem_resource, &pcie->all); + if (err < 0) + return err; + err = of_pci_parse_bus_range(np, &pcie->busn); if (err < 0) { dev_err(pcie->dev, "failed to parse ranges property: %d\n", @@ -1641,6 +1813,12 @@ static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port) unsigned int retries = 3; unsigned long value; + /* override presence detection */ + value = readl(port->base + RP_PRIV_MISC); + value &= ~RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT; + value |= RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT; + writel(value, port->base + RP_PRIV_MISC); + do { unsigned int timeout = TEGRA_PCIE_LINKUP_TIMEOUT; @@ -1705,7 +1883,9 @@ static int tegra_pcie_enable(struct tegra_pcie *pcie) hw.map_irq = tegra_pcie_map_irq; hw.add_bus = tegra_pcie_add_bus; hw.scan = tegra_pcie_scan_bus; + hw.teardown = tegra_pcie_teardown; hw.ops = &tegra_pcie_ops; + hw.sys = &pcie->sys; pci_common_init_dev(pcie->dev, &hw); @@ -1721,6 +1901,7 @@ static const struct tegra_pcie_soc_data tegra20_pcie_data = { .has_pex_bias_ctrl = false, .has_intr_prsnt_sense = false, .has_cml_clk = false, + .has_gen2 = false, }; static const struct tegra_pcie_soc_data tegra30_pcie_data = { @@ -1732,9 +1913,23 @@ static const struct tegra_pcie_soc_data tegra30_pcie_data = { .has_pex_bias_ctrl = true, .has_intr_prsnt_sense = true, .has_cml_clk = true, + .has_gen2 = false, +}; + +static const struct tegra_pcie_soc_data tegra124_pcie_data = { + .num_ports = 2, + .msi_base_shift = 8, + .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, + .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, + .has_pex_clkreq_en = true, + .has_pex_bias_ctrl = true, + .has_intr_prsnt_sense = true, + .has_cml_clk = true, + .has_gen2 = true, }; static const struct of_device_id tegra_pcie_of_match[] = { + { .compatible = "nvidia,tegra124-pcie", .data = &tegra124_pcie_data }, { .compatible = "nvidia,tegra30-pcie", .data = &tegra30_pcie_data }, { .compatible = "nvidia,tegra20-pcie", .data = &tegra20_pcie_data }, { }, @@ -1850,6 +2045,11 @@ remove: return -ENOMEM; } +static void tegra_pcie_debugfs_exit(struct tegra_pcie *pcie) +{ + debugfs_remove_recursive(pcie->debugfs); +} + static int tegra_pcie_probe(struct platform_device *pdev) { const struct of_device_id *match; @@ -1866,6 +2066,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) INIT_LIST_HEAD(&pcie->buses); INIT_LIST_HEAD(&pcie->ports); + INIT_LIST_HEAD(&pcie->sys); pcie->soc_data = match->data; pcie->dev = &pdev->dev; @@ -1922,14 +2123,43 @@ put_resources: return err; } +static int tegra_pcie_remove(struct platform_device *pdev) +{ + struct tegra_pcie *pcie = platform_get_drvdata(pdev); + struct tegra_pcie_bus *bus, *tmp; + int err; + + if (IS_ENABLED(CONFIG_DEBUG_FS)) + tegra_pcie_debugfs_exit(pcie); + + pci_common_exit(&pcie->sys); + + list_for_each_entry_safe(bus, tmp, &pcie->buses, list) { + vunmap(bus->area->addr); + kfree(bus); + } + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + err = tegra_pcie_disable_msi(pcie); + if (err < 0) + return err; + } + + err = tegra_pcie_put_resources(pcie); + if (err < 0) + return err; + + return 0; +} + static struct platform_driver tegra_pcie_driver = { .driver = { .name = "tegra-pcie", .owner = THIS_MODULE, .of_match_table = tegra_pcie_of_match, - .suppress_bind_attrs = true, }, .probe = tegra_pcie_probe, + .remove = tegra_pcie_remove, }; module_platform_driver(tegra_pcie_driver); diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 5a40516444f3..3424d6c8bb5b 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -244,11 +244,13 @@ void mask_msi_irq(struct irq_data *data) { msi_set_mask_bit(data, 1); } +EXPORT_SYMBOL(mask_msi_irq); void unmask_msi_irq(struct irq_data *data) { msi_set_mask_bit(data, 0); } +EXPORT_SYMBOL(unmask_msi_irq); void default_restore_msi_irqs(struct pci_dev *dev) { @@ -356,6 +358,7 @@ void write_msi_msg(unsigned int irq, struct msi_msg *msg) __write_msi_msg(entry, msg); } +EXPORT_SYMBOL(write_msi_msg); static void free_msi_irqs(struct pci_dev *dev) { diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index e3cf8a2e6292..7a9511c73371 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1852,6 +1852,7 @@ err_out: kfree(b); return NULL; } +EXPORT_SYMBOL(pci_create_root_bus); int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max) { diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 80c2d014283d..e6f39ad00c0c 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2312,6 +2312,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V4, nvbridge_check_legacy_irq_routing); +#ifndef CONFIG_ARCH_TEGRA static int ht_check_msi_mapping(struct pci_dev *dev) { int pos, ttl = 48; @@ -2522,6 +2523,7 @@ DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, nv_msi_ht_cap_q DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_ANY_ID, nv_msi_ht_cap_quirk_all); DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AL, PCI_ANY_ID, nv_msi_ht_cap_quirk_all); +#endif static void quirk_msi_intx_disable_bug(struct pci_dev *dev) { diff --git a/drivers/tty/serial/sunzilog.h b/drivers/tty/serial/sunzilog.h index 5dec7b47cc38..de2850c1ab57 100644 --- a/drivers/tty/serial/sunzilog.h +++ b/drivers/tty/serial/sunzilog.h @@ -2,10 +2,10 @@ #define _SUNZILOG_H struct zilog_channel { - volatile unsigned char control; - volatile unsigned char __pad1; - volatile unsigned char data; - volatile unsigned char __pad2; + unsigned char control; + unsigned char __pad1; + unsigned char data; + unsigned char __pad2; }; struct zilog_layout { diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index d7a3d13e72ec..1a8044567fd8 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -142,6 +142,7 @@ static int pwm_backlight_parse_dt(struct device *dev, return -ENODEV; memset(data, 0, sizeof(*data)); + data->pwm_id = -1; /* determine the number of brightness levels */ prop = of_find_property(node, "brightness-levels", &length); @@ -173,6 +174,8 @@ static int pwm_backlight_parse_dt(struct device *dev, data->max_brightness--; } + data->boot_off = of_property_read_bool(node, "backlight-boot-off"); + return 0; } @@ -317,6 +320,12 @@ static int pwm_backlight_probe(struct platform_device *pdev) } bl->props.brightness = data->dft_brightness; + + if (data->boot_off) + bl->props.power = FB_BLANK_POWERDOWN; + else + bl->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bl); platform_set_drvdata(pdev, bl); diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h index 975e1cc75edb..508a57257de5 100644 --- a/include/asm-generic/io.h +++ b/include/asm-generic/io.h @@ -24,250 +24,622 @@ #define mmiowb() do {} while (0) #endif -/*****************************************************************************/ /* - * readX/writeX() are used to access memory mapped devices. On some - * architectures the memory mapped IO stuff needs to be accessed - * differently. On the simple architectures, we just read/write the - * memory location directly. + * __raw_{read,write}{b,w,l,q}() access memory in native endianness. + * + * On some architectures memory mapped IO needs to be accessed differently. + * On the simple architectures, we just read/write the memory location + * directly. */ + #ifndef __raw_readb +#define __raw_readb __raw_readb static inline u8 __raw_readb(const volatile void __iomem *addr) { - return *(const volatile u8 __force *) addr; + return *(const volatile u8 __force *)addr; } #endif #ifndef __raw_readw +#define __raw_readw __raw_readw static inline u16 __raw_readw(const volatile void __iomem *addr) { - return *(const volatile u16 __force *) addr; + return *(const volatile u16 __force *)addr; } #endif #ifndef __raw_readl +#define __raw_readl __raw_readl static inline u32 __raw_readl(const volatile void __iomem *addr) { - return *(const volatile u32 __force *) addr; + return *(const volatile u32 __force *)addr; } #endif -#define readb __raw_readb - -#define readw readw -static inline u16 readw(const volatile void __iomem *addr) -{ - return __le16_to_cpu(__raw_readw(addr)); -} - -#define readl readl -static inline u32 readl(const volatile void __iomem *addr) +#ifdef CONFIG_64BIT +#ifndef __raw_readq +#define __raw_readq __raw_readq +static inline u64 __raw_readq(const volatile void __iomem *addr) { - return __le32_to_cpu(__raw_readl(addr)); + return *(const volatile u64 __force *)addr; } +#endif +#endif /* CONFIG_64BIT */ #ifndef __raw_writeb -static inline void __raw_writeb(u8 b, volatile void __iomem *addr) +#define __raw_writeb __raw_writeb +static inline void __raw_writeb(u8 value, volatile void __iomem *addr) { - *(volatile u8 __force *) addr = b; + *(volatile u8 __force *)addr = value; } #endif #ifndef __raw_writew -static inline void __raw_writew(u16 b, volatile void __iomem *addr) +#define __raw_writew __raw_writew +static inline void __raw_writew(u16 value, volatile void __iomem *addr) { - *(volatile u16 __force *) addr = b; + *(volatile u16 __force *)addr = value; } #endif #ifndef __raw_writel -static inline void __raw_writel(u32 b, volatile void __iomem *addr) +#define __raw_writel __raw_writel +static inline void __raw_writel(u32 value, volatile void __iomem *addr) { - *(volatile u32 __force *) addr = b; + *(volatile u32 __force *)addr = value; } #endif -#define writeb __raw_writeb -#define writew(b,addr) __raw_writew(__cpu_to_le16(b),addr) -#define writel(b,addr) __raw_writel(__cpu_to_le32(b),addr) - #ifdef CONFIG_64BIT -#ifndef __raw_readq -static inline u64 __raw_readq(const volatile void __iomem *addr) +#ifndef __raw_writeq +#define __raw_writeq __raw_writeq +static inline void __raw_writeq(u64 value, volatile void __iomem *addr) { - return *(const volatile u64 __force *) addr; + *(volatile u64 __force *)addr = value; } #endif +#endif /* CONFIG_64BIT */ -#define readq readq -static inline u64 readq(const volatile void __iomem *addr) -{ - return __le64_to_cpu(__raw_readq(addr)); -} +/* + * {read,write}{b,w,l,q}() access little endian memory and return result in + * native endianness. + */ -#ifndef __raw_writeq -static inline void __raw_writeq(u64 b, volatile void __iomem *addr) +#ifndef readb +#define readb readb +static inline u8 readb(const void __iomem *addr) { - *(volatile u64 __force *) addr = b; + return __raw_readb(addr); } #endif -#define writeq(b, addr) __raw_writeq(__cpu_to_le64(b), addr) -#endif /* CONFIG_64BIT */ - -#ifndef PCI_IOBASE -#define PCI_IOBASE ((void __iomem *) 0) +#ifndef readw +#define readw readw +static inline u16 readw(const void __iomem *addr) +{ + return __le16_to_cpu(__raw_readw(addr)); +} #endif -/*****************************************************************************/ -/* - * traditional input/output functions - */ - -static inline u8 inb(unsigned long addr) +#ifndef readl +#define readl readl +static inline u32 readl(const void __iomem *addr) { - return readb(addr + PCI_IOBASE); + return __le32_to_cpu(__raw_readl(addr)); } +#endif -static inline u16 inw(unsigned long addr) +#ifdef CONFIG_64BIT +#ifndef readq +#define readq readq +static inline u64 readq(const void __iomem *addr) { - return readw(addr + PCI_IOBASE); + return __le64_to_cpu(__raw_readq(addr)); } +#endif +#endif /* CONFIG_64BIT */ -static inline u32 inl(unsigned long addr) +#ifndef writeb +#define writeb writeb +static inline void writeb(u8 value, void __iomem *addr) { - return readl(addr + PCI_IOBASE); + __raw_writeb(value, addr); } +#endif -static inline void outb(u8 b, unsigned long addr) +#ifndef writew +#define writew writew +static inline void writew(u16 value, void __iomem *addr) { - writeb(b, addr + PCI_IOBASE); + __raw_writew(cpu_to_le16(value), addr); } +#endif -static inline void outw(u16 b, unsigned long addr) +#ifndef writel +#define writel writel +static inline void writel(u32 value, void __iomem *addr) { - writew(b, addr + PCI_IOBASE); + __raw_writel(__cpu_to_le32(value), addr); } +#endif -static inline void outl(u32 b, unsigned long addr) +#ifdef CONFIG_64BIT +#ifndef writeq +#define writeq writeq +static inline void writeq(u64 value, void __iomem *addr) { - writel(b, addr + PCI_IOBASE); + __raw_writeq(__cpu_to_le64(value), addr); } +#endif +#endif /* CONFIG_64BIT */ -#define inb_p(addr) inb(addr) -#define inw_p(addr) inw(addr) -#define inl_p(addr) inl(addr) -#define outb_p(x, addr) outb((x), (addr)) -#define outw_p(x, addr) outw((x), (addr)) -#define outl_p(x, addr) outl((x), (addr)) - -#ifndef insb -static inline void insb(unsigned long addr, void *buffer, int count) +/* + * {read,write}s{b,w,l,q}() repeatedly access the same memory address in + * native endianness in 8-, 16-, 32- or 64-bit chunks (@count times). + */ +#ifndef readsb +#define readsb readsb +static inline void readsb(const void __iomem *addr, void *buffer, + unsigned int count) { if (count) { u8 *buf = buffer; + do { - u8 x = __raw_readb(addr + PCI_IOBASE); + u8 x = __raw_readb(addr); *buf++ = x; } while (--count); } } #endif -#ifndef insw -static inline void insw(unsigned long addr, void *buffer, int count) +#ifndef readsw +#define readsw readsw +static inline void readsw(const void __iomem *addr, void *buffer, + unsigned int count) { if (count) { u16 *buf = buffer; + do { - u16 x = __raw_readw(addr + PCI_IOBASE); + u16 x = __raw_readw(addr); *buf++ = x; } while (--count); } } #endif -#ifndef insl -static inline void insl(unsigned long addr, void *buffer, int count) +#ifndef readsl +#define readsl readsl +static inline void readsl(const void __iomem *addr, void *buffer, + unsigned int count) { if (count) { u32 *buf = buffer; + do { - u32 x = __raw_readl(addr + PCI_IOBASE); + u32 x = __raw_readl(addr); *buf++ = x; } while (--count); } } #endif -#ifndef outsb -static inline void outsb(unsigned long addr, const void *buffer, int count) +#ifdef CONFIG_64BIT +#ifndef readsq +#define readsq readsq +static inline void readsq(const void __iomem *addr, void *buffer, + unsigned int count) +{ + if (count) { + u64 *buf = buffer; + + do { + u64 x = __raw_readq(addr); + *buf++ = x; + } while (--count); + } +} +#endif +#endif /* CONFIG_64BIT */ + +#ifndef writesb +#define writesb writesb +static inline void writesb(void __iomem *addr, const void *buffer, + unsigned int count) { if (count) { const u8 *buf = buffer; + do { - __raw_writeb(*buf++, addr + PCI_IOBASE); + __raw_writeb(*buf++, addr); } while (--count); } } #endif -#ifndef outsw -static inline void outsw(unsigned long addr, const void *buffer, int count) +#ifndef writesw +#define writesw writesw +static inline void writesw(void __iomem *addr, const void *buffer, + unsigned int count) { if (count) { const u16 *buf = buffer; + do { - __raw_writew(*buf++, addr + PCI_IOBASE); + __raw_writew(*buf++, addr); } while (--count); } } #endif -#ifndef outsl -static inline void outsl(unsigned long addr, const void *buffer, int count) +#ifndef writesl +#define writesl writesl +static inline void writesl(void __iomem *addr, const void *buffer, + unsigned int count) { if (count) { const u32 *buf = buffer; + do { - __raw_writel(*buf++, addr + PCI_IOBASE); + __raw_writel(*buf++, addr); } while (--count); } } #endif -#ifndef CONFIG_GENERIC_IOMAP -#define ioread8(addr) readb(addr) -#define ioread16(addr) readw(addr) -#define ioread16be(addr) __be16_to_cpu(__raw_readw(addr)) -#define ioread32(addr) readl(addr) -#define ioread32be(addr) __be32_to_cpu(__raw_readl(addr)) - -#define iowrite8(v, addr) writeb((v), (addr)) -#define iowrite16(v, addr) writew((v), (addr)) -#define iowrite16be(v, addr) __raw_writew(__cpu_to_be16(v), addr) -#define iowrite32(v, addr) writel((v), (addr)) -#define iowrite32be(v, addr) __raw_writel(__cpu_to_be32(v), addr) - -#define ioread8_rep(p, dst, count) \ - insb((unsigned long) (p), (dst), (count)) -#define ioread16_rep(p, dst, count) \ - insw((unsigned long) (p), (dst), (count)) -#define ioread32_rep(p, dst, count) \ - insl((unsigned long) (p), (dst), (count)) - -#define iowrite8_rep(p, src, count) \ - outsb((unsigned long) (p), (src), (count)) -#define iowrite16_rep(p, src, count) \ - outsw((unsigned long) (p), (src), (count)) -#define iowrite32_rep(p, src, count) \ - outsl((unsigned long) (p), (src), (count)) -#endif /* CONFIG_GENERIC_IOMAP */ +#ifdef CONFIG_64BIT +#ifndef writesq +#define writesq writesq +static inline void writesq(void __iomem *addr, const void *buffer, + unsigned int count) +{ + if (count) { + const u64 *buf = buffer; + + do { + __raw_writeq(*buf++, addr); + } while (--count); + } +} +#endif +#endif /* CONFIG_64BIT */ + +#ifndef PCI_IOBASE +#define PCI_IOBASE ((void __iomem *)0) +#endif #ifndef IO_SPACE_LIMIT #define IO_SPACE_LIMIT 0xffff #endif +/* + * {in,out}{b,w,l}() access little endian I/O. {in,out}{b,w,l}_p() can be + * implemented on hardware that needs an additional delay for I/O accesses to + * take effect. + */ + +#ifndef inb +#define inb inb +static inline u8 inb(unsigned long addr) +{ + return readb(PCI_IOBASE + addr); +} +#endif + +#ifndef inw +#define inw inw +static inline u16 inw(unsigned long addr) +{ + return readw(PCI_IOBASE + addr); +} +#endif + +#ifndef inl +#define inl inl +static inline u32 inl(unsigned long addr) +{ + return readl(PCI_IOBASE + addr); +} +#endif + +#ifndef outb +#define outb outb +static inline void outb(u8 value, unsigned long addr) +{ + writeb(value, PCI_IOBASE + addr); +} +#endif + +#ifndef outw +#define outw outw +static inline void outw(u16 value, unsigned long addr) +{ + writew(value, PCI_IOBASE + addr); +} +#endif + +#ifndef outl +#define outl outl +static inline void outl(u32 value, unsigned long addr) +{ + writel(value, PCI_IOBASE + addr); +} +#endif + +#ifndef inb_p +#define inb_p inb_p +static inline u8 inb_p(unsigned long addr) +{ + return inb(addr); +} +#endif + +#ifndef inw_p +#define inw_p inw_p +static inline u16 inw_p(unsigned long addr) +{ + return inw(addr); +} +#endif + +#ifndef inl_p +#define inl_p inl_p +static inline u32 inl_p(unsigned long addr) +{ + return inl(addr); +} +#endif + +#ifndef outb_p +#define outb_p outb_p +static inline void outb_p(u8 value, unsigned long addr) +{ + outb(value, addr); +} +#endif + +#ifndef outw_p +#define outw_p outw_p +static inline void outw_p(u16 value, unsigned long addr) +{ + outw(value, addr); +} +#endif + +#ifndef outl_p +#define outl_p outl_p +static inline void outl_p(u32 value, unsigned long addr) +{ + outl(value, addr); +} +#endif + +/* + * {in,out}s{b,w,l}{,_p}() are variants of the above that repeatedly access a + * single I/O port multiple times. + */ + +#ifndef insb +#define insb insb +static inline void insb(unsigned long addr, void *buffer, unsigned int count) +{ + readsb(PCI_IOBASE + addr, buffer, count); +} +#endif + +#ifndef insw +#define insw insw +static inline void insw(unsigned long addr, void *buffer, unsigned int count) +{ + readsw(PCI_IOBASE + addr, buffer, count); +} +#endif + +#ifndef insl +#define insl insl +static inline void insl(unsigned long addr, void *buffer, unsigned int count) +{ + readsl(PCI_IOBASE + addr, buffer, count); +} +#endif + +#ifndef outsb +#define outsb outsb +static inline void outsb(unsigned long addr, const void *buffer, + unsigned int count) +{ + writesb(PCI_IOBASE + addr, buffer, count); +} +#endif + +#ifndef outsw +#define outsw outsw +static inline void outsw(unsigned long addr, const void *buffer, + unsigned int count) +{ + writesw(PCI_IOBASE + addr, buffer, count); +} +#endif + +#ifndef outsl +#define outsl outsl +static inline void outsl(unsigned long addr, const void *buffer, + unsigned int count) +{ + writesl(PCI_IOBASE + addr, buffer, count); +} +#endif + +#ifndef insb_p +#define insb_p insb_p +static inline void insb_p(unsigned long addr, void *buffer, unsigned int count) +{ + insb(addr, buffer, count); +} +#endif + +#ifndef insw_p +#define insw_p insw_p +static inline void insw_p(unsigned long addr, void *buffer, unsigned int count) +{ + insw(addr, buffer, count); +} +#endif + +#ifndef insl_p +#define insl_p insl_p +static inline void insl_p(unsigned long addr, void *buffer, unsigned int count) +{ + insl(addr, buffer, count); +} +#endif + +#ifndef outsb_p +#define outsb_p outsb_p +static inline void outsb_p(unsigned long addr, const void *buffer, + unsigned int count) +{ + outsb(addr, buffer, count); +} +#endif + +#ifndef outsw_p +#define outsw_p outsw_p +static inline void outsw_p(unsigned long addr, const void *buffer, + unsigned int count) +{ + outsw(addr, buffer, count); +} +#endif + +#ifndef outsl_p +#define outsl_p outsl_p +static inline void outsl_p(unsigned long addr, const void *buffer, + unsigned int count) +{ + outsl(addr, buffer, count); +} +#endif + +#ifndef CONFIG_GENERIC_IOMAP +static inline u8 ioread8(const void __iomem *addr) +{ + return readb(addr); +} + +static inline u16 ioread16(const void __iomem *addr) +{ + return readw(addr); +} + +static inline u32 ioread32(const void __iomem *addr) +{ + return readl(addr); +} + +static inline void iowrite8(u8 value, void __iomem *addr) +{ + writeb(value, addr); +} + +static inline void iowrite16(u16 value, void __iomem *addr) +{ + writew(value, addr); +} + +static inline void iowrite32(u32 value, void __iomem *addr) +{ + writel(value, addr); +} + +#ifndef ioread16be +#define ioread16be ioread16be +static inline u16 ioread16be(const void __iomem *addr) +{ + return __be16_to_cpu(__raw_readw(addr)); +} +#endif + +#ifndef ioread32be +#define ioread32be ioread32be +static inline u32 ioread32be(const void __iomem *addr) +{ + return __be32_to_cpu(__raw_readl(addr)); +} +#endif + +#ifndef iowrite16be +#define iowrite16be iowrite16be +static inline void iowrite16be(u16 value, void __iomem *addr) +{ + __raw_writew(__cpu_to_be16(value), addr); +} +#endif + +#ifndef iowrite32be +#define iowrite32be iowrite32be +static inline void iowrite32be(u32 value, void __iomem *addr) +{ + __raw_writel(__cpu_to_be32(value), addr); +} +#endif + +#ifndef ioread8_rep +#define ioread8_rep ioread8_rep +static inline void ioread8_rep(const void __iomem *addr, void *buffer, + unsigned int count) +{ + readsb(addr, buffer, count); +} +#endif + +#ifndef ioread16_rep +#define ioread16_rep ioread16_rep +static inline void ioread16_rep(const void __iomem *addr, void *buffer, + unsigned int count) +{ + readsw(addr, buffer, count); +} +#endif + +#ifndef ioread32_rep +#define ioread32_rep ioread32_rep +static inline void ioread32_rep(const void __iomem *addr, void *buffer, + unsigned int count) +{ + readsl(addr, buffer, count); +} +#endif + +#ifndef iowrite8_rep +#define iowrite8_rep iowrite8_rep +static inline void iowrite8_rep(void __iomem *addr, const void *buffer, + unsigned int count) +{ + writesb(addr, buffer, count); +} +#endif + +#ifndef iowrite16_rep +#define iowrite16_rep iowrite16_rep +static inline void iowrite16_rep(void __iomem *addr, const void *buffer, + unsigned int count) +{ + writesw(addr, buffer, count); +} +#endif + +#ifndef iowrite32_rep +#define iowrite32_rep iowrite32_rep +static inline void iowrite32_rep(void __iomem *addr, const void *buffer, + unsigned int count) +{ + writesl(addr, buffer, count); +} +#endif +#endif /* CONFIG_GENERIC_IOMAP */ + #ifdef __KERNEL__ #include <linux/vmalloc.h> @@ -278,6 +650,7 @@ struct pci_dev; extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); #ifndef pci_iounmap +#define pci_iounmap pci_iounmap static inline void pci_iounmap(struct pci_dev *dev, void __iomem *p) { } @@ -289,11 +662,15 @@ static inline void pci_iounmap(struct pci_dev *dev, void __iomem *p) * These are pretty trivial */ #ifndef virt_to_phys +#define virt_to_phys virt_to_phys static inline unsigned long virt_to_phys(volatile void *address) { return __pa((unsigned long)address); } +#endif +#ifndef phys_to_virt +#define phys_to_virt phys_to_virt static inline void *phys_to_virt(unsigned long address) { return __va(address); @@ -306,20 +683,33 @@ static inline void *phys_to_virt(unsigned long address) * This implementation is for the no-MMU case only... if you have an MMU * you'll need to provide your own definitions. */ + #ifndef CONFIG_MMU -static inline void __iomem *ioremap(phys_addr_t offset, unsigned long size) +static inline void __iomem *ioremap(phys_addr_t offset, size_t size) { - return (void __iomem*) (unsigned long)offset; + return (void __iomem *)(unsigned long)offset; } -#define __ioremap(offset, size, flags) ioremap(offset, size) +static inline void __iomem *__ioremap(phys_addr_t offset, size_t size, + unsigned long flags) +{ + return ioremap(offset, size); +} #ifndef ioremap_nocache -#define ioremap_nocache ioremap +#define ioremap_nocache ioremap_nocache +static inline void __iomem *ioremap_nocache(phys_addr_t offset, size_t size) +{ + return ioremap(offset, size); +} #endif #ifndef ioremap_wc -#define ioremap_wc ioremap_nocache +#define ioremap_wc ioremap_wc +static inline void __iomem *ioremap_wc(phys_addr_t offset, size_t size) +{ + return ioremap_nocache(offset, size); +} #endif static inline void iounmap(void __iomem *addr) @@ -329,14 +719,20 @@ static inline void iounmap(void __iomem *addr) #ifdef CONFIG_HAS_IOPORT_MAP #ifndef CONFIG_GENERIC_IOMAP +#ifndef ioport_map +#define ioport_map ioport_map static inline void __iomem *ioport_map(unsigned long port, unsigned int nr) { - return (void __iomem *) port; + return PCI_IOBASE + (port & IO_SPACE_LIMIT); } +#endif +#ifndef ioport_unmap +#define ioport_unmap ioport_unmap static inline void ioport_unmap(void __iomem *p) { } +#endif #else /* CONFIG_GENERIC_IOMAP */ extern void __iomem *ioport_map(unsigned long port, unsigned int nr); extern void ioport_unmap(void __iomem *p); @@ -344,35 +740,66 @@ extern void ioport_unmap(void __iomem *p); #endif /* CONFIG_HAS_IOPORT_MAP */ #ifndef xlate_dev_kmem_ptr -#define xlate_dev_kmem_ptr(p) p +#define xlate_dev_kmem_ptr xlate_dev_kmem_ptr +static inline void *xlate_dev_kmem_ptr(void *addr) +{ + return addr; +} #endif + #ifndef xlate_dev_mem_ptr -#define xlate_dev_mem_ptr(p) __va(p) +#define xlate_dev_mem_ptr xlate_dev_mem_ptr +static inline void *xlate_dev_mem_ptr(phys_addr_t addr) +{ + return __va(addr); +} +#endif + +#ifndef unxlate_dev_mem_ptr +#define unxlate_dev_mem_ptr unxlate_dev_mem_ptr +static inline void unxlate_dev_mem_ptr(phys_addr_t phys, void *addr) +{ +} #endif #ifdef CONFIG_VIRT_TO_BUS #ifndef virt_to_bus -static inline unsigned long virt_to_bus(volatile void *address) +static inline unsigned long virt_to_bus(void *address) { - return ((unsigned long) address); + return (unsigned long)address; } static inline void *bus_to_virt(unsigned long address) { - return (void *) address; + return (void *)address; } #endif #endif #ifndef memset_io -#define memset_io(a, b, c) memset(__io_virt(a), (b), (c)) +#define memset_io memset_io +static inline void memset_io(void __iomem *addr, int value, size_t size) +{ + memset(__io_virt(addr), value, size); +} #endif #ifndef memcpy_fromio -#define memcpy_fromio(a, b, c) memcpy((a), __io_virt(b), (c)) +#define memcpy_fromio memcpy_fromio +static inline void memcpy_fromio(void *buffer, const void __iomem *addr, + size_t size) +{ + memcpy(buffer, __io_virt(addr), size); +} #endif + #ifndef memcpy_toio -#define memcpy_toio(a, b, c) memcpy(__io_virt(a), (b), (c)) +#define memcpy_toio memcpy_toio +static inline void memcpy_toio(void __iomem *addr, const void *buffer, + size_t size) +{ + memcpy(__io_virt(addr), buffer, size); +} #endif #endif /* __KERNEL__ */ diff --git a/include/dt-bindings/memory/tegra114-mc.h b/include/dt-bindings/memory/tegra114-mc.h new file mode 100644 index 000000000000..8f48985a3139 --- /dev/null +++ b/include/dt-bindings/memory/tegra114-mc.h @@ -0,0 +1,25 @@ +#ifndef DT_BINDINGS_MEMORY_TEGRA114_MC_H +#define DT_BINDINGS_MEMORY_TEGRA114_MC_H + +#define TEGRA_SWGROUP_PTC 0 +#define TEGRA_SWGROUP_DC 1 +#define TEGRA_SWGROUP_DCB 2 +#define TEGRA_SWGROUP_EPP 3 +#define TEGRA_SWGROUP_G2 4 +#define TEGRA_SWGROUP_AVPC 5 +#define TEGRA_SWGROUP_NV 6 +#define TEGRA_SWGROUP_HDA 7 +#define TEGRA_SWGROUP_HC 8 +#define TEGRA_SWGROUP_MSENC 9 +#define TEGRA_SWGROUP_PPCS 10 +#define TEGRA_SWGROUP_VDE 11 +#define TEGRA_SWGROUP_MPCORELP 12 +#define TEGRA_SWGROUP_MPCORE 13 +#define TEGRA_SWGROUP_VI 14 +#define TEGRA_SWGROUP_ISP 15 +#define TEGRA_SWGROUP_XUSB_HOST 16 +#define TEGRA_SWGROUP_XUSB_DEV 17 +#define TEGRA_SWGROUP_EMUCIF 18 +#define TEGRA_SWGROUP_TSEC 19 + +#endif diff --git a/include/dt-bindings/memory/tegra124-mc.h b/include/dt-bindings/memory/tegra124-mc.h new file mode 100644 index 000000000000..7d8ee798f34e --- /dev/null +++ b/include/dt-bindings/memory/tegra124-mc.h @@ -0,0 +1,31 @@ +#ifndef DT_BINDINGS_MEMORY_TEGRA124_MC_H +#define DT_BINDINGS_MEMORY_TEGRA124_MC_H + +#define TEGRA_SWGROUP_PTC 0 +#define TEGRA_SWGROUP_DC 1 +#define TEGRA_SWGROUP_DCB 2 +#define TEGRA_SWGROUP_AFI 3 +#define TEGRA_SWGROUP_AVPC 4 +#define TEGRA_SWGROUP_HDA 5 +#define TEGRA_SWGROUP_HC 6 +#define TEGRA_SWGROUP_MSENC 7 +#define TEGRA_SWGROUP_PPCS 8 +#define TEGRA_SWGROUP_SATA 9 +#define TEGRA_SWGROUP_VDE 10 +#define TEGRA_SWGROUP_MPCORELP 11 +#define TEGRA_SWGROUP_MPCORE 12 +#define TEGRA_SWGROUP_ISP2 13 +#define TEGRA_SWGROUP_XUSB_HOST 14 +#define TEGRA_SWGROUP_XUSB_DEV 15 +#define TEGRA_SWGROUP_ISP2B 16 +#define TEGRA_SWGROUP_TSEC 17 +#define TEGRA_SWGROUP_A9AVP 18 +#define TEGRA_SWGROUP_GPU 19 +#define TEGRA_SWGROUP_SDMMC1A 20 +#define TEGRA_SWGROUP_SDMMC2A 21 +#define TEGRA_SWGROUP_SDMMC3A 22 +#define TEGRA_SWGROUP_SDMMC4A 23 +#define TEGRA_SWGROUP_VIC 24 +#define TEGRA_SWGROUP_VI 25 + +#endif diff --git a/include/dt-bindings/memory/tegra30-mc.h b/include/dt-bindings/memory/tegra30-mc.h new file mode 100644 index 000000000000..502beb03d777 --- /dev/null +++ b/include/dt-bindings/memory/tegra30-mc.h @@ -0,0 +1,24 @@ +#ifndef DT_BINDINGS_MEMORY_TEGRA30_MC_H +#define DT_BINDINGS_MEMORY_TEGRA30_MC_H + +#define TEGRA_SWGROUP_PTC 0 +#define TEGRA_SWGROUP_DC 1 +#define TEGRA_SWGROUP_DCB 2 +#define TEGRA_SWGROUP_EPP 3 +#define TEGRA_SWGROUP_G2 4 +#define TEGRA_SWGROUP_MPE 5 +#define TEGRA_SWGROUP_VI 6 +#define TEGRA_SWGROUP_AFI 7 +#define TEGRA_SWGROUP_AVPC 8 +#define TEGRA_SWGROUP_NV 9 +#define TEGRA_SWGROUP_NV2 10 +#define TEGRA_SWGROUP_HDA 11 +#define TEGRA_SWGROUP_HC 12 +#define TEGRA_SWGROUP_PPCS 13 +#define TEGRA_SWGROUP_SATA 14 +#define TEGRA_SWGROUP_VDE 15 +#define TEGRA_SWGROUP_MPCORELP 16 +#define TEGRA_SWGROUP_MPCORE 17 +#define TEGRA_SWGROUP_ISP 18 + +#endif diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 20f9a527922a..7118b0c9b1ad 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -43,6 +43,17 @@ struct notifier_block; typedef int (*iommu_fault_handler_t)(struct iommu_domain *, struct device *, unsigned long, int, void *); +struct iommu { + struct device *dev; + + struct list_head list; + + const struct iommu_ops *ops; +}; + +int iommu_add(struct iommu *iommu); +void iommu_remove(struct iommu *iommu); + struct iommu_domain_geometry { dma_addr_t aperture_start; /* First address that can be mapped */ dma_addr_t aperture_end; /* Last address that can be mapped */ @@ -83,8 +94,6 @@ enum iommu_attr { DOMAIN_ATTR_MAX, }; -#ifdef CONFIG_IOMMU_API - /** * struct iommu_ops - iommu ops and capabilities * @domain_init: init iommu domain @@ -130,6 +139,9 @@ struct iommu_ops { /* Get the numer of window per domain */ u32 (*domain_get_windows)(struct iommu_domain *domain); + int (*attach)(struct iommu *iommu, struct device *dev); + int (*detach)(struct iommu *iommu, struct device *dev); + unsigned long pgsize_bitmap; }; @@ -140,6 +152,8 @@ struct iommu_ops { #define IOMMU_GROUP_NOTIFY_UNBIND_DRIVER 5 /* Pre Driver unbind */ #define IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER 6 /* Post Driver unbind */ +#ifdef CONFIG_IOMMU_API + extern int bus_set_iommu(struct bus_type *bus, const struct iommu_ops *ops); extern bool iommu_present(struct bus_type *bus); extern struct iommu_domain *iommu_domain_alloc(struct bus_type *bus); @@ -199,6 +213,10 @@ extern int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, phys_addr_t offset, u64 size, int prot); extern void iommu_domain_window_disable(struct iommu_domain *domain, u32 wnd_nr); + +int iommu_attach(struct device *dev); +int iommu_detach(struct device *dev); + /** * report_iommu_fault() - report about an IOMMU fault to the IOMMU framework * @domain: the iommu domain where the fault has happened @@ -242,8 +260,11 @@ static inline int report_iommu_fault(struct iommu_domain *domain, #else /* CONFIG_IOMMU_API */ -struct iommu_ops {}; -struct iommu_group {}; +static inline int bus_set_iommu(struct bus_type *bus, + const struct iommu_ops *ops) +{ + return -ENOSYS; +} static inline bool iommu_present(struct bus_type *bus) { @@ -424,6 +445,15 @@ static inline void iommu_device_unlink(struct device *dev, struct device *link) { } +static inline int iommu_attach(struct device *dev) +{ + return 0; +} + +static inline int iommu_detach(struct device *dev) +{ + return 0; +} #endif /* CONFIG_IOMMU_API */ #endif /* __LINUX_IOMMU_H */ diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 142ec544167c..2c5250222278 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -215,6 +215,11 @@ static inline int __deprecated check_region(resource_size_t s, /* Wrappers for managed devices */ struct device; + +extern int devm_request_resource(struct device *dev, struct resource *root, + struct resource *new); +extern void devm_release_resource(struct device *dev, struct resource *new); + #define devm_request_region(dev,start,n,name) \ __devm_request_region(dev, &ioport_resource, (start), (n), (name)) #define devm_request_mem_region(dev,start,n,name) \ diff --git a/include/linux/of.h b/include/linux/of.h index 6c4363b8ddc3..51c9178b3274 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -74,6 +74,18 @@ struct of_phandle_args { uint32_t args[MAX_PHANDLE_ARGS]; }; +/* + * keep the state at iterating a list of phandles with variable number + * of args + */ +struct of_phandle_iter { + const __be32 *cur; /* current phandle */ + const __be32 *end; /* end of the last phandle */ + const char *cells_name; + int cell_count; + struct of_phandle_args out_args; +}; + /* initialize a node */ extern struct kobj_type of_node_ktype; static inline void of_node_init(struct device_node *node) @@ -303,6 +315,12 @@ extern int of_parse_phandle_with_fixed_args(const struct device_node *np, extern int of_count_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name); +extern void of_phandle_iter_start(struct of_phandle_iter *iter, + const struct device_node *np, + const char *list_name, + const char *cells_name, int cell_count); +extern void of_phandle_iter_next(struct of_phandle_iter *iter); + extern void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)); extern int of_alias_get_id(struct device_node *np, const char *stem); @@ -555,6 +573,18 @@ static inline int of_count_phandle_with_args(struct device_node *np, return -ENOSYS; } +static inline void of_phandle_iter_start(struct of_phandle_iter *iter, + const struct device_node *np, + const char *list_name, + const char *cells_name, + int cell_count) +{ +} + +static inline void of_phandle_iter_next(struct of_phandle_iter *iter) +{ +} + static inline int of_alias_get_id(struct device_node *np, const char *stem) { return -ENOSYS; @@ -743,6 +773,12 @@ static inline int of_property_read_u32(const struct device_node *np, for (dn = of_find_node_with_property(NULL, prop_name); dn; \ dn = of_find_node_with_property(dn, prop_name)) +#define of_property_for_each_phandle_with_args(iter, np, list_name, \ + cells_name, cell_count) \ + for (of_phandle_iter_start(&iter, np, list_name, \ + cells_name, cell_count); \ + iter.cur; of_phandle_iter_next(&iter)) + static inline int of_get_child_count(const struct device_node *np) { struct device_node *child; diff --git a/include/linux/pwm_backlight.h b/include/linux/pwm_backlight.h index efdd9227a49c..1fc14989da4a 100644 --- a/include/linux/pwm_backlight.h +++ b/include/linux/pwm_backlight.h @@ -15,6 +15,8 @@ struct platform_pwm_backlight_data { unsigned int *levels; /* TODO remove once all users are switched to gpiod_* API */ int enable_gpio; + bool boot_off; + int (*init)(struct device *dev); int (*notify)(struct device *dev, int brightness); void (*notify_after)(struct device *dev, int brightness); diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index a2b28a2fd7b1..f0eff578f3c7 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -122,6 +122,7 @@ int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry) { return irq_set_msi_desc_off(irq, 0, entry); } +EXPORT_SYMBOL(irq_set_msi_desc); /** * irq_set_chip_data - set irq chip data for an irq diff --git a/kernel/resource.c b/kernel/resource.c index da14b8d09296..ca24f19f9d18 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -1248,6 +1248,76 @@ int release_mem_region_adjustable(struct resource *parent, /* * Managed region resource */ +static void devm_resource_release(struct device *dev, void *ptr) +{ + struct resource **r = ptr; + + release_resource(*r); +} + +/** + * devm_request_resource() - request and reserve an I/O or memory resource + * @dev: device for which to request the resource + * @root: root of the resource tree from which to request the resource + * @new: descriptor of the resource to request + * + * This is a device-managed version of request_resource(). There is usually + * no need to release resources requested by this function explicitly since + * that will be taken care of when the device is unbound from its driver. + * If for some reason the resource needs to be released explicitly, because + * of ordering issues for example, drivers must call devm_release_resource() + * rather than the regular release_resource(). + * + * When a conflict is detected between any existing resources and the newly + * requested resource, an error message will be printed. + * + * Returns 0 on success or a negative error code on failure. + */ +int devm_request_resource(struct device *dev, struct resource *root, + struct resource *new) +{ + struct resource *conflict, **ptr; + + ptr = devres_alloc(devm_resource_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + *ptr = new; + + conflict = request_resource_conflict(root, new); + if (conflict) { + dev_err(dev, "resource collision: %pR conflicts with %s %pR\n", + new, conflict->name, conflict); + devres_free(ptr); + return -EBUSY; + } + + devres_add(dev, ptr); + return 0; +} +EXPORT_SYMBOL(devm_request_resource); + +static int devm_resource_match(struct device *dev, void *res, void *data) +{ + struct resource **ptr = res; + + return *ptr == data; +} + +/** + * devm_release_resource() - release a previously requested resource + * @dev: device for which to release the resource + * @new: descriptor of the resource to release + * + * Releases a resource previously requested using devm_request_resource(). + */ +void devm_release_resource(struct device *dev, struct resource *new) +{ + WARN_ON(devres_release(dev, devm_resource_release, devm_resource_match, + new)); +} +EXPORT_SYMBOL(devm_release_resource); + struct region_devres { struct resource *parent; resource_size_t start; diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 2b0aa5486092..8e4ae83e4887 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -1372,6 +1372,7 @@ struct vm_struct *get_vm_area(unsigned long size, unsigned long flags) NUMA_NO_NODE, GFP_KERNEL, __builtin_return_address(0)); } +EXPORT_SYMBOL_GPL(get_vm_area); struct vm_struct *get_vm_area_caller(unsigned long size, unsigned long flags, const void *caller) |