Added SDMA driver for memory-to-memory transfers
authorSebastian Wicki <swicki@student.ethz.ch>
Tue, 4 Feb 2014 21:45:47 +0000 (22:45 +0100)
committerSebastian Wicki <swicki@student.ethz.ch>
Tue, 4 Feb 2014 22:17:52 +0000 (23:17 +0100)
This adds support for the OMAP4460 SDMA engine found on the Pandaboard.
The driver currently only supports memory-to-memory transfer. Those can
be requested through the Flounder interface described in if/omap_sdma.if.

errors/errno.fugu
hake/symbolic_targets.mk
if/Hakefile
if/omap_sdma.if [new file with mode: 0644]
usr/drivers/omap44xx/sdma/Hakefile [new file with mode: 0644]
usr/drivers/omap44xx/sdma/main.c [new file with mode: 0644]
usr/drivers/omap44xx/sdma/omap_sdma.c [new file with mode: 0644]
usr/drivers/omap44xx/sdma/omap_sdma.h [new file with mode: 0644]
usr/drivers/omap44xx/sdma/sdma.h [new file with mode: 0644]
usr/drivers/omap44xx/sdma/service.c [new file with mode: 0644]

index 3b84d7b..a7d44c3 100755 (executable)
@@ -959,6 +959,18 @@ errors cache FS_CACHE_ {
     failure CONFLICT            "Cache already contains an item with the requested key",
 };
 
+// errors generated by OMAP SDMA driver
+errors omap_sdma OMAP_SDMA_ERR_ {
+    failure NO_AVAIL_CHANNEL                "All channels are currently allocated",
+    failure TRANSACTION                     "Memory Transaction error occured",
+    failure SUPERVISOR                      "Supvervisor transaction error occured",
+    failure MISALIGNED_ADDRESS              "Transfer addresses were misaligned",
+    failure HARDWARE_LIMIT_SIZE             "Transfer size values too large for hardware",
+    failure HARDWARE_LIMIT_ADDR             "Transfer address modifier values too large for hardware",
+    failure OUT_OF_BOUNDS                   "Transfer access outside frame cap boundaries",
+    failure CAP_LOOKUP                      "Failure during frame capability lookup",
+};
+
 // common/generic errors
 errors common ERR_ {
     failure NOTIMP              "Not implemented",
index 7120851..3c5b487 100644 (file)
@@ -483,6 +483,7 @@ PANDABOARD_MODULES=\
        armv7/sbin/memtest \
        armv7/sbin/kaluga \
        armv7/sbin/fish \
+       armv7/sbin/sdma \
        armv7/sbin/usb_manager \
        armv7/sbin/usb_keyboard \
 
index a67bd93..f1e27a7 100644 (file)
@@ -50,6 +50,7 @@
                "skb",
                "skb_map",
                "octopus",               
+               "omap_sdma",
                "spawn",
                "terminal",
                "terminal_config",
@@ -79,6 +80,7 @@
                "mem",
                "xmplthc",
                "octopus",
+               "omap_sdma",
                "ata_rw28" ],
              arch <- allArchitectures
 ]
diff --git a/if/omap_sdma.if b/if/omap_sdma.if
new file mode 100644 (file)
index 0000000..5b23799
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2014, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, CAB F.78, Universitaetstr 6, CH-8092 Zurich.
+ */
+
+interface omap_sdma "Interface for the OMAP44xx SDMA driver" {
+
+    alias uint24 uint32;
+
+    typedef enum {
+        DATA_TYPE_8BIT,
+        DATA_TYPE_16BIT,
+        DATA_TYPE_32BIT
+    } data_type;
+
+    /**
+     * The count_2d struct is used to specify the size of the transferred
+     * frame. It is represented as a two-dimensional grid consisting of pixels.
+     * The x_count and y_count are specified as an amount of pixels.
+     *
+     *    +--->  x      The represented grid cannot exceed the boundaries of
+     *    |             frame capability found in the addr_2d struct.
+     *    v
+     *
+     *    y
+     */
+
+    typedef struct {
+        data_type pixel_size;
+
+        uint32 y_count;
+        uint32 x_count;
+    } count_2d;
+
+    /**
+     * The addr_2d struct can be used for flexible addressing. The x_start and
+     * y_start values define the start offset on each axis. The values of
+     * {x,y}_modify are used to calculate the address of consecutive accesses.
+     *
+     * For normal sequential access, set {x,y}_start to 0 and {x,y}_modify to 1.
+     *
+     * All values have the unit of one pixel, its size is defined in the
+     * count_2d struct. The following pseudo code gives a formal definition
+     * how these values are used:
+     *
+     *  pixel_t *addr; // 8, 16 or 32 bit integer pointer
+     *
+     *  addr += x_start + (y_count * y_start);
+     *  for (y = 1; y <= y_count; y++) {
+     *      for (x = 1; x <= x_count; x++) {
+     *
+     *          access( *addr );
+     *
+     *          if (x < x_count) {
+     *              // within the frame
+     *              addr += x_modify;
+     *          } else {
+     *              // at the end of a frame
+     *              addr += y_modify;
+     *          }
+     *      }
+     *  }
+     */
+
+    typedef struct {
+        cap cap;
+
+        uint32 x_start;
+        uint32 y_start;
+
+        int32 x_modify;
+        int32 y_modify;
+    } addr_2d;
+
+    /**
+     * Copies the whole content of the source frame into the destination frame.
+     */
+    rpc mem_copy(in cap dst, in cap src, out errval err);
+
+    /**
+     * Fills the whole destination frame with the specified color value.
+     */
+    rpc mem_fill(in cap dst, in uint8 color, out errval err);
+
+    /**
+     * Copies the amount of data specified by count_2d from the source frame
+     * to the destination frame. For both, source and destination, flexible
+     * address generation can be used for stride access, see above.
+     *
+     * If the 'transparent' boolean is set, the color value will be used for
+     * transparent copy mode: Source data values matching the color value will
+     * not be written to the destination. For a pixel size of 32 bits, only the
+     * lower 24 bits of the color value are used for comparison.
+     * The color value is ignored if the boolean is set to false.
+     */
+    rpc mem_copy_2d(in addr_2d dst, in addr_2d src, in count_2d count,
+                    in bool transparent, in uint24 color, out errval err);
+
+    /**
+     * Fills the destination frame with the specified color value, using
+     * the flexible, two-dimensional addressing mode described above.
+     * As the color value is 24 bits wide, the upper 8 bits are set to zero
+     * when using a pixel size of 32 bits.
+     */
+    rpc mem_fill_2d(in addr_2d dst, in uint24 color,
+                    in count_2d count, out errval err);
+
+};
diff --git a/usr/drivers/omap44xx/sdma/Hakefile b/usr/drivers/omap44xx/sdma/Hakefile
new file mode 100644 (file)
index 0000000..2c26402
--- /dev/null
@@ -0,0 +1,25 @@
+--------------------------------------------------------------------------
+-- Copyright (c) 2007-2014, ETH Zurich.
+-- All rights reserved.
+--
+-- This file is distributed under the terms in the attached LICENSE file.
+-- If you do not find this file, copies can be found by writing to:
+-- ETH Zurich D-INFK, CAB F.78, Universitaetstr 6, CH-8092 Zurich.
+--
+-- Hakefile for omap44xx sdma driver
+--
+--------------------------------------------------------------------------
+
+[
+    build application { target = "sdma",
+                    cFiles = (find withSuffices [".c"]),
+                    mackerelDevices = [ "omap/omap44xx_sdma" ],
+
+                    flounderDefs = [ "omap_sdma" ],
+                    flounderBindings = [ "omap_sdma" ],
+                    flounderTHCStubs = [ "omap_sdma" ],
+
+                    addLibraries = ["driverkit", "thc"],
+                    architectures = ["armv7", "armv7-m"]
+                  }
+]
diff --git a/usr/drivers/omap44xx/sdma/main.c b/usr/drivers/omap44xx/sdma/main.c
new file mode 100644 (file)
index 0000000..8452d58
--- /dev/null
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 2014, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, CAB F.78, Universitaetstr 6, CH-8092 Zurich.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <barrelfish/barrelfish.h>
+#include <barrelfish/waitset.h>
+#include <barrelfish/inthandler.h>
+#include <driverkit/driverkit.h>
+
+#include <thc/thc.h>
+
+#include <arch/arm/omap44xx/device_registers.h>
+
+#include "sdma.h"
+#include "omap_sdma.h"
+
+// Channel State. Filled by the interrupt callback, read by the request task.
+static struct {
+    awe_t *request;
+    errval_t err;
+} channel_state[OMAP44XX_SDMA_NUM_CHANNEL];
+
+/**
+ * \brief Interrupt callback which will be called when a channel interrupt
+ * occurs.
+ *
+ * \param channel   Channel which triggered the interrupt
+ * \param err       State of the channel, SYS_ERR_OK if transfer completed
+ */
+static void sdma_irq_handler(omap_sdma_channel_t channel, errval_t err)
+{
+    channel_state[channel].err = err;
+    THCSchedule(channel_state[channel].request);
+}
+
+/**
+ * \brief Execute a transfer on the SDMA engine. Blocks until the transfer is
+ * completed or an error occurred.
+ *
+ * \param conf   Pointer to valid & initialized channel configuration
+ */
+static errval_t run_omap_sdma_transfer(struct omap_sdma_channel_conf *conf)
+{
+    errval_t err;
+    omap_sdma_channel_t channel;
+
+    err = omap_sdma_allocate_channel(&channel);
+    if (err_is_fail(err)) return err;
+
+    // configure and enable allocated channel
+    omap_sdma_set_channel_conf(channel, conf);
+    omap_sdma_enable_channel(channel, true);
+
+    // this task will be rescheduled by the IRQ handler
+    THCSuspend(&channel_state[channel].request);
+
+    // read status flag set by IRQ handler
+    err = channel_state[channel].err;
+
+    omap_sdma_free_channel(channel);
+
+    return err;
+}
+
+/**
+ * \brief Converts the pixel size of the Flounder interface description to the
+ * element size needed for the hardware.
+ *
+ */
+static omap44xx_sdma_data_type_t extract_data_type(omap_sdma_data_type_t pixel_size)
+{
+    omap44xx_sdma_data_type_t data_type;
+
+    switch(pixel_size) {
+    case omap_sdma_DATA_TYPE_8BIT:
+        data_type = omap44xx_sdma_DATA_TYPE_8BIT;
+        break;
+    case omap_sdma_DATA_TYPE_16BIT:
+        data_type = omap44xx_sdma_DATA_TYPE_16BIT;
+        break;
+    case omap_sdma_DATA_TYPE_32BIT:
+    default:
+        data_type = omap44xx_sdma_DATA_TYPE_32BIT;
+        break;
+    }
+
+    return data_type;
+}
+
+/**
+ * \brief Initializes a configuration struct for the given parameters. It is
+ * the callers responsibility ensure that the start address and count values
+ * are valid.
+ */
+static void init_channel_conf(struct omap_sdma_channel_conf *conf,
+                lpaddr_t dst_start, lpaddr_t src_start,
+                int32_t dst_x_modify, int32_t dst_y_modify,
+                int32_t src_x_modify, int32_t src_y_modify,
+                omap_sdma_count_2d_t count,
+                omap44xx_sdma_color_mode_t color_mode, uint32_t color)
+{
+    assert(conf);
+
+    omap44xx_sdma_data_type_t data_type = extract_data_type(count.pixel_size);
+
+    // OMAP4460 TRM: SDMA 16.4.5
+    int32_t es = 1 << (data_type);
+    int32_t src_element_index = (src_x_modify - 1) * es + 1;
+    int32_t src_frame_index   = (src_y_modify - 1) * es + 1;
+
+    int32_t dst_element_index = (dst_x_modify - 1) * es + 1;
+    int32_t dst_frame_index   = (dst_y_modify - 1) * es + 1;
+
+    *conf = (struct omap_sdma_channel_conf) {
+        // low priority for software-synchronized transfers
+        .read_priority  = omap44xx_sdma_PORT_PRIORITY_LOW,
+        .write_priority = omap44xx_sdma_PORT_PRIORITY_LOW,
+
+        // normal copy/transparent copy/constant fill
+        .color_mode = color_mode,
+        .color = color,
+
+        // wait for last write to complete
+        .write_mode = omap44xx_sdma_WRITE_MODE_LAST_NON_POSTED,
+
+        // channel linking is not used
+        .enable_link = false,
+        .next_channel = 0,
+
+        // always use double indexing mode, packed & burst transfer
+        .src_conf = {
+            .start_address      = src_start,
+            .addr_mode          = omap44xx_sdma_ADDR_MODE_DOUBLE_IDX,
+            .element_index      = src_element_index,
+            .frame_index        = src_frame_index,
+            .packed_transfer    = omap44xx_sdma_SRC_PACKED_ENABLE,
+            .burst_mode         = omap44xx_sdma_BURST_EN_64BYTE,
+        },
+
+        .dst_conf = {
+            .start_address      = dst_start,
+            .addr_mode          = omap44xx_sdma_ADDR_MODE_DOUBLE_IDX,
+            .element_index      = dst_element_index,
+            .frame_index        = dst_frame_index,
+            .packed_transfer    = omap44xx_sdma_DST_PACKED_ENABLE,
+            .burst_mode         = omap44xx_sdma_BURST_EN_64BYTE,
+        },
+
+        // conversion of omap_count_2d
+        .transfer_size = {
+            .element_number = count.x_count,
+            .frame_number   = count.y_count,
+            .data_type      = data_type,
+        },
+    };
+}
+
+/**
+ * \brief Splits the physical frame size into two factors, as the SDMA engine
+ * needs the memory region to be specified in EN * FN.
+ *
+ * \param bits      Size of the frame as a power of two.
+ * \param retcount  Pointer to count struct which will be filled with frame size
+ */
+static void init_count_1d(uint8_t bits, omap_sdma_count_2d_t *retcount)
+{
+    assert(retcount);
+
+    // split frame size: 2^b = 2^(b/2) * 2^(b - b/2)
+    uint8_t x_bits = MIN(bits, OMAP44XX_SDMA_MAX_EN_BITS);
+    uint8_t y_bits = bits - x_bits;
+
+    // fill count struct
+    retcount->pixel_size = omap_sdma_DATA_TYPE_8BIT;
+    retcount->x_count = 1 << x_bits;
+    retcount->y_count = 1 << y_bits;
+}
+
+/**
+ * \brief Performs a 32bit integer multiplication and checks for overflow.
+ */
+inline static bool i32_mull_overflow(int32_t y, int32_t x, int32_t* prod) {
+    int64_t i64prod=(int64_t)x*y;
+    if (i64prod > INT32_MAX || i64prod < INT32_MIN) return true;
+    *prod = i64prod & 0xffffffff;
+    return false;
+}
+
+/**
+ * \brief Calculates the start address for a given frame capability in a
+ * two-dimensional transfer.
+ *
+ * \param cap       frame capability in which the transfer should happen
+ * \param addr      addr_2d struct containing start offset and modifiers
+ * \param count     count_2d struct specifing size of the transfer
+ * \param retaddr   filled with the physical start address of the transfer
+ *
+ * This function also does some sanity checks, to ensure that the hardware will
+ * not access any values outside the frame boundaries.
+ */
+static errval_t frame_address_2d(struct capref cap, omap_sdma_addr_2d_t *addr,
+                omap_sdma_count_2d_t *count, lpaddr_t *retaddr)
+{
+    assert(addr);
+    assert(count);
+    assert(retaddr);
+
+    errval_t err;
+    struct frame_identity id;
+
+    err = invoke_frame_identify(cap, &id);
+    if (err_is_fail(err)) return err_push(err, OMAP_SDMA_ERR_CAP_LOOKUP);
+
+    lpaddr_t frame_start = id.base;
+    int32_t frame_size = (1 << id.bits);
+
+    // image size cannot exceed hardware limits
+    if (count->x_count > OMAP44XX_SDMA_MAX_EN ||
+        count->y_count > OMAP44XX_SDMA_MAX_FN
+    ) {
+        return OMAP_SDMA_ERR_HARDWARE_LIMIT_SIZE;
+    }
+
+    // pixel size in bytes
+    int32_t pixel_size = 1 << extract_data_type(count->pixel_size);
+    // image size in pixels
+    int32_t x_cnt = count->x_count;
+    int32_t y_cnt = count->y_count;
+
+    // {x,y} modifiers and their accumulated value
+    // (all value in bytes, not pixels!)
+    int32_t x_mod, y_mod,
+            x_mod_sum, y_mod_sum;
+
+    // x_mod = addr->x_modify * pixel_size
+    // y_mod = addr->y_modify * pixel_size
+    // x_mod_sum = (x_cnt-1) * x_mod;
+    // y_mod_sum = (y_cnt-1) * y_mod;
+
+    // check for integer overflow
+    if (
+        (addr->x_modify > INT16_MAX || addr->x_modify < INT16_MIN) ||
+        i32_mull_overflow(addr->x_modify, pixel_size, &x_mod) ||
+        i32_mull_overflow(addr->y_modify, pixel_size, &y_mod) ||
+        i32_mull_overflow(x_cnt-1, x_mod, &x_mod_sum) ||
+        i32_mull_overflow(y_cnt-1, y_mod, &y_mod_sum)
+    ) {
+        return OMAP_SDMA_ERR_HARDWARE_LIMIT_ADDR;
+    }
+
+    // first access performed by the device (start offset)
+    int32_t first_access = (addr->y_start * y_cnt + addr->x_start) * pixel_size;
+    // last access performed by the device
+    int32_t last_access  = first_access + (y_cnt * x_mod_sum) + y_mod_sum;
+
+    int32_t lowest_access, highest_access;
+
+    if (x_mod >= 0 && y_mod >= 0) {
+        // monotonic access
+        // first access is smallest, last access is largest
+        lowest_access  = first_access;
+        highest_access = last_access;
+    } else if (x_mod < 0 && y_mod < 0) {
+        // monotonic access
+        // last access is smallest, first access is largest
+        lowest_access  = last_access;
+        highest_access = first_access;
+    } else {
+        // non-monotonic access
+        if (x_mod > 0) {
+            // x_mod > 0, y_mod < 0
+            if (x_mod_sum + y_mod < 0) {
+                lowest_access  = last_access  - x_mod_sum;
+                highest_access = first_access + x_mod_sum;
+            } else {
+                lowest_access  = first_access;
+                highest_access = last_access;
+            }
+        } else {
+            // x_mod < 0, y_mod > 0
+            if (x_mod_sum + y_mod > 0) {
+                lowest_access  = first_access + x_mod_sum;
+                highest_access = last_access  - x_mod_sum;
+            } else {
+                lowest_access  = last_access;
+                highest_access = first_access;
+            }
+        }
+    }
+
+    // all accesses have to be within frame boundaries
+    if (lowest_access < 0 || highest_access >= frame_size) {
+        return OMAP_SDMA_ERR_OUT_OF_BOUNDS;
+    }
+
+    *retaddr = frame_start + first_access;
+
+    return SYS_ERR_OK;
+}
+
+/**
+ * \brief Stub to perform simple frame-to-frame memory copy
+ * \see   Flounder definition in if/omap_sdma.if
+ */
+errval_t mem_copy(struct capref dst_cap, struct capref src_cap)
+{
+    errval_t err;
+    omap_sdma_count_2d_t count;
+    struct frame_identity src_id, dst_id;
+
+    // get frame sizes
+    err = invoke_frame_identify(src_cap, &src_id);
+    if (err_is_fail(err)) return err_push(err, OMAP_SDMA_ERR_CAP_LOOKUP);
+
+    err = invoke_frame_identify(dst_cap, &dst_id);
+    if (err_is_fail(err)) return err_push(err, OMAP_SDMA_ERR_CAP_LOOKUP);
+
+    // infer element/frame number for smaller frame
+    init_count_1d(MIN(dst_id.bits, dst_id.bits), &count);
+
+    // configure and initiate transfer
+    struct omap_sdma_channel_conf conf;
+    init_channel_conf(&conf, dst_id.base, src_id.base, 1, 1, 1, 1, count,
+                         omap44xx_sdma_DISABLE_COLOR_MODE, 0);
+    err = run_omap_sdma_transfer(&conf);
+
+    return err;
+}
+
+/**
+ * \brief Stub to fill a memory frame with a constant value
+ * \see   Flounder definition in if/omap_sdma.if
+ */
+errval_t mem_fill(struct capref dst_cap, uint8_t color)
+{
+    errval_t err;
+    omap_sdma_count_2d_t count;
+    struct frame_identity dst_id;
+
+    // get frame size and infer element/frame number
+    err = invoke_frame_identify(dst_cap, &dst_id);
+    if (err_is_fail(err)) return err_push(err, OMAP_SDMA_ERR_CAP_LOOKUP);
+    init_count_1d(dst_id.bits, &count);
+
+    // configure and initiate transfer
+    struct omap_sdma_channel_conf conf;
+    init_channel_conf(&conf, dst_id.base, 0, 1, 1, 1, 1, count,
+                         omap44xx_sdma_CONSTANT_FILL, color);
+    err = run_omap_sdma_transfer(&conf);
+
+    return err;
+}
+
+/**
+ * \brief Stub to perform a two-dimensional memory copy
+ * \see   Flounder definition in if/omap_sdma.if
+ */
+errval_t mem_copy_2d(omap_sdma_addr_2d_t dst, omap_sdma_addr_2d_t src,
+                omap_sdma_count_2d_t count, bool transparent, uint32_t color)
+{
+    errval_t err;
+    lpaddr_t src_start, dst_start;
+
+    // check boundaries and calculate start address for source/dest frames
+    err = frame_address_2d(dst.cap, &dst, &count, &dst_start);
+    if (err_is_fail(err)) return err;
+
+    err = frame_address_2d(src.cap, &src, &count, &src_start);
+    if (err_is_fail(err)) return err;
+
+    // use transparent copy mode if requested
+    omap44xx_sdma_color_mode_t color_mode = (transparent) ?
+                                                omap44xx_sdma_TRANSPARENT_COPY :
+                                                omap44xx_sdma_DISABLE_COLOR_MODE;
+
+    struct omap_sdma_channel_conf conf;
+    init_channel_conf(&conf, dst_start, src_start,
+                         dst.x_modify, dst.y_modify,
+                         src.x_modify, src.y_modify,
+                         count, color_mode, color);
+
+    err = run_omap_sdma_transfer(&conf);
+    return err;
+}
+
+/**
+ * \brief Stub to fill parts of a frame using two-dimensional indeces
+ * \see   Flounder definition in if/omap_sdma.if
+ */
+errval_t mem_fill_2d(omap_sdma_addr_2d_t dst, omap_sdma_count_2d_t count, uint32_t color)
+{
+    errval_t err;
+    lpaddr_t  dst_start;
+
+    err = frame_address_2d(dst.cap, &dst, &count, &dst_start);
+    if (err_is_fail(err)) return err;
+
+    struct omap_sdma_channel_conf conf;
+    init_channel_conf(&conf, dst_start, 0,
+                         dst.x_modify, dst.y_modify, 0, 0,
+                         count, omap44xx_sdma_CONSTANT_FILL, color);
+
+    err = run_omap_sdma_transfer(&conf);
+    return err;
+}
+
+int main(int argc, char **argv)
+{
+    errval_t err;
+    lvaddr_t dev_base;
+
+    err = map_device_register(OMAP44XX_SDMA, 0x1000, &dev_base);
+    if (err_is_fail(err)) {
+        USER_PANIC_ERR(err, "unable to map SDMA registers");
+    }
+
+    omap_sdma_init((mackerel_addr_t)dev_base, sdma_irq_handler);
+    start_service();
+
+    return 0;
+}
diff --git a/usr/drivers/omap44xx/sdma/omap_sdma.c b/usr/drivers/omap44xx/sdma/omap_sdma.c
new file mode 100644 (file)
index 0000000..56d828f
--- /dev/null
@@ -0,0 +1,507 @@
+/*
+ * Copyright (c) 2014, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, CAB F.78, Universitaetstr 6, CH-8092 Zurich.
+ */
+
+#include <barrelfish/barrelfish.h>
+
+#ifdef OMAP_SDMA_KERNELBENCH
+    #include <arch/armv7/gic.h>
+    #include <arm_hal.h>
+    #include <exceptions.h>
+    #include <kernel.h>
+#else
+    #include <barrelfish/waitset.h>
+    #include <barrelfish/inthandler.h>
+    #include <driverkit/driverkit.h>
+#endif
+
+#include <string.h>
+
+#include "omap_sdma.h"
+#include <omap44xx_map.h>
+
+static omap44xx_sdma_t devsdma;
+static bool allocated_channel[OMAP44XX_SDMA_NUM_CHANNEL];
+
+static omap_sdma_irq_handler_t irq_callback;
+
+static inline errval_t omap_sdma_read_csr(omap44xx_sdma_dma4_csr_t csr)
+{
+    if (omap44xx_sdma_dma4_csr_misaligned_adrs_err_extract(csr)) {
+        return OMAP_SDMA_ERR_MISALIGNED_ADDRESS;
+    } else if (omap44xx_sdma_dma4_csr_supervisor_err_extract(csr)) {
+        return OMAP_SDMA_ERR_SUPERVISOR;
+    } else if (omap44xx_sdma_dma4_csr_trans_err_extract(csr)) {
+        return OMAP_SDMA_ERR_TRANSACTION;
+    }
+
+    return SYS_ERR_OK;
+}
+
+static void omap_sdma_irq_handler(void *arg)
+{
+    uint32_t irqstatus = omap44xx_sdma_dma4_irqstatus_line_rd(&devsdma, OMAP44XX_SDMA_IRQ_LINE);
+
+    for (omap_sdma_channel_t channel=0; channel<OMAP44XX_SDMA_NUM_CHANNEL; channel++) {
+        bool active = (irqstatus >> channel) & 0x1;
+        if(!active) continue;
+
+        SDMA_PRINT("interrupt on channel %u\n", channel);
+
+        // read out status flags
+        omap44xx_sdma_dma4_csr_t csr = omap44xx_sdma_dma4_csr_rd(&devsdma, channel);
+
+        // check for errors
+        errval_t err = omap_sdma_read_csr(csr);
+
+        if (err_is_ok(err)) {
+            // no error found, check for "End of Block" event
+            if(omap44xx_sdma_dma4_csr_block_extract(csr)) {
+                irq_callback(channel, err);
+            }
+        } else {
+            // OMAP4460 Multimedia Device Silicon Errata, Revision A:
+            // 1.7 sDMA Channel Is Not Disabled After A Transaction Error
+            if (err_no(err) == OMAP_SDMA_ERR_TRANSACTION) {
+                // Workaround: disable channel by software
+                omap44xx_sdma_dma4_ccr_enable_wrf(&devsdma, channel, 0);
+            }
+
+            irq_callback(channel, err);
+        }
+
+        // clear all read status flags
+        omap44xx_sdma_dma4_csr_wr(&devsdma, channel, csr);
+    }
+
+    SDMA_PRINT("interrupt finished\n");
+
+    // clear all set status bits
+    omap44xx_sdma_dma4_irqstatus_line_wr(&devsdma, OMAP44XX_SDMA_IRQ_LINE, irqstatus);
+}
+
+
+#ifdef OMAP_SDMA_KERNELBENCH
+// This interrupt handler is for use in the SDMA kernel benchmark only!
+// It depends on GCC's code generation to create the prologue and epilogue
+// for it to be a valid interrupt handler.
+//
+// Vanilla Barrelfish is not designed handle interrupts in the kernel.
+__attribute__((interrupt("IRQ")))
+static void omap_sdma_kernel_irq_handler(void)
+{
+    int irq = gic_get_active_irq();
+    if(irq == OMAP44XX_SDMA_IRQ) {
+        omap_sdma_irq_handler(NULL);
+    }
+    gic_ack_irq(irq);
+}
+#endif
+
+static void omap_sdma_irq_config(omap_sdma_channel_t channel)
+{
+    omap44xx_sdma_dma4_cicr_t dma4_cicr = omap44xx_sdma_dma4_cicr_rd(&devsdma, channel);
+
+    dma4_cicr = omap44xx_sdma_dma4_cicr_super_block_ie_insert(dma4_cicr, 0x0);
+    dma4_cicr = omap44xx_sdma_dma4_cicr_drain_ie_insert(dma4_cicr, 0x0);
+    dma4_cicr = omap44xx_sdma_dma4_cicr_misaligned_err_ie_insert(dma4_cicr, 0x1);
+    dma4_cicr = omap44xx_sdma_dma4_cicr_supervisor_err_ie_insert(dma4_cicr, 0x1);
+    dma4_cicr = omap44xx_sdma_dma4_cicr_trans_err_ie_insert(dma4_cicr, 0x1);
+    dma4_cicr = omap44xx_sdma_dma4_cicr_pkt_ie_insert(dma4_cicr, 0x0);
+    dma4_cicr = omap44xx_sdma_dma4_cicr_block_ie_insert(dma4_cicr, 0x1);
+    dma4_cicr = omap44xx_sdma_dma4_cicr_last_ie_insert(dma4_cicr, 0x0);
+    dma4_cicr = omap44xx_sdma_dma4_cicr_frame_ie_insert(dma4_cicr, 0x0);
+    dma4_cicr = omap44xx_sdma_dma4_cicr_half_ie_insert(dma4_cicr, 0x0);
+    dma4_cicr = omap44xx_sdma_dma4_cicr_drop_ie_insert(dma4_cicr, 0x0);
+
+    omap44xx_sdma_dma4_cicr_wr(&devsdma, channel, dma4_cicr);
+}
+
+/**
+ * \brief Initialzes a channel configuraton struct with its reset values.
+ */
+void omap_sdma_init_channel_conf(struct omap_sdma_channel_conf *conf) {
+    // this function initializes the config struct with default values
+
+    // TRM reset values
+    conf->read_priority  = omap44xx_sdma_PORT_PRIORITY_LOW;
+    conf->write_priority = omap44xx_sdma_PORT_PRIORITY_LOW;
+
+    conf->color_mode = omap44xx_sdma_DISABLE_COLOR_MODE;
+    conf->color = 0x000000;
+
+    // no reset value here, use sane default
+    conf->write_mode = omap44xx_sdma_WRITE_MODE_LAST_NON_POSTED;
+
+    struct omap_sdma_transfer_size *transfer_size = &conf->transfer_size;
+    transfer_size->element_number = 0;
+    transfer_size->frame_number = 0;
+    transfer_size->data_type = omap44xx_sdma_DATA_TYPE_32BIT;
+
+    // default transfer config
+    struct omap_sdma_transfer_conf *src_conf = &conf->src_conf;
+    struct omap_sdma_transfer_conf *dst_conf = &conf->dst_conf;
+    src_conf->start_address = 0;
+    src_conf->addr_mode = omap44xx_sdma_ADDR_MODE_POST_INCR;
+    src_conf->element_index = 0;
+    src_conf->frame_index = 0;
+    src_conf->packed_transfer = omap44xx_sdma_SRC_PACKED_DISABLE;
+    src_conf->burst_mode = omap44xx_sdma_BURST_EN_SINGLE;
+
+    // use the same default values for the destination port
+    memcpy(dst_conf, src_conf, sizeof(struct omap_sdma_transfer_conf));
+
+    conf->enable_link = false;
+    conf->next_channel = 0;
+}
+
+static omap44xx_sdma_dma4_ccr_t omap_sdma_channel_conf_ccr(
+                                omap44xx_sdma_dma4_ccr_t dma4_ccr,
+                                struct omap_sdma_channel_conf *conf)
+{
+    dma4_ccr = omap44xx_sdma_dma4_ccr_src_amode_insert(dma4_ccr, conf->src_conf.addr_mode);
+    dma4_ccr = omap44xx_sdma_dma4_ccr_dst_amode_insert(dma4_ccr, conf->dst_conf.addr_mode);
+
+    assert(
+        (conf->read_priority == omap44xx_sdma_PORT_PRIORITY_LOW) ||
+        (conf->read_priority == omap44xx_sdma_PORT_PRIORITY_HIGH)
+    );
+    dma4_ccr = omap44xx_sdma_dma4_ccr_read_priority_insert(dma4_ccr, conf->read_priority);
+
+    assert(
+        (conf->write_priority == omap44xx_sdma_PORT_PRIORITY_LOW) ||
+        (conf->write_priority == omap44xx_sdma_PORT_PRIORITY_HIGH)
+    );
+    dma4_ccr = omap44xx_sdma_dma4_ccr_write_priority_insert(dma4_ccr, conf->write_priority);
+
+    omap44xx_sdma_transparent_copy_t
+        transparent_copy = (conf->color_mode == omap44xx_sdma_TRANSPARENT_COPY);
+    omap44xx_sdma_const_fill_t
+        const_fill = (conf->color_mode == omap44xx_sdma_CONSTANT_FILL);
+
+    dma4_ccr = omap44xx_sdma_dma4_ccr_transparent_copy_enable_insert(dma4_ccr, transparent_copy);
+    dma4_ccr = omap44xx_sdma_dma4_ccr_const_fill_enable_insert(dma4_ccr, const_fill);
+
+    return dma4_ccr;
+}
+
+
+static omap44xx_sdma_dma4_color_t omap_sdma_channel_conf_color(
+                                omap44xx_sdma_dma4_color_t dma4_color,
+                                struct omap_sdma_channel_conf *conf)
+{
+    // DMA4_COLORi can only be a 24 bit value
+    assert((conf->color & 0xFF000000) == 0);
+
+    return omap44xx_sdma_dma4_color_color_key_pattern_insert(dma4_color, conf->color);
+}
+
+static omap44xx_sdma_dma4_clnk_ctrl_t omap_sdma_channel_conf_clnk_ctrl(
+                                omap44xx_sdma_dma4_clnk_ctrl_t dma4_clnk_ctrl,
+                                struct omap_sdma_channel_conf *conf)
+{
+    // if we enable channel linking, the next channel has to be valid
+    assert(!conf->enable_link || conf->next_channel < OMAP44XX_SDMA_NUM_CHANNEL);
+
+    dma4_clnk_ctrl = omap44xx_sdma_dma4_clnk_ctrl_nextlch_id_insert(
+        dma4_clnk_ctrl, conf->next_channel);
+    dma4_clnk_ctrl = omap44xx_sdma_dma4_clnk_ctrl_enable_lnk_insert(
+        dma4_clnk_ctrl, conf->enable_link ? 1 : 0);
+
+    return dma4_clnk_ctrl;
+}
+
+static omap44xx_sdma_dma4_csdp_t omap_sdma_channel_conf_csdp(
+                                omap44xx_sdma_dma4_csdp_t dma4_csdp,
+                                struct omap_sdma_channel_conf *conf)
+{
+    assert(
+        (conf->write_mode == omap44xx_sdma_WRITE_MODE_NONE_POSTED) ||
+        (conf->write_mode == omap44xx_sdma_WRITE_MODE_ALL_POSTED) ||
+        (conf->write_mode == omap44xx_sdma_WRITE_MODE_LAST_NON_POSTED)
+    );
+
+    dma4_csdp = omap44xx_sdma_dma4_csdp_write_mode_insert(dma4_csdp, conf->write_mode);
+
+    // In memory to memory transfers, the endianness is always little endian
+    dma4_csdp = omap44xx_sdma_dma4_csdp_src_endian_insert(dma4_csdp, omap44xx_sdma_ENDIAN_LITTLE);
+    dma4_csdp = omap44xx_sdma_dma4_csdp_src_endian_lock_insert(dma4_csdp, omap44xx_sdma_ENDIAN_LOCK_ADAPT);
+
+    dma4_csdp = omap44xx_sdma_dma4_csdp_dst_endian_insert(dma4_csdp, omap44xx_sdma_ENDIAN_LITTLE);
+    dma4_csdp = omap44xx_sdma_dma4_csdp_dst_endian_lock_insert(dma4_csdp, omap44xx_sdma_ENDIAN_LOCK_ADAPT);
+
+    struct omap_sdma_transfer_conf *src_conf = &conf->src_conf;
+    dma4_csdp = omap44xx_sdma_dma4_csdp_src_burst_en_insert(dma4_csdp, src_conf->burst_mode);
+    dma4_csdp = omap44xx_sdma_dma4_csdp_src_packed_insert(dma4_csdp,
+        src_conf->packed_transfer
+            ? omap44xx_sdma_SRC_PACKED_ENABLE
+            : omap44xx_sdma_SRC_PACKED_DISABLE);
+
+    struct omap_sdma_transfer_conf *dst_conf = &conf->dst_conf;
+    dma4_csdp = omap44xx_sdma_dma4_csdp_dst_burst_en_insert(dma4_csdp, dst_conf->burst_mode);
+    dma4_csdp = omap44xx_sdma_dma4_csdp_dst_packed_insert(dma4_csdp,
+        dst_conf->packed_transfer
+            ? omap44xx_sdma_DST_PACKED_ENABLE
+            : omap44xx_sdma_DST_PACKED_DISABLE);
+
+    struct omap_sdma_transfer_size *transfer_size = &conf->transfer_size;
+
+    dma4_csdp = omap44xx_sdma_dma4_csdp_data_type_insert(dma4_csdp, transfer_size->data_type);
+
+    return dma4_csdp;
+}
+
+static void inline omap_sdma_channel_conf_assert_transfer_size(
+                                struct omap_sdma_transfer_size *transfer_size)
+{
+    assert(
+        (transfer_size->data_type == omap44xx_sdma_DATA_TYPE_8BIT) ||
+        (transfer_size->data_type == omap44xx_sdma_DATA_TYPE_16BIT) ||
+        (transfer_size->data_type == omap44xx_sdma_DATA_TYPE_32BIT)
+    );
+
+    // element number is 24 bit
+    assert((transfer_size->element_number & 0xFF000000) == 0);
+}
+
+static void inline omap_sdma_channel_conf_assert_transfer_conf(
+                                struct omap_sdma_transfer_conf *transfer_conf)
+{
+
+    // constant addressing mode is not allowed for memory to memory transfers
+    assert(
+        (transfer_conf->addr_mode == omap44xx_sdma_ADDR_MODE_POST_INCR) ||
+        (transfer_conf->addr_mode == omap44xx_sdma_ADDR_MODE_SINGLE_IDX) ||
+        (transfer_conf->addr_mode == omap44xx_sdma_ADDR_MODE_DOUBLE_IDX)
+    );
+
+    assert(
+        (transfer_conf->burst_mode == omap44xx_sdma_BURST_EN_SINGLE) ||
+        (transfer_conf->burst_mode == omap44xx_sdma_BURST_EN_16BYTE) ||
+        (transfer_conf->burst_mode == omap44xx_sdma_BURST_EN_32BYTE) ||
+        (transfer_conf->burst_mode == omap44xx_sdma_BURST_EN_64BYTE)
+    );
+
+    // if post-incrementing with burst transfers is used,
+    // data must be packed to DMA data-port width (TRM Section 16.4.7)
+    assert(
+        (transfer_conf->burst_mode == omap44xx_sdma_BURST_EN_SINGLE)    ||
+        (transfer_conf->addr_mode != omap44xx_sdma_ADDR_MODE_POST_INCR) ||
+        (transfer_conf->packed_transfer == omap44xx_sdma_SRC_PACKED_ENABLE)
+    );
+}
+
+/**
+ * \brief Configure an allocated channel with the given struct.
+ *
+ * \param channel   Channel to configure
+ * \param conf      Complete channel configuration
+ *
+ * This function will write all values of the struct to the SDMA device
+ * registers. Some basic santiy checks (using assert statements) are performed,
+ * but is it the callers responsibility to ensure that the configuration is
+ * sane and valid.
+ */
+void omap_sdma_set_channel_conf(omap_sdma_channel_t channel,
+                                struct omap_sdma_channel_conf *conf)
+{
+    // check transfer config and size parameters
+    assert(channel < OMAP44XX_SDMA_NUM_CHANNEL);
+
+    omap_sdma_channel_conf_assert_transfer_size(&conf->transfer_size);
+
+    omap_sdma_channel_conf_assert_transfer_conf(&conf->src_conf);
+    omap_sdma_channel_conf_assert_transfer_conf(&conf->dst_conf);
+
+
+    // Channel Control Register
+    omap44xx_sdma_dma4_ccr_t dma4_ccr;
+    dma4_ccr = omap44xx_sdma_dma4_ccr_rd(&devsdma, channel);
+    dma4_ccr = omap_sdma_channel_conf_ccr(dma4_ccr, conf);
+    omap44xx_sdma_dma4_ccr_wr(&devsdma, channel, dma4_ccr);
+
+    // Channel Color Register
+    omap44xx_sdma_dma4_color_t dma4_color;
+    dma4_color = omap44xx_sdma_dma4_color_rd(&devsdma, channel);
+    dma4_color = omap_sdma_channel_conf_color(channel, conf);
+    omap44xx_sdma_dma4_color_wr(&devsdma, channel, dma4_color);
+
+    // Channel Link Control Register
+    omap44xx_sdma_dma4_clnk_ctrl_t dma4_clnk_ctrl;
+    dma4_clnk_ctrl = omap44xx_sdma_dma4_clnk_ctrl_rd(&devsdma, channel);
+    dma4_clnk_ctrl = omap_sdma_channel_conf_clnk_ctrl(channel, conf);
+    omap44xx_sdma_dma4_clnk_ctrl_wr(&devsdma, channel, dma4_clnk_ctrl);
+
+    // Channel Source Destination Parameters
+    omap44xx_sdma_dma4_csdp_t dma4_csdp;
+    dma4_csdp = omap44xx_sdma_dma4_csdp_rd(&devsdma, channel);
+    dma4_csdp = omap_sdma_channel_conf_csdp(channel, conf);
+    omap44xx_sdma_dma4_csdp_wr(&devsdma, channel, dma4_csdp);
+
+    // Channel Element Number
+    omap44xx_sdma_dma4_cen_wr(&devsdma, channel, conf->transfer_size.element_number);
+
+    // Channel Frame Number
+    omap44xx_sdma_dma4_cfn_wr(&devsdma, channel, conf->transfer_size.frame_number);
+
+    // Channel Source Element Index
+    omap44xx_sdma_dma4_csei_wr(&devsdma, channel, conf->src_conf.element_index);
+    // Channel Source Frame Index
+    omap44xx_sdma_dma4_csfi_wr(&devsdma, channel, conf->src_conf.frame_index);
+    // Channel Destination Element Index
+    omap44xx_sdma_dma4_cdei_wr(&devsdma, channel, conf->dst_conf.element_index);
+    // Channel Destination Frame Index
+    omap44xx_sdma_dma4_cdfi_wr(&devsdma, channel, conf->dst_conf.frame_index);
+
+    // Channel Source Start Address
+    omap44xx_sdma_dma4_cssa_wr(&devsdma, channel, conf->src_conf.start_address);
+
+    // Channel Source Destination Address
+    omap44xx_sdma_dma4_cdsa_wr(&devsdma, channel, conf->dst_conf.start_address);
+}
+
+/**
+ * \brief Start a SDMA transfer on a pre-configured channel
+ *
+ * \param channel   Pre-configured channel to enable
+ * \param interrupt Indicate an interrupt should be sent on completion or error
+ *
+ * It is the callers responsibility to ensure that the channel was configured
+ * properly before this function is called.
+ */
+void omap_sdma_enable_channel(omap_sdma_channel_t channel, bool interrupt)
+{
+    assert(channel < OMAP44XX_SDMA_NUM_CHANNEL);
+
+    // clear all channel status flags
+    omap44xx_sdma_dma4_csr_wr(&devsdma, channel, 0xFFFFFFFF);
+
+    uint32_t irqenable = omap44xx_sdma_dma4_irqenable_rd(&devsdma, OMAP44XX_SDMA_IRQ_LINE);
+    if (interrupt) {
+        // set channel
+        irqenable |= 1 << channel;
+        // reset irq status for this channel
+        omap44xx_sdma_dma4_irqstatus_line_wr(&devsdma, OMAP44XX_SDMA_IRQ_LINE, 1 << channel);
+    } else {
+        // clear channel
+        irqenable &= ~(1 << channel);
+    }
+    omap44xx_sdma_dma4_irqenable_wr(&devsdma, OMAP44XX_SDMA_IRQ_LINE, irqenable);
+
+    omap44xx_sdma_dma4_ccr_enable_wrf(&devsdma, channel, 1);
+}
+
+/**
+ * \brief Poll a enabled channel for completion of the transfer.
+ */
+errval_t omap_sdma_poll_channel(omap_sdma_channel_t channel)
+{
+    assert(channel < OMAP44XX_SDMA_NUM_CHANNEL);
+
+    // interrupts should be disabled in polling mode
+    assert((omap44xx_sdma_dma4_irqenable_rd(
+                &devsdma, OMAP44XX_SDMA_IRQ_LINE) & 1 << channel) == 0x0
+    );
+
+    for (;;) {
+        omap44xx_sdma_dma4_csr_t csr = omap44xx_sdma_dma4_csr_rd(&devsdma, channel);
+
+        errval_t err = omap_sdma_read_csr(csr);
+
+        if (err_is_fail(err)) return err;
+
+        if(omap44xx_sdma_dma4_csr_block_extract(csr)) {
+            return err;
+        }
+    }
+}
+
+/**
+ * \brief Allocate a SDMA channel. Will return an error if there are no channels
+ * available.
+ */
+errval_t omap_sdma_allocate_channel(omap_sdma_channel_t *channel)
+{
+    assert(channel != NULL);
+
+    for (omap_sdma_channel_t c = 0; c<OMAP44XX_SDMA_NUM_CHANNEL; c++) {
+        if (!allocated_channel[c]) {
+            allocated_channel[c] = true;
+            *channel = c;
+            return SYS_ERR_OK;
+        }
+    }
+    return OMAP_SDMA_ERR_NO_AVAIL_CHANNEL;
+}
+
+/**
+ * \brief Frees a previously allocated SDMA channel.
+ */
+void omap_sdma_free_channel(omap_sdma_channel_t channel)
+{
+    assert(allocated_channel[channel]);
+    allocated_channel[channel] = false;
+}
+
+/**
+ * \brief Initializes this Mackerel wrapper.
+ *
+ * \param dev_base  Virtual address where the SDMA module is mapped to
+ * \param irq_cb    Mandatory interrupt callback
+ *
+ * The caller has map the SDMA hardware registers before calling this function.
+ * The interrupt callback is executed for every time an SDMA channel triggered
+ * an interrupt. The source channel and the the reason of the interrupt are
+ * passed to the callback as an the argument.
+ */
+errval_t omap_sdma_init(mackerel_addr_t dev_base, omap_sdma_irq_handler_t irq_cb)
+{
+    // init global variables
+    STATIC_ASSERT_SIZEOF(bool, 1);
+    memset(allocated_channel, false, OMAP44XX_SDMA_NUM_CHANNEL);
+
+    omap44xx_sdma_initialize(&devsdma, dev_base);
+
+    // check if we can read the revision
+    assert(omap44xx_sdma_dma4_revision_rd(&devsdma) == 0x10900);
+
+    assert(irq_cb != NULL);
+    irq_callback = irq_cb;
+
+    errval_t err = SYS_ERR_OK;
+#ifdef OMAP_SDMA_KERNELBENCH
+    // in kernelspace, we hijack the normal interrupt handler by overwriting
+    // the global interrupt handler table entry.
+
+    // this is obvioulsy a hack (!) and ONLY for use in the sdma kernel
+    // benchmark which will not allow the kernel to continue after this
+    uintptr_t *irq_handler_entry = (uintptr_t*)
+                        (ETABLE_ADDR + JUMP_TABLE_OFFSET + ARM_EVECTOR_IRQ);
+    *irq_handler_entry = (uintptr_t) omap_sdma_kernel_irq_handler;
+
+    // enable the SDMA interrupt in the global interrupt controller
+    gic_enable_interrupt(OMAP44XX_SDMA_IRQ, GIC_IRQ_CPU_TRG_ALL, 0,
+                            GIC_IRQ_EDGE_TRIGGERED, GIC_IRQ_N_TO_N);
+#else
+    // in userspace, register normal interrupt handler
+    err = inthandler_setup_arm(omap_sdma_irq_handler, NULL, OMAP44XX_SDMA_IRQ);
+#endif
+
+    // set fifo depth to maximum burst size
+    omap44xx_sdma_dma4_gcr_max_channel_fifo_depth_wrf(&devsdma, 64);
+
+    // configure error and interrupt handling of the device
+    for(omap_sdma_channel_t channel = 0;
+        channel < OMAP44XX_SDMA_NUM_CHANNEL;
+        channel++)
+    {
+        omap_sdma_irq_config(channel);
+    }
+
+    return err;
+}
diff --git a/usr/drivers/omap44xx/sdma/omap_sdma.h b/usr/drivers/omap44xx/sdma/omap_sdma.h
new file mode 100644 (file)
index 0000000..fcb4600
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2014, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, CAB F.78, Universitaetstr 6, CH-8092 Zurich.
+ */
+
+#ifndef OMAP44XX_SDMA_H_
+#define OMAP44XX_SDMA_H_
+
+#include <stdbool.h>
+#include <dev/omap/omap44xx_sdma_dev.h>
+
+typedef uint8_t omap_sdma_channel_t;
+
+typedef void (*omap_sdma_irq_handler_t)(omap_sdma_channel_t, errval_t);
+
+typedef uint8_t omap44xx_sdma_color_mode_t;
+#define omap44xx_sdma_DISABLE_COLOR_MODE ((omap44xx_sdma_color_mode_t)0x0)
+#define omap44xx_sdma_TRANSPARENT_COPY   ((omap44xx_sdma_color_mode_t)0x1)
+#define omap44xx_sdma_CONSTANT_FILL      ((omap44xx_sdma_color_mode_t)0x2)
+
+#define OMAP44XX_SDMA_IRQ_LINE (0)
+#define OMAP44XX_SDMA_NUM_CHANNEL (32u)
+#define OMAP44XX_SDMA_IRQ (32 + 12 + OMAP44XX_SDMA_IRQ_LINE) // MA_IRQ_12
+
+#define OMAP44XX_SDMA_MAX_FN    0x0000FFFF
+#define OMAP44XX_SDMA_MAX_EN    0x00FFFFFF
+
+#define OMAP44XX_SDMA_MAX_FN_BITS    15
+#define OMAP44XX_SDMA_MAX_EN_BITS    23
+
+#ifdef SDMA_DEBUG
+#define SDMA_PRINT(...) do{ printf(__VA_ARGS__ ); } while( false )
+#else
+#define SDMA_PRINT(...) do{ } while ( false )
+#endif
+
+struct omap_sdma_transfer_conf {
+    lpaddr_t start_address;
+    omap44xx_sdma_addr_mode_t addr_mode;
+    int16_t element_index;
+    int32_t frame_index;
+    bool packed_transfer;
+    omap44xx_sdma_burst_en_t burst_mode;
+};
+
+struct omap_sdma_transfer_size {
+    uint32_t element_number;
+    uint16_t frame_number;
+    omap44xx_sdma_data_type_t data_type;
+};
+
+/// The values in this struct are directly written into the hardware registers.
+/// While there are some basic sanity checks, it is the users responsibility to
+/// ensure that writes to these physical addresses are allowed.
+///
+/// The naming follows more or less Chapter 16 of the OMAP4466 TRM (Rev. AA)
+
+struct omap_sdma_channel_conf {
+    omap44xx_sdma_port_priority_t read_priority;
+    omap44xx_sdma_port_priority_t write_priority;
+
+    omap44xx_sdma_color_mode_t color_mode;
+    uint32_t color;
+
+    omap44xx_sdma_write_mode_t write_mode;
+
+    struct omap_sdma_transfer_size transfer_size;
+    struct omap_sdma_transfer_conf src_conf;
+    struct omap_sdma_transfer_conf dst_conf;
+
+    bool enable_link;
+    omap_sdma_channel_t next_channel;
+};
+
+errval_t omap_sdma_init(mackerel_addr_t dev_base, omap_sdma_irq_handler_t);
+
+errval_t omap_sdma_allocate_channel(omap_sdma_channel_t *channel);
+void omap_sdma_free_channel(omap_sdma_channel_t channel);
+
+void omap_sdma_init_channel_conf(struct omap_sdma_channel_conf *conf);
+
+void omap_sdma_set_channel_conf(omap_sdma_channel_t channel,
+    struct omap_sdma_channel_conf *conf);
+
+void omap_sdma_enable_channel(omap_sdma_channel_t channel, bool interrupt);
+
+errval_t omap_sdma_poll_channel(omap_sdma_channel_t channel);
+
+#endif // OMAP44XX_SDMA_H_
diff --git a/usr/drivers/omap44xx/sdma/sdma.h b/usr/drivers/omap44xx/sdma/sdma.h
new file mode 100644 (file)
index 0000000..2248d4a
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2014, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, CAB F.78, Universitaetstr 6, CH-8092 Zurich.
+ */
+
+#ifndef SDMA_H_
+#define SDMA_H_
+
+#include <if/omap_sdma_defs.h>
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+void start_service(void);
+
+errval_t mem_copy(struct capref dst_cap, struct capref src_cap);
+errval_t mem_fill(struct capref dst_cap, uint8_t color);
+
+errval_t mem_copy_2d(omap_sdma_addr_2d_t dst, omap_sdma_addr_2d_t src,
+                omap_sdma_count_2d_t count, bool transparent, uint32_t color);
+errval_t mem_fill_2d(omap_sdma_addr_2d_t dst, omap_sdma_count_2d_t count, uint32_t color);
+#endif
diff --git a/usr/drivers/omap44xx/sdma/service.c b/usr/drivers/omap44xx/sdma/service.c
new file mode 100644 (file)
index 0000000..ae9420c
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2014, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, CAB F.78, Universitaetstr 6, CH-8092 Zurich.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <barrelfish/barrelfish.h>
+#include <barrelfish/nameservice_client.h>
+
+#include <if/omap_sdma_defs.h>
+#include <if/omap_sdma_thc.h>
+#include <thc/thc.h>
+
+#include "sdma.h"
+
+static void run_service(struct omap_sdma_thc_service_binding_t *sv)
+{
+    omap_sdma_service_msg_t msg;
+    bool loop = true;
+
+    // this is the bitmap of messages we are interested in receiving
+    struct omap_sdma_service_selector selector = {
+        .mem_copy = 1, .mem_copy_2d = 1,
+        .mem_fill = 1, .mem_fill_2d = 1,
+    };
+
+    while (loop) {
+        // receive any message
+        sv->recv_any(sv, &msg, selector);
+
+        errval_t reterr = SYS_ERR_OK;
+
+        // dispatch it
+        switch(msg.msg) {
+        case omap_sdma_mem_copy:
+            reterr = mem_copy(
+                msg.args.mem_copy.in.dst,
+                msg.args.mem_copy.in.src);
+            sv->send.mem_copy(sv, reterr);
+            break;
+        case omap_sdma_mem_fill:
+            reterr = mem_fill(
+                msg.args.mem_fill.in.dst,
+                msg.args.mem_fill.in.color);
+            sv->send.mem_fill(sv, reterr);
+            break;
+        case omap_sdma_mem_copy_2d:
+            reterr = mem_copy_2d(
+                msg.args.mem_copy_2d.in.dst,
+                msg.args.mem_copy_2d.in.src,
+                msg.args.mem_copy_2d.in.count,
+                msg.args.mem_copy_2d.in.transparent,
+                msg.args.mem_copy_2d.in.color);
+            sv->send.mem_copy_2d(sv, reterr);
+            break;
+        case omap_sdma_mem_fill_2d:
+            reterr = mem_fill_2d(
+                msg.args.mem_fill_2d.in.dst,
+                msg.args.mem_fill_2d.in.count,
+                msg.args.mem_fill_2d.in.color);
+            sv->send.mem_fill_2d(sv, reterr);
+            break;
+        default:
+            debug_printf("unexpected message: %d\n", msg.msg);
+            loop = false;
+            break;
+        }
+    }
+
+    free(sv);
+}
+
+void start_service(void)
+{
+    errval_t err;
+
+    struct omap_sdma_thc_export_info e_info;
+    struct omap_sdma_thc_service_binding_t *sv;
+    struct omap_sdma_binding *b;
+    iref_t iref;
+
+    err = omap_sdma_thc_export(&e_info, "sdma",
+                             get_default_waitset(),
+                             IDC_EXPORT_FLAGS_DEFAULT,
+                             &iref);
+    if (err_is_fail(err)) {
+        USER_PANIC_ERR(err, "thc export failed");
+    }
+
+    DO_FINISH({
+        while(true) {
+            err = omap_sdma_thc_accept(&e_info, &b);
+            if (err_is_fail(err)) {
+                USER_PANIC_ERR(err, "thc accept failed");
+            }
+
+            sv = malloc(sizeof(struct omap_sdma_thc_service_binding_t));
+            assert(sv != NULL);
+
+            err = omap_sdma_thc_init_service(sv, b, b);
+            if (err_is_fail(err)) {
+                USER_PANIC_ERR(err, "thc init failed");
+            }
+
+            ASYNC({run_service(sv);});
+        }
+    });
+}