/* Driver Manager Driver Translate Interface Implementation * * COPYRIGHT (c) 2010 Cobham Gaisler AB. * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. */ /* * 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 #include #include #include #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); }