From patchwork Mon Mar 29 23:36:04 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 89142 Received: from lists.sourceforge.net (lists.sourceforge.net [216.34.181.88]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o2U0HA1x032032 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Tue, 30 Mar 2010 00:17:48 GMT Received: from localhost ([127.0.0.1] helo=sfs-ml-2.v29.ch3.sourceforge.com) by sfs-ml-2.v29.ch3.sourceforge.com with esmtp (Exim 4.69) (envelope-from ) id 1NwP8o-0002zO-DF; Tue, 30 Mar 2010 00:17:10 +0000 Received: from sfi-mx-4.v28.ch3.sourceforge.com ([172.29.28.124] helo=mx.sourceforge.net) by sfs-ml-2.v29.ch3.sourceforge.com with esmtp (Exim 4.69) (envelope-from ) id 1NwP8n-0002zE-SQ for spi-devel-general@lists.sourceforge.net; Tue, 30 Mar 2010 00:17:09 +0000 X-ACL-Warn: Received: from mail.df.lth.se ([194.47.250.12] helo=df.lth.se) by sfi-mx-4.v28.ch3.sourceforge.com with esmtps (TLSv1:AES256-SHA:256) (Exim 4.69) id 1NwP8l-0003Ib-Ao for spi-devel-general@lists.sourceforge.net; Tue, 30 Mar 2010 00:17:09 +0000 Received: from mer.df.lth.se (mer.df.lth.se [194.47.250.37]) by df.lth.se (8.14.2/8.13.7) with ESMTP id o2TNaTtX023155 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Tue, 30 Mar 2010 01:36:29 +0200 (CEST) Received: from mer.df.lth.se (triad@localhost.localdomain [127.0.0.1]) by mer.df.lth.se (8.14.3/8.14.3/Debian-9.1) with ESMTP id o2TNa5p4006232; Tue, 30 Mar 2010 01:36:05 +0200 Received: (from triad@localhost) by mer.df.lth.se (8.14.3/8.14.3/Submit) id o2TNa57A006231; Tue, 30 Mar 2010 01:36:05 +0200 From: Linus Walleij To: linux-arm-kernel@lists.infradead.org, Dan Williams , Grant Likely Date: Tue, 30 Mar 2010 01:36:04 +0200 Message-Id: <1269905764-5977-1-git-send-email-linus.walleij@stericsson.com> X-Mailer: git-send-email 1.6.2.rc1 X-Spam-Score: 0.0 (/) X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. _SUMMARY_ X-Headers-End: 1NwP8l-0003Ib-Ao Cc: STEricsson_nomadik_linux@list.st.com, spi-devel-general@lists.sourceforge.net, linux-mmc@vger.kernel.org, Linus Walleij Subject: [spi-devel-general] [PATCH 2/6] ARM: add generic PrimeCell interface to COH 901 318 X-BeenThere: spi-devel-general@lists.sourceforge.net X-Mailman-Version: 2.1.9 Precedence: list List-Id: Linux SPI core/device drivers discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: spi-devel-general-bounces@lists.sourceforge.net X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Tue, 30 Mar 2010 00:17:48 +0000 (UTC) diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index 4233440..1d5ed16 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "coh901318_lli.h" @@ -72,6 +73,9 @@ struct coh901318_chan { unsigned long nbr_active_done; unsigned long busy; + u32 amba_addr; + u32 amba_ctrl; + struct coh901318_base *base; }; @@ -190,6 +194,9 @@ static inline struct coh901318_chan *to_coh901318_chan(struct dma_chan *chan) static inline dma_addr_t cohc_dev_addr(struct coh901318_chan *cohc) { + /* PrimeCell supplied address will take precedence */ + if (cohc->amba_addr) + return cohc->amba_addr; return cohc->base->platform->chan_conf[cohc->id].dev_addr; } @@ -1055,6 +1062,14 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, params = cohc_chan_param(cohc); config = params->config; + /* + * Add primecell-specific control on top, make + * sure the bits you set per peripheral channel are + * cleared in the default config from the platform. + */ + ctrl_chained |= cohc->amba_ctrl; + ctrl_last |= cohc->amba_ctrl; + ctrl |= cohc->amba_ctrl; if (direction == DMA_TO_DEVICE) { u32 tx_flags = COH901318_CX_CTRL_PRDD_SOURCE | @@ -1113,6 +1128,12 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, if (ret) goto err_lli_fill; + /* + * Set the default ctrl for the channel to the one from the lli, + * things may have changed due to odd buffer alignment etc. + */ + coh901318_set_ctrl(cohc, lli->control); + COH_DBG(coh901318_list_print(cohc, lli)); /* Pick a descriptor to handle this transfer */ @@ -1239,6 +1260,131 @@ coh901318_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd) return 0; } + +/* + * Here we wrap in the PrimeCell dma interface + */ +struct burst_table { + int burst_8bit; + int burst_16bit; + int burst_32bit; + u32 reg; +}; + +static const struct burst_table burst_sizes[] = { + { + .burst_8bit = 64, + .burst_16bit = 32, + .burst_32bit = 16, + .reg = COH901318_CX_CTRL_BURST_COUNT_64_BYTES, + }, + { + .burst_8bit = 48, + .burst_16bit = 24, + .burst_32bit = 12, + .reg = COH901318_CX_CTRL_BURST_COUNT_48_BYTES, + }, + { + .burst_8bit = 32, + .burst_16bit = 16, + .burst_32bit = 8, + .reg = COH901318_CX_CTRL_BURST_COUNT_32_BYTES, + }, + { + .burst_8bit = 16, + .burst_16bit = 8, + .burst_32bit = 4, + .reg = COH901318_CX_CTRL_BURST_COUNT_16_BYTES, + }, + { + .burst_8bit = 8, + .burst_16bit = 4, + .burst_32bit = 2, + .reg = COH901318_CX_CTRL_BURST_COUNT_8_BYTES, + }, + { + .burst_8bit = 4, + .burst_16bit = 2, + .burst_32bit = 1, + .reg = COH901318_CX_CTRL_BURST_COUNT_4_BYTES, + }, + { + .burst_8bit = 2, + .burst_16bit = 1, + .burst_32bit = 0, + .reg = COH901318_CX_CTRL_BURST_COUNT_2_BYTES, + }, + { + .burst_8bit = 1, + .burst_16bit = 0, + .burst_32bit = 0, + .reg = COH901318_CX_CTRL_BURST_COUNT_1_BYTE, + }, +}; + +void dma_set_ambaconfig(struct dma_chan *chan, + struct amba_dma_channel_config *config) +{ + struct coh901318_chan *cohc = to_coh901318_chan(chan); + int maxburst = config->maxburst; + u32 amba_ctrl = 0; + int i = 0; + + dev_dbg(COHC_2_DEV(cohc), "configure channel for %d byte transfers\n", + config->addr_width); + switch (config->addr_width) { + case 1: + amba_ctrl |= + COH901318_CX_CTRL_SRC_BUS_SIZE_8_BITS | + COH901318_CX_CTRL_DST_BUS_SIZE_8_BITS; + + while (i < ARRAY_SIZE(burst_sizes)) { + if (burst_sizes[i].burst_8bit <= maxburst) + break; + i++; + } + + break; + case 2: + amba_ctrl |= + COH901318_CX_CTRL_SRC_BUS_SIZE_16_BITS | + COH901318_CX_CTRL_DST_BUS_SIZE_16_BITS; + + while (i < ARRAY_SIZE(burst_sizes)) { + if (burst_sizes[i].burst_16bit <= maxburst) + break; + i++; + } + + break; + case 4: + /* Direction doesn't matter here, it's 32/32 bits */ + amba_ctrl |= + COH901318_CX_CTRL_SRC_BUS_SIZE_32_BITS | + COH901318_CX_CTRL_DST_BUS_SIZE_32_BITS; + + while (i < ARRAY_SIZE(burst_sizes)) { + if (burst_sizes[i].burst_32bit <= maxburst) + break; + i++; + } + + break; + default: + dev_err(COHC_2_DEV(cohc), + "bad ambaconfig: alien address width\n"); + return; + } + + amba_ctrl |= burst_sizes[i].reg; + dev_dbg(COHC_2_DEV(cohc), + "selected burst size %d bytes for address width %d bytes, maxburst %d\n", + burst_sizes[i].burst_8bit, config->addr_width, maxburst); + + cohc->amba_addr = config->addr; + cohc->amba_ctrl = amba_ctrl; +} + void coh901318_base_init(struct dma_device *dma, const int *pick_chans, struct coh901318_base *base) { diff --git a/drivers/dma/coh901318_lli.c b/drivers/dma/coh901318_lli.c index 9f7e0e6..f37db49 100644 --- a/drivers/dma/coh901318_lli.c +++ b/drivers/dma/coh901318_lli.c @@ -153,6 +153,39 @@ coh901318_lli_fill_memcpy(struct coh901318_pool *pool, lli->src_addr = src; lli->dst_addr = dst; + /* + * The DMA hardware will increase its pointer by 1, + * 2 or 4 bytes at a time depending on configuration. + * If using som oddly-aligned buffer this will cause + * performance problems. + */ +#if 0 + ctrl_chained &= ~COH901318_CX_CTRL_SRC_BUS_SIZE_MASK; + ctrl_eom &= ~COH901318_CX_CTRL_SRC_BUS_SIZE_MASK; + ctrl_chained &= ~COH901318_CX_CTRL_DST_BUS_SIZE_MASK; + ctrl_eom &= ~COH901318_CX_CTRL_DST_BUS_SIZE_MASK; + if (src & 0x01) { + ctrl_chained |= COH901318_CX_CTRL_SRC_BUS_SIZE_8_BITS; + ctrl_eom |= COH901318_CX_CTRL_SRC_BUS_SIZE_8_BITS; + } else if (src & 0x02) { + ctrl_chained |= COH901318_CX_CTRL_SRC_BUS_SIZE_16_BITS; + ctrl_eom |= COH901318_CX_CTRL_SRC_BUS_SIZE_16_BITS; + } else { + ctrl_chained |= COH901318_CX_CTRL_SRC_BUS_SIZE_32_BITS; + ctrl_eom |= COH901318_CX_CTRL_SRC_BUS_SIZE_32_BITS; + } + if (dst & 0x01) { + ctrl_chained |= COH901318_CX_CTRL_DST_BUS_SIZE_8_BITS; + ctrl_eom |= COH901318_CX_CTRL_SRC_DST_SIZE_8_BITS; + } else if (dst & 0x02) { + ctrl_chained |= COH901318_CX_CTRL_DST_BUS_SIZE_16_BITS; + ctrl_eom |= COH901318_CX_CTRL_DST_BUS_SIZE_16_BITS; + } else { + ctrl_chained |= COH901318_CX_CTRL_DST_BUS_SIZE_32_BITS; + ctrl_eom |= COH901318_CX_CTRL_DST_BUS_SIZE_32_BITS; + } +#endif + while (lli->link_addr) { lli->control = ctrl_chained | MAX_DMA_PACKET_SIZE; lli->src_addr = src; @@ -268,13 +301,52 @@ coh901318_lli_fill_sg(struct coh901318_pool *pool, else ctrl_sg = ctrl ? ctrl : ctrl_last; - - if (dir == DMA_TO_DEVICE) + /* + * If the physical address is only divisible by 1 or + * two, we have to lower the destination bus size to + * the least common denominator + */ + if (dir == DMA_TO_DEVICE) { /* increment source address */ src = sg_phys(sg); - else +#if 0 + ctrl_sg &= ~COH901318_CX_CTRL_SRC_BUS_SIZE_MASK; + ctrl_chained &= ~COH901318_CX_CTRL_SRC_BUS_SIZE_MASK; + if (src & 0x01) { + pr_err("Aligned src buffer to 8bit access\n"); + ctrl_sg |= COH901318_CX_CTRL_SRC_BUS_SIZE_8_BITS; + ctrl_chained |= COH901318_CX_CTRL_SRC_BUS_SIZE_8_BITS; + } else if (src & 0x02) { + pr_err("Aligned src buffer to 16bit access\n"); + ctrl_sg |= COH901318_CX_CTRL_SRC_BUS_SIZE_16_BITS; + ctrl_chained |= COH901318_CX_CTRL_SRC_BUS_SIZE_16_BITS; + } else { + pr_err("Aligned src buffer to 32bit access\n"); + ctrl_sg |= COH901318_CX_CTRL_SRC_BUS_SIZE_32_BITS; + ctrl_chained |= COH901318_CX_CTRL_SRC_BUS_SIZE_32_BITS; + } +#endif + } else { /* increment destination address */ dst = sg_phys(sg); +#if 0 + ctrl_sg &= ~COH901318_CX_CTRL_DST_BUS_SIZE_MASK; + ctrl_chained &= ~COH901318_CX_CTRL_DST_BUS_SIZE_MASK; + if (dst & 0x01) { + pr_err("Aligned dst buffer to 8bit access\n"); + ctrl_sg |= COH901318_CX_CTRL_DST_BUS_SIZE_8_BITS; + ctrl_chained |= COH901318_CX_CTRL_DST_BUS_SIZE_8_BITS; + } else if (dst & 0x02) { + pr_err("Aligned dst buffer to 16bit access\n"); + ctrl_sg |= COH901318_CX_CTRL_DST_BUS_SIZE_16_BITS; + ctrl_chained |= COH901318_CX_CTRL_DST_BUS_SIZE_16_BITS; + } else { + pr_err("Aligned dst buffer to 32bit access\n"); + ctrl_sg |= COH901318_CX_CTRL_DST_BUS_SIZE_32_BITS; + ctrl_chained |= COH901318_CX_CTRL_DST_BUS_SIZE_32_BITS; + } +#endif + } bytes_to_transfer = sg_dma_len(sg);