Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 31 additions & 5 deletions components/drivers/include/drivers/ofw_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,46 @@ rt_inline rt_uint64_t rt_ofw_translate_dma2cpu(struct rt_ofw_node *np, rt_uint64
{
rt_uint64_t bus_addr, cpu_addr;

bus_addr = rt_ofw_reverse_address(np, "dma-ranges", address);
cpu_addr = rt_ofw_translate_address(np, "ranges", bus_addr);
cpu_addr = rt_ofw_translate_address(np, "dma-ranges", address);
if (cpu_addr != ~0ULL && cpu_addr != address)
{
return cpu_addr;
}

return cpu_addr != ~0ULL ? cpu_addr : address;
bus_addr = rt_ofw_translate_address(np, "dma-ranges", address);
if (bus_addr != ~0ULL && bus_addr != address)
{
cpu_addr = rt_ofw_translate_address(np, "ranges", bus_addr);
if (cpu_addr != ~0ULL && cpu_addr != address)
{
return cpu_addr;
}
}

return address;
}

rt_inline rt_uint64_t rt_ofw_translate_cpu2dma(struct rt_ofw_node *np, rt_uint64_t address)
{
rt_uint64_t bus_addr, dma_addr;

dma_addr = rt_ofw_reverse_address(np, "dma-ranges", address);
if (dma_addr != ~0ULL && dma_addr != address)
{
return dma_addr;
}

bus_addr = rt_ofw_reverse_address(np, "ranges", address);
dma_addr = rt_ofw_translate_address(np, "dma-ranges", bus_addr);
if (bus_addr != ~0ULL && bus_addr != address)
{
dma_addr = rt_ofw_reverse_address(np, "dma-ranges", bus_addr);
if (dma_addr != ~0ULL && dma_addr != address)
{
return dma_addr;
}
}

return dma_addr != ~0ULL ? dma_addr : address;
return address;
}

void *rt_ofw_iomap(struct rt_ofw_node *np, int index);
Expand Down
2 changes: 2 additions & 0 deletions components/drivers/include/drivers/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ void rt_pci_msix_init(struct rt_pci_device *pdev);

void rt_pci_set_master(struct rt_pci_device *pdev);
void rt_pci_clear_master(struct rt_pci_device *pdev);
rt_err_t rt_pci_enable_device(struct rt_pci_device *pdev);
rt_err_t rt_pci_disable_device(struct rt_pci_device *pdev);

struct rt_pci_host_bridge *rt_pci_host_bridge_alloc(rt_size_t priv_size);
rt_err_t rt_pci_host_bridge_free(struct rt_pci_host_bridge *);
Expand Down
224 changes: 137 additions & 87 deletions components/drivers/ofw/io.c
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,37 @@ int rt_ofw_get_address_array(struct rt_ofw_node *np, int nr, rt_uint64_t *out_re
return count;
}

static rt_bool_t ofw_bus_is_pci(struct rt_ofw_node *bus)
{
rt_ssize_t len;
struct rt_ofw_prop *prop;

if (!bus)
{
return RT_FALSE;
}

prop = rt_ofw_get_prop(bus, "device_type", &len);

return prop && prop->value && len >= 3 &&
!rt_strncmp(prop->value, "pci", 3);
}

static rt_uint64_t ofw_read_bus_address(const fdt32_t **cell, int addr_cells,
struct rt_ofw_node *bus)
{
if (addr_cells == 3 && ofw_bus_is_pci(bus))
{
const fdt32_t *c = *cell;

*cell += 3;

return ((rt_uint64_t)fdt32_to_cpu(c[1]) << 32) | fdt32_to_cpu(c[2]);
}

return rt_fdt_next_cell(cell, addr_cells);
}

static struct bus_ranges *ofw_bus_ranges(struct rt_ofw_node *np, struct rt_ofw_prop *prop)
{
int id;
Expand Down Expand Up @@ -321,12 +352,13 @@ static struct bus_ranges *ofw_bus_ranges(struct rt_ofw_node *np, struct rt_ofw_p

while (groups --> 0)
{
*child_addr++ = rt_fdt_next_cell(&cell, child_address_cells);
*parent_addr++ = rt_fdt_next_cell(&cell, parent_address_cells);
*child_addr++ = ofw_read_bus_address(&cell, child_address_cells, np);
*parent_addr++ = ofw_read_bus_address(&cell, parent_address_cells, np->parent);
*child_size++ = rt_fdt_next_cell(&cell, child_size_cells);
}

ranges->np = np;
ranges->range_type = prop->name;

id = (int)rt_atomic_add(&_bus_ranges_idx, 1);
RT_ASSERT(id < RT_ARRAY_SIZE(_bus_ranges));
Expand All @@ -337,142 +369,160 @@ static struct bus_ranges *ofw_bus_ranges(struct rt_ofw_node *np, struct rt_ofw_p
return ranges;
}

rt_uint64_t rt_ofw_translate_address(struct rt_ofw_node *np, const char *range_type, rt_uint64_t address)
static struct bus_ranges *ofw_get_bus_ranges(struct rt_ofw_node *bus, const char *range_type)
{
rt_uint64_t cpu_addr = address;
rt_ssize_t len;
struct rt_ofw_prop *prop;
struct bus_ranges *ranges = RT_NULL;

if (!range_type)
prop = rt_ofw_get_prop(bus, range_type, &len);

if (!prop || !len)
{
range_type = "ranges";
return RT_NULL;
}

rt_ofw_foreach_parent_node(np)
for (int i = 0; i < RT_ARRAY_SIZE(_bus_ranges); ++i)
{
rt_ssize_t len;
struct rt_ofw_prop *prop;
struct bus_ranges *ranges = RT_NULL;

prop = rt_ofw_get_prop(np, range_type, &len);

if (!prop || !len)
if (!_bus_ranges[i])
{
continue;
break;
}

for (int i = 0; i < RT_ARRAY_SIZE(_bus_ranges); ++i)
if (_bus_ranges[i]->np == bus &&
!rt_strcmp(_bus_ranges[i]->range_type, range_type))
{
if (!_bus_ranges[i])
{
break;
}

if (_bus_ranges[i]->np == np)
{
ranges = _bus_ranges[i];
break;
}
ranges = _bus_ranges[i];
break;
}
}

if (!ranges)
{
ranges = ofw_bus_ranges(np, prop);
}
if (!ranges)
{
ranges = ofw_bus_ranges(bus, prop);
}

if (ranges)
{
for (int i = 0; i < ranges->nr; ++i)
{
rt_uint64_t child_addr = ranges->child_addr[i];
rt_uint64_t child_size = ranges->child_size[i];
return ranges;
}

if (address >= child_addr && address < child_addr + child_size)
{
cpu_addr = address + (ranges->parent_addr[i] - child_addr);
static rt_uint64_t ofw_translate_at_bus(struct rt_ofw_node *bus, const char *range_type,
rt_uint64_t address)
{
struct bus_ranges *ranges = ofw_get_bus_ranges(bus, range_type);

break;
}
}
}
else
if (!ranges)
{
return ~0ULL;
}

for (int i = 0; i < ranges->nr; ++i)
{
rt_uint64_t child_addr = ranges->child_addr[i];
rt_uint64_t child_size = ranges->child_size[i];

if (address >= child_addr && address < child_addr + child_size)
{
cpu_addr = ~0ULL;
return address + (ranges->parent_addr[i] - child_addr);
}
}

return address;
}

static rt_uint64_t ofw_reverse_at_bus(struct rt_ofw_node *bus, const char *range_type,
rt_uint64_t address)
{
struct bus_ranges *ranges = ofw_get_bus_ranges(bus, range_type);

if (!ranges)
{
return ~0ULL;
}

rt_ofw_node_put(np);
for (int i = 0; i < ranges->nr; ++i)
{
rt_uint64_t parent_addr = ranges->parent_addr[i];
rt_uint64_t child_size = ranges->child_size[i];

break;
if (address >= parent_addr && address < parent_addr + child_size)
{
return ranges->child_addr[i] + (address - parent_addr);
}
}

return cpu_addr;
return address;
}

rt_uint64_t rt_ofw_reverse_address(struct rt_ofw_node *np, const char *range_type, rt_uint64_t address)
rt_uint64_t rt_ofw_translate_address(struct rt_ofw_node *np, const char *range_type, rt_uint64_t address)
{
rt_uint64_t bus_addr = address;
struct rt_ofw_node *bus;

if (!range_type)
{
range_type = "ranges";
}

rt_ofw_foreach_parent_node(np)
for (bus = np ? np->parent : RT_NULL; bus; bus = bus->parent)
{
rt_ssize_t len;
struct rt_ofw_prop *prop;
struct bus_ranges *ranges = RT_NULL;
rt_uint64_t translated;

prop = rt_ofw_get_prop(np, range_type, &len);

if (!prop || !len)
if (!rt_ofw_get_prop(bus, range_type, &len) || !len)
{
continue;
}

for (int i = 0; i < RT_ARRAY_SIZE(_bus_ranges); ++i)
translated = ofw_translate_at_bus(bus, range_type, address);
if (translated == ~0ULL)
{
if (!_bus_ranges[i])
{
break;
}

if (_bus_ranges[i]->np == np)
{
ranges = _bus_ranges[i];
break;
}
return ~0ULL;
}

if (!ranges)
address = translated;
}

return address;
}

rt_uint64_t rt_ofw_reverse_address(struct rt_ofw_node *np, const char *range_type, rt_uint64_t address)
{
struct rt_ofw_node *bus;
struct rt_ofw_node *parents[16];
int count = 0;

if (!range_type)
{
range_type = "ranges";
}

for (bus = np ? np->parent : RT_NULL; bus; bus = bus->parent)
{
rt_ssize_t len;

if (!rt_ofw_get_prop(bus, range_type, &len) || !len)
{
ranges = ofw_bus_ranges(np, prop);
continue;
}

if (ranges)
if (count < (int)RT_ARRAY_SIZE(parents))
{
for (int i = 0; i < ranges->nr; ++i)
{
rt_uint64_t parent_addr = ranges->parent_addr[i];
rt_uint64_t child_size = ranges->child_size[i];
parents[count++] = bus;
}
}

if (address >= parent_addr && address < parent_addr + child_size)
{
bus_addr = ranges->child_addr[i] + (address - parent_addr);
while (count-- > 0)
{
rt_uint64_t reversed = ofw_reverse_at_bus(parents[count], range_type, address);

break;
}
}
}
else
if (reversed == ~0ULL)
{
bus_addr = ~0ULL;
return ~0ULL;
}

rt_ofw_node_put(np);

break;
address = reversed;
}

return bus_addr;
return address;
}

#ifdef ARCH_CPU_64BIT
Expand Down
1 change: 1 addition & 0 deletions components/drivers/ofw/ofw_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ struct bus_ranges
{
rt_size_t nr;
struct rt_ofw_node *np;
const char *range_type;

rt_uint64_t *child_addr;
rt_uint64_t *parent_addr;
Expand Down
Loading
Loading