1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
23cc30d07SMichael Ellerman /*
33cc30d07SMichael Ellerman * Support PCI IO workaround
43cc30d07SMichael Ellerman *
53cc30d07SMichael Ellerman * Copyright (C) 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org>
63cc30d07SMichael Ellerman * IBM, Corp.
73cc30d07SMichael Ellerman * (C) Copyright 2007-2008 TOSHIBA CORPORATION
83cc30d07SMichael Ellerman */
93cc30d07SMichael Ellerman #undef DEBUG
103cc30d07SMichael Ellerman
113cc30d07SMichael Ellerman #include <linux/kernel.h>
12589ee628SIngo Molnar #include <linux/sched/mm.h> /* for init_mm */
1365fddcfcSMike Rapoport #include <linux/pgtable.h>
143cc30d07SMichael Ellerman
153cc30d07SMichael Ellerman #include <asm/io.h>
163cc30d07SMichael Ellerman #include <asm/machdep.h>
173cc30d07SMichael Ellerman #include <asm/ppc-pci.h>
183cc30d07SMichael Ellerman #include <asm/io-workarounds.h>
1994171b19SAneesh Kumar K.V #include <asm/pte-walk.h>
2094171b19SAneesh Kumar K.V
213cc30d07SMichael Ellerman
223cc30d07SMichael Ellerman #define IOWA_MAX_BUS 8
233cc30d07SMichael Ellerman
243cc30d07SMichael Ellerman static struct iowa_bus iowa_busses[IOWA_MAX_BUS];
253cc30d07SMichael Ellerman static unsigned int iowa_bus_count;
263cc30d07SMichael Ellerman
iowa_pci_find(unsigned long vaddr,unsigned long paddr)273cc30d07SMichael Ellerman static struct iowa_bus *iowa_pci_find(unsigned long vaddr, unsigned long paddr)
283cc30d07SMichael Ellerman {
293cc30d07SMichael Ellerman int i, j;
303cc30d07SMichael Ellerman struct resource *res;
313cc30d07SMichael Ellerman unsigned long vstart, vend;
323cc30d07SMichael Ellerman
333cc30d07SMichael Ellerman for (i = 0; i < iowa_bus_count; i++) {
343cc30d07SMichael Ellerman struct iowa_bus *bus = &iowa_busses[i];
353cc30d07SMichael Ellerman struct pci_controller *phb = bus->phb;
363cc30d07SMichael Ellerman
373cc30d07SMichael Ellerman if (vaddr) {
383cc30d07SMichael Ellerman vstart = (unsigned long)phb->io_base_virt;
393cc30d07SMichael Ellerman vend = vstart + phb->pci_io_size - 1;
403cc30d07SMichael Ellerman if ((vaddr >= vstart) && (vaddr <= vend))
413cc30d07SMichael Ellerman return bus;
423cc30d07SMichael Ellerman }
433cc30d07SMichael Ellerman
443cc30d07SMichael Ellerman if (paddr)
453cc30d07SMichael Ellerman for (j = 0; j < 3; j++) {
463cc30d07SMichael Ellerman res = &phb->mem_resources[j];
473cc30d07SMichael Ellerman if (paddr >= res->start && paddr <= res->end)
483cc30d07SMichael Ellerman return bus;
493cc30d07SMichael Ellerman }
503cc30d07SMichael Ellerman }
513cc30d07SMichael Ellerman
523cc30d07SMichael Ellerman return NULL;
533cc30d07SMichael Ellerman }
543cc30d07SMichael Ellerman
55ecd73cc5SBenjamin Herrenschmidt #ifdef CONFIG_PPC_INDIRECT_MMIO
iowa_mem_find_bus(const PCI_IO_ADDR addr)563cc30d07SMichael Ellerman struct iowa_bus *iowa_mem_find_bus(const PCI_IO_ADDR addr)
573cc30d07SMichael Ellerman {
583cc30d07SMichael Ellerman struct iowa_bus *bus;
593cc30d07SMichael Ellerman int token;
603cc30d07SMichael Ellerman
613cc30d07SMichael Ellerman token = PCI_GET_ADDR_TOKEN(addr);
623cc30d07SMichael Ellerman
633cc30d07SMichael Ellerman if (token && token <= iowa_bus_count)
643cc30d07SMichael Ellerman bus = &iowa_busses[token - 1];
653cc30d07SMichael Ellerman else {
663cc30d07SMichael Ellerman unsigned long vaddr, paddr;
673cc30d07SMichael Ellerman
683cc30d07SMichael Ellerman vaddr = (unsigned long)PCI_FIX_ADDR(addr);
693cc30d07SMichael Ellerman if (vaddr < PHB_IO_BASE || vaddr >= PHB_IO_END)
703cc30d07SMichael Ellerman return NULL;
71*5362a4b6SNicholas Piggin
72*5362a4b6SNicholas Piggin paddr = ppc_find_vmap_phys(vaddr);
73*5362a4b6SNicholas Piggin
743cc30d07SMichael Ellerman bus = iowa_pci_find(vaddr, paddr);
753cc30d07SMichael Ellerman
763cc30d07SMichael Ellerman if (bus == NULL)
773cc30d07SMichael Ellerman return NULL;
783cc30d07SMichael Ellerman }
793cc30d07SMichael Ellerman
803cc30d07SMichael Ellerman return bus;
813cc30d07SMichael Ellerman }
82ecd73cc5SBenjamin Herrenschmidt #else /* CONFIG_PPC_INDIRECT_MMIO */
iowa_mem_find_bus(const PCI_IO_ADDR addr)83ecd73cc5SBenjamin Herrenschmidt struct iowa_bus *iowa_mem_find_bus(const PCI_IO_ADDR addr)
84ecd73cc5SBenjamin Herrenschmidt {
85ecd73cc5SBenjamin Herrenschmidt return NULL;
86ecd73cc5SBenjamin Herrenschmidt }
87ecd73cc5SBenjamin Herrenschmidt #endif /* !CONFIG_PPC_INDIRECT_MMIO */
883cc30d07SMichael Ellerman
89ecd73cc5SBenjamin Herrenschmidt #ifdef CONFIG_PPC_INDIRECT_PIO
iowa_pio_find_bus(unsigned long port)903cc30d07SMichael Ellerman struct iowa_bus *iowa_pio_find_bus(unsigned long port)
913cc30d07SMichael Ellerman {
923cc30d07SMichael Ellerman unsigned long vaddr = (unsigned long)pci_io_base + port;
933cc30d07SMichael Ellerman return iowa_pci_find(vaddr, 0);
943cc30d07SMichael Ellerman }
95ecd73cc5SBenjamin Herrenschmidt #else
iowa_pio_find_bus(unsigned long port)96ecd73cc5SBenjamin Herrenschmidt struct iowa_bus *iowa_pio_find_bus(unsigned long port)
97ecd73cc5SBenjamin Herrenschmidt {
98ecd73cc5SBenjamin Herrenschmidt return NULL;
99ecd73cc5SBenjamin Herrenschmidt }
100ecd73cc5SBenjamin Herrenschmidt #endif
1013cc30d07SMichael Ellerman
1023cc30d07SMichael Ellerman #define DEF_PCI_AC_RET(name, ret, at, al, space, aa) \
1033cc30d07SMichael Ellerman static ret iowa_##name at \
1043cc30d07SMichael Ellerman { \
1053cc30d07SMichael Ellerman struct iowa_bus *bus; \
1063cc30d07SMichael Ellerman bus = iowa_##space##_find_bus(aa); \
1073cc30d07SMichael Ellerman if (bus && bus->ops && bus->ops->name) \
1083cc30d07SMichael Ellerman return bus->ops->name al; \
1093cc30d07SMichael Ellerman return __do_##name al; \
1103cc30d07SMichael Ellerman }
1113cc30d07SMichael Ellerman
1123cc30d07SMichael Ellerman #define DEF_PCI_AC_NORET(name, at, al, space, aa) \
1133cc30d07SMichael Ellerman static void iowa_##name at \
1143cc30d07SMichael Ellerman { \
1153cc30d07SMichael Ellerman struct iowa_bus *bus; \
1163cc30d07SMichael Ellerman bus = iowa_##space##_find_bus(aa); \
1173cc30d07SMichael Ellerman if (bus && bus->ops && bus->ops->name) { \
1183cc30d07SMichael Ellerman bus->ops->name al; \
1193cc30d07SMichael Ellerman return; \
1203cc30d07SMichael Ellerman } \
1213cc30d07SMichael Ellerman __do_##name al; \
1223cc30d07SMichael Ellerman }
1233cc30d07SMichael Ellerman
1243cc30d07SMichael Ellerman #include <asm/io-defs.h>
1253cc30d07SMichael Ellerman
1263cc30d07SMichael Ellerman #undef DEF_PCI_AC_RET
1273cc30d07SMichael Ellerman #undef DEF_PCI_AC_NORET
1283cc30d07SMichael Ellerman
129cad5cef6SGreg Kroah-Hartman static const struct ppc_pci_io iowa_pci_io = {
1303cc30d07SMichael Ellerman
1313cc30d07SMichael Ellerman #define DEF_PCI_AC_RET(name, ret, at, al, space, aa) .name = iowa_##name,
1323cc30d07SMichael Ellerman #define DEF_PCI_AC_NORET(name, at, al, space, aa) .name = iowa_##name,
1333cc30d07SMichael Ellerman
1343cc30d07SMichael Ellerman #include <asm/io-defs.h>
1353cc30d07SMichael Ellerman
1363cc30d07SMichael Ellerman #undef DEF_PCI_AC_RET
1373cc30d07SMichael Ellerman #undef DEF_PCI_AC_NORET
1383cc30d07SMichael Ellerman
1393cc30d07SMichael Ellerman };
1403cc30d07SMichael Ellerman
141ecd73cc5SBenjamin Herrenschmidt #ifdef CONFIG_PPC_INDIRECT_MMIO
iowa_ioremap(phys_addr_t addr,unsigned long size,pgprot_t prot,void * caller)14214b4d976SChristophe Leroy void __iomem *iowa_ioremap(phys_addr_t addr, unsigned long size,
143c766ee72SChristophe Leroy pgprot_t prot, void *caller)
1443cc30d07SMichael Ellerman {
1453cc30d07SMichael Ellerman struct iowa_bus *bus;
146c766ee72SChristophe Leroy void __iomem *res = __ioremap_caller(addr, size, prot, caller);
1473cc30d07SMichael Ellerman int busno;
1483cc30d07SMichael Ellerman
1493cc30d07SMichael Ellerman bus = iowa_pci_find(0, (unsigned long)addr);
1503cc30d07SMichael Ellerman if (bus != NULL) {
1513cc30d07SMichael Ellerman busno = bus - iowa_busses;
1523cc30d07SMichael Ellerman PCI_SET_ADDR_TOKEN(res, busno + 1);
1533cc30d07SMichael Ellerman }
1543cc30d07SMichael Ellerman return res;
1553cc30d07SMichael Ellerman }
156ecd73cc5SBenjamin Herrenschmidt #endif /* !CONFIG_PPC_INDIRECT_MMIO */
1573cc30d07SMichael Ellerman
15814b4d976SChristophe Leroy bool io_workaround_inited;
15914b4d976SChristophe Leroy
160d1109b75SMichael Ellerman /* Enable IO workaround */
io_workaround_init(void)161cad5cef6SGreg Kroah-Hartman static void io_workaround_init(void)
162d1109b75SMichael Ellerman {
163d1109b75SMichael Ellerman if (io_workaround_inited)
164d1109b75SMichael Ellerman return;
165d1109b75SMichael Ellerman ppc_pci_io = iowa_pci_io;
16614b4d976SChristophe Leroy io_workaround_inited = true;
167d1109b75SMichael Ellerman }
168d1109b75SMichael Ellerman
169d1109b75SMichael Ellerman /* Register new bus to support workaround */
iowa_register_bus(struct pci_controller * phb,struct ppc_pci_io * ops,int (* initfunc)(struct iowa_bus *,void *),void * data)170cad5cef6SGreg Kroah-Hartman void iowa_register_bus(struct pci_controller *phb, struct ppc_pci_io *ops,
1713cc30d07SMichael Ellerman int (*initfunc)(struct iowa_bus *, void *), void *data)
1723cc30d07SMichael Ellerman {
1733cc30d07SMichael Ellerman struct iowa_bus *bus;
1743cc30d07SMichael Ellerman struct device_node *np = phb->dn;
1753cc30d07SMichael Ellerman
176d1109b75SMichael Ellerman io_workaround_init();
177d1109b75SMichael Ellerman
1783cc30d07SMichael Ellerman if (iowa_bus_count >= IOWA_MAX_BUS) {
1793cc30d07SMichael Ellerman pr_err("IOWA:Too many pci bridges, "
180b7c670d6SRob Herring "workarounds disabled for %pOF\n", np);
1813cc30d07SMichael Ellerman return;
1823cc30d07SMichael Ellerman }
1833cc30d07SMichael Ellerman
1843cc30d07SMichael Ellerman bus = &iowa_busses[iowa_bus_count];
1853cc30d07SMichael Ellerman bus->phb = phb;
1863cc30d07SMichael Ellerman bus->ops = ops;
18769b12368SMichael Ellerman bus->private = data;
1883cc30d07SMichael Ellerman
1893cc30d07SMichael Ellerman if (initfunc)
1903cc30d07SMichael Ellerman if ((*initfunc)(bus, data))
1913cc30d07SMichael Ellerman return;
1923cc30d07SMichael Ellerman
1933cc30d07SMichael Ellerman iowa_bus_count++;
1943cc30d07SMichael Ellerman
195b7c670d6SRob Herring pr_debug("IOWA:[%d]Add bus, %pOF.\n", iowa_bus_count-1, np);
1963cc30d07SMichael Ellerman }
1973cc30d07SMichael Ellerman
198