diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2006-12-07 09:35:43 -0500 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2007-02-18 16:31:34 -0500 |
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 5ad501b30b43..9e5d985936b3 100644 --- a/arch/mips/lib/Makefile +++ b/arch/mips/lib/Makefile | |||
@@ -5,7 +5,8 @@ | |||
5 | lib-y += csum_partial.o memcpy.o memset.o promlib.o \ | 5 | lib-y += csum_partial.o memcpy.o memset.o promlib.o \ |
6 | strlen_user.o strncpy_user.o strnlen_user.o uncached.o | 6 | strlen_user.o strncpy_user.o strnlen_user.o uncached.o |
7 | 7 | ||
8 | obj-y += iomap.o | 8 | obj-y += iomap.o |
9 | obj-$(CONFIG_PCI) += iomap-pci.o | ||
9 | 10 | ||
10 | # libgcc-style stuff needed in the kernel | 11 | # libgcc-style stuff needed in the kernel |
11 | lib-y += ashldi3.o ashrdi3.o lshrdi3.o | 12 | 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 000000000000..c11b2494bb6e --- /dev/null +++ b/arch/mips/lib/iomap-pci.c | |||
@@ -0,0 +1,74 @@ | |||
1 | /* | ||
2 | * Implement the default iomap interfaces | ||
3 | * | ||
4 | * (C) Copyright 2004 Linus Torvalds | ||
5 | * (C) Copyright 2006 Ralf Baechle <ralf@linux-mips.org> | ||
6 | * (C) Copyright 2007 MIPS Technologies, Inc. | ||
7 | * written by Ralf Baechle <ralf@linux-mips.org> | ||
8 | */ | ||
9 | #include <linux/pci.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <asm/io.h> | ||
12 | |||
13 | static void __iomem *ioport_map_pci(struct pci_dev *dev, | ||
14 | unsigned long port, unsigned int nr) | ||
15 | { | ||
16 | struct pci_controller *ctrl = dev->bus->sysdata; | ||
17 | unsigned long base = ctrl->io_map_base; | ||
18 | |||
19 | /* This will eventually become a BUG_ON but for now be gentle */ | ||
20 | if (unlikely(!ctrl->io_map_base)) { | ||
21 | struct pci_bus *bus = dev->bus; | ||
22 | char name[8]; | ||
23 | |||
24 | while (bus->parent) | ||
25 | bus = bus->parent; | ||
26 | |||
27 | ctrl->io_map_base = base = mips_io_port_base; | ||
28 | |||
29 | sprintf(name, "%04x:%02x", pci_domain_nr(bus), bus->number); | ||
30 | printk(KERN_WARNING "io_map_base of root PCI bus %s unset. " | ||
31 | "Trying to continue but you better\nfix this issue or " | ||
32 | "report it to linux-mips@linux-mips.org or your " | ||
33 | "vendor.\n", name); | ||
34 | #ifdef CONFIG_PCI_DOMAINS | ||
35 | panic("To avoid data corruption io_map_base MUST be set with " | ||
36 | "multiple PCI domains."); | ||
37 | #endif | ||
38 | } | ||
39 | |||
40 | return (void __iomem *) (ctrl->io_map_base + port); | ||
41 | } | ||
42 | |||
43 | /* | ||
44 | * Create a virtual mapping cookie for a PCI BAR (memory or IO) | ||
45 | */ | ||
46 | void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) | ||
47 | { | ||
48 | unsigned long start = pci_resource_start(dev, bar); | ||
49 | unsigned long len = pci_resource_len(dev, bar); | ||
50 | unsigned long flags = pci_resource_flags(dev, bar); | ||
51 | |||
52 | if (!len || !start) | ||
53 | return NULL; | ||
54 | if (maxlen && len > maxlen) | ||
55 | len = maxlen; | ||
56 | if (flags & IORESOURCE_IO) | ||
57 | return ioport_map_pci(dev, start, len); | ||
58 | if (flags & IORESOURCE_MEM) { | ||
59 | if (flags & IORESOURCE_CACHEABLE) | ||
60 | return ioremap(start, len); | ||
61 | return ioremap_nocache(start, len); | ||
62 | } | ||
63 | /* What? */ | ||
64 | return NULL; | ||
65 | } | ||
66 | |||
67 | EXPORT_SYMBOL(pci_iomap); | ||
68 | |||
69 | void pci_iounmap(struct pci_dev *dev, void __iomem * addr) | ||
70 | { | ||
71 | iounmap(addr); | ||
72 | } | ||
73 | |||
74 | EXPORT_SYMBOL(pci_iounmap); | ||
diff --git a/arch/mips/lib/iomap.c b/arch/mips/lib/iomap.c index f4ac5bbcd81f..d51d5cb0a4a9 100644 --- a/arch/mips/lib/iomap.c +++ b/arch/mips/lib/iomap.c | |||
@@ -1,78 +1,227 @@ | |||
1 | /* | 1 | /* |
2 | * iomap.c, Memory Mapped I/O routines for MIPS architecture. | 2 | * Implement the default iomap interfaces |
3 | * | 3 | * |
4 | * This code is based on lib/iomap.c, by Linus Torvalds. | 4 | * (C) Copyright 2004 Linus Torvalds |
5 | * | 5 | * (C) Copyright 2006 Ralf Baechle <ralf@linux-mips.org> |
6 | * Copyright (C) 2004-2005 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> | 6 | * (C) Copyright 2007 MIPS Technologies, Inc. |
7 | * | 7 | * written by Ralf Baechle <ralf@linux-mips.org> |
8 | * This program is free software; you can redistribute it and/or modify | 8 | */ |
9 | * it under the terms of the GNU General Public License as published by | 9 | #include <linux/pci.h> |
10 | * the Free Software Foundation; either version 2 of the License, or | 10 | #include <linux/module.h> |
11 | * (at your option) any later version. | 11 | #include <asm/io.h> |
12 | |||
13 | /* | ||
14 | * Read/write from/to an (offsettable) iomem cookie. It might be a PIO | ||
15 | * access or a MMIO access, these functions don't care. The info is | ||
16 | * encoded in the hardware mapping set up by the mapping functions | ||
17 | * (or the cookie itself, depending on implementation and hw). | ||
12 | * | 18 | * |
13 | * This program is distributed in the hope that it will be useful, | 19 | * The generic routines don't assume any hardware mappings, and just |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 20 | * encode the PIO/MMIO as part of the cookie. They coldly assume that |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 21 | * the MMIO IO mappings are not in the low address range. |
16 | * GNU General Public License for more details. | ||
17 | * | 22 | * |
18 | * You should have received a copy of the GNU General Public License | 23 | * Architectures for which this is not true can't use this generic |
19 | * along with this program; if not, write to the Free Software | 24 | * implementation and should do their own copy. |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | 25 | */ |
22 | #include <linux/ioport.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/pci.h> | ||
25 | 26 | ||
26 | #include <asm/io.h> | 27 | #define PIO_MASK 0x0ffffUL |
27 | 28 | ||
28 | void __iomem *ioport_map(unsigned long port, unsigned int nr) | 29 | unsigned int ioread8(void __iomem *addr) |
29 | { | 30 | { |
30 | unsigned long end; | 31 | return readb(addr); |
32 | } | ||
31 | 33 | ||
32 | end = port + nr - 1UL; | 34 | EXPORT_SYMBOL(ioread8); |
33 | if (ioport_resource.start > port || | ||
34 | ioport_resource.end < end || port > end) | ||
35 | return NULL; | ||
36 | 35 | ||
37 | return (void __iomem *)(mips_io_port_base + port); | 36 | unsigned int ioread16(void __iomem *addr) |
37 | { | ||
38 | return readw(addr); | ||
38 | } | 39 | } |
39 | 40 | ||
40 | void ioport_unmap(void __iomem *addr) | 41 | EXPORT_SYMBOL(ioread16); |
42 | |||
43 | unsigned int ioread16be(void __iomem *addr) | ||
41 | { | 44 | { |
45 | return be16_to_cpu(__raw_readw(addr)); | ||
42 | } | 46 | } |
43 | EXPORT_SYMBOL(ioport_map); | ||
44 | EXPORT_SYMBOL(ioport_unmap); | ||
45 | 47 | ||
46 | void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) | 48 | EXPORT_SYMBOL(ioread16be); |
49 | |||
50 | unsigned int ioread32(void __iomem *addr) | ||
47 | { | 51 | { |
48 | unsigned long start, len, flags; | 52 | return readl(addr); |
53 | } | ||
49 | 54 | ||
50 | if (dev == NULL) | 55 | EXPORT_SYMBOL(ioread32); |
51 | return NULL; | ||
52 | 56 | ||
53 | start = pci_resource_start(dev, bar); | 57 | unsigned int ioread32be(void __iomem *addr) |
54 | len = pci_resource_len(dev, bar); | 58 | { |
55 | if (!start || !len) | 59 | return be32_to_cpu(__raw_readl(addr)); |
56 | return NULL; | 60 | } |
57 | 61 | ||
58 | if (maxlen != 0 && len > maxlen) | 62 | EXPORT_SYMBOL(ioread32be); |
59 | len = maxlen; | 63 | |
64 | void iowrite8(u8 val, void __iomem *addr) | ||
65 | { | ||
66 | writeb(val, addr); | ||
67 | } | ||
60 | 68 | ||
61 | flags = pci_resource_flags(dev, bar); | 69 | EXPORT_SYMBOL(iowrite8); |
62 | if (flags & IORESOURCE_IO) | 70 | |
63 | return ioport_map(start, len); | 71 | void iowrite16(u16 val, void __iomem *addr) |
64 | if (flags & IORESOURCE_MEM) { | 72 | { |
65 | if (flags & IORESOURCE_CACHEABLE) | 73 | writew(val, addr); |
66 | return ioremap_cachable(start, len); | 74 | } |
67 | return ioremap_nocache(start, len); | 75 | |
76 | EXPORT_SYMBOL(iowrite16); | ||
77 | |||
78 | void iowrite16be(u16 val, void __iomem *addr) | ||
79 | { | ||
80 | __raw_writew(cpu_to_be16(val), addr); | ||
81 | } | ||
82 | |||
83 | EXPORT_SYMBOL(iowrite16be); | ||
84 | |||
85 | void iowrite32(u32 val, void __iomem *addr) | ||
86 | { | ||
87 | writel(val, addr); | ||
88 | } | ||
89 | |||
90 | EXPORT_SYMBOL(iowrite32); | ||
91 | |||
92 | void iowrite32be(u32 val, void __iomem *addr) | ||
93 | { | ||
94 | __raw_writel(cpu_to_be32(val), addr); | ||
95 | } | ||
96 | |||
97 | EXPORT_SYMBOL(iowrite32be); | ||
98 | |||
99 | /* | ||
100 | * These are the "repeat MMIO read/write" functions. | ||
101 | * Note the "__raw" accesses, since we don't want to | ||
102 | * convert to CPU byte order. We write in "IO byte | ||
103 | * order" (we also don't have IO barriers). | ||
104 | */ | ||
105 | static inline void mmio_insb(void __iomem *addr, u8 *dst, int count) | ||
106 | { | ||
107 | while (--count >= 0) { | ||
108 | u8 data = __raw_readb(addr); | ||
109 | *dst = data; | ||
110 | dst++; | ||
68 | } | 111 | } |
112 | } | ||
69 | 113 | ||
70 | return NULL; | 114 | static inline void mmio_insw(void __iomem *addr, u16 *dst, int count) |
115 | { | ||
116 | while (--count >= 0) { | ||
117 | u16 data = __raw_readw(addr); | ||
118 | *dst = data; | ||
119 | dst++; | ||
120 | } | ||
71 | } | 121 | } |
72 | 122 | ||
73 | void pci_iounmap(struct pci_dev *dev, void __iomem *addr) | 123 | static inline void mmio_insl(void __iomem *addr, u32 *dst, int count) |
74 | { | 124 | { |
75 | iounmap(addr); | 125 | while (--count >= 0) { |
126 | u32 data = __raw_readl(addr); | ||
127 | *dst = data; | ||
128 | dst++; | ||
129 | } | ||
76 | } | 130 | } |
77 | EXPORT_SYMBOL(pci_iomap); | 131 | |
78 | EXPORT_SYMBOL(pci_iounmap); | 132 | static inline void mmio_outsb(void __iomem *addr, const u8 *src, int count) |
133 | { | ||
134 | while (--count >= 0) { | ||
135 | __raw_writeb(*src, addr); | ||
136 | src++; | ||
137 | } | ||
138 | } | ||
139 | |||
140 | static inline void mmio_outsw(void __iomem *addr, const u16 *src, int count) | ||
141 | { | ||
142 | while (--count >= 0) { | ||
143 | __raw_writew(*src, addr); | ||
144 | src++; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | static inline void mmio_outsl(void __iomem *addr, const u32 *src, int count) | ||
149 | { | ||
150 | while (--count >= 0) { | ||
151 | __raw_writel(*src, addr); | ||
152 | src++; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | void ioread8_rep(void __iomem *addr, void *dst, unsigned long count) | ||
157 | { | ||
158 | mmio_insb(addr, dst, count); | ||
159 | } | ||
160 | |||
161 | EXPORT_SYMBOL(ioread8_rep); | ||
162 | |||
163 | void ioread16_rep(void __iomem *addr, void *dst, unsigned long count) | ||
164 | { | ||
165 | mmio_insw(addr, dst, count); | ||
166 | } | ||
167 | |||
168 | EXPORT_SYMBOL(ioread16_rep); | ||
169 | |||
170 | void ioread32_rep(void __iomem *addr, void *dst, unsigned long count) | ||
171 | { | ||
172 | mmio_insl(addr, dst, count); | ||
173 | } | ||
174 | |||
175 | EXPORT_SYMBOL(ioread32_rep); | ||
176 | |||
177 | void iowrite8_rep(void __iomem *addr, const void *src, unsigned long count) | ||
178 | { | ||
179 | mmio_outsb(addr, src, count); | ||
180 | } | ||
181 | |||
182 | EXPORT_SYMBOL(iowrite8_rep); | ||
183 | |||
184 | void iowrite16_rep(void __iomem *addr, const void *src, unsigned long count) | ||
185 | { | ||
186 | mmio_outsw(addr, src, count); | ||
187 | } | ||
188 | |||
189 | EXPORT_SYMBOL(iowrite16_rep); | ||
190 | |||
191 | void iowrite32_rep(void __iomem *addr, const void *src, unsigned long count) | ||
192 | { | ||
193 | mmio_outsl(addr, src, count); | ||
194 | } | ||
195 | |||
196 | EXPORT_SYMBOL(iowrite32_rep); | ||
197 | |||
198 | /* | ||
199 | * Create a virtual mapping cookie for an IO port range | ||
200 | * | ||
201 | * This uses the same mapping are as the in/out family which has to be setup | ||
202 | * by the platform initialization code. | ||
203 | * | ||
204 | * Just to make matters somewhat more interesting on MIPS systems with | ||
205 | * multiple host bridge each will have it's own ioport address space. | ||
206 | */ | ||
207 | static void __iomem *ioport_map_legacy(unsigned long port, unsigned int nr) | ||
208 | { | ||
209 | return (void __iomem *) (mips_io_port_base + port); | ||
210 | } | ||
211 | |||
212 | void __iomem *ioport_map(unsigned long port, unsigned int nr) | ||
213 | { | ||
214 | if (port > PIO_MASK) | ||
215 | return NULL; | ||
216 | |||
217 | return ioport_map_legacy(port, nr); | ||
218 | } | ||
219 | |||
220 | EXPORT_SYMBOL(ioport_map); | ||
221 | |||
222 | void ioport_unmap(void __iomem *addr) | ||
223 | { | ||
224 | /* Nothing to do */ | ||
225 | } | ||
226 | |||
227 | EXPORT_SYMBOL(ioport_unmap); | ||
diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c index 5ace368657ad..697a7e48cb8d 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) | |||
79 | { | 79 | { |
80 | *hose_tail = hose; | 80 | *hose_tail = hose; |
81 | hose_tail = &hose->next; | 81 | hose_tail = &hose->next; |
82 | |||
83 | /* | ||
84 | * Do not panic here but later - this might hapen before console init. | ||
85 | */ | ||
86 | if (!hose->io_map_base) { | ||
87 | printk(KERN_WARNING | ||
88 | "registering PCI controller with io_map_base unset\n"); | ||
89 | } | ||
82 | } | 90 | } |
83 | 91 | ||
84 | /* Most MIPS systems have straight-forward swizzling needs. */ | 92 | /* Most MIPS systems have straight-forward swizzling needs. */ |
diff --git a/include/asm-mips/io.h b/include/asm-mips/io.h index b6a2eb816628..92ec2618560c 100644 --- a/include/asm-mips/io.h +++ b/include/asm-mips/io.h | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <asm/byteorder.h> | 20 | #include <asm/byteorder.h> |
21 | #include <asm/cpu.h> | 21 | #include <asm/cpu.h> |
22 | #include <asm/cpu-features.h> | 22 | #include <asm/cpu-features.h> |
23 | #include <asm-generic/iomap.h> | ||
23 | #include <asm/page.h> | 24 | #include <asm/page.h> |
24 | #include <asm/pgtable-bits.h> | 25 | #include <asm/pgtable-bits.h> |
25 | #include <asm/processor.h> | 26 | #include <asm/processor.h> |
@@ -518,34 +519,6 @@ static inline void memcpy_toio(volatile void __iomem *dst, const void *src, int | |||
518 | } | 519 | } |
519 | 520 | ||
520 | /* | 521 | /* |
521 | * Memory Mapped I/O | ||
522 | */ | ||
523 | #define ioread8(addr) readb(addr) | ||
524 | #define ioread16(addr) readw(addr) | ||
525 | #define ioread32(addr) readl(addr) | ||
526 | |||
527 | #define iowrite8(b,addr) writeb(b,addr) | ||
528 | #define iowrite16(w,addr) writew(w,addr) | ||
529 | #define iowrite32(l,addr) writel(l,addr) | ||
530 | |||
531 | #define ioread8_rep(a,b,c) readsb(a,b,c) | ||
532 | #define ioread16_rep(a,b,c) readsw(a,b,c) | ||
533 | #define ioread32_rep(a,b,c) readsl(a,b,c) | ||
534 | |||
535 | #define iowrite8_rep(a,b,c) writesb(a,b,c) | ||
536 | #define iowrite16_rep(a,b,c) writesw(a,b,c) | ||
537 | #define iowrite32_rep(a,b,c) writesl(a,b,c) | ||
538 | |||
539 | /* Create a virtual mapping cookie for an IO port range */ | ||
540 | extern void __iomem *ioport_map(unsigned long port, unsigned int nr); | ||
541 | extern void ioport_unmap(void __iomem *); | ||
542 | |||
543 | /* Create a virtual mapping cookie for a PCI BAR (memory or IO) */ | ||
544 | struct pci_dev; | ||
545 | extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); | ||
546 | extern void pci_iounmap(struct pci_dev *dev, void __iomem *); | ||
547 | |||
548 | /* | ||
549 | * ISA space is 'always mapped' on currently supported MIPS systems, no need | 522 | * ISA space is 'always mapped' on currently supported MIPS systems, no need |
550 | * to explicitly ioremap() it. The fact that the ISA IO space is mapped | 523 | * to explicitly ioremap() it. The fact that the ISA IO space is mapped |
551 | * to PAGE_OFFSET is pure coincidence - it does not mean ISA values | 524 | * 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 7f0f120ca07c..3eea3ba0fca5 100644 --- a/include/asm-mips/pci.h +++ b/include/asm-mips/pci.h | |||
@@ -32,6 +32,7 @@ struct pci_controller { | |||
32 | unsigned long mem_offset; | 32 | unsigned long mem_offset; |
33 | struct resource *io_resource; | 33 | struct resource *io_resource; |
34 | unsigned long io_offset; | 34 | unsigned long io_offset; |
35 | unsigned long io_map_base; | ||
35 | 36 | ||
36 | unsigned int index; | 37 | unsigned int index; |
37 | /* For compatibility with current (as of July 2003) pciutils | 38 | /* For compatibility with current (as of July 2003) pciutils |