| /* |
| * Implement the default iomap interfaces |
| * |
| * (C) Copyright 2004 Linus Torvalds |
| */ |
| #include <linux/pci.h> |
| #include <linux/io.h> |
| |
| #ifdef CONFIG_GENERIC_IOMAP |
| #include <linux/module.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). |
| * |
| * 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. |
| * |
| * Architectures for which this is not true can't use this generic |
| * implementation and should do their own copy. |
| */ |
| |
| #ifndef HAVE_ARCH_PIO_SIZE |
| /* |
| * We encode the physical PIO addresses (0-0xffff) into the |
| * pointer by offsetting them with a constant (0x10000) and |
| * assuming that all the low addresses are always PIO. That means |
| * we can do some sanity checks on the low bits, and don't |
| * need to just take things for granted. |
| */ |
| #define PIO_OFFSET 0x10000UL |
| #define PIO_MASK 0x0ffffUL |
| #define PIO_RESERVED 0x40000UL |
| #endif |
| |
| /* |
| * Ugly macros are a way of life. |
| */ |
| #define VERIFY_PIO(port) BUG_ON((port & ~PIO_MASK) != PIO_OFFSET) |
| |
| #define IO_COND(addr, is_pio, is_mmio) do { \ |
| unsigned long port = (unsigned long __force)addr; \ |
| if (port < PIO_RESERVED) { \ |
| VERIFY_PIO(port); \ |
| port &= PIO_MASK; \ |
| is_pio; \ |
| } else { \ |
| is_mmio; \ |
| } \ |
| } while (0) |
| |
| #ifndef pio_read16be |
| #define pio_read16be(port) swab16(inw(port)) |
| #define pio_read32be(port) swab32(inl(port)) |
| #endif |
| |
| #ifndef mmio_read16be |
| #define mmio_read16be(addr) be16_to_cpu(__raw_readw(addr)) |
| #define mmio_read32be(addr) be32_to_cpu(__raw_readl(addr)) |
| #endif |
| |
| unsigned int fastcall ioread8(void __iomem *addr) |
| { |
| IO_COND(addr, return inb(port), return readb(addr)); |
| } |
| unsigned int fastcall ioread16(void __iomem *addr) |
| { |
| IO_COND(addr, return inw(port), return readw(addr)); |
| } |
| unsigned int fastcall ioread16be(void __iomem *addr) |
| { |
| IO_COND(addr, return pio_read16be(port), return mmio_read16be(addr)); |
| } |
| unsigned int fastcall ioread32(void __iomem *addr) |
| { |
| IO_COND(addr, return inl(port), return readl(addr)); |
| } |
| unsigned int fastcall ioread32be(void __iomem *addr) |
| { |
| IO_COND(addr, return pio_read32be(port), return mmio_read32be(addr)); |
| } |
| EXPORT_SYMBOL(ioread8); |
| EXPORT_SYMBOL(ioread16); |
| EXPORT_SYMBOL(ioread16be); |
| EXPORT_SYMBOL(ioread32); |
| EXPORT_SYMBOL(ioread32be); |
| |
| #ifndef pio_write16be |
| #define pio_write16be(val,port) outw(swab16(val),port) |
| #define pio_write32be(val,port) outl(swab32(val),port) |
| #endif |
| |
| #ifndef mmio_write16be |
| #define mmio_write16be(val,port) __raw_writew(be16_to_cpu(val),port) |
| #define mmio_write32be(val,port) __raw_writel(be32_to_cpu(val),port) |
| #endif |
| |
| void fastcall iowrite8(u8 val, void __iomem *addr) |
| { |
| IO_COND(addr, outb(val,port), writeb(val, addr)); |
| } |
| void fastcall iowrite16(u16 val, void __iomem *addr) |
| { |
| IO_COND(addr, outw(val,port), writew(val, addr)); |
| } |
| void fastcall iowrite16be(u16 val, void __iomem *addr) |
| { |
| IO_COND(addr, pio_write16be(val,port), mmio_write16be(val, addr)); |
| } |
| void fastcall iowrite32(u32 val, void __iomem *addr) |
| { |
| IO_COND(addr, outl(val,port), writel(val, addr)); |
| } |
| void fastcall iowrite32be(u32 val, void __iomem *addr) |
| { |
| IO_COND(addr, pio_write32be(val,port), mmio_write32be(val, addr)); |
| } |
| EXPORT_SYMBOL(iowrite8); |
| EXPORT_SYMBOL(iowrite16); |
| EXPORT_SYMBOL(iowrite16be); |
| EXPORT_SYMBOL(iowrite32); |
| 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). |
| */ |
| #ifndef mmio_insb |
| static inline void mmio_insb(void __iomem *addr, u8 *dst, int count) |
| { |
| while (--count >= 0) { |
| u8 data = __raw_readb(addr); |
| *dst = data; |
| dst++; |
| } |
| } |
| static inline void mmio_insw(void __iomem *addr, u16 *dst, int count) |
| { |
| while (--count >= 0) { |
| u16 data = __raw_readw(addr); |
| *dst = data; |
| dst++; |
| } |
| } |
| static inline void mmio_insl(void __iomem *addr, u32 *dst, int count) |
| { |
| while (--count >= 0) { |
| u32 data = __raw_readl(addr); |
| *dst = data; |
| dst++; |
| } |
| } |
| #endif |
| |
| #ifndef mmio_outsb |
| 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++; |
| } |
| } |
| #endif |
| |
| void fastcall ioread8_rep(void __iomem *addr, void *dst, unsigned long count) |
| { |
| IO_COND(addr, insb(port,dst,count), mmio_insb(addr, dst, count)); |
| } |
| void fastcall ioread16_rep(void __iomem *addr, void *dst, unsigned long count) |
| { |
| IO_COND(addr, insw(port,dst,count), mmio_insw(addr, dst, count)); |
| } |
| void fastcall ioread32_rep(void __iomem *addr, void *dst, unsigned long count) |
| { |
| IO_COND(addr, insl(port,dst,count), mmio_insl(addr, dst, count)); |
| } |
| EXPORT_SYMBOL(ioread8_rep); |
| EXPORT_SYMBOL(ioread16_rep); |
| EXPORT_SYMBOL(ioread32_rep); |
| |
| void fastcall iowrite8_rep(void __iomem *addr, const void *src, unsigned long count) |
| { |
| IO_COND(addr, outsb(port, src, count), mmio_outsb(addr, src, count)); |
| } |
| void fastcall iowrite16_rep(void __iomem *addr, const void *src, unsigned long count) |
| { |
| IO_COND(addr, outsw(port, src, count), mmio_outsw(addr, src, count)); |
| } |
| void fastcall iowrite32_rep(void __iomem *addr, const void *src, unsigned long count) |
| { |
| IO_COND(addr, outsl(port, src,count), mmio_outsl(addr, src, count)); |
| } |
| EXPORT_SYMBOL(iowrite8_rep); |
| EXPORT_SYMBOL(iowrite16_rep); |
| EXPORT_SYMBOL(iowrite32_rep); |
| |
| /* Create a virtual mapping cookie for an IO port range */ |
| void __iomem *ioport_map(unsigned long port, unsigned int nr) |
| { |
| if (port > PIO_MASK) |
| return NULL; |
| return (void __iomem *) (unsigned long) (port + PIO_OFFSET); |
| } |
| |
| void ioport_unmap(void __iomem *addr) |
| { |
| /* Nothing to do */ |
| } |
| EXPORT_SYMBOL(ioport_map); |
| EXPORT_SYMBOL(ioport_unmap); |
| |
| /* 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(start, len); |
| if (flags & IORESOURCE_MEM) { |
| if (flags & IORESOURCE_CACHEABLE) |
| return ioremap(start, len); |
| return ioremap_nocache(start, len); |
| } |
| /* What? */ |
| return NULL; |
| } |
| |
| void pci_iounmap(struct pci_dev *dev, void __iomem * addr) |
| { |
| IO_COND(addr, /* nothing */, iounmap(addr)); |
| } |
| EXPORT_SYMBOL(pci_iomap); |
| EXPORT_SYMBOL(pci_iounmap); |
| |
| #endif /* CONFIG_GENERIC_IOMAP */ |
| |
| /* |
| * Generic iomap devres |
| */ |
| static void devm_ioport_map_release(struct device *dev, void *res) |
| { |
| ioport_unmap(*(void __iomem **)res); |
| } |
| |
| static int devm_ioport_map_match(struct device *dev, void *res, |
| void *match_data) |
| { |
| return *(void **)res == match_data; |
| } |
| |
| /** |
| * devm_ioport_map - Managed ioport_map() |
| * @dev: Generic device to map ioport for |
| * @port: Port to map |
| * @nr: Number of ports to map |
| * |
| * Managed ioport_map(). Map is automatically unmapped on driver |
| * detach. |
| */ |
| void __iomem * devm_ioport_map(struct device *dev, unsigned long port, |
| unsigned int nr) |
| { |
| void __iomem **ptr, *addr; |
| |
| ptr = devres_alloc(devm_ioport_map_release, sizeof(*ptr), GFP_KERNEL); |
| if (!ptr) |
| return NULL; |
| |
| addr = ioport_map(port, nr); |
| if (addr) { |
| *ptr = addr; |
| devres_add(dev, ptr); |
| } else |
| devres_free(ptr); |
| |
| return addr; |
| } |
| EXPORT_SYMBOL(devm_ioport_map); |
| |
| /** |
| * devm_ioport_unmap - Managed ioport_unmap() |
| * @dev: Generic device to unmap for |
| * @addr: Address to unmap |
| * |
| * Managed ioport_unmap(). @addr must have been mapped using |
| * devm_ioport_map(). |
| */ |
| void devm_ioport_unmap(struct device *dev, void __iomem *addr) |
| { |
| ioport_unmap(addr); |
| WARN_ON(devres_destroy(dev, devm_ioport_map_release, |
| devm_ioport_map_match, (void *)addr)); |
| } |
| EXPORT_SYMBOL(devm_ioport_unmap); |
| |
| static void devm_ioremap_release(struct device *dev, void *res) |
| { |
| iounmap(*(void __iomem **)res); |
| } |
| |
| static int devm_ioremap_match(struct device *dev, void *res, void *match_data) |
| { |
| return *(void **)res == match_data; |
| } |
| |
| /** |
| * devm_ioremap - Managed ioremap() |
| * @dev: Generic device to remap IO address for |
| * @offset: BUS offset to map |
| * @size: Size of map |
| * |
| * Managed ioremap(). Map is automatically unmapped on driver detach. |
| */ |
| void __iomem *devm_ioremap(struct device *dev, unsigned long offset, |
| unsigned long size) |
| { |
| void __iomem **ptr, *addr; |
| |
| ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL); |
| if (!ptr) |
| return NULL; |
| |
| addr = ioremap(offset, size); |
| if (addr) { |
| *ptr = addr; |
| devres_add(dev, ptr); |
| } else |
| devres_free(ptr); |
| |
| return addr; |
| } |
| EXPORT_SYMBOL(devm_ioremap); |
| |
| /** |
| * devm_ioremap_nocache - Managed ioremap_nocache() |
| * @dev: Generic device to remap IO address for |
| * @offset: BUS offset to map |
| * @size: Size of map |
| * |
| * Managed ioremap_nocache(). Map is automatically unmapped on driver |
| * detach. |
| */ |
| void __iomem *devm_ioremap_nocache(struct device *dev, unsigned long offset, |
| unsigned long size) |
| { |
| void __iomem **ptr, *addr; |
| |
| ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL); |
| if (!ptr) |
| return NULL; |
| |
| addr = ioremap_nocache(offset, size); |
| if (addr) { |
| *ptr = addr; |
| devres_add(dev, ptr); |
| } else |
| devres_free(ptr); |
| |
| return addr; |
| } |
| EXPORT_SYMBOL(devm_ioremap_nocache); |
| |
| /** |
| * devm_iounmap - Managed iounmap() |
| * @dev: Generic device to unmap for |
| * @addr: Address to unmap |
| * |
| * Managed iounmap(). @addr must have been mapped using devm_ioremap*(). |
| */ |
| void devm_iounmap(struct device *dev, void __iomem *addr) |
| { |
| iounmap(addr); |
| WARN_ON(devres_destroy(dev, devm_ioremap_release, devm_ioremap_match, |
| (void *)addr)); |
| } |
| EXPORT_SYMBOL(devm_iounmap); |
| |
| /* |
| * PCI iomap devres |
| */ |
| #define PCIM_IOMAP_MAX PCI_ROM_RESOURCE |
| |
| struct pcim_iomap_devres { |
| void __iomem *table[PCIM_IOMAP_MAX]; |
| }; |
| |
| static void pcim_iomap_release(struct device *gendev, void *res) |
| { |
| struct pci_dev *dev = container_of(gendev, struct pci_dev, dev); |
| struct pcim_iomap_devres *this = res; |
| int i; |
| |
| for (i = 0; i < PCIM_IOMAP_MAX; i++) |
| if (this->table[i]) |
| pci_iounmap(dev, this->table[i]); |
| } |
| |
| /** |
| * pcim_iomap_table - access iomap allocation table |
| * @pdev: PCI device to access iomap table for |
| * |
| * Access iomap allocation table for @dev. If iomap table doesn't |
| * exist and @pdev is managed, it will be allocated. All iomaps |
| * recorded in the iomap table are automatically unmapped on driver |
| * detach. |
| * |
| * This function might sleep when the table is first allocated but can |
| * be safely called without context and guaranteed to succed once |
| * allocated. |
| */ |
| void __iomem * const * pcim_iomap_table(struct pci_dev *pdev) |
| { |
| struct pcim_iomap_devres *dr, *new_dr; |
| |
| dr = devres_find(&pdev->dev, pcim_iomap_release, NULL, NULL); |
| if (dr) |
| return dr->table; |
| |
| new_dr = devres_alloc(pcim_iomap_release, sizeof(*new_dr), GFP_KERNEL); |
| if (!new_dr) |
| return NULL; |
| dr = devres_get(&pdev->dev, new_dr, NULL, NULL); |
| return dr->table; |
| } |
| EXPORT_SYMBOL(pcim_iomap_table); |
| |
| /** |
| * pcim_iomap - Managed pcim_iomap() |
| * @pdev: PCI device to iomap for |
| * @bar: BAR to iomap |
| * @maxlen: Maximum length of iomap |
| * |
| * Managed pci_iomap(). Map is automatically unmapped on driver |
| * detach. |
| */ |
| void __iomem * pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen) |
| { |
| void __iomem **tbl; |
| |
| BUG_ON(bar >= PCIM_IOMAP_MAX); |
| |
| tbl = (void __iomem **)pcim_iomap_table(pdev); |
| if (!tbl || tbl[bar]) /* duplicate mappings not allowed */ |
| return NULL; |
| |
| tbl[bar] = pci_iomap(pdev, bar, maxlen); |
| return tbl[bar]; |
| } |
| EXPORT_SYMBOL(pcim_iomap); |
| |
| /** |
| * pcim_iounmap - Managed pci_iounmap() |
| * @pdev: PCI device to iounmap for |
| * @addr: Address to unmap |
| * |
| * Managed pci_iounmap(). @addr must have been mapped using pcim_iomap(). |
| */ |
| void pcim_iounmap(struct pci_dev *pdev, void __iomem *addr) |
| { |
| void __iomem **tbl; |
| int i; |
| |
| pci_iounmap(pdev, addr); |
| |
| tbl = (void __iomem **)pcim_iomap_table(pdev); |
| BUG_ON(!tbl); |
| |
| for (i = 0; i < PCIM_IOMAP_MAX; i++) |
| if (tbl[i] == addr) { |
| tbl[i] = NULL; |
| return; |
| } |
| WARN_ON(1); |
| } |
| EXPORT_SYMBOL(pcim_iounmap); |
| |
| /** |
| * pcim_iomap_regions - Request and iomap PCI BARs |
| * @pdev: PCI device to map IO resources for |
| * @mask: Mask of BARs to request and iomap |
| * @name: Name used when requesting regions |
| * |
| * Request and iomap regions specified by @mask. |
| */ |
| int pcim_iomap_regions(struct pci_dev *pdev, u16 mask, const char *name) |
| { |
| void __iomem * const *iomap; |
| int i, rc; |
| |
| iomap = pcim_iomap_table(pdev); |
| if (!iomap) |
| return -ENOMEM; |
| |
| for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { |
| unsigned long len; |
| |
| if (!(mask & (1 << i))) |
| continue; |
| |
| rc = -EINVAL; |
| len = pci_resource_len(pdev, i); |
| if (!len) |
| goto err_inval; |
| |
| rc = pci_request_region(pdev, i, name); |
| if (rc) |
| goto err_region; |
| |
| rc = -ENOMEM; |
| if (!pcim_iomap(pdev, i, 0)) |
| goto err_iomap; |
| } |
| |
| return 0; |
| |
| err_iomap: |
| pcim_iounmap(pdev, iomap[i]); |
| err_region: |
| pci_release_region(pdev, i); |
| err_inval: |
| while (--i >= 0) { |
| pcim_iounmap(pdev, iomap[i]); |
| pci_release_region(pdev, i); |
| } |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(pcim_iomap_regions); |