diff options
Diffstat (limited to 'drivers/dma/imx-sdma.c')
-rw-r--r-- | drivers/dma/imx-sdma.c | 264 |
1 files changed, 126 insertions, 138 deletions
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 0834323a0599..b6d1455fa936 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -230,7 +230,7 @@ struct sdma_engine; * struct sdma_channel - housekeeping for a SDMA channel * * @sdma pointer to the SDMA engine for this channel - * @channel the channel number, matches dmaengine chan_id + * @channel the channel number, matches dmaengine chan_id + 1 * @direction transfer type. Needed for setting SDMA script * @peripheral_type Peripheral type. Needed for setting SDMA script * @event_id0 aka dma request line @@ -273,50 +273,6 @@ struct sdma_channel { #define MXC_SDMA_MIN_PRIORITY 1 #define MXC_SDMA_MAX_PRIORITY 7 -/** - * struct sdma_script_start_addrs - SDMA script start pointers - * - * start addresses of the different functions in the physical - * address space of the SDMA engine. - */ -struct sdma_script_start_addrs { - u32 ap_2_ap_addr; - u32 ap_2_bp_addr; - u32 ap_2_ap_fixed_addr; - u32 bp_2_ap_addr; - u32 loopback_on_dsp_side_addr; - u32 mcu_interrupt_only_addr; - u32 firi_2_per_addr; - u32 firi_2_mcu_addr; - u32 per_2_firi_addr; - u32 mcu_2_firi_addr; - u32 uart_2_per_addr; - u32 uart_2_mcu_addr; - u32 per_2_app_addr; - u32 mcu_2_app_addr; - u32 per_2_per_addr; - u32 uartsh_2_per_addr; - u32 uartsh_2_mcu_addr; - u32 per_2_shp_addr; - u32 mcu_2_shp_addr; - u32 ata_2_mcu_addr; - u32 mcu_2_ata_addr; - u32 app_2_per_addr; - u32 app_2_mcu_addr; - u32 shp_2_per_addr; - u32 shp_2_mcu_addr; - u32 mshc_2_mcu_addr; - u32 mcu_2_mshc_addr; - u32 spdif_2_mcu_addr; - u32 mcu_2_spdif_addr; - u32 asrc_2_mcu_addr; - u32 ext_mem_2_ipu_addr; - u32 descrambler_addr; - u32 dptc_dvfs_addr; - u32 utra_addr; - u32 ram_code_start_addr; -}; - #define SDMA_FIRMWARE_MAGIC 0x414d4453 /** @@ -345,6 +301,7 @@ struct sdma_firmware_header { struct sdma_engine { struct device *dev; + struct device_dma_parameters dma_parms; struct sdma_channel channel[MAX_DMA_CHANNELS]; struct sdma_channel_control *channel_control; void __iomem *regs; @@ -493,7 +450,7 @@ static void sdma_handle_channel_loop(struct sdma_channel *sdmac) if (bd->mode.status & BD_RROR) sdmac->status = DMA_ERROR; else - sdmac->status = DMA_SUCCESS; + sdmac->status = DMA_IN_PROGRESS; bd->mode.status |= BD_DONE; sdmac->buf_tail++; @@ -814,15 +771,15 @@ static void sdma_enable_channel(struct sdma_engine *sdma, int channel) __raw_writel(1 << channel, sdma->regs + SDMA_H_START); } -static dma_cookie_t sdma_assign_cookie(struct sdma_channel *sdma) +static dma_cookie_t sdma_assign_cookie(struct sdma_channel *sdmac) { - dma_cookie_t cookie = sdma->chan.cookie; + dma_cookie_t cookie = sdmac->chan.cookie; if (++cookie < 0) cookie = 1; - sdma->chan.cookie = cookie; - sdma->desc.cookie = cookie; + sdmac->chan.cookie = cookie; + sdmac->desc.cookie = cookie; return cookie; } @@ -842,7 +799,7 @@ static dma_cookie_t sdma_tx_submit(struct dma_async_tx_descriptor *tx) cookie = sdma_assign_cookie(sdmac); - sdma_enable_channel(sdma, tx->chan->chan_id); + sdma_enable_channel(sdma, sdmac->channel); spin_unlock_irq(&sdmac->lock); @@ -855,10 +812,6 @@ static int sdma_alloc_chan_resources(struct dma_chan *chan) struct imx_dma_data *data = chan->private; int prio, ret; - /* No need to execute this for internal channel 0 */ - if (chan->chan_id == 0) - return 0; - if (!data) return -EINVAL; @@ -923,7 +876,7 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg( struct sdma_channel *sdmac = to_sdma_chan(chan); struct sdma_engine *sdma = sdmac->sdma; int ret, i, count; - int channel = chan->chan_id; + int channel = sdmac->channel; struct scatterlist *sg; if (sdmac->status == DMA_IN_PROGRESS) @@ -951,7 +904,7 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg( struct sdma_buffer_descriptor *bd = &sdmac->bd[i]; int param; - bd->buffer_addr = sgl->dma_address; + bd->buffer_addr = sg->dma_address; count = sg->length; @@ -968,22 +921,33 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg( ret = -EINVAL; goto err_out; } - if (sdmac->word_size == DMA_SLAVE_BUSWIDTH_4_BYTES) + + switch (sdmac->word_size) { + case DMA_SLAVE_BUSWIDTH_4_BYTES: bd->mode.command = 0; - else - bd->mode.command = sdmac->word_size; + if (count & 3 || sg->dma_address & 3) + return NULL; + break; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + bd->mode.command = 2; + if (count & 1 || sg->dma_address & 1) + return NULL; + break; + case DMA_SLAVE_BUSWIDTH_1_BYTE: + bd->mode.command = 1; + break; + default: + return NULL; + } param = BD_DONE | BD_EXTD | BD_CONT; - if (sdmac->flags & IMX_DMA_SG_LOOP) { + if (i + 1 == sg_len) { param |= BD_INTR; - if (i + 1 == sg_len) - param |= BD_WRAP; + param |= BD_LAST; + param &= ~BD_CONT; } - if (i + 1 == sg_len) - param |= BD_INTR; - dev_dbg(sdma->dev, "entry %d: count: %d dma: 0x%08x %s%s\n", i, count, sg->dma_address, param & BD_WRAP ? "wrap" : "", @@ -997,6 +961,7 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg( return &sdmac->desc; err_out: + sdmac->status = DMA_ERROR; return NULL; } @@ -1007,7 +972,7 @@ static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic( struct sdma_channel *sdmac = to_sdma_chan(chan); struct sdma_engine *sdma = sdmac->sdma; int num_periods = buf_len / period_len; - int channel = chan->chan_id; + int channel = sdmac->channel; int ret, i = 0, buf = 0; dev_dbg(sdma->dev, "%s channel: %d\n", __func__, channel); @@ -1110,14 +1075,12 @@ static enum dma_status sdma_tx_status(struct dma_chan *chan, { struct sdma_channel *sdmac = to_sdma_chan(chan); dma_cookie_t last_used; - enum dma_status ret; last_used = chan->cookie; - ret = dma_async_is_complete(cookie, sdmac->last_completed, last_used); dma_set_tx_state(txstate, sdmac->last_completed, last_used, 0); - return ret; + return sdmac->status; } static void sdma_issue_pending(struct dma_chan *chan) @@ -1127,8 +1090,74 @@ static void sdma_issue_pending(struct dma_chan *chan) */ } -static int __init sdma_init(struct sdma_engine *sdma, - void *ram_code, int ram_code_size) +#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 34 + +static void sdma_add_scripts(struct sdma_engine *sdma, + const struct sdma_script_start_addrs *addr) +{ + s32 *addr_arr = (u32 *)addr; + s32 *saddr_arr = (u32 *)sdma->script_addrs; + int i; + + for (i = 0; i < SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1; i++) + if (addr_arr[i] > 0) + saddr_arr[i] = addr_arr[i]; +} + +static int __init sdma_get_firmware(struct sdma_engine *sdma, + const char *cpu_name, int to_version) +{ + const struct firmware *fw; + char *fwname; + const struct sdma_firmware_header *header; + int ret; + const struct sdma_script_start_addrs *addr; + unsigned short *ram_code; + + fwname = kasprintf(GFP_KERNEL, "sdma-%s-to%d.bin", cpu_name, to_version); + if (!fwname) + return -ENOMEM; + + ret = request_firmware(&fw, fwname, sdma->dev); + if (ret) { + kfree(fwname); + return ret; + } + kfree(fwname); + + if (fw->size < sizeof(*header)) + goto err_firmware; + + header = (struct sdma_firmware_header *)fw->data; + + if (header->magic != SDMA_FIRMWARE_MAGIC) + goto err_firmware; + if (header->ram_code_start + header->ram_code_size > fw->size) + goto err_firmware; + + addr = (void *)header + header->script_addrs_start; + ram_code = (void *)header + header->ram_code_start; + + clk_enable(sdma->clk); + /* download the RAM image for SDMA */ + sdma_load_script(sdma, ram_code, + header->ram_code_size, + addr->ram_code_start_addr); + clk_disable(sdma->clk); + + sdma_add_scripts(sdma, addr); + + dev_info(sdma->dev, "loaded firmware %d.%d\n", + header->version_major, + header->version_minor); + +err_firmware: + release_firmware(fw); + + return ret; +} + +static int __init sdma_init(struct sdma_engine *sdma) { int i, ret; dma_addr_t ccb_phys; @@ -1192,11 +1221,6 @@ static int __init sdma_init(struct sdma_engine *sdma, __raw_writel(ccb_phys, sdma->regs + SDMA_H_C0PTR); - /* download the RAM image for SDMA */ - sdma_load_script(sdma, ram_code, - ram_code_size, - sdma->script_addrs->ram_code_start_addr); - /* Set bits of CONFIG register with given context switching mode */ __raw_writel(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG); @@ -1216,16 +1240,10 @@ err_dma_alloc: static int __init sdma_probe(struct platform_device *pdev) { int ret; - const struct firmware *fw; - const struct sdma_firmware_header *header; - const struct sdma_script_start_addrs *addr; int irq; - unsigned short *ram_code; struct resource *iores; struct sdma_platform_data *pdata = pdev->dev.platform_data; - char *fwname; int i; - dma_cap_mask_t mask; struct sdma_engine *sdma; sdma = kzalloc(sizeof(*sdma), GFP_KERNEL); @@ -1262,41 +1280,15 @@ static int __init sdma_probe(struct platform_device *pdev) if (ret) goto err_request_irq; - fwname = kasprintf(GFP_KERNEL, "sdma-%s-to%d.bin", - pdata->cpu_name, pdata->to_version); - if (!fwname) { - ret = -ENOMEM; - goto err_cputype; - } - - ret = request_firmware(&fw, fwname, &pdev->dev); - if (ret) { - dev_err(&pdev->dev, "request firmware \"%s\" failed with %d\n", - fwname, ret); - kfree(fwname); - goto err_cputype; - } - kfree(fwname); - - if (fw->size < sizeof(*header)) - goto err_firmware; - - header = (struct sdma_firmware_header *)fw->data; - - if (header->magic != SDMA_FIRMWARE_MAGIC) - goto err_firmware; - if (header->ram_code_start + header->ram_code_size > fw->size) - goto err_firmware; - - addr = (void *)header + header->script_addrs_start; - ram_code = (void *)header + header->ram_code_start; - sdma->script_addrs = kmalloc(sizeof(*addr), GFP_KERNEL); + sdma->script_addrs = kzalloc(sizeof(*sdma->script_addrs), GFP_KERNEL); if (!sdma->script_addrs) - goto err_firmware; - memcpy(sdma->script_addrs, addr, sizeof(*addr)); + goto err_alloc; sdma->version = pdata->sdma_version; + dma_cap_set(DMA_SLAVE, sdma->dma_device.cap_mask); + dma_cap_set(DMA_CYCLIC, sdma->dma_device.cap_mask); + INIT_LIST_HEAD(&sdma->dma_device.channels); /* Initialize channel parameters */ for (i = 0; i < MAX_DMA_CHANNELS; i++) { @@ -1305,21 +1297,28 @@ static int __init sdma_probe(struct platform_device *pdev) sdmac->sdma = sdma; spin_lock_init(&sdmac->lock); - dma_cap_set(DMA_SLAVE, sdma->dma_device.cap_mask); - dma_cap_set(DMA_CYCLIC, sdma->dma_device.cap_mask); - sdmac->chan.device = &sdma->dma_device; - sdmac->chan.chan_id = i; sdmac->channel = i; - /* Add the channel to the DMAC list */ - list_add_tail(&sdmac->chan.device_node, &sdma->dma_device.channels); + /* + * Add the channel to the DMAC list. Do not add channel 0 though + * because we need it internally in the SDMA driver. This also means + * that channel 0 in dmaengine counting matches sdma channel 1. + */ + if (i) + list_add_tail(&sdmac->chan.device_node, + &sdma->dma_device.channels); } - ret = sdma_init(sdma, ram_code, header->ram_code_size); + ret = sdma_init(sdma); if (ret) goto err_init; + if (pdata->script_addrs) + sdma_add_scripts(sdma, pdata->script_addrs); + + sdma_get_firmware(sdma, pdata->cpu_name, pdata->to_version); + sdma->dma_device.dev = &pdev->dev; sdma->dma_device.device_alloc_chan_resources = sdma_alloc_chan_resources; @@ -1329,6 +1328,8 @@ static int __init sdma_probe(struct platform_device *pdev) sdma->dma_device.device_prep_dma_cyclic = sdma_prep_dma_cyclic; sdma->dma_device.device_control = sdma_control; sdma->dma_device.device_issue_pending = sdma_issue_pending; + sdma->dma_device.dev->dma_parms = &sdma->dma_parms; + dma_set_max_seg_size(sdma->dma_device.dev, 65535); ret = dma_async_device_register(&sdma->dma_device); if (ret) { @@ -1336,26 +1337,13 @@ static int __init sdma_probe(struct platform_device *pdev) goto err_init; } - dev_info(&pdev->dev, "initialized (firmware %d.%d)\n", - header->version_major, - header->version_minor); - - /* request channel 0. This is an internal control channel - * to the SDMA engine and not available to clients. - */ - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - dma_request_channel(mask, NULL, NULL); - - release_firmware(fw); + dev_info(sdma->dev, "initialized\n"); return 0; err_init: kfree(sdma->script_addrs); -err_firmware: - release_firmware(fw); -err_cputype: +err_alloc: free_irq(irq, sdma); err_request_irq: iounmap(sdma->regs); @@ -1366,7 +1354,7 @@ err_clk: err_request_region: err_irq: kfree(sdma); - return 0; + return ret; } static int __exit sdma_remove(struct platform_device *pdev) @@ -1385,7 +1373,7 @@ static int __init sdma_module_init(void) { return platform_driver_probe(&sdma_driver, sdma_probe); } -subsys_initcall(sdma_module_init); +module_init(sdma_module_init); MODULE_AUTHOR("Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>"); MODULE_DESCRIPTION("i.MX SDMA driver"); |