2 * Copyright (c) 2014, ETH Zurich.
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, CAB F.78, Universitaetstr 6, CH-8092 Zurich.
10 #include <barrelfish/barrelfish.h>
12 #ifdef OMAP_SDMA_KERNELBENCH
13 #include <arch/armv7/gic.h>
15 #include <exceptions.h>
18 #include <barrelfish/waitset.h>
19 #include <barrelfish/inthandler.h>
20 #include <driverkit/driverkit.h>
25 #include "omap_sdma.h"
26 #include <maps/omap44xx_map.h>
28 static inline errval_t omap_sdma_read_csr(omap44xx_sdma_dma4_csr_t csr)
30 if (omap44xx_sdma_dma4_csr_misaligned_adrs_err_extract(csr)) {
31 return OMAP_SDMA_ERR_MISALIGNED_ADDRESS;
32 } else if (omap44xx_sdma_dma4_csr_supervisor_err_extract(csr)) {
33 return OMAP_SDMA_ERR_SUPERVISOR;
34 } else if (omap44xx_sdma_dma4_csr_trans_err_extract(csr)) {
35 return OMAP_SDMA_ERR_TRANSACTION;
41 static void omap_sdma_irq_handler(void *arg)
43 struct sdma_driver_state* st = (struct sdma_driver_state*) arg;
44 uint32_t irqstatus = omap44xx_sdma_dma4_irqstatus_line_rd(&st->devsdma, OMAP44XX_SDMA_IRQ_LINE);
46 for (omap_sdma_channel_t channel=0; channel<OMAP44XX_SDMA_NUM_CHANNEL; channel++) {
47 bool active = (irqstatus >> channel) & 0x1;
50 SDMA_PRINT("interrupt on channel %u\n", channel);
52 // read out status flags
53 omap44xx_sdma_dma4_csr_t csr = omap44xx_sdma_dma4_csr_rd(&st->devsdma, channel);
56 errval_t err = omap_sdma_read_csr(csr);
59 // no error found, check for "End of Block" event
60 if(omap44xx_sdma_dma4_csr_block_extract(csr)) {
61 st->irq_callback(st, channel, err);
64 // OMAP4460 Multimedia Device Silicon Errata, Revision A:
65 // 1.7 sDMA Channel Is Not Disabled After A Transaction Error
66 if (err_no(err) == OMAP_SDMA_ERR_TRANSACTION) {
67 // Workaround: disable channel by software
68 omap44xx_sdma_dma4_ccr_enable_wrf(&st->devsdma, channel, 0);
71 st->irq_callback(st, channel, err);
74 // clear all read status flags
75 omap44xx_sdma_dma4_csr_wr(&st->devsdma, channel, csr);
78 SDMA_PRINT("interrupt finished\n");
80 // clear all set status bits
81 omap44xx_sdma_dma4_irqstatus_line_wr(&st->devsdma, OMAP44XX_SDMA_IRQ_LINE, irqstatus);
85 #ifdef OMAP_SDMA_KERNELBENCH
86 // This interrupt handler is for use in the SDMA kernel benchmark only!
87 // It depends on GCC's code generation to create the prologue and epilogue
88 // for it to be a valid interrupt handler.
90 // Vanilla Barrelfish is not designed handle interrupts in the kernel.
91 __attribute__((interrupt("IRQ")))
92 static void omap_sdma_kernel_irq_handler(void)
94 int irq = gic_get_active_irq();
95 if(irq == OMAP44XX_SDMA_IRQ) {
96 omap_sdma_irq_handler(NULL);
102 static void omap_sdma_irq_config(struct sdma_driver_state* st, omap_sdma_channel_t channel)
104 omap44xx_sdma_dma4_cicr_t dma4_cicr = omap44xx_sdma_dma4_cicr_rd(&st->devsdma, channel);
106 dma4_cicr = omap44xx_sdma_dma4_cicr_super_block_ie_insert(dma4_cicr, 0x0);
107 dma4_cicr = omap44xx_sdma_dma4_cicr_drain_ie_insert(dma4_cicr, 0x0);
108 dma4_cicr = omap44xx_sdma_dma4_cicr_misaligned_err_ie_insert(dma4_cicr, 0x1);
109 dma4_cicr = omap44xx_sdma_dma4_cicr_supervisor_err_ie_insert(dma4_cicr, 0x1);
110 dma4_cicr = omap44xx_sdma_dma4_cicr_trans_err_ie_insert(dma4_cicr, 0x1);
111 dma4_cicr = omap44xx_sdma_dma4_cicr_pkt_ie_insert(dma4_cicr, 0x0);
112 dma4_cicr = omap44xx_sdma_dma4_cicr_block_ie_insert(dma4_cicr, 0x1);
113 dma4_cicr = omap44xx_sdma_dma4_cicr_last_ie_insert(dma4_cicr, 0x0);
114 dma4_cicr = omap44xx_sdma_dma4_cicr_frame_ie_insert(dma4_cicr, 0x0);
115 dma4_cicr = omap44xx_sdma_dma4_cicr_half_ie_insert(dma4_cicr, 0x0);
116 dma4_cicr = omap44xx_sdma_dma4_cicr_drop_ie_insert(dma4_cicr, 0x0);
118 omap44xx_sdma_dma4_cicr_wr(&st->devsdma, channel, dma4_cicr);
122 * \brief Initialzes a channel configuraton struct with its reset values.
124 void omap_sdma_init_channel_conf(struct omap_sdma_channel_conf *conf) {
125 // this function initializes the config struct with default values
128 conf->read_priority = omap44xx_sdma_PORT_PRIORITY_LOW;
129 conf->write_priority = omap44xx_sdma_PORT_PRIORITY_LOW;
131 conf->color_mode = omap44xx_sdma_DISABLE_COLOR_MODE;
132 conf->color = 0x000000;
134 // no reset value here, use sane default
135 conf->write_mode = omap44xx_sdma_WRITE_MODE_LAST_NON_POSTED;
137 struct omap_sdma_transfer_size *transfer_size = &conf->transfer_size;
138 transfer_size->element_number = 0;
139 transfer_size->frame_number = 0;
140 transfer_size->data_type = omap44xx_sdma_DATA_TYPE_32BIT;
142 // default transfer config
143 struct omap_sdma_transfer_conf *src_conf = &conf->src_conf;
144 struct omap_sdma_transfer_conf *dst_conf = &conf->dst_conf;
145 src_conf->start_address = 0;
146 src_conf->addr_mode = omap44xx_sdma_ADDR_MODE_POST_INCR;
147 src_conf->element_index = 0;
148 src_conf->frame_index = 0;
149 src_conf->packed_transfer = omap44xx_sdma_SRC_PACKED_DISABLE;
150 src_conf->burst_mode = omap44xx_sdma_BURST_EN_SINGLE;
152 // use the same default values for the destination port
153 memcpy(dst_conf, src_conf, sizeof(struct omap_sdma_transfer_conf));
155 conf->enable_link = false;
156 conf->next_channel = 0;
159 static omap44xx_sdma_dma4_ccr_t omap_sdma_channel_conf_ccr(
160 omap44xx_sdma_dma4_ccr_t dma4_ccr,
161 struct omap_sdma_channel_conf *conf)
163 dma4_ccr = omap44xx_sdma_dma4_ccr_src_amode_insert(dma4_ccr, conf->src_conf.addr_mode);
164 dma4_ccr = omap44xx_sdma_dma4_ccr_dst_amode_insert(dma4_ccr, conf->dst_conf.addr_mode);
167 (conf->read_priority == omap44xx_sdma_PORT_PRIORITY_LOW) ||
168 (conf->read_priority == omap44xx_sdma_PORT_PRIORITY_HIGH)
170 dma4_ccr = omap44xx_sdma_dma4_ccr_read_priority_insert(dma4_ccr, conf->read_priority);
173 (conf->write_priority == omap44xx_sdma_PORT_PRIORITY_LOW) ||
174 (conf->write_priority == omap44xx_sdma_PORT_PRIORITY_HIGH)
176 dma4_ccr = omap44xx_sdma_dma4_ccr_write_priority_insert(dma4_ccr, conf->write_priority);
178 omap44xx_sdma_transparent_copy_t
179 transparent_copy = (conf->color_mode == omap44xx_sdma_TRANSPARENT_COPY);
180 omap44xx_sdma_const_fill_t
181 const_fill = (conf->color_mode == omap44xx_sdma_CONSTANT_FILL);
183 dma4_ccr = omap44xx_sdma_dma4_ccr_transparent_copy_enable_insert(dma4_ccr, transparent_copy);
184 dma4_ccr = omap44xx_sdma_dma4_ccr_const_fill_enable_insert(dma4_ccr, const_fill);
190 static omap44xx_sdma_dma4_color_t omap_sdma_channel_conf_color(
191 omap44xx_sdma_dma4_color_t dma4_color,
192 struct omap_sdma_channel_conf *conf)
194 // DMA4_COLORi can only be a 24 bit value
195 assert((conf->color & 0xFF000000) == 0);
197 return omap44xx_sdma_dma4_color_color_key_pattern_insert(dma4_color, conf->color);
200 static omap44xx_sdma_dma4_clnk_ctrl_t omap_sdma_channel_conf_clnk_ctrl(
201 omap44xx_sdma_dma4_clnk_ctrl_t dma4_clnk_ctrl,
202 struct omap_sdma_channel_conf *conf)
204 // if we enable channel linking, the next channel has to be valid
205 assert(!conf->enable_link || conf->next_channel < OMAP44XX_SDMA_NUM_CHANNEL);
207 dma4_clnk_ctrl = omap44xx_sdma_dma4_clnk_ctrl_nextlch_id_insert(
208 dma4_clnk_ctrl, conf->next_channel);
209 dma4_clnk_ctrl = omap44xx_sdma_dma4_clnk_ctrl_enable_lnk_insert(
210 dma4_clnk_ctrl, conf->enable_link ? 1 : 0);
212 return dma4_clnk_ctrl;
215 static omap44xx_sdma_dma4_csdp_t omap_sdma_channel_conf_csdp(
216 omap44xx_sdma_dma4_csdp_t dma4_csdp,
217 struct omap_sdma_channel_conf *conf)
220 (conf->write_mode == omap44xx_sdma_WRITE_MODE_NONE_POSTED) ||
221 (conf->write_mode == omap44xx_sdma_WRITE_MODE_ALL_POSTED) ||
222 (conf->write_mode == omap44xx_sdma_WRITE_MODE_LAST_NON_POSTED)
225 dma4_csdp = omap44xx_sdma_dma4_csdp_write_mode_insert(dma4_csdp, conf->write_mode);
227 // In memory to memory transfers, the endianness is always little endian
228 dma4_csdp = omap44xx_sdma_dma4_csdp_src_endian_insert(dma4_csdp, omap44xx_sdma_ENDIAN_LITTLE);
229 dma4_csdp = omap44xx_sdma_dma4_csdp_src_endian_lock_insert(dma4_csdp, omap44xx_sdma_ENDIAN_LOCK_ADAPT);
231 dma4_csdp = omap44xx_sdma_dma4_csdp_dst_endian_insert(dma4_csdp, omap44xx_sdma_ENDIAN_LITTLE);
232 dma4_csdp = omap44xx_sdma_dma4_csdp_dst_endian_lock_insert(dma4_csdp, omap44xx_sdma_ENDIAN_LOCK_ADAPT);
234 struct omap_sdma_transfer_conf *src_conf = &conf->src_conf;
235 dma4_csdp = omap44xx_sdma_dma4_csdp_src_burst_en_insert(dma4_csdp, src_conf->burst_mode);
236 dma4_csdp = omap44xx_sdma_dma4_csdp_src_packed_insert(dma4_csdp,
237 src_conf->packed_transfer
238 ? omap44xx_sdma_SRC_PACKED_ENABLE
239 : omap44xx_sdma_SRC_PACKED_DISABLE);
241 struct omap_sdma_transfer_conf *dst_conf = &conf->dst_conf;
242 dma4_csdp = omap44xx_sdma_dma4_csdp_dst_burst_en_insert(dma4_csdp, dst_conf->burst_mode);
243 dma4_csdp = omap44xx_sdma_dma4_csdp_dst_packed_insert(dma4_csdp,
244 dst_conf->packed_transfer
245 ? omap44xx_sdma_DST_PACKED_ENABLE
246 : omap44xx_sdma_DST_PACKED_DISABLE);
248 struct omap_sdma_transfer_size *transfer_size = &conf->transfer_size;
250 dma4_csdp = omap44xx_sdma_dma4_csdp_data_type_insert(dma4_csdp, transfer_size->data_type);
255 static void inline omap_sdma_channel_conf_assert_transfer_size(
256 struct omap_sdma_transfer_size *transfer_size)
259 (transfer_size->data_type == omap44xx_sdma_DATA_TYPE_8BIT) ||
260 (transfer_size->data_type == omap44xx_sdma_DATA_TYPE_16BIT) ||
261 (transfer_size->data_type == omap44xx_sdma_DATA_TYPE_32BIT)
264 // element number is 24 bit
265 assert((transfer_size->element_number & 0xFF000000) == 0);
268 static void inline omap_sdma_channel_conf_assert_transfer_conf(
269 struct omap_sdma_transfer_conf *transfer_conf)
272 // constant addressing mode is not allowed for memory to memory transfers
274 (transfer_conf->addr_mode == omap44xx_sdma_ADDR_MODE_POST_INCR) ||
275 (transfer_conf->addr_mode == omap44xx_sdma_ADDR_MODE_SINGLE_IDX) ||
276 (transfer_conf->addr_mode == omap44xx_sdma_ADDR_MODE_DOUBLE_IDX)
280 (transfer_conf->burst_mode == omap44xx_sdma_BURST_EN_SINGLE) ||
281 (transfer_conf->burst_mode == omap44xx_sdma_BURST_EN_16BYTE) ||
282 (transfer_conf->burst_mode == omap44xx_sdma_BURST_EN_32BYTE) ||
283 (transfer_conf->burst_mode == omap44xx_sdma_BURST_EN_64BYTE)
286 // if post-incrementing with burst transfers is used,
287 // data must be packed to DMA data-port width (TRM Section 16.4.7)
289 (transfer_conf->burst_mode == omap44xx_sdma_BURST_EN_SINGLE) ||
290 (transfer_conf->addr_mode != omap44xx_sdma_ADDR_MODE_POST_INCR) ||
291 (transfer_conf->packed_transfer == omap44xx_sdma_SRC_PACKED_ENABLE)
296 * \brief Configure an allocated channel with the given struct.
298 * \param channel Channel to configure
299 * \param conf Complete channel configuration
301 * This function will write all values of the struct to the SDMA device
302 * registers. Some basic santiy checks (using assert statements) are performed,
303 * but is it the callers responsibility to ensure that the configuration is
306 void omap_sdma_set_channel_conf(struct sdma_driver_state* st, omap_sdma_channel_t channel,
307 struct omap_sdma_channel_conf *conf)
309 // check transfer config and size parameters
310 assert(channel < OMAP44XX_SDMA_NUM_CHANNEL);
312 omap_sdma_channel_conf_assert_transfer_size(&conf->transfer_size);
314 omap_sdma_channel_conf_assert_transfer_conf(&conf->src_conf);
315 omap_sdma_channel_conf_assert_transfer_conf(&conf->dst_conf);
318 // Channel Control Register
319 omap44xx_sdma_dma4_ccr_t dma4_ccr;
320 dma4_ccr = omap44xx_sdma_dma4_ccr_rd(&st->devsdma, channel);
321 dma4_ccr = omap_sdma_channel_conf_ccr(dma4_ccr, conf);
322 omap44xx_sdma_dma4_ccr_wr(&st->devsdma, channel, dma4_ccr);
324 // Channel Color Register
325 omap44xx_sdma_dma4_color_t dma4_color;
326 dma4_color = omap44xx_sdma_dma4_color_rd(&st->devsdma, channel);
327 dma4_color = omap_sdma_channel_conf_color(channel, conf);
328 omap44xx_sdma_dma4_color_wr(&st->devsdma, channel, dma4_color);
330 // Channel Link Control Register
331 omap44xx_sdma_dma4_clnk_ctrl_t dma4_clnk_ctrl;
332 dma4_clnk_ctrl = omap44xx_sdma_dma4_clnk_ctrl_rd(&st->devsdma, channel);
333 dma4_clnk_ctrl = omap_sdma_channel_conf_clnk_ctrl(channel, conf);
334 omap44xx_sdma_dma4_clnk_ctrl_wr(&st->devsdma, channel, dma4_clnk_ctrl);
336 // Channel Source Destination Parameters
337 omap44xx_sdma_dma4_csdp_t dma4_csdp;
338 dma4_csdp = omap44xx_sdma_dma4_csdp_rd(&st->devsdma, channel);
339 dma4_csdp = omap_sdma_channel_conf_csdp(channel, conf);
340 omap44xx_sdma_dma4_csdp_wr(&st->devsdma, channel, dma4_csdp);
342 // Channel Element Number
343 omap44xx_sdma_dma4_cen_wr(&st->devsdma, channel, conf->transfer_size.element_number);
345 // Channel Frame Number
346 omap44xx_sdma_dma4_cfn_wr(&st->devsdma, channel, conf->transfer_size.frame_number);
348 // Channel Source Element Index
349 omap44xx_sdma_dma4_csei_wr(&st->devsdma, channel, conf->src_conf.element_index);
350 // Channel Source Frame Index
351 omap44xx_sdma_dma4_csfi_wr(&st->devsdma, channel, conf->src_conf.frame_index);
352 // Channel Destination Element Index
353 omap44xx_sdma_dma4_cdei_wr(&st->devsdma, channel, conf->dst_conf.element_index);
354 // Channel Destination Frame Index
355 omap44xx_sdma_dma4_cdfi_wr(&st->devsdma, channel, conf->dst_conf.frame_index);
357 // Channel Source Start Address
358 omap44xx_sdma_dma4_cssa_wr(&st->devsdma, channel, conf->src_conf.start_address);
360 // Channel Source Destination Address
361 omap44xx_sdma_dma4_cdsa_wr(&st->devsdma, channel, conf->dst_conf.start_address);
365 * \brief Start a SDMA transfer on a pre-configured channel
367 * \param channel Pre-configured channel to enable
368 * \param interrupt Indicate an interrupt should be sent on completion or error
370 * It is the callers responsibility to ensure that the channel was configured
371 * properly before this function is called.
373 void omap_sdma_enable_channel(struct sdma_driver_state* st, omap_sdma_channel_t channel, bool interrupt)
375 assert(channel < OMAP44XX_SDMA_NUM_CHANNEL);
377 // clear all channel status flags
378 omap44xx_sdma_dma4_csr_wr(&st->devsdma, channel, 0xFFFFFFFF);
380 uint32_t irqenable = omap44xx_sdma_dma4_irqenable_rd(&st->devsdma, OMAP44XX_SDMA_IRQ_LINE);
383 irqenable |= 1 << channel;
384 // reset irq status for this channel
385 omap44xx_sdma_dma4_irqstatus_line_wr(&st->devsdma, OMAP44XX_SDMA_IRQ_LINE, 1 << channel);
388 irqenable &= ~(1 << channel);
390 omap44xx_sdma_dma4_irqenable_wr(&st->devsdma, OMAP44XX_SDMA_IRQ_LINE, irqenable);
392 omap44xx_sdma_dma4_ccr_enable_wrf(&st->devsdma, channel, 1);
396 * \brief Poll a enabled channel for completion of the transfer.
398 errval_t omap_sdma_poll_channel(struct sdma_driver_state* st, omap_sdma_channel_t channel)
400 assert(channel < OMAP44XX_SDMA_NUM_CHANNEL);
402 // interrupts should be disabled in polling mode
403 assert((omap44xx_sdma_dma4_irqenable_rd(
404 &st->devsdma, OMAP44XX_SDMA_IRQ_LINE) & 1 << channel) == 0x0
408 omap44xx_sdma_dma4_csr_t csr = omap44xx_sdma_dma4_csr_rd(&st->devsdma, channel);
410 errval_t err = omap_sdma_read_csr(csr);
412 if (err_is_fail(err)) return err;
414 if(omap44xx_sdma_dma4_csr_block_extract(csr)) {
421 * \brief Allocate a SDMA channel. Will return an error if there are no channels
424 errval_t omap_sdma_allocate_channel(struct sdma_driver_state* st, omap_sdma_channel_t *channel)
426 assert(channel != NULL);
428 for (omap_sdma_channel_t c = 0; c<OMAP44XX_SDMA_NUM_CHANNEL; c++) {
429 if (!st->allocated_channel[c]) {
430 st->allocated_channel[c] = true;
435 return OMAP_SDMA_ERR_NO_AVAIL_CHANNEL;
439 * \brief Frees a previously allocated SDMA channel.
441 void omap_sdma_free_channel(struct sdma_driver_state* st, omap_sdma_channel_t channel)
443 assert(st->allocated_channel[channel]);
444 st->allocated_channel[channel] = false;
448 * \brief Initializes this Mackerel wrapper.
450 * \param dev_base Virtual address where the SDMA module is mapped to
451 * \param irq_cb Mandatory interrupt callback
453 * The caller has map the SDMA hardware registers before calling this function.
454 * The interrupt callback is executed for every time an SDMA channel triggered
455 * an interrupt. The source channel and the the reason of the interrupt are
456 * passed to the callback as an the argument.
458 errval_t omap_sdma_init(struct sdma_driver_state* st, mackerel_addr_t dev_base, omap_sdma_irq_handler_t irq_cb)
460 // init global variables
461 STATIC_ASSERT_SIZEOF(bool, 1);
462 memset(st->allocated_channel, false, OMAP44XX_SDMA_NUM_CHANNEL);
464 omap44xx_sdma_initialize(&st->devsdma, dev_base);
466 // check if we can read the revision
467 assert(omap44xx_sdma_dma4_revision_rd(&st->devsdma) == 0x10900);
469 assert(irq_cb != NULL);
470 st->irq_callback = irq_cb;
472 errval_t err = SYS_ERR_OK;
473 #ifdef OMAP_SDMA_KERNELBENCH
474 // in kernelspace, we hijack the normal interrupt handler by overwriting
475 // the global interrupt handler table entry.
477 // this is obvioulsy a hack (!) and ONLY for use in the sdma kernel
478 // benchmark which will not allow the kernel to continue after this
479 uintptr_t *irq_handler_entry = (uintptr_t*)
480 (ETABLE_ADDR + JUMP_TABLE_OFFSET + ARM_EVECTOR_IRQ);
481 *irq_handler_entry = (uintptr_t) omap_sdma_kernel_irq_handler;
483 // enable the SDMA interrupt in the global interrupt controller
484 gic_enable_interrupt(OMAP44XX_SDMA_IRQ, GIC_IRQ_CPU_TRG_ALL, 0,
485 GIC_IRQ_EDGE_TRIGGERED, GIC_IRQ_N_TO_N);
487 // in userspace, register normal interrupt handler
488 err = inthandler_setup_arm(omap_sdma_irq_handler, st, OMAP44XX_SDMA_IRQ);
491 // set fifo depth to maximum burst size
492 omap44xx_sdma_dma4_gcr_max_channel_fifo_depth_wrf(&st->devsdma, 64);
494 // configure error and interrupt handling of the device
495 for(omap_sdma_channel_t channel = 0;
496 channel < OMAP44XX_SDMA_NUM_CHANNEL;
499 omap_sdma_irq_config(st, channel);