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