From c27602086d08d22b067a1267e09fb32b4b096aa0 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 10 Jul 2014 23:36:29 +0200 Subject: PCI: mvebu: Remove ARCH_KIRKWOOD dependency mach-kirkwood has been removed, now that kirkwood lives in mach-mvebu. ARCH_MVEBU is sufficient. Signed-off-by: Andrew Lunn Signed-off-by: Bjorn Helgaas Acked-by: Jason Cooper diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 21df477..88f4862 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -3,7 +3,7 @@ menu "PCI host controller drivers" config PCI_MVEBU bool "Marvell EBU PCIe controller" - depends on ARCH_MVEBU || ARCH_DOVE || ARCH_KIRKWOOD + depends on ARCH_MVEBU || ARCH_DOVE depends on OF config PCIE_DW -- cgit v0.10.2 From 2cb989f6e99aa84997961fcef516e5cd123b9a78 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 22 Jul 2014 12:30:46 -0600 Subject: PCI: tegra: Add debugfs support Provide a debugfs file ("pcie/ports") that shows the current link status for each root port. Signed-off-by: Thierry Reding Signed-off-by: Bjorn Helgaas Reviewed-by: Stephen Warren diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index c284e84..869a921 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -25,6 +25,7 @@ */ #include +#include #include #include #include @@ -277,6 +278,7 @@ struct tegra_pcie { struct regulator *avdd_supply; const struct tegra_pcie_soc_data *soc_data; + struct dentry *debugfs; }; struct tegra_pcie_port { @@ -1638,6 +1640,115 @@ static const struct of_device_id tegra_pcie_of_match[] = { }; MODULE_DEVICE_TABLE(of, tegra_pcie_of_match); +static void *tegra_pcie_ports_seq_start(struct seq_file *s, loff_t *pos) +{ + struct tegra_pcie *pcie = s->private; + + if (list_empty(&pcie->ports)) + return NULL; + + seq_printf(s, "Index Status\n"); + + return seq_list_start(&pcie->ports, *pos); +} + +static void *tegra_pcie_ports_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct tegra_pcie *pcie = s->private; + + return seq_list_next(v, &pcie->ports, pos); +} + +static void tegra_pcie_ports_seq_stop(struct seq_file *s, void *v) +{ +} + +static int tegra_pcie_ports_seq_show(struct seq_file *s, void *v) +{ + bool up = false, active = false; + struct tegra_pcie_port *port; + unsigned int value; + + port = list_entry(v, struct tegra_pcie_port, list); + + value = readl(port->base + RP_VEND_XP); + + if (value & RP_VEND_XP_DL_UP) + up = true; + + value = readl(port->base + RP_LINK_CONTROL_STATUS); + + if (value & RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE) + active = true; + + seq_printf(s, "%2u ", port->index); + + if (up) + seq_printf(s, "up"); + + if (active) { + if (up) + seq_printf(s, ", "); + + seq_printf(s, "active"); + } + + seq_printf(s, "\n"); + return 0; +} + +static const struct seq_operations tegra_pcie_ports_seq_ops = { + .start = tegra_pcie_ports_seq_start, + .next = tegra_pcie_ports_seq_next, + .stop = tegra_pcie_ports_seq_stop, + .show = tegra_pcie_ports_seq_show, +}; + +static int tegra_pcie_ports_open(struct inode *inode, struct file *file) +{ + struct tegra_pcie *pcie = inode->i_private; + struct seq_file *s; + int err; + + err = seq_open(file, &tegra_pcie_ports_seq_ops); + if (err) + return err; + + s = file->private_data; + s->private = pcie; + + return 0; +} + +static const struct file_operations tegra_pcie_ports_ops = { + .owner = THIS_MODULE, + .open = tegra_pcie_ports_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie) +{ + struct dentry *file; + + pcie->debugfs = debugfs_create_dir("pcie", NULL); + if (!pcie->debugfs) + return -ENOMEM; + + file = debugfs_create_file("ports", S_IFREG | S_IRUGO, pcie->debugfs, + pcie, &tegra_pcie_ports_ops); + if (!file) + goto remove; + + return 0; + +remove: + debugfs_remove_recursive(pcie->debugfs); + pcie->debugfs = NULL; + return -ENOMEM; +} + static int tegra_pcie_probe(struct platform_device *pdev) { const struct of_device_id *match; @@ -1692,6 +1803,13 @@ static int tegra_pcie_probe(struct platform_device *pdev) goto disable_msi; } + if (IS_ENABLED(CONFIG_DEBUG_FS)) { + err = tegra_pcie_debugfs_init(pcie); + if (err < 0) + dev_err(&pdev->dev, "failed to setup debugfs: %d\n", + err); + } + platform_set_drvdata(pdev, pcie); return 0; -- cgit v0.10.2 From 4dd964df36d0e548e1806ec2ec275b62d4dc46e8 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 17 Jul 2014 14:30:40 +0530 Subject: PCI: designware: Look for configuration space in 'reg', not 'ranges' The configuration address space has so far been specified in *ranges*, however it should be specified in *reg* making it a platform MEM resource. Hence used 'platform_get_resource_*' API to get configuration address space in the designware driver. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Bjorn Helgaas Acked-by: Mohit Kumar Acked-by: Jingoo Han Cc: Jason Gunthorpe Cc: Marek Vasut Cc: Arnd Bergmann diff --git a/Documentation/devicetree/bindings/pci/designware-pcie.txt b/Documentation/devicetree/bindings/pci/designware-pcie.txt index d0d15ee..ed0d9b9 100644 --- a/Documentation/devicetree/bindings/pci/designware-pcie.txt +++ b/Documentation/devicetree/bindings/pci/designware-pcie.txt @@ -2,6 +2,10 @@ Required properties: - compatible: should contain "snps,dw-pcie" to identify the core. +- reg: Should contain the configuration address space. +- reg-names: Must be "config" for the PCIe configuration space. + (The old way of getting the configuration address space from "ranges" + is deprecated and should be avoided.) - #address-cells: set to <3> - #size-cells: set to <2> - device_type: set to "pci" diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 1eaf4df..0b7b455 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "pcie-designware.h" @@ -396,11 +397,23 @@ static const struct irq_domain_ops msi_domain_ops = { int __init dw_pcie_host_init(struct pcie_port *pp) { struct device_node *np = pp->dev->of_node; + struct platform_device *pdev = to_platform_device(pp->dev); struct of_pci_range range; struct of_pci_range_parser parser; + struct resource *cfg_res; u32 val; int i; + cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); + if (cfg_res) { + pp->config.cfg0_size = resource_size(cfg_res)/2; + pp->config.cfg1_size = resource_size(cfg_res)/2; + pp->cfg0_base = cfg_res->start; + pp->cfg1_base = cfg_res->start + pp->config.cfg0_size; + } else { + dev_err(pp->dev, "missing *config* reg space\n"); + } + if (of_pci_range_parser_init(&parser, np)) { dev_err(pp->dev, "missing ranges property\n"); return -EINVAL; @@ -433,6 +446,8 @@ int __init dw_pcie_host_init(struct pcie_port *pp) of_pci_range_to_resource(&range, np, &pp->cfg); pp->config.cfg0_size = resource_size(&pp->cfg)/2; pp->config.cfg1_size = resource_size(&pp->cfg)/2; + pp->cfg0_base = pp->cfg.start; + pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size; } } @@ -445,8 +460,6 @@ int __init dw_pcie_host_init(struct pcie_port *pp) } } - pp->cfg0_base = pp->cfg.start; - pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size; pp->mem_base = pp->mem.start; pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base, -- cgit v0.10.2 From f4c55c5a3f7f68c06cc559ed7af8b2d017cbb0a7 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 17 Jul 2014 14:30:41 +0530 Subject: PCI: designware: Program ATU with untranslated address In DRA7, the CPU sees 32-bit addresses, but the PCIe controller can see only 28-bit addresses. So whenever the CPU issues a read/write request, the 4 most significant bits are used by L3 to determine the target controller. For example, the CPU reserves [mem 0x20000000-0x2fffffff] for the PCIe controller but the PCIe controller will see only [0x00000000-0x0fffffff]. For programming the outbound translation window the *base* should be programmed as 0x00000000. Whenever we try to write to, e.g., 0x20000000, it will be translated to whatever we have programmed in the translation window with base as 0x00000000. This is needed when the dt node is modelled something like this: axi { compatible = "simple-bus"; #size-cells = <1>; #address-cells = <1>; ranges = <0x0 0x20000000 0x10000000 // 28-bit bus 0x51000000 0x51000000 0x3000>; pcie@51000000 { reg = <0x1000 0x2000>, <0x51002000 0x14c>, <0x51000000 0x2000>; reg-names = "config", "ti_conf", "rc_dbics"; #address-cells = <3>; #size-cells = <2>; ranges = <0x81000000 0 0 0x03000 0 0x00010000 0x82000000 0 0x20013000 0x13000 0 0xffed000>; }; }; Here the CPU address for configuration space is 0x20013000 and the controller address for configuration space is 0x13000. The controller address should be used while programming the ATU (in order for translation to happen properly in DRA7xx). Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Bjorn Helgaas Reviewed-by: Mohit Kumar Cc: Jason Gunthorpe Cc: Jingoo Han Cc: Marek Vasut Cc: Arnd Bergmann diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 0b7b455..8aab1d6 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -401,8 +401,15 @@ int __init dw_pcie_host_init(struct pcie_port *pp) struct of_pci_range range; struct of_pci_range_parser parser; struct resource *cfg_res; - u32 val; - int i; + u32 val, na, ns; + const __be32 *addrp; + int i, index; + + /* Find the address cell size and the number of cells in order to get + * the untranslated address. + */ + of_property_read_u32(np, "#address-cells", &na); + ns = of_n_size_cells(np); cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); if (cfg_res) { @@ -410,6 +417,12 @@ int __init dw_pcie_host_init(struct pcie_port *pp) pp->config.cfg1_size = resource_size(cfg_res)/2; pp->cfg0_base = cfg_res->start; pp->cfg1_base = cfg_res->start + pp->config.cfg0_size; + + /* Find the untranslated configuration space address */ + index = of_property_match_string(np, "reg-names", "config"); + addrp = of_get_address(np, index, false, false); + pp->cfg0_mod_base = of_read_number(addrp, ns); + pp->cfg1_mod_base = pp->cfg0_mod_base + pp->config.cfg0_size; } else { dev_err(pp->dev, "missing *config* reg space\n"); } @@ -435,12 +448,20 @@ int __init dw_pcie_host_init(struct pcie_port *pp) pp->config.io_size = resource_size(&pp->io); pp->config.io_bus_addr = range.pci_addr; pp->io_base = range.cpu_addr; + + /* Find the untranslated IO space address */ + pp->io_mod_base = of_read_number(parser.range - + parser.np + na, ns); } if (restype == IORESOURCE_MEM) { of_pci_range_to_resource(&range, np, &pp->mem); pp->mem.name = "MEM"; pp->config.mem_size = resource_size(&pp->mem); pp->config.mem_bus_addr = range.pci_addr; + + /* Find the untranslated MEM space address */ + pp->mem_mod_base = of_read_number(parser.range - + parser.np + na, ns); } if (restype == 0) { of_pci_range_to_resource(&range, np, &pp->cfg); @@ -448,6 +469,12 @@ int __init dw_pcie_host_init(struct pcie_port *pp) pp->config.cfg1_size = resource_size(&pp->cfg)/2; pp->cfg0_base = pp->cfg.start; pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size; + + /* Find the untranslated configuration space address */ + pp->cfg0_mod_base = of_read_number(parser.range - + parser.np + na, ns); + pp->cfg1_mod_base = pp->cfg0_mod_base + + pp->config.cfg0_size; } } @@ -522,9 +549,9 @@ static void dw_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev) /* Program viewport 0 : OUTBOUND : CFG0 */ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, PCIE_ATU_VIEWPORT); - dw_pcie_writel_rc(pp, pp->cfg0_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->cfg0_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->cfg0_base + pp->config.cfg0_size - 1, + dw_pcie_writel_rc(pp, pp->cfg0_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->cfg0_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->cfg0_mod_base + pp->config.cfg0_size - 1, PCIE_ATU_LIMIT); dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); @@ -538,9 +565,9 @@ static void dw_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev) dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, PCIE_ATU_VIEWPORT); dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG1, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, pp->cfg1_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->cfg1_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->cfg1_base + pp->config.cfg1_size - 1, + dw_pcie_writel_rc(pp, pp->cfg1_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->cfg1_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->cfg1_mod_base + pp->config.cfg1_size - 1, PCIE_ATU_LIMIT); dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); @@ -553,9 +580,9 @@ static void dw_pcie_prog_viewport_mem_outbound(struct pcie_port *pp) dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, PCIE_ATU_VIEWPORT); dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_MEM, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, pp->mem_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->mem_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->mem_base + pp->config.mem_size - 1, + dw_pcie_writel_rc(pp, pp->mem_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->mem_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->mem_mod_base + pp->config.mem_size - 1, PCIE_ATU_LIMIT); dw_pcie_writel_rc(pp, pp->config.mem_bus_addr, PCIE_ATU_LOWER_TARGET); dw_pcie_writel_rc(pp, upper_32_bits(pp->config.mem_bus_addr), @@ -569,9 +596,9 @@ static void dw_pcie_prog_viewport_io_outbound(struct pcie_port *pp) dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, PCIE_ATU_VIEWPORT); dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_IO, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, pp->io_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->io_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->io_base + pp->config.io_size - 1, + dw_pcie_writel_rc(pp, pp->io_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->io_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->io_mod_base + pp->config.io_size - 1, PCIE_ATU_LIMIT); dw_pcie_writel_rc(pp, pp->config.io_bus_addr, PCIE_ATU_LOWER_TARGET); dw_pcie_writel_rc(pp, upper_32_bits(pp->config.io_bus_addr), diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index 77f592f..add6527 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -36,11 +36,15 @@ struct pcie_port { u8 root_bus_nr; void __iomem *dbi_base; u64 cfg0_base; + u64 cfg0_mod_base; void __iomem *va_cfg0_base; u64 cfg1_base; + u64 cfg1_mod_base; void __iomem *va_cfg1_base; u64 io_base; + u64 io_mod_base; u64 mem_base; + u64 mem_mod_base; struct resource cfg; struct resource io; struct resource mem; -- cgit v0.10.2 From 47ff3de911a728cdf9ecc6ad777131902cff62b4 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 22 Jul 2014 15:23:45 -0600 Subject: PCI: dra7xx: Add TI DRA7xx PCIe driver Add support for PCIe controller in DRA7xx. This driver re-uses the designware core code that is already present in kernel. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Bjorn Helgaas Acked-by: Jingoo Han Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Ian Campbell Cc: Kumar Gala Cc: Jason Gunthorpe Cc: Mohit Kumar Cc: Marek Vasut Cc: Arnd Bergmann diff --git a/Documentation/devicetree/bindings/pci/ti-pci.txt b/Documentation/devicetree/bindings/pci/ti-pci.txt new file mode 100644 index 0000000..3d21791 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/ti-pci.txt @@ -0,0 +1,59 @@ +TI PCI Controllers + +PCIe Designware Controller + - compatible: Should be "ti,dra7-pcie"" + - reg : Two register ranges as listed in the reg-names property + - reg-names : The first entry must be "ti-conf" for the TI specific registers + The second entry must be "rc-dbics" for the designware pcie + registers + The third entry must be "config" for the PCIe configuration space + - phys : list of PHY specifiers (used by generic PHY framework) + - phy-names : must be "pcie-phy0", "pcie-phy1", "pcie-phyN".. based on the + number of PHYs as specified in *phys* property. + - ti,hwmods : Name of the hwmod associated to the pcie, "pcie", + where is the instance number of the pcie from the HW spec. + - interrupts : Two interrupt entries must be specified. The first one is for + main interrupt line and the second for MSI interrupt line. + - #address-cells, + #size-cells, + #interrupt-cells, + device_type, + ranges, + num-lanes, + interrupt-map-mask, + interrupt-map : as specified in ../designware-pcie.txt + +Example: +axi { + compatible = "simple-bus"; + #size-cells = <1>; + #address-cells = <1>; + ranges = <0x51000000 0x51000000 0x3000 + 0x0 0x20000000 0x10000000>; + pcie@51000000 { + compatible = "ti,dra7-pcie"; + reg = <0x51000000 0x2000>, <0x51002000 0x14c>, <0x1000 0x2000>; + reg-names = "rc_dbics", "ti_conf", "config"; + interrupts = <0 232 0x4>, <0 233 0x4>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges = <0x81000000 0 0 0x03000 0 0x00010000 + 0x82000000 0 0x20013000 0x13000 0 0xffed000>; + #interrupt-cells = <1>; + num-lanes = <1>; + ti,hwmods = "pcie1"; + phys = <&pcie1_phy>; + phy-names = "pcie-phy0"; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc 1>, + <0 0 0 2 &pcie_intc 2>, + <0 0 0 3 &pcie_intc 3>, + <0 0 0 4 &pcie_intc 4>; + pcie_intc: interrupt-controller { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + }; +}; diff --git a/MAINTAINERS b/MAINTAINERS index 134483f..fdd34d1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6796,6 +6796,14 @@ S: Supported F: Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt F: drivers/pci/host/pci-tegra.c +PCI DRIVER FOR TI DRA7XX +M: Kishon Vijay Abraham I +L: linux-omap@vger.kernel.org +L: linux-pci@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/pci/ti-pci.txt +F: drivers/pci/host/pci-dra7xx.c + PCI DRIVER FOR RENESAS R-CAR M: Simon Horman L: linux-pci@vger.kernel.org diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 21df477..f9492cc 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -1,6 +1,15 @@ menu "PCI host controller drivers" depends on PCI +config PCI_DRA7XX + bool "TI DRA7xx PCIe controller" + select PCIE_DW + depends on OF && HAS_IOMEM && TI_PIPE3 + help + Enables support for the PCIe controller in the DRA7xx SoC. There + are two instances of PCIe controller in DRA7xx. This controller can + act both as EP and RC. This reuses the Designware core. + config PCI_MVEBU bool "Marvell EBU PCIe controller" depends on ARCH_MVEBU || ARCH_DOVE || ARCH_KIRKWOOD diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index 611ba4b..c42844d 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_PCIE_DW) += pcie-designware.o +obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o obj-$(CONFIG_PCI_IMX6) += pci-imx6.o obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o diff --git a/drivers/pci/host/pci-dra7xx.c b/drivers/pci/host/pci-dra7xx.c new file mode 100644 index 0000000..52b34fe --- /dev/null +++ b/drivers/pci/host/pci-dra7xx.c @@ -0,0 +1,458 @@ +/* + * pcie-dra7xx - PCIe controller driver for TI DRA7xx SoCs + * + * Copyright (C) 2013-2014 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Kishon Vijay Abraham I + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +/* PCIe controller wrapper DRA7XX configuration registers */ + +#define PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN 0x0024 +#define PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN 0x0028 +#define ERR_SYS BIT(0) +#define ERR_FATAL BIT(1) +#define ERR_NONFATAL BIT(2) +#define ERR_COR BIT(3) +#define ERR_AXI BIT(4) +#define ERR_ECRC BIT(5) +#define PME_TURN_OFF BIT(8) +#define PME_TO_ACK BIT(9) +#define PM_PME BIT(10) +#define LINK_REQ_RST BIT(11) +#define LINK_UP_EVT BIT(12) +#define CFG_BME_EVT BIT(13) +#define CFG_MSE_EVT BIT(14) +#define INTERRUPTS (ERR_SYS | ERR_FATAL | ERR_NONFATAL | ERR_COR | ERR_AXI | \ + ERR_ECRC | PME_TURN_OFF | PME_TO_ACK | PM_PME | \ + LINK_REQ_RST | LINK_UP_EVT | CFG_BME_EVT | CFG_MSE_EVT) + +#define PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI 0x0034 +#define PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI 0x0038 +#define INTA BIT(0) +#define INTB BIT(1) +#define INTC BIT(2) +#define INTD BIT(3) +#define MSI BIT(4) +#define LEG_EP_INTERRUPTS (INTA | INTB | INTC | INTD) + +#define PCIECTRL_DRA7XX_CONF_DEVICE_CMD 0x0104 +#define LTSSM_EN 0x1 + +#define PCIECTRL_DRA7XX_CONF_PHY_CS 0x010C +#define LINK_UP BIT(16) + +struct dra7xx_pcie { + void __iomem *base; + struct phy **phy; + int phy_count; + struct device *dev; + struct pcie_port pp; +}; + +#define to_dra7xx_pcie(x) container_of((x), struct dra7xx_pcie, pp) + +static inline u32 dra7xx_pcie_readl(struct dra7xx_pcie *pcie, u32 offset) +{ + return readl(pcie->base + offset); +} + +static inline void dra7xx_pcie_writel(struct dra7xx_pcie *pcie, u32 offset, + u32 value) +{ + writel(value, pcie->base + offset); +} + +static int dra7xx_pcie_link_up(struct pcie_port *pp) +{ + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); + u32 reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_PHY_CS); + + return !!(reg & LINK_UP); +} + +static int dra7xx_pcie_establish_link(struct pcie_port *pp) +{ + u32 reg; + unsigned int retries = 1000; + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); + + if (dw_pcie_link_up(pp)) { + dev_err(pp->dev, "link is already up\n"); + return 0; + } + + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD); + reg |= LTSSM_EN; + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); + + while (retries--) { + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_PHY_CS); + if (reg & LINK_UP) + break; + usleep_range(10, 20); + } + + if (retries == 0) { + dev_err(pp->dev, "link is not up\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp) +{ + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); + + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, + ~INTERRUPTS); + dra7xx_pcie_writel(dra7xx, + PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN, INTERRUPTS); + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI, + ~LEG_EP_INTERRUPTS & ~MSI); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + dra7xx_pcie_writel(dra7xx, + PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI, MSI); + else + dra7xx_pcie_writel(dra7xx, + PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI, + LEG_EP_INTERRUPTS); +} + +static void dra7xx_pcie_host_init(struct pcie_port *pp) +{ + dw_pcie_setup_rc(pp); + dra7xx_pcie_establish_link(pp); + if (IS_ENABLED(CONFIG_PCI_MSI)) + dw_pcie_msi_init(pp); + dra7xx_pcie_enable_interrupts(pp); +} + +static struct pcie_host_ops dra7xx_pcie_host_ops = { + .link_up = dra7xx_pcie_link_up, + .host_init = dra7xx_pcie_host_init, +}; + +static int dra7xx_pcie_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); + irq_set_chip_data(irq, domain->host_data); + set_irq_flags(irq, IRQF_VALID); + + return 0; +} + +static const struct irq_domain_ops intx_domain_ops = { + .map = dra7xx_pcie_intx_map, +}; + +static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp) +{ + struct device *dev = pp->dev; + struct device_node *node = dev->of_node; + struct device_node *pcie_intc_node = of_get_next_child(node, NULL); + + if (!pcie_intc_node) { + dev_err(dev, "No PCIe Intc node found\n"); + return PTR_ERR(pcie_intc_node); + } + + pp->irq_domain = irq_domain_add_linear(pcie_intc_node, 4, + &intx_domain_ops, pp); + if (!pp->irq_domain) { + dev_err(dev, "Failed to get a INTx IRQ domain\n"); + return PTR_ERR(pp->irq_domain); + } + + return 0; +} + +static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg) +{ + struct pcie_port *pp = arg; + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); + u32 reg; + + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI); + + switch (reg) { + case MSI: + dw_handle_msi_irq(pp); + break; + case INTA: + case INTB: + case INTC: + case INTD: + generic_handle_irq(irq_find_mapping(pp->irq_domain, ffs(reg))); + break; + } + + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI, reg); + + return IRQ_HANDLED; +} + + +static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg) +{ + struct dra7xx_pcie *dra7xx = arg; + u32 reg; + + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN); + + if (reg & ERR_SYS) + dev_dbg(dra7xx->dev, "System Error\n"); + + if (reg & ERR_FATAL) + dev_dbg(dra7xx->dev, "Fatal Error\n"); + + if (reg & ERR_NONFATAL) + dev_dbg(dra7xx->dev, "Non Fatal Error\n"); + + if (reg & ERR_COR) + dev_dbg(dra7xx->dev, "Correctable Error\n"); + + if (reg & ERR_AXI) + dev_dbg(dra7xx->dev, "AXI tag lookup fatal Error\n"); + + if (reg & ERR_ECRC) + dev_dbg(dra7xx->dev, "ECRC Error\n"); + + if (reg & PME_TURN_OFF) + dev_dbg(dra7xx->dev, + "Power Management Event Turn-Off message received\n"); + + if (reg & PME_TO_ACK) + dev_dbg(dra7xx->dev, + "Power Management Turn-Off Ack message received\n"); + + if (reg & PM_PME) + dev_dbg(dra7xx->dev, + "PM Power Management Event message received\n"); + + if (reg & LINK_REQ_RST) + dev_dbg(dra7xx->dev, "Link Request Reset\n"); + + if (reg & LINK_UP_EVT) + dev_dbg(dra7xx->dev, "Link-up state change\n"); + + if (reg & CFG_BME_EVT) + dev_dbg(dra7xx->dev, "CFG 'Bus Master Enable' change\n"); + + if (reg & CFG_MSE_EVT) + dev_dbg(dra7xx->dev, "CFG 'Memory Space Enable' change\n"); + + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, reg); + + return IRQ_HANDLED; +} + +static int add_pcie_port(struct dra7xx_pcie *dra7xx, + struct platform_device *pdev) +{ + int ret; + struct pcie_port *pp; + struct resource *res; + struct device *dev = &pdev->dev; + + pp = &dra7xx->pp; + pp->dev = dev; + pp->ops = &dra7xx_pcie_host_ops; + + pp->irq = platform_get_irq(pdev, 1); + if (pp->irq < 0) { + dev_err(dev, "missing IRQ resource\n"); + return -EINVAL; + } + + ret = devm_request_irq(&pdev->dev, pp->irq, + dra7xx_pcie_msi_irq_handler, IRQF_SHARED, + "dra7-pcie-msi", pp); + if (ret) { + dev_err(&pdev->dev, "failed to request irq\n"); + return ret; + } + + if (!IS_ENABLED(CONFIG_PCI_MSI)) { + ret = dra7xx_pcie_init_irq_domain(pp); + if (ret < 0) + return ret; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbics"); + pp->dbi_base = devm_ioremap(dev, res->start, resource_size(res)); + if (!pp->dbi_base) + return -ENOMEM; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dra7xx->dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static int __init dra7xx_pcie_probe(struct platform_device *pdev) +{ + u32 reg; + int ret; + int irq; + int i; + int phy_count; + struct phy **phy; + void __iomem *base; + struct resource *res; + struct dra7xx_pcie *dra7xx; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + char name[10]; + + dra7xx = devm_kzalloc(dev, sizeof(*dra7xx), GFP_KERNEL); + if (!dra7xx) + return -ENOMEM; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "missing IRQ resource\n"); + return -EINVAL; + } + + ret = devm_request_irq(dev, irq, dra7xx_pcie_irq_handler, + IRQF_SHARED, "dra7xx-pcie-main", dra7xx); + if (ret) { + dev_err(dev, "failed to request irq\n"); + return ret; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ti_conf"); + base = devm_ioremap_nocache(dev, res->start, resource_size(res)); + if (!base) + return -ENOMEM; + + phy_count = of_property_count_strings(np, "phy-names"); + if (phy_count < 0) { + dev_err(dev, "unable to find the strings\n"); + return phy_count; + } + + phy = devm_kzalloc(dev, sizeof(*phy) * phy_count, GFP_KERNEL); + if (!phy) + return -ENOMEM; + + for (i = 0; i < phy_count; i++) { + snprintf(name, sizeof(name), "pcie-phy%d", i); + phy[i] = devm_phy_get(dev, name); + if (IS_ERR(phy[i])) + return PTR_ERR(phy[i]); + + ret = phy_init(phy[i]); + if (ret < 0) + goto err_phy; + + ret = phy_power_on(phy[i]); + if (ret < 0) { + phy_exit(phy[i]); + goto err_phy; + } + } + + dra7xx->base = base; + dra7xx->phy = phy; + dra7xx->dev = dev; + dra7xx->phy_count = phy_count; + + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "pm_runtime_get_sync failed\n"); + goto err_phy; + } + + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD); + reg &= ~LTSSM_EN; + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); + + platform_set_drvdata(pdev, dra7xx); + + ret = add_pcie_port(dra7xx, pdev); + if (ret < 0) + goto err_add_port; + + return 0; + +err_add_port: + pm_runtime_put(dev); + pm_runtime_disable(dev); + +err_phy: + while (--i >= 0) { + phy_power_off(phy[i]); + phy_exit(phy[i]); + } + + return ret; +} + +static int __exit dra7xx_pcie_remove(struct platform_device *pdev) +{ + struct dra7xx_pcie *dra7xx = platform_get_drvdata(pdev); + struct pcie_port *pp = &dra7xx->pp; + struct device *dev = &pdev->dev; + int count = dra7xx->phy_count; + + if (pp->irq_domain) + irq_domain_remove(pp->irq_domain); + pm_runtime_put(dev); + pm_runtime_disable(dev); + while (count--) { + phy_power_off(dra7xx->phy[count]); + phy_exit(dra7xx->phy[count]); + } + + return 0; +} + +static const struct of_device_id of_dra7xx_pcie_match[] = { + { .compatible = "ti,dra7-pcie", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_dra7xx_pcie_match); + +static struct platform_driver dra7xx_pcie_driver = { + .remove = __exit_p(dra7xx_pcie_remove), + .driver = { + .name = "dra7-pcie", + .owner = THIS_MODULE, + .of_match_table = of_dra7xx_pcie_match, + }, +}; + +module_platform_driver_probe(dra7xx_pcie_driver, dra7xx_pcie_probe); + +MODULE_AUTHOR("Kishon Vijay Abraham I "); +MODULE_DESCRIPTION("TI PCIe controller driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From a1c0ae9c24627a12c781ebd9947a6442861f6168 Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Mon, 21 Jul 2014 12:58:41 -0400 Subject: PCI: designware: Add config access-related pcie_host_ops for v3.65 hardware DesignWare v3.65 hardware requires application space registers to be configured to access the remote EP config space. To support this, add rd_other_conf() and wr_other_conf() to pcie_host_ops. [bhelgaas: changelog] Signed-off-by: Murali Karicheri Signed-off-by: Bjorn Helgaas Reviewed-by: Pratyush Anand Acked-by: Mohit Kumar Acked-by: Jingoo Han Acked-by: Santosh Shilimkar CC: Russell King CC: Grant Likely CC: Rob Herring CC: Richard Zhu CC: Kishon Vijay Abraham I CC: Marek Vasut CC: Arnd Bergmann CC: Pawel Moll CC: Mark Rutland CC: Ian Campbell CC: Kumar Gala CC: Randy Dunlap CC: Grant Likely diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 8aab1d6..0e9838a 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -696,7 +696,11 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, } if (bus->number != pp->root_bus_nr) - ret = dw_pcie_rd_other_conf(pp, bus, devfn, + if (pp->ops->rd_other_conf) + ret = pp->ops->rd_other_conf(pp, bus, devfn, + where, size, val); + else + ret = dw_pcie_rd_other_conf(pp, bus, devfn, where, size, val); else ret = dw_pcie_rd_own_conf(pp, where, size, val); @@ -719,7 +723,11 @@ static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, return PCIBIOS_DEVICE_NOT_FOUND; if (bus->number != pp->root_bus_nr) - ret = dw_pcie_wr_other_conf(pp, bus, devfn, + if (pp->ops->wr_other_conf) + ret = pp->ops->wr_other_conf(pp, bus, devfn, + where, size, val); + else + ret = dw_pcie_wr_other_conf(pp, bus, devfn, where, size, val); else ret = dw_pcie_wr_own_conf(pp, where, size, val); diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index add6527..93062229 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -65,6 +65,10 @@ struct pcie_host_ops { u32 val, void __iomem *dbi_base); int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val); int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val); + int (*rd_other_conf)(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 *val); + int (*wr_other_conf)(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 val); int (*link_up)(struct pcie_port *pp); void (*host_init)(struct pcie_port *pp); }; -- cgit v0.10.2 From 2f37c5a81cff2c341fa19fdd132ece6aea30a735 Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Mon, 21 Jul 2014 12:58:42 -0400 Subject: PCI: designware: Add MSI-related pcie_host_ops for v3.65 hardware DesignWare v3.65 hardware implements MSI controller registers in application space. This requires updates to the DesignWare core to support controllers based on this older hardware. Add msi_irq_set()/clear() interfaces to allow Set/Clear MSI IRQ enable bit in the application register. Also, v3.65 hardware uses the MSI_IRQ register in application register space to raise MSI IRQ to the RC from EP. Current code uses the standard mechanism as per PCI spec. So add get_msi_data() to get the address of this register so common code can work on both v3.65 and newer hardware. [bhelgaas: changelog] Signed-off-by: Murali Karicheri Signed-off-by: Bjorn Helgaas Reviewed-by: Pratyush Anand Acked-by: Mohit Kumar Acked-by: Jingoo Han Acked-by: Santosh Shilimkar CC: Russell King CC: Grant Likely CC: Rob Herring CC: Richard Zhu CC: Kishon Vijay Abraham I CC: Marek Vasut CC: Arnd Bergmann CC: Pawel Moll CC: Mark Rutland CC: Ian Campbell CC: Kumar Gala CC: Randy Dunlap CC: Grant Likely diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 0e9838a..52bd3a1 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -218,27 +218,47 @@ static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0) return 0; } +static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) +{ + unsigned int res, bit, val; + + res = (irq / 32) * 12; + bit = irq % 32; + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); + val &= ~(1 << bit); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); +} + static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, unsigned int nvec, unsigned int pos) { - unsigned int i, res, bit, val; + unsigned int i; for (i = 0; i < nvec; i++) { irq_set_msi_desc_off(irq_base, i, NULL); clear_bit(pos + i, pp->msi_irq_in_use); /* Disable corresponding interrupt on MSI controller */ - res = ((pos + i) / 32) * 12; - bit = (pos + i) % 32; - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); - val &= ~(1 << bit); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); + if (pp->ops->msi_clear_irq) + pp->ops->msi_clear_irq(pp, pos + i); + else + dw_pcie_msi_clear_irq(pp, pos + i); } } +static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) +{ + unsigned int res, bit, val; + + res = (irq / 32) * 12; + bit = irq % 32; + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); + val |= 1 << bit; + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); +} + static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) { - int res, bit, irq, pos0, pos1, i; - u32 val; + int irq, pos0, pos1, i; struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata); if (!pp) { @@ -282,11 +302,10 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) } set_bit(pos0 + i, pp->msi_irq_in_use); /*Enable corresponding interrupt in MSI interrupt controller */ - res = ((pos0 + i) / 32) * 12; - bit = (pos0 + i) % 32; - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); - val |= 1 << bit; - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); + if (pp->ops->msi_set_irq) + pp->ops->msi_set_irq(pp, pos0 + i); + else + dw_pcie_msi_set_irq(pp, pos0 + i); } *pos = pos0; @@ -354,7 +373,10 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, */ desc->msi_attrib.multiple = msgvec; - msg.address_lo = virt_to_phys((void *)pp->msi_data); + if (pp->ops->get_msi_data) + msg.address_lo = pp->ops->get_msi_data(pp); + else + msg.address_lo = virt_to_phys((void *)pp->msi_data); msg.address_hi = 0x0; msg.data = pos; write_msi_msg(irq, &msg); diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index 93062229..daf81f9 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -71,6 +71,9 @@ struct pcie_host_ops { unsigned int devfn, int where, int size, u32 val); int (*link_up)(struct pcie_port *pp); void (*host_init)(struct pcie_port *pp); + void (*msi_set_irq)(struct pcie_port *pp, int irq); + void (*msi_clear_irq)(struct pcie_port *pp, int irq); + u32 (*get_msi_data)(struct pcie_port *pp); }; int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val); -- cgit v0.10.2