These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / pci / host / pcie-iproc.c
index 329e1b5..eac719a 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2014 Hauke Mehrtens <hauke@hauke-m.de>
- * Copyright (C) 2015 Broadcom Corporatcommon ion
+ * Copyright (C) 2015 Broadcom Corporation
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -31,6 +31,8 @@
 #include "pcie-iproc.h"
 
 #define CLK_CONTROL_OFFSET           0x000
+#define EP_PERST_SOURCE_SELECT_SHIFT 2
+#define EP_PERST_SOURCE_SELECT       BIT(EP_PERST_SOURCE_SELECT_SHIFT)
 #define EP_MODE_SURVIVE_PERST_SHIFT  1
 #define EP_MODE_SURVIVE_PERST        BIT(EP_MODE_SURVIVE_PERST_SHIFT)
 #define RC_PCIE_RST_OUTPUT_SHIFT     0
 #define SYS_RC_INTX_EN               0x330
 #define SYS_RC_INTX_MASK             0xf
 
-static inline struct iproc_pcie *sys_to_pcie(struct pci_sys_data *sys)
+#define PCIE_LINK_STATUS_OFFSET      0xf0c
+#define PCIE_PHYLINKUP_SHIFT         3
+#define PCIE_PHYLINKUP               BIT(PCIE_PHYLINKUP_SHIFT)
+#define PCIE_DL_ACTIVE_SHIFT         2
+#define PCIE_DL_ACTIVE               BIT(PCIE_DL_ACTIVE_SHIFT)
+
+#define OARR_VALID_SHIFT             0
+#define OARR_VALID                   BIT(OARR_VALID_SHIFT)
+#define OARR_SIZE_CFG_SHIFT          1
+#define OARR_SIZE_CFG                BIT(OARR_SIZE_CFG_SHIFT)
+
+#define OARR_LO(window)              (0xd20 + (window) * 8)
+#define OARR_HI(window)              (0xd24 + (window) * 8)
+#define OMAP_LO(window)              (0xd40 + (window) * 8)
+#define OMAP_HI(window)              (0xd44 + (window) * 8)
+
+#define MAX_NUM_OB_WINDOWS           2
+
+static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
 {
-       return sys->private_data;
+       struct iproc_pcie *pcie;
+#ifdef CONFIG_ARM
+       struct pci_sys_data *sys = bus->sysdata;
+
+       pcie = sys->private_data;
+#else
+       pcie = bus->sysdata;
+#endif
+       return pcie;
 }
 
 /**
@@ -71,8 +99,7 @@ static void __iomem *iproc_pcie_map_cfg_bus(struct pci_bus *bus,
                                            unsigned int devfn,
                                            int where)
 {
-       struct pci_sys_data *sys = bus->sysdata;
-       struct iproc_pcie *pcie = sys_to_pcie(sys);
+       struct iproc_pcie *pcie = iproc_data(bus);
        unsigned slot = PCI_SLOT(devfn);
        unsigned fn = PCI_FUNC(devfn);
        unsigned busno = bus->number;
@@ -112,23 +139,32 @@ static void iproc_pcie_reset(struct iproc_pcie *pcie)
        u32 val;
 
        /*
-        * Configure the PCIe controller as root complex and send a downstream
-        * reset
+        * Select perst_b signal as reset source. Put the device into reset,
+        * and then bring it out of reset
         */
-       val = EP_MODE_SURVIVE_PERST | RC_PCIE_RST_OUTPUT;
+       val = readl(pcie->base + CLK_CONTROL_OFFSET);
+       val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST &
+               ~RC_PCIE_RST_OUTPUT;
        writel(val, pcie->base + CLK_CONTROL_OFFSET);
        udelay(250);
-       val &= ~EP_MODE_SURVIVE_PERST;
+
+       val |= RC_PCIE_RST_OUTPUT;
        writel(val, pcie->base + CLK_CONTROL_OFFSET);
-       msleep(250);
+       msleep(100);
 }
 
 static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
 {
        u8 hdr_type;
-       u32 link_ctrl;
+       u32 link_ctrl, class, val;
        u16 pos, link_status;
-       int link_is_active = 0;
+       bool link_is_active = false;
+
+       val = readl(pcie->base + PCIE_LINK_STATUS_OFFSET);
+       if (!(val & PCIE_PHYLINKUP) || !(val & PCIE_DL_ACTIVE)) {
+               dev_err(pcie->dev, "PHY or data link is INACTIVE!\n");
+               return -ENODEV;
+       }
 
        /* make sure we are not in EP mode */
        pci_bus_read_config_byte(bus, 0, PCI_HEADER_TYPE, &hdr_type);
@@ -138,14 +174,19 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
        }
 
        /* force class to PCI_CLASS_BRIDGE_PCI (0x0604) */
-       pci_bus_write_config_word(bus, 0, PCI_CLASS_DEVICE,
-                                 PCI_CLASS_BRIDGE_PCI);
+#define PCI_BRIDGE_CTRL_REG_OFFSET 0x43c
+#define PCI_CLASS_BRIDGE_MASK      0xffff00
+#define PCI_CLASS_BRIDGE_SHIFT     8
+       pci_bus_read_config_dword(bus, 0, PCI_BRIDGE_CTRL_REG_OFFSET, &class);
+       class &= ~PCI_CLASS_BRIDGE_MASK;
+       class |= (PCI_CLASS_BRIDGE_PCI << PCI_CLASS_BRIDGE_SHIFT);
+       pci_bus_write_config_dword(bus, 0, PCI_BRIDGE_CTRL_REG_OFFSET, class);
 
        /* check link status to see if link is active */
        pos = pci_bus_find_capability(bus, 0, PCI_CAP_ID_EXP);
        pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA, &link_status);
        if (link_status & PCI_EXP_LNKSTA_NLW)
-               link_is_active = 1;
+               link_is_active = true;
 
        if (!link_is_active) {
                /* try GEN 1 link speed */
@@ -169,7 +210,7 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
                        pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA,
                                                 &link_status);
                        if (link_status & PCI_EXP_LNKSTA_NLW)
-                               link_is_active = 1;
+                               link_is_active = true;
                }
        }
 
@@ -183,35 +224,140 @@ static void iproc_pcie_enable(struct iproc_pcie *pcie)
        writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN);
 }
 
-int iproc_pcie_setup(struct iproc_pcie *pcie)
+/**
+ * Some iProc SoCs require the SW to configure the outbound address mapping
+ *
+ * Outbound address translation:
+ *
+ * iproc_pcie_address = axi_address - axi_offset
+ * OARR = iproc_pcie_address
+ * OMAP = pci_addr
+ *
+ * axi_addr -> iproc_pcie_address -> OARR -> OMAP -> pci_address
+ */
+static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
+                              u64 pci_addr, resource_size_t size)
+{
+       struct iproc_pcie_ob *ob = &pcie->ob;
+       unsigned i;
+       u64 max_size = (u64)ob->window_size * MAX_NUM_OB_WINDOWS;
+       u64 remainder;
+
+       if (size > max_size) {
+               dev_err(pcie->dev,
+                       "res size 0x%pap exceeds max supported size 0x%llx\n",
+                       &size, max_size);
+               return -EINVAL;
+       }
+
+       div64_u64_rem(size, ob->window_size, &remainder);
+       if (remainder) {
+               dev_err(pcie->dev,
+                       "res size %pap needs to be multiple of window size %pap\n",
+                       &size, &ob->window_size);
+               return -EINVAL;
+       }
+
+       if (axi_addr < ob->axi_offset) {
+               dev_err(pcie->dev,
+                       "axi address %pap less than offset %pap\n",
+                       &axi_addr, &ob->axi_offset);
+               return -EINVAL;
+       }
+
+       /*
+        * Translate the AXI address to the internal address used by the iProc
+        * PCIe core before programming the OARR
+        */
+       axi_addr -= ob->axi_offset;
+
+       for (i = 0; i < MAX_NUM_OB_WINDOWS; i++) {
+               writel(lower_32_bits(axi_addr) | OARR_VALID |
+                      (ob->set_oarr_size ? 1 : 0), pcie->base + OARR_LO(i));
+               writel(upper_32_bits(axi_addr), pcie->base + OARR_HI(i));
+               writel(lower_32_bits(pci_addr), pcie->base + OMAP_LO(i));
+               writel(upper_32_bits(pci_addr), pcie->base + OMAP_HI(i));
+
+               size -= ob->window_size;
+               if (size == 0)
+                       break;
+
+               axi_addr += ob->window_size;
+               pci_addr += ob->window_size;
+       }
+
+       return 0;
+}
+
+static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
+                                struct list_head *resources)
+{
+       struct resource_entry *window;
+       int ret;
+
+       resource_list_for_each_entry(window, resources) {
+               struct resource *res = window->res;
+               u64 res_type = resource_type(res);
+
+               switch (res_type) {
+               case IORESOURCE_IO:
+               case IORESOURCE_BUS:
+                       break;
+               case IORESOURCE_MEM:
+                       ret = iproc_pcie_setup_ob(pcie, res->start,
+                                                 res->start - window->offset,
+                                                 resource_size(res));
+                       if (ret)
+                               return ret;
+                       break;
+               default:
+                       dev_err(pcie->dev, "invalid resource %pR\n", res);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
 {
        int ret;
+       void *sysdata;
        struct pci_bus *bus;
 
        if (!pcie || !pcie->dev || !pcie->base)
                return -EINVAL;
 
-       if (pcie->phy) {
-               ret = phy_init(pcie->phy);
-               if (ret) {
-                       dev_err(pcie->dev, "unable to initialize PCIe PHY\n");
-                       return ret;
-               }
-
-               ret = phy_power_on(pcie->phy);
-               if (ret) {
-                       dev_err(pcie->dev, "unable to power on PCIe PHY\n");
-                       goto err_exit_phy;
-               }
+       ret = phy_init(pcie->phy);
+       if (ret) {
+               dev_err(pcie->dev, "unable to initialize PCIe PHY\n");
+               return ret;
+       }
 
+       ret = phy_power_on(pcie->phy);
+       if (ret) {
+               dev_err(pcie->dev, "unable to power on PCIe PHY\n");
+               goto err_exit_phy;
        }
 
        iproc_pcie_reset(pcie);
 
+       if (pcie->need_ob_cfg) {
+               ret = iproc_pcie_map_ranges(pcie, res);
+               if (ret) {
+                       dev_err(pcie->dev, "map failed\n");
+                       goto err_power_off_phy;
+               }
+       }
+
+#ifdef CONFIG_ARM
        pcie->sysdata.private_data = pcie;
+       sysdata = &pcie->sysdata;
+#else
+       sysdata = pcie;
+#endif
 
-       bus = pci_create_root_bus(pcie->dev, 0, &iproc_pcie_ops,
-                                 &pcie->sysdata, pcie->resources);
+       bus = pci_create_root_bus(pcie->dev, 0, &iproc_pcie_ops, sysdata, res);
        if (!bus) {
                dev_err(pcie->dev, "unable to create PCI root bus\n");
                ret = -ENOMEM;
@@ -229,7 +375,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie)
 
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
-       pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
+       pci_fixup_irqs(pci_common_swizzle, pcie->map_irq);
        pci_bus_add_devices(bus);
 
        return 0;
@@ -239,12 +385,9 @@ err_rm_root_bus:
        pci_remove_root_bus(bus);
 
 err_power_off_phy:
-       if (pcie->phy)
-               phy_power_off(pcie->phy);
+       phy_power_off(pcie->phy);
 err_exit_phy:
-       if (pcie->phy)
-               phy_exit(pcie->phy);
-
+       phy_exit(pcie->phy);
        return ret;
 }
 EXPORT_SYMBOL(iproc_pcie_setup);
@@ -254,10 +397,8 @@ int iproc_pcie_remove(struct iproc_pcie *pcie)
        pci_stop_root_bus(pcie->root_bus);
        pci_remove_root_bus(pcie->root_bus);
 
-       if (pcie->phy) {
-               phy_power_off(pcie->phy);
-               phy_exit(pcie->phy);
-       }
+       phy_power_off(pcie->phy);
+       phy_exit(pcie->phy);
 
        return 0;
 }