diff options
Diffstat (limited to 'drivers/dma')
-rw-r--r-- | drivers/dma/imx-sdma.c | 174 | ||||
-rw-r--r-- | drivers/dma/mv_xor.c | 2 | ||||
-rw-r--r-- | drivers/dma/shdma.c | 130 | ||||
-rw-r--r-- | drivers/dma/shdma.h | 1 |
4 files changed, 192 insertions, 115 deletions
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index d0602dd5d1b2..d5a5d4d9c19b 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -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 /** @@ -1127,8 +1083,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, + sdma->script_addrs->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 +1214,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,14 +1233,9 @@ 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; @@ -1262,38 +1274,9 @@ 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; @@ -1316,10 +1299,15 @@ static int __init sdma_probe(struct platform_device *pdev) 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; @@ -1336,10 +1324,6 @@ 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. */ @@ -1347,15 +1331,13 @@ static int __init sdma_probe(struct platform_device *pdev) 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); diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index 411d5bf50fc4..a25f5f61e0e0 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -449,7 +449,7 @@ mv_xor_slot_cleanup(struct mv_xor_chan *mv_chan) static void mv_xor_tasklet(unsigned long data) { struct mv_xor_chan *chan = (struct mv_xor_chan *) data; - __mv_xor_slot_cleanup(chan); + mv_xor_slot_cleanup(chan); } static struct mv_xor_desc_slot * diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 85ffd5e38c50..28720d3103c4 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -27,7 +27,10 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/sh_dma.h> - +#include <linux/notifier.h> +#include <linux/kdebug.h> +#include <linux/spinlock.h> +#include <linux/rculist.h> #include "shdma.h" /* DMA descriptor control */ @@ -43,6 +46,13 @@ enum sh_dmae_desc_status { /* Default MEMCPY transfer size = 2^2 = 4 bytes */ #define LOG2_DEFAULT_XFER_SIZE 2 +/* + * Used for write-side mutual exclusion for the global device list, + * read-side synchronization by way of RCU. + */ +static DEFINE_SPINLOCK(sh_dmae_lock); +static LIST_HEAD(sh_dmae_devices); + /* A bitmask with bits enough for enum sh_dmae_slave_chan_id */ static unsigned long sh_dmae_slave_used[BITS_TO_LONGS(SH_DMA_SLAVE_NUMBER)]; @@ -817,10 +827,9 @@ static irqreturn_t sh_dmae_interrupt(int irq, void *data) return ret; } -#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) -static irqreturn_t sh_dmae_err(int irq, void *data) +static unsigned int sh_dmae_reset(struct sh_dmae_device *shdev) { - struct sh_dmae_device *shdev = (struct sh_dmae_device *)data; + unsigned int handled = 0; int i; /* halt the dma controller */ @@ -829,25 +838,35 @@ static irqreturn_t sh_dmae_err(int irq, void *data) /* We cannot detect, which channel caused the error, have to reset all */ for (i = 0; i < SH_DMAC_MAX_CHANNELS; i++) { struct sh_dmae_chan *sh_chan = shdev->chan[i]; - if (sh_chan) { - struct sh_desc *desc; - /* Stop the channel */ - dmae_halt(sh_chan); - /* Complete all */ - list_for_each_entry(desc, &sh_chan->ld_queue, node) { - struct dma_async_tx_descriptor *tx = &desc->async_tx; - desc->mark = DESC_IDLE; - if (tx->callback) - tx->callback(tx->callback_param); - } - list_splice_init(&sh_chan->ld_queue, &sh_chan->ld_free); + struct sh_desc *desc; + + if (!sh_chan) + continue; + + /* Stop the channel */ + dmae_halt(sh_chan); + + /* Complete all */ + list_for_each_entry(desc, &sh_chan->ld_queue, node) { + struct dma_async_tx_descriptor *tx = &desc->async_tx; + desc->mark = DESC_IDLE; + if (tx->callback) + tx->callback(tx->callback_param); } + + list_splice_init(&sh_chan->ld_queue, &sh_chan->ld_free); + handled++; } + sh_dmae_rst(shdev); - return IRQ_HANDLED; + return !!handled; +} + +static irqreturn_t sh_dmae_err(int irq, void *data) +{ + return IRQ_RETVAL(sh_dmae_reset(data)); } -#endif static void dmae_do_tasklet(unsigned long data) { @@ -876,6 +895,60 @@ static void dmae_do_tasklet(unsigned long data) sh_dmae_chan_ld_cleanup(sh_chan, false); } +static bool sh_dmae_nmi_notify(struct sh_dmae_device *shdev) +{ + unsigned int handled; + + /* Fast path out if NMIF is not asserted for this controller */ + if ((dmaor_read(shdev) & DMAOR_NMIF) == 0) + return false; + + handled = sh_dmae_reset(shdev); + if (handled) + return true; + + return false; +} + +static int sh_dmae_nmi_handler(struct notifier_block *self, + unsigned long cmd, void *data) +{ + struct sh_dmae_device *shdev; + int ret = NOTIFY_DONE; + bool triggered; + + /* + * Only concern ourselves with NMI events. + * + * Normally we would check the die chain value, but as this needs + * to be architecture independent, check for NMI context instead. + */ + if (!in_nmi()) + return NOTIFY_DONE; + + rcu_read_lock(); + list_for_each_entry_rcu(shdev, &sh_dmae_devices, node) { + /* + * Only stop if one of the controllers has NMIF asserted, + * we do not want to interfere with regular address error + * handling or NMI events that don't concern the DMACs. + */ + triggered = sh_dmae_nmi_notify(shdev); + if (triggered == true) + ret = NOTIFY_OK; + } + rcu_read_unlock(); + + return ret; +} + +static struct notifier_block sh_dmae_nmi_notifier __read_mostly = { + .notifier_call = sh_dmae_nmi_handler, + + /* Run before NMI debug handler and KGDB */ + .priority = 1, +}; + static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id, int irq, unsigned long flags) { @@ -967,6 +1040,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev) struct sh_dmae_pdata *pdata = pdev->dev.platform_data; unsigned long irqflags = IRQF_DISABLED, chan_flag[SH_DMAC_MAX_CHANNELS] = {}; + unsigned long flags; int errirq, chan_irq[SH_DMAC_MAX_CHANNELS]; int err, i, irq_cnt = 0, irqres = 0; struct sh_dmae_device *shdev; @@ -1032,6 +1106,10 @@ static int __init sh_dmae_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); + spin_lock_irqsave(&sh_dmae_lock, flags); + list_add_tail_rcu(&shdev->node, &sh_dmae_devices); + spin_unlock_irqrestore(&sh_dmae_lock, flags); + /* reset dma controller */ err = sh_dmae_rst(shdev); if (err) @@ -1135,6 +1213,10 @@ eirqres: eirq_err: #endif rst_err: + spin_lock_irqsave(&sh_dmae_lock, flags); + list_del_rcu(&shdev->node); + spin_unlock_irqrestore(&sh_dmae_lock, flags); + pm_runtime_put(&pdev->dev); if (dmars) iounmap(shdev->dmars); @@ -1155,6 +1237,7 @@ static int __exit sh_dmae_remove(struct platform_device *pdev) { struct sh_dmae_device *shdev = platform_get_drvdata(pdev); struct resource *res; + unsigned long flags; int errirq = platform_get_irq(pdev, 0); dma_async_device_unregister(&shdev->common); @@ -1162,6 +1245,10 @@ static int __exit sh_dmae_remove(struct platform_device *pdev) if (errirq > 0) free_irq(errirq, shdev); + spin_lock_irqsave(&sh_dmae_lock, flags); + list_del_rcu(&shdev->node); + spin_unlock_irqrestore(&sh_dmae_lock, flags); + /* channel data remove */ sh_dmae_chan_remove(shdev); @@ -1200,6 +1287,11 @@ static struct platform_driver sh_dmae_driver = { static int __init sh_dmae_init(void) { + /* Wire up NMI handling */ + int err = register_die_notifier(&sh_dmae_nmi_notifier); + if (err) + return err; + return platform_driver_probe(&sh_dmae_driver, sh_dmae_probe); } module_init(sh_dmae_init); @@ -1207,6 +1299,8 @@ module_init(sh_dmae_init); static void __exit sh_dmae_exit(void) { platform_driver_unregister(&sh_dmae_driver); + + unregister_die_notifier(&sh_dmae_nmi_notifier); } module_exit(sh_dmae_exit); diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h index 4021275a0a43..52e4fb173805 100644 --- a/drivers/dma/shdma.h +++ b/drivers/dma/shdma.h @@ -43,6 +43,7 @@ struct sh_dmae_device { struct dma_device common; struct sh_dmae_chan *chan[SH_DMAC_MAX_CHANNELS]; struct sh_dmae_pdata *pdata; + struct list_head node; u32 __iomem *chan_reg; u16 __iomem *dmars; }; |