xref: /openbmc/linux/arch/sparc/kernel/ioport.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
288278ca2SAdrian Bunk /*
31da177e4SLinus Torvalds  * ioport.c:  Simple io mapping allocator.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
61da177e4SLinus Torvalds  * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * 1996: sparc_free_io, 1999: ioremap()/iounmap() by Pete Zaitcev.
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  * 2000/01/29
111da177e4SLinus Torvalds  * <rth> zait: as long as pci_alloc_consistent produces something addressable,
121da177e4SLinus Torvalds  *	things are ok.
131da177e4SLinus Torvalds  * <zaitcev> rth: no, it is relevant, because get_free_pages returns you a
141da177e4SLinus Torvalds  *	pointer into the big page mapping
151da177e4SLinus Torvalds  * <rth> zait: so what?
161da177e4SLinus Torvalds  * <rth> zait: remap_it_my_way(virt_to_phys(get_free_page()))
171da177e4SLinus Torvalds  * <zaitcev> Hmm
181da177e4SLinus Torvalds  * <zaitcev> Suppose I did this remap_it_my_way(virt_to_phys(get_free_page())).
191da177e4SLinus Torvalds  *	So far so good.
201da177e4SLinus Torvalds  * <zaitcev> Now, driver calls pci_free_consistent(with result of
211da177e4SLinus Torvalds  *	remap_it_my_way()).
221da177e4SLinus Torvalds  * <zaitcev> How do you find the address to pass to free_pages()?
231da177e4SLinus Torvalds  * <rth> zait: walk the page tables?  It's only two or three level after all.
241da177e4SLinus Torvalds  * <rth> zait: you have to walk them anyway to remove the mapping.
251da177e4SLinus Torvalds  * <zaitcev> Hmm
261da177e4SLinus Torvalds  * <zaitcev> Sounds reasonable
271da177e4SLinus Torvalds  */
281da177e4SLinus Torvalds 
293ca9fab4SDavid S. Miller #include <linux/module.h>
301da177e4SLinus Torvalds #include <linux/sched.h>
311da177e4SLinus Torvalds #include <linux/kernel.h>
321da177e4SLinus Torvalds #include <linux/errno.h>
331da177e4SLinus Torvalds #include <linux/types.h>
341da177e4SLinus Torvalds #include <linux/ioport.h>
351da177e4SLinus Torvalds #include <linux/mm.h>
361da177e4SLinus Torvalds #include <linux/slab.h>
371da177e4SLinus Torvalds #include <linux/pci.h>		/* struct pci_dev */
381da177e4SLinus Torvalds #include <linux/proc_fs.h>
39e7a088f9SAlexey Dobriyan #include <linux/seq_file.h>
400912a5dbSJens Axboe #include <linux/scatterlist.h>
419f4df96bSChristoph Hellwig #include <linux/dma-map-ops.h>
42*263291faSRob Herring #include <linux/of.h>
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds #include <asm/io.h>
451da177e4SLinus Torvalds #include <asm/vaddrs.h>
461da177e4SLinus Torvalds #include <asm/oplib.h>
47576c352eSDavid S. Miller #include <asm/prom.h>
481da177e4SLinus Torvalds #include <asm/page.h>
491da177e4SLinus Torvalds #include <asm/pgalloc.h>
501da177e4SLinus Torvalds #include <asm/dma.h>
51e0039348SDavid S. Miller #include <asm/iommu.h>
52e0039348SDavid S. Miller #include <asm/io-unit.h>
538401707fSKonrad Eisele #include <asm/leon.h>
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds static void __iomem *_sparc_ioremap(struct resource *res, u32 bus, u32 pa, int sz);
561da177e4SLinus Torvalds static void __iomem *_sparc_alloc_io(unsigned int busno, unsigned long phys,
571da177e4SLinus Torvalds     unsigned long size, char *name);
581da177e4SLinus Torvalds static void _sparc_free_io(struct resource *res);
591da177e4SLinus Torvalds 
60c61c65cdSAdrian Bunk static void register_proc_sparc_ioport(void);
61c61c65cdSAdrian Bunk 
621da177e4SLinus Torvalds /* This points to the next to use virtual memory for DVMA mappings */
631da177e4SLinus Torvalds static struct resource _sparc_dvma = {
641da177e4SLinus Torvalds 	.name = "sparc_dvma", .start = DVMA_VADDR, .end = DVMA_END - 1
651da177e4SLinus Torvalds };
661da177e4SLinus Torvalds /* This points to the start of I/O mappings, cluable from outside. */
671da177e4SLinus Torvalds /*ext*/ struct resource sparc_iomap = {
681da177e4SLinus Torvalds 	.name = "sparc_iomap", .start = IOBASE_VADDR, .end = IOBASE_END - 1
691da177e4SLinus Torvalds };
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds /*
721da177e4SLinus Torvalds  * Our mini-allocator...
731da177e4SLinus Torvalds  * Boy this is gross! We need it because we must map I/O for
741da177e4SLinus Torvalds  * timers and interrupt controller before the kmalloc is available.
751da177e4SLinus Torvalds  */
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds #define XNMLN  15
781da177e4SLinus Torvalds #define XNRES  10	/* SS-10 uses 8 */
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds struct xresource {
811da177e4SLinus Torvalds 	struct resource xres;	/* Must be first */
821da177e4SLinus Torvalds 	int xflag;		/* 1 == used */
831da177e4SLinus Torvalds 	char xname[XNMLN+1];
841da177e4SLinus Torvalds };
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds static struct xresource xresv[XNRES];
871da177e4SLinus Torvalds 
xres_alloc(void)881da177e4SLinus Torvalds static struct xresource *xres_alloc(void) {
891da177e4SLinus Torvalds 	struct xresource *xrp;
901da177e4SLinus Torvalds 	int n;
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds 	xrp = xresv;
931da177e4SLinus Torvalds 	for (n = 0; n < XNRES; n++) {
941da177e4SLinus Torvalds 		if (xrp->xflag == 0) {
951da177e4SLinus Torvalds 			xrp->xflag = 1;
961da177e4SLinus Torvalds 			return xrp;
971da177e4SLinus Torvalds 		}
981da177e4SLinus Torvalds 		xrp++;
991da177e4SLinus Torvalds 	}
1001da177e4SLinus Torvalds 	return NULL;
1011da177e4SLinus Torvalds }
1021da177e4SLinus Torvalds 
xres_free(struct xresource * xrp)1031da177e4SLinus Torvalds static void xres_free(struct xresource *xrp) {
1041da177e4SLinus Torvalds 	xrp->xflag = 0;
1051da177e4SLinus Torvalds }
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds /*
1081da177e4SLinus Torvalds  * These are typically used in PCI drivers
1091da177e4SLinus Torvalds  * which are trying to be cross-platform.
1101da177e4SLinus Torvalds  *
1111da177e4SLinus Torvalds  * Bus type is always zero on IIep.
1121da177e4SLinus Torvalds  */
ioremap(phys_addr_t offset,size_t size)113b3ada9d0SGreentime Hu void __iomem *ioremap(phys_addr_t offset, size_t size)
1141da177e4SLinus Torvalds {
1151da177e4SLinus Torvalds 	char name[14];
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds 	sprintf(name, "phys_%08x", (u32)offset);
118b3ada9d0SGreentime Hu 	return _sparc_alloc_io(0, (unsigned long)offset, size, name);
1191da177e4SLinus Torvalds }
1206943f3daSSam Ravnborg EXPORT_SYMBOL(ioremap);
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds /*
12308f80073SAdam Buchbinder  * Complementary to ioremap().
1241da177e4SLinus Torvalds  */
iounmap(volatile void __iomem * virtual)1251da177e4SLinus Torvalds void iounmap(volatile void __iomem *virtual)
1261da177e4SLinus Torvalds {
1271da177e4SLinus Torvalds 	unsigned long vaddr = (unsigned long) virtual & PAGE_MASK;
1281da177e4SLinus Torvalds 	struct resource *res;
1291da177e4SLinus Torvalds 
130a0e997c2SGeert Uytterhoeven 	/*
131a0e997c2SGeert Uytterhoeven 	 * XXX Too slow. Can have 8192 DVMA pages on sun4m in the worst case.
132a0e997c2SGeert Uytterhoeven 	 * This probably warrants some sort of hashing.
133a0e997c2SGeert Uytterhoeven 	*/
134a0e997c2SGeert Uytterhoeven 	if ((res = lookup_resource(&sparc_iomap, vaddr)) == NULL) {
1351da177e4SLinus Torvalds 		printk("free_io/iounmap: cannot free %lx\n", vaddr);
1361da177e4SLinus Torvalds 		return;
1371da177e4SLinus Torvalds 	}
1381da177e4SLinus Torvalds 	_sparc_free_io(res);
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds 	if ((char *)res >= (char*)xresv && (char *)res < (char *)&xresv[XNRES]) {
1411da177e4SLinus Torvalds 		xres_free((struct xresource *)res);
1421da177e4SLinus Torvalds 	} else {
1431da177e4SLinus Torvalds 		kfree(res);
1441da177e4SLinus Torvalds 	}
1451da177e4SLinus Torvalds }
1466943f3daSSam Ravnborg EXPORT_SYMBOL(iounmap);
1471da177e4SLinus Torvalds 
of_ioremap(struct resource * res,unsigned long offset,unsigned long size,char * name)1483ca9fab4SDavid S. Miller void __iomem *of_ioremap(struct resource *res, unsigned long offset,
1493ca9fab4SDavid S. Miller 			 unsigned long size, char *name)
1503ca9fab4SDavid S. Miller {
1513ca9fab4SDavid S. Miller 	return _sparc_alloc_io(res->flags & 0xF,
1523ca9fab4SDavid S. Miller 			       res->start + offset,
1533ca9fab4SDavid S. Miller 			       size, name);
1543ca9fab4SDavid S. Miller }
1553ca9fab4SDavid S. Miller EXPORT_SYMBOL(of_ioremap);
1563ca9fab4SDavid S. Miller 
of_iounmap(struct resource * res,void __iomem * base,unsigned long size)157e3a411a3SDavid S. Miller void of_iounmap(struct resource *res, void __iomem *base, unsigned long size)
1583ca9fab4SDavid S. Miller {
1593ca9fab4SDavid S. Miller 	iounmap(base);
1603ca9fab4SDavid S. Miller }
1613ca9fab4SDavid S. Miller EXPORT_SYMBOL(of_iounmap);
1623ca9fab4SDavid S. Miller 
1631da177e4SLinus Torvalds /*
1641da177e4SLinus Torvalds  * Meat of mapping
1651da177e4SLinus Torvalds  */
_sparc_alloc_io(unsigned int busno,unsigned long phys,unsigned long size,char * name)1661da177e4SLinus Torvalds static void __iomem *_sparc_alloc_io(unsigned int busno, unsigned long phys,
1671da177e4SLinus Torvalds     unsigned long size, char *name)
1681da177e4SLinus Torvalds {
1691da177e4SLinus Torvalds 	static int printed_full;
1701da177e4SLinus Torvalds 	struct xresource *xres;
1711da177e4SLinus Torvalds 	struct resource *res;
1721da177e4SLinus Torvalds 	char *tack;
1731da177e4SLinus Torvalds 	int tlen;
1741da177e4SLinus Torvalds 	void __iomem *va;	/* P3 diag */
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds 	if (name == NULL) name = "???";
1771da177e4SLinus Torvalds 
178c31f7651SSam Ravnborg 	if ((xres = xres_alloc()) != NULL) {
1791da177e4SLinus Torvalds 		tack = xres->xname;
1801da177e4SLinus Torvalds 		res = &xres->xres;
1811da177e4SLinus Torvalds 	} else {
1821da177e4SLinus Torvalds 		if (!printed_full) {
1831da177e4SLinus Torvalds 			printk("ioremap: done with statics, switching to malloc\n");
1841da177e4SLinus Torvalds 			printed_full = 1;
1851da177e4SLinus Torvalds 		}
1861da177e4SLinus Torvalds 		tlen = strlen(name);
1871da177e4SLinus Torvalds 		tack = kmalloc(sizeof (struct resource) + tlen + 1, GFP_KERNEL);
1881da177e4SLinus Torvalds 		if (tack == NULL) return NULL;
1891da177e4SLinus Torvalds 		memset(tack, 0, sizeof(struct resource));
1901da177e4SLinus Torvalds 		res = (struct resource *) tack;
1911da177e4SLinus Torvalds 		tack += sizeof (struct resource);
1921da177e4SLinus Torvalds 	}
1931da177e4SLinus Torvalds 
194bb07972fSAzeem Shaikh 	strscpy(tack, name, XNMLN+1);
1951da177e4SLinus Torvalds 	res->name = tack;
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds 	va = _sparc_ioremap(res, busno, phys, size);
1981da177e4SLinus Torvalds 	/* printk("ioremap(0x%x:%08lx[0x%lx])=%p\n", busno, phys, size, va); */ /* P3 diag */
1991da177e4SLinus Torvalds 	return va;
2001da177e4SLinus Torvalds }
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds /*
2031da177e4SLinus Torvalds  */
2041da177e4SLinus Torvalds static void __iomem *
_sparc_ioremap(struct resource * res,u32 bus,u32 pa,int sz)2051da177e4SLinus Torvalds _sparc_ioremap(struct resource *res, u32 bus, u32 pa, int sz)
2061da177e4SLinus Torvalds {
2071da177e4SLinus Torvalds 	unsigned long offset = ((unsigned long) pa) & (~PAGE_MASK);
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds 	if (allocate_resource(&sparc_iomap, res,
2101da177e4SLinus Torvalds 	    (offset + sz + PAGE_SIZE-1) & PAGE_MASK,
2111da177e4SLinus Torvalds 	    sparc_iomap.start, sparc_iomap.end, PAGE_SIZE, NULL, NULL) != 0) {
2121da177e4SLinus Torvalds 		/* Usually we cannot see printks in this case. */
2131da177e4SLinus Torvalds 		prom_printf("alloc_io_res(%s): cannot occupy\n",
2141da177e4SLinus Torvalds 		    (res->name != NULL)? res->name: "???");
2151da177e4SLinus Torvalds 		prom_halt();
2161da177e4SLinus Torvalds 	}
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds 	pa &= PAGE_MASK;
2199701b264SSam Ravnborg 	srmmu_mapiorange(bus, pa, res->start, resource_size(res));
2201da177e4SLinus Torvalds 
221d75fc8bbSGreg Kroah-Hartman 	return (void __iomem *)(unsigned long)(res->start + offset);
2221da177e4SLinus Torvalds }
2231da177e4SLinus Torvalds 
2241da177e4SLinus Torvalds /*
22508f80073SAdam Buchbinder  * Complementary to _sparc_ioremap().
2261da177e4SLinus Torvalds  */
_sparc_free_io(struct resource * res)2271da177e4SLinus Torvalds static void _sparc_free_io(struct resource *res)
2281da177e4SLinus Torvalds {
2291da177e4SLinus Torvalds 	unsigned long plen;
2301da177e4SLinus Torvalds 
23128f65c11SJoe Perches 	plen = resource_size(res);
23230d4d1ffSEric Sesterhenn 	BUG_ON((plen & (PAGE_SIZE-1)) != 0);
2339701b264SSam Ravnborg 	srmmu_unmapiorange(res->start, plen);
2341da177e4SLinus Torvalds 	release_resource(res);
2351da177e4SLinus Torvalds }
2361da177e4SLinus Torvalds 
sparc_dma_alloc_resource(struct device * dev,size_t len)237ce65d36fSChristoph Hellwig unsigned long sparc_dma_alloc_resource(struct device *dev, size_t len)
23853b7670eSChristoph Hellwig {
23953b7670eSChristoph Hellwig 	struct resource *res;
24053b7670eSChristoph Hellwig 
24153b7670eSChristoph Hellwig 	res = kzalloc(sizeof(*res), GFP_KERNEL);
24253b7670eSChristoph Hellwig 	if (!res)
24353b7670eSChristoph Hellwig 		return 0;
24448cc8f7aSChristoph Hellwig 	res->name = dev->of_node->full_name;
24553b7670eSChristoph Hellwig 
24653b7670eSChristoph Hellwig 	if (allocate_resource(&_sparc_dvma, res, len, _sparc_dvma.start,
24753b7670eSChristoph Hellwig 			      _sparc_dvma.end, PAGE_SIZE, NULL, NULL) != 0) {
24853b7670eSChristoph Hellwig 		printk("%s: cannot occupy 0x%zx", __func__, len);
24953b7670eSChristoph Hellwig 		kfree(res);
25053b7670eSChristoph Hellwig 		return 0;
25153b7670eSChristoph Hellwig 	}
25253b7670eSChristoph Hellwig 
25353b7670eSChristoph Hellwig 	return res->start;
25453b7670eSChristoph Hellwig }
25553b7670eSChristoph Hellwig 
sparc_dma_free_resource(void * cpu_addr,size_t size)256ce65d36fSChristoph Hellwig bool sparc_dma_free_resource(void *cpu_addr, size_t size)
25753b7670eSChristoph Hellwig {
25853b7670eSChristoph Hellwig 	unsigned long addr = (unsigned long)cpu_addr;
25953b7670eSChristoph Hellwig 	struct resource *res;
26053b7670eSChristoph Hellwig 
26153b7670eSChristoph Hellwig 	res = lookup_resource(&_sparc_dvma, addr);
26253b7670eSChristoph Hellwig 	if (!res) {
26353b7670eSChristoph Hellwig 		printk("%s: cannot free %p\n", __func__, cpu_addr);
26453b7670eSChristoph Hellwig 		return false;
26553b7670eSChristoph Hellwig 	}
26653b7670eSChristoph Hellwig 
26753b7670eSChristoph Hellwig 	if ((addr & (PAGE_SIZE - 1)) != 0) {
26853b7670eSChristoph Hellwig 		printk("%s: unaligned va %p\n", __func__, cpu_addr);
26953b7670eSChristoph Hellwig 		return false;
27053b7670eSChristoph Hellwig 	}
27153b7670eSChristoph Hellwig 
27253b7670eSChristoph Hellwig 	size = PAGE_ALIGN(size);
27353b7670eSChristoph Hellwig 	if (resource_size(res) != size) {
27453b7670eSChristoph Hellwig 		printk("%s: region 0x%lx asked 0x%zx\n",
27553b7670eSChristoph Hellwig 			__func__, (long)resource_size(res), size);
27653b7670eSChristoph Hellwig 		return false;
27753b7670eSChristoph Hellwig 	}
27853b7670eSChristoph Hellwig 
27953b7670eSChristoph Hellwig 	release_resource(res);
28053b7670eSChristoph Hellwig 	kfree(res);
28153b7670eSChristoph Hellwig 	return true;
28253b7670eSChristoph Hellwig }
28353b7670eSChristoph Hellwig 
2841da177e4SLinus Torvalds #ifdef CONFIG_SBUS
2851da177e4SLinus Torvalds 
sbus_set_sbus64(struct device * dev,int x)28663237eebSDavid S. Miller void sbus_set_sbus64(struct device *dev, int x)
2878fae097dSDavid S. Miller {
2881da177e4SLinus Torvalds 	printk("sbus_set_sbus64: unsupported\n");
2891da177e4SLinus Torvalds }
2906943f3daSSam Ravnborg EXPORT_SYMBOL(sbus_set_sbus64);
2911da177e4SLinus Torvalds 
sparc_register_ioport(void)292f8e4d32cSDavid S. Miller static int __init sparc_register_ioport(void)
293576c352eSDavid S. Miller {
294576c352eSDavid S. Miller 	register_proc_sparc_ioport();
295576c352eSDavid S. Miller 
296576c352eSDavid S. Miller 	return 0;
297576c352eSDavid S. Miller }
298576c352eSDavid S. Miller 
299f8e4d32cSDavid S. Miller arch_initcall(sparc_register_ioport);
300f8e4d32cSDavid S. Miller 
3011da177e4SLinus Torvalds #endif /* CONFIG_SBUS */
3021da177e4SLinus Torvalds 
303837e80b3SChristoph Hellwig /*
304837e80b3SChristoph Hellwig  * IIep is write-through, not flushing on cpu to device transfer.
305837e80b3SChristoph Hellwig  *
306837e80b3SChristoph Hellwig  * On LEON systems without cache snooping, the entire D-CACHE must be flushed to
307837e80b3SChristoph Hellwig  * make DMA to cacheable memory coherent.
308837e80b3SChristoph Hellwig  */
arch_sync_dma_for_cpu(phys_addr_t paddr,size_t size,enum dma_data_direction dir)30956e35f9cSChristoph Hellwig void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
31056e35f9cSChristoph Hellwig 		enum dma_data_direction dir)
3111da177e4SLinus Torvalds {
3120fb3436bSChristophe JAILLET 	if (dir != DMA_TO_DEVICE &&
313837e80b3SChristoph Hellwig 	    sparc_cpu_model == sparc_leon &&
314837e80b3SChristoph Hellwig 	    !sparc_leon3_snooping_enabled())
315837e80b3SChristoph Hellwig 		leon_flush_dcache_all();
3161da177e4SLinus Torvalds }
317ee664a92SFUJITA Tomonori 
3181da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
3191da177e4SLinus Torvalds 
sparc_io_proc_show(struct seq_file * m,void * v)320e7a088f9SAlexey Dobriyan static int sparc_io_proc_show(struct seq_file *m, void *v)
3211da177e4SLinus Torvalds {
322e7a088f9SAlexey Dobriyan 	struct resource *root = m->private, *r;
3231da177e4SLinus Torvalds 	const char *nm;
3241da177e4SLinus Torvalds 
325e7a088f9SAlexey Dobriyan 	for (r = root->child; r != NULL; r = r->sibling) {
326c31f7651SSam Ravnborg 		if ((nm = r->name) == NULL) nm = "???";
327e7a088f9SAlexey Dobriyan 		seq_printf(m, "%016llx-%016llx: %s\n",
328685143acSGreg Kroah-Hartman 				(unsigned long long)r->start,
329685143acSGreg Kroah-Hartman 				(unsigned long long)r->end, nm);
3301da177e4SLinus Torvalds 	}
3311da177e4SLinus Torvalds 
332e7a088f9SAlexey Dobriyan 	return 0;
3331da177e4SLinus Torvalds }
3341da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */
3351da177e4SLinus Torvalds 
register_proc_sparc_ioport(void)336c61c65cdSAdrian Bunk static void register_proc_sparc_ioport(void)
3371da177e4SLinus Torvalds {
3381da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
3393f3942acSChristoph Hellwig 	proc_create_single_data("io_map", 0, NULL, sparc_io_proc_show,
3403f3942acSChristoph Hellwig 			&sparc_iomap);
3413f3942acSChristoph Hellwig 	proc_create_single_data("dvma_map", 0, NULL, sparc_io_proc_show,
3423f3942acSChristoph Hellwig 			&_sparc_dvma);
3431da177e4SLinus Torvalds #endif
3441da177e4SLinus Torvalds }
345