Messages in this thread | | | Date | Fri, 16 Mar 2012 09:35:10 +0100 | From | Nicolas Ferre <> | Subject | Re: [PATCH] dmaengine: at_hdmac: add slave config operation |
| |
On 03/14/2012 12:41 PM, Nicolas Ferre : > This patch introduces DMA_SLAVE_CONFIG to at_hdmac Atmel DMA driver. > > It is needed to fix a regression in the use of atmel-mci.c driver on Atmel > AT91 platforms brouth by e2b35f3: > "dmaengine/dw_dmac: Fix dw_dmac user drivers to adapt to slave_config changes"
Hi Vinod,
Can we queue this patch for 3.4 quickly as the current linux-next is broken as far as at91 + sd/mmc is concerned.
I have written it on top of Russell's patch series about cookies management. Please tell me if it applies nicely on your 3.4 queue...
Thanks a lot, best regards,
> We remove some parts of the private structure "at_dma_slave" and use the > information provided by "struct dma_slave_config": source/destination > peripheral registers and access width. > > AT_DMA_SLAVE_WIDTH_* values used previously are not needed anymore as we > now use the standard ones. Although some conversion functions are needed to > match register expected values. > > Some AT91 sub-architecture specific files are slightly touched by this patch > but it cannot be split because it can break compilation. > > Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> > --- > arch/arm/mach-at91/at91sam9g45_devices.c | 1 - > arch/arm/mach-at91/include/mach/at_hdmac.h | 15 ------- > drivers/dma/at_hdmac.c | 56 +++++++++++++++++++++------ > drivers/dma/at_hdmac_regs.h | 32 ++++++++++++++++ > 4 files changed, 75 insertions(+), 29 deletions(-) > > diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c > index 96e2adc..28d3401 100644 > --- a/arch/arm/mach-at91/at91sam9g45_devices.c > +++ b/arch/arm/mach-at91/at91sam9g45_devices.c > @@ -432,7 +432,6 @@ void __init at91_add_device_mci(short mmc_id, struct mci_platform_data *data) > > /* DMA slave channel configuration */ > atslave->dma_dev = &at_hdmac_device.dev; > - atslave->reg_width = AT_DMA_SLAVE_WIDTH_32BIT; > atslave->cfg = ATC_FIFOCFG_HALFFIFO > | ATC_SRC_H2SEL_HW | ATC_DST_H2SEL_HW; > atslave->ctrla = ATC_SCSIZE_16 | ATC_DCSIZE_16; > diff --git a/arch/arm/mach-at91/include/mach/at_hdmac.h b/arch/arm/mach-at91/include/mach/at_hdmac.h > index 187cb58..fff48d1 100644 > --- a/arch/arm/mach-at91/include/mach/at_hdmac.h > +++ b/arch/arm/mach-at91/include/mach/at_hdmac.h > @@ -24,18 +24,6 @@ struct at_dma_platform_data { > }; > > /** > - * enum at_dma_slave_width - DMA slave register access width. > - * @AT_DMA_SLAVE_WIDTH_8BIT: Do 8-bit slave register accesses > - * @AT_DMA_SLAVE_WIDTH_16BIT: Do 16-bit slave register accesses > - * @AT_DMA_SLAVE_WIDTH_32BIT: Do 32-bit slave register accesses > - */ > -enum at_dma_slave_width { > - AT_DMA_SLAVE_WIDTH_8BIT = 0, > - AT_DMA_SLAVE_WIDTH_16BIT, > - AT_DMA_SLAVE_WIDTH_32BIT, > -}; > - > -/** > * struct at_dma_slave - Controller-specific information about a slave > * @dma_dev: required DMA master device > * @tx_reg: physical address of data register used for > @@ -48,9 +36,6 @@ enum at_dma_slave_width { > */ > struct at_dma_slave { > struct device *dma_dev; > - dma_addr_t tx_reg; > - dma_addr_t rx_reg; > - enum at_dma_slave_width reg_width; > u32 cfg; > u32 ctrla; > }; > diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c > index 5d225dd..b015a5c 100644 > --- a/drivers/dma/at_hdmac.c > +++ b/drivers/dma/at_hdmac.c > @@ -647,6 +647,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, > { > struct at_dma_chan *atchan = to_at_dma_chan(chan); > struct at_dma_slave *atslave = chan->private; > + struct dma_slave_config *sconfig = &atchan->dma_sconfig; > struct at_desc *first = NULL; > struct at_desc *prev = NULL; > u32 ctrla; > @@ -668,19 +669,18 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, > return NULL; > } > > - reg_width = atslave->reg_width; > - > ctrla = ATC_DEFAULT_CTRLA | atslave->ctrla; > ctrlb = ATC_IEN; > > switch (direction) { > case DMA_MEM_TO_DEV: > + reg_width = convert_buswidth(sconfig->dst_addr_width); > ctrla |= ATC_DST_WIDTH(reg_width); > ctrlb |= ATC_DST_ADDR_MODE_FIXED > | ATC_SRC_ADDR_MODE_INCR > | ATC_FC_MEM2PER > | ATC_SIF(AT_DMA_MEM_IF) | ATC_DIF(AT_DMA_PER_IF); > - reg = atslave->tx_reg; > + reg = sconfig->dst_addr; > for_each_sg(sgl, sg, sg_len, i) { > struct at_desc *desc; > u32 len; > @@ -708,13 +708,14 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, > } > break; > case DMA_DEV_TO_MEM: > + reg_width = convert_buswidth(sconfig->src_addr_width); > ctrla |= ATC_SRC_WIDTH(reg_width); > ctrlb |= ATC_DST_ADDR_MODE_INCR > | ATC_SRC_ADDR_MODE_FIXED > | ATC_FC_PER2MEM > | ATC_SIF(AT_DMA_PER_IF) | ATC_DIF(AT_DMA_MEM_IF); > > - reg = atslave->rx_reg; > + reg = sconfig->src_addr; > for_each_sg(sgl, sg, sg_len, i) { > struct at_desc *desc; > u32 len; > @@ -790,12 +791,15 @@ err_out: > * atc_dma_cyclic_fill_desc - Fill one period decriptor > */ > static int > -atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc, > +atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc, > unsigned int period_index, dma_addr_t buf_addr, > - size_t period_len, enum dma_transfer_direction direction) > + unsigned int reg_width, size_t period_len, > + enum dma_transfer_direction direction) > { > - u32 ctrla; > - unsigned int reg_width = atslave->reg_width; > + struct at_dma_chan *atchan = to_at_dma_chan(chan); > + struct at_dma_slave *atslave = chan->private; > + struct dma_slave_config *sconfig = &atchan->dma_sconfig; > + u32 ctrla; > > /* prepare common CRTLA value */ > ctrla = ATC_DEFAULT_CTRLA | atslave->ctrla > @@ -806,7 +810,7 @@ atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc, > switch (direction) { > case DMA_MEM_TO_DEV: > desc->lli.saddr = buf_addr + (period_len * period_index); > - desc->lli.daddr = atslave->tx_reg; > + desc->lli.daddr = sconfig->dst_addr; > desc->lli.ctrla = ctrla; > desc->lli.ctrlb = ATC_DST_ADDR_MODE_FIXED > | ATC_SRC_ADDR_MODE_INCR > @@ -816,7 +820,7 @@ atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc, > break; > > case DMA_DEV_TO_MEM: > - desc->lli.saddr = atslave->rx_reg; > + desc->lli.saddr = sconfig->src_addr; > desc->lli.daddr = buf_addr + (period_len * period_index); > desc->lli.ctrla = ctrla; > desc->lli.ctrlb = ATC_DST_ADDR_MODE_INCR > @@ -847,9 +851,11 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, > { > struct at_dma_chan *atchan = to_at_dma_chan(chan); > struct at_dma_slave *atslave = chan->private; > + struct dma_slave_config *sconfig = &atchan->dma_sconfig; > struct at_desc *first = NULL; > struct at_desc *prev = NULL; > unsigned long was_cyclic; > + unsigned int reg_width; > unsigned int periods = buf_len / period_len; > unsigned int i; > > @@ -869,8 +875,13 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, > return NULL; > } > > + if (sconfig->direction == DMA_MEM_TO_DEV) > + reg_width = convert_buswidth(sconfig->dst_addr_width); > + else > + reg_width = convert_buswidth(sconfig->src_addr_width); > + > /* Check for too big/unaligned periods and unaligned DMA buffer */ > - if (atc_dma_cyclic_check_values(atslave->reg_width, buf_addr, > + if (atc_dma_cyclic_check_values(reg_width, buf_addr, > period_len, direction)) > goto err_out; > > @@ -882,8 +893,8 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, > if (!desc) > goto err_desc_get; > > - if (atc_dma_cyclic_fill_desc(atslave, desc, i, buf_addr, > - period_len, direction)) > + if (atc_dma_cyclic_fill_desc(chan, desc, i, buf_addr, > + reg_width, period_len, direction)) > goto err_desc_get; > > atc_desc_chain(&first, &prev, desc); > @@ -906,6 +917,23 @@ err_out: > return NULL; > } > > +static int set_runtime_config(struct dma_chan *chan, > + struct dma_slave_config *sconfig) > +{ > + struct at_dma_chan *atchan = to_at_dma_chan(chan); > + > + /* Check if it is chan is configured for slave transfers */ > + if (!chan->private) > + return -EINVAL; > + > + memcpy(&atchan->dma_sconfig, sconfig, sizeof(*sconfig)); > + > + convert_burst(&atchan->dma_sconfig.src_maxburst); > + convert_burst(&atchan->dma_sconfig.dst_maxburst); > + > + return 0; > +} > + > > static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, > unsigned long arg) > @@ -966,6 +994,8 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, > clear_bit(ATC_IS_CYCLIC, &atchan->status); > > spin_unlock_irqrestore(&atchan->lock, flags); > + } else if (cmd == DMA_SLAVE_CONFIG) { > + return set_runtime_config(chan, (struct dma_slave_config *)arg); > } else { > return -ENXIO; > } > diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h > index 08fd8a0..897a8bc 100644 > --- a/drivers/dma/at_hdmac_regs.h > +++ b/drivers/dma/at_hdmac_regs.h > @@ -207,6 +207,7 @@ enum atc_status { > * @save_cfg: configuration register that is saved on suspend/resume cycle > * @save_dscr: for cyclic operations, preserve next descriptor address in > * the cyclic list on suspend/resume cycle > + * @dma_sconfig: configuration for slave transfers, passed via DMA_SLAVE_CONFIG > * @lock: serializes enqueue/dequeue operations to descriptors lists > * @active_list: list of descriptors dmaengine is being running on > * @queue: list of descriptors ready to be submitted to engine > @@ -222,6 +223,7 @@ struct at_dma_chan { > struct tasklet_struct tasklet; > u32 save_cfg; > u32 save_dscr; > + struct dma_slave_config dma_sconfig; > > spinlock_t lock; > > @@ -243,6 +245,36 @@ static inline struct at_dma_chan *to_at_dma_chan(struct dma_chan *dchan) > return container_of(dchan, struct at_dma_chan, chan_common); > } > > +/* > + * Fix sconfig's burst size according to at_hdmac. We need to convert them as: > + * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3, 32 -> 4, 64 -> 5, 128 -> 6, 256 -> 7. > + * > + * This can be done by finding most significant bit set. > + */ > +static inline void convert_burst(u32 *maxburst) > +{ > + if (*maxburst > 1) > + *maxburst = fls(*maxburst) - 2; > + else > + *maxburst = 0; > +} > + > +/* > + * Fix sconfig's bus width according to at_hdmac. > + * 1 byte -> 0, 2 bytes -> 1, 4 bytes -> 2. > + */ > +static inline u8 convert_buswidth(enum dma_slave_buswidth addr_width) > +{ > + switch (addr_width) { > + case DMA_SLAVE_BUSWIDTH_2_BYTES: > + return 1; > + case DMA_SLAVE_BUSWIDTH_4_BYTES: > + return 2; > + default: > + /* For 1 byte width or fallback */ > + return 0; > + } > +} > > /*-- Controller ------------------------------------------------------*/ >
-- Nicolas Ferre
| |