diff options
author | Stan Shebs <shebs@codesourcery.com> | 1999-04-16 01:35:26 +0000 |
---|---|---|
committer | Stan Shebs <shebs@codesourcery.com> | 1999-04-16 01:35:26 +0000 |
commit | c906108c21474dfb4ed285bcc0ac6fe02cd400cc (patch) | |
tree | a0015aa5cedc19ccbab307251353a41722a3ae13 /sim/ppc/hw_phb.c | |
parent | cd946cff9ede3f30935803403f06f6ed30cad136 (diff) | |
download | gdb-c906108c21474dfb4ed285bcc0ac6fe02cd400cc.zip gdb-c906108c21474dfb4ed285bcc0ac6fe02cd400cc.tar.gz gdb-c906108c21474dfb4ed285bcc0ac6fe02cd400cc.tar.bz2 |
Initial creation of sourceware repositorygdb-4_18-branchpoint
Diffstat (limited to 'sim/ppc/hw_phb.c')
-rw-r--r-- | sim/ppc/hw_phb.c | 1068 |
1 files changed, 1068 insertions, 0 deletions
diff --git a/sim/ppc/hw_phb.c b/sim/ppc/hw_phb.c new file mode 100644 index 0000000..0071f59 --- /dev/null +++ b/sim/ppc/hw_phb.c @@ -0,0 +1,1068 @@ +/* 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _HW_PHB_C_ +#define _HW_PHB_C_ + +#include "device_table.h" + +#include "hw_phb.h" + +#include "corefile.h" + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#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 (type != hw_phb_normal_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, 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, 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_ */ |