diff options
Diffstat (limited to 'drivers/dma/ste_dma40.c')
| -rw-r--r-- | drivers/dma/ste_dma40.c | 967 |
1 files changed, 449 insertions, 518 deletions
diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 5ab5880d5c90..d52e1685aed5 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1,9 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) Ericsson AB 2007-2008 * Copyright (C) ST-Ericsson SA 2008-2010 * Author: Per Forlin <per.forlin@stericsson.com> for ST-Ericsson * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson - * License terms: GNU General Public License (GPL) version 2 */ #include <linux/dma-mapping.h> @@ -14,18 +14,46 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/delay.h> +#include <linux/log2.h> #include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/err.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/of_dma.h> #include <linux/amba/bus.h> #include <linux/regulator/consumer.h> -#include <linux/platform_data/dma-ste-dma40.h> #include "dmaengine.h" +#include "ste_dma40.h" #include "ste_dma40_ll.h" +/** + * struct stedma40_platform_data - Configuration struct for the dma device. + * + * @disabled_channels: A vector, ending with -1, that marks physical channels + * that are for different reasons not available for the driver. + * @soft_lli_chans: A vector, that marks physical channels will use LLI by SW + * which avoids HW bug that exists in some versions of the controller. + * SoftLLI introduces relink overhead that could impact performance for + * certain use cases. + * @num_of_soft_lli_chans: The number of channels that needs to be configured + * to use SoftLLI. + * @use_esram_lcla: flag for mapping the lcla into esram region + * @num_of_memcpy_chans: The number of channels reserved for memcpy. + * @num_of_phy_chans: The number of physical channels implemented in HW. + * 0 means reading the number of channels from DMA HW but this is only valid + * for 'multiple of 4' channels, like 8. + */ +struct stedma40_platform_data { + int disabled_channels[STEDMA40_MAX_PHYS]; + int *soft_lli_chans; + int num_of_soft_lli_chans; + bool use_esram_lcla; + int num_of_memcpy_chans; + int num_of_phy_chans; +}; + #define D40_NAME "dma40" #define D40_PHY_CHAN -1 @@ -77,8 +105,8 @@ static int dma40_memcpy_channels[] = { DB8500_DMA_MEMCPY_EV_5, }; -/* Default configuration for physcial memcpy */ -static struct stedma40_chan_cfg dma40_memcpy_conf_phy = { +/* Default configuration for physical memcpy */ +static const struct stedma40_chan_cfg dma40_memcpy_conf_phy = { .mode = STEDMA40_MODE_PHYSICAL, .dir = DMA_MEM_TO_MEM, @@ -92,7 +120,7 @@ static struct stedma40_chan_cfg dma40_memcpy_conf_phy = { }; /* Default configuration for logical memcpy */ -static struct stedma40_chan_cfg dma40_memcpy_conf_log = { +static const struct stedma40_chan_cfg dma40_memcpy_conf_log = { .mode = STEDMA40_MODE_LOGICAL, .dir = DMA_MEM_TO_MEM, @@ -106,7 +134,7 @@ static struct stedma40_chan_cfg dma40_memcpy_conf_log = { }; /** - * enum 40_command - The different commands and/or statuses. + * enum d40_command - The different commands and/or statuses. * * @D40_DMA_STOP: DMA channel command STOP or status STOPPED, * @D40_DMA_RUN: The DMA channel is RUNNING of the command RUN. @@ -141,7 +169,7 @@ enum d40_events { * when the DMA hw is powered off. * TODO: Add save/restore of D40_DREG_GCC on dma40 v3 or later, if that works. */ -static u32 d40_backup_regs[] = { +static __maybe_unused u32 d40_backup_regs[] = { D40_DREG_LCPA, D40_DREG_LCLA, D40_DREG_PRMSE, @@ -154,7 +182,7 @@ static u32 d40_backup_regs[] = { /* * since 9540 and 8540 has the same HW revision - * use v4a for 9540 or ealier + * use v4a for 9540 or earlier * use v4b for 8540 or later * HW revision: * DB8500ed has revision 0 @@ -210,7 +238,7 @@ static u32 d40_backup_regs_v4b[] = { #define BACKUP_REGS_SZ_V4B ARRAY_SIZE(d40_backup_regs_v4b) -static u32 d40_backup_regs_chan[] = { +static __maybe_unused u32 d40_backup_regs_chan[] = { D40_CHAN_REG_SSCFG, D40_CHAN_REG_SSELT, D40_CHAN_REG_SSPTR, @@ -380,7 +408,8 @@ struct d40_desc { * struct d40_lcla_pool - LCLA pool settings and data. * * @base: The virtual address of LCLA. 18 bit aligned. - * @base_unaligned: The orignal kmalloc pointer, if kmalloc is used. + * @dma_addr: DMA address, if mapped + * @base_unaligned: The original kmalloc pointer, if kmalloc is used. * This pointer is only there for clean-up on error. * @pages: The number of pages needed for all physical channels. * Only used later for clean-up on error @@ -441,6 +470,7 @@ struct d40_base; * @queue: Queued jobs. * @prepare_queue: Prepared jobs. * @dma_cfg: The client configuration of this dma channel. + * @slave_config: DMA slave configuration. * @configured: whether the dma_cfg configuration is valid * @base: Pointer to the device instance struct. * @src_def_cfg: Default cfg register setting for src. @@ -467,6 +497,7 @@ struct d40_chan { struct list_head queue; struct list_head prepare_queue; struct stedma40_chan_cfg dma_cfg; + struct dma_slave_config slave_config; bool configured; struct d40_base *base; /* Default register configurations */ @@ -521,8 +552,6 @@ struct d40_gen_dmac { * @virtbase: The virtual base address of the DMA's register. * @rev: silicon revision detected. * @clk: Pointer to the DMA clock structure. - * @phy_start: Physical memory start of the DMA registers. - * @phy_size: Size of the DMA register map. * @irq: The IRQ number. * @num_memcpy_chans: The number of channels used for memcpy (mem-to-mem * transfers). @@ -554,8 +583,8 @@ struct d40_gen_dmac { * @reg_val_backup_v4: Backup of registers that only exits on dma40 v3 and * later * @reg_val_backup_chan: Backup data for standard channel parameter registers. + * @regs_interrupt: Scratch space for registers during interrupt. * @gcc_pwr_off_mask: Mask to maintain the channels that can be turned off. - * @initialized: true if the dma has been initialized * @gen_dmac: the struct for generic registers values to represent u8500/8540 * DMA controller */ @@ -566,13 +595,10 @@ struct d40_base { void __iomem *virtbase; u8 rev:4; struct clk *clk; - phys_addr_t phy_start; - resource_size_t phy_size; int irq; int num_memcpy_chans; int num_phy_chans; int num_log_chans; - struct device_dma_parameters dma_parms; struct dma_device dma_both; struct dma_device dma_slave; struct dma_device dma_memcpy; @@ -592,8 +618,8 @@ struct d40_base { u32 reg_val_backup[BACKUP_REGS_SZ]; u32 reg_val_backup_v4[BACKUP_REGS_SZ_MAX]; u32 *reg_val_backup_chan; + u32 *regs_interrupt; u16 gcc_pwr_off_mask; - bool initialized; struct d40_gen_dmac gen_dmac; }; @@ -624,6 +650,10 @@ static void __iomem *chan_base(struct d40_chan *chan) #define chan_err(d40c, format, arg...) \ d40_err(chan2dev(d40c), format, ## arg) +static int d40_set_runtime_config_write(struct dma_chan *chan, + struct dma_slave_config *config, + enum dma_transfer_direction direction); + static int d40_pool_lli_alloc(struct d40_chan *d40c, struct d40_desc *d40d, int lli_len) { @@ -875,7 +905,7 @@ static void d40_log_lli_to_lcxa(struct d40_chan *chan, struct d40_desc *desc) } if (curr_lcla < 0) - goto out; + goto set_current; for (; lli_current < lli_len; lli_current++) { unsigned int lcla_offset = chan->phy_chan->num * 1024 + @@ -926,8 +956,7 @@ static void d40_log_lli_to_lcxa(struct d40_chan *chan, struct d40_desc *desc) break; } } - -out: + set_current: desc->lli_current = lli_current; } @@ -942,15 +971,7 @@ static void d40_desc_load(struct d40_chan *d40c, struct d40_desc *d40d) static struct d40_desc *d40_first_active_get(struct d40_chan *d40c) { - struct d40_desc *d; - - if (list_empty(&d40c->active)) - return NULL; - - d = list_first_entry(&d40c->active, - struct d40_desc, - node); - return d; + return list_first_entry_or_null(&d40c->active, struct d40_desc, node); } /* remove desc from current queue and add it to the pending_queue */ @@ -963,36 +984,18 @@ static void d40_desc_queue(struct d40_chan *d40c, struct d40_desc *desc) static struct d40_desc *d40_first_pending(struct d40_chan *d40c) { - struct d40_desc *d; - - if (list_empty(&d40c->pending_queue)) - return NULL; - - d = list_first_entry(&d40c->pending_queue, - struct d40_desc, - node); - return d; + return list_first_entry_or_null(&d40c->pending_queue, struct d40_desc, + node); } static struct d40_desc *d40_first_queued(struct d40_chan *d40c) { - struct d40_desc *d; - - if (list_empty(&d40c->queue)) - return NULL; - - d = list_first_entry(&d40c->queue, - struct d40_desc, - node); - return d; + return list_first_entry_or_null(&d40c->queue, struct d40_desc, node); } static struct d40_desc *d40_first_done(struct d40_chan *d40c) { - if (list_empty(&d40c->done)) - return NULL; - - return list_first_entry(&d40c->done, struct d40_desc, node); + return list_first_entry_or_null(&d40c->done, struct d40_desc, node); } static int d40_psize_2_burst_size(bool is_log, int psize) @@ -1055,62 +1058,6 @@ static int d40_sg_2_dmalen(struct scatterlist *sgl, int sg_len, return len; } - -#ifdef CONFIG_PM -static void dma40_backup(void __iomem *baseaddr, u32 *backup, - u32 *regaddr, int num, bool save) -{ - int i; - - for (i = 0; i < num; i++) { - void __iomem *addr = baseaddr + regaddr[i]; - - if (save) - backup[i] = readl_relaxed(addr); - else - writel_relaxed(backup[i], addr); - } -} - -static void d40_save_restore_registers(struct d40_base *base, bool save) -{ - int i; - - /* Save/Restore channel specific registers */ - for (i = 0; i < base->num_phy_chans; i++) { - void __iomem *addr; - int idx; - - if (base->phy_res[i].reserved) - continue; - - addr = base->virtbase + D40_DREG_PCBASE + i * D40_DREG_PCDELTA; - idx = i * ARRAY_SIZE(d40_backup_regs_chan); - - dma40_backup(addr, &base->reg_val_backup_chan[idx], - d40_backup_regs_chan, - ARRAY_SIZE(d40_backup_regs_chan), - save); - } - - /* Save/Restore global registers */ - dma40_backup(base->virtbase, base->reg_val_backup, - d40_backup_regs, ARRAY_SIZE(d40_backup_regs), - save); - - /* Save/Restore registers only existing on dma40 v3 and later */ - if (base->gen_dmac.backup) - dma40_backup(base->virtbase, base->reg_val_backup_v4, - base->gen_dmac.backup, - base->gen_dmac.backup_size, - save); -} -#else -static void d40_save_restore_registers(struct d40_base *base, bool save) -{ -} -#endif - static int __d40_execute_command_phy(struct d40_chan *d40c, enum d40_command command) { @@ -1140,7 +1087,7 @@ static int __d40_execute_command_phy(struct d40_chan *d40c, D40_CHAN_POS(d40c->phy_chan->num); if (status == D40_DMA_SUSPENDED || status == D40_DMA_STOP) - goto done; + goto unlock; } wmask = 0xffffffff & ~(D40_CHAN_POS_MASK(d40c->phy_chan->num)); @@ -1176,7 +1123,7 @@ static int __d40_execute_command_phy(struct d40_chan *d40c, } } -done: + unlock: spin_unlock_irqrestore(&d40c->base->execmd_lock, flags); return ret; } @@ -1486,16 +1433,22 @@ static bool d40_tx_is_linked(struct d40_chan *d40c) return is_link; } -static int d40_pause(struct d40_chan *d40c) +static int d40_pause(struct dma_chan *chan) { + struct d40_chan *d40c = container_of(chan, struct d40_chan, chan); int res = 0; unsigned long flags; + if (d40c->phy_chan == NULL) { + chan_err(d40c, "Channel is not allocated!\n"); + return -EINVAL; + } + if (!d40c->busy) return 0; - pm_runtime_get_sync(d40c->base->dev); spin_lock_irqsave(&d40c->lock, flags); + pm_runtime_get_sync(d40c->base->dev); res = d40_channel_execute_command(d40c, D40_DMA_SUSPEND_REQ); @@ -1505,11 +1458,17 @@ static int d40_pause(struct d40_chan *d40c) return res; } -static int d40_resume(struct d40_chan *d40c) +static int d40_resume(struct dma_chan *chan) { + struct d40_chan *d40c = container_of(chan, struct d40_chan, chan); int res = 0; unsigned long flags; + if (d40c->phy_chan == NULL) { + chan_err(d40c, "Channel is not allocated!\n"); + return -EINVAL; + } + if (!d40c->busy) return 0; @@ -1635,13 +1594,13 @@ static void dma_tc_handle(struct d40_chan *d40c) } -static void dma_tasklet(unsigned long data) +static void dma_tasklet(struct tasklet_struct *t) { - struct d40_chan *d40c = (struct d40_chan *) data; + struct d40_chan *d40c = from_tasklet(d40c, t, tasklet); struct d40_desc *d40d; unsigned long flags; - dma_async_tx_callback callback; - void *callback_param; + bool callback_active; + struct dmaengine_desc_callback cb; spin_lock_irqsave(&d40c->lock, flags); @@ -1651,7 +1610,7 @@ static void dma_tasklet(unsigned long data) /* Check if we have reached here for cyclic job */ d40d = d40_first_active_get(d40c); if (d40d == NULL || !d40d->cyclic) - goto err; + goto check_pending_tx; } if (!d40d->cyclic) @@ -1667,8 +1626,8 @@ static void dma_tasklet(unsigned long data) } /* Callback to client */ - callback = d40d->txd.callback; - callback_param = d40d->txd.callback_param; + callback_active = !!(d40d->txd.flags & DMA_PREP_INTERRUPT); + dmaengine_desc_get_callback(&d40d->txd, &cb); if (!d40d->cyclic) { if (async_tx_test_ack(&d40d->txd)) { @@ -1689,13 +1648,12 @@ static void dma_tasklet(unsigned long data) spin_unlock_irqrestore(&d40c->lock, flags); - if (callback && (d40d->txd.flags & DMA_PREP_INTERRUPT)) - callback(callback_param); + if (callback_active) + dmaengine_desc_callback_invoke(&cb, NULL); return; - -err: - /* Rescue manouver if receiving double interrupts */ + check_pending_tx: + /* Rescue maneuver if receiving double interrupts */ if (d40c->pending_tx > 0) d40c->pending_tx--; spin_unlock_irqrestore(&d40c->lock, flags); @@ -1708,13 +1666,12 @@ static irqreturn_t d40_handle_interrupt(int irq, void *data) u32 row; long chan = -1; struct d40_chan *d40c; - unsigned long flags; struct d40_base *base = data; - u32 regs[base->gen_dmac.il_size]; + u32 *regs = base->regs_interrupt; struct d40_interrupt_lookup *il = base->gen_dmac.il; u32 il_size = base->gen_dmac.il_size; - spin_lock_irqsave(&base->interrupt_lock, flags); + spin_lock(&base->interrupt_lock); /* Read interrupt status of both logical and physical channels */ for (i = 0; i < il_size; i++) @@ -1759,7 +1716,7 @@ static irqreturn_t d40_handle_interrupt(int irq, void *data) spin_unlock(&d40c->lock); } - spin_unlock_irqrestore(&base->interrupt_lock, flags); + spin_unlock(&base->interrupt_lock); return IRQ_HANDLED; } @@ -1823,42 +1780,40 @@ static bool d40_alloc_mask_set(struct d40_phy_res *phy, phy->allocated_dst == D40_ALLOC_FREE) { phy->allocated_dst = D40_ALLOC_PHY; phy->allocated_src = D40_ALLOC_PHY; - goto found; + goto found_unlock; } else - goto not_found; + goto not_found_unlock; } /* Logical channel */ if (is_src) { if (phy->allocated_src == D40_ALLOC_PHY) - goto not_found; + goto not_found_unlock; if (phy->allocated_src == D40_ALLOC_FREE) phy->allocated_src = D40_ALLOC_LOG_FREE; if (!(phy->allocated_src & BIT(log_event_line))) { phy->allocated_src |= BIT(log_event_line); - goto found; + goto found_unlock; } else - goto not_found; + goto not_found_unlock; } else { if (phy->allocated_dst == D40_ALLOC_PHY) - goto not_found; + goto not_found_unlock; if (phy->allocated_dst == D40_ALLOC_FREE) phy->allocated_dst = D40_ALLOC_LOG_FREE; if (!(phy->allocated_dst & BIT(log_event_line))) { phy->allocated_dst |= BIT(log_event_line); - goto found; - } else - goto not_found; + goto found_unlock; + } } - -not_found: + not_found_unlock: spin_unlock_irqrestore(&phy->lock, flags); return false; -found: + found_unlock: spin_unlock_irqrestore(&phy->lock, flags); return true; } @@ -1874,7 +1829,7 @@ static bool d40_alloc_mask_free(struct d40_phy_res *phy, bool is_src, phy->allocated_dst = D40_ALLOC_FREE; phy->allocated_src = D40_ALLOC_FREE; is_free = true; - goto out; + goto unlock; } /* Logical channel */ @@ -1890,8 +1845,7 @@ static bool d40_alloc_mask_free(struct d40_phy_res *phy, bool is_src, is_free = ((phy->allocated_src | phy->allocated_dst) == D40_ALLOC_FREE); - -out: + unlock: spin_unlock_irqrestore(&phy->lock, flags); return is_free; @@ -2039,7 +1993,7 @@ static int d40_config_memcpy(struct d40_chan *d40c) dma_has_cap(DMA_SLAVE, cap)) { d40c->dma_cfg = dma40_memcpy_conf_phy; - /* Generate interrrupt at end of transfer or relink. */ + /* Generate interrupt at end of transfer or relink. */ d40c->dst_def_cfg |= BIT(D40_SREG_CFG_TIM_POS); /* Generate interrupt on error. */ @@ -2090,7 +2044,7 @@ static int d40_free_dma(struct d40_chan *d40c) res = d40_channel_execute_command(d40c, D40_DMA_STOP); if (res) { chan_err(d40c, "stop failed\n"); - goto out; + goto mark_last_busy; } d40_alloc_mask_free(phy, is_src, chan_is_logical(d40c) ? event : 0); @@ -2108,8 +2062,7 @@ static int d40_free_dma(struct d40_chan *d40c) d40c->busy = false; d40c->phy_chan = NULL; d40c->configured = false; -out: - + mark_last_busy: pm_runtime_mark_last_busy(d40c->base->dev); pm_runtime_put_autosuspend(d40c->base->dev); return res; @@ -2137,8 +2090,7 @@ static bool d40_is_paused(struct d40_chan *d40c) D40_CHAN_POS(d40c->phy_chan->num); if (status == D40_DMA_SUSPENDED || status == D40_DMA_STOP) is_paused = true; - - goto _exit; + goto unlock; } if (d40c->dma_cfg.dir == DMA_MEM_TO_DEV || @@ -2148,7 +2100,7 @@ static bool d40_is_paused(struct d40_chan *d40c) status = readl(chanbase + D40_CHAN_REG_SSLNK); } else { chan_err(d40c, "Unknown direction\n"); - goto _exit; + goto unlock; } status = (status & D40_EVENTLINE_MASK(event)) >> @@ -2156,7 +2108,7 @@ static bool d40_is_paused(struct d40_chan *d40c) if (status != D40_DMA_RUN) is_paused = true; -_exit: + unlock: spin_unlock_irqrestore(&d40c->lock, flags); return is_paused; @@ -2241,7 +2193,7 @@ static struct d40_desc * d40_prep_desc(struct d40_chan *chan, struct scatterlist *sg, unsigned int sg_len, unsigned long dma_flags) { - struct stedma40_chan_cfg *cfg = &chan->dma_cfg; + struct stedma40_chan_cfg *cfg; struct d40_desc *desc; int ret; @@ -2249,17 +2201,18 @@ d40_prep_desc(struct d40_chan *chan, struct scatterlist *sg, if (!desc) return NULL; + cfg = &chan->dma_cfg; desc->lli_len = d40_sg_2_dmalen(sg, sg_len, cfg->src_info.data_width, cfg->dst_info.data_width); if (desc->lli_len < 0) { chan_err(chan, "Unaligned size\n"); - goto err; + goto free_desc; } ret = d40_pool_lli_alloc(chan, desc, desc->lli_len); if (ret < 0) { chan_err(chan, "Could not allocate lli\n"); - goto err; + goto free_desc; } desc->lli_current = 0; @@ -2269,8 +2222,7 @@ d40_prep_desc(struct d40_chan *chan, struct scatterlist *sg, dma_async_tx_descriptor_init(&desc->txd, &chan->chan); return desc; - -err: + free_desc: d40_desc_free(chan, desc); return NULL; } @@ -2281,8 +2233,8 @@ d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src, enum dma_transfer_direction direction, unsigned long dma_flags) { struct d40_chan *chan = container_of(dchan, struct d40_chan, chan); - dma_addr_t src_dev_addr = 0; - dma_addr_t dst_dev_addr = 0; + dma_addr_t src_dev_addr; + dma_addr_t dst_dev_addr; struct d40_desc *desc; unsigned long flags; int ret; @@ -2292,15 +2244,19 @@ d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src, return NULL; } + d40_set_runtime_config_write(dchan, &chan->slave_config, direction); + spin_lock_irqsave(&chan->lock, flags); desc = d40_prep_desc(chan, sg_src, sg_len, dma_flags); if (desc == NULL) - goto err; + goto unlock; if (sg_next(&sg_src[sg_len - 1]) == sg_src) desc->cyclic = true; + src_dev_addr = 0; + dst_dev_addr = 0; if (direction == DMA_DEV_TO_MEM) src_dev_addr = chan->runtime_addr; else if (direction == DMA_MEM_TO_DEV) @@ -2316,7 +2272,7 @@ d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src, if (ret) { chan_err(chan, "Failed to prepare %s sg job: %d\n", chan_is_logical(chan) ? "log" : "phy", ret); - goto err; + goto free_desc; } /* @@ -2328,15 +2284,14 @@ d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src, spin_unlock_irqrestore(&chan->lock, flags); return &desc->txd; - -err: - if (desc) - d40_desc_free(chan, desc); + free_desc: + d40_desc_free(chan, desc); + unlock: spin_unlock_irqrestore(&chan->lock, flags); return NULL; } -bool stedma40_filter(struct dma_chan *chan, void *data) +static bool stedma40_filter(struct dma_chan *chan, void *data) { struct stedma40_chan_cfg *info = data; struct d40_chan *d40c = @@ -2355,7 +2310,6 @@ bool stedma40_filter(struct dma_chan *chan, void *data) return err == 0; } -EXPORT_SYMBOL(stedma40_filter); static void __d40_set_prio_rt(struct d40_chan *d40c, int dev_type, bool src) { @@ -2408,6 +2362,7 @@ static void d40_set_prio_realtime(struct d40_chan *d40c) #define D40_DT_FLAGS_DIR(flags) ((flags >> 1) & 0x1) #define D40_DT_FLAGS_BIG_ENDIAN(flags) ((flags >> 2) & 0x1) #define D40_DT_FLAGS_FIXED_CHAN(flags) ((flags >> 3) & 0x1) +#define D40_DT_FLAGS_HIGH_PRIO(flags) ((flags >> 4) & 0x1) static struct dma_chan *d40_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) @@ -2445,6 +2400,9 @@ static struct dma_chan *d40_xlate(struct of_phandle_args *dma_spec, cfg.use_fixed_channel = true; } + if (D40_DT_FLAGS_HIGH_PRIO(flags)) + cfg.high_priority = true; + return dma_request_channel(cap, stedma40_filter, &cfg); } @@ -2465,7 +2423,7 @@ static int d40_alloc_chan_resources(struct dma_chan *chan) err = d40_config_memcpy(d40c); if (err) { chan_err(d40c, "Failed to configure memcpy channel\n"); - goto fail; + goto mark_last_busy; } } @@ -2473,7 +2431,7 @@ static int d40_alloc_chan_resources(struct dma_chan *chan) if (err) { chan_err(d40c, "Failed to allocate channel\n"); d40c->configured = false; - goto fail; + goto mark_last_busy; } pm_runtime_get_sync(d40c->base->dev); @@ -2507,7 +2465,7 @@ static int d40_alloc_chan_resources(struct dma_chan *chan) */ if (is_free_phy) d40_config_write(d40c); -fail: + mark_last_busy: pm_runtime_mark_last_busy(d40c->base->dev); pm_runtime_put_autosuspend(d40c->base->dev); spin_unlock_irqrestore(&d40c->lock, flags); @@ -2553,19 +2511,8 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan, sg_dma_len(&dst_sg) = size; sg_dma_len(&src_sg) = size; - return d40_prep_sg(chan, &src_sg, &dst_sg, 1, DMA_NONE, dma_flags); -} - -static struct dma_async_tx_descriptor * -d40_prep_memcpy_sg(struct dma_chan *chan, - struct scatterlist *dst_sg, unsigned int dst_nents, - struct scatterlist *src_sg, unsigned int src_nents, - unsigned long dma_flags) -{ - if (dst_nents != src_nents) - return NULL; - - return d40_prep_sg(chan, src_sg, dst_sg, src_nents, DMA_NONE, dma_flags); + return d40_prep_sg(chan, &src_sg, &dst_sg, 1, + DMA_MEM_TO_MEM, dma_flags); } static struct dma_async_tx_descriptor * @@ -2582,8 +2529,7 @@ d40_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, static struct dma_async_tx_descriptor * dma40_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, size_t period_len, - enum dma_transfer_direction direction, unsigned long flags, - void *context) + enum dma_transfer_direction direction, unsigned long flags) { unsigned int periods = buf_len / period_len; struct dma_async_tx_descriptor *txd; @@ -2591,16 +2537,16 @@ dma40_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr, int i; sg = kcalloc(periods + 1, sizeof(struct scatterlist), GFP_NOWAIT); + if (!sg) + return NULL; + for (i = 0; i < periods; i++) { sg_dma_address(&sg[i]) = dma_addr; sg_dma_len(&sg[i]) = period_len; dma_addr += period_len; } - sg[periods].offset = 0; - sg_dma_len(&sg[periods]) = 0; - sg[periods].page_link = - ((unsigned long)sg | 0x01) & ~0x02; + sg_chain(sg, periods + 1, sg); txd = d40_prep_sg(chan, sg, sg, periods, direction, DMA_PREP_INTERRUPT); @@ -2623,7 +2569,7 @@ static enum dma_status d40_tx_status(struct dma_chan *chan, } ret = dma_cookie_status(chan, cookie, txstate); - if (ret != DMA_SUCCESS) + if (ret != DMA_COMPLETE && txstate) dma_set_residue(txstate, stedma40_residue(chan)); if (d40_is_paused(d40c)) @@ -2653,12 +2599,17 @@ static void d40_issue_pending(struct dma_chan *chan) spin_unlock_irqrestore(&d40c->lock, flags); } -static void d40_terminate_all(struct dma_chan *chan) +static int d40_terminate_all(struct dma_chan *chan) { unsigned long flags; struct d40_chan *d40c = container_of(chan, struct d40_chan, chan); int ret; + if (d40c->phy_chan == NULL) { + chan_err(d40c, "Channel is not allocated!\n"); + return -EINVAL; + } + spin_lock_irqsave(&d40c->lock, flags); pm_runtime_get_sync(d40c->base->dev); @@ -2676,6 +2627,7 @@ static void d40_terminate_all(struct dma_chan *chan) d40c->busy = false; spin_unlock_irqrestore(&d40c->lock, flags); + return 0; } static int @@ -2711,23 +2663,39 @@ dma40_config_to_halfchannel(struct d40_chan *d40c, return 0; } -/* Runtime reconfiguration extension */ static int d40_set_runtime_config(struct dma_chan *chan, struct dma_slave_config *config) { struct d40_chan *d40c = container_of(chan, struct d40_chan, chan); + + memcpy(&d40c->slave_config, config, sizeof(*config)); + + return 0; +} + +/* Runtime reconfiguration extension */ +static int d40_set_runtime_config_write(struct dma_chan *chan, + struct dma_slave_config *config, + enum dma_transfer_direction direction) +{ + struct d40_chan *d40c = container_of(chan, struct d40_chan, chan); struct stedma40_chan_cfg *cfg = &d40c->dma_cfg; enum dma_slave_buswidth src_addr_width, dst_addr_width; dma_addr_t config_addr; u32 src_maxburst, dst_maxburst; int ret; + if (d40c->phy_chan == NULL) { + chan_err(d40c, "Channel is not allocated!\n"); + return -EINVAL; + } + src_addr_width = config->src_addr_width; src_maxburst = config->src_maxburst; dst_addr_width = config->dst_addr_width; dst_maxburst = config->dst_maxburst; - if (config->direction == DMA_DEV_TO_MEM) { + if (direction == DMA_DEV_TO_MEM) { config_addr = config->src_addr; if (cfg->dir != DMA_DEV_TO_MEM) @@ -2743,7 +2711,7 @@ static int d40_set_runtime_config(struct dma_chan *chan, if (dst_maxburst == 0) dst_maxburst = src_maxburst; - } else if (config->direction == DMA_MEM_TO_DEV) { + } else if (direction == DMA_MEM_TO_DEV) { config_addr = config->dst_addr; if (cfg->dir != DMA_MEM_TO_DEV) @@ -2761,7 +2729,7 @@ static int d40_set_runtime_config(struct dma_chan *chan, } else { dev_err(d40c->base->dev, "unrecognized channel direction %d\n", - config->direction); + direction); return -EINVAL; } @@ -2793,8 +2761,8 @@ static int d40_set_runtime_config(struct dma_chan *chan, src_addr_width > DMA_SLAVE_BUSWIDTH_8_BYTES || dst_addr_width <= DMA_SLAVE_BUSWIDTH_UNDEFINED || dst_addr_width > DMA_SLAVE_BUSWIDTH_8_BYTES || - ((src_addr_width > 1) && (src_addr_width & 1)) || - ((dst_addr_width > 1) && (dst_addr_width & 1))) + !is_power_of_2(src_addr_width) || + !is_power_of_2(dst_addr_width)) return -EINVAL; cfg->src_info.data_width = src_addr_width; @@ -2818,47 +2786,18 @@ static int d40_set_runtime_config(struct dma_chan *chan, /* These settings will take precedence later */ d40c->runtime_addr = config_addr; - d40c->runtime_direction = config->direction; + d40c->runtime_direction = direction; dev_dbg(d40c->base->dev, "configured channel %s for %s, data width %d/%d, " "maxburst %d/%d elements, LE, no flow control\n", dma_chan_name(chan), - (config->direction == DMA_DEV_TO_MEM) ? "RX" : "TX", + (direction == DMA_DEV_TO_MEM) ? "RX" : "TX", src_addr_width, dst_addr_width, src_maxburst, dst_maxburst); return 0; } -static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) -{ - struct d40_chan *d40c = container_of(chan, struct d40_chan, chan); - - if (d40c->phy_chan == NULL) { - chan_err(d40c, "Channel is not allocated!\n"); - return -EINVAL; - } - - switch (cmd) { - case DMA_TERMINATE_ALL: - d40_terminate_all(chan); - return 0; - case DMA_PAUSE: - return d40_pause(d40c); - case DMA_RESUME: - return d40_resume(d40c); - case DMA_SLAVE_CONFIG: - return d40_set_runtime_config(chan, - (struct dma_slave_config *) arg); - default: - break; - } - - /* Other commands are unimplemented */ - return -ENXIO; -} - /* Initialization functions */ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma, @@ -2886,8 +2825,7 @@ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma, INIT_LIST_HEAD(&d40c->client); INIT_LIST_HEAD(&d40c->prepare_queue); - tasklet_init(&d40c->tasklet, dma_tasklet, - (unsigned long) d40c); + tasklet_setup(&d40c->tasklet, dma_tasklet); list_add_tail(&d40c->chan.device_node, &dma->channels); @@ -2896,22 +2834,21 @@ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma, static void d40_ops_init(struct d40_base *base, struct dma_device *dev) { - if (dma_has_cap(DMA_SLAVE, dev->cap_mask)) + if (dma_has_cap(DMA_SLAVE, dev->cap_mask)) { dev->device_prep_slave_sg = d40_prep_slave_sg; + dev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + } if (dma_has_cap(DMA_MEMCPY, dev->cap_mask)) { dev->device_prep_dma_memcpy = d40_prep_memcpy; - + dev->directions = BIT(DMA_MEM_TO_MEM); /* * This controller can only access address at even * 32bit boundaries, i.e. 2^2 */ - dev->copy_align = 2; + dev->copy_align = DMAENGINE_ALIGN_4_BYTES; } - if (dma_has_cap(DMA_SG, dev->cap_mask)) - dev->device_prep_dma_sg = d40_prep_memcpy_sg; - if (dma_has_cap(DMA_CYCLIC, dev->cap_mask)) dev->device_prep_dma_cyclic = dma40_prep_dma_cyclic; @@ -2919,7 +2856,11 @@ static void d40_ops_init(struct d40_base *base, struct dma_device *dev) dev->device_free_chan_resources = d40_free_chan_resources; dev->device_issue_pending = d40_issue_pending; dev->device_tx_status = d40_tx_status; - dev->device_control = d40_control; + dev->device_config = d40_set_runtime_config; + dev->device_pause = d40_pause; + dev->device_resume = d40_resume; + dev->device_terminate_all = d40_terminate_all; + dev->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; dev->dev = base->dev; } @@ -2937,11 +2878,11 @@ static int __init d40_dmaengine_init(struct d40_base *base, d40_ops_init(base, &base->dma_slave); - err = dma_async_device_register(&base->dma_slave); + err = dmaenginem_async_device_register(&base->dma_slave); if (err) { d40_err(base->dev, "Failed to register slave channels\n"); - goto failure1; + goto exit; } d40_chan_init(base, &base->dma_memcpy, base->log_chans, @@ -2949,16 +2890,15 @@ static int __init d40_dmaengine_init(struct d40_base *base, dma_cap_zero(base->dma_memcpy.cap_mask); dma_cap_set(DMA_MEMCPY, base->dma_memcpy.cap_mask); - dma_cap_set(DMA_SG, base->dma_memcpy.cap_mask); d40_ops_init(base, &base->dma_memcpy); - err = dma_async_device_register(&base->dma_memcpy); + err = dmaenginem_async_device_register(&base->dma_memcpy); if (err) { d40_err(base->dev, - "Failed to regsiter memcpy only channels\n"); - goto failure2; + "Failed to register memcpy only channels\n"); + goto exit; } d40_chan_init(base, &base->dma_both, base->phy_chans, @@ -2967,43 +2907,105 @@ static int __init d40_dmaengine_init(struct d40_base *base, dma_cap_zero(base->dma_both.cap_mask); dma_cap_set(DMA_SLAVE, base->dma_both.cap_mask); dma_cap_set(DMA_MEMCPY, base->dma_both.cap_mask); - dma_cap_set(DMA_SG, base->dma_both.cap_mask); dma_cap_set(DMA_CYCLIC, base->dma_slave.cap_mask); d40_ops_init(base, &base->dma_both); - err = dma_async_device_register(&base->dma_both); + err = dmaenginem_async_device_register(&base->dma_both); if (err) { d40_err(base->dev, "Failed to register logical and physical capable channels\n"); - goto failure3; + goto exit; } return 0; -failure3: - dma_async_device_unregister(&base->dma_memcpy); -failure2: - dma_async_device_unregister(&base->dma_slave); -failure1: + exit: return err; } /* Suspend resume functionality */ -#ifdef CONFIG_PM -static int dma40_pm_suspend(struct device *dev) +#ifdef CONFIG_PM_SLEEP +static int dma40_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct d40_base *base = platform_get_drvdata(pdev); - int ret = 0; + struct d40_base *base = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_suspend(dev); + if (ret) + return ret; if (base->lcpa_regulator) ret = regulator_disable(base->lcpa_regulator); return ret; } +static int dma40_resume(struct device *dev) +{ + struct d40_base *base = dev_get_drvdata(dev); + int ret = 0; + + if (base->lcpa_regulator) { + ret = regulator_enable(base->lcpa_regulator); + if (ret) + return ret; + } + + return pm_runtime_force_resume(dev); +} +#endif + +#ifdef CONFIG_PM +static void dma40_backup(void __iomem *baseaddr, u32 *backup, + u32 *regaddr, int num, bool save) +{ + int i; + + for (i = 0; i < num; i++) { + void __iomem *addr = baseaddr + regaddr[i]; + + if (save) + backup[i] = readl_relaxed(addr); + else + writel_relaxed(backup[i], addr); + } +} + +static void d40_save_restore_registers(struct d40_base *base, bool save) +{ + int i; + + /* Save/Restore channel specific registers */ + for (i = 0; i < base->num_phy_chans; i++) { + void __iomem *addr; + int idx; + + if (base->phy_res[i].reserved) + continue; + + addr = base->virtbase + D40_DREG_PCBASE + i * D40_DREG_PCDELTA; + idx = i * ARRAY_SIZE(d40_backup_regs_chan); + + dma40_backup(addr, &base->reg_val_backup_chan[idx], + d40_backup_regs_chan, + ARRAY_SIZE(d40_backup_regs_chan), + save); + } + + /* Save/Restore global registers */ + dma40_backup(base->virtbase, base->reg_val_backup, + d40_backup_regs, ARRAY_SIZE(d40_backup_regs), + save); + + /* Save/Restore registers only existing on dma40 v3 and later */ + if (base->gen_dmac.backup) + dma40_backup(base->virtbase, base->reg_val_backup_v4, + base->gen_dmac.backup, + base->gen_dmac.backup_size, + save); +} + static int dma40_runtime_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct d40_base *base = platform_get_drvdata(pdev); + struct d40_base *base = dev_get_drvdata(dev); d40_save_restore_registers(base, true); @@ -3017,39 +3019,22 @@ static int dma40_runtime_suspend(struct device *dev) static int dma40_runtime_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct d40_base *base = platform_get_drvdata(pdev); + struct d40_base *base = dev_get_drvdata(dev); - if (base->initialized) - d40_save_restore_registers(base, false); + d40_save_restore_registers(base, false); writel_relaxed(D40_DREG_GCC_ENABLE_ALL, base->virtbase + D40_DREG_GCC); return 0; } - -static int dma40_resume(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct d40_base *base = platform_get_drvdata(pdev); - int ret = 0; - - if (base->lcpa_regulator) - ret = regulator_enable(base->lcpa_regulator); - - return ret; -} +#endif static const struct dev_pm_ops dma40_pm_ops = { - .suspend = dma40_pm_suspend, - .runtime_suspend = dma40_runtime_suspend, - .runtime_resume = dma40_runtime_resume, - .resume = dma40_resume, + SET_LATE_SYSTEM_SLEEP_PM_OPS(dma40_suspend, dma40_resume) + SET_RUNTIME_PM_OPS(dma40_runtime_suspend, + dma40_runtime_resume, + NULL) }; -#define DMA40_PM_OPS (&dma40_pm_ops) -#else -#define DMA40_PM_OPS NULL -#endif /* Initialization functions. */ @@ -3137,64 +3122,57 @@ static int __init d40_phy_res_init(struct d40_base *base) return num_phy_chans_avail; } -static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) +/* Called from the registered devm action */ +static void d40_drop_kmem_cache_action(void *d) +{ + struct kmem_cache *desc_slab = d; + + kmem_cache_destroy(desc_slab); +} + +static int __init d40_hw_detect_init(struct platform_device *pdev, + struct d40_base **retbase) { - struct stedma40_platform_data *plat_data = pdev->dev.platform_data; - struct clk *clk = NULL; - void __iomem *virtbase = NULL; - struct resource *res = NULL; - struct d40_base *base = NULL; - int num_log_chans = 0; + struct stedma40_platform_data *plat_data = dev_get_platdata(&pdev->dev); + struct device *dev = &pdev->dev; + struct clk *clk; + void __iomem *virtbase; + struct d40_base *base; + int num_log_chans; int num_phy_chans; int num_memcpy_chans; - int clk_ret = -EINVAL; int i; u32 pid; u32 cid; u8 rev; + int ret; - clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) { - d40_err(&pdev->dev, "No matching clock found\n"); - goto failure; - } - - clk_ret = clk_prepare_enable(clk); - if (clk_ret) { - d40_err(&pdev->dev, "Failed to prepare/enable clock\n"); - goto failure; - } + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); /* Get IO for DMAC base address */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base"); - if (!res) - goto failure; - - if (request_mem_region(res->start, resource_size(res), - D40_NAME " I/O base") == NULL) - goto failure; - - virtbase = ioremap(res->start, resource_size(res)); - if (!virtbase) - goto failure; + virtbase = devm_platform_ioremap_resource_byname(pdev, "base"); + if (IS_ERR(virtbase)) + return PTR_ERR(virtbase); /* This is just a regular AMBA PrimeCell ID actually */ for (pid = 0, i = 0; i < 4; i++) - pid |= (readl(virtbase + resource_size(res) - 0x20 + 4 * i) + pid |= (readl(virtbase + SZ_4K - 0x20 + 4 * i) & 255) << (i * 8); for (cid = 0, i = 0; i < 4; i++) - cid |= (readl(virtbase + resource_size(res) - 0x10 + 4 * i) + cid |= (readl(virtbase + SZ_4K - 0x10 + 4 * i) & 255) << (i * 8); if (cid != AMBA_CID) { - d40_err(&pdev->dev, "Unknown hardware! No PrimeCell ID\n"); - goto failure; + d40_err(dev, "Unknown hardware! No PrimeCell ID\n"); + return -EINVAL; } if (AMBA_MANF_BITS(pid) != AMBA_VENDOR_ST) { - d40_err(&pdev->dev, "Unknown designer! Got %x wanted %x\n", + d40_err(dev, "Unknown designer! Got %x wanted %x\n", AMBA_MANF_BITS(pid), AMBA_VENDOR_ST); - goto failure; + return -EINVAL; } /* * HW revision: @@ -3207,8 +3185,8 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) */ rev = AMBA_REV_BITS(pid); if (rev < 2) { - d40_err(&pdev->dev, "hardware revision: %d is not supported", rev); - goto failure; + d40_err(dev, "hardware revision: %d is not supported", rev); + return -EINVAL; } /* The number of physical channels on this HW */ @@ -3225,29 +3203,26 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) num_log_chans = num_phy_chans * D40_MAX_LOG_CHAN_PER_PHY; - dev_info(&pdev->dev, - "hardware rev: %d @ 0x%x with %d physical and %d logical channels\n", - rev, res->start, num_phy_chans, num_log_chans); + dev_info(dev, + "hardware rev: %d with %d physical and %d logical channels\n", + rev, num_phy_chans, num_log_chans); - base = kzalloc(ALIGN(sizeof(struct d40_base), 4) + - (num_phy_chans + num_log_chans + num_memcpy_chans) * - sizeof(struct d40_chan), GFP_KERNEL); + base = devm_kzalloc(dev, + ALIGN(sizeof(struct d40_base), 4) + + (num_phy_chans + num_log_chans + num_memcpy_chans) * + sizeof(struct d40_chan), GFP_KERNEL); - if (base == NULL) { - d40_err(&pdev->dev, "Out of memory\n"); - goto failure; - } + if (!base) + return -ENOMEM; base->rev = rev; base->clk = clk; base->num_memcpy_chans = num_memcpy_chans; base->num_phy_chans = num_phy_chans; base->num_log_chans = num_log_chans; - base->phy_start = res->start; - base->phy_size = resource_size(res); base->virtbase = virtbase; base->plat_data = plat_data; - base->dev = &pdev->dev; + base->dev = dev; base->phy_chans = ((void *)base) + ALIGN(sizeof(struct d40_base), 4); base->log_chans = &base->phy_chans[num_phy_chans]; @@ -3281,66 +3256,57 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) base->gen_dmac.init_reg_size = ARRAY_SIZE(dma_init_reg_v4a); } - base->phy_res = kzalloc(num_phy_chans * sizeof(struct d40_phy_res), - GFP_KERNEL); + base->phy_res = devm_kcalloc(dev, num_phy_chans, + sizeof(*base->phy_res), + GFP_KERNEL); if (!base->phy_res) - goto failure; + return -ENOMEM; - base->lookup_phy_chans = kzalloc(num_phy_chans * - sizeof(struct d40_chan *), - GFP_KERNEL); + base->lookup_phy_chans = devm_kcalloc(dev, num_phy_chans, + sizeof(*base->lookup_phy_chans), + GFP_KERNEL); if (!base->lookup_phy_chans) - goto failure; + return -ENOMEM; - base->lookup_log_chans = kzalloc(num_log_chans * - sizeof(struct d40_chan *), - GFP_KERNEL); + base->lookup_log_chans = devm_kcalloc(dev, num_log_chans, + sizeof(*base->lookup_log_chans), + GFP_KERNEL); if (!base->lookup_log_chans) - goto failure; + return -ENOMEM; - base->reg_val_backup_chan = kmalloc(base->num_phy_chans * - sizeof(d40_backup_regs_chan), - GFP_KERNEL); + base->reg_val_backup_chan = devm_kmalloc_array(dev, base->num_phy_chans, + sizeof(d40_backup_regs_chan), + GFP_KERNEL); if (!base->reg_val_backup_chan) - goto failure; + return -ENOMEM; - base->lcla_pool.alloc_map = - kzalloc(num_phy_chans * sizeof(struct d40_desc *) - * D40_LCLA_LINK_PER_EVENT_GRP, GFP_KERNEL); + base->lcla_pool.alloc_map = devm_kcalloc(dev, num_phy_chans + * D40_LCLA_LINK_PER_EVENT_GRP, + sizeof(*base->lcla_pool.alloc_map), + GFP_KERNEL); if (!base->lcla_pool.alloc_map) - goto failure; + return -ENOMEM; + + base->regs_interrupt = devm_kmalloc_array(dev, base->gen_dmac.il_size, + sizeof(*base->regs_interrupt), + GFP_KERNEL); + if (!base->regs_interrupt) + return -ENOMEM; base->desc_slab = kmem_cache_create(D40_NAME, sizeof(struct d40_desc), 0, SLAB_HWCACHE_ALIGN, NULL); - if (base->desc_slab == NULL) - goto failure; - - return base; - -failure: - if (!clk_ret) - clk_disable_unprepare(clk); - if (!IS_ERR(clk)) - clk_put(clk); - if (virtbase) - iounmap(virtbase); - if (res) - release_mem_region(res->start, - resource_size(res)); - if (virtbase) - iounmap(virtbase); - - if (base) { - kfree(base->lcla_pool.alloc_map); - kfree(base->reg_val_backup_chan); - kfree(base->lookup_log_chans); - kfree(base->lookup_phy_chans); - kfree(base->phy_res); - kfree(base); - } + if (!base->desc_slab) + return -ENOMEM; - return NULL; + ret = devm_add_action_or_reset(dev, d40_drop_kmem_cache_action, + base->desc_slab); + if (ret) + return ret; + + *retbase = base; + + return 0; } static void __init d40_hw_init(struct d40_base *base) @@ -3402,20 +3368,18 @@ static int __init d40_lcla_allocate(struct d40_base *base) struct d40_lcla_pool *pool = &base->lcla_pool; unsigned long *page_list; int i, j; - int ret = 0; + int ret; /* * This is somewhat ugly. We need 8192 bytes that are 18 bit aligned, * To full fill this hardware requirement without wasting 256 kb * we allocate pages until we get an aligned one. */ - page_list = kmalloc(sizeof(unsigned long) * MAX_LCLA_ALLOC_ATTEMPTS, - GFP_KERNEL); - - if (!page_list) { - ret = -ENOMEM; - goto failure; - } + page_list = kmalloc_array(MAX_LCLA_ALLOC_ATTEMPTS, + sizeof(*page_list), + GFP_KERNEL); + if (!page_list) + return -ENOMEM; /* Calculating how many pages that are required */ base->lcla_pool.pages = SZ_1K * base->num_phy_chans / PAGE_SIZE; @@ -3427,10 +3391,11 @@ static int __init d40_lcla_allocate(struct d40_base *base) d40_err(base->dev, "Failed to allocate %d pages.\n", base->lcla_pool.pages); + ret = -ENOMEM; for (j = 0; j < i; j++) free_pages(page_list[j], base->lcla_pool.pages); - goto failure; + goto free_page_list; } if ((virt_to_phys((void *)page_list[i]) & @@ -3445,7 +3410,7 @@ static int __init d40_lcla_allocate(struct d40_base *base) base->lcla_pool.base = (void *)page_list[i]; } else { /* - * After many attempts and no succees with finding the correct + * After many attempts and no success with finding the correct * alignment, try with allocating a big buffer. */ dev_warn(base->dev, @@ -3457,7 +3422,7 @@ static int __init d40_lcla_allocate(struct d40_base *base) GFP_KERNEL); if (!base->lcla_pool.base_unaligned) { ret = -ENOMEM; - goto failure; + goto free_page_list; } base->lcla_pool.base = PTR_ALIGN(base->lcla_pool.base_unaligned, @@ -3470,26 +3435,25 @@ static int __init d40_lcla_allocate(struct d40_base *base) if (dma_mapping_error(base->dev, pool->dma_addr)) { pool->dma_addr = 0; ret = -ENOMEM; - goto failure; + goto free_page_list; } writel(virt_to_phys(base->lcla_pool.base), base->virtbase + D40_DREG_LCLA); -failure: + ret = 0; + free_page_list: kfree(page_list); return ret; } -static int __init d40_of_probe(struct platform_device *pdev, +static int __init d40_of_probe(struct device *dev, struct device_node *np) { struct stedma40_platform_data *pdata; int num_phy = 0, num_memcpy = 0, num_disabled = 0; - const const __be32 *list; + const __be32 *list; - pdata = devm_kzalloc(&pdev->dev, - sizeof(struct stedma40_platform_data), - GFP_KERNEL); + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; @@ -3502,7 +3466,7 @@ static int __init d40_of_probe(struct platform_device *pdev, num_memcpy /= sizeof(*list); if (num_memcpy > D40_MEMCPY_MAX_CHANS || num_memcpy <= 0) { - d40_err(&pdev->dev, + d40_err(dev, "Invalid number of memcpy channels specified (%d)\n", num_memcpy); return -EINVAL; @@ -3516,8 +3480,8 @@ static int __init d40_of_probe(struct platform_device *pdev, list = of_get_property(np, "disabled-channels", &num_disabled); num_disabled /= sizeof(*list); - if (num_disabled > STEDMA40_MAX_PHYS || num_disabled < 0) { - d40_err(&pdev->dev, + if (num_disabled >= STEDMA40_MAX_PHYS || num_disabled < 0) { + d40_err(dev, "Invalid number of disabled channels specified (%d)\n", num_disabled); return -EINVAL; @@ -3528,36 +3492,31 @@ static int __init d40_of_probe(struct platform_device *pdev, num_disabled); pdata->disabled_channels[num_disabled] = -1; - pdev->dev.platform_data = pdata; + dev->platform_data = pdata; return 0; } static int __init d40_probe(struct platform_device *pdev) { - struct stedma40_platform_data *plat_data = pdev->dev.platform_data; + struct device *dev = &pdev->dev; struct device_node *np = pdev->dev.of_node; - int ret = -ENOENT; - struct d40_base *base = NULL; - struct resource *res = NULL; + struct device_node *np_lcpa; + struct d40_base *base; + struct resource *res; + struct resource res_lcpa; int num_reserved_chans; u32 val; + int ret; - if (!plat_data) { - if (np) { - if(d40_of_probe(pdev, np)) { - ret = -ENOMEM; - goto failure; - } - } else { - d40_err(&pdev->dev, "No pdata or Device Tree provided\n"); - goto failure; - } + if (d40_of_probe(dev, np)) { + ret = -ENOMEM; + goto report_failure; } - base = d40_hw_detect_init(pdev); - if (!base) - goto failure; + ret = d40_hw_detect_init(pdev, &base); + if (ret) + goto report_failure; num_reserved_chans = d40_phy_res_init(base); @@ -3566,39 +3525,38 @@ static int __init d40_probe(struct platform_device *pdev) spin_lock_init(&base->interrupt_lock); spin_lock_init(&base->execmd_lock); - /* Get IO for logical channel parameter address */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lcpa"); - if (!res) { - ret = -ENOENT; - d40_err(&pdev->dev, "No \"lcpa\" memory resource\n"); - goto failure; + /* Get IO for logical channel parameter address (LCPA) */ + np_lcpa = of_parse_phandle(np, "sram", 0); + if (!np_lcpa) { + dev_err(dev, "no LCPA SRAM node\n"); + ret = -EINVAL; + goto report_failure; } - base->lcpa_size = resource_size(res); - base->phy_lcpa = res->start; - - if (request_mem_region(res->start, resource_size(res), - D40_NAME " I/O lcpa") == NULL) { - ret = -EBUSY; - d40_err(&pdev->dev, - "Failed to request LCPA region 0x%x-0x%x\n", - res->start, res->end); - goto failure; + /* This is no device so read the address directly from the node */ + ret = of_address_to_resource(np_lcpa, 0, &res_lcpa); + if (ret) { + dev_err(dev, "no LCPA SRAM resource\n"); + goto report_failure; } + base->lcpa_size = resource_size(&res_lcpa); + base->phy_lcpa = res_lcpa.start; + dev_info(dev, "found LCPA SRAM at %pad, size %pa\n", + &base->phy_lcpa, &base->lcpa_size); /* We make use of ESRAM memory for this. */ val = readl(base->virtbase + D40_DREG_LCPA); - if (res->start != val && val != 0) { - dev_warn(&pdev->dev, - "[%s] Mismatch LCPA dma 0x%x, def 0x%x\n", - __func__, val, res->start); + if (base->phy_lcpa != val && val != 0) { + dev_warn(dev, + "[%s] Mismatch LCPA dma 0x%x, def %08x\n", + __func__, val, (u32)base->phy_lcpa); } else - writel(res->start, base->virtbase + D40_DREG_LCPA); + writel(base->phy_lcpa, base->virtbase + D40_DREG_LCPA); - base->lcpa_base = ioremap(res->start, resource_size(res)); + base->lcpa_base = devm_ioremap(dev, base->phy_lcpa, base->lcpa_size); if (!base->lcpa_base) { ret = -ENOMEM; - d40_err(&pdev->dev, "Failed to ioremap LCPA region\n"); - goto failure; + d40_err(dev, "Failed to ioremap LCPA region\n"); + goto report_failure; } /* If lcla has to be located in ESRAM we don't need to allocate */ if (base->plat_data->use_esram_lcla) { @@ -3606,134 +3564,108 @@ static int __init d40_probe(struct platform_device *pdev) "lcla_esram"); if (!res) { ret = -ENOENT; - d40_err(&pdev->dev, + d40_err(dev, "No \"lcla_esram\" memory resource\n"); - goto failure; + goto report_failure; } - base->lcla_pool.base = ioremap(res->start, - resource_size(res)); + base->lcla_pool.base = devm_ioremap(dev, res->start, + resource_size(res)); if (!base->lcla_pool.base) { ret = -ENOMEM; - d40_err(&pdev->dev, "Failed to ioremap LCLA region\n"); - goto failure; + d40_err(dev, "Failed to ioremap LCLA region\n"); + goto report_failure; } writel(res->start, base->virtbase + D40_DREG_LCLA); } else { ret = d40_lcla_allocate(base); if (ret) { - d40_err(&pdev->dev, "Failed to allocate LCLA area\n"); - goto failure; + d40_err(dev, "Failed to allocate LCLA area\n"); + goto destroy_cache; } } spin_lock_init(&base->lcla_pool.lock); base->irq = platform_get_irq(pdev, 0); + if (base->irq < 0) { + ret = base->irq; + goto destroy_cache; + } ret = request_irq(base->irq, d40_handle_interrupt, 0, D40_NAME, base); if (ret) { - d40_err(&pdev->dev, "No IRQ defined\n"); - goto failure; + d40_err(dev, "No IRQ defined\n"); + goto destroy_cache; } - pm_runtime_irq_safe(base->dev); - pm_runtime_set_autosuspend_delay(base->dev, DMA40_AUTOSUSPEND_DELAY); - pm_runtime_use_autosuspend(base->dev); - pm_runtime_enable(base->dev); - pm_runtime_resume(base->dev); - if (base->plat_data->use_esram_lcla) { base->lcpa_regulator = regulator_get(base->dev, "lcla_esram"); if (IS_ERR(base->lcpa_regulator)) { - d40_err(&pdev->dev, "Failed to get lcpa_regulator\n"); + d40_err(dev, "Failed to get lcpa_regulator\n"); ret = PTR_ERR(base->lcpa_regulator); base->lcpa_regulator = NULL; - goto failure; + goto destroy_cache; } ret = regulator_enable(base->lcpa_regulator); if (ret) { - d40_err(&pdev->dev, + d40_err(dev, "Failed to enable lcpa_regulator\n"); regulator_put(base->lcpa_regulator); base->lcpa_regulator = NULL; - goto failure; + goto destroy_cache; } } - base->initialized = true; + writel_relaxed(D40_DREG_GCC_ENABLE_ALL, base->virtbase + D40_DREG_GCC); + + pm_runtime_irq_safe(base->dev); + pm_runtime_set_autosuspend_delay(base->dev, DMA40_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(base->dev); + pm_runtime_mark_last_busy(base->dev); + pm_runtime_set_active(base->dev); + pm_runtime_enable(base->dev); + ret = d40_dmaengine_init(base, num_reserved_chans); if (ret) - goto failure; + goto destroy_cache; - base->dev->dma_parms = &base->dma_parms; - ret = dma_set_max_seg_size(base->dev, STEDMA40_MAX_SEG_SIZE); - if (ret) { - d40_err(&pdev->dev, "Failed to set dma max seg size\n"); - goto failure; - } + dma_set_max_seg_size(base->dev, STEDMA40_MAX_SEG_SIZE); d40_hw_init(base); - if (np) { - ret = of_dma_controller_register(np, d40_xlate, NULL); - if (ret) - dev_err(&pdev->dev, - "could not register of_dma_controller\n"); + ret = of_dma_controller_register(np, d40_xlate, NULL); + if (ret) { + dev_err(dev, + "could not register of_dma_controller\n"); + goto destroy_cache; } dev_info(base->dev, "initialized\n"); return 0; -failure: - if (base) { - if (base->desc_slab) - kmem_cache_destroy(base->desc_slab); - if (base->virtbase) - iounmap(base->virtbase); + destroy_cache: + if (base->lcla_pool.dma_addr) + dma_unmap_single(base->dev, base->lcla_pool.dma_addr, + SZ_1K * base->num_phy_chans, + DMA_TO_DEVICE); - if (base->lcla_pool.base && base->plat_data->use_esram_lcla) { - iounmap(base->lcla_pool.base); - base->lcla_pool.base = NULL; - } + if (!base->lcla_pool.base_unaligned && base->lcla_pool.base) + free_pages((unsigned long)base->lcla_pool.base, + base->lcla_pool.pages); - if (base->lcla_pool.dma_addr) - dma_unmap_single(base->dev, base->lcla_pool.dma_addr, - SZ_1K * base->num_phy_chans, - DMA_TO_DEVICE); - - if (!base->lcla_pool.base_unaligned && base->lcla_pool.base) - free_pages((unsigned long)base->lcla_pool.base, - base->lcla_pool.pages); - - kfree(base->lcla_pool.base_unaligned); - - if (base->phy_lcpa) - release_mem_region(base->phy_lcpa, - base->lcpa_size); - if (base->phy_start) - release_mem_region(base->phy_start, - base->phy_size); - if (base->clk) { - clk_disable_unprepare(base->clk); - clk_put(base->clk); - } - - if (base->lcpa_regulator) { - regulator_disable(base->lcpa_regulator); - regulator_put(base->lcpa_regulator); - } + kfree(base->lcla_pool.base_unaligned); - kfree(base->lcla_pool.alloc_map); - kfree(base->lookup_log_chans); - kfree(base->lookup_phy_chans); - kfree(base->phy_res); - kfree(base); + if (base->lcpa_regulator) { + regulator_disable(base->lcpa_regulator); + regulator_put(base->lcpa_regulator); } + pm_runtime_disable(base->dev); - d40_err(&pdev->dev, "probe failed\n"); + report_failure: + d40_err(dev, "probe failed\n"); return ret; } @@ -3744,9 +3676,8 @@ static const struct of_device_id d40_match[] = { static struct platform_driver d40_driver = { .driver = { - .owner = THIS_MODULE, .name = D40_NAME, - .pm = DMA40_PM_OPS, + .pm = &dma40_pm_ops, .of_match_table = d40_match, }, }; |
