Messages in this thread Patch in this message | | | From | Andy Shevchenko <> | Subject | [PATCH 5/7] dw_dmac: autoconfigure data_width or get it via platform data | Date | Mon, 17 Sep 2012 10:39:35 +0300 |
| |
Not all of the controllers support the 64 bit data width. Make it configurable via platform data. The driver will try to get a value from the component parameters, otherwise it will use the platform data..
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> --- arch/arm/mach-spear13xx/spear13xx.c | 2 ++ arch/avr32/mach-at32ap/at32ap700x.c | 2 ++ drivers/dma/dw_dmac.c | 46 +++++++++++++++++++++++++++++++---- drivers/dma/dw_dmac_regs.h | 13 ++++++++++ include/linux/dw_dmac.h | 5 ++++ 5 files changed, 63 insertions(+), 5 deletions(-)
diff --git a/arch/arm/mach-spear13xx/spear13xx.c b/arch/arm/mach-spear13xx/spear13xx.c index c64d812..6a7dfe1 100644 --- a/arch/arm/mach-spear13xx/spear13xx.c +++ b/arch/arm/mach-spear13xx/spear13xx.c @@ -79,6 +79,8 @@ struct dw_dma_platform_data dmac_plat_data = { .chan_allocation_order = CHAN_ALLOCATION_DESCENDING, .chan_priority = CHAN_PRIORITY_DESCENDING, .block_size = 4095U, + .nr_masters = 2, + .data_width = { 3, 3, 0, 0 }, }; void __init spear13xx_l2x0_init(void) diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 2c4aefe..b323d8d 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -606,6 +606,8 @@ static void __init genclk_init_parent(struct clk *clk) static struct dw_dma_platform_data dw_dmac0_data = { .nr_channels = 3, .block_size = 4095U, + .nr_masters = 2, + .data_width = { 2, 2, 0, 0 }, }; static struct resource dw_dmac0_resource[] = { diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index ab718ee..fdb7d5a 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -38,12 +38,22 @@ * which does not support descriptor writeback. */ +static inline unsigned int dwc_get_dms(struct dw_dma_slave *slave) +{ + return slave ? slave->dst_master : 0; +} + +static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) +{ + return slave ? slave->src_master : 1; +} + #define DWC_DEFAULT_CTLLO(_chan) ({ \ struct dw_dma_slave *__slave = (_chan->private); \ struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \ struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \ - int _dms = __slave ? __slave->dst_master : 0; \ - int _sms = __slave ? __slave->src_master : 1; \ + int _dms = dwc_get_dms(__slave); \ + int _sms = dwc_get_sms(__slave); \ u8 _smsize = __slave ? _sconfig->src_maxburst : \ DW_DMA_MSIZE_16; \ u8 _dmsize = __slave ? _sconfig->dst_maxburst : \ @@ -647,7 +657,11 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, return NULL; } - src_width = dst_width = dwc_fast_fls(src | dest | len); + src_width = dst_width = min_t(unsigned int, + /* For memory-to-memory transfers we + * always use AHB master 1 */ + dwc->dw->data_width[0], + dwc_fast_fls(src | dest | len)); ctllo = DWC_DEFAULT_CTLLO(chan) | DWC_CTLL_DST_WIDTH(dst_width) @@ -717,6 +731,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, dma_addr_t reg; unsigned int reg_width; unsigned int mem_width; + unsigned int data_width; unsigned int i; struct scatterlist *sg; size_t total_len = 0; @@ -740,6 +755,8 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) : DWC_CTLL_FC(DW_DMA_FC_D_M2P); + data_width = dwc->dw->data_width[dwc_get_sms(dws)]; + for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; u32 len, dlen, mem; @@ -747,7 +764,9 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, mem = sg_dma_address(sg); len = sg_dma_len(sg); - mem_width = dwc_fast_fls(mem | len); + mem_width = min_t(unsigned int, + data_width, + dwc_fast_fls(mem | len)); slave_sg_todev_fill_desc: desc = dwc_desc_get(dwc); @@ -800,6 +819,8 @@ slave_sg_todev_fill_desc: ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) : DWC_CTLL_FC(DW_DMA_FC_D_P2M); + data_width = dwc->dw->data_width[dwc_get_dms(dws)]; + for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; u32 len, dlen, mem; @@ -807,7 +828,9 @@ slave_sg_todev_fill_desc: mem = sg_dma_address(sg); len = sg_dma_len(sg); - mem_width = dwc_fast_fls(mem | len); + mem_width = min_t(unsigned int, + data_width, + dwc_fast_fls(mem | len)); slave_sg_fromdev_fill_desc: desc = dwc_desc_get(dwc); @@ -1413,6 +1436,17 @@ static int __devinit dw_probe(struct platform_device *pdev) /* get hardware configuration parameters */ max_blk_size = dma_readl(dw, MAX_BLK_SIZE); + if (autocfg) + dw->nr_masters = (dw_params >> DW_PARAMS_NR_MASTER & 3) + 1; + else + dw->nr_masters = pdata->nr_masters; + for (i = 0; i < dw->nr_masters; i++) { + if (autocfg) + dw->data_width[i] = + (dw_params >> DW_PARAMS_DATA_WIDTH(i) & 3) + 2; + else + dw->data_width[i] = pdata->data_width[i]; + } /* Calculate all channel mask before DMA setup */ dw->all_chan_mask = (1 << nr_channels) - 1; @@ -1460,6 +1494,8 @@ static int __devinit dw_probe(struct platform_device *pdev) channel_clear_bit(dw, CH_EN, dwc->mask); + dwc->dw = dw; + /* hardware configuration */ if (autocfg) { dwc->block_size = diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 233f5e5..9beaef0 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -106,6 +106,12 @@ struct dw_dma_regs { /* Bitfields in DW_PARAMS */ #define DW_PARAMS_NR_CHAN 8 /* number of channels */ +#define DW_PARAMS_NR_MASTER 11 /* number of AHB masters */ +#define DW_PARAMS_DATA_WIDTH(n) (15 + 2 * (n)) +#define DW_PARAMS_DATA_WIDTH1 15 /* master 1 data width */ +#define DW_PARAMS_DATA_WIDTH2 17 /* master 2 data width */ +#define DW_PARAMS_DATA_WIDTH3 19 /* master 3 data width */ +#define DW_PARAMS_DATA_WIDTH4 21 /* master 4 data width */ #define DW_PARAMS_EN 28 /* encoded parameters */ /* Bitfields in CTL_LO */ @@ -191,6 +197,9 @@ struct dw_dma_chan { /* configuration passed via DMA_SLAVE_CONFIG */ struct dma_slave_config dma_sconfig; + + /* backlink to dw_dma */ + struct dw_dma *dw; }; static inline struct dw_dma_chan_regs __iomem * @@ -217,6 +226,10 @@ struct dw_dma { u8 all_chan_mask; + /* hardware configuration */ + unsigned char nr_masters; + unsigned char data_width[4]; + struct dw_dma_chan chan[0]; }; diff --git a/include/linux/dw_dmac.h b/include/linux/dw_dmac.h index 3315ef9..e1c8c9e 100644 --- a/include/linux/dw_dmac.h +++ b/include/linux/dw_dmac.h @@ -20,6 +20,9 @@ * @is_private: The device channels should be marked as private and not for * by the general purpose DMA channel allocator. * @block_size: Maximum block size supported by the controller + * @nr_masters: Number of AHB masters supported by the controller + * @data_width: Maximum data width supported by hardware per AHB master + * (0 - 8bits, 1 - 16bits, ..., 5 - 256bits) */ struct dw_dma_platform_data { unsigned int nr_channels; @@ -31,6 +34,8 @@ struct dw_dma_platform_data { #define CHAN_PRIORITY_DESCENDING 1 /* chan7 highest */ unsigned char chan_priority; unsigned short block_size; + unsigned char nr_masters; + unsigned char data_width[4]; }; /* bursts size */ -- 1.7.10.4
| |