Skip to content

Commit

Permalink
[MIPS] Iomap implementation.
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
ralfbaechle committed Feb 18, 2007
1 parent 4c15699 commit 140c172
Showing 6 changed files with 287 additions and 81 deletions.
3 changes: 2 additions & 1 deletion arch/mips/lib/Makefile
Original file line number Diff line number Diff line change
@@ -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
74 changes: 74 additions & 0 deletions arch/mips/lib/iomap-pci.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Implement the default iomap interfaces
*
* (C) Copyright 2004 Linus Torvalds
* (C) Copyright 2006 Ralf Baechle <[email protected]>
* (C) Copyright 2007 MIPS Technologies, Inc.
* written by Ralf Baechle <[email protected]>
*/
#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 [email protected] 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);
253 changes: 201 additions & 52 deletions arch/mips/lib/iomap.c
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
*
* 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 <[email protected]>
* (C) Copyright 2007 MIPS Technologies, Inc.
* written by Ralf Baechle <[email protected]>
*/
#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);
8 changes: 8 additions & 0 deletions arch/mips/pci/pci.c
Original file line number Diff line number Diff line change
@@ -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. */
Loading

0 comments on commit 140c172

Please sign in to comment.