summaryrefslogtreecommitdiffstats
path: root/cpukit/libdrvmgr/drvmgr_translate.c
diff options
context:
space:
mode:
authorDaniel Hellstrom <daniel@gaisler.com>2011-11-28 09:52:03 +0100
committerDaniel Hellstrom <daniel@gaisler.com>2015-04-17 01:10:16 +0200
commite7fade3ac4559214ab0508dc54a71a3d1f522afb (patch)
tree13e32728a3b6adccd3f435db73091a8233d5dadf /cpukit/libdrvmgr/drvmgr_translate.c
parentLIBPCI: added PCI shell command (diff)
downloadrtems-e7fade3ac4559214ab0508dc54a71a3d1f522afb.tar.bz2
DRVMGR: added driver manager to cpukit/libdrvmgr
Diffstat (limited to 'cpukit/libdrvmgr/drvmgr_translate.c')
-rw-r--r--cpukit/libdrvmgr/drvmgr_translate.c149
1 files changed, 149 insertions, 0 deletions
diff --git a/cpukit/libdrvmgr/drvmgr_translate.c b/cpukit/libdrvmgr/drvmgr_translate.c
new file mode 100644
index 0000000000..7febf77d60
--- /dev/null
+++ b/cpukit/libdrvmgr/drvmgr_translate.c
@@ -0,0 +1,149 @@
+/* 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.com/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 <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 = drv_mgr.root_dev.bus;
+ to = dev->parent;
+ } else { /* CPUMEM_FROM_DMA || DMAMEM_TO_CPU */
+ from = dev->parent;
+ to = drv_mgr.root_dev.bus;
+ }
+
+ return drvmgr_translate_bus(from, to, rev, src_address, dst_address);
+}