diff options
Diffstat (limited to 'drivers/pci/host/pcie-designware.c')
-rw-r--r-- | drivers/pci/host/pcie-designware.c | 91 |
1 files changed, 59 insertions, 32 deletions
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index e33b68be0391..17ce88f79d2b 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c | |||
@@ -74,7 +74,7 @@ static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys) | |||
74 | return sys->private_data; | 74 | return sys->private_data; |
75 | } | 75 | } |
76 | 76 | ||
77 | int cfg_read(void __iomem *addr, int where, int size, u32 *val) | 77 | int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val) |
78 | { | 78 | { |
79 | *val = readl(addr); | 79 | *val = readl(addr); |
80 | 80 | ||
@@ -88,7 +88,7 @@ int cfg_read(void __iomem *addr, int where, int size, u32 *val) | |||
88 | return PCIBIOS_SUCCESSFUL; | 88 | return PCIBIOS_SUCCESSFUL; |
89 | } | 89 | } |
90 | 90 | ||
91 | int cfg_write(void __iomem *addr, int where, int size, u32 val) | 91 | int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val) |
92 | { | 92 | { |
93 | if (size == 4) | 93 | if (size == 4) |
94 | writel(val, addr); | 94 | writel(val, addr); |
@@ -126,7 +126,8 @@ static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, | |||
126 | if (pp->ops->rd_own_conf) | 126 | if (pp->ops->rd_own_conf) |
127 | ret = pp->ops->rd_own_conf(pp, where, size, val); | 127 | ret = pp->ops->rd_own_conf(pp, where, size, val); |
128 | else | 128 | else |
129 | ret = cfg_read(pp->dbi_base + (where & ~0x3), where, size, val); | 129 | ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, |
130 | size, val); | ||
130 | 131 | ||
131 | return ret; | 132 | return ret; |
132 | } | 133 | } |
@@ -139,8 +140,8 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, | |||
139 | if (pp->ops->wr_own_conf) | 140 | if (pp->ops->wr_own_conf) |
140 | ret = pp->ops->wr_own_conf(pp, where, size, val); | 141 | ret = pp->ops->wr_own_conf(pp, where, size, val); |
141 | else | 142 | else |
142 | ret = cfg_write(pp->dbi_base + (where & ~0x3), where, size, | 143 | ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3), where, |
143 | val); | 144 | size, val); |
144 | 145 | ||
145 | return ret; | 146 | return ret; |
146 | } | 147 | } |
@@ -167,11 +168,13 @@ void dw_handle_msi_irq(struct pcie_port *pp) | |||
167 | while ((pos = find_next_bit(&val, 32, pos)) != 32) { | 168 | while ((pos = find_next_bit(&val, 32, pos)) != 32) { |
168 | irq = irq_find_mapping(pp->irq_domain, | 169 | irq = irq_find_mapping(pp->irq_domain, |
169 | i * 32 + pos); | 170 | i * 32 + pos); |
171 | dw_pcie_wr_own_conf(pp, | ||
172 | PCIE_MSI_INTR0_STATUS + i * 12, | ||
173 | 4, 1 << pos); | ||
170 | generic_handle_irq(irq); | 174 | generic_handle_irq(irq); |
171 | pos++; | 175 | pos++; |
172 | } | 176 | } |
173 | } | 177 | } |
174 | dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, val); | ||
175 | } | 178 | } |
176 | } | 179 | } |
177 | 180 | ||
@@ -209,6 +212,23 @@ static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0) | |||
209 | return 0; | 212 | return 0; |
210 | } | 213 | } |
211 | 214 | ||
215 | static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, | ||
216 | unsigned int nvec, unsigned int pos) | ||
217 | { | ||
218 | unsigned int i, res, bit, val; | ||
219 | |||
220 | for (i = 0; i < nvec; i++) { | ||
221 | irq_set_msi_desc_off(irq_base, i, NULL); | ||
222 | clear_bit(pos + i, pp->msi_irq_in_use); | ||
223 | /* Disable corresponding interrupt on MSI controller */ | ||
224 | res = ((pos + i) / 32) * 12; | ||
225 | bit = (pos + i) % 32; | ||
226 | dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); | ||
227 | val &= ~(1 << bit); | ||
228 | dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); | ||
229 | } | ||
230 | } | ||
231 | |||
212 | static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) | 232 | static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) |
213 | { | 233 | { |
214 | int res, bit, irq, pos0, pos1, i; | 234 | int res, bit, irq, pos0, pos1, i; |
@@ -242,18 +262,25 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) | |||
242 | if (!irq) | 262 | if (!irq) |
243 | goto no_valid_irq; | 263 | goto no_valid_irq; |
244 | 264 | ||
245 | i = 0; | 265 | /* |
246 | while (i < no_irqs) { | 266 | * irq_create_mapping (called from dw_pcie_host_init) pre-allocates |
267 | * descs so there is no need to allocate descs here. We can therefore | ||
268 | * assume that if irq_find_mapping above returns non-zero, then the | ||
269 | * descs are also successfully allocated. | ||
270 | */ | ||
271 | |||
272 | for (i = 0; i < no_irqs; i++) { | ||
273 | if (irq_set_msi_desc_off(irq, i, desc) != 0) { | ||
274 | clear_irq_range(pp, irq, i, pos0); | ||
275 | goto no_valid_irq; | ||
276 | } | ||
247 | set_bit(pos0 + i, pp->msi_irq_in_use); | 277 | set_bit(pos0 + i, pp->msi_irq_in_use); |
248 | irq_alloc_descs((irq + i), (irq + i), 1, 0); | ||
249 | irq_set_msi_desc(irq + i, desc); | ||
250 | /*Enable corresponding interrupt in MSI interrupt controller */ | 278 | /*Enable corresponding interrupt in MSI interrupt controller */ |
251 | res = ((pos0 + i) / 32) * 12; | 279 | res = ((pos0 + i) / 32) * 12; |
252 | bit = (pos0 + i) % 32; | 280 | bit = (pos0 + i) % 32; |
253 | dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); | 281 | dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); |
254 | val |= 1 << bit; | 282 | val |= 1 << bit; |
255 | dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); | 283 | dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); |
256 | i++; | ||
257 | } | 284 | } |
258 | 285 | ||
259 | *pos = pos0; | 286 | *pos = pos0; |
@@ -266,7 +293,7 @@ no_valid_irq: | |||
266 | 293 | ||
267 | static void clear_irq(unsigned int irq) | 294 | static void clear_irq(unsigned int irq) |
268 | { | 295 | { |
269 | int res, bit, val, pos; | 296 | unsigned int pos, nvec; |
270 | struct irq_desc *desc; | 297 | struct irq_desc *desc; |
271 | struct msi_desc *msi; | 298 | struct msi_desc *msi; |
272 | struct pcie_port *pp; | 299 | struct pcie_port *pp; |
@@ -281,18 +308,15 @@ static void clear_irq(unsigned int irq) | |||
281 | return; | 308 | return; |
282 | } | 309 | } |
283 | 310 | ||
311 | /* undo what was done in assign_irq */ | ||
284 | pos = data->hwirq; | 312 | pos = data->hwirq; |
313 | nvec = 1 << msi->msi_attrib.multiple; | ||
285 | 314 | ||
286 | irq_free_desc(irq); | 315 | clear_irq_range(pp, irq, nvec, pos); |
287 | |||
288 | clear_bit(pos, pp->msi_irq_in_use); | ||
289 | 316 | ||
290 | /* Disable corresponding interrupt on MSI interrupt controller */ | 317 | /* all irqs cleared; reset attributes */ |
291 | res = (pos / 32) * 12; | 318 | msi->irq = 0; |
292 | bit = pos % 32; | 319 | msi->msi_attrib.multiple = 0; |
293 | dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); | ||
294 | val &= ~(1 << bit); | ||
295 | dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); | ||
296 | } | 320 | } |
297 | 321 | ||
298 | static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, | 322 | static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, |
@@ -320,10 +344,10 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, | |||
320 | if (irq < 0) | 344 | if (irq < 0) |
321 | return irq; | 345 | return irq; |
322 | 346 | ||
323 | msg_ctr &= ~PCI_MSI_FLAGS_QSIZE; | 347 | /* |
324 | msg_ctr |= msgvec << 4; | 348 | * write_msi_msg() will update PCI_MSI_FLAGS so there is |
325 | pci_write_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, | 349 | * no need to explicitly call pci_write_config_word(). |
326 | msg_ctr); | 350 | */ |
327 | desc->msi_attrib.multiple = msgvec; | 351 | desc->msi_attrib.multiple = msgvec; |
328 | 352 | ||
329 | msg.address_lo = virt_to_phys((void *)pp->msi_data); | 353 | msg.address_lo = virt_to_phys((void *)pp->msi_data); |
@@ -394,6 +418,7 @@ int __init dw_pcie_host_init(struct pcie_port *pp) | |||
394 | + global_io_offset); | 418 | + global_io_offset); |
395 | pp->config.io_size = resource_size(&pp->io); | 419 | pp->config.io_size = resource_size(&pp->io); |
396 | pp->config.io_bus_addr = range.pci_addr; | 420 | pp->config.io_bus_addr = range.pci_addr; |
421 | pp->io_base = range.cpu_addr; | ||
397 | } | 422 | } |
398 | if (restype == IORESOURCE_MEM) { | 423 | if (restype == IORESOURCE_MEM) { |
399 | of_pci_range_to_resource(&range, np, &pp->mem); | 424 | of_pci_range_to_resource(&range, np, &pp->mem); |
@@ -419,7 +444,6 @@ int __init dw_pcie_host_init(struct pcie_port *pp) | |||
419 | 444 | ||
420 | pp->cfg0_base = pp->cfg.start; | 445 | pp->cfg0_base = pp->cfg.start; |
421 | pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size; | 446 | pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size; |
422 | pp->io_base = pp->io.start; | ||
423 | pp->mem_base = pp->mem.start; | 447 | pp->mem_base = pp->mem.start; |
424 | 448 | ||
425 | pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base, | 449 | pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base, |
@@ -551,11 +575,13 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, | |||
551 | 575 | ||
552 | if (bus->parent->number == pp->root_bus_nr) { | 576 | if (bus->parent->number == pp->root_bus_nr) { |
553 | dw_pcie_prog_viewport_cfg0(pp, busdev); | 577 | dw_pcie_prog_viewport_cfg0(pp, busdev); |
554 | ret = cfg_read(pp->va_cfg0_base + address, where, size, val); | 578 | ret = dw_pcie_cfg_read(pp->va_cfg0_base + address, where, size, |
579 | val); | ||
555 | dw_pcie_prog_viewport_mem_outbound(pp); | 580 | dw_pcie_prog_viewport_mem_outbound(pp); |
556 | } else { | 581 | } else { |
557 | dw_pcie_prog_viewport_cfg1(pp, busdev); | 582 | dw_pcie_prog_viewport_cfg1(pp, busdev); |
558 | ret = cfg_read(pp->va_cfg1_base + address, where, size, val); | 583 | ret = dw_pcie_cfg_read(pp->va_cfg1_base + address, where, size, |
584 | val); | ||
559 | dw_pcie_prog_viewport_io_outbound(pp); | 585 | dw_pcie_prog_viewport_io_outbound(pp); |
560 | } | 586 | } |
561 | 587 | ||
@@ -574,18 +600,19 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, | |||
574 | 600 | ||
575 | if (bus->parent->number == pp->root_bus_nr) { | 601 | if (bus->parent->number == pp->root_bus_nr) { |
576 | dw_pcie_prog_viewport_cfg0(pp, busdev); | 602 | dw_pcie_prog_viewport_cfg0(pp, busdev); |
577 | ret = cfg_write(pp->va_cfg0_base + address, where, size, val); | 603 | ret = dw_pcie_cfg_write(pp->va_cfg0_base + address, where, size, |
604 | val); | ||
578 | dw_pcie_prog_viewport_mem_outbound(pp); | 605 | dw_pcie_prog_viewport_mem_outbound(pp); |
579 | } else { | 606 | } else { |
580 | dw_pcie_prog_viewport_cfg1(pp, busdev); | 607 | dw_pcie_prog_viewport_cfg1(pp, busdev); |
581 | ret = cfg_write(pp->va_cfg1_base + address, where, size, val); | 608 | ret = dw_pcie_cfg_write(pp->va_cfg1_base + address, where, size, |
609 | val); | ||
582 | dw_pcie_prog_viewport_io_outbound(pp); | 610 | dw_pcie_prog_viewport_io_outbound(pp); |
583 | } | 611 | } |
584 | 612 | ||
585 | return ret; | 613 | return ret; |
586 | } | 614 | } |
587 | 615 | ||
588 | |||
589 | static int dw_pcie_valid_config(struct pcie_port *pp, | 616 | static int dw_pcie_valid_config(struct pcie_port *pp, |
590 | struct pci_bus *bus, int dev) | 617 | struct pci_bus *bus, int dev) |
591 | { | 618 | { |
@@ -679,7 +706,7 @@ static int dw_pcie_setup(int nr, struct pci_sys_data *sys) | |||
679 | 706 | ||
680 | if (global_io_offset < SZ_1M && pp->config.io_size > 0) { | 707 | if (global_io_offset < SZ_1M && pp->config.io_size > 0) { |
681 | sys->io_offset = global_io_offset - pp->config.io_bus_addr; | 708 | sys->io_offset = global_io_offset - pp->config.io_bus_addr; |
682 | pci_ioremap_io(sys->io_offset, pp->io.start); | 709 | pci_ioremap_io(global_io_offset, pp->io_base); |
683 | global_io_offset += SZ_64K; | 710 | global_io_offset += SZ_64K; |
684 | pci_add_resource_offset(&sys->resources, &pp->io, | 711 | pci_add_resource_offset(&sys->resources, &pp->io, |
685 | sys->io_offset); | 712 | sys->io_offset); |