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