1 | /* $NetBSD: cardbus_map.c,v 1.36 2010/03/15 19:48:31 dyoung Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1999 and 2000 |
5 | * HAYAKAWA Koichi. All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
19 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
20 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
22 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
24 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
25 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
26 | * POSSIBILITY OF SUCH DAMAGE. |
27 | */ |
28 | |
29 | #include <sys/cdefs.h> |
30 | __KERNEL_RCSID(0, "$NetBSD: cardbus_map.c,v 1.36 2010/03/15 19:48:31 dyoung Exp $" ); |
31 | |
32 | #include <sys/param.h> |
33 | #include <sys/systm.h> |
34 | #include <sys/device.h> |
35 | |
36 | #include <sys/bus.h> |
37 | |
38 | #include <dev/cardbus/cardbusvar.h> |
39 | |
40 | #include <dev/pci/pcireg.h> /* XXX */ |
41 | |
42 | #if defined DEBUG && !defined CARDBUS_MAP_DEBUG |
43 | #define CARDBUS_MAP_DEBUG |
44 | #endif |
45 | |
46 | #if defined CARDBUS_MAP_DEBUG |
47 | #define STATIC |
48 | #define DPRINTF(a) printf a |
49 | #else |
50 | #define STATIC static |
51 | #define DPRINTF(a) |
52 | #endif |
53 | |
54 | |
55 | static int cardbus_io_find(cardbus_chipset_tag_t, cardbus_function_tag_t, |
56 | pcitag_t, int, pcireg_t, |
57 | bus_addr_t *, bus_size_t *, int *); |
58 | static int cardbus_mem_find(cardbus_chipset_tag_t, cardbus_function_tag_t, |
59 | pcitag_t, int, pcireg_t, |
60 | bus_addr_t *, bus_size_t *, int *); |
61 | |
62 | /* |
63 | * static int cardbus_io_find(cardbus_chipset_tag_t cc, |
64 | * cardbus_function_tag_t cf, pcitag_t tag, |
65 | * int reg, pcireg_t type, bus_addr_t *basep, |
66 | * bus_size_t *sizep, int *flagsp) |
67 | * This code is stolen from sys/dev/pci_map.c. |
68 | */ |
69 | static int |
70 | cardbus_io_find( |
71 | cardbus_chipset_tag_t cc, |
72 | cardbus_function_tag_t cf, |
73 | pcitag_t tag, |
74 | int reg, |
75 | pcireg_t type, |
76 | bus_addr_t *basep, |
77 | bus_size_t *sizep, |
78 | int *flagsp) |
79 | { |
80 | pcireg_t address, mask; |
81 | int s; |
82 | |
83 | /* EXT ROM is able to map on memory space ONLY. */ |
84 | if (reg == CARDBUS_ROM_REG) { |
85 | return 1; |
86 | } |
87 | |
88 | if(reg < PCI_MAPREG_START || reg >= PCI_MAPREG_END || (reg & 3)) { |
89 | panic("cardbus_io_find: bad request" ); |
90 | } |
91 | |
92 | /* |
93 | * Section 6.2.5.1, `Address Maps', tells us that: |
94 | * |
95 | * 1) The builtin software should have already mapped the device in a |
96 | * reasonable way. |
97 | * |
98 | * 2) A device which wants 2^n bytes of memory will hardwire the bottom |
99 | * n bits of the address to 0. As recommended, we write all 1s and see |
100 | * what we get back. |
101 | */ |
102 | s = splhigh(); |
103 | address = cardbus_conf_read(cc, cf, tag, reg); |
104 | cardbus_conf_write(cc, cf, tag, reg, 0xffffffff); |
105 | mask = cardbus_conf_read(cc, cf, tag, reg); |
106 | cardbus_conf_write(cc, cf, tag, reg, address); |
107 | splx(s); |
108 | |
109 | if (PCI_MAPREG_TYPE(address) != PCI_MAPREG_TYPE_IO) { |
110 | printf("cardbus_io_find: expected type i/o, found mem\n" ); |
111 | return 1; |
112 | } |
113 | |
114 | if (PCI_MAPREG_IO_SIZE(mask) == 0) { |
115 | printf("cardbus_io_find: void region\n" ); |
116 | return 1; |
117 | } |
118 | |
119 | if (basep != 0) { |
120 | *basep = PCI_MAPREG_IO_ADDR(address); |
121 | } |
122 | if (sizep != 0) { |
123 | *sizep = PCI_MAPREG_IO_SIZE(mask); |
124 | } |
125 | if (flagsp != 0) { |
126 | *flagsp = 0; |
127 | } |
128 | |
129 | return 0; |
130 | } |
131 | |
132 | |
133 | |
134 | /* |
135 | * static int cardbus_mem_find(cardbus_chipset_tag_t cc, |
136 | * cardbus_function_tag_t cf, pcitag_t tag, |
137 | * int reg, pcireg_t type, bus_addr_t *basep, |
138 | * bus_size_t *sizep, int *flagsp) |
139 | * This code is stolen from sys/dev/pci_map.c. |
140 | */ |
141 | static int |
142 | cardbus_mem_find(cardbus_chipset_tag_t cc, cardbus_function_tag_t cf, pcitag_t tag, int reg, pcireg_t type, bus_addr_t *basep, bus_size_t *sizep, int *flagsp) |
143 | { |
144 | pcireg_t address, mask; |
145 | int s; |
146 | |
147 | if (reg != CARDBUS_ROM_REG && |
148 | (reg < PCI_MAPREG_START || reg >= PCI_MAPREG_END || (reg & 3))) { |
149 | panic("cardbus_mem_find: bad request" ); |
150 | } |
151 | |
152 | /* |
153 | * Section 6.2.5.1, `Address Maps', tells us that: |
154 | * |
155 | * 1) The builtin software should have already mapped the device in a |
156 | * reasonable way. |
157 | * |
158 | * 2) A device which wants 2^n bytes of memory will hardwire the bottom |
159 | * n bits of the address to 0. As recommended, we write all 1s and see |
160 | * what we get back. |
161 | */ |
162 | s = splhigh(); |
163 | address = cardbus_conf_read(cc, cf, tag, reg); |
164 | cardbus_conf_write(cc, cf, tag, reg, 0xffffffff); |
165 | mask = cardbus_conf_read(cc, cf, tag, reg); |
166 | cardbus_conf_write(cc, cf, tag, reg, address); |
167 | splx(s); |
168 | |
169 | if (reg != CARDBUS_ROM_REG) { |
170 | /* memory space BAR */ |
171 | |
172 | if (PCI_MAPREG_TYPE(address) != PCI_MAPREG_TYPE_MEM) { |
173 | printf("cardbus_mem_find: expected type mem, found i/o\n" ); |
174 | return 1; |
175 | } |
176 | if (PCI_MAPREG_MEM_TYPE(address) != PCI_MAPREG_MEM_TYPE(type)) { |
177 | printf("cardbus_mem_find: expected mem type %08x, found %08x\n" , |
178 | PCI_MAPREG_MEM_TYPE(type), |
179 | PCI_MAPREG_MEM_TYPE(address)); |
180 | return 1; |
181 | } |
182 | } |
183 | |
184 | if (PCI_MAPREG_MEM_SIZE(mask) == 0) { |
185 | printf("cardbus_mem_find: void region\n" ); |
186 | return 1; |
187 | } |
188 | |
189 | switch (PCI_MAPREG_MEM_TYPE(address)) { |
190 | case PCI_MAPREG_MEM_TYPE_32BIT: |
191 | case PCI_MAPREG_MEM_TYPE_32BIT_1M: |
192 | break; |
193 | case PCI_MAPREG_MEM_TYPE_64BIT: |
194 | printf("cardbus_mem_find: 64-bit memory mapping register\n" ); |
195 | return 1; |
196 | default: |
197 | printf("cardbus_mem_find: reserved mapping register type\n" ); |
198 | return 1; |
199 | } |
200 | |
201 | if (basep != 0) { |
202 | *basep = PCI_MAPREG_MEM_ADDR(address); |
203 | } |
204 | if (sizep != 0) { |
205 | *sizep = PCI_MAPREG_MEM_SIZE(mask); |
206 | } |
207 | if (flagsp != 0) { |
208 | *flagsp = PCI_MAPREG_MEM_PREFETCHABLE(address) ? |
209 | BUS_SPACE_MAP_PREFETCHABLE : 0; |
210 | } |
211 | |
212 | return 0; |
213 | } |
214 | |
215 | |
216 | |
217 | |
218 | /* |
219 | * int cardbus_mapreg_map(struct cardbus_softc *, int, int, pcireg_t, |
220 | * int bus_space_tag_t *, bus_space_handle_t *, |
221 | * bus_addr_t *, bus_size_t *) |
222 | * This function maps bus-space on the value of Base Address |
223 | * Register (BAR) indexed by the argument `reg' (the second argument). |
224 | * When the value of the BAR is not valid, such as 0x00000000, a new |
225 | * address should be allocated for the BAR and new address values is |
226 | * written on the BAR. |
227 | */ |
228 | int |
229 | cardbus_mapreg_map(struct cardbus_softc *sc, int func, int reg, pcireg_t type, int busflags, bus_space_tag_t *tagp, bus_space_handle_t *handlep, bus_addr_t *basep, bus_size_t *sizep) |
230 | { |
231 | cardbus_chipset_tag_t cc = sc->sc_cc; |
232 | cardbus_function_tag_t cf = sc->sc_cf; |
233 | bus_space_tag_t bustag; |
234 | rbus_tag_t rbustag; |
235 | bus_space_handle_t handle; |
236 | bus_addr_t base; |
237 | bus_size_t size; |
238 | int flags; |
239 | int status = 0; |
240 | pcitag_t tag; |
241 | |
242 | size = 0; /* XXX gcc */ |
243 | flags = 0; /* XXX gcc */ |
244 | |
245 | tag = cardbus_make_tag(cc, cf, sc->sc_bus, func); |
246 | |
247 | DPRINTF(("cardbus_mapreg_map called: %s %x\n" , device_xname(sc->sc_dev), |
248 | type)); |
249 | |
250 | if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO) { |
251 | if (cardbus_io_find(cc, cf, tag, reg, type, &base, &size, &flags)) { |
252 | status = 1; |
253 | } |
254 | bustag = sc->sc_iot; |
255 | rbustag = sc->sc_rbus_iot; |
256 | } else { |
257 | if (cardbus_mem_find(cc, cf, tag, reg, type, &base, &size, &flags)){ |
258 | status = 1; |
259 | } |
260 | bustag = sc->sc_memt; |
261 | rbustag = sc->sc_rbus_memt; |
262 | } |
263 | if (status == 0) { |
264 | bus_addr_t mask = size - 1; |
265 | if (base != 0) { |
266 | mask = 0xffffffff; |
267 | } |
268 | if ((*cf->cardbus_space_alloc)(cc, rbustag, base, size, mask, |
269 | size, busflags | flags, &base, &handle)) { |
270 | panic("io alloc" ); |
271 | } |
272 | } |
273 | cardbus_conf_write(cc, cf, tag, reg, base); |
274 | |
275 | DPRINTF(("cardbus_mapreg_map: physaddr %lx\n" , (unsigned long)base)); |
276 | |
277 | if (tagp != 0) { |
278 | *tagp = bustag; |
279 | } |
280 | if (handlep != 0) { |
281 | *handlep = handle; |
282 | } |
283 | if (basep != 0) { |
284 | *basep = base; |
285 | } |
286 | if (sizep != 0) { |
287 | *sizep = size; |
288 | } |
289 | |
290 | return 0; |
291 | } |
292 | |
293 | |
294 | |
295 | |
296 | |
297 | /* |
298 | * int cardbus_mapreg_unmap(struct cardbus_softc *sc, int func, int reg, |
299 | * bus_space_tag_t tag, bus_space_handle_t handle, |
300 | * bus_size_t size) |
301 | * |
302 | * This function releases bus-space region and close memory or io |
303 | * window on the bridge. |
304 | * |
305 | * Arguments: |
306 | * struct cardbus_softc *sc; the pointer to the device structure of cardbus. |
307 | * int func; the number of function on the device. |
308 | * int reg; the offset of BAR register. |
309 | */ |
310 | int |
311 | cardbus_mapreg_unmap(struct cardbus_softc *sc, int func, int reg, bus_space_tag_t tag, bus_space_handle_t handle, bus_size_t size) |
312 | { |
313 | cardbus_chipset_tag_t cc = sc->sc_cc; |
314 | cardbus_function_tag_t cf = sc->sc_cf; |
315 | int st = 1; |
316 | pcitag_t cardbustag; |
317 | rbus_tag_t rbustag; |
318 | |
319 | if (sc->sc_iot == tag) { |
320 | /* bus space is io space */ |
321 | DPRINTF(("%s: unmap i/o space\n" , device_xname(sc->sc_dev))); |
322 | rbustag = sc->sc_rbus_iot; |
323 | } else if (sc->sc_memt == tag) { |
324 | /* bus space is memory space */ |
325 | DPRINTF(("%s: unmap mem space\n" , device_xname(sc->sc_dev))); |
326 | rbustag = sc->sc_rbus_memt; |
327 | } else { |
328 | return 1; |
329 | } |
330 | |
331 | cardbustag = cardbus_make_tag(cc, cf, sc->sc_bus, func); |
332 | |
333 | cardbus_conf_write(cc, cf, cardbustag, reg, 0); |
334 | |
335 | (*cf->cardbus_space_free)(cc, rbustag, handle, size); |
336 | |
337 | return st; |
338 | } |
339 | |