Message ID | 20181130203646.68615-3-andriy.shevchenko@linux.intel.com (mailing list archive) |
---|---|
State | Changes Requested |
Headers | show |
Series | [v1,1/5] dmaengine: dw: Fix FIFO size for Intel Merrifield | expand |
On Fri, Nov 30, 2018 at 10:36:44PM +0200, Andy Shevchenko wrote: > Here is a kinda big refactoring that should have been done > in the first place, when Intel iDMA 32-bit support appeared. > > It splits operations which are different to Synopsys DesignWare and > Intel iDMA 32-bit controllers. > > No functional change intended. While first two are real bug fixes and really small, this one bigger and suddenly has a typo which I didn't notice during my tests. Vinod, please, consider applying first two until I will come with better this one and might be something more. > > Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> > --- > drivers/dma/dw/Makefile | 2 +- > drivers/dma/dw/core.c | 188 +++++---------------------- > drivers/dma/dw/dw.c | 109 ++++++++++++++++ > drivers/dma/dw/idma32.c | 137 +++++++++++++++++++ > drivers/dma/dw/internal.h | 10 +- > drivers/dma/dw/pci.c | 45 ++++--- > drivers/dma/dw/platform.c | 8 +- > drivers/dma/dw/regs.h | 13 ++ > include/linux/dma/dw.h | 4 + > include/linux/platform_data/dma-dw.h | 2 - > 10 files changed, 339 insertions(+), 179 deletions(-) > create mode 100644 drivers/dma/dw/dw.c > create mode 100644 drivers/dma/dw/idma32.c > > diff --git a/drivers/dma/dw/Makefile b/drivers/dma/dw/Makefile > index 2b949c2e4504..63ed895c09aa 100644 > --- a/drivers/dma/dw/Makefile > +++ b/drivers/dma/dw/Makefile > @@ -1,6 +1,6 @@ > # SPDX-License-Identifier: GPL-2.0 > obj-$(CONFIG_DW_DMAC_CORE) += dw_dmac_core.o > -dw_dmac_core-objs := core.o > +dw_dmac_core-objs := core.o dw.o idma32.o > > obj-$(CONFIG_DW_DMAC) += dw_dmac.o > dw_dmac-objs := platform.o > diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c > index e85b078fc207..711f4d19bbf1 100644 > --- a/drivers/dma/dw/core.c > +++ b/drivers/dma/dw/core.c > @@ -138,42 +138,6 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) > dwc->descs_allocated--; > } > > -static void dwc_initialize_chan_idma32(struct dw_dma_chan *dwc) > -{ > - u32 cfghi = 0; > - u32 cfglo = 0; > - > - /* Set default burst alignment */ > - cfglo |= IDMA32C_CFGL_DST_BURST_ALIGN | IDMA32C_CFGL_SRC_BURST_ALIGN; > - > - /* Low 4 bits of the request lines */ > - cfghi |= IDMA32C_CFGH_DST_PER(dwc->dws.dst_id & 0xf); > - cfghi |= IDMA32C_CFGH_SRC_PER(dwc->dws.src_id & 0xf); > - > - /* Request line extension (2 bits) */ > - cfghi |= IDMA32C_CFGH_DST_PER_EXT(dwc->dws.dst_id >> 4 & 0x3); > - cfghi |= IDMA32C_CFGH_SRC_PER_EXT(dwc->dws.src_id >> 4 & 0x3); > - > - channel_writel(dwc, CFG_LO, cfglo); > - channel_writel(dwc, CFG_HI, cfghi); > -} > - > -static void dwc_initialize_chan_dw(struct dw_dma_chan *dwc) > -{ > - u32 cfghi = DWC_CFGH_FIFO_MODE; > - u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority); > - bool hs_polarity = dwc->dws.hs_polarity; > - > - cfghi |= DWC_CFGH_DST_PER(dwc->dws.dst_id); > - cfghi |= DWC_CFGH_SRC_PER(dwc->dws.src_id); > - > - /* Set polarity of handshake interface */ > - cfglo |= hs_polarity ? DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL : 0; > - > - channel_writel(dwc, CFG_LO, cfglo); > - channel_writel(dwc, CFG_HI, cfghi); > -} > - > static void dwc_initialize(struct dw_dma_chan *dwc) > { > struct dw_dma *dw = to_dw_dma(dwc->chan.device); > @@ -181,10 +145,7 @@ static void dwc_initialize(struct dw_dma_chan *dwc) > if (test_bit(DW_DMA_IS_INITIALIZED, &dwc->flags)) > return; > > - if (dw->pdata->is_idma32) > - dwc_initialize_chan_idma32(dwc); > - else > - dwc_initialize_chan_dw(dwc); > + dw->initialize_chan(dwc); > > /* Enable interrupts */ > channel_set_bit(dw, MASK.XFER, dwc->mask); > @@ -213,37 +174,6 @@ static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc) > cpu_relax(); > } > > -static u32 bytes2block(struct dw_dma_chan *dwc, size_t bytes, > - unsigned int width, size_t *len) > -{ > - struct dw_dma *dw = to_dw_dma(dwc->chan.device); > - u32 block; > - > - /* Always in bytes for iDMA 32-bit */ > - if (dw->pdata->is_idma32) > - width = 0; > - > - if ((bytes >> width) > dwc->block_size) { > - block = dwc->block_size; > - *len = block << width; > - } else { > - block = bytes >> width; > - *len = bytes; > - } > - > - return block; > -} > - > -static size_t block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width) > -{ > - struct dw_dma *dw = to_dw_dma(dwc->chan.device); > - > - if (dw->pdata->is_idma32) > - return IDMA32C_CTLH_BLOCK_TS(block); > - > - return DWC_CTLH_BLOCK_TS(block) << width; > -} > - > /*----------------------------------------------------------------------*/ > > /* Perform single block transfer */ > @@ -389,10 +319,11 @@ static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc) > /* Returns how many bytes were already received from source */ > static inline u32 dwc_get_sent(struct dw_dma_chan *dwc) > { > + struct dw_dma *dw = to_dw_dma(dwc->chan.device); > u32 ctlhi = channel_readl(dwc, CTL_HI); > u32 ctllo = channel_readl(dwc, CTL_LO); > > - return block2bytes(dwc, ctlhi, ctllo >> 4 & 7); > + return dw->block2bytes(dwc, ctlhi, ctllo >> 4 & 7); > } > > static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) > @@ -649,7 +580,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, > unsigned int src_width; > unsigned int dst_width; > unsigned int data_width = dw->pdata->data_width[m_master]; > - u32 ctllo; > + u32 ctllo, ctlhi; > u8 lms = DWC_LLP_LMS(m_master); > > dev_vdbg(chan2dev(chan), > @@ -678,10 +609,12 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, > if (!desc) > goto err_desc_get; > > + ctlhi = dw->bytes2block(dwc, len - offset, src_width, &xfer_count); > + > lli_write(desc, sar, src + offset); > lli_write(desc, dar, dest + offset); > lli_write(desc, ctllo, ctllo); > - lli_write(desc, ctlhi, bytes2block(dwc, len - offset, src_width, &xfer_count)); > + lli_write(desc, ctlhi, ctlhi); > desc->len = xfer_count; > > if (!first) { > @@ -719,7 +652,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, > struct dma_slave_config *sconfig = &dwc->dma_sconfig; > struct dw_desc *prev; > struct dw_desc *first; > - u32 ctllo; > + u32 ctllo, ctlhi; > u8 m_master = dwc->dws.m_master; > u8 lms = DWC_LLP_LMS(m_master); > dma_addr_t reg; > @@ -766,9 +699,11 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, > if (!desc) > goto err_desc_get; > > + ctlhi = dw->bytes2block(dwc, len, mem_width, &dlen); > + > lli_write(desc, sar, mem); > lli_write(desc, dar, reg); > - lli_write(desc, ctlhi, bytes2block(dwc, len, mem_width, &dlen)); > + lli_write(desc, ctlhi, ctlhi); > lli_write(desc, ctllo, ctllo | DWC_CTLL_SRC_WIDTH(mem_width)); > desc->len = dlen; > > @@ -812,9 +747,11 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, > if (!desc) > goto err_desc_get; > > + ctlhi = dw->bytes2block(dwc, len, reg_width, &dlen); > + > lli_write(desc, sar, reg); > lli_write(desc, dar, mem); > - lli_write(desc, ctlhi, bytes2block(dwc, len, reg_width, &dlen)); > + lli_write(desc, ctlhi, ctlhi); > mem_width = __ffs(data_width | mem | dlen); > lli_write(desc, ctllo, ctllo | DWC_CTLL_DST_WIDTH(mem_width)); > desc->len = dlen; > @@ -874,22 +811,12 @@ EXPORT_SYMBOL_GPL(dw_dma_filter); > static int dwc_config(struct dma_chan *chan, struct dma_slave_config *sconfig) > { > struct dw_dma_chan *dwc = to_dw_dma_chan(chan); > - struct dma_slave_config *sc = &dwc->dma_sconfig; > struct dw_dma *dw = to_dw_dma(chan->device); > - /* > - * Fix sconfig's burst size according to dw_dmac. We need to convert > - * them as: > - * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3. > - * > - * NOTE: burst size 2 is not supported by DesignWare controller. > - * iDMA 32-bit supports it. > - */ > - u32 s = dw->pdata->is_idma32 ? 1 : 2; > > memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); > > - sc->src_maxburst = sc->src_maxburst > 1 ? fls(sc->src_maxburst) - s : 0; > - sc->dst_maxburst = sc->dst_maxburst > 1 ? fls(sc->dst_maxburst) - s : 0; > + dw->encode_maxburst(dwc, &dwc->dma_sconfig.src_maxburst); > + dw->encode_maxburst(dwc, &dwc->dma_sconfig.dst_maxburst); > > return 0; > } > @@ -898,16 +825,9 @@ static void dwc_chan_pause(struct dw_dma_chan *dwc, bool drain) > { > struct dw_dma *dw = to_dw_dma(dwc->chan.device); > unsigned int count = 20; /* timeout iterations */ > - u32 cfglo; > > - cfglo = channel_readl(dwc, CFG_LO); > - if (dw->pdata->is_idma32) { > - if (drain) > - cfglo |= IDMA32C_CFGL_CH_DRAIN; > - else > - cfglo &= ~IDMA32C_CFGL_CH_DRAIN; > - } > - channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); > + dw->suspend_chan(dwc, drain); > + > while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY) && count--) > udelay(2); > > @@ -1056,33 +976,7 @@ static void dwc_issue_pending(struct dma_chan *chan) > > /*----------------------------------------------------------------------*/ > > -/* > - * Program FIFO size of channels. > - * > - * By default full FIFO (512 bytes) is assigned to channel 0. Here we > - * slice FIFO on equal parts between channels. > - */ > -static void idma32_fifo_partition(struct dw_dma *dw) > -{ > - u64 value = IDMA32C_FP_PSIZE_CH0(64) | IDMA32C_FP_PSIZE_CH1(64) | > - IDMA32C_FP_UPDATE; > - u64 fifo_partition = 0; > - > - if (!dw->pdata->is_idma32) > - return; > - > - /* Fill FIFO_PARTITION low bits (Channels 0..1, 4..5) */ > - fifo_partition |= value << 0; > - > - /* Fill FIFO_PARTITION high bits (Channels 2..3, 6..7) */ > - fifo_partition |= value << 32; > - > - /* Program FIFO Partition registers - 128 bytes for each channel */ > - idma32_writeq(dw, FIFO_PARTITION1, fifo_partition); > - idma32_writeq(dw, FIFO_PARTITION0, fifo_partition); > -} > - > -static void dw_dma_off(struct dw_dma *dw) > +void do_dma_off(struct dw_dma *dw) > { > unsigned int i; > > @@ -1101,7 +995,7 @@ static void dw_dma_off(struct dw_dma *dw) > clear_bit(DW_DMA_IS_INITIALIZED, &dw->chan[i].flags); > } > > -static void dw_dma_on(struct dw_dma *dw) > +void do_dma_on(struct dw_dma *dw) > { > dma_writel(dw, CFG, DW_CFG_DMA_EN); > } > @@ -1137,7 +1031,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) > > /* Enable controller here if needed */ > if (!dw->in_use) > - dw_dma_on(dw); > + do_dma_on(dw); > dw->in_use |= dwc->mask; > > return 0; > @@ -1175,30 +1069,25 @@ static void dwc_free_chan_resources(struct dma_chan *chan) > /* Disable controller in case it was a last user */ > dw->in_use &= ~dwc->mask; > if (!dw->in_use) > - dw_dma_off(dw); > + do_dma_off(dw); > > dev_vdbg(chan2dev(chan), "%s: done\n", __func__); > } > > -int dw_dma_probe(struct dw_dma_chip *chip) > +int do_dma_probe(struct dw_dma_chip *chip) > { > + struct dw_dma *dw = chip->dw; > struct dw_dma_platform_data *pdata; > - struct dw_dma *dw; > bool autocfg = false; > unsigned int dw_params; > unsigned int i; > int err; > > - dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL); > - if (!dw) > - return -ENOMEM; > - > dw->pdata = devm_kzalloc(chip->dev, sizeof(*dw->pdata), GFP_KERNEL); > if (!dw->pdata) > return -ENOMEM; > > dw->regs = chip->regs; > - chip->dw = dw; > > pm_runtime_get_sync(chip->dev); > > @@ -1250,15 +1139,10 @@ int dw_dma_probe(struct dw_dma_chip *chip) > dw->all_chan_mask = (1 << pdata->nr_channels) - 1; > > /* Force dma off, just in case */ > - dw_dma_off(dw); > - > - idma32_fifo_partition(dw); > + dw->disable(dw); > > /* Device and instance ID for IRQ and DMA pool */ > - if (pdata->is_idma32) > - snprintf(dw->name, sizeof(dw->name), "idma32:dmac%d", chip->id); > - else > - snprintf(dw->name, sizeof(dw->name), "dw:dmac%d", chip->id); > + dw->set_device_name(dw, chip->id); > > /* Create a pool of consistent memory blocks for hardware descriptors */ > dw->desc_pool = dmam_pool_create(dw->name, chip->dev, > @@ -1382,16 +1266,15 @@ int dw_dma_probe(struct dw_dma_chip *chip) > pm_runtime_put_sync_suspend(chip->dev); > return err; > } > -EXPORT_SYMBOL_GPL(dw_dma_probe); > > -int dw_dma_remove(struct dw_dma_chip *chip) > +int do_dma_remove(struct dw_dma_chip *chip) > { > struct dw_dma *dw = chip->dw; > struct dw_dma_chan *dwc, *_dwc; > > pm_runtime_get_sync(chip->dev); > > - dw_dma_off(dw); > + do_dma_off(dw); > dma_async_device_unregister(&dw->dma); > > free_irq(chip->irq, dw); > @@ -1406,27 +1289,24 @@ int dw_dma_remove(struct dw_dma_chip *chip) > pm_runtime_put_sync_suspend(chip->dev); > return 0; > } > -EXPORT_SYMBOL_GPL(dw_dma_remove); > > -int dw_dma_disable(struct dw_dma_chip *chip) > +int do_dw_dma_disable(struct dw_dma_chip *chip) > { > struct dw_dma *dw = chip->dw; > > - dw_dma_off(dw); > + dw->disable(dw); > return 0; > } > -EXPORT_SYMBOL_GPL(dw_dma_disable); > +EXPORT_SYMBOL_GPL(do_dw_dma_disable); > > -int dw_dma_enable(struct dw_dma_chip *chip) > +int do_dw_dma_enable(struct dw_dma_chip *chip) > { > struct dw_dma *dw = chip->dw; > > - idma32_fifo_partition(dw); > - > - dw_dma_on(dw); > + dw->disable(dw); > return 0; > } > -EXPORT_SYMBOL_GPL(dw_dma_enable); > +EXPORT_SYMBOL_GPL(do_dw_dma_enable); > > MODULE_LICENSE("GPL v2"); > MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller core driver"); > diff --git a/drivers/dma/dw/dw.c b/drivers/dma/dw/dw.c > new file mode 100644 > index 000000000000..998a9eeb561d > --- /dev/null > +++ b/drivers/dma/dw/dw.c > @@ -0,0 +1,109 @@ > +// SPDX-License-Identifier: GPL-2.0 > +// Copyright (C) 2007-2008 Atmel Corporation > +// Copyright (C) 2010-2011 ST Microelectronics > +// Copyright (C) 2013,2018 Intel Corporation > + > +#include <linux/bitops.h> > +#include <linux/errno.h> > +#include <linux/slab.h> > + > +#include "internal.h" > + > +static void dw_dma_initialize_chan(struct dw_dma_chan *dwc) > +{ > + u32 cfghi = DWC_CFGH_FIFO_MODE; > + u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority); > + bool hs_polarity = dwc->dws.hs_polarity; > + > + cfghi |= DWC_CFGH_DST_PER(dwc->dws.dst_id); > + cfghi |= DWC_CFGH_SRC_PER(dwc->dws.src_id); > + > + /* Set polarity of handshake interface */ > + cfglo |= hs_polarity ? DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL : 0; > + > + channel_writel(dwc, CFG_LO, cfglo); > + channel_writel(dwc, CFG_HI, cfghi); > +} > + > +static void dw_dma_suspend_chan(struct dw_dma_chan *dwc, bool drain) > +{ > + u32 cfglo = channel_readl(dwc, CFG_LO); > + > + channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); > +} > + > +static u32 dw_dma_bytes2block(struct dw_dma_chan *dwc, > + size_t bytes, unsigned int width, size_t *len) > +{ > + u32 block; > + > + if ((bytes >> width) > dwc->block_size) { > + block = dwc->block_size; > + *len = block << width; > + } else { > + block = bytes >> width; > + *len = bytes; > + } > + > + return block; > +} > + > +static size_t dw_dma_block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width) > +{ > + return DWC_CTLH_BLOCK_TS(block) << width; > +} > + > +static void dw_dma_encode_maxburst(struct dw_dma_chan *dwc, u32 *maxburst) > +{ > + /* > + * Fix burst size according to dw_dmac. We need to convert them as: > + * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3. > + */ > + *maxburst = *maxburst > 1 ? fls(*maxburst) - 2 : 0; > +} > + > +static void dw_dma_set_device_name(struct dw_dma *dw, int id) > +{ > + snprintf(dw->name, sizeof(dw->name), "dw:dmac%d", id); > +} > + > +static void dw_dma_disable(struct dw_dma *dw) > +{ > + do_dma_off(dw); > +} > + > +static void dw_dma_enable(struct dw_dma *dw) > +{ > + do_dma_on(dw); > +} > + > +int dw_dma_probe(struct dw_dma_chip *chip) > +{ > + struct dw_dma *dw; > + > + dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL); > + if (!dw) > + return -ENOMEM; > + > + /* Channel operations */ > + dw->initialize_chan = dw_dma_initialize_chan; > + dw->suspend_chan = dw_dma_suspend_chan; > + dw->encode_maxburst = dw_dma_encode_maxburst; > + dw->bytes2block = dw_dma_bytes2block; > + dw->block2bytes = dw_dma_block2bytes; > + > + /* Device operations */ > + dw->set_device_name = dw_dma_set_device_name; > + dw->disable = dw_dma_disable; > + dw->enable = dw_dma_enable; > + > + chip->dw = dw; > + return do_dma_probe(chip); > +} > +EXPORT_SYMBOL_GPL(dw_dma_probe); > + > +int dw_dma_remove(struct dw_dma_chip *chip) > +{ > + return do_dma_remove(chip); > +} > +EXPORT_SYMBOL_GPL(dw_dma_remove); > diff --git a/drivers/dma/dw/idma32.c b/drivers/dma/dw/idma32.c > new file mode 100644 > index 000000000000..d06fb00fb862 > --- /dev/null > +++ b/drivers/dma/dw/idma32.c > @@ -0,0 +1,137 @@ > +// SPDX-License-Identifier: GPL-2.0 > +// Copyright (C) 2013,2018 Intel Corporation > + > +#include <linux/bitops.h> > +#include <linux/errno.h> > +#include <linux/slab.h> > + > +#include "internal.h" > + > +static void idma32_initialize_chan(struct dw_dma_chan *dwc) > +{ > + u32 cfghi = 0; > + u32 cfglo = 0; > + > + /* Set default burst alignment */ > + cfglo |= IDMA32C_CFGL_DST_BURST_ALIGN | IDMA32C_CFGL_SRC_BURST_ALIGN; > + > + /* Low 4 bits of the request lines */ > + cfghi |= IDMA32C_CFGH_DST_PER(dwc->dws.dst_id & 0xf); > + cfghi |= IDMA32C_CFGH_SRC_PER(dwc->dws.src_id & 0xf); > + > + /* Request line extension (2 bits) */ > + cfghi |= IDMA32C_CFGH_DST_PER_EXT(dwc->dws.dst_id >> 4 & 0x3); > + cfghi |= IDMA32C_CFGH_SRC_PER_EXT(dwc->dws.src_id >> 4 & 0x3); > + > + channel_writel(dwc, CFG_LO, cfglo); > + channel_writel(dwc, CFG_HI, cfghi); > +} > + > +static void idma32_suspend_chan(struct dw_dma_chan *dwc, bool drain) > +{ > + u32 cfglo = channel_readl(dwc, CFG_LO); > + > + if (drain) > + cfglo |= IDMA32C_CFGL_CH_DRAIN; > + else > + cfglo &= ~IDMA32C_CFGL_CH_DRAIN; > + > + channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); > +} > + > +static u32 idma32_bytes2block(struct dw_dma_chan *dwc, > + size_t bytes, unsigned int width, size_t *len) > +{ > + u32 block; > + > + if (bytes > dwc->block_size) { > + block = dwc->block_size; > + *len = dwc->block_size; > + } else { > + block = bytes; > + *len = bytes; > + } > + > + return block; > +} > + > +static size_t idma32_block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width) > +{ > + return IDMA32C_CTLH_BLOCK_TS(block); > +} > + > +static void idma32_encode_maxburst(struct dw_dma_chan *dwc, u32 *maxburst) > +{ > + *maxburst = *maxburst > 1 ? fls(*maxburst) - 1 : 0; > +} > + > +static void idma32_set_device_name(struct dw_dma *dw, int id) > +{ > + snprintf(dw->name, sizeof(dw->name), "idma32:dmac%d", id); > +} > + > +/* > + * Program FIFO size of channels. > + * > + * By default full FIFO (512 bytes) is assigned to channel 0. Here we > + * slice FIFO on equal parts between channels. > + */ > +static void idma32_fifo_partition(struct dw_dma *dw) > +{ > + u64 value = IDMA32C_FP_PSIZE_CH0(64) | IDMA32C_FP_PSIZE_CH1(64) | > + IDMA32C_FP_UPDATE; > + u64 fifo_partition = 0; > + > + /* Fill FIFO_PARTITION low bits (Channels 0..1, 4..5) */ > + fifo_partition |= value << 0; > + > + /* Fill FIFO_PARTITION high bits (Channels 2..3, 6..7) */ > + fifo_partition |= value << 32; > + > + /* Program FIFO Partition registers - 128 bytes for each channel */ > + idma32_writeq(dw, FIFO_PARTITION1, fifo_partition); > + idma32_writeq(dw, FIFO_PARTITION0, fifo_partition); > +} > + > +static void idma32_disable(struct dw_dma *dw) > +{ > + do_dma_off(dw); > + idma32_fifo_partition(dw); > +} > + > +static void idma32_enable(struct dw_dma *dw) > +{ > + idma32_fifo_partition(dw); > + do_dma_on(dw); > +} > + > +int idma32_dma_probe(struct dw_dma_chip *chip) > +{ > + struct dw_dma *dw; > + > + dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL); > + if (!dw) > + return -ENOMEM; > + > + /* Channel operations */ > + dw->initialize_chan = idma32_initialize_chan; > + dw->suspend_chan = idma32_suspend_chan; > + dw->encode_maxburst = idma32_encode_maxburst; > + dw->bytes2block = idma32_bytes2block; > + dw->block2bytes = idma32_block2bytes; > + > + /* Device operations */ > + dw->set_device_name = idma32_set_device_name; > + dw->disable = idma32_disable; > + dw->enable = idma32_enable; > + > + chip->dw = dw; > + return do_dma_probe(chip); > +} > +EXPORT_SYMBOL_GPL(idma32_dma_probe); > + > +int idma32_dma_remove(struct dw_dma_chip *chip) > +{ > + return do_dma_remove(chip); > +} > +EXPORT_SYMBOL_GPL(idma32_dma_remove); > diff --git a/drivers/dma/dw/internal.h b/drivers/dma/dw/internal.h > index 41439732ff6b..1f2ddfe745bb 100644 > --- a/drivers/dma/dw/internal.h > +++ b/drivers/dma/dw/internal.h > @@ -15,8 +15,14 @@ > > #include "regs.h" > > -int dw_dma_disable(struct dw_dma_chip *chip); > -int dw_dma_enable(struct dw_dma_chip *chip); > +int do_dma_probe(struct dw_dma_chip *chip); > +int do_dma_remove(struct dw_dma_chip *chip); > + > +void do_dma_on(struct dw_dma *dw); > +void do_dma_off(struct dw_dma *dw); > + > +int do_dw_dma_disable(struct dw_dma_chip *chip); > +int do_dw_dma_enable(struct dw_dma_chip *chip); > > extern bool dw_dma_filter(struct dma_chan *chan, void *param); > > diff --git a/drivers/dma/dw/pci.c b/drivers/dma/dw/pci.c > index 61f2274f6dd4..f5ea58cc027b 100644 > --- a/drivers/dma/dw/pci.c > +++ b/drivers/dma/dw/pci.c > @@ -15,11 +15,19 @@ > > #include "internal.h" > > -static struct dw_dma_platform_data mrfld_pdata = { > +static struct dw_dma_pci_data { > + const struct dw_dma_platform_data *pdata; > + int (*probe)(struct dw_dma_chip *chip); > +}; > + > +static const struct dw_dma_pci_data dw_pci_data = { > + .probe = dw_dma_probe, > +}; > + > +static const struct dw_dma_platform_data idma32_pdata = { > .nr_channels = 8, > .is_private = true, > .is_memcpy = true, > - .is_idma32 = true, > .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, > .chan_priority = CHAN_PRIORITY_ASCENDING, > .block_size = 131071, > @@ -28,9 +36,14 @@ static struct dw_dma_platform_data mrfld_pdata = { > .multi_block = {true, true, true, true, true, true, true, true}, > }; > > +static const struct dw_dma_pci_data idma32_pci_data = { > + .pdata = &idma32_pdata, > + .probe = idma32_dma_probe, > +}; > + > static int dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) > { > - const struct dw_dma_platform_data *pdata = (void *)pid->driver_data; > + const struct dw_dma_pci_data *data = (void *)pid->driver_data; > struct dw_dma_chip *chip; > int ret; > > @@ -63,9 +76,9 @@ static int dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) > chip->id = pdev->devfn; > chip->regs = pcim_iomap_table(pdev)[0]; > chip->irq = pdev->irq; > - chip->pdata = pdata; > + chip->pdata = data->pdata; > > - ret = dw_dma_probe(chip); > + ret = data->probe(chip); > if (ret) > return ret; > > @@ -91,7 +104,7 @@ static int dw_pci_suspend_late(struct device *dev) > struct pci_dev *pci = to_pci_dev(dev); > struct dw_dma_chip *chip = pci_get_drvdata(pci); > > - return dw_dma_disable(chip); > + return do_dw_dma_disable(chip); > }; > > static int dw_pci_resume_early(struct device *dev) > @@ -99,7 +112,7 @@ static int dw_pci_resume_early(struct device *dev) > struct pci_dev *pci = to_pci_dev(dev); > struct dw_dma_chip *chip = pci_get_drvdata(pci); > > - return dw_dma_enable(chip); > + return do_dw_dma_enable(chip); > }; > > #endif /* CONFIG_PM_SLEEP */ > @@ -110,24 +123,24 @@ static const struct dev_pm_ops dw_pci_dev_pm_ops = { > > static const struct pci_device_id dw_pci_id_table[] = { > /* Medfield (GPDMA) */ > - { PCI_VDEVICE(INTEL, 0x0827) }, > + { PCI_VDEVICE(INTEL, 0x0827), (kernel_ulong_t)&dw_pci_data }, > > /* BayTrail */ > - { PCI_VDEVICE(INTEL, 0x0f06) }, > - { PCI_VDEVICE(INTEL, 0x0f40) }, > + { PCI_VDEVICE(INTEL, 0x0f06), (kernel_ulong_t)&dw_pci_data }, > + { PCI_VDEVICE(INTEL, 0x0f40), (kernel_ulong_t)&dw_pci_data }, > > - /* Merrifield iDMA 32-bit (GPDMA) */ > - { PCI_VDEVICE(INTEL, 0x11a2), (kernel_ulong_t)&mrfld_pdata }, > + /* Merrifield */ > + { PCI_VDEVICE(INTEL, 0x11a2), (kernel_ulong_t)&idma32_pci_data }, > > /* Braswell */ > - { PCI_VDEVICE(INTEL, 0x2286) }, > - { PCI_VDEVICE(INTEL, 0x22c0) }, > + { PCI_VDEVICE(INTEL, 0x2286), (kernel_ulong_t)&dw_pci_data }, > + { PCI_VDEVICE(INTEL, 0x22c0), (kernel_ulong_t)&dw_pci_data }, > > /* Haswell */ > - { PCI_VDEVICE(INTEL, 0x9c60) }, > + { PCI_VDEVICE(INTEL, 0x9c60), (kernel_ulong_t)&dw_pci_data }, > > /* Broadwell */ > - { PCI_VDEVICE(INTEL, 0x9ce0) }, > + { PCI_VDEVICE(INTEL, 0x9ce0), (kernel_ulong_t)&dw_pci_data }, > > { } > }; > diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c > index f01b2c173fa6..6bf46c9b7ee4 100644 > --- a/drivers/dma/dw/platform.c > +++ b/drivers/dma/dw/platform.c > @@ -258,7 +258,7 @@ static void dw_shutdown(struct platform_device *pdev) > struct dw_dma_chip *chip = platform_get_drvdata(pdev); > > /* > - * We have to call dw_dma_disable() to stop any ongoing transfer. On > + * We have to call do_dw_dma_disable() to stop any ongoing transfer. On > * some platforms we can't do that since DMA device is powered off. > * Moreover we have no possibility to check if the platform is affected > * or not. That's why we call pm_runtime_get_sync() / pm_runtime_put() > @@ -267,7 +267,7 @@ static void dw_shutdown(struct platform_device *pdev) > * used by the driver. > */ > pm_runtime_get_sync(chip->dev); > - dw_dma_disable(chip); > + do_dw_dma_disable(chip); > pm_runtime_put_sync_suspend(chip->dev); > > clk_disable_unprepare(chip->clk); > @@ -297,7 +297,7 @@ static int dw_suspend_late(struct device *dev) > { > struct dw_dma_chip *chip = dev_get_drvdata(dev); > > - dw_dma_disable(chip); > + do_dw_dma_disable(chip); > clk_disable_unprepare(chip->clk); > > return 0; > @@ -312,7 +312,7 @@ static int dw_resume_early(struct device *dev) > if (ret) > return ret; > > - return dw_dma_enable(chip); > + return do_dw_dma_enable(chip); > } > > #endif /* CONFIG_PM_SLEEP */ > diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h > index 09e7dfdbb790..90e46e69c047 100644 > --- a/drivers/dma/dw/regs.h > +++ b/drivers/dma/dw/regs.h > @@ -308,6 +308,19 @@ struct dw_dma { > u8 all_chan_mask; > u8 in_use; > > + /* Channel operations */ > + void (*initialize_chan)(struct dw_dma_chan *dwc); > + void (*suspend_chan)(struct dw_dma_chan *dwc, bool drain); > + void (*encode_maxburst)(struct dw_dma_chan *dwc, u32 *maxburst); > + u32 (*bytes2block)(struct dw_dma_chan *dwc, size_t bytes, > + unsigned int width, size_t *len); > + size_t (*block2bytes)(struct dw_dma_chan *dwc, u32 block, u32 width); > + > + /* Device operations */ > + void (*set_device_name)(struct dw_dma *dw, int id); > + void (*disable)(struct dw_dma *dw); > + void (*enable)(struct dw_dma *dw); > + > /* platform data */ > struct dw_dma_platform_data *pdata; > }; > diff --git a/include/linux/dma/dw.h b/include/linux/dma/dw.h > index e166cac8e870..d643d331c20e 100644 > --- a/include/linux/dma/dw.h > +++ b/include/linux/dma/dw.h > @@ -45,9 +45,13 @@ struct dw_dma_chip { > #if IS_ENABLED(CONFIG_DW_DMAC_CORE) > int dw_dma_probe(struct dw_dma_chip *chip); > int dw_dma_remove(struct dw_dma_chip *chip); > +int idma32_dma_probe(struct dw_dma_chip *chip); > +int idma32_dma_remove(struct dw_dma_chip *chip); > #else > static inline int dw_dma_probe(struct dw_dma_chip *chip) { return -ENODEV; } > static inline int dw_dma_remove(struct dw_dma_chip *chip) { return 0; } > +static inline int idma32_dma_probe(struct dw_dma_chip *chip) { return -ENODEV; } > +static inline int idma32_dma_remove(struct dw_dma_chip *chip) { return 0; } > #endif /* CONFIG_DW_DMAC_CORE */ > > #endif /* _DMA_DW_H */ > diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h > index 896cb71a382c..e69e415d0d98 100644 > --- a/include/linux/platform_data/dma-dw.h > +++ b/include/linux/platform_data/dma-dw.h > @@ -41,7 +41,6 @@ struct dw_dma_slave { > * @is_private: The device channels should be marked as private and not for > * by the general purpose DMA channel allocator. > * @is_memcpy: The device channels do support memory-to-memory transfers. > - * @is_idma32: The type of the DMA controller is iDMA32 > * @chan_allocation_order: Allocate channels starting from 0 or 7 > * @chan_priority: Set channel priority increasing from 0 to 7 or 7 to 0. > * @block_size: Maximum block size supported by the controller > @@ -54,7 +53,6 @@ struct dw_dma_platform_data { > unsigned int nr_channels; > bool is_private; > bool is_memcpy; > - bool is_idma32; > #define CHAN_ALLOCATION_ASCENDING 0 /* zero to seven */ > #define CHAN_ALLOCATION_DESCENDING 1 /* seven to zero */ > unsigned char chan_allocation_order; > -- > 2.19.2 >
Hi Andy, I love your patch! Perhaps something to improve: [auto build test WARNING on linus/master] [also build test WARNING on v4.20-rc4] [cannot apply to next-20181130] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] url: https://github.com/0day-ci/linux/commits/Andy-Shevchenko/dmaengine-dw-Fix-FIFO-size-for-Intel-Merrifield/20181201-110953 config: x86_64-randconfig-x014-201847 (attached as .config) compiler: gcc-7 (Debian 7.3.0-1) 7.3.0 reproduce: # save the attached .config to linux build tree make ARCH=x86_64 All warnings (new ones prefixed by >>): >> drivers/dma/dw/pci.c:21:1: warning: useless storage class specifier in empty declaration }; ^ vim +21 drivers/dma/dw/pci.c 17 18 static struct dw_dma_pci_data { 19 const struct dw_dma_platform_data *pdata; 20 int (*probe)(struct dw_dma_chip *chip); > 21 }; 22 --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
diff --git a/drivers/dma/dw/Makefile b/drivers/dma/dw/Makefile index 2b949c2e4504..63ed895c09aa 100644 --- a/drivers/dma/dw/Makefile +++ b/drivers/dma/dw/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DW_DMAC_CORE) += dw_dmac_core.o -dw_dmac_core-objs := core.o +dw_dmac_core-objs := core.o dw.o idma32.o obj-$(CONFIG_DW_DMAC) += dw_dmac.o dw_dmac-objs := platform.o diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index e85b078fc207..711f4d19bbf1 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -138,42 +138,6 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) dwc->descs_allocated--; } -static void dwc_initialize_chan_idma32(struct dw_dma_chan *dwc) -{ - u32 cfghi = 0; - u32 cfglo = 0; - - /* Set default burst alignment */ - cfglo |= IDMA32C_CFGL_DST_BURST_ALIGN | IDMA32C_CFGL_SRC_BURST_ALIGN; - - /* Low 4 bits of the request lines */ - cfghi |= IDMA32C_CFGH_DST_PER(dwc->dws.dst_id & 0xf); - cfghi |= IDMA32C_CFGH_SRC_PER(dwc->dws.src_id & 0xf); - - /* Request line extension (2 bits) */ - cfghi |= IDMA32C_CFGH_DST_PER_EXT(dwc->dws.dst_id >> 4 & 0x3); - cfghi |= IDMA32C_CFGH_SRC_PER_EXT(dwc->dws.src_id >> 4 & 0x3); - - channel_writel(dwc, CFG_LO, cfglo); - channel_writel(dwc, CFG_HI, cfghi); -} - -static void dwc_initialize_chan_dw(struct dw_dma_chan *dwc) -{ - u32 cfghi = DWC_CFGH_FIFO_MODE; - u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority); - bool hs_polarity = dwc->dws.hs_polarity; - - cfghi |= DWC_CFGH_DST_PER(dwc->dws.dst_id); - cfghi |= DWC_CFGH_SRC_PER(dwc->dws.src_id); - - /* Set polarity of handshake interface */ - cfglo |= hs_polarity ? DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL : 0; - - channel_writel(dwc, CFG_LO, cfglo); - channel_writel(dwc, CFG_HI, cfghi); -} - static void dwc_initialize(struct dw_dma_chan *dwc) { struct dw_dma *dw = to_dw_dma(dwc->chan.device); @@ -181,10 +145,7 @@ static void dwc_initialize(struct dw_dma_chan *dwc) if (test_bit(DW_DMA_IS_INITIALIZED, &dwc->flags)) return; - if (dw->pdata->is_idma32) - dwc_initialize_chan_idma32(dwc); - else - dwc_initialize_chan_dw(dwc); + dw->initialize_chan(dwc); /* Enable interrupts */ channel_set_bit(dw, MASK.XFER, dwc->mask); @@ -213,37 +174,6 @@ static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc) cpu_relax(); } -static u32 bytes2block(struct dw_dma_chan *dwc, size_t bytes, - unsigned int width, size_t *len) -{ - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - u32 block; - - /* Always in bytes for iDMA 32-bit */ - if (dw->pdata->is_idma32) - width = 0; - - if ((bytes >> width) > dwc->block_size) { - block = dwc->block_size; - *len = block << width; - } else { - block = bytes >> width; - *len = bytes; - } - - return block; -} - -static size_t block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width) -{ - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - - if (dw->pdata->is_idma32) - return IDMA32C_CTLH_BLOCK_TS(block); - - return DWC_CTLH_BLOCK_TS(block) << width; -} - /*----------------------------------------------------------------------*/ /* Perform single block transfer */ @@ -389,10 +319,11 @@ static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc) /* Returns how many bytes were already received from source */ static inline u32 dwc_get_sent(struct dw_dma_chan *dwc) { + struct dw_dma *dw = to_dw_dma(dwc->chan.device); u32 ctlhi = channel_readl(dwc, CTL_HI); u32 ctllo = channel_readl(dwc, CTL_LO); - return block2bytes(dwc, ctlhi, ctllo >> 4 & 7); + return dw->block2bytes(dwc, ctlhi, ctllo >> 4 & 7); } static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) @@ -649,7 +580,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, unsigned int src_width; unsigned int dst_width; unsigned int data_width = dw->pdata->data_width[m_master]; - u32 ctllo; + u32 ctllo, ctlhi; u8 lms = DWC_LLP_LMS(m_master); dev_vdbg(chan2dev(chan), @@ -678,10 +609,12 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, if (!desc) goto err_desc_get; + ctlhi = dw->bytes2block(dwc, len - offset, src_width, &xfer_count); + lli_write(desc, sar, src + offset); lli_write(desc, dar, dest + offset); lli_write(desc, ctllo, ctllo); - lli_write(desc, ctlhi, bytes2block(dwc, len - offset, src_width, &xfer_count)); + lli_write(desc, ctlhi, ctlhi); desc->len = xfer_count; if (!first) { @@ -719,7 +652,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, struct dma_slave_config *sconfig = &dwc->dma_sconfig; struct dw_desc *prev; struct dw_desc *first; - u32 ctllo; + u32 ctllo, ctlhi; u8 m_master = dwc->dws.m_master; u8 lms = DWC_LLP_LMS(m_master); dma_addr_t reg; @@ -766,9 +699,11 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, if (!desc) goto err_desc_get; + ctlhi = dw->bytes2block(dwc, len, mem_width, &dlen); + lli_write(desc, sar, mem); lli_write(desc, dar, reg); - lli_write(desc, ctlhi, bytes2block(dwc, len, mem_width, &dlen)); + lli_write(desc, ctlhi, ctlhi); lli_write(desc, ctllo, ctllo | DWC_CTLL_SRC_WIDTH(mem_width)); desc->len = dlen; @@ -812,9 +747,11 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, if (!desc) goto err_desc_get; + ctlhi = dw->bytes2block(dwc, len, reg_width, &dlen); + lli_write(desc, sar, reg); lli_write(desc, dar, mem); - lli_write(desc, ctlhi, bytes2block(dwc, len, reg_width, &dlen)); + lli_write(desc, ctlhi, ctlhi); mem_width = __ffs(data_width | mem | dlen); lli_write(desc, ctllo, ctllo | DWC_CTLL_DST_WIDTH(mem_width)); desc->len = dlen; @@ -874,22 +811,12 @@ EXPORT_SYMBOL_GPL(dw_dma_filter); static int dwc_config(struct dma_chan *chan, struct dma_slave_config *sconfig) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dma_slave_config *sc = &dwc->dma_sconfig; struct dw_dma *dw = to_dw_dma(chan->device); - /* - * Fix sconfig's burst size according to dw_dmac. We need to convert - * them as: - * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3. - * - * NOTE: burst size 2 is not supported by DesignWare controller. - * iDMA 32-bit supports it. - */ - u32 s = dw->pdata->is_idma32 ? 1 : 2; memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); - sc->src_maxburst = sc->src_maxburst > 1 ? fls(sc->src_maxburst) - s : 0; - sc->dst_maxburst = sc->dst_maxburst > 1 ? fls(sc->dst_maxburst) - s : 0; + dw->encode_maxburst(dwc, &dwc->dma_sconfig.src_maxburst); + dw->encode_maxburst(dwc, &dwc->dma_sconfig.dst_maxburst); return 0; } @@ -898,16 +825,9 @@ static void dwc_chan_pause(struct dw_dma_chan *dwc, bool drain) { struct dw_dma *dw = to_dw_dma(dwc->chan.device); unsigned int count = 20; /* timeout iterations */ - u32 cfglo; - cfglo = channel_readl(dwc, CFG_LO); - if (dw->pdata->is_idma32) { - if (drain) - cfglo |= IDMA32C_CFGL_CH_DRAIN; - else - cfglo &= ~IDMA32C_CFGL_CH_DRAIN; - } - channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); + dw->suspend_chan(dwc, drain); + while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY) && count--) udelay(2); @@ -1056,33 +976,7 @@ static void dwc_issue_pending(struct dma_chan *chan) /*----------------------------------------------------------------------*/ -/* - * Program FIFO size of channels. - * - * By default full FIFO (512 bytes) is assigned to channel 0. Here we - * slice FIFO on equal parts between channels. - */ -static void idma32_fifo_partition(struct dw_dma *dw) -{ - u64 value = IDMA32C_FP_PSIZE_CH0(64) | IDMA32C_FP_PSIZE_CH1(64) | - IDMA32C_FP_UPDATE; - u64 fifo_partition = 0; - - if (!dw->pdata->is_idma32) - return; - - /* Fill FIFO_PARTITION low bits (Channels 0..1, 4..5) */ - fifo_partition |= value << 0; - - /* Fill FIFO_PARTITION high bits (Channels 2..3, 6..7) */ - fifo_partition |= value << 32; - - /* Program FIFO Partition registers - 128 bytes for each channel */ - idma32_writeq(dw, FIFO_PARTITION1, fifo_partition); - idma32_writeq(dw, FIFO_PARTITION0, fifo_partition); -} - -static void dw_dma_off(struct dw_dma *dw) +void do_dma_off(struct dw_dma *dw) { unsigned int i; @@ -1101,7 +995,7 @@ static void dw_dma_off(struct dw_dma *dw) clear_bit(DW_DMA_IS_INITIALIZED, &dw->chan[i].flags); } -static void dw_dma_on(struct dw_dma *dw) +void do_dma_on(struct dw_dma *dw) { dma_writel(dw, CFG, DW_CFG_DMA_EN); } @@ -1137,7 +1031,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) /* Enable controller here if needed */ if (!dw->in_use) - dw_dma_on(dw); + do_dma_on(dw); dw->in_use |= dwc->mask; return 0; @@ -1175,30 +1069,25 @@ static void dwc_free_chan_resources(struct dma_chan *chan) /* Disable controller in case it was a last user */ dw->in_use &= ~dwc->mask; if (!dw->in_use) - dw_dma_off(dw); + do_dma_off(dw); dev_vdbg(chan2dev(chan), "%s: done\n", __func__); } -int dw_dma_probe(struct dw_dma_chip *chip) +int do_dma_probe(struct dw_dma_chip *chip) { + struct dw_dma *dw = chip->dw; struct dw_dma_platform_data *pdata; - struct dw_dma *dw; bool autocfg = false; unsigned int dw_params; unsigned int i; int err; - dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL); - if (!dw) - return -ENOMEM; - dw->pdata = devm_kzalloc(chip->dev, sizeof(*dw->pdata), GFP_KERNEL); if (!dw->pdata) return -ENOMEM; dw->regs = chip->regs; - chip->dw = dw; pm_runtime_get_sync(chip->dev); @@ -1250,15 +1139,10 @@ int dw_dma_probe(struct dw_dma_chip *chip) dw->all_chan_mask = (1 << pdata->nr_channels) - 1; /* Force dma off, just in case */ - dw_dma_off(dw); - - idma32_fifo_partition(dw); + dw->disable(dw); /* Device and instance ID for IRQ and DMA pool */ - if (pdata->is_idma32) - snprintf(dw->name, sizeof(dw->name), "idma32:dmac%d", chip->id); - else - snprintf(dw->name, sizeof(dw->name), "dw:dmac%d", chip->id); + dw->set_device_name(dw, chip->id); /* Create a pool of consistent memory blocks for hardware descriptors */ dw->desc_pool = dmam_pool_create(dw->name, chip->dev, @@ -1382,16 +1266,15 @@ int dw_dma_probe(struct dw_dma_chip *chip) pm_runtime_put_sync_suspend(chip->dev); return err; } -EXPORT_SYMBOL_GPL(dw_dma_probe); -int dw_dma_remove(struct dw_dma_chip *chip) +int do_dma_remove(struct dw_dma_chip *chip) { struct dw_dma *dw = chip->dw; struct dw_dma_chan *dwc, *_dwc; pm_runtime_get_sync(chip->dev); - dw_dma_off(dw); + do_dma_off(dw); dma_async_device_unregister(&dw->dma); free_irq(chip->irq, dw); @@ -1406,27 +1289,24 @@ int dw_dma_remove(struct dw_dma_chip *chip) pm_runtime_put_sync_suspend(chip->dev); return 0; } -EXPORT_SYMBOL_GPL(dw_dma_remove); -int dw_dma_disable(struct dw_dma_chip *chip) +int do_dw_dma_disable(struct dw_dma_chip *chip) { struct dw_dma *dw = chip->dw; - dw_dma_off(dw); + dw->disable(dw); return 0; } -EXPORT_SYMBOL_GPL(dw_dma_disable); +EXPORT_SYMBOL_GPL(do_dw_dma_disable); -int dw_dma_enable(struct dw_dma_chip *chip) +int do_dw_dma_enable(struct dw_dma_chip *chip) { struct dw_dma *dw = chip->dw; - idma32_fifo_partition(dw); - - dw_dma_on(dw); + dw->disable(dw); return 0; } -EXPORT_SYMBOL_GPL(dw_dma_enable); +EXPORT_SYMBOL_GPL(do_dw_dma_enable); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller core driver"); diff --git a/drivers/dma/dw/dw.c b/drivers/dma/dw/dw.c new file mode 100644 index 000000000000..998a9eeb561d --- /dev/null +++ b/drivers/dma/dw/dw.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2007-2008 Atmel Corporation +// Copyright (C) 2010-2011 ST Microelectronics +// Copyright (C) 2013,2018 Intel Corporation + +#include <linux/bitops.h> +#include <linux/errno.h> +#include <linux/slab.h> + +#include "internal.h" + +static void dw_dma_initialize_chan(struct dw_dma_chan *dwc) +{ + u32 cfghi = DWC_CFGH_FIFO_MODE; + u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority); + bool hs_polarity = dwc->dws.hs_polarity; + + cfghi |= DWC_CFGH_DST_PER(dwc->dws.dst_id); + cfghi |= DWC_CFGH_SRC_PER(dwc->dws.src_id); + + /* Set polarity of handshake interface */ + cfglo |= hs_polarity ? DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL : 0; + + channel_writel(dwc, CFG_LO, cfglo); + channel_writel(dwc, CFG_HI, cfghi); +} + +static void dw_dma_suspend_chan(struct dw_dma_chan *dwc, bool drain) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + + channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); +} + +static u32 dw_dma_bytes2block(struct dw_dma_chan *dwc, + size_t bytes, unsigned int width, size_t *len) +{ + u32 block; + + if ((bytes >> width) > dwc->block_size) { + block = dwc->block_size; + *len = block << width; + } else { + block = bytes >> width; + *len = bytes; + } + + return block; +} + +static size_t dw_dma_block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width) +{ + return DWC_CTLH_BLOCK_TS(block) << width; +} + +static void dw_dma_encode_maxburst(struct dw_dma_chan *dwc, u32 *maxburst) +{ + /* + * Fix burst size according to dw_dmac. We need to convert them as: + * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3. + */ + *maxburst = *maxburst > 1 ? fls(*maxburst) - 2 : 0; +} + +static void dw_dma_set_device_name(struct dw_dma *dw, int id) +{ + snprintf(dw->name, sizeof(dw->name), "dw:dmac%d", id); +} + +static void dw_dma_disable(struct dw_dma *dw) +{ + do_dma_off(dw); +} + +static void dw_dma_enable(struct dw_dma *dw) +{ + do_dma_on(dw); +} + +int dw_dma_probe(struct dw_dma_chip *chip) +{ + struct dw_dma *dw; + + dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL); + if (!dw) + return -ENOMEM; + + /* Channel operations */ + dw->initialize_chan = dw_dma_initialize_chan; + dw->suspend_chan = dw_dma_suspend_chan; + dw->encode_maxburst = dw_dma_encode_maxburst; + dw->bytes2block = dw_dma_bytes2block; + dw->block2bytes = dw_dma_block2bytes; + + /* Device operations */ + dw->set_device_name = dw_dma_set_device_name; + dw->disable = dw_dma_disable; + dw->enable = dw_dma_enable; + + chip->dw = dw; + return do_dma_probe(chip); +} +EXPORT_SYMBOL_GPL(dw_dma_probe); + +int dw_dma_remove(struct dw_dma_chip *chip) +{ + return do_dma_remove(chip); +} +EXPORT_SYMBOL_GPL(dw_dma_remove); diff --git a/drivers/dma/dw/idma32.c b/drivers/dma/dw/idma32.c new file mode 100644 index 000000000000..d06fb00fb862 --- /dev/null +++ b/drivers/dma/dw/idma32.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013,2018 Intel Corporation + +#include <linux/bitops.h> +#include <linux/errno.h> +#include <linux/slab.h> + +#include "internal.h" + +static void idma32_initialize_chan(struct dw_dma_chan *dwc) +{ + u32 cfghi = 0; + u32 cfglo = 0; + + /* Set default burst alignment */ + cfglo |= IDMA32C_CFGL_DST_BURST_ALIGN | IDMA32C_CFGL_SRC_BURST_ALIGN; + + /* Low 4 bits of the request lines */ + cfghi |= IDMA32C_CFGH_DST_PER(dwc->dws.dst_id & 0xf); + cfghi |= IDMA32C_CFGH_SRC_PER(dwc->dws.src_id & 0xf); + + /* Request line extension (2 bits) */ + cfghi |= IDMA32C_CFGH_DST_PER_EXT(dwc->dws.dst_id >> 4 & 0x3); + cfghi |= IDMA32C_CFGH_SRC_PER_EXT(dwc->dws.src_id >> 4 & 0x3); + + channel_writel(dwc, CFG_LO, cfglo); + channel_writel(dwc, CFG_HI, cfghi); +} + +static void idma32_suspend_chan(struct dw_dma_chan *dwc, bool drain) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + + if (drain) + cfglo |= IDMA32C_CFGL_CH_DRAIN; + else + cfglo &= ~IDMA32C_CFGL_CH_DRAIN; + + channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); +} + +static u32 idma32_bytes2block(struct dw_dma_chan *dwc, + size_t bytes, unsigned int width, size_t *len) +{ + u32 block; + + if (bytes > dwc->block_size) { + block = dwc->block_size; + *len = dwc->block_size; + } else { + block = bytes; + *len = bytes; + } + + return block; +} + +static size_t idma32_block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width) +{ + return IDMA32C_CTLH_BLOCK_TS(block); +} + +static void idma32_encode_maxburst(struct dw_dma_chan *dwc, u32 *maxburst) +{ + *maxburst = *maxburst > 1 ? fls(*maxburst) - 1 : 0; +} + +static void idma32_set_device_name(struct dw_dma *dw, int id) +{ + snprintf(dw->name, sizeof(dw->name), "idma32:dmac%d", id); +} + +/* + * Program FIFO size of channels. + * + * By default full FIFO (512 bytes) is assigned to channel 0. Here we + * slice FIFO on equal parts between channels. + */ +static void idma32_fifo_partition(struct dw_dma *dw) +{ + u64 value = IDMA32C_FP_PSIZE_CH0(64) | IDMA32C_FP_PSIZE_CH1(64) | + IDMA32C_FP_UPDATE; + u64 fifo_partition = 0; + + /* Fill FIFO_PARTITION low bits (Channels 0..1, 4..5) */ + fifo_partition |= value << 0; + + /* Fill FIFO_PARTITION high bits (Channels 2..3, 6..7) */ + fifo_partition |= value << 32; + + /* Program FIFO Partition registers - 128 bytes for each channel */ + idma32_writeq(dw, FIFO_PARTITION1, fifo_partition); + idma32_writeq(dw, FIFO_PARTITION0, fifo_partition); +} + +static void idma32_disable(struct dw_dma *dw) +{ + do_dma_off(dw); + idma32_fifo_partition(dw); +} + +static void idma32_enable(struct dw_dma *dw) +{ + idma32_fifo_partition(dw); + do_dma_on(dw); +} + +int idma32_dma_probe(struct dw_dma_chip *chip) +{ + struct dw_dma *dw; + + dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL); + if (!dw) + return -ENOMEM; + + /* Channel operations */ + dw->initialize_chan = idma32_initialize_chan; + dw->suspend_chan = idma32_suspend_chan; + dw->encode_maxburst = idma32_encode_maxburst; + dw->bytes2block = idma32_bytes2block; + dw->block2bytes = idma32_block2bytes; + + /* Device operations */ + dw->set_device_name = idma32_set_device_name; + dw->disable = idma32_disable; + dw->enable = idma32_enable; + + chip->dw = dw; + return do_dma_probe(chip); +} +EXPORT_SYMBOL_GPL(idma32_dma_probe); + +int idma32_dma_remove(struct dw_dma_chip *chip) +{ + return do_dma_remove(chip); +} +EXPORT_SYMBOL_GPL(idma32_dma_remove); diff --git a/drivers/dma/dw/internal.h b/drivers/dma/dw/internal.h index 41439732ff6b..1f2ddfe745bb 100644 --- a/drivers/dma/dw/internal.h +++ b/drivers/dma/dw/internal.h @@ -15,8 +15,14 @@ #include "regs.h" -int dw_dma_disable(struct dw_dma_chip *chip); -int dw_dma_enable(struct dw_dma_chip *chip); +int do_dma_probe(struct dw_dma_chip *chip); +int do_dma_remove(struct dw_dma_chip *chip); + +void do_dma_on(struct dw_dma *dw); +void do_dma_off(struct dw_dma *dw); + +int do_dw_dma_disable(struct dw_dma_chip *chip); +int do_dw_dma_enable(struct dw_dma_chip *chip); extern bool dw_dma_filter(struct dma_chan *chan, void *param); diff --git a/drivers/dma/dw/pci.c b/drivers/dma/dw/pci.c index 61f2274f6dd4..f5ea58cc027b 100644 --- a/drivers/dma/dw/pci.c +++ b/drivers/dma/dw/pci.c @@ -15,11 +15,19 @@ #include "internal.h" -static struct dw_dma_platform_data mrfld_pdata = { +static struct dw_dma_pci_data { + const struct dw_dma_platform_data *pdata; + int (*probe)(struct dw_dma_chip *chip); +}; + +static const struct dw_dma_pci_data dw_pci_data = { + .probe = dw_dma_probe, +}; + +static const struct dw_dma_platform_data idma32_pdata = { .nr_channels = 8, .is_private = true, .is_memcpy = true, - .is_idma32 = true, .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, .chan_priority = CHAN_PRIORITY_ASCENDING, .block_size = 131071, @@ -28,9 +36,14 @@ static struct dw_dma_platform_data mrfld_pdata = { .multi_block = {true, true, true, true, true, true, true, true}, }; +static const struct dw_dma_pci_data idma32_pci_data = { + .pdata = &idma32_pdata, + .probe = idma32_dma_probe, +}; + static int dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) { - const struct dw_dma_platform_data *pdata = (void *)pid->driver_data; + const struct dw_dma_pci_data *data = (void *)pid->driver_data; struct dw_dma_chip *chip; int ret; @@ -63,9 +76,9 @@ static int dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) chip->id = pdev->devfn; chip->regs = pcim_iomap_table(pdev)[0]; chip->irq = pdev->irq; - chip->pdata = pdata; + chip->pdata = data->pdata; - ret = dw_dma_probe(chip); + ret = data->probe(chip); if (ret) return ret; @@ -91,7 +104,7 @@ static int dw_pci_suspend_late(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct dw_dma_chip *chip = pci_get_drvdata(pci); - return dw_dma_disable(chip); + return do_dw_dma_disable(chip); }; static int dw_pci_resume_early(struct device *dev) @@ -99,7 +112,7 @@ static int dw_pci_resume_early(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct dw_dma_chip *chip = pci_get_drvdata(pci); - return dw_dma_enable(chip); + return do_dw_dma_enable(chip); }; #endif /* CONFIG_PM_SLEEP */ @@ -110,24 +123,24 @@ static const struct dev_pm_ops dw_pci_dev_pm_ops = { static const struct pci_device_id dw_pci_id_table[] = { /* Medfield (GPDMA) */ - { PCI_VDEVICE(INTEL, 0x0827) }, + { PCI_VDEVICE(INTEL, 0x0827), (kernel_ulong_t)&dw_pci_data }, /* BayTrail */ - { PCI_VDEVICE(INTEL, 0x0f06) }, - { PCI_VDEVICE(INTEL, 0x0f40) }, + { PCI_VDEVICE(INTEL, 0x0f06), (kernel_ulong_t)&dw_pci_data }, + { PCI_VDEVICE(INTEL, 0x0f40), (kernel_ulong_t)&dw_pci_data }, - /* Merrifield iDMA 32-bit (GPDMA) */ - { PCI_VDEVICE(INTEL, 0x11a2), (kernel_ulong_t)&mrfld_pdata }, + /* Merrifield */ + { PCI_VDEVICE(INTEL, 0x11a2), (kernel_ulong_t)&idma32_pci_data }, /* Braswell */ - { PCI_VDEVICE(INTEL, 0x2286) }, - { PCI_VDEVICE(INTEL, 0x22c0) }, + { PCI_VDEVICE(INTEL, 0x2286), (kernel_ulong_t)&dw_pci_data }, + { PCI_VDEVICE(INTEL, 0x22c0), (kernel_ulong_t)&dw_pci_data }, /* Haswell */ - { PCI_VDEVICE(INTEL, 0x9c60) }, + { PCI_VDEVICE(INTEL, 0x9c60), (kernel_ulong_t)&dw_pci_data }, /* Broadwell */ - { PCI_VDEVICE(INTEL, 0x9ce0) }, + { PCI_VDEVICE(INTEL, 0x9ce0), (kernel_ulong_t)&dw_pci_data }, { } }; diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c index f01b2c173fa6..6bf46c9b7ee4 100644 --- a/drivers/dma/dw/platform.c +++ b/drivers/dma/dw/platform.c @@ -258,7 +258,7 @@ static void dw_shutdown(struct platform_device *pdev) struct dw_dma_chip *chip = platform_get_drvdata(pdev); /* - * We have to call dw_dma_disable() to stop any ongoing transfer. On + * We have to call do_dw_dma_disable() to stop any ongoing transfer. On * some platforms we can't do that since DMA device is powered off. * Moreover we have no possibility to check if the platform is affected * or not. That's why we call pm_runtime_get_sync() / pm_runtime_put() @@ -267,7 +267,7 @@ static void dw_shutdown(struct platform_device *pdev) * used by the driver. */ pm_runtime_get_sync(chip->dev); - dw_dma_disable(chip); + do_dw_dma_disable(chip); pm_runtime_put_sync_suspend(chip->dev); clk_disable_unprepare(chip->clk); @@ -297,7 +297,7 @@ static int dw_suspend_late(struct device *dev) { struct dw_dma_chip *chip = dev_get_drvdata(dev); - dw_dma_disable(chip); + do_dw_dma_disable(chip); clk_disable_unprepare(chip->clk); return 0; @@ -312,7 +312,7 @@ static int dw_resume_early(struct device *dev) if (ret) return ret; - return dw_dma_enable(chip); + return do_dw_dma_enable(chip); } #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h index 09e7dfdbb790..90e46e69c047 100644 --- a/drivers/dma/dw/regs.h +++ b/drivers/dma/dw/regs.h @@ -308,6 +308,19 @@ struct dw_dma { u8 all_chan_mask; u8 in_use; + /* Channel operations */ + void (*initialize_chan)(struct dw_dma_chan *dwc); + void (*suspend_chan)(struct dw_dma_chan *dwc, bool drain); + void (*encode_maxburst)(struct dw_dma_chan *dwc, u32 *maxburst); + u32 (*bytes2block)(struct dw_dma_chan *dwc, size_t bytes, + unsigned int width, size_t *len); + size_t (*block2bytes)(struct dw_dma_chan *dwc, u32 block, u32 width); + + /* Device operations */ + void (*set_device_name)(struct dw_dma *dw, int id); + void (*disable)(struct dw_dma *dw); + void (*enable)(struct dw_dma *dw); + /* platform data */ struct dw_dma_platform_data *pdata; }; diff --git a/include/linux/dma/dw.h b/include/linux/dma/dw.h index e166cac8e870..d643d331c20e 100644 --- a/include/linux/dma/dw.h +++ b/include/linux/dma/dw.h @@ -45,9 +45,13 @@ struct dw_dma_chip { #if IS_ENABLED(CONFIG_DW_DMAC_CORE) int dw_dma_probe(struct dw_dma_chip *chip); int dw_dma_remove(struct dw_dma_chip *chip); +int idma32_dma_probe(struct dw_dma_chip *chip); +int idma32_dma_remove(struct dw_dma_chip *chip); #else static inline int dw_dma_probe(struct dw_dma_chip *chip) { return -ENODEV; } static inline int dw_dma_remove(struct dw_dma_chip *chip) { return 0; } +static inline int idma32_dma_probe(struct dw_dma_chip *chip) { return -ENODEV; } +static inline int idma32_dma_remove(struct dw_dma_chip *chip) { return 0; } #endif /* CONFIG_DW_DMAC_CORE */ #endif /* _DMA_DW_H */ diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h index 896cb71a382c..e69e415d0d98 100644 --- a/include/linux/platform_data/dma-dw.h +++ b/include/linux/platform_data/dma-dw.h @@ -41,7 +41,6 @@ struct dw_dma_slave { * @is_private: The device channels should be marked as private and not for * by the general purpose DMA channel allocator. * @is_memcpy: The device channels do support memory-to-memory transfers. - * @is_idma32: The type of the DMA controller is iDMA32 * @chan_allocation_order: Allocate channels starting from 0 or 7 * @chan_priority: Set channel priority increasing from 0 to 7 or 7 to 0. * @block_size: Maximum block size supported by the controller @@ -54,7 +53,6 @@ struct dw_dma_platform_data { unsigned int nr_channels; bool is_private; bool is_memcpy; - bool is_idma32; #define CHAN_ALLOCATION_ASCENDING 0 /* zero to seven */ #define CHAN_ALLOCATION_DESCENDING 1 /* seven to zero */ unsigned char chan_allocation_order;
Here is a kinda big refactoring that should have been done in the first place, when Intel iDMA 32-bit support appeared. It splits operations which are different to Synopsys DesignWare and Intel iDMA 32-bit controllers. No functional change intended. Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> --- drivers/dma/dw/Makefile | 2 +- drivers/dma/dw/core.c | 188 +++++---------------------- drivers/dma/dw/dw.c | 109 ++++++++++++++++ drivers/dma/dw/idma32.c | 137 +++++++++++++++++++ drivers/dma/dw/internal.h | 10 +- drivers/dma/dw/pci.c | 45 ++++--- drivers/dma/dw/platform.c | 8 +- drivers/dma/dw/regs.h | 13 ++ include/linux/dma/dw.h | 4 + include/linux/platform_data/dma-dw.h | 2 - 10 files changed, 339 insertions(+), 179 deletions(-) create mode 100644 drivers/dma/dw/dw.c create mode 100644 drivers/dma/dw/idma32.c