Commit | Line | Data |
---|---|---|
c906108c SS |
1 | /* This file is part of the program psim. |
2 | ||
3 | Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au> | |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 2 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program; if not, write to the Free Software | |
17 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
18 | ||
19 | */ | |
20 | ||
21 | ||
22 | #ifndef _HW_PHB_C_ | |
23 | #define _HW_PHB_C_ | |
24 | ||
25 | #include "device_table.h" | |
26 | ||
27 | #include "hw_phb.h" | |
28 | ||
29 | #include "corefile.h" | |
30 | ||
31 | #ifdef HAVE_STDLIB_H | |
32 | #include <stdlib.h> | |
33 | #endif | |
34 | ||
35 | #include <ctype.h> | |
36 | ||
37 | ||
38 | /* DEVICE | |
39 | ||
40 | ||
41 | phb - PCI Host Bridge | |
42 | ||
43 | ||
44 | DESCRIPTION | |
45 | ||
46 | ||
47 | PHB implements a model of the PCI-host bridge described in the PPCP | |
48 | document. | |
49 | ||
50 | For bridge devices, Open Firmware specifies that the <<ranges>> | |
51 | property be used to specify the mapping of address spaces between a | |
52 | bridges parent and child busses. This PHB model configures itsself | |
53 | according to the information specified in its ranges property. The | |
54 | <<ranges>> property is described in detail in the Open Firmware | |
55 | documentation. | |
56 | ||
57 | For DMA transfers, any access to a PCI address space which falls | |
58 | outside of the mapped memory space is assumed to be a transfer | |
59 | intended for the parent bus. | |
60 | ||
61 | ||
62 | PROPERTIES | |
63 | ||
64 | ||
65 | ranges = <my-phys-addr> <parent-phys-addr> <my-size> ... (required) | |
66 | ||
67 | Define a number of mappings from the parent bus to one of this | |
68 | devices PCI busses. The exact format of the <<parent-phys-addr>> | |
69 | is parent bus dependant. The format of <<my-phys-addr>> is | |
70 | described in the Open Firmware PCI bindings document (note that the | |
71 | address must be non-relocatable). | |
72 | ||
73 | ||
74 | #address-cells = 3 (required) | |
75 | ||
76 | Number of cells used by an Open Firmware PCI address. This | |
77 | property must be defined before specifying the <<ranges>> property. | |
78 | ||
79 | ||
80 | #size-cells = 2 (required) | |
81 | ||
82 | Number of cells used by an Open Firmware PCI size. This property | |
83 | must be defined before specifying the <<ranges>> property. | |
84 | ||
85 | ||
86 | EXAMPLES | |
87 | ||
88 | ||
89 | Enable tracing: | |
90 | ||
91 | | $ psim \ | |
92 | | -t phb-device \ | |
93 | ||
94 | ||
95 | Since device tree entries that are specified on the command line | |
96 | are added before most of the device tree has been built it is often | |
97 | necessary to explictly add certain device properties and thus | |
98 | ensure they are already present in the device tree. For the | |
99 | <<phb>> one such property is parent busses <<#address-cells>>. | |
100 | ||
101 | | -o '/#address-cells 1' \ | |
102 | ||
103 | ||
104 | Create the PHB remembering to include the cell size properties: | |
105 | ||
106 | | -o '/phb@0x80000000/#address-cells 3' \ | |
107 | | -o '/phb@0x80000000/#size-cells 2' \ | |
108 | ||
109 | ||
110 | Specify that the memory address range <<0x80000000>> to | |
111 | <<0x8fffffff>> should map directly onto the PCI memory address | |
112 | space while the processor address range <<0xc0000000>> to | |
113 | <<0xc000ffff>> should map onto the PCI I/O address range starting | |
114 | at location zero: | |
115 | ||
116 | | -o '/phb@0x80000000/ranges \ | |
117 | | nm0,0,0,80000000 0x80000000 0x10000000 \ | |
118 | | ni0,0,0,0 0xc0000000 0x10000' \ | |
119 | ||
120 | ||
121 | Insert a 4k <<nvram>> into slot zero of the PCI bus. Have it | |
122 | directly accessible in both the I/O (address <<0x100>>) and memory | |
123 | (address 0x80001000) spaces: | |
124 | ||
125 | | -o '/phb@0x80000000/nvram@0/assigned-addresses \ | |
126 | | nm0,0,10,80001000 4096 \ | |
127 | | ni0,0,14,100 4096' | |
128 | | -o '/phb@0x80000000/nvram@0/reg \ | |
129 | | 0 0 \ | |
130 | | i0,0,14,0 4096' | |
131 | | -o '/phb@0x80000000/nvram@0/alternate-reg \ | |
132 | | 0 0 \ | |
133 | | m0,0,10,0 4096' | |
134 | ||
135 | The <<assigned-address>> property corresponding to what (if it were | |
136 | implemented) be found in the config base registers while the | |
137 | <<reg>> and <<alternative-reg>> properties indicating the location | |
138 | of registers within each address space. | |
139 | ||
140 | Of the possible addresses, only the non-relocatable versions are | |
141 | used when attaching the device to the bus. | |
142 | ||
143 | ||
144 | BUGS | |
145 | ||
146 | ||
147 | The implementation of the PCI configuration space is left as an | |
148 | exercise for the reader. Such a restriction should only impact on | |
149 | systems wanting to dynamically configure devices on the PCI bus. | |
150 | ||
151 | The <<CHRP>> document specfies additional (optional) functionality | |
152 | of the primary PHB. The implementation of such functionality is | |
153 | left as an exercise for the reader. | |
154 | ||
155 | The Open Firmware PCI bus bindings document (rev 1.6 and 2.0) is | |
156 | unclear on the value of the "ss" bits for a 64bit memory address. | |
157 | The correct value, as used by this module, is 0b11. | |
158 | ||
159 | The Open Firmware PCI bus bindings document (rev 1.6) suggests that | |
160 | the register field of non-relocatable PCI address should be zero. | |
161 | Unfortunatly, PCI addresses specified in the <<assigned-addresses>> | |
162 | property must be both non-relocatable and have non-zero register | |
163 | fields. | |
164 | ||
165 | The unit-decode method is not inserting a bus number into any | |
166 | address that it decodes. Instead the bus-number is left as zero. | |
167 | ||
168 | Support for aliased memory and I/O addresses is left as an exercise | |
169 | for the reader. | |
170 | ||
171 | Support for interrupt-ack and special cycles are left as an | |
172 | exercise for the reader. One issue to consider when attempting | |
173 | this exercise is how to specify the address of the int-ack and | |
174 | special cycle register. Hint: <</8259-interrupt-ackowledge>> is | |
175 | the wrong answer. | |
176 | ||
177 | Children of this node can only use the client callback interface | |
178 | when attaching themselves to the <<phb>>. | |
179 | ||
180 | ||
181 | REFERENCES | |
182 | ||
183 | ||
184 | http://playground.sun.com/1275/home.html#OFDbusPCI | |
185 | ||
186 | ||
187 | */ | |
188 | ||
189 | ||
190 | typedef struct _phb_space { | |
191 | core *map; | |
192 | core_map *readable; | |
193 | core_map *writeable; | |
194 | unsigned_word parent_base; | |
195 | int parent_space; | |
196 | unsigned_word my_base; | |
197 | int my_space; | |
198 | unsigned size; | |
199 | const char *name; | |
200 | } phb_space; | |
201 | ||
202 | typedef struct _hw_phb_device { | |
203 | phb_space space[nr_hw_phb_spaces]; | |
204 | } hw_phb_device; | |
205 | ||
206 | ||
207 | static const char * | |
208 | hw_phb_decode_name(hw_phb_decode level) | |
209 | { | |
210 | switch (level) { | |
211 | case hw_phb_normal_decode: return "normal"; | |
212 | case hw_phb_subtractive_decode: return "subtractive"; | |
213 | case hw_phb_master_abort_decode: return "master-abort"; | |
214 | default: return "invalid decode"; | |
215 | } | |
216 | } | |
217 | ||
218 | ||
219 | static void | |
220 | hw_phb_init_address(device *me) | |
221 | { | |
222 | hw_phb_device *phb = device_data(me); | |
223 | ||
224 | /* check some basic properties */ | |
225 | if (device_nr_address_cells(me) != 3) | |
226 | device_error(me, "incorrect #address-cells"); | |
227 | if (device_nr_size_cells(me) != 2) | |
228 | device_error(me, "incorrect #size-cells"); | |
229 | ||
230 | /* (re) initialize each PCI space */ | |
231 | { | |
232 | hw_phb_spaces space_nr; | |
233 | for (space_nr = 0; space_nr < nr_hw_phb_spaces; space_nr++) { | |
234 | phb_space *pci_space = &phb->space[space_nr]; | |
235 | core_init(pci_space->map); | |
236 | pci_space->size = 0; | |
237 | } | |
238 | } | |
239 | ||
240 | /* decode each of the ranges properties entering the information | |
241 | into the space table */ | |
242 | { | |
243 | range_property_spec range; | |
244 | int ranges_entry; | |
245 | ||
246 | for (ranges_entry = 0; | |
247 | device_find_range_array_property(me, "ranges", ranges_entry, | |
248 | &range); | |
249 | ranges_entry++) { | |
250 | int my_attach_space; | |
251 | unsigned_word my_attach_address; | |
252 | int parent_attach_space; | |
253 | unsigned_word parent_attach_address; | |
254 | unsigned size; | |
255 | phb_space *pci_space; | |
256 | /* convert the addresses into something meaningful */ | |
257 | device_address_to_attach_address(me, &range.child_address, | |
258 | &my_attach_space, | |
259 | &my_attach_address, | |
260 | me); | |
261 | device_address_to_attach_address(device_parent(me), | |
262 | &range.parent_address, | |
263 | &parent_attach_space, | |
264 | &parent_attach_address, | |
265 | me); | |
266 | device_size_to_attach_size(me, &range.size, &size, me); | |
267 | if (my_attach_space < 0 || my_attach_space >= nr_hw_phb_spaces) | |
268 | device_error(me, "ranges property contains an invalid address space"); | |
269 | pci_space = &phb->space[my_attach_space]; | |
270 | if (pci_space->size != 0) | |
271 | device_error(me, "ranges property contains duplicate mappings for %s address space", | |
272 | pci_space->name); | |
273 | pci_space->parent_base = parent_attach_address; | |
274 | pci_space->parent_space = parent_attach_space; | |
275 | pci_space->my_base = my_attach_address; | |
276 | pci_space->my_space = my_attach_space; | |
277 | pci_space->size = size; | |
278 | device_attach_address(device_parent(me), | |
279 | attach_callback, | |
280 | parent_attach_space, parent_attach_address, size, | |
281 | access_read_write_exec, | |
282 | me); | |
283 | DTRACE(phb, ("map %d:0x%lx to %s:0x%lx (0x%lx bytes)\n", | |
284 | (int)parent_attach_space, | |
285 | (unsigned long)parent_attach_address, | |
286 | pci_space->name, | |
287 | (unsigned long)my_attach_address, | |
288 | (unsigned long)size)); | |
289 | } | |
290 | ||
291 | if (ranges_entry == 0) { | |
292 | device_error(me, "Missing or empty ranges property"); | |
293 | } | |
294 | ||
295 | } | |
296 | ||
297 | } | |
298 | ||
299 | static void | |
300 | hw_phb_attach_address(device *me, | |
301 | attach_type type, | |
302 | int space, | |
303 | unsigned_word addr, | |
304 | unsigned nr_bytes, | |
305 | access_type access, | |
306 | device *client) /*callback/default*/ | |
307 | { | |
308 | hw_phb_device *phb = device_data(me); | |
309 | phb_space *pci_space; | |
310 | /* sanity checks */ | |
311 | if (space < 0 || space >= nr_hw_phb_spaces) | |
312 | device_error(me, "attach space (%d) specified by %s invalid", | |
313 | space, device_path(client)); | |
314 | pci_space = &phb->space[space]; | |
315 | if (addr + nr_bytes > pci_space->my_base + pci_space->size | |
316 | || addr < pci_space->my_base) | |
317 | device_error(me, "attach addr (0x%lx) specified by %s outside of bus address range", | |
318 | (unsigned long)addr, device_path(client)); | |
319 | if (type != hw_phb_normal_decode | |
320 | && type != hw_phb_subtractive_decode) | |
321 | device_error(me, "attach type (%d) specified by %s invalid", | |
322 | type, device_path(client)); | |
323 | /* attach it to the relevent bus */ | |
324 | DTRACE(phb, ("attach %s - %s %s:0x%lx (0x%lx bytes)\n", | |
325 | device_path(client), | |
326 | hw_phb_decode_name(type), | |
327 | pci_space->name, | |
328 | (unsigned long)addr, | |
329 | (unsigned long)nr_bytes)); | |
330 | core_attach(pci_space->map, | |
331 | type, | |
332 | space, | |
333 | access, | |
334 | addr, | |
335 | nr_bytes, | |
336 | client); | |
337 | } | |
338 | ||
339 | ||
340 | /* Extract/set various fields from a PCI unit address. | |
341 | ||
342 | Note: only the least significant 32 bits of each cell is used. | |
343 | ||
344 | Note: for PPC MSB is 0 while for PCI it is 31. */ | |
345 | ||
346 | ||
347 | /* relocatable bit n */ | |
348 | ||
349 | static unsigned | |
350 | extract_n(const device_unit *address) | |
351 | { | |
352 | return EXTRACTED32(address->cells[0], 0, 0); | |
353 | } | |
354 | ||
355 | static void | |
356 | set_n(device_unit *address) | |
357 | { | |
358 | BLIT32(address->cells[0], 0, 1); | |
359 | } | |
360 | ||
361 | ||
362 | /* prefetchable bit p */ | |
363 | ||
364 | static unsigned | |
365 | extract_p(const device_unit *address) | |
366 | { | |
367 | ASSERT(address->nr_cells == 3); | |
368 | return EXTRACTED32(address->cells[0], 1, 1); | |
369 | } | |
370 | ||
371 | static void | |
372 | set_p(device_unit *address) | |
373 | { | |
374 | BLIT32(address->cells[0], 1, 1); | |
375 | } | |
376 | ||
377 | ||
378 | /* aliased bit t */ | |
379 | ||
380 | static unsigned | |
381 | extract_t(const device_unit *address) | |
382 | { | |
383 | ASSERT(address->nr_cells == 3); | |
384 | return EXTRACTED32(address->cells[0], 2, 2); | |
385 | } | |
386 | ||
387 | static void | |
388 | set_t(device_unit *address) | |
389 | { | |
390 | BLIT32(address->cells[0], 2, 1); | |
391 | } | |
392 | ||
393 | ||
394 | /* space code ss */ | |
395 | ||
396 | typedef enum { | |
397 | ss_config_code = 0, | |
398 | ss_io_code = 1, | |
399 | ss_32bit_memory_code = 2, | |
400 | ss_64bit_memory_code = 3, | |
401 | } ss_type; | |
402 | ||
403 | static ss_type | |
404 | extract_ss(const device_unit *address) | |
405 | { | |
406 | ASSERT(address->nr_cells == 3); | |
407 | return EXTRACTED32(address->cells[0], 6, 7); | |
408 | } | |
409 | ||
410 | static void | |
411 | set_ss(device_unit *address, ss_type val) | |
412 | { | |
413 | MBLIT32(address->cells[0], 6, 7, val); | |
414 | } | |
415 | ||
416 | ||
417 | /* bus number bbbbbbbb */ | |
418 | ||
419 | #if 0 | |
420 | static unsigned | |
421 | extract_bbbbbbbb(const device_unit *address) | |
422 | { | |
423 | ASSERT(address->nr_cells == 3); | |
424 | return EXTRACTED32(address->cells[0], 8, 15); | |
425 | } | |
426 | #endif | |
427 | ||
428 | #if 0 | |
429 | static void | |
430 | set_bbbbbbbb(device_unit *address, unsigned val) | |
431 | { | |
432 | MBLIT32(address->cells[0], 8, 15, val); | |
433 | } | |
434 | #endif | |
435 | ||
436 | ||
437 | /* device number ddddd */ | |
438 | ||
439 | static unsigned | |
440 | extract_ddddd(const device_unit *address) | |
441 | { | |
442 | ASSERT(address->nr_cells == 3); | |
443 | return EXTRACTED32(address->cells[0], 16, 20); | |
444 | } | |
445 | ||
446 | static void | |
447 | set_ddddd(device_unit *address, unsigned val) | |
448 | { | |
449 | MBLIT32(address->cells[0], 16, 20, val); | |
450 | } | |
451 | ||
452 | ||
453 | /* function number fff */ | |
454 | ||
455 | static unsigned | |
456 | extract_fff(const device_unit *address) | |
457 | { | |
458 | ASSERT(address->nr_cells == 3); | |
459 | return EXTRACTED32(address->cells[0], 21, 23); | |
460 | } | |
461 | ||
462 | static void | |
463 | set_fff(device_unit *address, unsigned val) | |
464 | { | |
465 | MBLIT32(address->cells[0], 21, 23, val); | |
466 | } | |
467 | ||
468 | ||
469 | /* register number rrrrrrrr */ | |
470 | ||
471 | static unsigned | |
472 | extract_rrrrrrrr(const device_unit *address) | |
473 | { | |
474 | ASSERT(address->nr_cells == 3); | |
475 | return EXTRACTED32(address->cells[0], 24, 31); | |
476 | } | |
477 | ||
478 | static void | |
479 | set_rrrrrrrr(device_unit *address, unsigned val) | |
480 | { | |
481 | MBLIT32(address->cells[0], 24, 31, val); | |
482 | } | |
483 | ||
484 | ||
485 | /* MSW of 64bit address hh..hh */ | |
486 | ||
487 | static unsigned | |
488 | extract_hh_hh(const device_unit *address) | |
489 | { | |
490 | ASSERT(address->nr_cells == 3); | |
491 | return address->cells[1]; | |
492 | } | |
493 | ||
494 | static void | |
495 | set_hh_hh(device_unit *address, unsigned val) | |
496 | { | |
497 | address->cells[2] = val; | |
498 | } | |
499 | ||
500 | ||
501 | /* LSW of 64bit address ll..ll */ | |
502 | ||
503 | static unsigned | |
504 | extract_ll_ll(const device_unit *address) | |
505 | { | |
506 | ASSERT(address->nr_cells == 3); | |
507 | return address->cells[2]; | |
508 | } | |
509 | ||
510 | static void | |
511 | set_ll_ll(device_unit *address, unsigned val) | |
512 | { | |
513 | address->cells[2] = val; | |
514 | } | |
515 | ||
516 | ||
517 | /* Convert PCI textual bus address into a device unit */ | |
518 | ||
519 | static int | |
520 | hw_phb_unit_decode(device *me, | |
521 | const char *unit, | |
522 | device_unit *address) | |
523 | { | |
524 | char *end = NULL; | |
525 | const char *chp = unit; | |
526 | unsigned long val; | |
527 | ||
528 | if (device_nr_address_cells(me) != 3) | |
529 | device_error(me, "PCI bus should have #address-cells == 3"); | |
530 | memset(address, 0, sizeof(*address)); | |
531 | ||
532 | if (unit == NULL) | |
533 | return 0; | |
534 | ||
535 | address->nr_cells = 3; | |
536 | ||
537 | if (isxdigit(*chp)) { | |
538 | set_ss(address, ss_config_code); | |
539 | } | |
540 | else { | |
541 | ||
542 | /* non-relocatable? */ | |
543 | if (*chp == 'n') { | |
544 | set_n(address); | |
545 | chp++; | |
546 | } | |
547 | ||
548 | /* address-space? */ | |
549 | if (*chp == 'i') { | |
550 | set_ss(address, ss_io_code); | |
551 | chp++; | |
552 | } | |
553 | else if (*chp == 'm') { | |
554 | set_ss(address, ss_32bit_memory_code); | |
555 | chp++; | |
556 | } | |
557 | else if (*chp == 'x') { | |
558 | set_ss(address, ss_64bit_memory_code); | |
559 | chp++; | |
560 | } | |
561 | else | |
562 | device_error(me, "Problem parsing PCI address %s", unit); | |
563 | ||
564 | /* possible alias */ | |
565 | if (*chp == 't') { | |
566 | if (extract_ss(address) == ss_64bit_memory_code) | |
567 | device_error(me, "Invalid alias bit in PCI address %s", unit); | |
568 | set_t(address); | |
569 | chp++; | |
570 | } | |
571 | ||
572 | /* possible p */ | |
573 | if (*chp == 'p') { | |
574 | if (extract_ss(address) != ss_32bit_memory_code) | |
575 | device_error(me, "Invalid prefetchable bit (p) in PCI address %s", | |
576 | unit); | |
577 | set_p(address); | |
578 | chp++; | |
579 | } | |
580 | ||
581 | } | |
582 | ||
583 | /* required DD */ | |
584 | if (!isxdigit(*chp)) | |
585 | device_error(me, "Missing device number in PCI address %s", unit); | |
586 | val = strtoul(chp, &end, 16); | |
587 | if (chp == end) | |
588 | device_error(me, "Problem parsing device number in PCI address %s", unit); | |
589 | if ((val & 0x1f) != val) | |
590 | device_error(me, "Device number (0x%lx) out of range (0..0x1f) in PCI address %s", | |
591 | val, unit); | |
592 | set_ddddd(address, val); | |
593 | chp = end; | |
594 | ||
595 | /* For config space, the F is optional */ | |
596 | if (extract_ss(address) == ss_config_code | |
597 | && (isspace(*chp) || *chp == '\0')) | |
598 | return chp - unit; | |
599 | ||
600 | /* function number F */ | |
601 | if (*chp != ',') | |
602 | device_error(me, "Missing function number in PCI address %s", unit); | |
603 | chp++; | |
604 | val = strtoul(chp, &end, 10); | |
605 | if (chp == end) | |
606 | device_error(me, "Problem parsing function number in PCI address %s", | |
607 | unit); | |
608 | if ((val & 7) != val) | |
609 | device_error(me, "Function number (%ld) out of range (0..7) in PCI address %s", | |
610 | (long)val, unit); | |
611 | set_fff(address, val); | |
612 | chp = end; | |
613 | ||
614 | /* for config space, must be end */ | |
615 | if (extract_ss(address) == ss_config_code) { | |
616 | if (!isspace(*chp) && *chp != '\0') | |
617 | device_error(me, "Problem parsing PCI config address %s", | |
618 | unit); | |
619 | return chp - unit; | |
620 | } | |
621 | ||
622 | /* register number RR */ | |
623 | if (*chp != ',') | |
624 | device_error(me, "Missing register number in PCI address %s", unit); | |
625 | chp++; | |
626 | val = strtoul(chp, &end, 16); | |
627 | if (chp == end) | |
628 | device_error(me, "Problem parsing register number in PCI address %s", | |
629 | unit); | |
630 | switch (extract_ss(address)) { | |
631 | case ss_io_code: | |
632 | #if 0 | |
633 | if (extract_n(address) && val != 0) | |
634 | device_error(me, "non-relocatable I/O register must be zero in PCI address %s", unit); | |
635 | else if (!extract_n(address) | |
636 | && val != 0x10 && val != 0x14 && val != 0x18 | |
637 | && val != 0x1c && val != 0x20 && val != 0x24) | |
638 | device_error(me, "I/O register invalid in PCI address %s", unit); | |
639 | #endif | |
640 | break; | |
641 | case ss_32bit_memory_code: | |
642 | #if 0 | |
643 | if (extract_n(address) && val != 0) | |
644 | device_error(me, "non-relocatable memory register must be zero in PCI address %s", unit); | |
645 | else if (!extract_n(address) | |
646 | && val != 0x10 && val != 0x14 && val != 0x18 | |
647 | && val != 0x1c && val != 0x20 && val != 0x24 && val != 0x30) | |
648 | device_error(me, "I/O register (0x%lx) invalid in PCI address %s", | |
649 | val, unit); | |
650 | #endif | |
651 | break; | |
652 | case ss_64bit_memory_code: | |
653 | if (extract_n(address) && val != 0) | |
654 | device_error(me, "non-relocatable 32bit memory register must be zero in PCI address %s", unit); | |
655 | else if (!extract_n(address) | |
656 | && val != 0x10 && val != 0x18 && val != 0x20) | |
657 | device_error(me, "Register number (0x%lx) invalid in 64bit PCI address %s", | |
658 | val, unit); | |
659 | case ss_config_code: | |
660 | device_error(me, "internal error"); | |
661 | } | |
662 | if ((val & 0xff) != val) | |
663 | device_error(me, "Register number (0x%lx) out of range (0..0xff) in PCI address %s", | |
664 | val, unit); | |
665 | set_rrrrrrrr(address, val); | |
666 | chp = end; | |
667 | ||
668 | /* address */ | |
669 | if (*chp != ',') | |
670 | device_error(me, "Missing address in PCI address %s", unit); | |
671 | chp++; | |
672 | switch (extract_ss(address)) { | |
673 | case ss_io_code: | |
674 | case ss_32bit_memory_code: | |
675 | val = strtoul(chp, &end, 16); | |
676 | if (chp == end) | |
677 | device_error(me, "Problem parsing address in PCI address %s", unit); | |
678 | switch (extract_ss(address)) { | |
679 | case ss_io_code: | |
680 | if (extract_n(address) && extract_t(address) | |
681 | && (val & 1024) != val) | |
682 | device_error(me, "10bit aliased non-relocatable address (0x%lx) out of range in PCI address %s", | |
683 | val, unit); | |
684 | if (!extract_n(address) && extract_t(address) | |
685 | && (val & 0xffff) != val) | |
686 | device_error(me, "64k relocatable address (0x%lx) out of range in PCI address %s", | |
687 | val, unit); | |
688 | break; | |
689 | case ss_32bit_memory_code: | |
690 | if (extract_t(address) && (val & 0xfffff) != val) | |
691 | device_error(me, "1mb memory address (0x%lx) out of range in PCI address %s", | |
692 | val, unit); | |
693 | if (!extract_t(address) && (val & 0xffffffff) != val) | |
694 | device_error(me, "32bit memory address (0x%lx) out of range in PCI address %s", | |
695 | val, unit); | |
696 | break; | |
697 | case ss_64bit_memory_code: | |
698 | case ss_config_code: | |
699 | device_error(me, "internal error"); | |
700 | } | |
701 | set_ll_ll(address, val); | |
702 | chp = end; | |
703 | break; | |
704 | case ss_64bit_memory_code: | |
705 | device_error(me, "64bit addresses unimplemented"); | |
706 | set_hh_hh(address, val); | |
707 | set_ll_ll(address, val); | |
708 | break; | |
709 | case ss_config_code: | |
710 | device_error(me, "internal error"); | |
711 | break; | |
712 | } | |
713 | ||
714 | /* finished? */ | |
715 | if (!isspace(*chp) && *chp != '\0') | |
716 | device_error(me, "Problem parsing PCI address %s", unit); | |
717 | ||
718 | return chp - unit; | |
719 | } | |
720 | ||
721 | ||
722 | /* Convert PCI device unit into its corresponding textual | |
723 | representation */ | |
724 | ||
725 | static int | |
726 | hw_phb_unit_encode(device *me, | |
727 | const device_unit *unit_address, | |
728 | char *buf, | |
729 | int sizeof_buf) | |
730 | { | |
731 | if (unit_address->nr_cells != 3) | |
732 | device_error(me, "Incorrect number of cells in PCI unit address"); | |
733 | if (device_nr_address_cells(me) != 3) | |
734 | device_error(me, "PCI bus should have #address-cells == 3"); | |
735 | if (extract_ss(unit_address) == ss_config_code | |
736 | && extract_fff(unit_address) == 0 | |
737 | && extract_rrrrrrrr(unit_address) == 0 | |
738 | && extract_hh_hh(unit_address) == 0 | |
739 | && extract_ll_ll(unit_address) == 0) { | |
740 | /* DD - Configuration Space address */ | |
741 | sprintf(buf, "%x", | |
742 | extract_ddddd(unit_address)); | |
743 | } | |
744 | else if (extract_ss(unit_address) == ss_config_code | |
745 | && extract_fff(unit_address) != 0 | |
746 | && extract_rrrrrrrr(unit_address) == 0 | |
747 | && extract_hh_hh(unit_address) == 0 | |
748 | && extract_ll_ll(unit_address) == 0) { | |
749 | /* DD,F - Configuration Space */ | |
750 | sprintf(buf, "%x,%d", | |
751 | extract_ddddd(unit_address), | |
752 | extract_fff(unit_address)); | |
753 | } | |
754 | else if (extract_ss(unit_address) == ss_io_code | |
755 | && extract_hh_hh(unit_address) == 0) { | |
756 | /* [n]i[t]DD,F,RR,NNNNNNNN - 32bit I/O space */ | |
757 | sprintf(buf, "%si%s%x,%d,%x,%x", | |
758 | extract_n(unit_address) ? "n" : "", | |
759 | extract_t(unit_address) ? "t" : "", | |
760 | extract_ddddd(unit_address), | |
761 | extract_fff(unit_address), | |
762 | extract_rrrrrrrr(unit_address), | |
763 | extract_ll_ll(unit_address)); | |
764 | } | |
765 | else if (extract_ss(unit_address) == ss_32bit_memory_code | |
766 | && extract_hh_hh(unit_address) == 0) { | |
767 | /* [n]m[t][p]DD,F,RR,NNNNNNNN - 32bit memory space */ | |
768 | sprintf(buf, "%sm%s%s%x,%d,%x,%x", | |
769 | extract_n(unit_address) ? "n" : "", | |
770 | extract_t(unit_address) ? "t" : "", | |
771 | extract_p(unit_address) ? "p" : "", | |
772 | extract_ddddd(unit_address), | |
773 | extract_fff(unit_address), | |
774 | extract_rrrrrrrr(unit_address), | |
775 | extract_ll_ll(unit_address)); | |
776 | } | |
777 | else if (extract_ss(unit_address) == ss_32bit_memory_code) { | |
778 | /* [n]x[p]DD,F,RR,NNNNNNNNNNNNNNNN - 64bit memory space */ | |
779 | sprintf(buf, "%sx%s%x,%d,%x,%x%08x", | |
780 | extract_n(unit_address) ? "n" : "", | |
781 | extract_p(unit_address) ? "p" : "", | |
782 | extract_ddddd(unit_address), | |
783 | extract_fff(unit_address), | |
784 | extract_rrrrrrrr(unit_address), | |
785 | extract_hh_hh(unit_address), | |
786 | extract_ll_ll(unit_address)); | |
787 | } | |
788 | else { | |
789 | device_error(me, "Invalid PCI unit address 0x%08lx 0x%08lx 0x%08lx", | |
790 | (unsigned long)unit_address->cells[0], | |
791 | (unsigned long)unit_address->cells[1], | |
792 | (unsigned long)unit_address->cells[2]); | |
793 | } | |
794 | if (strlen(buf) > sizeof_buf) | |
795 | error("buffer overflow"); | |
796 | return strlen(buf); | |
797 | } | |
798 | ||
799 | ||
800 | static int | |
801 | hw_phb_address_to_attach_address(device *me, | |
802 | const device_unit *address, | |
803 | int *attach_space, | |
804 | unsigned_word *attach_address, | |
805 | device *client) | |
806 | { | |
807 | if (address->nr_cells != 3) | |
808 | device_error(me, "attach address has incorrect number of cells"); | |
809 | if (address->cells[1] != 0) | |
810 | device_error(me, "64bit attach address unsupported"); | |
811 | ||
812 | /* directly decode the address/space */ | |
813 | *attach_address = address->cells[2]; | |
814 | switch (extract_ss(address)) { | |
815 | case ss_config_code: | |
816 | *attach_space = hw_phb_config_space; | |
817 | break; | |
818 | case ss_io_code: | |
819 | *attach_space = hw_phb_io_space; | |
820 | break; | |
821 | case ss_32bit_memory_code: | |
822 | case ss_64bit_memory_code: | |
823 | *attach_space = hw_phb_memory_space; | |
824 | break; | |
825 | } | |
826 | ||
827 | /* if non-relocatable finished */ | |
828 | if (extract_n(address)) | |
829 | return 1; | |
830 | ||
831 | /* make memory and I/O addresses absolute */ | |
832 | if (*attach_space == hw_phb_io_space | |
833 | || *attach_space == hw_phb_memory_space) { | |
834 | int reg_nr; | |
835 | reg_property_spec assigned; | |
836 | if (extract_ss(address) == ss_64bit_memory_code) | |
837 | device_error(me, "64bit memory address not unsuported"); | |
838 | for (reg_nr = 0; | |
839 | device_find_reg_array_property(client, "assigned-addresses", reg_nr, | |
840 | &assigned); | |
841 | reg_nr++) { | |
842 | if (!extract_n(&assigned.address) | |
843 | || extract_rrrrrrrr(&assigned.address) == 0) | |
844 | device_error(me, "client %s has invalid assigned-address property", | |
845 | device_path(client)); | |
846 | if (extract_rrrrrrrr(address) == extract_rrrrrrrr(&assigned.address)) { | |
847 | /* corresponding base register */ | |
848 | if (extract_ss(address) != extract_ss(&assigned.address)) | |
849 | device_error(me, "client %s has conflicting types for base register 0x%lx", | |
850 | device_path(client), | |
851 | (unsigned long)extract_rrrrrrrr(address)); | |
852 | *attach_address += assigned.address.cells[2]; | |
853 | return 0; | |
854 | } | |
855 | } | |
856 | device_error(me, "client %s missing base address register 0x%lx in assigned-addresses property", | |
857 | device_path(client), | |
858 | (unsigned long)extract_rrrrrrrr(address)); | |
859 | } | |
860 | ||
861 | return 0; | |
862 | } | |
863 | ||
864 | ||
865 | static int | |
866 | hw_phb_size_to_attach_size(device *me, | |
867 | const device_unit *size, | |
868 | unsigned *nr_bytes, | |
869 | device *client) | |
870 | { | |
871 | if (size->nr_cells != 2) | |
872 | device_error(me, "size has incorrect number of cells"); | |
873 | if (size->cells[0] != 0) | |
874 | device_error(me, "64bit size unsupported"); | |
875 | *nr_bytes = size->cells[1]; | |
876 | return size->cells[1]; | |
877 | } | |
878 | ||
879 | ||
880 | static const phb_space * | |
881 | find_phb_space(hw_phb_device *phb, | |
882 | unsigned_word addr, | |
883 | unsigned nr_bytes) | |
884 | { | |
885 | hw_phb_spaces space; | |
886 | /* find the space that matches the address */ | |
887 | for (space = 0; space < nr_hw_phb_spaces; space++) { | |
888 | phb_space *pci_space = &phb->space[space]; | |
889 | if (addr >= pci_space->parent_base | |
890 | && (addr + nr_bytes) <= (pci_space->parent_base + pci_space->size)) { | |
891 | return pci_space; | |
892 | } | |
893 | } | |
894 | return NULL; | |
895 | } | |
896 | ||
897 | ||
898 | static unsigned_word | |
899 | map_phb_addr(const phb_space *space, | |
900 | unsigned_word addr) | |
901 | { | |
902 | return addr - space->parent_base + space->my_base; | |
903 | } | |
904 | ||
905 | ||
906 | ||
907 | static unsigned | |
908 | hw_phb_io_read_buffer(device *me, | |
909 | void *dest, | |
910 | int space, | |
911 | unsigned_word addr, | |
912 | unsigned nr_bytes, | |
913 | cpu *processor, | |
914 | unsigned_word cia) | |
915 | { | |
916 | hw_phb_device *phb = (hw_phb_device*)device_data(me); | |
917 | const phb_space *pci_space = find_phb_space(phb, addr, nr_bytes); | |
918 | unsigned_word bus_addr; | |
919 | if (pci_space == NULL) | |
920 | return 0; | |
921 | bus_addr = map_phb_addr(pci_space, addr); | |
922 | DTRACE(phb, ("io read - %d:0x%lx -> %s:0x%lx (%u bytes)\n", | |
923 | space, (unsigned long)addr, pci_space->name, (unsigned long)bus_addr, | |
924 | nr_bytes)); | |
925 | return core_map_read_buffer(pci_space->readable, | |
926 | dest, bus_addr, nr_bytes); | |
927 | } | |
928 | ||
929 | ||
930 | static unsigned | |
931 | hw_phb_io_write_buffer(device *me, | |
932 | const void *source, | |
933 | int space, | |
934 | unsigned_word addr, | |
935 | unsigned nr_bytes, | |
936 | cpu *processor, | |
937 | unsigned_word cia) | |
938 | { | |
939 | hw_phb_device *phb = (hw_phb_device*)device_data(me); | |
940 | const phb_space *pci_space = find_phb_space(phb, addr, nr_bytes); | |
941 | unsigned_word bus_addr; | |
942 | if (pci_space == NULL) | |
943 | return 0; | |
944 | bus_addr = map_phb_addr(pci_space, addr); | |
945 | DTRACE(phb, ("io write - %d:0x%lx -> %s:0x%lx (%u bytes)\n", | |
946 | space, (unsigned long)addr, pci_space->name, (unsigned long)bus_addr, | |
947 | nr_bytes)); | |
948 | return core_map_write_buffer(pci_space->writeable, source, | |
949 | bus_addr, nr_bytes); | |
950 | } | |
951 | ||
952 | ||
953 | static unsigned | |
954 | hw_phb_dma_read_buffer(device *me, | |
955 | void *dest, | |
956 | int space, | |
957 | unsigned_word addr, | |
958 | unsigned nr_bytes) | |
959 | { | |
960 | hw_phb_device *phb = (hw_phb_device*)device_data(me); | |
961 | const phb_space *pci_space; | |
962 | /* find the space */ | |
963 | if (space != hw_phb_memory_space) | |
964 | device_error(me, "invalid dma address space %d", space); | |
965 | pci_space = &phb->space[space]; | |
966 | /* check out the address */ | |
967 | if ((addr >= pci_space->my_base | |
968 | && addr <= pci_space->my_base + pci_space->size) | |
969 | || (addr + nr_bytes >= pci_space->my_base | |
970 | && addr + nr_bytes <= pci_space->my_base + pci_space->size)) | |
971 | device_error(me, "Do not support DMA into own bus"); | |
972 | /* do it */ | |
973 | DTRACE(phb, ("dma read - %s:0x%lx (%d bytes)\n", | |
974 | pci_space->name, addr, nr_bytes)); | |
975 | return device_dma_read_buffer(device_parent(me), | |
976 | dest, pci_space->parent_space, | |
977 | addr, nr_bytes); | |
978 | } | |
979 | ||
980 | ||
981 | static unsigned | |
982 | hw_phb_dma_write_buffer(device *me, | |
983 | const void *source, | |
984 | int space, | |
985 | unsigned_word addr, | |
986 | unsigned nr_bytes, | |
987 | int violate_read_only_section) | |
988 | { | |
989 | hw_phb_device *phb = (hw_phb_device*)device_data(me); | |
990 | const phb_space *pci_space; | |
991 | /* find the space */ | |
992 | if (space != hw_phb_memory_space) | |
993 | device_error(me, "invalid dma address space %d", space); | |
994 | pci_space = &phb->space[space]; | |
995 | /* check out the address */ | |
996 | if ((addr >= pci_space->my_base | |
997 | && addr <= pci_space->my_base + pci_space->size) | |
998 | || (addr + nr_bytes >= pci_space->my_base | |
999 | && addr + nr_bytes <= pci_space->my_base + pci_space->size)) | |
1000 | device_error(me, "Do not support DMA into own bus"); | |
1001 | /* do it */ | |
1002 | DTRACE(phb, ("dma write - %s:0x%lx (%d bytes)\n", | |
1003 | pci_space->name, addr, nr_bytes)); | |
1004 | return device_dma_write_buffer(device_parent(me), | |
1005 | source, pci_space->parent_space, | |
1006 | addr, nr_bytes, | |
1007 | violate_read_only_section); | |
1008 | } | |
1009 | ||
1010 | ||
1011 | static device_callbacks const hw_phb_callbacks = { | |
1012 | { hw_phb_init_address, }, | |
1013 | { hw_phb_attach_address, }, | |
1014 | { hw_phb_io_read_buffer, hw_phb_io_write_buffer }, | |
1015 | { hw_phb_dma_read_buffer, hw_phb_dma_write_buffer }, | |
1016 | { NULL, }, /* interrupt */ | |
1017 | { hw_phb_unit_decode, | |
1018 | hw_phb_unit_encode, | |
1019 | hw_phb_address_to_attach_address, | |
1020 | hw_phb_size_to_attach_size } | |
1021 | }; | |
1022 | ||
1023 | ||
1024 | static void * | |
1025 | hw_phb_create(const char *name, | |
1026 | const device_unit *unit_address, | |
1027 | const char *args) | |
1028 | { | |
1029 | /* create the descriptor */ | |
1030 | hw_phb_device *phb = ZALLOC(hw_phb_device); | |
1031 | ||
1032 | /* create the core maps now */ | |
1033 | hw_phb_spaces space_nr; | |
1034 | for (space_nr = 0; space_nr < nr_hw_phb_spaces; space_nr++) { | |
1035 | phb_space *pci_space = &phb->space[space_nr]; | |
1036 | pci_space->map = core_create(); | |
1037 | pci_space->readable = core_readable(pci_space->map); | |
1038 | pci_space->writeable = core_writeable(pci_space->map); | |
1039 | switch (space_nr) { | |
1040 | case hw_phb_memory_space: | |
1041 | pci_space->name = "memory"; | |
1042 | break; | |
1043 | case hw_phb_io_space: | |
1044 | pci_space->name = "I/O"; | |
1045 | break; | |
1046 | case hw_phb_config_space: | |
1047 | pci_space->name = "config"; | |
1048 | break; | |
1049 | case hw_phb_special_space: | |
1050 | pci_space->name = "special"; | |
1051 | break; | |
1052 | default: | |
1053 | error ("internal error"); | |
1054 | break; | |
1055 | } | |
1056 | } | |
1057 | ||
1058 | return phb; | |
1059 | } | |
1060 | ||
1061 | ||
1062 | const device_descriptor hw_phb_device_descriptor[] = { | |
1063 | { "phb", hw_phb_create, &hw_phb_callbacks }, | |
1064 | { "pci", NULL, &hw_phb_callbacks }, | |
1065 | { NULL, }, | |
1066 | }; | |
1067 | ||
1068 | #endif /* _HW_PHB_ */ |