--- /dev/null
+From 95aafe911db602d19b00d2a88c3d54a84119f5dc Mon Sep 17 00:00:00 2001
+Date: Sun, 25 Apr 2021 02:30:38 +0200
+Subject: net: ethernet: ixp4xx: Support device tree probing
+
+This adds device tree probing to the IXP4xx ethernet
+driver.
+
+Add a platform data bool to tell us whether to
+register an MDIO bus for the device or not, as well
+as the corresponding NPE.
+
+We need to drop the memory region request as part of
+this since the OF core will request the memory for the
+device.
+
+---
+ drivers/net/ethernet/xscale/Kconfig | 1 +
+ drivers/net/ethernet/xscale/ixp4xx_eth.c | 210 +++++++++++++++++++++----------
+ include/linux/platform_data/eth_ixp4xx.h | 2 +
+ 3 files changed, 150 insertions(+), 63 deletions(-)
+
+diff --git a/drivers/net/ethernet/xscale/Kconfig b/drivers/net/ethernet/xscale/Kconfig
+index 7b83a6e5d894d..468ffe3d17075 100644
+--- a/drivers/net/ethernet/xscale/Kconfig
++++ b/drivers/net/ethernet/xscale/Kconfig
+@@ -22,6 +22,7 @@ config IXP4XX_ETH
+ tristate "Intel IXP4xx Ethernet support"
+ depends on ARM && ARCH_IXP4XX && IXP4XX_NPE && IXP4XX_QMGR
+ select PHYLIB
++ select OF_MDIO if OF
+ select NET_PTP_CLASSIFY
+ help
+ Say Y here if you want to use built-in Ethernet ports
+diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c
+index 9d323e8595e2b..1149e88e64546 100644
+--- a/drivers/net/ethernet/xscale/ixp4xx_eth.c
++++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c
+@@ -28,6 +28,7 @@
+ #include <linux/kernel.h>
+ #include <linux/net_tstamp.h>
+ #include <linux/of.h>
++#include <linux/of_mdio.h>
+ #include <linux/phy.h>
+ #include <linux/platform_data/eth_ixp4xx.h>
+ #include <linux/platform_device.h>
+@@ -165,7 +166,6 @@ struct eth_regs {
+ };
+
+ struct port {
+- struct resource *mem_res;
+ struct eth_regs __iomem *regs;
+ struct npe *npe;
+ struct net_device *netdev;
+@@ -250,6 +250,7 @@ static inline void memcpy_swab32(u32 *dest, u32 *src, int cnt)
+ static DEFINE_SPINLOCK(mdio_lock);
+ static struct eth_regs __iomem *mdio_regs; /* mdio command and status only */
+ static struct mii_bus *mdio_bus;
++static struct device_node *mdio_bus_np;
+ static int ports_open;
+ static struct port *npe_port_tab[MAX_NPES];
+ static struct dma_pool *dma_pool;
+@@ -533,7 +534,8 @@ static int ixp4xx_mdio_register(struct eth_regs __iomem *regs)
+ mdio_bus->write = &ixp4xx_mdio_write;
+ snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "ixp4xx-eth-0");
+
+- if ((err = mdiobus_register(mdio_bus)))
++ err = of_mdiobus_register(mdio_bus, mdio_bus_np);
++ if (err)
+ mdiobus_free(mdio_bus);
+ return err;
+ }
+@@ -1358,18 +1360,118 @@ static const struct net_device_ops ixp4xx_netdev_ops = {
+ .ndo_validate_addr = eth_validate_addr,
+ };
+
++#ifdef CONFIG_OF
++static struct eth_plat_info *ixp4xx_of_get_platdata(struct device *dev)
++{
++ struct device_node *np = dev->of_node;
++ struct of_phandle_args queue_spec;
++ struct of_phandle_args npe_spec;
++ struct device_node *mdio_np;
++ struct eth_plat_info *plat;
++ int ret;
++
++ plat = devm_kzalloc(dev, sizeof(*plat), GFP_KERNEL);
++ if (!plat)
++ return NULL;
++
++ ret = of_parse_phandle_with_fixed_args(np, "intel,npe-handle", 1, 0,
++ &npe_spec);
++ if (ret) {
++ dev_err(dev, "no NPE engine specified\n");
++ return NULL;
++ }
++ /* NPE ID 0x00, 0x10, 0x20... */
++ plat->npe = (npe_spec.args[0] << 4);
++
++ /* Check if this device has an MDIO bus */
++ mdio_np = of_get_child_by_name(np, "mdio");
++ if (mdio_np) {
++ plat->has_mdio = true;
++ mdio_bus_np = mdio_np;
++ /* DO NOT put the mdio_np, it will be used */
++ }
++
++ /* Get the rx queue as a resource from queue manager */
++ ret = of_parse_phandle_with_fixed_args(np, "queue-rx", 1, 0,
++ &queue_spec);
++ if (ret) {
++ dev_err(dev, "no rx queue phandle\n");
++ return NULL;
++ }
++ plat->rxq = queue_spec.args[0];
++
++ /* Get the txready queue as resource from queue manager */
++ ret = of_parse_phandle_with_fixed_args(np, "queue-txready", 1, 0,
++ &queue_spec);
++ if (ret) {
++ dev_err(dev, "no txready queue phandle\n");
++ return NULL;
++ }
++ plat->txreadyq = queue_spec.args[0];
++
++ return plat;
++}
++#else
++static struct eth_plat_info *ixp4xx_of_get_platdata(struct device *dev)
++{
++ return NULL;
++}
++#endif
++
+ static int ixp4xx_eth_probe(struct platform_device *pdev)
+ {
+ struct phy_device *phydev = NULL;
+ struct device *dev = &pdev->dev;
++ struct device_node *np = dev->of_node;
+ struct eth_plat_info *plat;
+- resource_size_t regs_phys;
+ struct net_device *ndev;
+ struct resource *res;
+ struct port *port;
+ int err;
+
+- plat = dev_get_platdata(dev);
++ if (np) {
++ plat = ixp4xx_of_get_platdata(dev);
++ if (!plat)
++ return -ENODEV;
++ } else {
++ plat = dev_get_platdata(dev);
++ if (!plat)
++ return -ENODEV;
++ plat->npe = pdev->id;
++ switch (plat->npe) {
++ case IXP4XX_ETH_NPEA:
++ /* If the MDIO bus is not up yet, defer probe */
++ break;
++ case IXP4XX_ETH_NPEB:
++ /* On all except IXP43x, NPE-B is used for the MDIO bus.
++ * If there is no NPE-B in the feature set, bail out,
++ * else we have the MDIO bus here.
++ */
++ if (!cpu_is_ixp43x()) {
++ if (!(ixp4xx_read_feature_bits() &
++ IXP4XX_FEATURE_NPEB_ETH0))
++ return -ENODEV;
++ /* Else register the MDIO bus on NPE-B */
++ plat->has_mdio = true;
++ }
++ break;
++ case IXP4XX_ETH_NPEC:
++ /* IXP43x lacks NPE-B and uses NPE-C for the MDIO bus
++ * access, if there is no NPE-C, no bus, nothing works,
++ * so bail out.
++ */
++ if (cpu_is_ixp43x()) {
++ if (!(ixp4xx_read_feature_bits() &
++ IXP4XX_FEATURE_NPEC_ETH))
++ return -ENODEV;
++ /* Else register the MDIO bus on NPE-B */
++ plat->has_mdio = true;
++ }
++ break;
++ default:
++ return -ENODEV;
++ }
++ }
+
+ if (!(ndev = devm_alloc_etherdev(dev, sizeof(struct port))))
+ return -ENOMEM;
+@@ -1377,59 +1479,29 @@ static int ixp4xx_eth_probe(struct platform_device *pdev)
+ SET_NETDEV_DEV(ndev, dev);
+ port = netdev_priv(ndev);
+ port->netdev = ndev;
+- port->id = pdev->id;
++ port->id = plat->npe;
+
+ /* Get the port resource and remap */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+- regs_phys = res->start;
+ port->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(port->regs))
+ return PTR_ERR(port->regs);
+
+- switch (port->id) {
+- case IXP4XX_ETH_NPEA:
+- /* If the MDIO bus is not up yet, defer probe */
+- if (!mdio_bus)
+- return -EPROBE_DEFER;
+- break;
+- case IXP4XX_ETH_NPEB:
+- /*
+- * On all except IXP43x, NPE-B is used for the MDIO bus.
+- * If there is no NPE-B in the feature set, bail out, else
+- * register the MDIO bus.
+- */
+- if (!cpu_is_ixp43x()) {
+- if (!(ixp4xx_read_feature_bits() &
+- IXP4XX_FEATURE_NPEB_ETH0))
+- return -ENODEV;
+- /* Else register the MDIO bus on NPE-B */
+- if ((err = ixp4xx_mdio_register(port->regs)))
+- return err;
+- }
+- if (!mdio_bus)
+- return -EPROBE_DEFER;
+- break;
+- case IXP4XX_ETH_NPEC:
+- /*
+- * IXP43x lacks NPE-B and uses NPE-C for the MDIO bus access,
+- * of there is no NPE-C, no bus, nothing works, so bail out.
+- */
+- if (cpu_is_ixp43x()) {
+- if (!(ixp4xx_read_feature_bits() &
+- IXP4XX_FEATURE_NPEC_ETH))
+- return -ENODEV;
+- /* Else register the MDIO bus on NPE-C */
+- if ((err = ixp4xx_mdio_register(port->regs)))
+- return err;
++ /* Register the MDIO bus if we have it */
++ if (plat->has_mdio) {
++ err = ixp4xx_mdio_register(port->regs);
++ if (err) {
++ dev_err(dev, "failed to register MDIO bus\n");
++ return err;
+ }
+- if (!mdio_bus)
+- return -EPROBE_DEFER;
+- break;
+- default:
+- return -ENODEV;
+ }
++ /* If the instance with the MDIO bus has not yet appeared,
++ * defer probing until it gets probed.
++ */
++ if (!mdio_bus)
++ return -EPROBE_DEFER;
+
+ ndev->netdev_ops = &ixp4xx_netdev_ops;
+ ndev->ethtool_ops = &ixp4xx_ethtool_ops;
+@@ -1440,12 +1512,6 @@ static int ixp4xx_eth_probe(struct platform_device *pdev)
+ if (!(port->npe = npe_request(NPE_ID(port->id))))
+ return -EIO;
+
+- port->mem_res = request_mem_region(regs_phys, REGS_SIZE, ndev->name);
+- if (!port->mem_res) {
+- err = -EBUSY;
+- goto err_npe_rel;
+- }
+-
+ port->plat = plat;
+ npe_port_tab[NPE_ID(port->id)] = port;
+ memcpy(ndev->dev_addr, plat->hwaddr, ETH_ALEN);
+@@ -1458,15 +1524,26 @@ static int ixp4xx_eth_probe(struct platform_device *pdev)
+ __raw_writel(DEFAULT_CORE_CNTRL, &port->regs->core_control);
+ udelay(50);
+
+- phydev = mdiobus_get_phy(mdio_bus, plat->phy);
+- if (IS_ERR(phydev)) {
+- err = PTR_ERR(phydev);
+- goto err_free_mem;
++ if (np) {
++ phydev = of_phy_get_and_connect(ndev, np, ixp4xx_adjust_link);
++ } else {
++ phydev = mdiobus_get_phy(mdio_bus, plat->phy);
++ if (IS_ERR(phydev)) {
++ err = PTR_ERR(phydev);
++ dev_err(dev, "could not connect phydev (%d)\n", err);
++ goto err_free_mem;
++ }
++ err = phy_connect_direct(ndev, phydev, ixp4xx_adjust_link,
++ PHY_INTERFACE_MODE_MII);
++ if (err)
++ goto err_free_mem;
++
+ }
+- err = phy_connect_direct(ndev, phydev, ixp4xx_adjust_link,
+- PHY_INTERFACE_MODE_MII);
+- if (err)
++ if (!phydev) {
++ err = -ENODEV;
++ dev_err(dev, "no phydev\n");
+ goto err_free_mem;
++ }
+
+ phydev->irq = PHY_POLL;
+
+@@ -1482,8 +1559,6 @@ err_phy_dis:
+ phy_disconnect(phydev);
+ err_free_mem:
+ npe_port_tab[NPE_ID(port->id)] = NULL;
+- release_resource(port->mem_res);
+-err_npe_rel:
+ npe_release(port->npe);
+ return err;
+ }
+@@ -1499,12 +1574,21 @@ static int ixp4xx_eth_remove(struct platform_device *pdev)
+ ixp4xx_mdio_remove();
+ npe_port_tab[NPE_ID(port->id)] = NULL;
+ npe_release(port->npe);
+- release_resource(port->mem_res);
+ return 0;
+ }
+
++static const struct of_device_id ixp4xx_eth_of_match[] = {
++ {
++ .compatible = "intel,ixp4xx-ethernet",
++ },
++ { },
++};
++
+ static struct platform_driver ixp4xx_eth_driver = {
+- .driver.name = DRV_NAME,
++ .driver = {
++ .name = DRV_NAME,
++ .of_match_table = of_match_ptr(ixp4xx_eth_of_match),
++ },
+ .probe = ixp4xx_eth_probe,
+ .remove = ixp4xx_eth_remove,
+ };
+diff --git a/include/linux/platform_data/eth_ixp4xx.h b/include/linux/platform_data/eth_ixp4xx.h
+index 6f652ea0c6ae2..114b0940729f5 100644
+--- a/include/linux/platform_data/eth_ixp4xx.h
++++ b/include/linux/platform_data/eth_ixp4xx.h
+@@ -14,6 +14,8 @@ struct eth_plat_info {
+ u8 rxq; /* configurable, currently 0 - 31 only */
+ u8 txreadyq;
+ u8 hwaddr[6];
++ u8 npe; /* NPE instance used by this interface */
++ bool has_mdio; /* If this instance has an MDIO bus */
+ };
+
+ #endif
+--
+cgit 1.2.3-1.el7
+
--- /dev/null
+From patchwork Sun May 9 22:20:52 2021
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+X-Patchwork-Id: 1475993
+Authentication-Results: ozlabs.org;
+ spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org
+ (client-ip=23.128.96.18; helo=vger.kernel.org;
+Authentication-Results: ozlabs.org;
+ dkim=pass (2048-bit key;
+ header.s=google header.b=E/riOnsy;
+ dkim-atps=neutral
+Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
+ by ozlabs.org (Postfix) with ESMTP id 4Fddtp0j1Wz9tlN
+ Sun, 9 May 2021 18:22:08 -0400
+Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47568 "EHLO
+ lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
+ with ESMTP id S229699AbhEIWWI (ORCPT
+Received: from mail-lf1-x133.google.com (mail-lf1-x133.google.com
+ [IPv6:2a00:1450:4864:20::133])
+ by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A138AC061573
+Received: by mail-lf1-x133.google.com with SMTP id i9so13944168lfe.13
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=linaro.org; s=google;
+ h=from:to:cc:subject:date:message-id:in-reply-to:references
+ :mime-version:content-transfer-encoding;
+ bh=gNke3Wg2Ntk9EmsCen3APo/ovEtU9o2zaCxzSbnHEjc=;
+ b=E/riOnsyi0z1LVj74roTINvavou0wy/aH+tbOcLOWKnNl52bsoqcZY0xvpiMktv/xI
+ 5A69KJogEoICmslVDQxGNcmoDP67ec3mVcUBA8xxRYR9REsd0Ht4iaODg/B7CGdwna+6
+ /IkxbUE5wOkjx8zmltpMRmt15a1pg2TIm8XxszspCuyftJnCZZXqfIEX9ZSlwDv0KtEv
+ I7U4XZjHFrW4SENTQ48qVXZKbQpRbK2aTqbRx/sw7pQBA0T0QfNwuDFBowXsY333Iwnm
+ McHw7WiVLXuiB2CucJhrMN7KSHfZGqLxYtrvFHmynWnIHFP04CvJcx98CJzurvf7xFKp
+ DWHw==
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=1e100.net; s=20161025;
+ h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to
+ :references:mime-version:content-transfer-encoding;
+ bh=gNke3Wg2Ntk9EmsCen3APo/ovEtU9o2zaCxzSbnHEjc=;
+ b=gQGZUxhIfdOO0fV0Xr7yBX2ZK+Ly67R1HIS6dxJTF6JbGOaROWc025cnbYcNN7EekA
+ 4v9iJbHa2cO3N103hqRi+KVZeJ9aMX8ltcQDJWHwPpjrPVrC9zBR1umx/r6RxP/7Q7qP
+ 94OVIBzMbnLVzcZtf7yMyjHXEeROUBxtTBSe3lxreqiubxlyfrbvNZfRROJSoMjgEc5P
+ DylF6lgoGgX4RcUHaxY6fTk52BNDDu9x4CO8z9pvbu7U7VZF7BNXYlj7UDt4QXE8km3k
+ 71nIJ5Ov6TZy8prbWcvHltiYX4w+gJGoJdhT7ZbXOuNqk4ozTKp7Q4Eej8y5wgYXRmwg
+ sPtw==
+X-Gm-Message-State: AOAM530kYdWDP7TzhIg7am/8LSXFf2MfdgoYncXdrLr7cZQqNVUKH9lK
+ unbrMSsk3sagwTxVAAlauuvTQux0J76qvg==
+X-Google-Smtp-Source:
+ ABdhPJxWl2X24W0gh7u2BYDaANrXtvvx7rcfVGg85va5f0PByl5HL9ebofT8nhpAYJ9sk+10RCHTjA==
+X-Received: by 2002:a05:6512:3d0f:: with SMTP id
+ d15mr14937066lfv.639.1620598862132;
+ Sun, 09 May 2021 15:21:02 -0700 (PDT)
+Received: from localhost.localdomain
+ (c-fdcc225c.014-348-6c756e10.bbcust.telenor.se. [92.34.204.253])
+ by smtp.gmail.com with ESMTPSA id
+ u12sm2978012ljo.82.2021.05.09.15.21.01
+ (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
+ Sun, 09 May 2021 15:21:01 -0700 (PDT)
+Subject: [PATCH 1/4 v3] ARM/ixp4xx: Move the virtual IObases
+Date: Mon, 10 May 2021 00:20:52 +0200
+X-Mailer: git-send-email 2.30.2
+MIME-Version: 1.0
+Precedence: bulk
+List-ID: <linux-pci.vger.kernel.org>
+
+UART1, UART2 and the expansion bus config registers
+are the only registers mapped in a fixed location
+when using device tree.
+
+For device tree we also want to get rid of the custom
+<mach/io.h> for IXP4xx. So we need to undefine
+CONFIG_NEED_MACH_IO_H. Doing that activates the fixed
+mapping of the PCI IO space to PCI_IO_VIRT_BASE which
+is hardcoded to 0xFEE00000 and this would collide with
+the old fixed mappings.
+
+Move the fixed virtual IO base address from 0xFEF00000
+to 0xFEC00000 in order to avoid the collision.
+
+For the OF-only boot path let's even cut the reliance
+on <mach/io.h> and just hardcode the one single virtbase
+we need apart from the UART, which is hardcoded in
+Kconfig.debug.
+
+---
+ChangeLog v2->v3:
+- No changes, resend with the rest of the patches.
+ChangeLog v1->v2:
+- Instead of handling the UART and expansion ports
+ separately just move all peripherals from 0xfef00000
+ to 0xfec00000.
+- Stay out of the fixmap area, that area has special
+ uses.
+
+PCI maintainers: this patch is mostly FYI, will be
+merged through ARM SoC
+---
+ arch/arm/Kconfig.debug | 4 ++--
+ arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h | 7 ++++---
+ arch/arm/mach-ixp4xx/ixp4xx-of.c | 8 ++++++--
+ 3 files changed, 12 insertions(+), 7 deletions(-)
+
+diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
+index 9e0b5e7f12af..f672b23301e6 100644
+--- a/arch/arm/Kconfig.debug
++++ b/arch/arm/Kconfig.debug
+@@ -1803,8 +1803,8 @@ config DEBUG_UART_VIRT
+ default 0xfedc0000 if DEBUG_EP93XX
+ default 0xfee003f8 if DEBUG_FOOTBRIDGE_COM1
+ default 0xfee20000 if DEBUG_NSPIRE_CLASSIC_UART || DEBUG_NSPIRE_CX_UART
+- default 0xfef00000 if ARCH_IXP4XX && !CPU_BIG_ENDIAN
+- default 0xfef00003 if ARCH_IXP4XX && CPU_BIG_ENDIAN
++ default 0xfec00000 if ARCH_IXP4XX && !CPU_BIG_ENDIAN
++ default 0xfec00003 if ARCH_IXP4XX && CPU_BIG_ENDIAN
+ default 0xfef36000 if DEBUG_HIGHBANK_UART
+ default 0xfefb0000 if DEBUG_OMAP1UART1 || DEBUG_OMAP7XXUART1
+ default 0xfefb0800 if DEBUG_OMAP1UART2 || DEBUG_OMAP7XXUART2
+diff --git a/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h b/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h
+index 708d085ce39f..f375c1c005d4 100644
+--- a/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h
++++ b/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h
+@@ -45,21 +45,21 @@
+ * it can be used with the low-level debug code.
+ */
+ #define IXP4XX_PERIPHERAL_BASE_PHYS 0xC8000000
+-#define IXP4XX_PERIPHERAL_BASE_VIRT IOMEM(0xFEF00000)
++#define IXP4XX_PERIPHERAL_BASE_VIRT IOMEM(0xFEC00000)
+ #define IXP4XX_PERIPHERAL_REGION_SIZE 0x00013000
+
+ /*
+ * PCI Config registers
+ */
+ #define IXP4XX_PCI_CFG_BASE_PHYS 0xC0000000
+-#define IXP4XX_PCI_CFG_BASE_VIRT IOMEM(0xFEF13000)
++#define IXP4XX_PCI_CFG_BASE_VIRT IOMEM(0xFEC13000)
+ #define IXP4XX_PCI_CFG_REGION_SIZE 0x00001000
+
+ /*
+ * Expansion BUS Configuration registers
+ */
+ #define IXP4XX_EXP_CFG_BASE_PHYS 0xC4000000
+-#define IXP4XX_EXP_CFG_BASE_VIRT 0xFEF14000
++#define IXP4XX_EXP_CFG_BASE_VIRT 0xFEC14000
+ #define IXP4XX_EXP_CFG_REGION_SIZE 0x00001000
+
+ #define IXP4XX_EXP_CS0_OFFSET 0x00
+@@ -120,6 +120,7 @@
+ #define IXP4XX_SSP_BASE_PHYS (IXP4XX_PERIPHERAL_BASE_PHYS + 0x12000)
+
+
++/* The UART is explicitly put in the beginning of fixmap */
+ #define IXP4XX_UART1_BASE_VIRT (IXP4XX_PERIPHERAL_BASE_VIRT + 0x0000)
+ #define IXP4XX_UART2_BASE_VIRT (IXP4XX_PERIPHERAL_BASE_VIRT + 0x1000)
+ #define IXP4XX_PMU_BASE_VIRT (IXP4XX_PERIPHERAL_BASE_VIRT + 0x2000)
+diff --git a/arch/arm/mach-ixp4xx/ixp4xx-of.c b/arch/arm/mach-ixp4xx/ixp4xx-of.c
+index 7449b8319c8a..f9904716ec7f 100644
+--- a/arch/arm/mach-ixp4xx/ixp4xx-of.c
++++ b/arch/arm/mach-ixp4xx/ixp4xx-of.c
+@@ -9,8 +9,12 @@
+ #include <asm/mach/arch.h>
+ #include <asm/mach/map.h>
+
+-#include <mach/hardware.h>
+-#include <mach/ixp4xx-regs.h>
++/*
++ * These are the only fixed phys to virt mappings we ever need
++ * we put it right after the UART mapping at 0xffc80000-0xffc81fff
++ */
++#define IXP4XX_EXP_CFG_BASE_PHYS 0xC4000000
++#define IXP4XX_EXP_CFG_BASE_VIRT 0xFEC14000
+
+ static struct map_desc ixp4xx_of_io_desc[] __initdata = {
+ /*
+
+From patchwork Sun May 9 22:20:53 2021
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+X-Patchwork-Id: 1475994
+Authentication-Results: ozlabs.org;
+ spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org
+ (client-ip=23.128.96.18; helo=vger.kernel.org;
+Authentication-Results: ozlabs.org;
+ dkim=pass (2048-bit key;
+ header.s=google header.b=N5ug85eb;
+ dkim-atps=neutral
+Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
+ by ozlabs.org (Postfix) with ESMTP id 4Fddtq1Hkwz9tlN
+ Sun, 9 May 2021 18:22:09 -0400
+Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47576 "EHLO
+ lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
+ with ESMTP id S229973AbhEIWWJ (ORCPT
+Received: from mail-lf1-x134.google.com (mail-lf1-x134.google.com
+ [IPv6:2a00:1450:4864:20::134])
+ by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5AA24C061573
+Received: by mail-lf1-x134.google.com with SMTP id c3so20502850lfs.7
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=linaro.org; s=google;
+ h=from:to:cc:subject:date:message-id:in-reply-to:references
+ :mime-version:content-transfer-encoding;
+ bh=zf+S7hvXyl8k4/OTCUtnv5DVgW50Qe9kDD2QLo75Eic=;
+ b=N5ug85ebB91Ju07EvncOpKAYdBT1sOVqmHUdsUsgsrOoJuEvxnJRiIDNV/Y8s9Bvm5
+ VVL7HRX+537AbZ6w1RAzREwmyBTh8rYUbL2f1MJ44NmBGHU9DjVjljIL96et25XIxj67
+ u9fH4cX3Fb5h8DOSSXPBYvx0YNewx5q6wA8sD7/Y0lYTEmO7/G0lyoDI4RNqdgBfCYdw
+ 1H6Z5G3MfB8HgOh6ztH2tD0gLHwD0rV/RUL96YWPCdwdIbeQnzNo2KNzVYgmiLQRKzDN
+ HIXjQic8B9MMn6rMnBqwuAcK4JVQI7AO3HvTQvhAUmG5pELC4Ey8LUVmcyCBXomxfhFr
+ s8nw==
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=1e100.net; s=20161025;
+ h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to
+ :references:mime-version:content-transfer-encoding;
+ bh=zf+S7hvXyl8k4/OTCUtnv5DVgW50Qe9kDD2QLo75Eic=;
+ b=ODkBlC9ARXGLWzrsUK1q2U9tZ9H2C/HXiG79lJBZ52kvH4qRKj9opOFobqwCpRPOUb
+ eKdvxZkdIGlXPB4G52PuARuquLtUEj6v8t8NfF3THjDlNLymO6bz2rqeOMwJEORe/Obj
+ 8HZZVM2+wtP4Z9GDxKV/YMq9MEENiq2AZEyK9/IT25u/i/71pIJPXJWDM6JwhR/ODrYl
+ Z1NXdsdC3+u4DDwW1pGadU1t7WsWlF3SR6UEaDPi8UkJslm5iWUu5MD0jEJaivZ4NnhC
+ kjMNYK6//ettPbJ86awcZ0AHov6klD8m8WNLULagoF7h7jA11bPydSOEUoQ/IlC0YSZg
+ k8kA==
+X-Gm-Message-State: AOAM531uOKv/GEXmQTZdvjGDPQlBFdnU9sLW7eTB83j24nvy2gr/SzWN
+ JrgK6jV6FZipJKPniX0yjkT50g==
+X-Google-Smtp-Source:
+ ABdhPJygLiDXDZtYmq8f0GFbVIJ8L9U7adTeiB5wUhFzMFYjEzXyvieC46iqA5iRNgU8NEkBfFOpfw==
+X-Received: by 2002:a05:6512:12d0:: with SMTP id
+ p16mr14832876lfg.232.1620598863857;
+ Sun, 09 May 2021 15:21:03 -0700 (PDT)
+Received: from localhost.localdomain
+ (c-fdcc225c.014-348-6c756e10.bbcust.telenor.se. [92.34.204.253])
+ by smtp.gmail.com with ESMTPSA id
+ u12sm2978012ljo.82.2021.05.09.15.21.02
+ (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
+ Sun, 09 May 2021 15:21:03 -0700 (PDT)
+Subject: [PATCH 2/4 v3] ARM/ixp4xx: Make NEED_MACH_IO_H optional
+Date: Mon, 10 May 2021 00:20:53 +0200
+X-Mailer: git-send-email 2.30.2
+MIME-Version: 1.0
+Precedence: bulk
+List-ID: <linux-pci.vger.kernel.org>
+
+In order to create a proper PCI driver for the IXP4xx
+we need to make the old PCI driver and its reliance
+on <mach/io.h> optional.
+
+Create a new Kconfig symbol for the legacy PCI driver
+IXP4XX_PCI_LEGACY and only activate NEED_MACH_IO_H
+for this driver.
+
+A few files need to be adjusted to explicitly include
+the <mach/hardware.h> and <mach/cpu.h> headers that
+they previously obtained implicitly using <linux/io.h>
+that would include <mach/io.h> and in turn include
+these two headers.
+
+This breaks our reliance on the old PCI and indirect
+PCI support so we can reimplement a proper purely
+DT-based driver in the PCI subsystem.
+
+---
+ChangeLog v2->v3:
+- No changes, resend with the rest of the patches.
+
+PCI maintainers: this patch is mostly FYI, will be
+merged through ARM SoC
+---
+ arch/arm/Kconfig | 3 ++-
+ arch/arm/mach-ixp4xx/Kconfig | 33 +++++++++++++++---------
+ arch/arm/mach-ixp4xx/common.c | 1 -
+ arch/arm/mach-ixp4xx/fsg-setup.c | 1 +
+ arch/arm/mach-ixp4xx/nas100d-setup.c | 1 +
+ arch/arm/mach-ixp4xx/nslu2-setup.c | 1 +
+ drivers/ata/pata_ixp4xx_cf.c | 1 +
+ drivers/net/ethernet/xscale/ixp4xx_eth.c | 1 +
+ drivers/soc/ixp4xx/ixp4xx-npe.c | 2 ++
+ drivers/soc/ixp4xx/ixp4xx-qmgr.c | 2 ++
+ 10 files changed, 32 insertions(+), 14 deletions(-)
+
+diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
+index 853aab5ab327..4ca2ab19d265 100644
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -394,7 +394,8 @@ config ARCH_IXP4XX
+ select HAVE_PCI
+ select IXP4XX_IRQ
+ select IXP4XX_TIMER
+- select NEED_MACH_IO_H
++ # With the new PCI driver this is not needed
++ select NEED_MACH_IO_H if PCI_IXP4XX_LEGACY
+ select USB_EHCI_BIG_ENDIAN_DESC
+ select USB_EHCI_BIG_ENDIAN_MMIO
+ help
+diff --git a/arch/arm/mach-ixp4xx/Kconfig b/arch/arm/mach-ixp4xx/Kconfig
+index 165c184801e1..cabb37232704 100644
+--- a/arch/arm/mach-ixp4xx/Kconfig
++++ b/arch/arm/mach-ixp4xx/Kconfig
+@@ -20,7 +20,7 @@ config MACH_IXP4XX_OF
+ config MACH_NSLU2
+ bool
+ prompt "Linksys NSLU2"
+- select FORCE_PCI
++ depends on IXP4XX_PCI_LEGACY
+ help
+ Say 'Y' here if you want your kernel to support Linksys's
+ NSLU2 NAS device. For more information on this platform,
+@@ -28,7 +28,7 @@ config MACH_NSLU2
+
+ config MACH_AVILA
+ bool "Avila"
+- select FORCE_PCI
++ depends on IXP4XX_PCI_LEGACY
+ help
+ Say 'Y' here if you want your kernel to support the Gateworks
+ Avila Network Platform. For more information on this platform,
+@@ -44,7 +44,7 @@ config MACH_LOFT
+
+ config ARCH_ADI_COYOTE
+ bool "Coyote"
+- select FORCE_PCI
++ depends on IXP4XX_PCI_LEGACY
+ help
+ Say 'Y' here if you want your kernel to support the ADI
+ Engineering Coyote Gateway Reference Platform. For more
+@@ -52,7 +52,7 @@ config ARCH_ADI_COYOTE
+
+ config MACH_GATEWAY7001
+ bool "Gateway 7001"
+- select FORCE_PCI
++ depends on IXP4XX_PCI_LEGACY
+ help
+ Say 'Y' here if you want your kernel to support Gateway's
+ 7001 Access Point. For more information on this platform,
+@@ -60,7 +60,7 @@ config MACH_GATEWAY7001
+
+ config MACH_WG302V2
+ bool "Netgear WG302 v2 / WAG302 v2"
+- select FORCE_PCI
++ depends on IXP4XX_PCI_LEGACY
+ help
+ Say 'Y' here if you want your kernel to support Netgear's
+ WG302 v2 or WAG302 v2 Access Points. For more information
+@@ -68,6 +68,7 @@ config MACH_WG302V2
+
+ config ARCH_IXDP425
+ bool "IXDP425"
++ depends on IXP4XX_PCI_LEGACY
+ help
+ Say 'Y' here if you want your kernel to support Intel's
+ IXDP425 Development Platform (Also known as Richfield).
+@@ -75,6 +76,7 @@ config ARCH_IXDP425
+
+ config MACH_IXDPG425
+ bool "IXDPG425"
++ depends on IXP4XX_PCI_LEGACY
+ help
+ Say 'Y' here if you want your kernel to support Intel's
+ IXDPG425 Development Platform (Also known as Montajade).
+@@ -120,7 +122,7 @@ config ARCH_PRPMC1100
+ config MACH_NAS100D
+ bool
+ prompt "NAS100D"
+- select FORCE_PCI
++ depends on IXP4XX_PCI_LEGACY
+ help
+ Say 'Y' here if you want your kernel to support Iomega's
+ NAS 100d device. For more information on this platform,
+@@ -129,7 +131,7 @@ config MACH_NAS100D
+ config MACH_DSMG600
+ bool
+ prompt "D-Link DSM-G600 RevA"
+- select FORCE_PCI
++ depends on IXP4XX_PCI_LEGACY
+ help
+ Say 'Y' here if you want your kernel to support D-Link's
+ DSM-G600 RevA device. For more information on this platform,
+@@ -143,7 +145,7 @@ config ARCH_IXDP4XX
+ config MACH_FSG
+ bool
+ prompt "Freecom FSG-3"
+- select FORCE_PCI
++ depends on IXP4XX_PCI_LEGACY
+ help
+ Say 'Y' here if you want your kernel to support Freecom's
+ FSG-3 device. For more information on this platform,
+@@ -152,7 +154,7 @@ config MACH_FSG
+ config MACH_ARCOM_VULCAN
+ bool
+ prompt "Arcom/Eurotech Vulcan"
+- select FORCE_PCI
++ depends on IXP4XX_PCI_LEGACY
+ help
+ Say 'Y' here if you want your kernel to support Arcom's
+ Vulcan board.
+@@ -173,7 +175,7 @@ config CPU_IXP43X
+ config MACH_GTWX5715
+ bool "Gemtek WX5715 (Linksys WRV54G)"
+ depends on ARCH_IXP4XX
+- select FORCE_PCI
++ depends on IXP4XX_PCI_LEGACY
+ help
+ This board is currently inside the Linksys WRV54G Gateways.
+
+@@ -196,7 +198,7 @@ config MACH_DEVIXP
+
+ config MACH_MICCPT
+ bool "Omicron MICCPT"
+- select FORCE_PCI
++ depends on IXP4XX_PCI_LEGACY
+ help
+ Say 'Y' here if you want your kernel to support the MICCPT
+ board from OMICRON electronics GmbH.
+@@ -209,9 +211,16 @@ config MACH_MIC256
+
+ comment "IXP4xx Options"
+
++config IXP4XX_PCI_LEGACY
++ bool "IXP4xx legacy PCI driver support"
++ depends on PCI
++ help
++ Selects legacy PCI driver.
++ Not recommended for new development.
++
+ config IXP4XX_INDIRECT_PCI
+ bool "Use indirect PCI memory access"
+- depends on PCI
++ depends on IXP4XX_PCI_LEGACY
+ help
+ IXP4xx provides two methods of accessing PCI memory space:
+
+diff --git a/arch/arm/mach-ixp4xx/common.c b/arch/arm/mach-ixp4xx/common.c
+index 000f672a94c9..431da1b4f6bd 100644
+--- a/arch/arm/mach-ixp4xx/common.c
++++ b/arch/arm/mach-ixp4xx/common.c
+@@ -32,7 +32,6 @@
+ #include <linux/dma-map-ops.h>
+ #include <mach/udc.h>
+ #include <mach/hardware.h>
+-#include <mach/io.h>
+ #include <linux/uaccess.h>
+ #include <asm/page.h>
+ #include <asm/exception.h>
+diff --git a/arch/arm/mach-ixp4xx/fsg-setup.c b/arch/arm/mach-ixp4xx/fsg-setup.c
+index 507ee3878769..844329c5610d 100644
+--- a/arch/arm/mach-ixp4xx/fsg-setup.c
++++ b/arch/arm/mach-ixp4xx/fsg-setup.c
+@@ -28,6 +28,7 @@
+ #include <asm/mach-types.h>
+ #include <asm/mach/arch.h>
+ #include <asm/mach/flash.h>
++#include <mach/hardware.h>
+
+ #include "irqs.h"
+
+diff --git a/arch/arm/mach-ixp4xx/nas100d-setup.c b/arch/arm/mach-ixp4xx/nas100d-setup.c
+index 6959ad2e3aec..6133cf01cbe4 100644
+--- a/arch/arm/mach-ixp4xx/nas100d-setup.c
++++ b/arch/arm/mach-ixp4xx/nas100d-setup.c
+@@ -33,6 +33,7 @@
+ #include <asm/mach-types.h>
+ #include <asm/mach/arch.h>
+ #include <asm/mach/flash.h>
++#include <mach/hardware.h>
+
+ #include "irqs.h"
+
+diff --git a/arch/arm/mach-ixp4xx/nslu2-setup.c b/arch/arm/mach-ixp4xx/nslu2-setup.c
+index a428bb918703..8526a70e401b 100644
+--- a/arch/arm/mach-ixp4xx/nslu2-setup.c
++++ b/arch/arm/mach-ixp4xx/nslu2-setup.c
+@@ -31,6 +31,7 @@
+ #include <asm/mach/arch.h>
+ #include <asm/mach/flash.h>
+ #include <asm/mach/time.h>
++#include <mach/hardware.h>
+
+ #include "irqs.h"
+
+diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c
+index d1644a8ef9fa..9929d0150141 100644
+--- a/drivers/ata/pata_ixp4xx_cf.c
++++ b/drivers/ata/pata_ixp4xx_cf.c
+@@ -18,6 +18,7 @@
+ #include <linux/irq.h>
+ #include <linux/platform_device.h>
+ #include <scsi/scsi_host.h>
++#include <mach/hardware.h>
+
+ #define DRV_NAME "pata_ixp4xx_cf"
+ #define DRV_VERSION "0.2"
+diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c
+index 0152f1e70783..88ad1639a7da 100644
+--- a/drivers/net/ethernet/xscale/ixp4xx_eth.c
++++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c
+@@ -36,6 +36,7 @@
+ #include <linux/module.h>
+ #include <linux/soc/ixp4xx/npe.h>
+ #include <linux/soc/ixp4xx/qmgr.h>
++#include <mach/hardware.h>
+
+ #include "ixp46x_ts.h"
+
+diff --git a/drivers/soc/ixp4xx/ixp4xx-npe.c b/drivers/soc/ixp4xx/ixp4xx-npe.c
+index ec90b44fa0cd..0a16ac46ab59 100644
+--- a/drivers/soc/ixp4xx/ixp4xx-npe.c
++++ b/drivers/soc/ixp4xx/ixp4xx-npe.c
+@@ -20,6 +20,8 @@
+ #include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/soc/ixp4xx/npe.h>
++#include <mach/hardware.h>
++#include <mach/cpu.h>
+
+ #define DEBUG_MSG 0
+ #define DEBUG_FW 0
+diff --git a/drivers/soc/ixp4xx/ixp4xx-qmgr.c b/drivers/soc/ixp4xx/ixp4xx-qmgr.c
+index 8c968382cea7..1b1631ac0438 100644
+--- a/drivers/soc/ixp4xx/ixp4xx-qmgr.c
++++ b/drivers/soc/ixp4xx/ixp4xx-qmgr.c
+@@ -12,6 +12,8 @@
+ #include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/soc/ixp4xx/qmgr.h>
++#include <mach/hardware.h>
++#include <mach/cpu.h>
+
+ static struct qmgr_regs __iomem *qmgr_regs;
+ static int qmgr_irq_1;
+
+From patchwork Sun May 9 22:20:54 2021
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+X-Patchwork-Id: 1475995
+Authentication-Results: ozlabs.org;
+ spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org
+ (client-ip=23.128.96.18; helo=vger.kernel.org;
+Authentication-Results: ozlabs.org;
+ dkim=pass (2048-bit key;
+ header.s=google header.b=bHKoczEP;
+ dkim-atps=neutral
+Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
+ by ozlabs.org (Postfix) with ESMTP id 4Fddtt39Ttz9tlN
+ Sun, 9 May 2021 18:22:13 -0400
+Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47588 "EHLO
+ lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
+ with ESMTP id S229977AbhEIWWL (ORCPT
+Received: from mail-lf1-x12e.google.com (mail-lf1-x12e.google.com
+ [IPv6:2a00:1450:4864:20::12e])
+ by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1420EC061573
+Received: by mail-lf1-x12e.google.com with SMTP id 7so43886lfp.9
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=linaro.org; s=google;
+ h=from:to:cc:subject:date:message-id:in-reply-to:references
+ :mime-version:content-transfer-encoding;
+ bh=sjNmqgcstGAxj0Xo8lZD7LTDFmxFM0Yqxj4VjUg9Qdw=;
+ b=bHKoczEPIMs4+OuEfUaeip+Kx4QcMrQRhH1/FpV85lJgzzgdMedlKhBPf074UYfLXT
+ yAuzfmjaqjz/jNdg4XUgVKAXJ+xR0F0GQ6fx0fQE7onCHFb2cOE0QDcduG3phiVWb3ta
+ wNR5Fmxbm4wXPSCmpr222cLSxbQ3uA7RtL9uFNAW4dhzAziKqKHTe5/ZYMbq9pvNTuHT
+ 3J3jcOMat4gxGNc03EJvYmzShUEOOM+jrqqw5PPyFPugSE++IImG/5VXpLuEJ5Scg+uZ
+ NWwWu2or1nv3u9ShsCDAdR7MBP17UnpV5dRGhF3XzGZ3w6XLsNZK3i/i8AsYQQmwg3vB
+ IV6A==
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=1e100.net; s=20161025;
+ h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to
+ :references:mime-version:content-transfer-encoding;
+ bh=sjNmqgcstGAxj0Xo8lZD7LTDFmxFM0Yqxj4VjUg9Qdw=;
+ b=qKHwpcfwF9facukQef6HgAyvqUs/GYAgOZJ/wICu5UWS07w2N3LOqQZaARkc11iYpu
+ OeLvtX5Glr6DxJtjXjEigBWeiI3xgnl0+gbJCF1A6N2c0rVvJKi2thyDJa1zwM12F8K3
+ lIydjWaz+TKrW0DclnwFi16LM5wU/YX/YgqoWmdqM2gvqfgojNz62277My15evxCeoiD
+ M+uo7bm3QNnm2GRwynBS9S5hgWAHCdgEyKzPhVg/GIE9L0ufct+MJ1WqIha7SVbVlG4g
+ 7RkAUnIEhbVpRmYQXM5ILnV1dqaD7N+4xHcveqCoWSgCD2qThyO4C+V6XpJA5wS3Emh8
+ vlFA==
+X-Gm-Message-State: AOAM530ZNyrHdlIT42YWjuOs9q6OS58z7kHPvNflUcpx+Bp/A5iz/Hrv
+ uf3wqWDg8neJ6WX6nmj/D6KWNw==
+X-Google-Smtp-Source:
+ ABdhPJw6BKQQGcIDZdsMlJ9qaiNbzUsRzwi01N8Yn7U0BfQ91i9Ou6kqCQL4toNmGMfOdugqMKV50A==
+X-Received: by 2002:a05:6512:acc:: with SMTP id
+ n12mr14168468lfu.408.1620598865621;
+ Sun, 09 May 2021 15:21:05 -0700 (PDT)
+Received: from localhost.localdomain
+ (c-fdcc225c.014-348-6c756e10.bbcust.telenor.se. [92.34.204.253])
+ by smtp.gmail.com with ESMTPSA id
+ u12sm2978012ljo.82.2021.05.09.15.21.04
+ (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
+ Sun, 09 May 2021 15:21:05 -0700 (PDT)
+Subject: [PATCH 3/4 v3] PCI: ixp4xx: Add device tree bindings for IXP4xx
+Date: Mon, 10 May 2021 00:20:54 +0200
+X-Mailer: git-send-email 2.30.2
+MIME-Version: 1.0
+Precedence: bulk
+List-ID: <linux-pci.vger.kernel.org>
+
+This adds device tree bindings for the Intel IXP4xx
+PCI controller which can be used as both host and
+option.
+
+---
+ChangeLog v2->v3:
+- Drop ranges, these are part of pci-bus.yaml
+- Drop status = "disabled" on the node
+ChangeLog v1->v2:
+- Add the three controller interrupts to the binding.
+
+PCI maintainers: mainly looking for a review and ACK (if
+you care about DT bindings) the patch will be merged
+through ARM SoC.
+---
+ .../bindings/pci/intel,ixp4xx-pci.yaml | 100 ++++++++++++++++++
+ 1 file changed, 100 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/pci/intel,ixp4xx-pci.yaml
+
+diff --git a/Documentation/devicetree/bindings/pci/intel,ixp4xx-pci.yaml b/Documentation/devicetree/bindings/pci/intel,ixp4xx-pci.yaml
+new file mode 100644
+index 000000000000..debfb54a8042
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pci/intel,ixp4xx-pci.yaml
+@@ -0,0 +1,100 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/pci/intel,ixp4xx-pci.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Intel IXP4xx PCI controller
++
++maintainers:
++
++description: PCI host controller found in the Intel IXP4xx SoC series.
++
++allOf:
++ - $ref: /schemas/pci/pci-bus.yaml#
++
++properties:
++ compatible:
++ items:
++ - enum:
++ - intel,ixp42x-pci
++ - intel,ixp43x-pci
++ description: The two supported variants are ixp42x and ixp43x,
++ though more variants may exist.
++
++ reg:
++ items:
++ - description: IXP4xx-specific registers
++
++ interrupts:
++ items:
++ - description: Main PCI interrupt
++ - description: PCI DMA interrupt 1
++ - description: PCI DMA interrupt 2
++
++ ranges:
++ maxItems: 2
++ description: Typically one memory range of 64MB and one IO
++ space range of 64KB.
++
++ dma-ranges:
++ maxItems: 1
++ description: The DMA range tells the PCI host which addresses
++ the RAM is at. It can map only 64MB so if the RAM is bigger
++ than 64MB the DMA access has to be restricted to these
++ addresses.
++
++ "#interrupt-cells": true
++
++ interrupt-map: true
++
++ interrupt-map-mask:
++ items:
++ - const: 0xf800
++ - const: 0
++ - const: 0
++ - const: 7
++
++required:
++ - compatible
++ - reg
++ - dma-ranges
++ - "#interrupt-cells"
++ - interrupt-map
++ - interrupt-map-mask
++
++unevaluatedProperties: false
++
++examples:
++ - |
++ pci@c0000000 {
++ compatible = "intel,ixp43x-pci";
++ reg = <0xc0000000 0x1000>;
++ #address-cells = <3>;
++ #size-cells = <2>;
++ device_type = "pci";
++ bus-range = <0x00 0xff>;
++
++ ranges =
++ <0x02000000 0 0x48000000 0x48000000 0 0x04000000>,
++ <0x01000000 0 0x00000000 0x4c000000 0 0x00010000>;
++ dma-ranges =
++ <0x02000000 0 0x00000000 0x00000000 0 0x04000000>;
++
++ #interrupt-cells = <1>;
++ interrupt-map-mask = <0xf800 0 0 7>;
++ interrupt-map =
++ <0x0800 0 0 1 &gpio0 11 3>, /* INT A on slot 1 is irq 11 */
++ <0x0800 0 0 2 &gpio0 10 3>, /* INT B on slot 1 is irq 10 */
++ <0x0800 0 0 3 &gpio0 9 3>, /* INT C on slot 1 is irq 9 */
++ <0x0800 0 0 4 &gpio0 8 3>, /* INT D on slot 1 is irq 8 */
++ <0x1000 0 0 1 &gpio0 10 3>, /* INT A on slot 2 is irq 10 */
++ <0x1000 0 0 2 &gpio0 9 3>, /* INT B on slot 2 is irq 9 */
++ <0x1000 0 0 3 &gpio0 8 3>, /* INT C on slot 2 is irq 8 */
++ <0x1000 0 0 4 &gpio0 11 3>, /* INT D on slot 2 is irq 11 */
++ <0x1800 0 0 1 &gpio0 9 3>, /* INT A on slot 3 is irq 9 */
++ <0x1800 0 0 2 &gpio0 8 3>, /* INT B on slot 3 is irq 8 */
++ <0x1800 0 0 3 &gpio0 11 3>, /* INT C on slot 3 is irq 11 */
++ <0x1800 0 0 4 &gpio0 10 3>; /* INT D on slot 3 is irq 10 */
++ };
+
+From patchwork Sun May 9 22:20:55 2021
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+X-Patchwork-Id: 1475997
+Authentication-Results: ozlabs.org;
+ spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org
+ (client-ip=23.128.96.18; helo=vger.kernel.org;
+Authentication-Results: ozlabs.org;
+ dkim=pass (2048-bit key;
+ header.s=google header.b=dNbB4YJO;
+ dkim-atps=neutral
+Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
+ by ozlabs.org (Postfix) with ESMTP id 4Fddtv292vz9tlN
+ Sun, 9 May 2021 18:22:14 -0400
+Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47600 "EHLO
+ lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
+ with ESMTP id S229847AbhEIWWN (ORCPT
+Received: from mail-lf1-x134.google.com (mail-lf1-x134.google.com
+ [IPv6:2a00:1450:4864:20::134])
+ by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 76657C061573
+Received: by mail-lf1-x134.google.com with SMTP id x20so20529051lfu.6
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=linaro.org; s=google;
+ h=from:to:cc:subject:date:message-id:in-reply-to:references
+ :mime-version:content-transfer-encoding;
+ bh=6FxZJIZJKsIXSdwmLwW0dcAjXO7AU+sUjkMzDxOe2zQ=;
+ b=dNbB4YJOWtRNhJm2wJweaJu9aYxZOM2fQ7lxGl8DC5V8DlItGRk2z+ApMZpVuyVVyr
+ aQWR+wrYP96QuP4elWugjIGZ7F/T0VuhSxIMEZLArr4D4+rL90s+GIRCZuA7iE8qSVCw
+ 6kNNUznDcB5FUIPajM7zM938PMVIlfI1r39GZK7MHpFnXEo51h53r8hbPnFmGN06XhpI
+ ccPVmXVL9G43jlFzEQQg0Q6vZOW8XqlJwILw3cQLu6A5kYmPIMAgy2wtlE4SgeKv5obp
+ vN39K9uKdPqwS9SDsg+rWmCFSk4YKLBadKW8gOXn0ob7wWnuoayB8G2m/4wYLajt4c9o
+ GFEg==
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=1e100.net; s=20161025;
+ h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to
+ :references:mime-version:content-transfer-encoding;
+ bh=6FxZJIZJKsIXSdwmLwW0dcAjXO7AU+sUjkMzDxOe2zQ=;
+ b=l5ul/r0sOjlR/8YAxEbdFyFTEBMweKk74SPObV43bMdfHGidfoi9cRqeYKWHp9I+Yu
+ Pg7jTajBGOXAxYAjMGoTUF9XbqxPUixJEVQ6DG9mLlamxpeqnQkDqpmFzArsc6xZMeM/
+ iQuaI7vOiqrPaWyFjFHgQJwBBYCqBnqRJoG5s2wCezNv6DkMp+l31EPZr8KV6DsXL+0Q
+ gzOBZW7rjmVxpDRimUIlVInQZOlA2vCf5VKWkvdn9HF4ZJcMnKS5x/NisQQwstxPgqtP
+ SRahIfGTyjQFCpUsGvlUuJFbf7/9bdfK5sw7b1S2YBcKqC5ggltF87XnFdDjjrkpD1om
+ t/ZQ==
+X-Gm-Message-State: AOAM532pLFO1Euiz1u0BRgooHgFfEUG8sQqvfSZFWCMMg/c4DbFTF9YV
+ f1cXo0YvCu8VyEDFR7VaXRo6B9TBxftrig==
+X-Google-Smtp-Source:
+ ABdhPJwsZG5hhkYgAVVyr3BgGn68+GmnJz5Nc0ymbw0PwuCzcgUHcQuJAhfrtNNK8UQUZHvpOfAQaA==
+X-Received: by 2002:ac2:528f:: with SMTP id
+ q15mr15401765lfm.145.1620598866907;
+ Sun, 09 May 2021 15:21:06 -0700 (PDT)
+Received: from localhost.localdomain
+ (c-fdcc225c.014-348-6c756e10.bbcust.telenor.se. [92.34.204.253])
+ by smtp.gmail.com with ESMTPSA id
+ u12sm2978012ljo.82.2021.05.09.15.21.06
+ (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
+ Sun, 09 May 2021 15:21:06 -0700 (PDT)
+Subject: [PATCH 4/4 v3] PCI: ixp4xx: Add a new driver for IXP4xx
+Date: Mon, 10 May 2021 00:20:55 +0200
+X-Mailer: git-send-email 2.30.2
+MIME-Version: 1.0
+Precedence: bulk
+List-ID: <linux-pci.vger.kernel.org>
+
+This adds a new PCI controller driver for the Intel IXP4xx
+(IX425, IXP435 etc), based on the XScale microarchitecture.
+
+This replaces the old driver in arch/arm/mach-ixp4xx/common-pci.c
+which utilized the ARM-specific BIOS32 PCI framework,
+and all parameterization for such things as memory and
+IO space as well as interrupt swizzling is done from the
+device tree.
+
+The __raw_writel() and __raw_readl() are used for accessing
+the PCI controller for the same reason that these accessors
+are used in the timer, IRQ and GPIO drivers: the platform
+will alter its address bus pattern based on whether the
+system is booted in big- or little-endian mode. For this
+reason all register on IXP4xx must always be accessed in
+native (CPU) endianness.
+
+This driver supports 64MB of PCI memory space, but not the
+indirect access of 1GB that is available in the old driver.
+We can address that later if and only if there are users
+that need all 1GB of PCI address space.
+
+Tested by booting the NSLU2, attaching a USB stick, mounting
+and browsing the drive.
+
+---
+ChangeLog v2->v3:
+- Fix a double assignment of .suppress_bind_attrs
+ChangeLog v1->v2:
+- Add dependencies on ARM to Kconfig since we are regisering
+ and ARM only abort handler.
+- Create ixp4xx_readl() and ixp4xx_writel() static inline
+ wrappers around the __raw_readl() and __raw_writel() calls
+ with a big comment block explaining what is going on.
+- Drop bus pointer from state container, it is only used in
+ probe()
+- Use pci_host_probe() and get rid of a lot of boilerplate.
+- Use builtin_driver_probe() and explain why this is
+ necessary with comments in the code.
+
+PCI maintainers: looking for review or ACK to take this
+driver throght ARM SoC since it is dependent on the first
+patches in the series in order not to cause build
+problems.
+---
+ MAINTAINERS | 6 +
+ drivers/pci/controller/Kconfig | 8 +
+ drivers/pci/controller/Makefile | 1 +
+ drivers/pci/controller/pci-ixp4xx.c | 705 ++++++++++++++++++++++++++++
+ 4 files changed, 720 insertions(+)
+ create mode 100644 drivers/pci/controller/pci-ixp4xx.c
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index d92f85ca831d..ae220d52a6d7 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -13692,6 +13692,12 @@ S: Maintained
+ F: Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
+ F: drivers/pci/controller/dwc/*imx6*
+
++PCI DRIVER FOR INTEL IXP4XX
++S: Maintained
++F: Documentation/devicetree/bindings/pci/intel,ixp4xx-pci.yaml
++F: drivers/pci/controller/pci-ixp4xx.c
++
+ PCI DRIVER FOR INTEL VOLUME MANAGEMENT DEVICE (VMD)
+diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
+index 5aa8977d7b0f..b9a9a05be0e7 100644
+--- a/drivers/pci/controller/Kconfig
++++ b/drivers/pci/controller/Kconfig
+@@ -37,6 +37,14 @@ config PCI_FTPCI100
+ depends on OF
+ default ARCH_GEMINI
+
++config PCI_IXP4XX
++ bool "Intel IXP4xx PCI controller"
++ depends on ARM && OF
++ default ARCH_IXP4XX
++ help
++ Say Y here if you want support for the PCI host controller found
++ in the Intel IXP4xx XScale-based network processor SoC.
++
+ config PCI_TEGRA
+ bool "NVIDIA Tegra PCIe controller"
+ depends on ARCH_TEGRA || COMPILE_TEST
+diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
+index e4559f2182f2..f81f3fd7a9e0 100644
+--- a/drivers/pci/controller/Makefile
++++ b/drivers/pci/controller/Makefile
+@@ -1,6 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0
+ obj-$(CONFIG_PCIE_CADENCE) += cadence/
+ obj-$(CONFIG_PCI_FTPCI100) += pci-ftpci100.o
++obj-$(CONFIG_PCI_IXP4XX) += pci-ixp4xx.o
+ obj-$(CONFIG_PCI_HYPERV) += pci-hyperv.o
+ obj-$(CONFIG_PCI_HYPERV_INTERFACE) += pci-hyperv-intf.o
+ obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
+diff --git a/drivers/pci/controller/pci-ixp4xx.c b/drivers/pci/controller/pci-ixp4xx.c
+new file mode 100644
+index 000000000000..c6912fd630b4
+--- /dev/null
++++ b/drivers/pci/controller/pci-ixp4xx.c
+@@ -0,0 +1,705 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Support for Intel IXP4xx PCI host controller
++ *
++ *
++ * Based on the IXP4xx arch/arm/mach-ixp4xx/common-pci.c driver
++ * Copyright (C) 2002 Intel Corporation
++ * Copyright (C) 2003-2004 MontaVista Software, Inc.
++ *
++ * TODO:
++ * - Test IO-space access
++ * - DMA support
++ */
++
++#include <linux/init.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/of_address.h>
++#include <linux/of_device.h>
++#include <linux/of_pci.h>
++#include <linux/pci.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/bits.h>
++
++/* Register offsets */
++#define IXP4XX_PCI_NP_AD 0x00
++#define IXP4XX_PCI_NP_CBE 0x04
++#define IXP4XX_PCI_NP_WDATA 0x08
++#define IXP4XX_PCI_NP_RDATA 0x0c
++#define IXP4XX_PCI_CRP_AD_CBE 0x10
++#define IXP4XX_PCI_CRP_WDATA 0x14
++#define IXP4XX_PCI_CRP_RDATA 0x18
++#define IXP4XX_PCI_CSR 0x1c
++#define IXP4XX_PCI_ISR 0x20
++#define IXP4XX_PCI_INTEN 0x24
++#define IXP4XX_PCI_DMACTRL 0x28
++#define IXP4XX_PCI_AHBMEMBASE 0x2c
++#define IXP4XX_PCI_AHBIOBASE 0x30
++#define IXP4XX_PCI_PCIMEMBASE 0x34
++#define IXP4XX_PCI_AHBDOORBELL 0x38
++#define IXP4XX_PCI_PCIDOORBELL 0x3C
++#define IXP4XX_PCI_ATPDMA0_AHBADDR 0x40
++#define IXP4XX_PCI_ATPDMA0_PCIADDR 0x44
++#define IXP4XX_PCI_ATPDMA0_LENADDR 0x48
++#define IXP4XX_PCI_ATPDMA1_AHBADDR 0x4C
++#define IXP4XX_PCI_ATPDMA1_PCIADDR 0x50
++#define IXP4XX_PCI_ATPDMA1_LENADDR 0x54
++
++/* CSR bit definitions */
++#define IXP4XX_PCI_CSR_HOST BIT(0)
++#define IXP4XX_PCI_CSR_ARBEN BIT(1)
++#define IXP4XX_PCI_CSR_ADS BIT(2)
++#define IXP4XX_PCI_CSR_PDS BIT(3)
++#define IXP4XX_PCI_CSR_ABE BIT(4)
++#define IXP4XX_PCI_CSR_DBT BIT(5)
++#define IXP4XX_PCI_CSR_ASE BIT(8)
++#define IXP4XX_PCI_CSR_IC BIT(15)
++#define IXP4XX_PCI_CSR_PRST BIT(16)
++
++/* ISR (Interrupt status) Register bit definitions */
++#define IXP4XX_PCI_ISR_PSE BIT(0)
++#define IXP4XX_PCI_ISR_PFE BIT(1)
++#define IXP4XX_PCI_ISR_PPE BIT(2)
++#define IXP4XX_PCI_ISR_AHBE BIT(3)
++#define IXP4XX_PCI_ISR_APDC BIT(4)
++#define IXP4XX_PCI_ISR_PADC BIT(5)
++#define IXP4XX_PCI_ISR_ADB BIT(6)
++#define IXP4XX_PCI_ISR_PDB BIT(7)
++
++/* INTEN (Interrupt Enable) Register bit definitions */
++#define IXP4XX_PCI_INTEN_PSE BIT(0)
++#define IXP4XX_PCI_INTEN_PFE BIT(1)
++#define IXP4XX_PCI_INTEN_PPE BIT(2)
++#define IXP4XX_PCI_INTEN_AHBE BIT(3)
++#define IXP4XX_PCI_INTEN_APDC BIT(4)
++#define IXP4XX_PCI_INTEN_PADC BIT(5)
++#define IXP4XX_PCI_INTEN_ADB BIT(6)
++#define IXP4XX_PCI_INTEN_PDB BIT(7)
++
++/* Shift value for byte enable on NP cmd/byte enable register */
++#define IXP4XX_PCI_NP_CBE_BESL 4
++
++/* PCI commands supported by NP access unit */
++#define NP_CMD_IOREAD 0x2
++#define NP_CMD_IOWRITE 0x3
++#define NP_CMD_CONFIGREAD 0xa
++#define NP_CMD_CONFIGWRITE 0xb
++#define NP_CMD_MEMREAD 0x6
++#define NP_CMD_MEMWRITE 0x7
++
++/* Constants for CRP access into local config space */
++#define CRP_AD_CBE_BESL 20
++#define CRP_AD_CBE_WRITE 0x00010000
++
++/* Special PCI configuration space registers for this controller */
++#define IXP4XX_PCI_RTOTTO 0x40
++
++struct ixp4xx_pci {
++ struct device *dev;
++ void __iomem *base;
++ raw_spinlock_t lock; /* Protects bus writes */
++ bool errata_hammer;
++ bool host_mode;
++};
++
++/*
++ * The IXP4xx has a peculiar address bus that will change the
++ * byte order on SoC peripherals depending on whether the device
++ * operates in big endian or little endian mode. That means that
++ * readl() and writel() that always use little-endian access
++ * will not work for SoC peripherals such as the PCI controller
++ * when used in big endian mode. The accesses to the individual
++ * PCI devices on the other hand, are always little-endian and
++ * can use readl() and writel().
++ *
++ * For local AHB bus access we need to use __raw_[readl|writel]()
++ * to make sure that we access the SoC devices in the CPU native
++ * endianness.
++ */
++static inline u32 ixp4xx_readl(struct ixp4xx_pci *p, u32 reg)
++{
++ return __raw_readl(p->base + reg);
++}
++
++static inline void ixp4xx_writel(struct ixp4xx_pci *p, u32 reg, u32 val)
++{
++ __raw_writel(val, p->base + reg);
++}
++
++static int ixp4xx_pci_check_master_abort(struct ixp4xx_pci *p)
++{
++ u32 isr = ixp4xx_readl(p, IXP4XX_PCI_ISR);
++
++ if (isr & IXP4XX_PCI_ISR_PFE) {
++ /* Make sure the master abort bit is reset */
++ ixp4xx_writel(p, IXP4XX_PCI_ISR, IXP4XX_PCI_ISR_PFE);
++ dev_dbg(p->dev, "master abort detected\n");
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int ixp4xx_pci_read(struct ixp4xx_pci *p, u32 addr, u32 cmd, u32 *data)
++{
++ unsigned long flags;
++ int ret;
++
++ raw_spin_lock_irqsave(&p->lock, flags);
++
++ ixp4xx_writel(p, IXP4XX_PCI_NP_AD, addr);
++
++ if (p->errata_hammer) {
++ int i;
++
++ /*
++ * PCI workaround - only works if NP PCI space reads have
++ * no side effects. Hammer the register and read twice 8
++ * times. last one will be good.
++ */
++ for (i = 0; i < 8; i++) {
++ ixp4xx_writel(p, IXP4XX_PCI_NP_CBE, cmd);
++ *data = ixp4xx_readl(p, IXP4XX_PCI_NP_RDATA);
++ *data = ixp4xx_readl(p, IXP4XX_PCI_NP_RDATA);
++ }
++ } else {
++ ixp4xx_writel(p, IXP4XX_PCI_NP_CBE, cmd);
++ *data = ixp4xx_readl(p, IXP4XX_PCI_NP_RDATA);
++ }
++
++ /* Check for master abort */
++ ret = ixp4xx_pci_check_master_abort(p);
++
++ raw_spin_unlock_irqrestore(&p->lock, flags);
++ return ret;
++}
++
++static int ixp4xx_pci_write(struct ixp4xx_pci *p, u32 addr, u32 cmd, u32 data)
++{
++ unsigned long flags;
++ int ret;
++
++ raw_spin_lock_irqsave(&p->lock, flags);
++
++ ixp4xx_writel(p, IXP4XX_PCI_NP_AD, addr);
++
++ /* Set up the write */
++ ixp4xx_writel(p, IXP4XX_PCI_NP_CBE, cmd);
++
++ /* Execute the write by writing to NP_WDATA */
++ ixp4xx_writel(p, IXP4XX_PCI_NP_WDATA, data);
++
++ /* Check for master abort */
++ ret = ixp4xx_pci_check_master_abort(p);
++
++ raw_spin_unlock_irqrestore(&p->lock, flags);
++ return ret;
++}
++
++static u32 ixp4xx_config_addr(u8 bus_num, u16 devfn, int where)
++{
++ u32 addr;
++
++ if (!bus_num) {
++ /* type 0 */
++ addr = BIT(32-PCI_SLOT(devfn)) | ((PCI_FUNC(devfn)) << 8) |
++ (where & ~3);
++ } else {
++ /* type 1 */
++ addr = (bus_num << 16) | ((PCI_SLOT(devfn)) << 11) |
++ ((PCI_FUNC(devfn)) << 8) | (where & ~3) | 1;
++ }
++ return addr;
++}
++
++/*
++ * CRP functions are "Controller Configuration Port" accesses
++ * initiated from within this driver itself to read/write PCI
++ * control information in the config space.
++ */
++static u32 ixp4xx_crp_byte_lane_enable_bits(u32 n, int size)
++{
++ if (size == 1)
++ return (0xf & ~BIT(n)) << CRP_AD_CBE_BESL;
++ if (size == 2)
++ return (0xf & ~(BIT(n) | BIT(n+1))) << CRP_AD_CBE_BESL;
++ if (size == 4)
++ return 0;
++ return 0xffffffff;
++}
++
++static int ixp4xx_crp_read_config(struct ixp4xx_pci *p, int where, int size,
++ u32 *value)
++{
++ unsigned long flags;
++ u32 n, cmd, val;
++
++ n = where % 4;
++ cmd = where & ~3;
++
++ dev_dbg(p->dev, "%s from %d size %d cmd %08x\n",
++ __func__, where, size, cmd);
++
++ raw_spin_lock_irqsave(&p->lock, flags);
++ ixp4xx_writel(p, IXP4XX_PCI_CRP_AD_CBE, cmd);
++ val = ixp4xx_readl(p, IXP4XX_PCI_CRP_RDATA);
++ raw_spin_unlock_irqrestore(&p->lock, flags);
++
++ val >>= (8*n);
++ switch (size) {
++ case 1:
++ val &= U8_MAX;
++ dev_dbg(p->dev, "%s read byte %02x\n", __func__, val);
++ break;
++ case 2:
++ val &= U16_MAX;
++ dev_dbg(p->dev, "%s read word %04x\n", __func__, val);
++ break;
++ case 4:
++ val &= U32_MAX;
++ dev_dbg(p->dev, "%s read long %08x\n", __func__, val);
++ break;
++ default:
++ /* Should not happen */
++ dev_err(p->dev, "%s illegal size\n", __func__);
++ return PCIBIOS_DEVICE_NOT_FOUND;
++ }
++ *value = val;
++
++ return PCIBIOS_SUCCESSFUL;
++}
++
++static int ixp4xx_crp_write_config(struct ixp4xx_pci *p, int where, int size,
++ u32 value)
++{
++ unsigned long flags;
++ u32 n, cmd, val;
++
++ n = where % 4;
++ cmd = ixp4xx_crp_byte_lane_enable_bits(n, size);
++ if (cmd == 0xffffffff)
++ return PCIBIOS_BAD_REGISTER_NUMBER;
++ cmd |= where & ~3;
++ cmd |= CRP_AD_CBE_WRITE;
++
++ val = value << (8*n);
++
++ dev_dbg(p->dev, "%s to %d size %d cmd %08x val %08x\n",
++ __func__, where, size, cmd, val);
++
++ raw_spin_lock_irqsave(&p->lock, flags);
++ ixp4xx_writel(p, IXP4XX_PCI_CRP_AD_CBE, cmd);
++ ixp4xx_writel(p, IXP4XX_PCI_CRP_WDATA, val);
++ raw_spin_unlock_irqrestore(&p->lock, flags);
++
++ return PCIBIOS_SUCCESSFUL;
++}
++
++/*
++ * Then follows the functions that read and write from the common
++ * PCI configuration space.
++ */
++
++static u32 ixp4xx_byte_lane_enable_bits(u32 n, int size)
++{
++ if (size == 1)
++ return (0xf & ~BIT(n)) << 4;
++ if (size == 2)
++ return (0xf & ~(BIT(n) | BIT(n+1))) << 4;
++ if (size == 4)
++ return 0;
++ return 0xffffffff;
++}
++
++static int ixp4xx_pci_read_config(struct pci_bus *bus, unsigned int devfn,
++ int where, int size, u32 *value)
++{
++ struct ixp4xx_pci *p = bus->sysdata;
++ u32 n, addr, val, cmd;
++ u8 bus_num = bus->number;
++ int ret;
++
++ *value = 0xffffffff;
++ n = where % 4;
++ cmd = ixp4xx_byte_lane_enable_bits(n, size);
++ if (cmd == 0xffffffff)
++ return PCIBIOS_BAD_REGISTER_NUMBER;
++
++ addr = ixp4xx_config_addr(bus_num, devfn, where);
++ cmd |= NP_CMD_CONFIGREAD;
++ dev_dbg(p->dev, "read_config from %d size %d dev %d:%d:%d address: %08x cmd: %08x\n",
++ where, size, bus_num, PCI_SLOT(devfn), PCI_FUNC(devfn), addr, cmd);
++
++ ret = ixp4xx_pci_read(p, addr, cmd, &val);
++ if (ret)
++ return PCIBIOS_DEVICE_NOT_FOUND;
++
++ val >>= (8*n);
++ switch (size) {
++ case 1:
++ val &= U8_MAX;
++ dev_dbg(p->dev, "%s read byte %02x\n", __func__, val);
++ break;
++ case 2:
++ val &= U16_MAX;
++ dev_dbg(p->dev, "%s read word %04x\n", __func__, val);
++ break;
++ case 4:
++ val &= U32_MAX;
++ dev_dbg(p->dev, "%s read long %08x\n", __func__, val);
++ break;
++ default:
++ /* Should not happen */
++ dev_err(p->dev, "%s illegal size\n", __func__);
++ return PCIBIOS_DEVICE_NOT_FOUND;
++ }
++ *value = val;
++
++ return PCIBIOS_SUCCESSFUL;
++}
++
++static int ixp4xx_pci_write_config(struct pci_bus *bus, unsigned int devfn,
++ int where, int size, u32 value)
++{
++ struct ixp4xx_pci *p = bus->sysdata;
++ u32 n, addr, val, cmd;
++ u8 bus_num = bus->number;
++ int ret;
++
++ n = where % 4;
++ cmd = ixp4xx_byte_lane_enable_bits(n, size);
++ if (cmd == 0xffffffff)
++ return PCIBIOS_BAD_REGISTER_NUMBER;
++
++ addr = ixp4xx_config_addr(bus_num, devfn, where);
++ cmd |= NP_CMD_CONFIGWRITE;
++ val = value << (8*n);
++
++ dev_dbg(p->dev, "write_config_byte %#x to %d size %d dev %d:%d:%d addr: %08x cmd %08x\n",
++ value, where, size, bus_num, PCI_SLOT(devfn), PCI_FUNC(devfn), addr, cmd);
++
++ ret = ixp4xx_pci_write(p, addr, cmd, val);
++ if (ret)
++ return PCIBIOS_DEVICE_NOT_FOUND;
++
++ return PCIBIOS_SUCCESSFUL;
++}
++
++static struct pci_ops ixp4xx_pci_ops = {
++ .read = ixp4xx_pci_read_config,
++ .write = ixp4xx_pci_write_config,
++};
++
++static u32 ixp4xx_pci_addr_to_64mconf(phys_addr_t addr)
++{
++ u8 base;
++
++ base = ((addr & 0xff000000) >> 24);
++ return (base << 24) | ((base + 1) << 16)
++ | ((base + 2) << 8) | (base + 3);
++}
++
++static int ixp4xx_pci_parse_map_ranges(struct ixp4xx_pci *p)
++{
++ struct device *dev = p->dev;
++ struct pci_host_bridge *bridge = pci_host_bridge_from_priv(p);
++ struct resource_entry *win;
++ struct resource *res;
++ phys_addr_t addr;
++
++ win = resource_list_first_type(&bridge->windows, IORESOURCE_MEM);
++ if (win) {
++ u32 pcimembase;
++
++ res = win->res;
++ addr = res->start - win->offset;
++
++ if (res->flags & IORESOURCE_PREFETCH)
++ res->name = "IXP4xx PCI PRE-MEM";
++ else
++ res->name = "IXP4xx PCI NON-PRE-MEM";
++
++ dev_dbg(dev, "%s window %pR, bus addr %pa\n",
++ res->name, res, &addr);
++ if (resource_size(res) != SZ_64M) {
++ dev_err(dev, "memory range is not 64MB\n");
++ return -EINVAL;
++ }
++
++ pcimembase = ixp4xx_pci_addr_to_64mconf(addr);
++ /* Commit configuration */
++ ixp4xx_writel(p, IXP4XX_PCI_PCIMEMBASE, pcimembase);
++ } else {
++ dev_err(dev, "no AHB memory mapping defined\n");
++ }
++
++ win = resource_list_first_type(&bridge->windows, IORESOURCE_IO);
++ if (win) {
++ res = win->res;
++
++ addr = pci_pio_to_address(res->start);
++ if (addr & 0xff) {
++ dev_err(dev, "IO mem at uneven address: %pa\n", &addr);
++ return -EINVAL;
++ }
++
++ res->name = "IXP4xx PCI IO MEM";
++ /*
++ * Setup I/O space location for PCI->AHB access, the
++ * upper 24 bits of the address goes into the lower
++ * 24 bits of this register.
++ */
++ ixp4xx_writel(p, IXP4XX_PCI_AHBIOBASE, (addr >> 8));
++ } else {
++ dev_info(dev, "no IO space AHB memory mapping defined\n");
++ }
++
++ return 0;
++}
++
++static int ixp4xx_pci_parse_map_dma_ranges(struct ixp4xx_pci *p)
++{
++ struct device *dev = p->dev;
++ struct pci_host_bridge *bridge = pci_host_bridge_from_priv(p);
++ struct resource_entry *win;
++ struct resource *res;
++ phys_addr_t addr;
++ u32 ahbmembase;
++
++ win = resource_list_first_type(&bridge->dma_ranges, IORESOURCE_MEM);
++ if (win) {
++ res = win->res;
++ addr = res->start - win->offset;
++
++ if (resource_size(res) != SZ_64M) {
++ dev_err(dev, "DMA memory range is not 64MB\n");
++ return -EINVAL;
++ }
++
++ dev_dbg(dev, "DMA MEM BASE: %pa\n", &addr);
++ /*
++ * 4 PCI-to-AHB windows of 16 MB each, write the 8 high bits
++ * into each byte of the PCI_AHBMEMBASE register.
++ */
++ ahbmembase = ixp4xx_pci_addr_to_64mconf(addr);
++ /* Commit AHB membase */
++ ixp4xx_writel(p, IXP4XX_PCI_AHBMEMBASE, ahbmembase);
++ } else {
++ dev_err(dev, "no DMA memory range defined\n");
++ }
++
++ return 0;
++}
++
++/* Only used to get context for abort handling */
++static struct ixp4xx_pci *ixp4xx_pci_abort_singleton;
++
++static int ixp4xx_pci_abort_handler(unsigned long addr, unsigned int fsr,
++ struct pt_regs *regs)
++{
++ struct ixp4xx_pci *p = ixp4xx_pci_abort_singleton;
++ u32 isr, status;
++ int ret;
++
++ isr = ixp4xx_readl(p, IXP4XX_PCI_ISR);
++ ret = ixp4xx_crp_read_config(p, PCI_STATUS, 2, &status);
++ if (ret) {
++ dev_err(p->dev, "unable to read abort status\n");
++ return -EINVAL;
++ }
++
++ dev_err(p->dev,
++ "PCI: abort_handler addr = %#lx, isr = %#x, status = %#x\n",
++ addr, isr, status);
++
++ /* Make sure the Master Abort bit is reset */
++ ixp4xx_writel(p, IXP4XX_PCI_ISR, IXP4XX_PCI_ISR_PFE);
++ status |= PCI_STATUS_REC_MASTER_ABORT;
++ ret = ixp4xx_crp_write_config(p, PCI_STATUS, 2, status);
++ if (ret)
++ dev_err(p->dev, "unable to clear abort status bit\n");
++
++ /*
++ * If it was an imprecise abort, then we need to correct the
++ * return address to be _after_ the instruction.
++ */
++ if (fsr & (1 << 10)) {
++ dev_err(p->dev, "imprecise abort\n");
++ regs->ARM_pc += 4;
++ }
++
++ return 0;
++}
++
++static int __init ixp4xx_pci_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct device_node *np = dev->of_node;
++ struct ixp4xx_pci *p;
++ struct pci_host_bridge *host;
++ int ret;
++ u32 val;
++ phys_addr_t addr;
++ u32 basereg[4] = {
++ PCI_BASE_ADDRESS_0,
++ PCI_BASE_ADDRESS_1,
++ PCI_BASE_ADDRESS_2,
++ PCI_BASE_ADDRESS_3,
++ };
++ int i;
++
++ host = devm_pci_alloc_host_bridge(dev, sizeof(*p));
++ if (!host)
++ return -ENOMEM;
++
++ host->ops = &ixp4xx_pci_ops;
++ p = pci_host_bridge_priv(host);
++ host->sysdata = p;
++ p->dev = dev;
++ raw_spin_lock_init(&p->lock);
++ dev_set_drvdata(dev, p);
++
++ /*
++ * Set up quirk for erratic behaviour in the 42x variant
++ * when accessing config space.
++ */
++ if (of_device_is_compatible(np, "intel,ixp42x-pci")) {
++ p->errata_hammer = true;
++ dev_info(dev, "activate hammering errata\n");
++ }
++
++ p->base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(p->base))
++ return PTR_ERR(p->base);
++
++ val = ixp4xx_readl(p, IXP4XX_PCI_CSR);
++ p->host_mode = !!(val & IXP4XX_PCI_CSR_HOST);
++ dev_info(dev, "controller is in %s mode\n",
++ p->host_mode ? "host" : "option");
++
++ /* Hook in our fault handler for PCI errors */
++ ixp4xx_pci_abort_singleton = p;
++ hook_fault_code(16+6, ixp4xx_pci_abort_handler, SIGBUS, 0,
++ "imprecise external abort");
++
++ ret = ixp4xx_pci_parse_map_ranges(p);
++ if (ret)
++ return ret;
++
++ ret = ixp4xx_pci_parse_map_dma_ranges(p);
++ if (ret)
++ return ret;
++
++ /* This is only configured in host mode */
++ if (p->host_mode) {
++ addr = __pa(PAGE_OFFSET);
++ /* This is a noop (0x00) but explains what is going on */
++ addr |= PCI_BASE_ADDRESS_SPACE_MEMORY;
++
++ for (i = 0; i < 4; i++) {
++ /* Write this directly into the config space */
++ ret = ixp4xx_crp_write_config(p, basereg[i], 4, addr);
++ if (ret)
++ dev_err(dev, "failed to set up PCI_BASE_ADDRESS_%d\n", i);
++ else
++ dev_info(dev, "set PCI_BASE_ADDR_%d to %pa\n", i, &addr);
++ addr += SZ_16M;
++ }
++
++ /*
++ * Enable CSR window at 64 MiB to allow PCI masters to continue
++ * prefetching past the 64 MiB boundary, if all AHB to PCI windows
++ * are consecutive.
++ */
++ ret = ixp4xx_crp_write_config(p, PCI_BASE_ADDRESS_4, 4, addr);
++ if (ret)
++ dev_err(dev, "failed to set up PCI_BASE_ADDRESS_4\n");
++ else
++ dev_info(dev, "set PCI_BASE_ADDR_4 to %pa\n", &addr);
++
++ /*
++ * Put the IO memory at the very end of physical memory at
++ * 0xfffffc00. This is when the PCI is trying to access IO
++ * memory over AHB.
++ */
++ addr = 0xfffffc00;
++ addr |= PCI_BASE_ADDRESS_SPACE_IO;
++ ret = ixp4xx_crp_write_config(p, PCI_BASE_ADDRESS_5, 4, addr);
++ if (ret)
++ dev_err(dev, "failed to set up PCI_BASE_ADDRESS_5\n");
++ else
++ dev_info(dev, "set PCI_BASE_ADDR_5 to %pa\n", &addr);
++
++ /*
++ * Retry timeout to 0x80
++ * Transfer ready timeout to 0xff
++ */
++ ret = ixp4xx_crp_write_config(p, IXP4XX_PCI_RTOTTO, 4,
++ 0x000080ff);
++ if (ret)
++ dev_err(dev, "failed to set up TRDY limit\n");
++ else
++ dev_info(dev, "set TRDY limit to 0x80ff\n");
++ }
++
++ /* Clear interrupts */
++ val = IXP4XX_PCI_ISR_PSE | IXP4XX_PCI_ISR_PFE | IXP4XX_PCI_ISR_PPE | IXP4XX_PCI_ISR_AHBE;
++ ixp4xx_writel(p, IXP4XX_PCI_ISR, val);
++
++ /*
++ * Set Initialize Complete in PCI Control Register: allow IXP4XX to
++ * respond to PCI configuration cycles. Specify that the AHB bus is
++ * operating in big endian mode. Set up byte lane swapping between
++ * little-endian PCI and the big-endian AHB bus.
++ */
++ val = IXP4XX_PCI_CSR_IC | IXP4XX_PCI_CSR_ABE;
++#ifdef __ARMEB__
++ val |= (IXP4XX_PCI_CSR_PDS | IXP4XX_PCI_CSR_ADS);
++#endif
++ ixp4xx_writel(p, IXP4XX_PCI_CSR, val);
++
++ ret = ixp4xx_crp_write_config(p, PCI_COMMAND, 2, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
++ if (ret)
++ dev_err(dev, "unable to initialize master and command memory\n");
++ else
++ dev_info(dev, "initialized as master\n");
++
++ pci_host_probe(host);
++
++ return 0;
++}
++
++static const struct of_device_id ixp4xx_pci_of_match[] = {
++ {
++ .compatible = "intel,ixp42x-pci",
++ },
++ {
++ .compatible = "intel,ixp43x-pci",
++ },
++ {},
++};
++
++/*
++ * This driver needs to be a builtin module with suppressed bind
++ * attributes since the probe() is initializing a hard exception
++ * handler and this can only be done from __init-tagged code
++ * sections. This module cannot be removed and inserted at all.
++ */
++static struct platform_driver ixp4xx_pci_driver = {
++ .driver = {
++ .name = "ixp4xx-pci",
++ .suppress_bind_attrs = true,
++ .of_match_table = of_match_ptr(ixp4xx_pci_of_match),
++ },
++};
++/*
++ * This is the only way to have an __init tagged probe that does
++ * not cause link errors.
++ */
++builtin_platform_driver_probe(ixp4xx_pci_driver, ixp4xx_pci_probe);