forked from Imagelibrary/rtems
168 lines
4.6 KiB
C
168 lines
4.6 KiB
C
/* SPDX-License-Identifier: BSD-2-Clause */
|
|
|
|
/* Driver Manager Driver Translate Interface Implementation
|
|
*
|
|
* COPYRIGHT (c) 2010 Cobham Gaisler AB.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* Used by device drivers. The functions rely on that the parent bus driver
|
|
* has implemented the neccessary operations correctly.
|
|
*
|
|
* The translate functions are used to translate addresses between buses
|
|
* for DMA cores located on a "remote" bus, or for memory-mapped obtaining
|
|
* an address that can be used to access an remote bus.
|
|
*
|
|
* For example, PCI I/O might be memory-mapped at the PCI Host bridge,
|
|
* say address 0xfff10000-0xfff1ffff is mapped to the PCI I/O address
|
|
* of 0x00000000-0x0000ffff. The PCI Host bridge driver may then set up
|
|
* a map so that a driver that get PCI address 0x100 can translate that
|
|
* into 0xfff10100.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <drvmgr/drvmgr.h>
|
|
#include "drvmgr_internal.h"
|
|
|
|
unsigned int drvmgr_translate_bus(
|
|
struct drvmgr_bus *from,
|
|
struct drvmgr_bus *to,
|
|
int reverse,
|
|
void *src_address,
|
|
void **dst_address)
|
|
{
|
|
struct drvmgr_bus *path[16];
|
|
int dir, levels, i;
|
|
void *dst, *from_adr, *to_adr;
|
|
struct drvmgr_map_entry *map;
|
|
struct drvmgr_bus *bus;
|
|
unsigned int sz;
|
|
struct drvmgr_bus *bus_bot, *bus_top;
|
|
|
|
dst = src_address;
|
|
sz = 0xffffffff;
|
|
|
|
if (from == to) /* no need translating addresses when on same bus */
|
|
goto out;
|
|
|
|
/* Always find translation path from remote bus towards root bus. All
|
|
* buses have root bus has parent at some level
|
|
*/
|
|
if (from->depth > to->depth) {
|
|
bus_bot = from;
|
|
bus_top = to;
|
|
dir = 0;
|
|
} else {
|
|
bus_bot = to;
|
|
bus_top = from;
|
|
dir = 1;
|
|
}
|
|
levels = bus_bot->depth - bus_top->depth;
|
|
if (levels >= 16)
|
|
return 0; /* Does not support such a big depth */
|
|
i = 0;
|
|
while ((bus_bot != NULL) && bus_bot != bus_top) {
|
|
if (dir)
|
|
path[(levels - 1) - i] = bus_bot;
|
|
else
|
|
path[i] = bus_bot;
|
|
i++;
|
|
bus_bot = bus_bot->dev->parent;
|
|
}
|
|
if (bus_bot == NULL)
|
|
return 0; /* from -> to is not linearly connected */
|
|
|
|
for (i = 0; i < levels; i++) {
|
|
bus = path[i];
|
|
|
|
if ((dir && reverse) || (!dir && !reverse))
|
|
map = bus->maps_up;
|
|
else
|
|
map = bus->maps_down;
|
|
|
|
if (map == NULL)
|
|
continue; /* No translation needed - 1:1 mapping */
|
|
|
|
if (map == DRVMGR_TRANSLATE_NO_BRIDGE) {
|
|
sz = 0;
|
|
break; /* No bridge interface in this direction */
|
|
}
|
|
|
|
while (map->size != 0) {
|
|
if (reverse) {
|
|
/* Opposite direction */
|
|
from_adr = map->to_adr;
|
|
to_adr = map->from_adr;
|
|
} else {
|
|
from_adr = map->from_adr;
|
|
to_adr = map->to_adr;
|
|
}
|
|
|
|
if ((dst >= from_adr) &&
|
|
(dst <= (from_adr + (map->size - 1)))) {
|
|
if (((from_adr + (map->size - 1)) - dst) < sz)
|
|
sz = (from_adr + (map->size - 1)) - dst;
|
|
dst = (dst - from_adr) + to_adr;
|
|
break;
|
|
}
|
|
map++;
|
|
}
|
|
/* quit if no matching translation information */
|
|
if (map->size == 0) {
|
|
sz = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
out:
|
|
if (dst_address)
|
|
*dst_address = dst;
|
|
|
|
return sz;
|
|
}
|
|
|
|
unsigned int drvmgr_translate(
|
|
struct drvmgr_dev *dev,
|
|
unsigned int options,
|
|
void *src_address,
|
|
void **dst_address)
|
|
{
|
|
struct drvmgr_bus *to, *from;
|
|
int rev = 0;
|
|
|
|
rev = (~options) & 1;
|
|
if ((options == CPUMEM_TO_DMA) || (options == DMAMEM_FROM_CPU)) {
|
|
from = drvmgr.root_dev.bus;
|
|
to = dev->parent;
|
|
} else { /* CPUMEM_FROM_DMA || DMAMEM_TO_CPU */
|
|
from = dev->parent;
|
|
to = drvmgr.root_dev.bus;
|
|
}
|
|
|
|
return drvmgr_translate_bus(from, to, rev, src_address, dst_address);
|
|
}
|