diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2006-12-07 15:35:43 +0100 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2007-02-18 21:31:34 +0000 |
commit | 140c1729a221dc6eacfcbf2a073dbf00fad13e43 (patch) | |
tree | b6baff7c6e68f7ca45aacded88b02ccb5f2cd286 | |
parent | 4c1569949a756327aa0ad7aa15a62266b6a00c3e (diff) |
[MIPS] Iomap implementation.
This implementation has support for the concept of one separate ioport
address space by PCI domain. A pointer to the virtual address where
the port space of a domain has been mapped has been added to struct
pci_controller and systems should be fixed to fill in this value. For
single domain systems this will be the same value as passed to
set_io_port_base().
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
-rw-r--r-- | arch/mips/lib/Makefile | 3 | ||||
-rw-r--r-- | arch/mips/lib/iomap-pci.c | 74 | ||||
-rw-r--r-- | arch/mips/lib/iomap.c | 253 | ||||
-rw-r--r-- | arch/mips/pci/pci.c | 8 | ||||
-rw-r--r-- | include/asm-mips/io.h | 29 | ||||
-rw-r--r-- | include/asm-mips/pci.h | 1 |
6 files changed, 287 insertions, 81 deletions
diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile index 5ad501b30b4..9e5d985936b 100644 --- a/arch/mips/lib/Makefile +++ b/arch/mips/lib/Makefile @@ -5,7 +5,8 @@ lib-y += csum_partial.o memcpy.o memset.o promlib.o \ strlen_user.o strncpy_user.o strnlen_user.o uncached.o -obj-y += iomap.o +obj-y += iomap.o +obj-$(CONFIG_PCI) += iomap-pci.o # libgcc-style stuff needed in the kernel lib-y += ashldi3.o ashrdi3.o lshrdi3.o diff --git a/arch/mips/lib/iomap-pci.c b/arch/mips/lib/iomap-pci.c new file mode 100644 index 00000000000..c11b2494bb6 --- /dev/null +++ b/arch/mips/lib/iomap-pci.c @@ -0,0 +1,74 @@ +/* + * Implement the default iomap interfaces + * + * (C) Copyright 2004 Linus Torvalds + * (C) Copyright 2006 Ralf Baechle <ralf@linux-mips.org> + * (C) Copyright 2007 MIPS Technologies, Inc. + * written by Ralf Baechle <ralf@linux-mips.org> + */ +#include <linux/pci.h> +#include <linux/module.h> +#include <asm/io.h> + +static void __iomem *ioport_map_pci(struct pci_dev *dev, + unsigned long port, unsigned int nr) +{ + struct pci_controller *ctrl = dev->bus->sysdata; + unsigned long base = ctrl->io_map_base; + + /* This will eventually become a BUG_ON but for now be gentle */ + if (unlikely(!ctrl->io_map_base)) { + struct pci_bus *bus = dev->bus; + char name[8]; + + while (bus->parent) + bus = bus->parent; + + ctrl->io_map_base = base = mips_io_port_base; + + sprintf(name, "%04x:%02x", pci_domain_nr(bus), bus->number); + printk(KERN_WARNING "io_map_base of root PCI bus %s unset. " + "Trying to continue but you better\nfix this issue or " + "report it to linux-mips@linux-mips.org or your " + "vendor.\n", name); +#ifdef CONFIG_PCI_DOMAINS + panic("To avoid data corruption io_map_base MUST be set with " + "multiple PCI domains."); +#endif + } + + return (void __iomem *) (ctrl->io_map_base + port); +} + +/* + * Create a virtual mapping cookie for a PCI BAR (memory or IO) + */ +void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) +{ + unsigned long start = pci_resource_start(dev, bar); + unsigned long len = pci_resource_len(dev, bar); + unsigned long flags = pci_resource_flags(dev, bar); + + if (!len || !start) + return NULL; + if (maxlen && len > maxlen) + len = maxlen; + if (flags & IORESOURCE_IO) + return ioport_map_pci(dev, start, len); + if (flags & IORESOURCE_MEM) { + if (flags & IORESOURCE_CACHEABLE) + return ioremap(start, len); + return ioremap_nocache(start, len); + } + /* What? */ + return NULL; +} + +EXPORT_SYMBOL(pci_iomap); + +void pci_iounmap(struct pci_dev *dev, void __iomem * addr) +{ + iounmap(addr); +} + +EXPORT_SYMBOL(pci_iounmap); diff --git a/arch/mips/lib/iomap.c b/arch/mips/lib/iomap.c index f4ac5bbcd81..d51d5cb0a4a 100644 --- a/arch/mips/lib/iomap.c +++ b/arch/mips/lib/iomap.c @@ -1,78 +1,227 @@ /* - * iomap.c, Memory Mapped I/O routines for MIPS architecture. + * Implement the default iomap interfaces * - * This code is based on lib/iomap.c, by Linus Torvalds. - * - * Copyright (C) 2004-2005 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * (C) Copyright 2004 Linus Torvalds + * (C) Copyright 2006 Ralf Baechle <ralf@linux-mips.org> + * (C) Copyright 2007 MIPS Technologies, Inc. + * written by Ralf Baechle <ralf@linux-mips.org> + */ +#include <linux/pci.h> +#include <linux/module.h> +#include <asm/io.h> + +/* + * Read/write from/to an (offsettable) iomem cookie. It might be a PIO + * access or a MMIO access, these functions don't care. The info is + * encoded in the hardware mapping set up by the mapping functions + * (or the cookie itself, depending on implementation and hw). * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * The generic routines don't assume any hardware mappings, and just + * encode the PIO/MMIO as part of the cookie. They coldly assume that + * the MMIO IO mappings are not in the low address range. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Architectures for which this is not true can't use this generic + * implementation and should do their own copy. */ -#include <linux/ioport.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <asm/io.h> +#define PIO_MASK 0x0ffffUL -void __iomem *ioport_map(unsigned long port, unsigned int nr) +unsigned int ioread8(void __iomem *addr) { - unsigned long end; + return readb(addr); +} - end = port + nr - 1UL; - if (ioport_resource.start > port || - ioport_resource.end < end || port > end) - return NULL; +EXPORT_SYMBOL(ioread8); - return (void __iomem *)(mips_io_port_base + port); +unsigned int ioread16(void __iomem *addr) +{ + return readw(addr); } -void ioport_unmap(void __iomem *addr) +EXPORT_SYMBOL(ioread16); + +unsigned int ioread16be(void __iomem *addr) { + return be16_to_cpu(__raw_readw(addr)); } -EXPORT_SYMBOL(ioport_map); -EXPORT_SYMBOL(ioport_unmap); -void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) +EXPORT_SYMBOL(ioread16be); + +unsigned int ioread32(void __iomem *addr) { - unsigned long start, len, flags; + return readl(addr); +} - if (dev == NULL) - return NULL; +EXPORT_SYMBOL(ioread32); - start = pci_resource_start(dev, bar); - len = pci_resource_len(dev, bar); - if (!start || !len) - return NULL; +unsigned int ioread32be(void __iomem *addr) +{ + return be32_to_cpu(__raw_readl(addr)); +} - if (maxlen != 0 && len > maxlen) - len = maxlen; +EXPORT_SYMBOL(ioread32be); + +void iowrite8(u8 val, void __iomem *addr) +{ + writeb(val, addr); +} - flags = pci_resource_flags(dev, bar); - if (flags & IORESOURCE_IO) - return ioport_map(start, len); - if (flags & IORESOURCE_MEM) { - if (flags & IORESOURCE_CACHEABLE) - return ioremap_cachable(start, len); - return ioremap_nocache(start, len); +EXPORT_SYMBOL(iowrite8); + +void iowrite16(u16 val, void __iomem *addr) +{ + writew(val, addr); +} + +EXPORT_SYMBOL(iowrite16); + +void iowrite16be(u16 val, void __iomem *addr) +{ + __raw_writew(cpu_to_be16(val), addr); +} + +EXPORT_SYMBOL(iowrite16be); + +void iowrite32(u32 val, void __iomem *addr) +{ + writel(val, addr); +} + +EXPORT_SYMBOL(iowrite32); + +void iowrite32be(u32 val, void __iomem *addr) +{ + __raw_writel(cpu_to_be32(val), addr); +} + +EXPORT_SYMBOL(iowrite32be); + +/* + * These are the "repeat MMIO read/write" functions. + * Note the "__raw" accesses, since we don't want to + * convert to CPU byte order. We write in "IO byte + * order" (we also don't have IO barriers). + */ +static inline void mmio_insb(void __iomem *addr, u8 *dst, int count) +{ + while (--count >= 0) { + u8 data = __raw_readb(addr); + *dst = data; + dst++; } +} - return NULL; +static inline void mmio_insw(void __iomem *addr, u16 *dst, int count) +{ + while (--count >= 0) { + u16 data = __raw_readw(addr); + *dst = data; + dst++; + } } -void pci_iounmap(struct pci_dev *dev, void __iomem *addr) +static inline void mmio_insl(void __iomem *addr, u32 *dst, int count) { - iounmap(addr); + while (--count >= 0) { + u32 data = __raw_readl(addr); + *dst = data; + dst++; + } } -EXPORT_SYMBOL(pci_iomap); -EXPORT_SYMBOL(pci_iounmap); + +static inline void mmio_outsb(void __iomem *addr, const u8 *src, int count) +{ + while (--count >= 0) { + __raw_writeb(*src, addr); + src++; + } +} + +static inline void mmio_outsw(void __iomem *addr, const u16 *src, int count) +{ + while (--count >= 0) { + __raw_writew(*src, addr); + src++; + } +} + +static inline void mmio_outsl(void __iomem *addr, const u32 *src, int count) +{ + while (--count >= 0) { + __raw_writel(*src, addr); + src++; + } +} + +void ioread8_rep(void __iomem *addr, void *dst, unsigned long count) +{ + mmio_insb(addr, dst, count); +} + +EXPORT_SYMBOL(ioread8_rep); + +void ioread16_rep(void __iomem *addr, void *dst, unsigned long count) +{ + mmio_insw(addr, dst, count); +} + +EXPORT_SYMBOL(ioread16_rep); + +void ioread32_rep(void __iomem *addr, void *dst, unsigned long count) +{ + mmio_insl(addr, dst, count); +} + +EXPORT_SYMBOL(ioread32_rep); + +void iowrite8_rep(void __iomem *addr, const void *src, unsigned long count) +{ + mmio_outsb(addr, src, count); +} + +EXPORT_SYMBOL(iowrite8_rep); + +void iowrite16_rep(void __iomem *addr, const void *src, unsigned long count) +{ + mmio_outsw(addr, src, count); +} + +EXPORT_SYMBOL(iowrite16_rep); + +void iowrite32_rep(void __iomem *addr, const void *src, unsigned long count) +{ + mmio_outsl(addr, src, count); +} + +EXPORT_SYMBOL(iowrite32_rep); + +/* + * Create a virtual mapping cookie for an IO port range + * + * This uses the same mapping are as the in/out family which has to be setup + * by the platform initialization code. + * + * Just to make matters somewhat more interesting on MIPS systems with + * multiple host bridge each will have it's own ioport address space. + */ +static void __iomem *ioport_map_legacy(unsigned long port, unsigned int nr) +{ + return (void __iomem *) (mips_io_port_base + port); +} + +void __iomem *ioport_map(unsigned long port, unsigned int nr) +{ + if (port > PIO_MASK) + return NULL; + + return ioport_map_legacy(port, nr); +} + +EXPORT_SYMBOL(ioport_map); + +void ioport_unmap(void __iomem *addr) +{ + /* Nothing to do */ +} + +EXPORT_SYMBOL(ioport_unmap); diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c index 5ace368657a..697a7e48cb8 100644 --- a/arch/mips/pci/pci.c +++ b/arch/mips/pci/pci.c @@ -79,6 +79,14 @@ void __init register_pci_controller(struct pci_controller *hose) { *hose_tail = hose; hose_tail = &hose->next; + + /* + * Do not panic here but later - this might hapen before console init. + */ + if (!hose->io_map_base) { + printk(KERN_WARNING + "registering PCI controller with io_map_base unset\n"); + } } /* Most MIPS systems have straight-forward swizzling needs. */ diff --git a/include/asm-mips/io.h b/include/asm-mips/io.h index b6a2eb81662..92ec2618560 100644 --- a/include/asm-mips/io.h +++ b/include/asm-mips/io.h @@ -20,6 +20,7 @@ #include <asm/byteorder.h> #include <asm/cpu.h> #include <asm/cpu-features.h> +#include <asm-generic/iomap.h> #include <asm/page.h> #include <asm/pgtable-bits.h> #include <asm/processor.h> @@ -518,34 +519,6 @@ static inline void memcpy_toio(volatile void __iomem *dst, const void *src, int } /* - * Memory Mapped I/O - */ -#define ioread8(addr) readb(addr) -#define ioread16(addr) readw(addr) -#define ioread32(addr) readl(addr) - -#define iowrite8(b,addr) writeb(b,addr) -#define iowrite16(w,addr) writew(w,addr) -#define iowrite32(l,addr) writel(l,addr) - -#define ioread8_rep(a,b,c) readsb(a,b,c) -#define ioread16_rep(a,b,c) readsw(a,b,c) -#define ioread32_rep(a,b,c) readsl(a,b,c) - -#define iowrite8_rep(a,b,c) writesb(a,b,c) -#define iowrite16_rep(a,b,c) writesw(a,b,c) -#define iowrite32_rep(a,b,c) writesl(a,b,c) - -/* Create a virtual mapping cookie for an IO port range */ -extern void __iomem *ioport_map(unsigned long port, unsigned int nr); -extern void ioport_unmap(void __iomem *); - -/* Create a virtual mapping cookie for a PCI BAR (memory or IO) */ -struct pci_dev; -extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); -extern void pci_iounmap(struct pci_dev *dev, void __iomem *); - -/* * ISA space is 'always mapped' on currently supported MIPS systems, no need * to explicitly ioremap() it. The fact that the ISA IO space is mapped * to PAGE_OFFSET is pure coincidence - it does not mean ISA values diff --git a/include/asm-mips/pci.h b/include/asm-mips/pci.h index 7f0f120ca07..3eea3ba0fca 100644 --- a/include/asm-mips/pci.h +++ b/include/asm-mips/pci.h @@ -32,6 +32,7 @@ struct pci_controller { unsigned long mem_offset; struct resource *io_resource; unsigned long io_offset; + unsigned long io_map_base; unsigned int index; /* For compatibility with current (as of July 2003) pciutils |