diff options
Diffstat (limited to 'drivers/dma/pl330.c')
| -rw-r--r-- | drivers/dma/pl330.c | 471 |
1 files changed, 267 insertions, 204 deletions
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 80680eee017..73fa9b7a10a 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -25,6 +25,8 @@ #include <linux/amba/pl330.h> #include <linux/scatterlist.h> #include <linux/of.h> +#include <linux/of_dma.h> +#include <linux/err.h> #include "dmaengine.h" #define PL330_MAX_CHAN 8 @@ -155,7 +157,6 @@ enum pl330_reqtype { #define PERIPH_REV_R0P0 0 #define PERIPH_REV_R1P0 1 #define PERIPH_REV_R1P1 2 -#define PCELL_ID 0xff0 #define CR0_PERIPH_REQ_SET (1 << 0) #define CR0_BOOT_EN_SET (1 << 1) @@ -191,8 +192,6 @@ enum pl330_reqtype { #define INTEG_CFG 0x0 #define PERIPH_ID_VAL ((PART << 0) | (DESIGNER << 12)) -#define PCELL_ID_VAL 0xb105f00d - #define PL330_STATE_STOPPED (1 << 0) #define PL330_STATE_EXECUTING (1 << 1) #define PL330_STATE_WFE (1 << 2) @@ -290,7 +289,6 @@ static unsigned cmd_line; /* Populated by the PL330 core driver for DMA API driver's info */ struct pl330_config { u32 periph_id; - u32 pcell_id; #define DMAC_MODE_NS (1 << 0) unsigned int mode; unsigned int data_bus_width:10; /* In number of bits */ @@ -503,7 +501,7 @@ struct pl330_dmac { /* Maximum possible events/irqs */ int events[32]; /* BUS address of MicroCode buffer */ - u32 mcode_bus; + dma_addr_t mcode_bus; /* CPU address of MicroCode buffer */ void *mcode_cpu; /* List of all Channel threads */ @@ -545,8 +543,12 @@ struct dma_pl330_chan { /* DMA-Engine Channel */ struct dma_chan chan; - /* List of to be xfered descriptors */ + /* List of submitted descriptors */ + struct list_head submitted_list; + /* List of issued descriptors */ struct list_head work_list; + /* List of completed descriptors */ + struct list_head completed_list; /* Pointer to the DMAC that manages this channel, * NULL if the channel is available to be acquired. @@ -578,12 +580,16 @@ struct dma_pl330_dmac { /* DMA-Engine Device */ struct dma_device ddma; + /* Holds info about sg limitations */ + struct device_dma_parameters dma_parms; + /* Pool of descriptors available for the DMAC's channels */ struct list_head desc_pool; /* To protect desc_pool manipulation */ spinlock_t pool_lock; /* Peripheral channels connected to this DMAC */ + unsigned int num_peripherals; struct dma_pl330_chan *peripherals; /* keep at end */ }; @@ -643,19 +649,6 @@ static inline bool _manager_ns(struct pl330_thread *thrd) return (pl330->pinfo->pcfg.mode & DMAC_MODE_NS) ? true : false; } -static inline u32 get_id(struct pl330_info *pi, u32 off) -{ - void __iomem *regs = pi->base; - u32 id = 0; - - id |= (readb(regs + off + 0x0) << 0); - id |= (readb(regs + off + 0x4) << 8); - id |= (readb(regs + off + 0x8) << 16); - id |= (readb(regs + off + 0xc) << 24); - - return id; -} - static inline u32 get_revision(u32 periph_id) { return (periph_id >> PERIPH_REV_SHIFT) & PERIPH_REV_MASK; @@ -1979,9 +1972,6 @@ static void read_dmac_config(struct pl330_info *pi) pi->pcfg.num_events = val; pi->pcfg.irq_ns = readl(regs + CR3); - - pi->pcfg.periph_id = get_id(pi, PERIPH_ID); - pi->pcfg.pcell_id = get_id(pi, PCELL_ID); } static inline void _reset_thread(struct pl330_thread *thrd) @@ -2091,10 +2081,8 @@ static int pl330_add(struct pl330_info *pi) regs = pi->base; /* Check if we can handle this DMAC */ - if ((get_id(pi, PERIPH_ID) & 0xfffff) != PERIPH_ID_VAL - || get_id(pi, PCELL_ID) != PCELL_ID_VAL) { - dev_err(pi->dev, "PERIPH_ID 0x%x, PCELL_ID 0x%x !\n", - get_id(pi, PERIPH_ID), get_id(pi, PCELL_ID)); + if ((pi->pcfg.periph_id & 0xfffff) != PERIPH_ID_VAL) { + dev_err(pi->dev, "PERIPH_ID 0x%x !\n", pi->pcfg.periph_id); return -EINVAL; } @@ -2213,66 +2201,6 @@ to_desc(struct dma_async_tx_descriptor *tx) return container_of(tx, struct dma_pl330_desc, txd); } -static inline void free_desc_list(struct list_head *list) -{ - struct dma_pl330_dmac *pdmac; - struct dma_pl330_desc *desc; - struct dma_pl330_chan *pch = NULL; - unsigned long flags; - - /* Finish off the work list */ - list_for_each_entry(desc, list, node) { - dma_async_tx_callback callback; - void *param; - - /* All desc in a list belong to same channel */ - pch = desc->pchan; - callback = desc->txd.callback; - param = desc->txd.callback_param; - - if (callback) - callback(param); - - desc->pchan = NULL; - } - - /* pch will be unset if list was empty */ - if (!pch) - return; - - pdmac = pch->dmac; - - spin_lock_irqsave(&pdmac->pool_lock, flags); - list_splice_tail_init(list, &pdmac->desc_pool); - spin_unlock_irqrestore(&pdmac->pool_lock, flags); -} - -static inline void handle_cyclic_desc_list(struct list_head *list) -{ - struct dma_pl330_desc *desc; - struct dma_pl330_chan *pch = NULL; - unsigned long flags; - - list_for_each_entry(desc, list, node) { - dma_async_tx_callback callback; - - /* Change status to reload it */ - desc->status = PREP; - pch = desc->pchan; - callback = desc->txd.callback; - if (callback) - callback(desc->txd.callback_param); - } - - /* pch will be unset if list was empty */ - if (!pch) - return; - - spin_lock_irqsave(&pch->lock, flags); - list_splice_tail_init(list, &pch->work_list); - spin_unlock_irqrestore(&pch->lock, flags); -} - static inline void fill_queue(struct dma_pl330_chan *pch) { struct dma_pl330_desc *desc; @@ -2282,13 +2210,12 @@ static inline void fill_queue(struct dma_pl330_chan *pch) /* If already submitted */ if (desc->status == BUSY) - break; + continue; ret = pl330_submit_req(pch->pl330_chid, &desc->req); if (!ret) { desc->status = BUSY; - break; } else if (ret == -EAGAIN) { /* QFull or DMAC Dying */ break; @@ -2307,7 +2234,6 @@ static void pl330_tasklet(unsigned long data) struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data; struct dma_pl330_desc *desc, *_dt; unsigned long flags; - LIST_HEAD(list); spin_lock_irqsave(&pch->lock, flags); @@ -2316,7 +2242,7 @@ static void pl330_tasklet(unsigned long data) if (desc->status == DONE) { if (!pch->cyclic) dma_cookie_complete(&desc->txd); - list_move_tail(&desc->node, &list); + list_move_tail(&desc->node, &pch->completed_list); } /* Try to submit a req imm. next to the last completed cookie */ @@ -2325,12 +2251,33 @@ static void pl330_tasklet(unsigned long data) /* Make sure the PL330 Channel thread is active */ pl330_chan_ctrl(pch->pl330_chid, PL330_OP_START); - spin_unlock_irqrestore(&pch->lock, flags); + while (!list_empty(&pch->completed_list)) { + dma_async_tx_callback callback; + void *callback_param; - if (pch->cyclic) - handle_cyclic_desc_list(&list); - else - free_desc_list(&list); + desc = list_first_entry(&pch->completed_list, + struct dma_pl330_desc, node); + + callback = desc->txd.callback; + callback_param = desc->txd.callback_param; + + if (pch->cyclic) { + desc->status = PREP; + list_move_tail(&desc->node, &pch->work_list); + } else { + desc->status = FREE; + list_move_tail(&desc->node, &pch->dmac->desc_pool); + } + + dma_descriptor_unmap(&desc->txd); + + if (callback) { + spin_unlock_irqrestore(&pch->lock, flags); + callback(callback_param); + spin_lock_irqsave(&pch->lock, flags); + } + } + spin_unlock_irqrestore(&pch->lock, flags); } static void dma_pl330_rqcb(void *token, enum pl330_op_err err) @@ -2359,25 +2306,28 @@ bool pl330_filter(struct dma_chan *chan, void *param) if (chan->device->dev->driver != &pl330_driver.drv) return false; -#ifdef CONFIG_OF - if (chan->device->dev->of_node) { - const __be32 *prop_value; - phandle phandle; - struct device_node *node; - - prop_value = ((struct property *)param)->value; - phandle = be32_to_cpup(prop_value++); - node = of_find_node_by_phandle(phandle); - return ((chan->private == node) && - (chan->chan_id == be32_to_cpup(prop_value))); - } -#endif - peri_id = chan->private; - return *peri_id == (unsigned)param; + return *peri_id == (unsigned long)param; } EXPORT_SYMBOL(pl330_filter); +static struct dma_chan *of_dma_pl330_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + int count = dma_spec->args_count; + struct dma_pl330_dmac *pdmac = ofdma->of_dma_data; + unsigned int chan_id; + + if (count != 1) + return NULL; + + chan_id = dma_spec->args[0]; + if (chan_id >= pdmac->num_peripherals) + return NULL; + + return dma_get_slave_channel(&pdmac->peripherals[chan_id].chan); +} + static int pl330_alloc_chan_resources(struct dma_chan *chan) { struct dma_pl330_chan *pch = to_pchan(chan); @@ -2405,7 +2355,7 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) { struct dma_pl330_chan *pch = to_pchan(chan); - struct dma_pl330_desc *desc, *_dt; + struct dma_pl330_desc *desc; unsigned long flags; struct dma_pl330_dmac *pdmac = pch->dmac; struct dma_slave_config *slave_config; @@ -2419,12 +2369,24 @@ static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH); /* Mark all desc done */ - list_for_each_entry_safe(desc, _dt, &pch->work_list , node) { - desc->status = DONE; - list_move_tail(&desc->node, &list); + list_for_each_entry(desc, &pch->submitted_list, node) { + desc->status = FREE; + dma_cookie_complete(&desc->txd); + } + + list_for_each_entry(desc, &pch->work_list , node) { + desc->status = FREE; + dma_cookie_complete(&desc->txd); + } + + list_for_each_entry(desc, &pch->completed_list , node) { + desc->status = FREE; + dma_cookie_complete(&desc->txd); } - list_splice_tail_init(&list, &pdmac->desc_pool); + list_splice_tail_init(&pch->submitted_list, &pdmac->desc_pool); + list_splice_tail_init(&pch->work_list, &pdmac->desc_pool); + list_splice_tail_init(&pch->completed_list, &pdmac->desc_pool); spin_unlock_irqrestore(&pch->lock, flags); break; case DMA_SLAVE_CONFIG: @@ -2459,10 +2421,10 @@ static void pl330_free_chan_resources(struct dma_chan *chan) struct dma_pl330_chan *pch = to_pchan(chan); unsigned long flags; - spin_lock_irqsave(&pch->lock, flags); - tasklet_kill(&pch->task); + spin_lock_irqsave(&pch->lock, flags); + pl330_release_channel(pch->pl330_chid); pch->pl330_chid = NULL; @@ -2481,7 +2443,14 @@ pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie, static void pl330_issue_pending(struct dma_chan *chan) { - pl330_tasklet((unsigned long) to_pchan(chan)); + struct dma_pl330_chan *pch = to_pchan(chan); + unsigned long flags; + + spin_lock_irqsave(&pch->lock, flags); + list_splice_tail_init(&pch->submitted_list, &pch->work_list); + spin_unlock_irqrestore(&pch->lock, flags); + + pl330_tasklet((unsigned long)pch); } /* @@ -2501,14 +2470,18 @@ static dma_cookie_t pl330_tx_submit(struct dma_async_tx_descriptor *tx) /* Assign cookies to all nodes */ while (!list_empty(&last->node)) { desc = list_entry(last->node.next, struct dma_pl330_desc, node); + if (pch->cyclic) { + desc->txd.callback = last->txd.callback; + desc->txd.callback_param = last->txd.callback_param; + } dma_cookie_assign(&desc->txd); - list_move_tail(&desc->node, &pch->work_list); + list_move_tail(&desc->node, &pch->submitted_list); } cookie = dma_cookie_assign(&last->txd); - list_add_tail(&last->node, &pch->work_list); + list_add_tail(&last->node, &pch->submitted_list); spin_unlock_irqrestore(&pch->lock, flags); return cookie; @@ -2516,12 +2489,9 @@ static dma_cookie_t pl330_tx_submit(struct dma_async_tx_descriptor *tx) static inline void _init_desc(struct dma_pl330_desc *desc) { - desc->pchan = NULL; desc->req.x = &desc->px; desc->req.token = desc; desc->rqcfg.swap = SWAP_NO; - desc->rqcfg.privileged = 0; - desc->rqcfg.insnaccess = 0; desc->rqcfg.scctl = SCCTRL0; desc->rqcfg.dcctl = DCCTRL0; desc->req.cfg = &desc->rqcfg; @@ -2541,7 +2511,7 @@ static int add_desc(struct dma_pl330_dmac *pdmac, gfp_t flg, int count) if (!pdmac) return 0; - desc = kmalloc(count * sizeof(*desc), flg); + desc = kcalloc(count, sizeof(*desc), flg); if (!desc) return 0; @@ -2684,45 +2654,82 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( size_t period_len, enum dma_transfer_direction direction, unsigned long flags, void *context) { - struct dma_pl330_desc *desc; + struct dma_pl330_desc *desc = NULL, *first = NULL; struct dma_pl330_chan *pch = to_pchan(chan); + struct dma_pl330_dmac *pdmac = pch->dmac; + unsigned int i; dma_addr_t dst; dma_addr_t src; - desc = pl330_get_desc(pch); - if (!desc) { - dev_err(pch->dmac->pif.dev, "%s:%d Unable to fetch desc\n", - __func__, __LINE__); + if (len % period_len != 0) return NULL; - } - switch (direction) { - case DMA_MEM_TO_DEV: - desc->rqcfg.src_inc = 1; - desc->rqcfg.dst_inc = 0; - desc->req.rqtype = MEMTODEV; - src = dma_addr; - dst = pch->fifo_addr; - break; - case DMA_DEV_TO_MEM: - desc->rqcfg.src_inc = 0; - desc->rqcfg.dst_inc = 1; - desc->req.rqtype = DEVTOMEM; - src = pch->fifo_addr; - dst = dma_addr; - break; - default: + if (!is_slave_direction(direction)) { dev_err(pch->dmac->pif.dev, "%s:%d Invalid dma direction\n", __func__, __LINE__); return NULL; } - desc->rqcfg.brst_size = pch->burst_sz; - desc->rqcfg.brst_len = 1; + for (i = 0; i < len / period_len; i++) { + desc = pl330_get_desc(pch); + if (!desc) { + dev_err(pch->dmac->pif.dev, "%s:%d Unable to fetch desc\n", + __func__, __LINE__); - pch->cyclic = true; + if (!first) + return NULL; + + spin_lock_irqsave(&pdmac->pool_lock, flags); - fill_px(&desc->px, dst, src, period_len); + while (!list_empty(&first->node)) { + desc = list_entry(first->node.next, + struct dma_pl330_desc, node); + list_move_tail(&desc->node, &pdmac->desc_pool); + } + + list_move_tail(&first->node, &pdmac->desc_pool); + + spin_unlock_irqrestore(&pdmac->pool_lock, flags); + + return NULL; + } + + switch (direction) { + case DMA_MEM_TO_DEV: + desc->rqcfg.src_inc = 1; + desc->rqcfg.dst_inc = 0; + desc->req.rqtype = MEMTODEV; + src = dma_addr; + dst = pch->fifo_addr; + break; + case DMA_DEV_TO_MEM: + desc->rqcfg.src_inc = 0; + desc->rqcfg.dst_inc = 1; + desc->req.rqtype = DEVTOMEM; + src = pch->fifo_addr; + dst = dma_addr; + break; + default: + break; + } + + desc->rqcfg.brst_size = pch->burst_sz; + desc->rqcfg.brst_len = 1; + fill_px(&desc->px, dst, src, period_len); + + if (!first) + first = desc; + else + list_add_tail(&desc->node, &first->node); + + dma_addr += period_len; + } + + if (!desc) + return NULL; + + pch->cyclic = true; + desc->txd.flags = flags; return &desc->txd; } @@ -2769,6 +2776,28 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, return &desc->txd; } +static void __pl330_giveback_desc(struct dma_pl330_dmac *pdmac, + struct dma_pl330_desc *first) +{ + unsigned long flags; + struct dma_pl330_desc *desc; + + if (!first) + return; + + spin_lock_irqsave(&pdmac->pool_lock, flags); + + while (!list_empty(&first->node)) { + desc = list_entry(first->node.next, + struct dma_pl330_desc, node); + list_move_tail(&desc->node, &pdmac->desc_pool); + } + + list_move_tail(&first->node, &pdmac->desc_pool); + + spin_unlock_irqrestore(&pdmac->pool_lock, flags); +} + static struct dma_async_tx_descriptor * pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, @@ -2777,7 +2806,6 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, struct dma_pl330_desc *first, *desc = NULL; struct dma_pl330_chan *pch = to_pchan(chan); struct scatterlist *sg; - unsigned long flags; int i; dma_addr_t addr; @@ -2797,20 +2825,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, dev_err(pch->dmac->pif.dev, "%s:%d Unable to fetch desc\n", __func__, __LINE__); - if (!first) - return NULL; - - spin_lock_irqsave(&pdmac->pool_lock, flags); - - while (!list_empty(&first->node)) { - desc = list_entry(first->node.next, - struct dma_pl330_desc, node); - list_move_tail(&desc->node, &pdmac->desc_pool); - } - - list_move_tail(&first->node, &pdmac->desc_pool); - - spin_unlock_irqrestore(&pdmac->pool_lock, flags); + __pl330_giveback_desc(pdmac, first); return NULL; } @@ -2851,22 +2866,46 @@ static irqreturn_t pl330_irq_handler(int irq, void *data) return IRQ_NONE; } +#define PL330_DMA_BUSWIDTHS \ + BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) | \ + BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_8_BYTES) + +static int pl330_dma_device_slave_caps(struct dma_chan *dchan, + struct dma_slave_caps *caps) +{ + caps->src_addr_widths = PL330_DMA_BUSWIDTHS; + caps->dstn_addr_widths = PL330_DMA_BUSWIDTHS; + caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + caps->cmd_pause = false; + caps->cmd_terminate = true; + caps->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; + + return 0; +} + static int pl330_probe(struct amba_device *adev, const struct amba_id *id) { struct dma_pl330_platdata *pdat; struct dma_pl330_dmac *pdmac; - struct dma_pl330_chan *pch; + struct dma_pl330_chan *pch, *_p; struct pl330_info *pi; struct dma_device *pd; struct resource *res; int i, ret, irq; int num_chan; - pdat = adev->dev.platform_data; + pdat = dev_get_platdata(&adev->dev); + + ret = dma_set_mask_and_coherent(&adev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; /* Allocate a new DMAC and its Channels */ - pdmac = kzalloc(sizeof(*pdmac), GFP_KERNEL); + pdmac = devm_kzalloc(&adev->dev, sizeof(*pdmac), GFP_KERNEL); if (!pdmac) { dev_err(&adev->dev, "unable to allocate mem\n"); return -ENOMEM; @@ -2878,25 +2917,29 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pi->mcbufsz = pdat ? pdat->mcbuf_sz : 0; res = &adev->res; - request_mem_region(res->start, resource_size(res), "dma-pl330"); - - pi->base = ioremap(res->start, resource_size(res)); - if (!pi->base) { - ret = -ENXIO; - goto probe_err1; - } + pi->base = devm_ioremap_resource(&adev->dev, res); + if (IS_ERR(pi->base)) + return PTR_ERR(pi->base); amba_set_drvdata(adev, pdmac); - irq = adev->irq[0]; - ret = request_irq(irq, pl330_irq_handler, 0, - dev_name(&adev->dev), pi); - if (ret) - goto probe_err2; + for (i = 0; i < AMBA_NR_IRQS; i++) { + irq = adev->irq[i]; + if (irq) { + ret = devm_request_irq(&adev->dev, irq, + pl330_irq_handler, 0, + dev_name(&adev->dev), pi); + if (ret) + return ret; + } else { + break; + } + } + pi->pcfg.periph_id = adev->periphid; ret = pl330_add(pi); if (ret) - goto probe_err3; + return ret; INIT_LIST_HEAD(&pdmac->desc_pool); spin_lock_init(&pdmac->pool_lock); @@ -2914,11 +2957,13 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) else num_chan = max_t(int, pi->pcfg.num_peri, pi->pcfg.num_chan); + pdmac->num_peripherals = num_chan; + pdmac->peripherals = kzalloc(num_chan * sizeof(*pch), GFP_KERNEL); if (!pdmac->peripherals) { ret = -ENOMEM; dev_err(&adev->dev, "unable to allocate pdmac->peripherals\n"); - goto probe_err4; + goto probe_err2; } for (i = 0; i < num_chan; i++) { @@ -2928,7 +2973,9 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) else pch->chan.private = adev->dev.of_node; + INIT_LIST_HEAD(&pch->submitted_list); INIT_LIST_HEAD(&pch->work_list); + INIT_LIST_HEAD(&pch->completed_list); spin_lock_init(&pch->lock); pch->pl330_chid = NULL; pch->chan.device = pd; @@ -2958,13 +3005,34 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pd->device_prep_slave_sg = pl330_prep_slave_sg; pd->device_control = pl330_control; pd->device_issue_pending = pl330_issue_pending; + pd->device_slave_caps = pl330_dma_device_slave_caps; ret = dma_async_device_register(pd); if (ret) { dev_err(&adev->dev, "unable to register DMAC\n"); - goto probe_err4; + goto probe_err3; } + if (adev->dev.of_node) { + ret = of_dma_controller_register(adev->dev.of_node, + of_dma_pl330_xlate, pdmac); + if (ret) { + dev_err(&adev->dev, + "unable to register DMA to the generic DT DMA helpers\n"); + } + } + + adev->dev.dma_parms = &pdmac->dma_parms; + + /* + * This is the limit for transfers with a buswidth of 1, larger + * buswidths will have larger limits. + */ + ret = dma_set_max_seg_size(&adev->dev, 1900800); + if (ret) + dev_err(&adev->dev, "unable to set the seg size\n"); + + dev_info(&adev->dev, "Loaded driver for PL330 DMAC-%d\n", adev->periphid); dev_info(&adev->dev, @@ -2974,16 +3042,20 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pi->pcfg.num_peri, pi->pcfg.num_events); return 0; - -probe_err4: - pl330_del(pi); probe_err3: - free_irq(irq, pi); + /* Idle the DMAC */ + list_for_each_entry_safe(pch, _p, &pdmac->ddma.channels, + chan.device_node) { + + /* Remove the channel */ + list_del(&pch->chan.device_node); + + /* Flush the channel */ + pl330_control(&pch->chan, DMA_TERMINATE_ALL, 0); + pl330_free_chan_resources(&pch->chan); + } probe_err2: - iounmap(pi->base); -probe_err1: - release_mem_region(res->start, resource_size(res)); - kfree(pdmac); + pl330_del(pi); return ret; } @@ -2993,13 +3065,14 @@ static int pl330_remove(struct amba_device *adev) struct dma_pl330_dmac *pdmac = amba_get_drvdata(adev); struct dma_pl330_chan *pch, *_p; struct pl330_info *pi; - struct resource *res; - int irq; if (!pdmac) return 0; - amba_set_drvdata(adev, NULL); + if (adev->dev.of_node) + of_dma_controller_free(adev->dev.of_node); + + dma_async_device_unregister(&pdmac->ddma); /* Idle the DMAC */ list_for_each_entry_safe(pch, _p, &pdmac->ddma.channels, @@ -3017,16 +3090,6 @@ static int pl330_remove(struct amba_device *adev) pl330_del(pi); - irq = adev->irq[0]; - free_irq(irq, pi); - - iounmap(pi->base); - - res = &adev->res; - release_mem_region(res->start, resource_size(res)); - - kfree(pdmac); - return 0; } |
