12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064 |
- /* This file is part of the program psim.
- Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
- 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 3 of the License, or
- (at your option) any later version.
- 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.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- */
- #ifndef _HW_PHB_C_
- #define _HW_PHB_C_
- #include "device_table.h"
- #include "hw_phb.h"
- #include "corefile.h"
- #include <stdlib.h>
- #include <ctype.h>
- /* DEVICE
- phb - PCI Host Bridge
- DESCRIPTION
- PHB implements a model of the PCI-host bridge described in the PPCP
- document.
- For bridge devices, Open Firmware specifies that the <<ranges>>
- property be used to specify the mapping of address spaces between a
- bridges parent and child busses. This PHB model configures itsself
- according to the information specified in its ranges property. The
- <<ranges>> property is described in detail in the Open Firmware
- documentation.
- For DMA transfers, any access to a PCI address space which falls
- outside of the mapped memory space is assumed to be a transfer
- intended for the parent bus.
- PROPERTIES
- ranges = <my-phys-addr> <parent-phys-addr> <my-size> ... (required)
-
- Define a number of mappings from the parent bus to one of this
- devices PCI busses. The exact format of the <<parent-phys-addr>>
- is parent bus dependant. The format of <<my-phys-addr>> is
- described in the Open Firmware PCI bindings document (note that the
- address must be non-relocatable).
- #address-cells = 3 (required)
- Number of cells used by an Open Firmware PCI address. This
- property must be defined before specifying the <<ranges>> property.
- #size-cells = 2 (required)
- Number of cells used by an Open Firmware PCI size. This property
- must be defined before specifying the <<ranges>> property.
- EXAMPLES
-
- Enable tracing:
- | $ psim \
- | -t phb-device \
- Since device tree entries that are specified on the command line
- are added before most of the device tree has been built it is often
- necessary to explictly add certain device properties and thus
- ensure they are already present in the device tree. For the
- <<phb>> one such property is parent busses <<#address-cells>>.
- | -o '/#address-cells 1' \
- Create the PHB remembering to include the cell size properties:
-
- | -o '/phb@0x80000000/#address-cells 3' \
- | -o '/phb@0x80000000/#size-cells 2' \
- Specify that the memory address range <<0x80000000>> to
- <<0x8fffffff>> should map directly onto the PCI memory address
- space while the processor address range <<0xc0000000>> to
- <<0xc000ffff>> should map onto the PCI I/O address range starting
- at location zero:
- | -o '/phb@0x80000000/ranges \
- | nm0,0,0,80000000 0x80000000 0x10000000 \
- | ni0,0,0,0 0xc0000000 0x10000' \
- Insert a 4k <<nvram>> into slot zero of the PCI bus. Have it
- directly accessible in both the I/O (address <<0x100>>) and memory
- (address 0x80001000) spaces:
- | -o '/phb@0x80000000/nvram@0/assigned-addresses \
- | nm0,0,10,80001000 4096 \
- | ni0,0,14,100 4096'
- | -o '/phb@0x80000000/nvram@0/reg \
- | 0 0 \
- | i0,0,14,0 4096'
- | -o '/phb@0x80000000/nvram@0/alternate-reg \
- | 0 0 \
- | m0,0,10,0 4096'
- The <<assigned-address>> property corresponding to what (if it were
- implemented) be found in the config base registers while the
- <<reg>> and <<alternative-reg>> properties indicating the location
- of registers within each address space.
- Of the possible addresses, only the non-relocatable versions are
- used when attaching the device to the bus.
- BUGS
-
- The implementation of the PCI configuration space is left as an
- exercise for the reader. Such a restriction should only impact on
- systems wanting to dynamically configure devices on the PCI bus.
- The <<CHRP>> document specfies additional (optional) functionality
- of the primary PHB. The implementation of such functionality is
- left as an exercise for the reader.
- The Open Firmware PCI bus bindings document (rev 1.6 and 2.0) is
- unclear on the value of the "ss" bits for a 64bit memory address.
- The correct value, as used by this module, is 0b11.
-
- The Open Firmware PCI bus bindings document (rev 1.6) suggests that
- the register field of non-relocatable PCI address should be zero.
- Unfortunatly, PCI addresses specified in the <<assigned-addresses>>
- property must be both non-relocatable and have non-zero register
- fields.
- The unit-decode method is not inserting a bus number into any
- address that it decodes. Instead the bus-number is left as zero.
- Support for aliased memory and I/O addresses is left as an exercise
- for the reader.
- Support for interrupt-ack and special cycles are left as an
- exercise for the reader. One issue to consider when attempting
- this exercise is how to specify the address of the int-ack and
- special cycle register. Hint: <</8259-interrupt-ackowledge>> is
- the wrong answer.
- Children of this node can only use the client callback interface
- when attaching themselves to the <<phb>>.
-
- REFERENCES
- http://playground.sun.com/1275/home.html#OFDbusPCI
- */
-
- typedef struct _phb_space {
- core *map;
- core_map *readable;
- core_map *writeable;
- unsigned_word parent_base;
- int parent_space;
- unsigned_word my_base;
- int my_space;
- unsigned size;
- const char *name;
- } phb_space;
- typedef struct _hw_phb_device {
- phb_space space[nr_hw_phb_spaces];
- } hw_phb_device;
- static const char *
- hw_phb_decode_name(hw_phb_decode level)
- {
- switch (level) {
- case hw_phb_normal_decode: return "normal";
- case hw_phb_subtractive_decode: return "subtractive";
- case hw_phb_master_abort_decode: return "master-abort";
- default: return "invalid decode";
- }
- }
- static void
- hw_phb_init_address(device *me)
- {
- hw_phb_device *phb = device_data(me);
-
- /* check some basic properties */
- if (device_nr_address_cells(me) != 3)
- device_error(me, "incorrect #address-cells");
- if (device_nr_size_cells(me) != 2)
- device_error(me, "incorrect #size-cells");
- /* (re) initialize each PCI space */
- {
- hw_phb_spaces space_nr;
- for (space_nr = 0; space_nr < nr_hw_phb_spaces; space_nr++) {
- phb_space *pci_space = &phb->space[space_nr];
- core_init(pci_space->map);
- pci_space->size = 0;
- }
- }
- /* decode each of the ranges properties entering the information
- into the space table */
- {
- range_property_spec range;
- int ranges_entry;
-
- for (ranges_entry = 0;
- device_find_range_array_property(me, "ranges", ranges_entry,
- &range);
- ranges_entry++) {
- int my_attach_space;
- unsigned_word my_attach_address;
- int parent_attach_space;
- unsigned_word parent_attach_address;
- unsigned size;
- phb_space *pci_space;
- /* convert the addresses into something meaningful */
- device_address_to_attach_address(me, &range.child_address,
- &my_attach_space,
- &my_attach_address,
- me);
- device_address_to_attach_address(device_parent(me),
- &range.parent_address,
- &parent_attach_space,
- &parent_attach_address,
- me);
- device_size_to_attach_size(me, &range.size, &size, me);
- if (my_attach_space < 0 || my_attach_space >= nr_hw_phb_spaces)
- device_error(me, "ranges property contains an invalid address space");
- pci_space = &phb->space[my_attach_space];
- if (pci_space->size != 0)
- device_error(me, "ranges property contains duplicate mappings for %s address space",
- pci_space->name);
- pci_space->parent_base = parent_attach_address;
- pci_space->parent_space = parent_attach_space;
- pci_space->my_base = my_attach_address;
- pci_space->my_space = my_attach_space;
- pci_space->size = size;
- device_attach_address(device_parent(me),
- attach_callback,
- parent_attach_space, parent_attach_address, size,
- access_read_write_exec,
- me);
- DTRACE(phb, ("map %d:0x%lx to %s:0x%lx (0x%lx bytes)\n",
- (int)parent_attach_space,
- (unsigned long)parent_attach_address,
- pci_space->name,
- (unsigned long)my_attach_address,
- (unsigned long)size));
- }
-
- if (ranges_entry == 0) {
- device_error(me, "Missing or empty ranges property");
- }
- }
-
- }
- static void
- hw_phb_attach_address(device *me,
- attach_type type,
- int space,
- unsigned_word addr,
- unsigned nr_bytes,
- access_type access,
- device *client) /*callback/default*/
- {
- hw_phb_device *phb = device_data(me);
- phb_space *pci_space;
- /* sanity checks */
- if (space < 0 || space >= nr_hw_phb_spaces)
- device_error(me, "attach space (%d) specified by %s invalid",
- space, device_path(client));
- pci_space = &phb->space[space];
- if (addr + nr_bytes > pci_space->my_base + pci_space->size
- || addr < pci_space->my_base)
- device_error(me, "attach addr (0x%lx) specified by %s outside of bus address range",
- (unsigned long)addr, device_path(client));
- if ((hw_phb_decode)type != hw_phb_normal_decode
- && (hw_phb_decode)type != hw_phb_subtractive_decode)
- device_error(me, "attach type (%d) specified by %s invalid",
- type, device_path(client));
- /* attach it to the relevent bus */
- DTRACE(phb, ("attach %s - %s %s:0x%lx (0x%lx bytes)\n",
- device_path(client),
- hw_phb_decode_name(type),
- pci_space->name,
- (unsigned long)addr,
- (unsigned long)nr_bytes));
- core_attach(pci_space->map,
- type,
- space,
- access,
- addr,
- nr_bytes,
- client);
- }
- /* Extract/set various fields from a PCI unit address.
- Note: only the least significant 32 bits of each cell is used.
- Note: for PPC MSB is 0 while for PCI it is 31. */
- /* relocatable bit n */
- static unsigned
- extract_n(const device_unit *address)
- {
- return EXTRACTED32(address->cells[0], 0, 0);
- }
- static void
- set_n(device_unit *address)
- {
- BLIT32(address->cells[0], 0, 1);
- }
- /* prefetchable bit p */
- static unsigned
- extract_p(const device_unit *address)
- {
- ASSERT(address->nr_cells == 3);
- return EXTRACTED32(address->cells[0], 1, 1);
- }
- static void
- set_p(device_unit *address)
- {
- BLIT32(address->cells[0], 1, 1);
- }
- /* aliased bit t */
- static unsigned
- extract_t(const device_unit *address)
- {
- ASSERT(address->nr_cells == 3);
- return EXTRACTED32(address->cells[0], 2, 2);
- }
- static void
- set_t(device_unit *address)
- {
- BLIT32(address->cells[0], 2, 1);
- }
- /* space code ss */
- typedef enum {
- ss_config_code = 0,
- ss_io_code = 1,
- ss_32bit_memory_code = 2,
- ss_64bit_memory_code = 3,
- } ss_type;
- static ss_type
- extract_ss(const device_unit *address)
- {
- ASSERT(address->nr_cells == 3);
- return EXTRACTED32(address->cells[0], 6, 7);
- }
- static void
- set_ss(device_unit *address, ss_type val)
- {
- MBLIT32(address->cells[0], 6, 7, val);
- }
- /* bus number bbbbbbbb */
- #if 0
- static unsigned
- extract_bbbbbbbb(const device_unit *address)
- {
- ASSERT(address->nr_cells == 3);
- return EXTRACTED32(address->cells[0], 8, 15);
- }
- #endif
- #if 0
- static void
- set_bbbbbbbb(device_unit *address, unsigned val)
- {
- MBLIT32(address->cells[0], 8, 15, val);
- }
- #endif
- /* device number ddddd */
- static unsigned
- extract_ddddd(const device_unit *address)
- {
- ASSERT(address->nr_cells == 3);
- return EXTRACTED32(address->cells[0], 16, 20);
- }
- static void
- set_ddddd(device_unit *address, unsigned val)
- {
- MBLIT32(address->cells[0], 16, 20, val);
- }
- /* function number fff */
- static unsigned
- extract_fff(const device_unit *address)
- {
- ASSERT(address->nr_cells == 3);
- return EXTRACTED32(address->cells[0], 21, 23);
- }
- static void
- set_fff(device_unit *address, unsigned val)
- {
- MBLIT32(address->cells[0], 21, 23, val);
- }
- /* register number rrrrrrrr */
- static unsigned
- extract_rrrrrrrr(const device_unit *address)
- {
- ASSERT(address->nr_cells == 3);
- return EXTRACTED32(address->cells[0], 24, 31);
- }
- static void
- set_rrrrrrrr(device_unit *address, unsigned val)
- {
- MBLIT32(address->cells[0], 24, 31, val);
- }
- /* MSW of 64bit address hh..hh */
- static unsigned
- extract_hh_hh(const device_unit *address)
- {
- ASSERT(address->nr_cells == 3);
- return address->cells[1];
- }
- static void
- set_hh_hh(device_unit *address, unsigned val)
- {
- address->cells[2] = val;
- }
- /* LSW of 64bit address ll..ll */
- static unsigned
- extract_ll_ll(const device_unit *address)
- {
- ASSERT(address->nr_cells == 3);
- return address->cells[2];
- }
- static void
- set_ll_ll(device_unit *address, unsigned val)
- {
- address->cells[2] = val;
- }
- /* Convert PCI textual bus address into a device unit */
- static int
- hw_phb_unit_decode(device *me,
- const char *unit,
- device_unit *address)
- {
- char *end = NULL;
- const char *chp = unit;
- unsigned long val;
- if (device_nr_address_cells(me) != 3)
- device_error(me, "PCI bus should have #address-cells == 3");
- memset(address, 0, sizeof(*address));
- if (unit == NULL)
- return 0;
- address->nr_cells = 3;
- if (isxdigit(*chp)) {
- set_ss(address, ss_config_code);
- }
- else {
- /* non-relocatable? */
- if (*chp == 'n') {
- set_n(address);
- chp++;
- }
- /* address-space? */
- if (*chp == 'i') {
- set_ss(address, ss_io_code);
- chp++;
- }
- else if (*chp == 'm') {
- set_ss(address, ss_32bit_memory_code);
- chp++;
- }
- else if (*chp == 'x') {
- set_ss(address, ss_64bit_memory_code);
- chp++;
- }
- else
- device_error(me, "Problem parsing PCI address %s", unit);
- /* possible alias */
- if (*chp == 't') {
- if (extract_ss(address) == ss_64bit_memory_code)
- device_error(me, "Invalid alias bit in PCI address %s", unit);
- set_t(address);
- chp++;
- }
- /* possible p */
- if (*chp == 'p') {
- if (extract_ss(address) != ss_32bit_memory_code)
- device_error(me, "Invalid prefetchable bit (p) in PCI address %s",
- unit);
- set_p(address);
- chp++;
- }
- }
- /* required DD */
- if (!isxdigit(*chp))
- device_error(me, "Missing device number in PCI address %s", unit);
- val = strtoul(chp, &end, 16);
- if (chp == end)
- device_error(me, "Problem parsing device number in PCI address %s", unit);
- if ((val & 0x1f) != val)
- device_error(me, "Device number (0x%lx) out of range (0..0x1f) in PCI address %s",
- val, unit);
- set_ddddd(address, val);
- chp = end;
- /* For config space, the F is optional */
- if (extract_ss(address) == ss_config_code
- && (isspace(*chp) || *chp == '\0'))
- return chp - unit;
- /* function number F */
- if (*chp != ',')
- device_error(me, "Missing function number in PCI address %s", unit);
- chp++;
- val = strtoul(chp, &end, 10);
- if (chp == end)
- device_error(me, "Problem parsing function number in PCI address %s",
- unit);
- if ((val & 7) != val)
- device_error(me, "Function number (%ld) out of range (0..7) in PCI address %s",
- (long)val, unit);
- set_fff(address, val);
- chp = end;
- /* for config space, must be end */
- if (extract_ss(address) == ss_config_code) {
- if (!isspace(*chp) && *chp != '\0')
- device_error(me, "Problem parsing PCI config address %s",
- unit);
- return chp - unit;
- }
- /* register number RR */
- if (*chp != ',')
- device_error(me, "Missing register number in PCI address %s", unit);
- chp++;
- val = strtoul(chp, &end, 16);
- if (chp == end)
- device_error(me, "Problem parsing register number in PCI address %s",
- unit);
- switch (extract_ss(address)) {
- case ss_io_code:
- #if 0
- if (extract_n(address) && val != 0)
- device_error(me, "non-relocatable I/O register must be zero in PCI address %s", unit);
- else if (!extract_n(address)
- && val != 0x10 && val != 0x14 && val != 0x18
- && val != 0x1c && val != 0x20 && val != 0x24)
- device_error(me, "I/O register invalid in PCI address %s", unit);
- #endif
- break;
- case ss_32bit_memory_code:
- #if 0
- if (extract_n(address) && val != 0)
- device_error(me, "non-relocatable memory register must be zero in PCI address %s", unit);
- else if (!extract_n(address)
- && val != 0x10 && val != 0x14 && val != 0x18
- && val != 0x1c && val != 0x20 && val != 0x24 && val != 0x30)
- device_error(me, "I/O register (0x%lx) invalid in PCI address %s",
- val, unit);
- #endif
- break;
- case ss_64bit_memory_code:
- if (extract_n(address) && val != 0)
- device_error(me, "non-relocatable 32bit memory register must be zero in PCI address %s", unit);
- else if (!extract_n(address)
- && val != 0x10 && val != 0x18 && val != 0x20)
- device_error(me, "Register number (0x%lx) invalid in 64bit PCI address %s",
- val, unit);
- case ss_config_code:
- device_error(me, "internal error");
- }
- if ((val & 0xff) != val)
- device_error(me, "Register number (0x%lx) out of range (0..0xff) in PCI address %s",
- val, unit);
- set_rrrrrrrr(address, val);
- chp = end;
- /* address */
- if (*chp != ',')
- device_error(me, "Missing address in PCI address %s", unit);
- chp++;
- switch (extract_ss(address)) {
- case ss_io_code:
- case ss_32bit_memory_code:
- val = strtoul(chp, &end, 16);
- if (chp == end)
- device_error(me, "Problem parsing address in PCI address %s", unit);
- switch (extract_ss(address)) {
- case ss_io_code:
- if (extract_n(address) && extract_t(address)
- && (val & 1024) != val)
- device_error(me, "10bit aliased non-relocatable address (0x%lx) out of range in PCI address %s",
- val, unit);
- if (!extract_n(address) && extract_t(address)
- && (val & 0xffff) != val)
- device_error(me, "64k relocatable address (0x%lx) out of range in PCI address %s",
- val, unit);
- break;
- case ss_32bit_memory_code:
- if (extract_t(address) && (val & 0xfffff) != val)
- device_error(me, "1mb memory address (0x%lx) out of range in PCI address %s",
- val, unit);
- if (!extract_t(address) && (val & 0xffffffff) != val)
- device_error(me, "32bit memory address (0x%lx) out of range in PCI address %s",
- val, unit);
- break;
- case ss_64bit_memory_code:
- case ss_config_code:
- device_error(me, "internal error");
- }
- set_ll_ll(address, val);
- chp = end;
- break;
- case ss_64bit_memory_code:
- device_error(me, "64bit addresses unimplemented");
- set_hh_hh(address, val);
- set_ll_ll(address, val);
- break;
- case ss_config_code:
- device_error(me, "internal error");
- break;
- }
- /* finished? */
- if (!isspace(*chp) && *chp != '\0')
- device_error(me, "Problem parsing PCI address %s", unit);
- return chp - unit;
- }
- /* Convert PCI device unit into its corresponding textual
- representation */
- static int
- hw_phb_unit_encode(device *me,
- const device_unit *unit_address,
- char *buf,
- int sizeof_buf)
- {
- if (unit_address->nr_cells != 3)
- device_error(me, "Incorrect number of cells in PCI unit address");
- if (device_nr_address_cells(me) != 3)
- device_error(me, "PCI bus should have #address-cells == 3");
- if (extract_ss(unit_address) == ss_config_code
- && extract_fff(unit_address) == 0
- && extract_rrrrrrrr(unit_address) == 0
- && extract_hh_hh(unit_address) == 0
- && extract_ll_ll(unit_address) == 0) {
- /* DD - Configuration Space address */
- sprintf(buf, "%x",
- extract_ddddd(unit_address));
- }
- else if (extract_ss(unit_address) == ss_config_code
- && extract_fff(unit_address) != 0
- && extract_rrrrrrrr(unit_address) == 0
- && extract_hh_hh(unit_address) == 0
- && extract_ll_ll(unit_address) == 0) {
- /* DD,F - Configuration Space */
- sprintf(buf, "%x,%d",
- extract_ddddd(unit_address),
- extract_fff(unit_address));
- }
- else if (extract_ss(unit_address) == ss_io_code
- && extract_hh_hh(unit_address) == 0) {
- /* [n]i[t]DD,F,RR,NNNNNNNN - 32bit I/O space */
- sprintf(buf, "%si%s%x,%d,%x,%x",
- extract_n(unit_address) ? "n" : "",
- extract_t(unit_address) ? "t" : "",
- extract_ddddd(unit_address),
- extract_fff(unit_address),
- extract_rrrrrrrr(unit_address),
- extract_ll_ll(unit_address));
- }
- else if (extract_ss(unit_address) == ss_32bit_memory_code
- && extract_hh_hh(unit_address) == 0) {
- /* [n]m[t][p]DD,F,RR,NNNNNNNN - 32bit memory space */
- sprintf(buf, "%sm%s%s%x,%d,%x,%x",
- extract_n(unit_address) ? "n" : "",
- extract_t(unit_address) ? "t" : "",
- extract_p(unit_address) ? "p" : "",
- extract_ddddd(unit_address),
- extract_fff(unit_address),
- extract_rrrrrrrr(unit_address),
- extract_ll_ll(unit_address));
- }
- else if (extract_ss(unit_address) == ss_32bit_memory_code) {
- /* [n]x[p]DD,F,RR,NNNNNNNNNNNNNNNN - 64bit memory space */
- sprintf(buf, "%sx%s%x,%d,%x,%x%08x",
- extract_n(unit_address) ? "n" : "",
- extract_p(unit_address) ? "p" : "",
- extract_ddddd(unit_address),
- extract_fff(unit_address),
- extract_rrrrrrrr(unit_address),
- extract_hh_hh(unit_address),
- extract_ll_ll(unit_address));
- }
- else {
- device_error(me, "Invalid PCI unit address 0x%08lx 0x%08lx 0x%08lx",
- (unsigned long)unit_address->cells[0],
- (unsigned long)unit_address->cells[1],
- (unsigned long)unit_address->cells[2]);
- }
- if (strlen(buf) > sizeof_buf)
- error("buffer overflow");
- return strlen(buf);
- }
- static int
- hw_phb_address_to_attach_address(device *me,
- const device_unit *address,
- int *attach_space,
- unsigned_word *attach_address,
- device *client)
- {
- if (address->nr_cells != 3)
- device_error(me, "attach address has incorrect number of cells");
- if (address->cells[1] != 0)
- device_error(me, "64bit attach address unsupported");
- /* directly decode the address/space */
- *attach_address = address->cells[2];
- switch (extract_ss(address)) {
- case ss_config_code:
- *attach_space = hw_phb_config_space;
- break;
- case ss_io_code:
- *attach_space = hw_phb_io_space;
- break;
- case ss_32bit_memory_code:
- case ss_64bit_memory_code:
- *attach_space = hw_phb_memory_space;
- break;
- }
- /* if non-relocatable finished */
- if (extract_n(address))
- return 1;
- /* make memory and I/O addresses absolute */
- if (*attach_space == hw_phb_io_space
- || *attach_space == hw_phb_memory_space) {
- int reg_nr;
- reg_property_spec assigned;
- if (extract_ss(address) == ss_64bit_memory_code)
- device_error(me, "64bit memory address not unsuported");
- for (reg_nr = 0;
- device_find_reg_array_property(client, "assigned-addresses", reg_nr,
- &assigned);
- reg_nr++) {
- if (!extract_n(&assigned.address)
- || extract_rrrrrrrr(&assigned.address) == 0)
- device_error(me, "client %s has invalid assigned-address property",
- device_path(client));
- if (extract_rrrrrrrr(address) == extract_rrrrrrrr(&assigned.address)) {
- /* corresponding base register */
- if (extract_ss(address) != extract_ss(&assigned.address))
- device_error(me, "client %s has conflicting types for base register 0x%lx",
- device_path(client),
- (unsigned long)extract_rrrrrrrr(address));
- *attach_address += assigned.address.cells[2];
- return 0;
- }
- }
- device_error(me, "client %s missing base address register 0x%lx in assigned-addresses property",
- device_path(client),
- (unsigned long)extract_rrrrrrrr(address));
- }
-
- return 0;
- }
- static int
- hw_phb_size_to_attach_size(device *me,
- const device_unit *size,
- unsigned *nr_bytes,
- device *client)
- {
- if (size->nr_cells != 2)
- device_error(me, "size has incorrect number of cells");
- if (size->cells[0] != 0)
- device_error(me, "64bit size unsupported");
- *nr_bytes = size->cells[1];
- return size->cells[1];
- }
- static const phb_space *
- find_phb_space(hw_phb_device *phb,
- unsigned_word addr,
- unsigned nr_bytes)
- {
- hw_phb_spaces space;
- /* find the space that matches the address */
- for (space = 0; space < nr_hw_phb_spaces; space++) {
- phb_space *pci_space = &phb->space[space];
- if (addr >= pci_space->parent_base
- && (addr + nr_bytes) <= (pci_space->parent_base + pci_space->size)) {
- return pci_space;
- }
- }
- return NULL;
- }
- static unsigned_word
- map_phb_addr(const phb_space *space,
- unsigned_word addr)
- {
- return addr - space->parent_base + space->my_base;
- }
- static unsigned
- hw_phb_io_read_buffer(device *me,
- void *dest,
- int space,
- unsigned_word addr,
- unsigned nr_bytes,
- cpu *processor,
- unsigned_word cia)
- {
- hw_phb_device *phb = (hw_phb_device*)device_data(me);
- const phb_space *pci_space = find_phb_space(phb, addr, nr_bytes);
- unsigned_word bus_addr;
- if (pci_space == NULL)
- return 0;
- bus_addr = map_phb_addr(pci_space, addr);
- DTRACE(phb, ("io read - %d:0x%lx -> %s:0x%lx (%u bytes)\n",
- space, (unsigned long)addr, pci_space->name, (unsigned long)bus_addr,
- nr_bytes));
- return core_map_read_buffer(pci_space->readable,
- dest, bus_addr, nr_bytes);
- }
- static unsigned
- hw_phb_io_write_buffer(device *me,
- const void *source,
- int space,
- unsigned_word addr,
- unsigned nr_bytes,
- cpu *processor,
- unsigned_word cia)
- {
- hw_phb_device *phb = (hw_phb_device*)device_data(me);
- const phb_space *pci_space = find_phb_space(phb, addr, nr_bytes);
- unsigned_word bus_addr;
- if (pci_space == NULL)
- return 0;
- bus_addr = map_phb_addr(pci_space, addr);
- DTRACE(phb, ("io write - %d:0x%lx -> %s:0x%lx (%u bytes)\n",
- space, (unsigned long)addr, pci_space->name, (unsigned long)bus_addr,
- nr_bytes));
- return core_map_write_buffer(pci_space->writeable, source,
- bus_addr, nr_bytes);
- }
- static unsigned
- hw_phb_dma_read_buffer(device *me,
- void *dest,
- int space,
- unsigned_word addr,
- unsigned nr_bytes)
- {
- hw_phb_device *phb = (hw_phb_device*)device_data(me);
- const phb_space *pci_space;
- /* find the space */
- if (space != hw_phb_memory_space)
- device_error(me, "invalid dma address space %d", space);
- pci_space = &phb->space[space];
- /* check out the address */
- if ((addr >= pci_space->my_base
- && addr <= pci_space->my_base + pci_space->size)
- || (addr + nr_bytes >= pci_space->my_base
- && addr + nr_bytes <= pci_space->my_base + pci_space->size))
- device_error(me, "Do not support DMA into own bus");
- /* do it */
- DTRACE(phb, ("dma read - %s:0x%lx (%d bytes)\n",
- pci_space->name, (unsigned long)addr, nr_bytes));
- return device_dma_read_buffer(device_parent(me),
- dest, pci_space->parent_space,
- addr, nr_bytes);
- }
- static unsigned
- hw_phb_dma_write_buffer(device *me,
- const void *source,
- int space,
- unsigned_word addr,
- unsigned nr_bytes,
- int violate_read_only_section)
- {
- hw_phb_device *phb = (hw_phb_device*)device_data(me);
- const phb_space *pci_space;
- /* find the space */
- if (space != hw_phb_memory_space)
- device_error(me, "invalid dma address space %d", space);
- pci_space = &phb->space[space];
- /* check out the address */
- if ((addr >= pci_space->my_base
- && addr <= pci_space->my_base + pci_space->size)
- || (addr + nr_bytes >= pci_space->my_base
- && addr + nr_bytes <= pci_space->my_base + pci_space->size))
- device_error(me, "Do not support DMA into own bus");
- /* do it */
- DTRACE(phb, ("dma write - %s:0x%lx (%d bytes)\n",
- pci_space->name, (unsigned long)addr, nr_bytes));
- return device_dma_write_buffer(device_parent(me),
- source, pci_space->parent_space,
- addr, nr_bytes,
- violate_read_only_section);
- }
- static device_callbacks const hw_phb_callbacks = {
- { hw_phb_init_address, },
- { hw_phb_attach_address, },
- { hw_phb_io_read_buffer, hw_phb_io_write_buffer },
- { hw_phb_dma_read_buffer, hw_phb_dma_write_buffer },
- { NULL, }, /* interrupt */
- { hw_phb_unit_decode,
- hw_phb_unit_encode,
- hw_phb_address_to_attach_address,
- hw_phb_size_to_attach_size }
- };
- static void *
- hw_phb_create(const char *name,
- const device_unit *unit_address,
- const char *args)
- {
- /* create the descriptor */
- hw_phb_device *phb = ZALLOC(hw_phb_device);
- /* create the core maps now */
- hw_phb_spaces space_nr;
- for (space_nr = 0; space_nr < nr_hw_phb_spaces; space_nr++) {
- phb_space *pci_space = &phb->space[space_nr];
- pci_space->map = core_create();
- pci_space->readable = core_readable(pci_space->map);
- pci_space->writeable = core_writeable(pci_space->map);
- switch (space_nr) {
- case hw_phb_memory_space:
- pci_space->name = "memory";
- break;
- case hw_phb_io_space:
- pci_space->name = "I/O";
- break;
- case hw_phb_config_space:
- pci_space->name = "config";
- break;
- case hw_phb_special_space:
- pci_space->name = "special";
- break;
- default:
- error ("internal error");
- break;
- }
- }
- return phb;
- }
- const device_descriptor hw_phb_device_descriptor[] = {
- { "phb", hw_phb_create, &hw_phb_callbacks },
- { "pci", NULL, &hw_phb_callbacks },
- { NULL, },
- };
- #endif /* _HW_PHB_ */
|