#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>
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);
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");
{
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;
}
}
{
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;
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)
{
hw_phb_device *phb = device_data(me);
phb_space *pci_space;
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));
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);
}
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);
}
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);
}
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);
}
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);
}
#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
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);
}
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);
}
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);
}
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;
}
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;
}
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 {
if (*chp == 'n') {
set_n(address);
chp++;
}
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);
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++;
}
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++;
}
}
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;
if (extract_ss(address) == ss_config_code
&& (isspace(*chp) || *chp == '\0'))
return chp - unit;
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;
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;
}
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;
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;
}
if (!isspace(*chp) && *chp != '\0')
device_error(me, "Problem parsing PCI address %s", unit);
return chp - unit;
}
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) {
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) {
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) {
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) {
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) {
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");
*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 (extract_n(address))
return 1;
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)) {
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;
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;
if (space != hw_phb_memory_space)
device_error(me, "invalid dma address space %d", space);
pci_space = &phb->space[space];
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");
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;
if (space != hw_phb_memory_space)
device_error(me, "invalid dma address space %d", space);
pci_space = &phb->space[space];
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");
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, },
{ 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)
{
hw_phb_device *phb = ZALLOC(hw_phb_device);
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