]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/pci/host/pcie-designware.c
PCI: designware: Require config accesses to be naturally aligned
[karo-tx-linux.git] / drivers / pci / host / pcie-designware.c
index 52aa6e34002bc2543f20b8607efdab9e2a5a669a..b77535f3967b98a031024aab5c9fd6d627ef6ad0 100644 (file)
@@ -35,7 +35,7 @@
 
 #define PCIE_LINK_WIDTH_SPEED_CONTROL  0x80C
 #define PORT_LOGIC_SPEED_CHANGE                (0x1 << 17)
-#define PORT_LOGIC_LINK_WIDTH_MASK     (0x1ff << 8)
+#define PORT_LOGIC_LINK_WIDTH_MASK     (0x1f << 8)
 #define PORT_LOGIC_LINK_WIDTH_1_LANES  (0x1 << 8)
 #define PORT_LOGIC_LINK_WIDTH_2_LANES  (0x2 << 8)
 #define PORT_LOGIC_LINK_WIDTH_4_LANES  (0x4 << 8)
@@ -80,28 +80,38 @@ static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys)
        return sys->private_data;
 }
 
-int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val)
+int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val)
 {
-       *val = readl(addr);
+       if ((uintptr_t)addr & (size - 1)) {
+               *val = 0;
+               return PCIBIOS_BAD_REGISTER_NUMBER;
+       }
 
-       if (size == 1)
-               *val = (*val >> (8 * (where & 3))) & 0xff;
+       if (size == 4)
+               *val = readl(addr);
        else if (size == 2)
-               *val = (*val >> (8 * (where & 3))) & 0xffff;
-       else if (size != 4)
+               *val = readw(addr);
+       else if (size == 1)
+               *val = readb(addr);
+       else {
+               *val = 0;
                return PCIBIOS_BAD_REGISTER_NUMBER;
+       }
 
        return PCIBIOS_SUCCESSFUL;
 }
 
-int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val)
+int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val)
 {
+       if ((uintptr_t)addr & (size - 1))
+               return PCIBIOS_BAD_REGISTER_NUMBER;
+
        if (size == 4)
                writel(val, addr);
        else if (size == 2)
-               writew(val, addr + (where & 2));
+               writew(val, addr);
        else if (size == 1)
-               writeb(val, addr + (where & 3));
+               writeb(val, addr);
        else
                return PCIBIOS_BAD_REGISTER_NUMBER;
 
@@ -132,8 +142,7 @@ static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
        if (pp->ops->rd_own_conf)
                ret = pp->ops->rd_own_conf(pp, where, size, val);
        else
-               ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where,
-                               size, val);
+               ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val);
 
        return ret;
 }
@@ -146,8 +155,7 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
        if (pp->ops->wr_own_conf)
                ret = pp->ops->wr_own_conf(pp, where, size, val);
        else
-               ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3), where,
-                               size, val);
+               ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val);
 
        return ret;
 }
@@ -205,12 +213,16 @@ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp)
 
 void dw_pcie_msi_init(struct pcie_port *pp)
 {
+       u64 msi_target;
+
        pp->msi_data = __get_free_pages(GFP_KERNEL, 0);
+       msi_target = virt_to_phys((void *)pp->msi_data);
 
        /* program the msi_data */
        dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4,
-                       virt_to_phys((void *)pp->msi_data));
-       dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0);
+                           (u32)(msi_target & 0xffffffff));
+       dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4,
+                           (u32)(msi_target >> 32 & 0xffffffff));
 }
 
 static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
@@ -286,6 +298,9 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos)
        }
 
        *pos = pos0;
+       desc->nvec_used = no_irqs;
+       desc->msi_attrib.multiple = order_base_2(no_irqs);
+
        return irq;
 
 no_valid_irq:
@@ -293,11 +308,31 @@ no_valid_irq:
        return -ENOSPC;
 }
 
+static void dw_msi_setup_msg(struct pcie_port *pp, unsigned int irq, u32 pos)
+{
+       struct msi_msg msg;
+       u64 msi_target;
+
+       if (pp->ops->get_msi_addr)
+               msi_target = pp->ops->get_msi_addr(pp);
+       else
+               msi_target = virt_to_phys((void *)pp->msi_data);
+
+       msg.address_lo = (u32)(msi_target & 0xffffffff);
+       msg.address_hi = (u32)(msi_target >> 32 & 0xffffffff);
+
+       if (pp->ops->get_msi_data)
+               msg.data = pp->ops->get_msi_data(pp, pos);
+       else
+               msg.data = pos;
+
+       pci_write_msi_msg(irq, &msg);
+}
+
 static int dw_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev,
                        struct msi_desc *desc)
 {
        int irq, pos;
-       struct msi_msg msg;
        struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata);
 
        if (desc->msi_attrib.is_msix)
@@ -307,20 +342,36 @@ static int dw_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev,
        if (irq < 0)
                return irq;
 
-       if (pp->ops->get_msi_addr)
-               msg.address_lo = pp->ops->get_msi_addr(pp);
-       else
-               msg.address_lo = virt_to_phys((void *)pp->msi_data);
-       msg.address_hi = 0x0;
+       dw_msi_setup_msg(pp, irq, pos);
 
-       if (pp->ops->get_msi_data)
-               msg.data = pp->ops->get_msi_data(pp, pos);
-       else
-               msg.data = pos;
+       return 0;
+}
 
-       pci_write_msi_msg(irq, &msg);
+static int dw_msi_setup_irqs(struct msi_controller *chip, struct pci_dev *pdev,
+                            int nvec, int type)
+{
+#ifdef CONFIG_PCI_MSI
+       int irq, pos;
+       struct msi_desc *desc;
+       struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata);
+
+       /* MSI-X interrupts are not supported */
+       if (type == PCI_CAP_ID_MSIX)
+               return -EINVAL;
+
+       WARN_ON(!list_is_singular(&pdev->dev.msi_list));
+       desc = list_entry(pdev->dev.msi_list.next, struct msi_desc, list);
+
+       irq = assign_irq(nvec, desc, &pos);
+       if (irq < 0)
+               return irq;
+
+       dw_msi_setup_msg(pp, irq, pos);
 
        return 0;
+#else
+       return -EINVAL;
+#endif
 }
 
 static void dw_msi_teardown_irq(struct msi_controller *chip, unsigned int irq)
@@ -334,6 +385,7 @@ static void dw_msi_teardown_irq(struct msi_controller *chip, unsigned int irq)
 
 static struct msi_controller dw_pcie_msi_chip = {
        .setup_irq = dw_msi_setup_irq,
+       .setup_irqs = dw_msi_setup_irqs,
        .teardown_irq = dw_msi_teardown_irq,
 };
 
@@ -539,13 +591,12 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
                u32 devfn, int where, int size, u32 *val)
 {
        int ret, type;
-       u32 address, busdev, cfg_size;
+       u32 busdev, cfg_size;
        u64 cpu_addr;
        void __iomem *va_cfg_base;
 
        busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
                 PCIE_ATU_FUNC(PCI_FUNC(devfn));
-       address = where & ~0x3;
 
        if (bus->parent->number == pp->root_bus_nr) {
                type = PCIE_ATU_TYPE_CFG0;
@@ -562,7 +613,7 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
        dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
                                  type, cpu_addr,
                                  busdev, cfg_size);
-       ret = dw_pcie_cfg_read(va_cfg_base + address, where, size, val);
+       ret = dw_pcie_cfg_read(va_cfg_base + where, size, val);
        dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
                                  PCIE_ATU_TYPE_IO, pp->io_mod_base,
                                  pp->io_bus_addr, pp->io_size);
@@ -574,13 +625,12 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
                u32 devfn, int where, int size, u32 val)
 {
        int ret, type;
-       u32 address, busdev, cfg_size;
+       u32 busdev, cfg_size;
        u64 cpu_addr;
        void __iomem *va_cfg_base;
 
        busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
                 PCIE_ATU_FUNC(PCI_FUNC(devfn));
-       address = where & ~0x3;
 
        if (bus->parent->number == pp->root_bus_nr) {
                type = PCIE_ATU_TYPE_CFG0;
@@ -597,7 +647,7 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
        dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
                                  type, cpu_addr,
                                  busdev, cfg_size);
-       ret = dw_pcie_cfg_write(va_cfg_base + address, where, size, val);
+       ret = dw_pcie_cfg_write(va_cfg_base + where, size, val);
        dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
                                  PCIE_ATU_TYPE_IO, pp->io_mod_base,
                                  pp->io_bus_addr, pp->io_size);