From patchwork Tue Aug 24 11:04:25 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: manjugk manjugk X-Patchwork-Id: 126281 X-Patchwork-Delegate: khilman@deeprootsystems.com Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.4/8.14.3) with ESMTP id o7OB65cT021441 for ; Tue, 24 Aug 2010 11:06:05 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754911Ab0HXLGE (ORCPT ); Tue, 24 Aug 2010 07:06:04 -0400 Received: from devils.ext.ti.com ([198.47.26.153]:59632 "EHLO devils.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754449Ab0HXLGD (ORCPT ); Tue, 24 Aug 2010 07:06:03 -0400 Received: from dlep36.itg.ti.com ([157.170.170.91]) by devils.ext.ti.com (8.13.7/8.13.7) with ESMTP id o7OB62G8008718 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Tue, 24 Aug 2010 06:06:02 -0500 Received: from legion.dal.design.ti.com (localhost [127.0.0.1]) by dlep36.itg.ti.com (8.13.8/8.13.8) with ESMTP id o7OB5vCL021893; Tue, 24 Aug 2010 06:05:57 -0500 (CDT) Received: from localhost (dfl-61.apr.dhcp.ti.com [172.24.136.151]) by legion.dal.design.ti.com (8.11.7p1+Sun/8.11.7) with ESMTP id o7OB5sf18106; Tue, 24 Aug 2010 06:05:54 -0500 (CDT) From: Manjunatha GK To: linux-omap@vger.kernel.org Cc: Benoit Cousson , Kevin Hilman , Santosh Shilimkar Subject: [PATCH v2 10/11] OMAP: DMA: Use DMA device attributes Date: Tue, 24 Aug 2010 16:34:25 +0530 Message-Id: <1282647866-6918-11-git-send-email-manjugk@ti.com> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1282647866-6918-1-git-send-email-manjugk@ti.com> References: <1282647866-6918-1-git-send-email-manjugk@ti.com> Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Tue, 24 Aug 2010 11:06:06 +0000 (UTC) diff --git a/arch/arm/mach-omap1/dma.c b/arch/arm/mach-omap1/dma.c index 615c5f5..563e60f 100644 --- a/arch/arm/mach-omap1/dma.c +++ b/arch/arm/mach-omap1/dma.c @@ -140,13 +140,177 @@ static struct resource res[] __initdata = { }; static void __iomem *dma_base; +static struct omap_dma_lch *dma_chan; +struct omap_dma_dev_attr *d; + +u32 enable_1510_mode; + +#define OMAP_FUNC_MUX_ARM_BASE (0xfffe1000 + 0xec) +static inline int omap1_get_gdma_dev(int req) +{ + u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4; + int shift = ((req - 1) % 5) * 6; + + return ((omap_readl(reg) >> shift) & 0x3f) + 1; +} + +static inline void omap1_set_gdma_dev(int req, int dev) +{ + u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4; + int shift = ((req - 1) % 5) * 6; + u32 l; + + l = omap_readl(reg); + l &= ~(0x3f << shift); + l |= (dev - 1) << shift; + omap_writel(l, reg); +} + +static void omap1_clear_lch_regs(int lch) +{ + int i; + void __iomem *lch_base = dma_base + OMAP1_DMA_CH_BASE(lch); + + for (i = 0; i < 0x2c; i += 2) + __raw_writew(0, lch_base + i); +} + +static inline void omap1_enable_channel_irq(int lch) +{ + u32 status; + + status = dma_read(CSR(lch)); + dma_write(dma_chan[lch].enabled_irqs, CICR(lch)); +} + +static void omap1_disable_channel_irq(int lch) +{ + return; +} + +static inline void omap1_enable_lnk(int lch) +{ + u32 l; + + l = dma_read(CLNK_CTRL(lch)); + + l &= ~(1 << 14); + + /* Set the ENABLE_LNK bits */ + if (dma_chan[lch].next_lch != -1) + l = dma_chan[lch].next_lch | (1 << 15); + + dma_write(l, CLNK_CTRL(lch)); +} + +static inline void omap1_disable_lnk(int lch) +{ + u32 l; + + l = dma_read(CLNK_CTRL(lch)); + + /* Disable interrupts */ + dma_write(0, CICR(lch)); + /* Set the STOP_LNK bit */ + l |= 1 << 14; + + dma_write(l, CLNK_CTRL(lch)); + dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE; +} + +static int omap1_dma_handle_ch(int ch) +{ + u32 csr; + + if (enable_1510_mode && ch >= 6) { + csr = dma_chan[ch].saved_csr; + dma_chan[ch].saved_csr = 0; + } else + csr = dma_read(CSR(ch)); + if (enable_1510_mode && ch <= 2 && (csr >> 7) != 0) { + dma_chan[ch + 6].saved_csr = csr >> 7; + csr &= 0x7f; + } + if ((csr & 0x3f) == 0) + return 0; + if (unlikely(dma_chan[ch].dev_id == -1)) { + printk(KERN_WARNING "Spurious interrupt from DMA channel " + "%d (CSR %04x)\n", ch, csr); + return 0; + } + if (unlikely(csr & OMAP1_DMA_TOUT_IRQ)) + printk(KERN_WARNING "DMA timeout with device %d\n", + dma_chan[ch].dev_id); + if (unlikely(csr & OMAP_DMA_DROP_IRQ)) + printk(KERN_WARNING "DMA synchronization event drop occurred " + "with device %d\n", dma_chan[ch].dev_id); + if (likely(csr & OMAP_DMA_BLOCK_IRQ)) + dma_chan[ch].flags &= ~OMAP_DMA_ACTIVE; + if (likely(dma_chan[ch].callback != NULL)) + dma_chan[ch].callback(ch, csr, dma_chan[ch].data); + + return 1; +} + +static irqreturn_t irq_handler(int irq, void *dev_id) +{ + int ch = ((int) dev_id) - 1; + int handled = 0; + + for (;;) { + int handled_now = 0; + + handled_now += omap1_dma_handle_ch(ch); + if (enable_1510_mode && dma_chan[ch + 6].saved_csr) + handled_now += omap1_dma_handle_ch(ch + 6); + if (!handled_now) + break; + handled += handled_now; + } + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +void omap_set_dma_channel_mode(int lch, enum omap_dma_channel_mode mode) +{ + if (!(d->dev_caps & ENABLE_1510_MODE)) { + u32 l; + + l = dma_read(LCH_CTRL(lch)); + l &= ~0x7; + l |= mode; + dma_write(l, LCH_CTRL(lch)); + } +} +EXPORT_SYMBOL(omap_set_dma_channel_mode); + +void omap_set_dma_src_index(int lch, int eidx, int fidx) +{ + dma_write(eidx, CSEI(lch)); + dma_write(fidx, CSFI(lch)); +} +EXPORT_SYMBOL(omap_set_dma_src_index); + +void omap_set_dma_dest_index(int lch, int eidx, int fidx) +{ + dma_write(eidx, CDEI(lch)); + dma_write(fidx, CDFI(lch)); +} +EXPORT_SYMBOL(omap_set_dma_dest_index); + +void omap_dma_set_global_params(int arb_rate, int max_fifo_depth, int tparams) +{ + return; +} +EXPORT_SYMBOL(omap_dma_set_global_params); static int __init omap1_system_dma_init(void) { struct platform_device *pdev; struct omap_system_dma_plat_info *pdata; - struct omap_dma_dev_attr *d; - int ret; + int dma_irq, ret, ch; + char irq_name[4]; + int irq_rel; pdev = platform_device_alloc("system_dma", 0); if (!pdev) { @@ -159,7 +323,7 @@ static int __init omap1_system_dma_init(void) if (ret) { pr_err("%s: Unable to add resources for %s%d\n", __func__, pdev->name, pdev->id); - goto exit_device_put; + goto exit_pdev; } pdata = kzalloc(sizeof(struct omap_system_dma_plat_info), GFP_KERNEL); @@ -167,7 +331,7 @@ static int __init omap1_system_dma_init(void) dev_err(&pdev->dev, "%s: Unable to allocate pdata for %s\n", __func__, pdev->name); ret = -ENOMEM; - goto exit_device_put; + goto exit_pdev; } /* Errata handling for all omap1 plus processors */ @@ -184,6 +348,8 @@ static int __init omap1_system_dma_init(void) if (cpu_is_omap15xx()) d->dev_caps = ENABLE_1510_MODE; + enable_1510_mode = d->dev_caps & ENABLE_1510_MODE; + d->dev_caps |= SRC_PORT; d->dev_caps |= DST_PORT; d->dev_caps |= SRC_INDEX; @@ -194,6 +360,19 @@ static int __init omap1_system_dma_init(void) d->lch_count = OMAP1_LOGICAL_DMA_CH_COUNT; + pdata->enable_channel_irq = omap1_enable_channel_irq; + pdata->disable_channel_irq = omap1_disable_channel_irq; + pdata->enable_lnk = omap1_enable_lnk; + pdata->disable_lnk = omap1_disable_lnk; + pdata->clear_lch_regs = omap1_clear_lch_regs; + if (cpu_is_omap16xx()) { + pdata->get_gdma_dev = omap1_get_gdma_dev; + pdata->set_gdma_dev = omap1_set_gdma_dev; + } + pdata->enable_irq_lch = NULL; + pdata->disable_irq_lch = NULL; + pdata->set_dma_chain_ch = NULL; + if (cpu_is_omap15xx()) d->chan_count = 9; else if (cpu_is_omap16xx() || cpu_is_omap7xx()) { @@ -203,36 +382,63 @@ static int __init omap1_system_dma_init(void) d->chan_count = 9; } + for (ch = 0; ch < d->chan_count; ch++) { + if (ch >= 6 && enable_1510_mode) + continue; + + sprintf(&irq_name[0], "%d", ch); + dma_irq = platform_get_irq_byname(pdev, irq_name); + if (dma_irq < 0) { + dev_err(&pdev->dev, "%s:unable to get irq\n", + __func__); + ret = dma_irq; + goto exit_pdata; + } + + ret = request_irq(dma_irq, irq_handler, 0, "DMA", + (void *) (ch + 1)); + if (ret != 0) + goto exit_pdata; + } + pdata->omap_dma_base = (void __iomem *)res[0].start; dma_base = pdata->omap_dma_base; - - d->dma_chan = kzalloc(sizeof(struct omap_dma_lch) * + d->chan = kzalloc(sizeof(struct omap_dma_lch) * (d->lch_count), GFP_KERNEL); - if (!d->dma_chan) { + if (!d->chan) { dev_err(&pdev->dev, "%s: Memory allcation failed" "for dma_chan!!!\n", __func__); - goto exit_release_pdata; + goto exit_pdata; } + dma_chan = d->chan; ret = platform_device_add_data(pdev, pdata, sizeof(*pdata)); if (ret) { dev_err(&pdev->dev, "%s: Unable to add resources for %s%d\n", __func__, pdev->name, pdev->id); - goto exit_release_dma_chan; + goto exit_pdata; } ret = platform_device_add(pdev); if (ret) { dev_err(&pdev->dev, "%s: Unable to add resources for %s%d\n", __func__, pdev->name, pdev->id); - goto exit_release_dma_chan; + goto exit_dma_chan; } -exit_release_dma_chan: - kfree(d->dma_chan); -exit_release_pdata: +exit_dma_chan: + kfree(d->chan); +exit_pdata: + printk(KERN_ERR "unable to request IRQ %d" + "for DMA (error %d)\n", dma_irq, ret); + if (enable_1510_mode) + ch = 6; + for (irq_rel = 0; irq_rel < ch; irq_rel++) { + dma_irq = platform_get_irq(pdev, irq_rel); + free_irq(dma_irq, (void *)(irq_rel + 1)); + } kfree(pdata); -exit_device_put: +exit_pdev: platform_device_put(pdev); return ret; diff --git a/arch/arm/mach-omap2/dma.c b/arch/arm/mach-omap2/dma.c index 8832bd1..6a5160d 100644 --- a/arch/arm/mach-omap2/dma.c +++ b/arch/arm/mach-omap2/dma.c @@ -50,9 +50,70 @@ __raw_writel((val), dma_base + OMAP_DMA4_##reg); \ }) +static struct omap_dma_global_context_registers { + u32 dma_irqenable_l0; + u32 dma_ocp_sysconfig; + u32 dma_gcr; +} omap_dma_global_context; + +struct dma_link_info { + int *linked_dmach_q; + int no_of_lchs_linked; + + int q_count; + int q_tail; + int q_head; + + int chain_state; + int chain_mode; + +}; + +static struct dma_link_info *dma_linked_lch; +static struct omap_dma_lch *dma_chan; + +static int dma_chan_count; + +enum { DMA_CH_ALLOC_DONE, DMA_CH_PARAMS_SET_DONE, DMA_CH_STARTED, + DMA_CH_QUEUED, DMA_CH_NOTSTARTED, DMA_CH_PAUSED, DMA_CH_LINK_ENABLED +}; + +enum { DMA_CHAIN_STARTED, DMA_CHAIN_NOTSTARTED }; + +/* Chain handling macros */ +#define OMAP_DMA_CHAIN_QINIT(chain_id) \ + do { \ + dma_linked_lch[chain_id].q_head = \ + dma_linked_lch[chain_id].q_tail = \ + dma_linked_lch[chain_id].q_count = 0; \ + } while (0) +#define OMAP_DMA_CHAIN_QFULL(chain_id) \ + (dma_linked_lch[chain_id].no_of_lchs_linked == \ + dma_linked_lch[chain_id].q_count) +#define OMAP_DMA_CHAIN_QLAST(chain_id) \ + do { \ + ((dma_linked_lch[chain_id].no_of_lchs_linked-1) == \ + dma_linked_lch[chain_id].q_count) \ + } while (0) +#define OMAP_DMA_CHAIN_QEMPTY(chain_id) \ + (0 == dma_linked_lch[chain_id].q_count) +#define __OMAP_DMA_CHAIN_INCQ(end) \ + ((end) = ((end)+1) % dma_linked_lch[chain_id].no_of_lchs_linked) +#define OMAP_DMA_CHAIN_INCQHEAD(chain_id) \ + do { \ + __OMAP_DMA_CHAIN_INCQ(dma_linked_lch[chain_id].q_head); \ + dma_linked_lch[chain_id].q_count--; \ + } while (0) + +#define OMAP_DMA_CHAIN_INCQTAIL(chain_id) \ + do { \ + __OMAP_DMA_CHAIN_INCQ(dma_linked_lch[chain_id].q_tail); \ + dma_linked_lch[chain_id].q_count++; \ + } while (0) + static struct omap_dma_dev_attr *d; static void __iomem *dma_base; -static struct omap_system_dma_plat_info *omap2_pdata; +static struct omap_system_dma_plat_info *p; static int dma_caps0_status; static struct omap_device_pm_latency omap2_dma_latency[] = { @@ -63,6 +124,874 @@ static struct omap_device_pm_latency omap2_dma_latency[] = { }, }; +static inline void omap2_enable_irq_lch(int lch) +{ + u32 val; + + val = dma_read(IRQENABLE_L0); + val |= 1 << lch; + dma_write(val, IRQENABLE_L0); +} + +static inline void omap2_disable_irq_lch(int lch) +{ + u32 val; + + val = dma_read(IRQENABLE_L0); + val &= ~(1 << lch); + dma_write(val, IRQENABLE_L0); +} + +static inline void omap2_enable_channel_irq(int lch) +{ + /* Clear CSR */ + dma_write(OMAP2_DMA_CSR_CLEAR_MASK, CSR(lch)); + + dma_write(dma_chan[lch].enabled_irqs, CICR(lch)); +} + +static void omap2_disable_channel_irq(int lch) +{ + dma_write(0, CICR(lch)); +} + +static inline void omap2_enable_lnk(int lch) +{ + u32 l; + + l = dma_read(CLNK_CTRL(lch)); + + /* Set the ENABLE_LNK bits */ + if (dma_chan[lch].next_lch != -1) + l = dma_chan[lch].next_lch | (1 << 15); + + if (dma_chan[lch].next_linked_ch != -1) + l = dma_chan[lch].next_linked_ch | (1 << 15); + + dma_write(l, CLNK_CTRL(lch)); +} + +static inline void omap2_disable_lnk(int lch) +{ + u32 l; + + l = dma_read(CLNK_CTRL(lch)); + + omap2_disable_channel_irq(lch); + /* Clear the ENABLE_LNK bit */ + l &= ~(1 << 15); + + dma_write(l, CLNK_CTRL(lch)); + dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE; +} + +static void dma_ocpsysconfig_errata(u32 *sys_cf, bool flag) +{ + u32 l; + + /* + * DMA Errata: + * Special programming model needed to disable DMA before end of block + */ + if (!flag) { + *sys_cf = dma_read(OCP_SYSCONFIG); + l = *sys_cf; + /* Middle mode reg set no Standby */ + l &= ~((1 << 12)|(1 << 13)); + dma_write(l, OCP_SYSCONFIG); + } else + /* put back old value */ + dma_write(*sys_cf, OCP_SYSCONFIG); +} + +void omap_dma_global_context_save(void) +{ + omap_dma_global_context.dma_irqenable_l0 = + dma_read(IRQENABLE_L0); + omap_dma_global_context.dma_ocp_sysconfig = + dma_read(OCP_SYSCONFIG); + omap_dma_global_context.dma_gcr = dma_read(GCR); +} + +void omap_dma_global_context_restore(void) +{ + int ch; + + dma_write(omap_dma_global_context.dma_gcr, GCR); + dma_write(omap_dma_global_context.dma_ocp_sysconfig, + OCP_SYSCONFIG); + dma_write(omap_dma_global_context.dma_irqenable_l0, + IRQENABLE_L0); + + /* + * A bug in ROM code leaves IRQ status for channels 0 and 1 uncleared + * after secure sram context save and restore. Hence we need to + * manually clear those IRQs to avoid spurious interrupts. This + * affects only secure devices. + */ + if (cpu_is_omap34xx() && (omap_type() != OMAP2_DEVICE_TYPE_GP)) + dma_write(0x3 , IRQSTATUS_L0); + + for (ch = 0; ch < dma_chan_count; ch++) + if (dma_chan[ch].dev_id != -1) + omap_clear_dma(ch); +} + +/* Create chain of DMA channesls */ +static void create_dma_lch_chain(int lch_head, int lch_queue) +{ + u32 l; + + /* Check if this is the first link in chain */ + if (dma_chan[lch_head].next_linked_ch == -1) { + dma_chan[lch_head].next_linked_ch = lch_queue; + dma_chan[lch_head].prev_linked_ch = lch_queue; + dma_chan[lch_queue].next_linked_ch = lch_head; + dma_chan[lch_queue].prev_linked_ch = lch_head; + } + + /* a link exists, link the new channel in circular chain */ + else { + dma_chan[lch_queue].next_linked_ch = + dma_chan[lch_head].next_linked_ch; + dma_chan[lch_queue].prev_linked_ch = lch_head; + dma_chan[lch_head].next_linked_ch = lch_queue; + dma_chan[dma_chan[lch_queue].next_linked_ch].prev_linked_ch = + lch_queue; + } + + l = dma_read(CLNK_CTRL(lch_head)); + l &= ~(0x1f); + l |= lch_queue; + dma_write(l, CLNK_CTRL(lch_head)); + + l = dma_read(CLNK_CTRL(lch_queue)); + l &= ~(0x1f); + l |= (dma_chan[lch_queue].next_linked_ch); + dma_write(l, CLNK_CTRL(lch_queue)); +} + +static void set_dma_chain_ch(int free_ch) +{ + dma_chan[free_ch].chain_id = -1; + dma_chan[free_ch].next_linked_ch = -1; +} + +/** + * @brief omap_request_dma_chain : Request a chain of DMA channels + * + * @param dev_id - Device id using the dma channel + * @param dev_name - Device name + * @param callback - Call back function + * @chain_id - + * @no_of_chans - Number of channels requested + * @chain_mode - Dynamic or static chaining : OMAP_DMA_STATIC_CHAIN + * OMAP_DMA_DYNAMIC_CHAIN + * @params - Channel parameters + * + * @return - Success : 0 + * Failure : -EINVAL/-ENOMEM + */ +int omap_request_dma_chain(int dev_id, const char *dev_name, + void (*callback) (int lch, u16 ch_status, + void *data), + int *chain_id, int no_of_chans, int chain_mode, + struct omap_dma_channel_params params) +{ + int *channels; + int i, err; + + /* Is the chain mode valid ? */ + if (chain_mode != OMAP_DMA_STATIC_CHAIN + && chain_mode != OMAP_DMA_DYNAMIC_CHAIN) { + printk(KERN_ERR "Invalid chain mode requested\n"); + return -EINVAL; + } + + if (unlikely((no_of_chans < 1 + || no_of_chans > dma_chan_count))) { + printk(KERN_ERR "Invalid Number of channels requested\n"); + return -EINVAL; + } + + /* + * Allocate a queue to maintain the status of the channels + * in the chain + */ + channels = kmalloc(sizeof(*channels) * no_of_chans, GFP_KERNEL); + if (channels == NULL) { + printk(KERN_ERR "omap_dma: No memory for channel queue\n"); + return -ENOMEM; + } + + /* request and reserve DMA channels for the chain */ + for (i = 0; i < no_of_chans; i++) { + err = omap_request_dma(dev_id, dev_name, + callback, NULL, &channels[i]); + if (err < 0) { + int j; + for (j = 0; j < i; j++) + omap_free_dma(channels[j]); + kfree(channels); + printk(KERN_ERR "omap_dma: Request failed %d\n", err); + return err; + } + dma_chan[channels[i]].prev_linked_ch = -1; + dma_chan[channels[i]].state = DMA_CH_NOTSTARTED; + + /* + * Allowing client drivers to set common parameters now, + * so that later only relevant (src_start, dest_start + * and element count) can be set + */ + omap_set_dma_params(channels[i], ¶ms); + } + + *chain_id = channels[0]; + dma_linked_lch[*chain_id].linked_dmach_q = channels; + dma_linked_lch[*chain_id].chain_mode = chain_mode; + dma_linked_lch[*chain_id].chain_state = DMA_CHAIN_NOTSTARTED; + dma_linked_lch[*chain_id].no_of_lchs_linked = no_of_chans; + + for (i = 0; i < no_of_chans; i++) + dma_chan[channels[i]].chain_id = *chain_id; + + /* Reset the Queue pointers */ + OMAP_DMA_CHAIN_QINIT(*chain_id); + + /* Set up the chain */ + if (no_of_chans == 1) + create_dma_lch_chain(channels[0], channels[0]); + else { + for (i = 0; i < (no_of_chans - 1); i++) + create_dma_lch_chain(channels[i], channels[i + 1]); + } + + return 0; +} +EXPORT_SYMBOL(omap_request_dma_chain); + +/** + * @brief omap_modify_dma_chain_param : Modify the chain's params - Modify the + * params after setting it. Dont do this while dma is running!! + * + * @param chain_id - Chained logical channel id. + * @param params + * + * @return - Success : 0 + * Failure : -EINVAL + */ +int omap_modify_dma_chain_params(int chain_id, + struct omap_dma_channel_params params) +{ + int *channels; + u32 i; + + /* Check for input params */ + if (unlikely((chain_id < 0 + || chain_id >= dma_chan_count))) { + printk(KERN_ERR "Invalid chain id\n"); + return -EINVAL; + } + + /* Check if the chain exists */ + if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { + printk(KERN_ERR "Chain doesn't exists\n"); + return -EINVAL; + } + channels = dma_linked_lch[chain_id].linked_dmach_q; + + for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) { + /* + * Allowing client drivers to set common parameters now, + * so that later only relevant (src_start, dest_start + * and element count) can be set + */ + omap_set_dma_params(channels[i], ¶ms); + } + + return 0; +} +EXPORT_SYMBOL(omap_modify_dma_chain_params); + +/** + * @brief omap_free_dma_chain - Free all the logical channels in a chain. + * + * @param chain_id + * + * @return - Success : 0 + * Failure : -EINVAL + */ +int omap_free_dma_chain(int chain_id) +{ + int *channels; + u32 i; + + /* Check for input params */ + if (unlikely((chain_id < 0 || chain_id >= dma_chan_count))) { + printk(KERN_ERR "Invalid chain id\n"); + return -EINVAL; + } + + /* Check if the chain exists */ + if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { + printk(KERN_ERR "Chain doesn't exists\n"); + return -EINVAL; + } + + channels = dma_linked_lch[chain_id].linked_dmach_q; + for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) { + dma_chan[channels[i]].next_linked_ch = -1; + dma_chan[channels[i]].prev_linked_ch = -1; + dma_chan[channels[i]].chain_id = -1; + dma_chan[channels[i]].state = DMA_CH_NOTSTARTED; + omap_free_dma(channels[i]); + } + + kfree(channels); + + dma_linked_lch[chain_id].linked_dmach_q = NULL; + dma_linked_lch[chain_id].chain_mode = -1; + dma_linked_lch[chain_id].chain_state = -1; + + return 0; +} +EXPORT_SYMBOL(omap_free_dma_chain); + +/** + * @brief omap_dma_chain_status - Check if the chain is in + * active / inactive state. + * @param chain_id + * + * @return - Success : OMAP_DMA_CHAIN_ACTIVE/OMAP_DMA_CHAIN_INACTIVE + * Failure : -EINVAL + */ +int omap_dma_chain_status(int chain_id) +{ + /* Check for input params */ + if (unlikely((chain_id < 0 || chain_id >= dma_chan_count))) { + printk(KERN_ERR "Invalid chain id\n"); + return -EINVAL; + } + + /* Check if the chain exists */ + if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { + printk(KERN_ERR "Chain doesn't exists\n"); + return -EINVAL; + } + pr_debug("CHAINID=%d, qcnt=%d\n", chain_id, + dma_linked_lch[chain_id].q_count); + + if (OMAP_DMA_CHAIN_QEMPTY(chain_id)) + return OMAP_DMA_CHAIN_INACTIVE; + + return OMAP_DMA_CHAIN_ACTIVE; +} +EXPORT_SYMBOL(omap_dma_chain_status); + +/** + * @brief omap_dma_chain_a_transfer - Get a free channel from a chain, + * set the params and start the transfer. + * + * @param chain_id + * @param src_start - buffer start address + * @param dest_start - Dest address + * @param elem_count + * @param frame_count + * @param callbk_data - channel callback parameter data. + * + * @return - Success : OMAP_DMA_CHAIN_ACTIVE/OMAP_DMA_CHAIN_INACTIVE + * Failure : -EINVAL + */ +int omap_dma_chain_a_transfer(int chain_id, int src_start, int dest_start, + int elem_count, int frame_count, void *callbk_data) +{ + int *channels; + u32 l, lch; + int start_dma = 0; + + /* + * if buffer size is less than 1 then there is + * no use of starting the chain + */ + if (elem_count < 1) { + printk(KERN_ERR "Invalid buffer size\n"); + return -EINVAL; + } + + /* Check for input params */ + if (unlikely((chain_id < 0 + || chain_id >= dma_chan_count))) { + printk(KERN_ERR "Invalid chain id\n"); + return -EINVAL; + } + + /* Check if the chain exists */ + if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { + printk(KERN_ERR "Chain doesn't exist\n"); + return -EINVAL; + } + + /* Check if all the channels in chain are in use */ + if (OMAP_DMA_CHAIN_QFULL(chain_id)) + return -EBUSY; + + /* Frame count may be negative in case of indexed transfers */ + channels = dma_linked_lch[chain_id].linked_dmach_q; + + /* Get a free channel */ + lch = channels[dma_linked_lch[chain_id].q_tail]; + + /* Store the callback data */ + dma_chan[lch].data = callbk_data; + + /* Increment the q_tail */ + OMAP_DMA_CHAIN_INCQTAIL(chain_id); + + /* Set the params to the free channel */ + if (src_start != 0) + dma_write(src_start, CSSA(lch)); + if (dest_start != 0) + dma_write(dest_start, CDSA(lch)); + + /* Write the buffer size */ + dma_write(elem_count, CEN(lch)); + dma_write(frame_count, CFN(lch)); + + /* + * If the chain is dynamically linked, + * then we may have to start the chain if its not active + */ + if (dma_linked_lch[chain_id].chain_mode == OMAP_DMA_DYNAMIC_CHAIN) { + + /* + * In Dynamic chain, if the chain is not started, + * queue the channel + */ + if (dma_linked_lch[chain_id].chain_state == + DMA_CHAIN_NOTSTARTED) { + /* Enable the link in previous channel */ + if (dma_chan[dma_chan[lch].prev_linked_ch].state == + DMA_CH_QUEUED) + omap2_enable_lnk(dma_chan[lch].prev_linked_ch); + dma_chan[lch].state = DMA_CH_QUEUED; + } + + /* + * Chain is already started, make sure its active, + * if not then start the chain + */ + else { + start_dma = 1; + + if (dma_chan[dma_chan[lch].prev_linked_ch].state == + DMA_CH_STARTED) { + omap2_enable_lnk(dma_chan[lch].prev_linked_ch); + dma_chan[lch].state = DMA_CH_QUEUED; + start_dma = 0; + if (0 == ((1 << 7) & dma_read( + CCR(dma_chan[lch].prev_linked_ch)))) { + omap2_disable_lnk(dma_chan[lch]. + prev_linked_ch); + pr_debug("\n prev ch is stopped\n"); + start_dma = 1; + } + } + + else if (dma_chan[dma_chan[lch].prev_linked_ch].state + == DMA_CH_QUEUED) { + omap2_enable_lnk(dma_chan[lch].prev_linked_ch); + dma_chan[lch].state = DMA_CH_QUEUED; + start_dma = 0; + } + omap2_enable_channel_irq(lch); + + l = dma_read(CCR(lch)); + + if ((0 == (l & (1 << 24)))) + l &= ~(1 << 25); + else + l |= (1 << 25); + if (start_dma == 1) { + if (0 == (l & (1 << 7))) { + l |= (1 << 7); + dma_chan[lch].state = DMA_CH_STARTED; + pr_debug("starting %d\n", lch); + dma_write(l, CCR(lch)); + } else + start_dma = 0; + } else { + if (0 == (l & (1 << 7))) + dma_write(l, CCR(lch)); + } + dma_chan[lch].flags |= OMAP_DMA_ACTIVE; + } + } + + return 0; +} +EXPORT_SYMBOL(omap_dma_chain_a_transfer); + +/** + * @brief omap_start_dma_chain_transfers - Start the chain + * + * @param chain_id + * + * @return - Success : OMAP_DMA_CHAIN_ACTIVE/OMAP_DMA_CHAIN_INACTIVE + * Failure : -EINVAL + */ +int omap_start_dma_chain_transfers(int chain_id) +{ + int *channels; + u32 l, i; + + if (unlikely((chain_id < 0 || chain_id >= dma_chan_count))) { + printk(KERN_ERR "Invalid chain id\n"); + return -EINVAL; + } + + channels = dma_linked_lch[chain_id].linked_dmach_q; + + if (dma_linked_lch[channels[0]].chain_state == DMA_CHAIN_STARTED) { + printk(KERN_ERR "Chain is already started\n"); + return -EBUSY; + } + + if (dma_linked_lch[chain_id].chain_mode == OMAP_DMA_STATIC_CHAIN) { + for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; + i++) { + omap2_enable_lnk(channels[i]); + omap2_enable_channel_irq(channels[i]); + } + } else { + omap2_enable_channel_irq(channels[0]); + } + + l = dma_read(CCR(channels[0])); + l |= (1 << 7); + dma_linked_lch[chain_id].chain_state = DMA_CHAIN_STARTED; + dma_chan[channels[0]].state = DMA_CH_STARTED; + + if ((0 == (l & (1 << 24)))) + l &= ~(1 << 25); + else + l |= (1 << 25); + dma_write(l, CCR(channels[0])); + + dma_chan[channels[0]].flags |= OMAP_DMA_ACTIVE; + + return 0; +} +EXPORT_SYMBOL(omap_start_dma_chain_transfers); + +/** + * @brief omap_stop_dma_chain_transfers - Stop the dma transfer of a chain. + * + * @param chain_id + * + * @return - Success : OMAP_DMA_CHAIN_ACTIVE/OMAP_DMA_CHAIN_INACTIVE + * Failure : -EINVAL + */ +int omap_stop_dma_chain_transfers(int chain_id) +{ + int *channels; + u32 l, i; + u32 get_sysconfig; + + /* Check for input params */ + if (unlikely((chain_id < 0 || chain_id >= dma_chan_count))) { + printk(KERN_ERR "Invalid chain id\n"); + return -EINVAL; + } + + /* Check if the chain exists */ + if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { + printk(KERN_ERR "Chain doesn't exists\n"); + return -EINVAL; + } + channels = dma_linked_lch[chain_id].linked_dmach_q; + + if (p->errata & DMA_SYSCONFIG_ERRATA) + dma_ocpsysconfig_errata(&get_sysconfig, false); + + for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) { + + /* Stop the Channel transmission */ + l = dma_read(CCR(channels[i])); + l &= ~(1 << 7); + dma_write(l, CCR(channels[i])); + + /* Disable the link in all the channels */ + omap2_disable_lnk(channels[i]); + dma_chan[channels[i]].state = DMA_CH_NOTSTARTED; + + } + dma_linked_lch[chain_id].chain_state = DMA_CHAIN_NOTSTARTED; + + /* Reset the Queue pointers */ + OMAP_DMA_CHAIN_QINIT(chain_id); + + if (p->errata & DMA_SYSCONFIG_ERRATA) + dma_ocpsysconfig_errata(&get_sysconfig, true); + + return 0; +} +EXPORT_SYMBOL(omap_stop_dma_chain_transfers); + +/* Get the index of the ongoing DMA in chain */ +/** + * @brief omap_get_dma_chain_index - Get the element and frame index + * of the ongoing DMA in chain + * + * @param chain_id + * @param ei - Element index + * @param fi - Frame index + * + * @return - Success : OMAP_DMA_CHAIN_ACTIVE/OMAP_DMA_CHAIN_INACTIVE + * Failure : -EINVAL + */ +int omap_get_dma_chain_index(int chain_id, int *ei, int *fi) +{ + int lch; + int *channels; + + /* Check for input params */ + if (unlikely((chain_id < 0 || chain_id >= dma_chan_count))) { + printk(KERN_ERR "Invalid chain id\n"); + return -EINVAL; + } + + /* Check if the chain exists */ + if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { + printk(KERN_ERR "Chain doesn't exists\n"); + return -EINVAL; + } + if ((!ei) || (!fi)) + return -EINVAL; + + channels = dma_linked_lch[chain_id].linked_dmach_q; + + /* Get the current channel */ + lch = channels[dma_linked_lch[chain_id].q_head]; + + *ei = dma_read(CCEN(lch)); + *fi = dma_read(CCFN(lch)); + + return 0; +} +EXPORT_SYMBOL(omap_get_dma_chain_index); + +/** + * @brief omap_get_dma_chain_dst_pos - Get the destination position of the + * ongoing DMA in chain + * + * @param chain_id + * + * @return - Success : OMAP_DMA_CHAIN_ACTIVE/OMAP_DMA_CHAIN_INACTIVE + * Failure : -EINVAL + */ +int omap_get_dma_chain_dst_pos(int chain_id) +{ + int lch; + int *channels; + + /* Check for input params */ + if (unlikely((chain_id < 0 || chain_id >= dma_chan_count))) { + printk(KERN_ERR "Invalid chain id\n"); + return -EINVAL; + } + + /* Check if the chain exists */ + if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { + printk(KERN_ERR "Chain doesn't exists\n"); + return -EINVAL; + } + + channels = dma_linked_lch[chain_id].linked_dmach_q; + + /* Get the current channel */ + lch = channels[dma_linked_lch[chain_id].q_head]; + + return dma_read(CDAC(lch)); +} +EXPORT_SYMBOL(omap_get_dma_chain_dst_pos); + +/** + * @brief omap_get_dma_chain_src_pos - Get the source position + * of the ongoing DMA in chain + * @param chain_id + * + * @return - Success : OMAP_DMA_CHAIN_ACTIVE/OMAP_DMA_CHAIN_INACTIVE + * Failure : -EINVAL + */ +int omap_get_dma_chain_src_pos(int chain_id) +{ + int lch; + int *channels; + + /* Check for input params */ + if (unlikely((chain_id < 0 || chain_id >= dma_chan_count))) { + printk(KERN_ERR "Invalid chain id\n"); + return -EINVAL; + } + + /* Check if the chain exists */ + if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { + printk(KERN_ERR "Chain doesn't exists\n"); + return -EINVAL; + } + + channels = dma_linked_lch[chain_id].linked_dmach_q; + + /* Get the current channel */ + lch = channels[dma_linked_lch[chain_id].q_head]; + + return dma_read(CSAC(lch)); +} +EXPORT_SYMBOL(omap_get_dma_chain_src_pos); + +void omap_set_dma_write_mode(int lch, enum omap_dma_write_mode mode) +{ + u32 csdp; + + csdp = dma_read(CSDP(lch)); + csdp &= ~(0x3 << 16); + csdp |= (mode << 16); + dma_write(csdp, CSDP(lch)); +} +EXPORT_SYMBOL(omap_set_dma_write_mode); + +static int omap2_dma_handle_ch(int ch) +{ + u32 status = dma_read(CSR(ch)); + + if (!status) { + if (printk_ratelimit()) + printk(KERN_WARNING "Spurious DMA IRQ for lch %d\n", + ch); + dma_write(1 << ch, IRQSTATUS_L0); + return 0; + } + if (unlikely(dma_chan[ch].dev_id == -1)) { + if (printk_ratelimit()) + printk(KERN_WARNING "IRQ %04x for non-allocated DMA" + "channel %d\n", status, ch); + return 0; + } + if (unlikely(status & OMAP_DMA_DROP_IRQ)) + printk(KERN_INFO + "DMA synchronization event drop occurred with device " + "%d\n", dma_chan[ch].dev_id); + if (unlikely(status & OMAP2_DMA_TRANS_ERR_IRQ)) { + printk(KERN_INFO "DMA transaction error with device %d\n", + dma_chan[ch].dev_id); + if (cpu_class_is_omap2()) { + /* + * Errata: sDMA Channel is not disabled + * after a transaction error. So we explicitely + * disable the channel + */ + u32 ccr; + + ccr = dma_read(CCR(ch)); + ccr &= ~OMAP_DMA_CCR_EN; + dma_write(ccr, CCR(ch)); + dma_chan[ch].flags &= ~OMAP_DMA_ACTIVE; + } + } + if (unlikely(status & OMAP2_DMA_SECURE_ERR_IRQ)) + printk(KERN_INFO "DMA secure error with device %d\n", + dma_chan[ch].dev_id); + if (unlikely(status & OMAP2_DMA_MISALIGNED_ERR_IRQ)) + printk(KERN_INFO "DMA misaligned error with device %d\n", + dma_chan[ch].dev_id); + + dma_write(OMAP2_DMA_CSR_CLEAR_MASK, CSR(ch)); + dma_write(1 << ch, IRQSTATUS_L0); + + /* If the ch is not chained then chain_id will be -1 */ + if (dma_chan[ch].chain_id != -1) { + int chain_id = dma_chan[ch].chain_id; + dma_chan[ch].state = DMA_CH_NOTSTARTED; + if (dma_read(CLNK_CTRL(ch)) & (1 << 15)) + dma_chan[dma_chan[ch].next_linked_ch].state = + DMA_CH_STARTED; + if (dma_linked_lch[chain_id].chain_mode == + OMAP_DMA_DYNAMIC_CHAIN) + omap2_disable_lnk(ch); + + if (!OMAP_DMA_CHAIN_QEMPTY(chain_id)) + OMAP_DMA_CHAIN_INCQHEAD(chain_id); + + status = dma_read(CSR(ch)); + } + + dma_write(status, CSR(ch)); + + if (likely(dma_chan[ch].callback != NULL)) + dma_chan[ch].callback(ch, status, dma_chan[ch].data); + + return 0; +} + +/* STATUS register count is from 1-32 while our is 0-31 */ +static irqreturn_t irq_handler(int irq, void *dev_id) +{ + u32 val, enable_reg; + int i; + + val = dma_read(IRQSTATUS_L0); + if (val == 0) { + if (printk_ratelimit()) + printk(KERN_WARNING "Spurious DMA IRQ\n"); + return IRQ_HANDLED; + } + enable_reg = dma_read(IRQENABLE_L0); + val &= enable_reg; /* Dispatch only relevant interrupts */ + for (i = 0; i < dma_chan_count && val != 0; i++) { + if (val & 1) + omap2_dma_handle_ch(i); + val >>= 1; + } + + return IRQ_HANDLED; +} + +static struct irqaction omap24xx_dma_irq = { + .name = "DMA", + .handler = irq_handler, + .flags = IRQF_DISABLED +}; + +/** + * @brief omap_dma_set_global_params : Set global priority settings for dma + * + * @param arb_rate + * @param max_fifo_depth + * @param tparams - Number of threads to reserve: + * DMA_THREAD_RESERVE_NORM + * DMA_THREAD_RESERVE_ONET + * DMA_THREAD_RESERVE_TWOT + * DMA_THREAD_RESERVE_THREET + */ +void +omap_dma_set_global_params(int arb_rate, int max_fifo_depth, int tparams) +{ + u32 reg; + + if (max_fifo_depth == 0) + max_fifo_depth = 1; + if (arb_rate == 0) + arb_rate = 1; + + reg = 0xff & max_fifo_depth; + reg |= (0x3 & tparams) << 12; + reg |= (arb_rate & 0xff) << 16; + + dma_write(reg, GCR); +} +EXPORT_SYMBOL(omap_dma_set_global_params); + /* One time initializations */ static int __init omap2_system_dma_init_dev(struct omap_hwmod *oh, void *user) { @@ -70,6 +999,7 @@ static int __init omap2_system_dma_init_dev(struct omap_hwmod *oh, void *user) struct omap_system_dma_plat_info *pdata; struct resource *mem; char *name = "dma"; + int dma_irq, ret; pdata = kzalloc(sizeof(struct omap_system_dma_plat_info), GFP_KERNEL); if (!pdata) { @@ -114,6 +1044,18 @@ static int __init omap2_system_dma_init_dev(struct omap_hwmod *oh, void *user) /* Errata3.3: Applicable for all omap2 plus */ pdata->errata |= OMAP3_3_ERRATUM; + p = pdata; + p->enable_irq_lch = omap2_enable_irq_lch; + p->disable_irq_lch = omap2_disable_irq_lch; + p->enable_channel_irq = omap2_enable_channel_irq; + p->disable_channel_irq = omap2_disable_channel_irq; + p->enable_lnk = omap2_enable_lnk; + p->disable_lnk = omap2_disable_lnk; + p->set_dma_chain_ch = set_dma_chain_ch; + p->clear_lch_regs = NULL; + p->get_gdma_dev = NULL; + p->set_gdma_dev = NULL; + od = omap_device_build(name, 0, oh, pdata, sizeof(*pdata), omap2_dma_latency, ARRAY_SIZE(omap2_dma_latency), 0); @@ -136,11 +1078,19 @@ static int __init omap2_system_dma_init_dev(struct omap_hwmod *oh, void *user) return -ENOMEM; } + dma_irq = platform_get_irq_byname(&od->pdev, "0"); + ret = setup_irq(dma_irq, &omap24xx_dma_irq); + if (ret) { + dev_err(&od->pdev.dev, "%s:irq handler setup fail\n", __func__); + return -EINVAL; + } + /* Get DMA device attributes from hwmod data base */ d = (struct omap_dma_dev_attr *)oh->dev_attr; /* OMAP2 Plus: physical and logical channel count is same */ - d->chan_count = d->lch_count; + d->chan_count = d->lch_count; + dma_chan_count = d->chan_count; d->chan = kzalloc(sizeof(struct omap_dma_lch) * (d->lch_count), GFP_KERNEL); @@ -150,7 +1100,15 @@ static int __init omap2_system_dma_init_dev(struct omap_hwmod *oh, void *user) return -ENOMEM; } - omap2_pdata = pdata; + dma_linked_lch = kzalloc(sizeof(struct dma_link_info) * + dma_chan_count, GFP_KERNEL); + if (!dma_linked_lch) { + kfree(d->chan); + return -ENOMEM; + } + + dma_chan = d->chan; + dma_caps0_status = dma_read(CAPS_0); return 0; diff --git a/arch/arm/mach-omap2/include/mach/dma.h b/arch/arm/mach-omap2/include/mach/dma.h index 22f4b41..f3e21d5 100644 --- a/arch/arm/mach-omap2/include/mach/dma.h +++ b/arch/arm/mach-omap2/include/mach/dma.h @@ -160,4 +160,25 @@ struct omap_dma_lch { int status; }; +/* Chaining APIs */ +extern int omap_request_dma_chain(int dev_id, const char *dev_name, + void (*callback) (int lch, u16 ch_status, + void *data), + int *chain_id, int no_of_chans, + int chain_mode, + struct omap_dma_channel_params params); +extern int omap_free_dma_chain(int chain_id); +extern int omap_dma_chain_a_transfer(int chain_id, int src_start, + int dest_start, int elem_count, + int frame_count, void *callbk_data); +extern int omap_start_dma_chain_transfers(int chain_id); +extern int omap_stop_dma_chain_transfers(int chain_id); +extern int omap_get_dma_chain_index(int chain_id, int *ei, int *fi); +extern int omap_get_dma_chain_dst_pos(int chain_id); +extern int omap_get_dma_chain_src_pos(int chain_id); + +extern int omap_modify_dma_chain_params(int chain_id, + struct omap_dma_channel_params params); +extern int omap_dma_chain_status(int chain_id); + #endif /* __ASM_ARCH_OMAP2_DMA_H */ diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c index 409586a..3934b98 100644 --- a/arch/arm/plat-omap/dma.c +++ b/arch/arm/plat-omap/dma.c @@ -43,98 +43,20 @@ #include #include - -#undef DEBUG - -#ifndef CONFIG_ARCH_OMAP1 -enum { DMA_CH_ALLOC_DONE, DMA_CH_PARAMS_SET_DONE, DMA_CH_STARTED, - DMA_CH_QUEUED, DMA_CH_NOTSTARTED, DMA_CH_PAUSED, DMA_CH_LINK_ENABLED -}; - -enum { DMA_CHAIN_STARTED, DMA_CHAIN_NOTSTARTED }; -#endif - -#define OMAP_DMA_ACTIVE 0x01 -#define OMAP2_DMA_CSR_CLEAR_MASK 0xffe - -#define OMAP_FUNC_MUX_ARM_BASE (0xfffe1000 + 0xec) - static int enable_1510_mode; - -static struct omap_dma_global_context_registers { - u32 dma_irqenable_l0; - u32 dma_ocp_sysconfig; - u32 dma_gcr; -} omap_dma_global_context; - -struct dma_link_info { - int *linked_dmach_q; - int no_of_lchs_linked; - - int q_count; - int q_tail; - int q_head; - - int chain_state; - int chain_mode; - -}; - -static struct dma_link_info *dma_linked_lch; -static struct platform_device *ddev; - -#ifndef CONFIG_ARCH_OMAP1 - -/* Chain handling macros */ -#define OMAP_DMA_CHAIN_QINIT(chain_id) \ - do { \ - dma_linked_lch[chain_id].q_head = \ - dma_linked_lch[chain_id].q_tail = \ - dma_linked_lch[chain_id].q_count = 0; \ - } while (0) -#define OMAP_DMA_CHAIN_QFULL(chain_id) \ - (dma_linked_lch[chain_id].no_of_lchs_linked == \ - dma_linked_lch[chain_id].q_count) -#define OMAP_DMA_CHAIN_QLAST(chain_id) \ - do { \ - ((dma_linked_lch[chain_id].no_of_lchs_linked-1) == \ - dma_linked_lch[chain_id].q_count) \ - } while (0) -#define OMAP_DMA_CHAIN_QEMPTY(chain_id) \ - (0 == dma_linked_lch[chain_id].q_count) -#define __OMAP_DMA_CHAIN_INCQ(end) \ - ((end) = ((end)+1) % dma_linked_lch[chain_id].no_of_lchs_linked) -#define OMAP_DMA_CHAIN_INCQHEAD(chain_id) \ - do { \ - __OMAP_DMA_CHAIN_INCQ(dma_linked_lch[chain_id].q_head); \ - dma_linked_lch[chain_id].q_count--; \ - } while (0) - -#define OMAP_DMA_CHAIN_INCQTAIL(chain_id) \ - do { \ - __OMAP_DMA_CHAIN_INCQ(dma_linked_lch[chain_id].q_tail); \ - dma_linked_lch[chain_id].q_count++; \ - } while (0) -#endif - static int dma_lch_count; static int dma_chan_count; static int omap_dma_reserve_channels; static spinlock_t dma_chan_lock; -static struct omap_dma_lch *dma_chan; static void __iomem *omap_dma_base; + +static struct platform_device *ddev; +static struct omap_dma_lch *dma_chan; static struct omap_system_dma_plat_info *p; static struct omap_dma_dev_attr *d; -static inline void disable_lnk(int lch); -static void omap_disable_channel_irq(int lch); -static inline void omap_enable_channel_irq(int lch); - -#define REVISIT_24XX() printk(KERN_ERR "FIXME: no %s on 24xx\n", \ - __func__); - #define dma_read(reg) \ ({ \ u32 __val; \ @@ -153,75 +75,12 @@ static inline void omap_enable_channel_irq(int lch); __raw_writel((val), omap_dma_base + OMAP_DMA4_##reg); \ }) -#ifdef CONFIG_ARCH_OMAP15XX -/* Returns 1 if the DMA module is in OMAP1510-compatible mode, 0 otherwise */ -int omap_dma_in_1510_mode(void) -{ - return enable_1510_mode; -} -#else -#define omap_dma_in_1510_mode() 0 -#endif - -#ifdef CONFIG_ARCH_OMAP1 -static inline int get_gdma_dev(int req) -{ - u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4; - int shift = ((req - 1) % 5) * 6; - - return ((omap_readl(reg) >> shift) & 0x3f) + 1; -} - -static inline void set_gdma_dev(int req, int dev) -{ - u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4; - int shift = ((req - 1) % 5) * 6; - u32 l; - - l = omap_readl(reg); - l &= ~(0x3f << shift); - l |= (dev - 1) << shift; - omap_writel(l, reg); -} -#else -#define set_gdma_dev(req, dev) do {} while (0) -#endif - -static void dma_ocpsysconfig_errata(u32 *sys_cf, bool flag) -{ - u32 l; - - /* - * DMA Errata: - * Special programming model needed to disable DMA before end of block - */ - if (!flag) { - *sys_cf = dma_read(OCP_SYSCONFIG); - l = *sys_cf; - /* Middle mode reg set no Standby */ - l &= ~((1 << 12)|(1 << 13)); - dma_write(l, OCP_SYSCONFIG); - } else - /* put back old value */ - dma_write(*sys_cf, OCP_SYSCONFIG); -} - -/* Omap1 only */ -static void clear_lch_regs(int lch) -{ - int i; - void __iomem *lch_base = omap_dma_base + OMAP1_DMA_CH_BASE(lch); - - for (i = 0; i < 0x2c; i += 2) - __raw_writew(0, lch_base + i); -} - void omap_set_dma_priority(int lch, int dst_port, int priority) { unsigned long reg; u32 l; - if (cpu_class_is_omap1()) { + if (d->dev_caps & IS_WORD_16) { switch (dst_port) { case OMAP_DMA_PORT_OCP_T1: /* FFFECC00 */ reg = OMAP_TC_OCPT1_PRIOR; @@ -243,9 +102,7 @@ void omap_set_dma_priority(int lch, int dst_port, int priority) l &= ~(0xf << 8); l |= (priority & 0xf) << 8; omap_writel(l, reg); - } - - if (cpu_class_is_omap2()) { + } else { u32 ccr; ccr = dma_read(CCR(lch)); @@ -269,7 +126,7 @@ void omap_set_dma_transfer_params(int lch, int data_type, int elem_count, l |= data_type; dma_write(l, CSDP(lch)); - if (cpu_class_is_omap1()) { + if (d->dev_caps & IS_WORD_16) { u16 ccr; ccr = dma_read(CCR(lch)); @@ -283,15 +140,13 @@ void omap_set_dma_transfer_params(int lch, int data_type, int elem_count, if (sync_mode == OMAP_DMA_SYNC_BLOCK) ccr |= 1 << 2; dma_write(ccr, CCR2(lch)); - } - - if (cpu_class_is_omap2() && dma_trigger) { + } else if (dma_trigger) { u32 val; val = dma_read(CCR(lch)); /* DMA_SYNCHRO_CONTROL_UPPER depends on the channel number */ - val &= ~((1 << 23) | (3 << 19) | 0x1f); + val &= ~((3 << 19) | 0x1f); val |= (dma_trigger & ~0x1f) << 14; val |= dma_trigger & 0x1f; @@ -305,14 +160,11 @@ void omap_set_dma_transfer_params(int lch, int data_type, int elem_count, else val &= ~(1 << 18); - if (src_or_dst_synch == OMAP_DMA_DST_SYNC_PREFETCH) { - val &= ~(1 << 24); /* dest synch */ - val |= (1 << 23); /* Prefetch */ - } else if (src_or_dst_synch) { + if (src_or_dst_synch) val |= 1 << 24; /* source synch */ - } else { + else val &= ~(1 << 24); /* dest synch */ - } + dma_write(val, CCR(lch)); } @@ -323,9 +175,9 @@ EXPORT_SYMBOL(omap_set_dma_transfer_params); void omap_set_dma_color_mode(int lch, enum omap_dma_color_mode mode, u32 color) { - BUG_ON(omap_dma_in_1510_mode()); + BUG_ON(enable_1510_mode); - if (cpu_class_is_omap1()) { + if (d->dev_caps & IS_WORD_16) { u16 w; w = dma_read(CCR2(lch)); @@ -354,9 +206,7 @@ void omap_set_dma_color_mode(int lch, enum omap_dma_color_mode mode, u32 color) w |= 1; /* Channel type G */ } dma_write(w, LCH_CTRL(lch)); - } - - if (cpu_class_is_omap2()) { + } else { u32 val; val = dma_read(CCR(lch)); @@ -382,32 +232,6 @@ void omap_set_dma_color_mode(int lch, enum omap_dma_color_mode mode, u32 color) } EXPORT_SYMBOL(omap_set_dma_color_mode); -void omap_set_dma_write_mode(int lch, enum omap_dma_write_mode mode) -{ - if (cpu_class_is_omap2()) { - u32 csdp; - - csdp = dma_read(CSDP(lch)); - csdp &= ~(0x3 << 16); - csdp |= (mode << 16); - dma_write(csdp, CSDP(lch)); - } -} -EXPORT_SYMBOL(omap_set_dma_write_mode); - -void omap_set_dma_channel_mode(int lch, enum omap_dma_channel_mode mode) -{ - if (cpu_class_is_omap1() && !cpu_is_omap15xx()) { - u32 l; - - l = dma_read(LCH_CTRL(lch)); - l &= ~0x7; - l |= mode; - dma_write(l, LCH_CTRL(lch)); - } -} -EXPORT_SYMBOL(omap_set_dma_channel_mode); - /* Note that src_port is only for omap1 */ void omap_set_dma_src_params(int lch, int src_port, int src_amode, unsigned long src_start, @@ -415,7 +239,7 @@ void omap_set_dma_src_params(int lch, int src_port, int src_amode, { u32 l; - if (cpu_class_is_omap1()) { + if (d->dev_caps & SRC_PORT) { u16 w; w = dma_read(CSDP(lch)); @@ -429,12 +253,10 @@ void omap_set_dma_src_params(int lch, int src_port, int src_amode, l |= src_amode << 12; dma_write(l, CCR(lch)); - if (cpu_class_is_omap1()) { + if (d->dev_caps & IS_WORD_16) { dma_write(src_start >> 16, CSSA_U(lch)); dma_write((u16)src_start, CSSA_L(lch)); - } - - if (cpu_class_is_omap2()) + } else dma_write(src_start, CSSA(lch)); dma_write(src_ei, CSEI(lch)); @@ -461,16 +283,6 @@ void omap_set_dma_params(int lch, struct omap_dma_channel_params *params) } EXPORT_SYMBOL(omap_set_dma_params); -void omap_set_dma_src_index(int lch, int eidx, int fidx) -{ - if (cpu_class_is_omap2()) - return; - - dma_write(eidx, CSEI(lch)); - dma_write(fidx, CSFI(lch)); -} -EXPORT_SYMBOL(omap_set_dma_src_index); - void omap_set_dma_src_data_pack(int lch, int enable) { u32 l; @@ -495,13 +307,13 @@ void omap_set_dma_src_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) case OMAP_DMA_DATA_BURST_DIS: break; case OMAP_DMA_DATA_BURST_4: - if (cpu_class_is_omap2()) - burst = 0x1; - else + if (d->dev_caps & IS_BURST_ONLY4) burst = 0x2; + else + burst = 0x1; break; case OMAP_DMA_DATA_BURST_8: - if (cpu_class_is_omap2()) { + if (!(d->dev_caps & IS_BURST_ONLY4)) { burst = 0x2; break; } @@ -511,7 +323,7 @@ void omap_set_dma_src_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) * fall through */ case OMAP_DMA_DATA_BURST_16: - if (cpu_class_is_omap2()) { + if (!(d->dev_caps & IS_BURST_ONLY4)) { burst = 0x3; break; } @@ -535,7 +347,7 @@ void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode, { u32 l; - if (cpu_class_is_omap1()) { + if (d->dev_caps & DST_PORT) { l = dma_read(CSDP(lch)); l &= ~(0x1f << 9); l |= dest_port << 9; @@ -547,12 +359,10 @@ void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode, l |= dest_amode << 14; dma_write(l, CCR(lch)); - if (cpu_class_is_omap1()) { + if (d->dev_caps & IS_WORD_16) { dma_write(dest_start >> 16, CDSA_U(lch)); dma_write(dest_start, CDSA_L(lch)); - } - - if (cpu_class_is_omap2()) + } else dma_write(dest_start, CDSA(lch)); dma_write(dst_ei, CDEI(lch)); @@ -560,16 +370,6 @@ void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode, } EXPORT_SYMBOL(omap_set_dma_dest_params); -void omap_set_dma_dest_index(int lch, int eidx, int fidx) -{ - if (cpu_class_is_omap2()) - return; - - dma_write(eidx, CDEI(lch)); - dma_write(fidx, CDFI(lch)); -} -EXPORT_SYMBOL(omap_set_dma_dest_index); - void omap_set_dma_dest_data_pack(int lch, int enable) { u32 l; @@ -594,19 +394,19 @@ void omap_set_dma_dest_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) case OMAP_DMA_DATA_BURST_DIS: break; case OMAP_DMA_DATA_BURST_4: - if (cpu_class_is_omap2()) - burst = 0x1; - else + if (d->dev_caps & IS_BURST_ONLY4) burst = 0x2; + else + burst = 0x1; break; case OMAP_DMA_DATA_BURST_8: - if (cpu_class_is_omap2()) - burst = 0x2; - else + if (d->dev_caps & IS_BURST_ONLY4) burst = 0x3; + else + burst = 0x2; break; case OMAP_DMA_DATA_BURST_16: - if (cpu_class_is_omap2()) { + if (!(d->dev_caps & IS_BURST_ONLY4)) { burst = 0x3; break; } @@ -624,26 +424,6 @@ void omap_set_dma_dest_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) } EXPORT_SYMBOL(omap_set_dma_dest_burst_mode); -static inline void omap_enable_channel_irq(int lch) -{ - u32 status; - - /* Clear CSR */ - if (cpu_class_is_omap1()) - status = dma_read(CSR(lch)); - else if (cpu_class_is_omap2()) - dma_write(OMAP2_DMA_CSR_CLEAR_MASK, CSR(lch)); - - /* Enable some nice interrupts. */ - dma_write(dma_chan[lch].enabled_irqs, CICR(lch)); -} - -static void omap_disable_channel_irq(int lch) -{ - if (cpu_class_is_omap2()) - dma_write(0, CICR(lch)); -} - void omap_enable_dma_irq(int lch, u16 bits) { dma_chan[lch].enabled_irqs |= bits; @@ -656,81 +436,6 @@ void omap_disable_dma_irq(int lch, u16 bits) } EXPORT_SYMBOL(omap_disable_dma_irq); -static inline void enable_lnk(int lch) -{ - u32 l; - - l = dma_read(CLNK_CTRL(lch)); - - if (cpu_class_is_omap1()) - l &= ~(1 << 14); - - /* Set the ENABLE_LNK bits */ - if (dma_chan[lch].next_lch != -1) - l = dma_chan[lch].next_lch | (1 << 15); - -#ifndef CONFIG_ARCH_OMAP1 - if (cpu_class_is_omap2()) - if (dma_chan[lch].next_linked_ch != -1) - l = dma_chan[lch].next_linked_ch | (1 << 15); -#endif - - dma_write(l, CLNK_CTRL(lch)); -} - -static inline void disable_lnk(int lch) -{ - u32 l; - - l = dma_read(CLNK_CTRL(lch)); - - /* Disable interrupts */ - if (cpu_class_is_omap1()) { - dma_write(0, CICR(lch)); - /* Set the STOP_LNK bit */ - l |= 1 << 14; - } - - if (cpu_class_is_omap2()) { - omap_disable_channel_irq(lch); - /* Clear the ENABLE_LNK bit */ - l &= ~(1 << 15); - } - - dma_write(l, CLNK_CTRL(lch)); - dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE; -} - -static inline void omap2_enable_irq_lch(int lch) -{ - u32 val; - unsigned long flags; - - if (!cpu_class_is_omap2()) - return; - - spin_lock_irqsave(&dma_chan_lock, flags); - val = dma_read(IRQENABLE_L0); - val |= 1 << lch; - dma_write(val, IRQENABLE_L0); - spin_unlock_irqrestore(&dma_chan_lock, flags); -} - -static inline void omap2_disable_irq_lch(int lch) -{ - u32 val; - unsigned long flags; - - if (!cpu_class_is_omap2()) - return; - - spin_lock_irqsave(&dma_chan_lock, flags); - val = dma_read(IRQENABLE_L0); - val &= ~(1 << lch); - dma_write(val, IRQENABLE_L0); - spin_unlock_irqrestore(&dma_chan_lock, flags); -} - int omap_request_dma(int dev_id, const char *dev_name, void (*callback)(int lch, u16 ch_status, void *data), void *data, int *dma_ch_out) @@ -754,14 +459,12 @@ int omap_request_dma(int dev_id, const char *dev_name, chan = dma_chan + free_ch; chan->dev_id = dev_id; - pm_runtime_get_sync(&ddev->dev); - - if (cpu_class_is_omap1()) - clear_lch_regs(free_ch); - - if (cpu_class_is_omap2()) + if (p->clear_lch_regs) + p->clear_lch_regs(free_ch); + else omap_clear_dma(free_ch); + pm_runtime_get_sync(&ddev->dev); spin_unlock_irqrestore(&dma_chan_lock, flags); chan->dev_name = dev_name; @@ -769,25 +472,21 @@ int omap_request_dma(int dev_id, const char *dev_name, chan->data = data; chan->flags = 0; -#ifndef CONFIG_ARCH_OMAP1 - if (cpu_class_is_omap2()) { - chan->chain_id = -1; - chan->next_linked_ch = -1; - } -#endif + if (p->set_dma_chain_ch) + p->set_dma_chain_ch(free_ch); chan->enabled_irqs = OMAP_DMA_DROP_IRQ | OMAP_DMA_BLOCK_IRQ; - if (cpu_class_is_omap1()) + if (d->dev_caps & IS_WORD_16) chan->enabled_irqs |= OMAP1_DMA_TOUT_IRQ; - else if (cpu_class_is_omap2()) + else chan->enabled_irqs |= OMAP2_DMA_MISALIGNED_ERR_IRQ | - OMAP2_DMA_TRANS_ERR_IRQ; + OMAP2_DMA_TRANS_ERR_IRQ; - if (cpu_is_omap16xx()) { + if (p->set_gdma_dev) { /* If the sync device is set, configure it dynamically. */ if (dev_id != 0) { - set_gdma_dev(free_ch + 1, dev_id); + p->set_gdma_dev(free_ch + 1, dev_id); dev_id = free_ch + 1; } /* @@ -795,13 +494,15 @@ int omap_request_dma(int dev_id, const char *dev_name, * id. */ dma_write(dev_id | (1 << 10), CCR(free_ch)); - } else if (cpu_is_omap7xx() || cpu_is_omap15xx()) { + } else if (d->dev_caps & IS_WORD_16) dma_write(dev_id, CCR(free_ch)); - } - if (cpu_class_is_omap2()) { - omap2_enable_irq_lch(free_ch); - omap_enable_channel_irq(free_ch); + if (p->enable_irq_lch) { + spin_lock_irqsave(&dma_chan_lock, flags); + p->enable_irq_lch(free_ch); + spin_unlock_irqrestore(&dma_chan_lock, flags); + if (p->enable_channel_irq) + p->enable_channel_irq(free_ch); /* Clear the CSR register and IRQ status register */ dma_write(OMAP2_DMA_CSR_CLEAR_MASK, CSR(free_ch)); dma_write(1 << free_ch, IRQSTATUS_L0); @@ -823,15 +524,18 @@ void omap_free_dma(int lch) return; } - if (cpu_class_is_omap1()) { + if (d->dev_caps & IS_WORD_16) { /* Disable all DMA interrupts for the channel. */ dma_write(0, CICR(lch)); /* Make sure the DMA transfer is stopped. */ dma_write(0, CCR(lch)); } - if (cpu_class_is_omap2()) { - omap2_disable_irq_lch(lch); + if (p->disable_irq_lch) { + unsigned long flags; + spin_lock_irqsave(&dma_chan_lock, flags); + p->disable_irq_lch(lch); + spin_unlock_irqrestore(&dma_chan_lock, flags); /* Clear the CSR register and IRQ status register */ dma_write(OMAP2_DMA_CSR_CLEAR_MASK, CSR(lch)); @@ -857,39 +561,6 @@ void omap_free_dma(int lch) EXPORT_SYMBOL(omap_free_dma); /** - * @brief omap_dma_set_global_params : Set global priority settings for dma - * - * @param arb_rate - * @param max_fifo_depth - * @param tparams - Number of threads to reserve : DMA_THREAD_RESERVE_NORM - * DMA_THREAD_RESERVE_ONET - * DMA_THREAD_RESERVE_TWOT - * DMA_THREAD_RESERVE_THREET - */ -void -omap_dma_set_global_params(int arb_rate, int max_fifo_depth, int tparams) -{ - u32 reg; - - if (!cpu_class_is_omap2()) { - printk(KERN_ERR "FIXME: no %s on 15xx/16xx\n", __func__); - return; - } - - if (max_fifo_depth == 0) - max_fifo_depth = 1; - if (arb_rate == 0) - arb_rate = 1; - - reg = 0xff & max_fifo_depth; - reg |= (0x3 & tparams) << 12; - reg |= (arb_rate & 0xff) << 16; - - dma_write(reg, GCR); -} -EXPORT_SYMBOL(omap_dma_set_global_params); - -/** * @brief omap_dma_set_prio_lch : Set channel wise priority settings * * @param lch @@ -910,7 +581,7 @@ omap_dma_set_prio_lch(int lch, unsigned char read_prio, } l = dma_read(CCR(lch)); l &= ~((1 << 6) | (1 << 26)); - if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) + if (d->dev_caps & IS_RW_PRIORIY) l |= ((read_prio & 0x1) << 6) | ((write_prio & 0x1) << 26); else l |= ((read_prio & 0x1) << 6); @@ -931,7 +602,7 @@ void omap_clear_dma(int lch) local_irq_save(flags); - if (cpu_class_is_omap1()) { + if (d->dev_caps & IS_WORD_16) { u32 l; l = dma_read(CCR(lch)); @@ -940,9 +611,7 @@ void omap_clear_dma(int lch) /* Clear pending interrupts */ l = dma_read(CSR(lch)); - } - - if (cpu_class_is_omap2()) { + } else { int i; void __iomem *lch_base = omap_dma_base + OMAP_DMA4_CH_BASE(lch); for (i = 0; i < 0x44; i += 4) @@ -961,18 +630,19 @@ void omap_start_dma(int lch) * The CPC/CDAC register needs to be initialized to zero * before starting dma transfer. */ - if (cpu_is_omap15xx()) + if (d->dev_caps & IS_WORD_16) dma_write(0, CPC(lch)); else dma_write(0, CDAC(lch)); - if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) { + if (!enable_1510_mode && dma_chan[lch].next_lch != -1) { int next_lch, cur_lch; char dma_chan_link_map[dma_chan_count]; dma_chan_link_map[lch] = 1; /* Set the link register of the first channel */ - enable_lnk(lch); + if (p->enable_lnk) + p->enable_lnk(lch); memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map)); cur_lch = dma_chan[lch].next_lch; @@ -985,8 +655,10 @@ void omap_start_dma(int lch) /* Mark the current channel */ dma_chan_link_map[cur_lch] = 1; - enable_lnk(cur_lch); - omap_enable_channel_irq(cur_lch); + if (p->enable_lnk) + p->enable_lnk(cur_lch); + if (p->enable_channel_irq) + p->enable_channel_irq(cur_lch); cur_lch = next_lch; } while (next_lch != -1); @@ -995,7 +667,8 @@ void omap_start_dma(int lch) if (p->errata & DMA_CHAINING_ERRATA) dma_write(lch, CLNK_CTRL(lch)); - omap_enable_channel_irq(lch); + if (p->enable_channel_irq) + p->enable_channel_irq(lch); l = dma_read(CCR(lch)); @@ -1014,14 +687,14 @@ void omap_stop_dma(int lch) u32 l; /* Disable all interrupts on the channel */ - if (cpu_class_is_omap1()) + if (d->dev_caps & IS_WORD_16) dma_write(0, CICR(lch)); l = dma_read(CCR(lch)); l &= ~OMAP_DMA_CCR_EN; dma_write(l, CCR(lch)); - if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) { + if (!(enable_1510_mode && (dma_chan[lch].next_lch != -1))) { int next_lch, cur_lch = lch; char dma_chan_link_map[dma_chan_count]; @@ -1033,7 +706,8 @@ void omap_stop_dma(int lch) /* Mark the current channel */ dma_chan_link_map[cur_lch] = 1; - disable_lnk(cur_lch); + if (p->disable_lnk) + p->disable_lnk(cur_lch); next_lch = dma_chan[cur_lch].next_lch; cur_lch = next_lch; @@ -1083,19 +757,16 @@ dma_addr_t omap_get_dma_src_pos(int lch) { dma_addr_t offset = 0; - if (cpu_is_omap15xx()) + if (d->dev_caps & ENABLE_1510_MODE) offset = dma_read(CPC(lch)); else offset = dma_read(CSAC(lch)); - /* - * omap 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is - * read before the DMA controller finished disabling the channel. - */ - if (!cpu_is_omap15xx() && offset == 0) + if ((p->errata & OMAP3_3_ERRATUM) && !(enable_1510_mode) + && (offset == 0)) offset = dma_read(CSAC(lch)); - if (cpu_class_is_omap1()) + if (d->dev_caps & IS_WORD_16) offset |= (dma_read(CSSA_U(lch)) << 16); return offset; @@ -1114,19 +785,16 @@ dma_addr_t omap_get_dma_dst_pos(int lch) { dma_addr_t offset = 0; - if (cpu_is_omap15xx()) + if (d->dev_caps & ENABLE_1510_MODE) offset = dma_read(CPC(lch)); else offset = dma_read(CDAC(lch)); - /* - * omap 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is - * read before the DMA controller finished disabling the channel. - */ - if (!cpu_is_omap15xx() && offset == 0) + if ((p->errata & OMAP3_3_ERRATUM) && !(enable_1510_mode) + && (offset == 0)) offset = dma_read(CDAC(lch)); - if (cpu_class_is_omap1()) + if (d->dev_caps & IS_WORD_16) offset |= (dma_read(CDSA_U(lch)) << 16); return offset; @@ -1143,7 +811,7 @@ int omap_dma_running(void) { int lch; - if (cpu_class_is_omap1()) + if (d->dev_caps & IS_WORD_16) if (omap_lcd_dma_running()) return 1; @@ -1161,7 +829,7 @@ int omap_dma_running(void) */ void omap_dma_link_lch(int lch_head, int lch_queue) { - if (omap_dma_in_1510_mode()) { + if (enable_1510_mode) { if (lch_head == lch_queue) { dma_write(dma_read(CCR(lch_head)) | (3 << 8), CCR(lch_head)); @@ -1188,7 +856,7 @@ EXPORT_SYMBOL(omap_dma_link_lch); */ void omap_dma_unlink_lch(int lch_head, int lch_queue) { - if (omap_dma_in_1510_mode()) { + if (enable_1510_mode) { if (lch_head == lch_queue) { dma_write(dma_read(CCR(lch_head)) & ~(3 << 8), CCR(lch_head)); @@ -1217,828 +885,11 @@ void omap_dma_unlink_lch(int lch_head, int lch_queue) } EXPORT_SYMBOL(omap_dma_unlink_lch); -/*----------------------------------------------------------------------------*/ - -#ifndef CONFIG_ARCH_OMAP1 -/* Create chain of DMA channesls */ -static void create_dma_lch_chain(int lch_head, int lch_queue) -{ - u32 l; - - /* Check if this is the first link in chain */ - if (dma_chan[lch_head].next_linked_ch == -1) { - dma_chan[lch_head].next_linked_ch = lch_queue; - dma_chan[lch_head].prev_linked_ch = lch_queue; - dma_chan[lch_queue].next_linked_ch = lch_head; - dma_chan[lch_queue].prev_linked_ch = lch_head; - } - - /* a link exists, link the new channel in circular chain */ - else { - dma_chan[lch_queue].next_linked_ch = - dma_chan[lch_head].next_linked_ch; - dma_chan[lch_queue].prev_linked_ch = lch_head; - dma_chan[lch_head].next_linked_ch = lch_queue; - dma_chan[dma_chan[lch_queue].next_linked_ch].prev_linked_ch = - lch_queue; - } - - l = dma_read(CLNK_CTRL(lch_head)); - l &= ~(0x1f); - l |= lch_queue; - dma_write(l, CLNK_CTRL(lch_head)); - - l = dma_read(CLNK_CTRL(lch_queue)); - l &= ~(0x1f); - l |= (dma_chan[lch_queue].next_linked_ch); - dma_write(l, CLNK_CTRL(lch_queue)); -} - -/** - * @brief omap_request_dma_chain : Request a chain of DMA channels - * - * @param dev_id - Device id using the dma channel - * @param dev_name - Device name - * @param callback - Call back function - * @chain_id - - * @no_of_chans - Number of channels requested - * @chain_mode - Dynamic or static chaining : OMAP_DMA_STATIC_CHAIN - * OMAP_DMA_DYNAMIC_CHAIN - * @params - Channel parameters - * - * @return - Success : 0 - * Failure: -EINVAL/-ENOMEM - */ -int omap_request_dma_chain(int dev_id, const char *dev_name, - void (*callback) (int lch, u16 ch_status, - void *data), - int *chain_id, int no_of_chans, int chain_mode, - struct omap_dma_channel_params params) -{ - int *channels; - int i, err; - - /* Is the chain mode valid ? */ - if (chain_mode != OMAP_DMA_STATIC_CHAIN - && chain_mode != OMAP_DMA_DYNAMIC_CHAIN) { - printk(KERN_ERR "Invalid chain mode requested\n"); - return -EINVAL; - } - - if (unlikely((no_of_chans < 1 - || no_of_chans > dma_lch_count))) { - printk(KERN_ERR "Invalid Number of channels requested\n"); - return -EINVAL; - } - - /* - * Allocate a queue to maintain the status of the channels - * in the chain - */ - channels = kmalloc(sizeof(*channels) * no_of_chans, GFP_KERNEL); - if (channels == NULL) { - printk(KERN_ERR "omap_dma: No memory for channel queue\n"); - return -ENOMEM; - } - - /* request and reserve DMA channels for the chain */ - for (i = 0; i < no_of_chans; i++) { - err = omap_request_dma(dev_id, dev_name, - callback, NULL, &channels[i]); - if (err < 0) { - int j; - for (j = 0; j < i; j++) - omap_free_dma(channels[j]); - kfree(channels); - printk(KERN_ERR "omap_dma: Request failed %d\n", err); - return err; - } - dma_chan[channels[i]].prev_linked_ch = -1; - dma_chan[channels[i]].state = DMA_CH_NOTSTARTED; - - /* - * Allowing client drivers to set common parameters now, - * so that later only relevant (src_start, dest_start - * and element count) can be set - */ - omap_set_dma_params(channels[i], ¶ms); - } - - *chain_id = channels[0]; - dma_linked_lch[*chain_id].linked_dmach_q = channels; - dma_linked_lch[*chain_id].chain_mode = chain_mode; - dma_linked_lch[*chain_id].chain_state = DMA_CHAIN_NOTSTARTED; - dma_linked_lch[*chain_id].no_of_lchs_linked = no_of_chans; - - for (i = 0; i < no_of_chans; i++) - dma_chan[channels[i]].chain_id = *chain_id; - - /* Reset the Queue pointers */ - OMAP_DMA_CHAIN_QINIT(*chain_id); - - /* Set up the chain */ - if (no_of_chans == 1) - create_dma_lch_chain(channels[0], channels[0]); - else { - for (i = 0; i < (no_of_chans - 1); i++) - create_dma_lch_chain(channels[i], channels[i + 1]); - } - - return 0; -} -EXPORT_SYMBOL(omap_request_dma_chain); - -/** - * @brief omap_modify_dma_chain_param : Modify the chain's params - Modify the - * params after setting it. Dont do this while dma is running!! - * - * @param chain_id - Chained logical channel id. - * @param params - * - * @return - Success : 0 - * Failure : -EINVAL - */ -int omap_modify_dma_chain_params(int chain_id, - struct omap_dma_channel_params params) -{ - int *channels; - u32 i; - - /* Check for input params */ - if (unlikely((chain_id < 0 - || chain_id >= dma_lch_count))) { - printk(KERN_ERR "Invalid chain id\n"); - return -EINVAL; - } - - /* Check if the chain exists */ - if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { - printk(KERN_ERR "Chain doesn't exists\n"); - return -EINVAL; - } - channels = dma_linked_lch[chain_id].linked_dmach_q; - - for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) { - /* - * Allowing client drivers to set common parameters now, - * so that later only relevant (src_start, dest_start - * and element count) can be set - */ - omap_set_dma_params(channels[i], ¶ms); - } - - return 0; -} -EXPORT_SYMBOL(omap_modify_dma_chain_params); - -/** - * @brief omap_free_dma_chain - Free all the logical channels in a chain. - * - * @param chain_id - * - * @return - Success : 0 - * Failure : -EINVAL - */ -int omap_free_dma_chain(int chain_id) -{ - int *channels; - u32 i; - - /* Check for input params */ - if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) { - printk(KERN_ERR "Invalid chain id\n"); - return -EINVAL; - } - - /* Check if the chain exists */ - if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { - printk(KERN_ERR "Chain doesn't exists\n"); - return -EINVAL; - } - - channels = dma_linked_lch[chain_id].linked_dmach_q; - for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) { - dma_chan[channels[i]].next_linked_ch = -1; - dma_chan[channels[i]].prev_linked_ch = -1; - dma_chan[channels[i]].chain_id = -1; - dma_chan[channels[i]].state = DMA_CH_NOTSTARTED; - omap_free_dma(channels[i]); - } - - kfree(channels); - - dma_linked_lch[chain_id].linked_dmach_q = NULL; - dma_linked_lch[chain_id].chain_mode = -1; - dma_linked_lch[chain_id].chain_state = -1; - - return (0); -} -EXPORT_SYMBOL(omap_free_dma_chain); - -/** - * @brief omap_dma_chain_status - Check if the chain is in - * active / inactive state. - * @param chain_id - * - * @return - Success : OMAP_DMA_CHAIN_ACTIVE/OMAP_DMA_CHAIN_INACTIVE - * Failure : -EINVAL - */ -int omap_dma_chain_status(int chain_id) -{ - /* Check for input params */ - if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) { - printk(KERN_ERR "Invalid chain id\n"); - return -EINVAL; - } - - /* Check if the chain exists */ - if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { - printk(KERN_ERR "Chain doesn't exists\n"); - return -EINVAL; - } - pr_debug("CHAINID=%d, qcnt=%d\n", chain_id, - dma_linked_lch[chain_id].q_count); - - if (OMAP_DMA_CHAIN_QEMPTY(chain_id)) - return OMAP_DMA_CHAIN_INACTIVE; - - return OMAP_DMA_CHAIN_ACTIVE; -} -EXPORT_SYMBOL(omap_dma_chain_status); - -/** - * @brief omap_dma_chain_a_transfer - Get a free channel from a chain, - * set the params and start the transfer. - * - * @param chain_id - * @param src_start - buffer start address - * @param dest_start - Dest address - * @param elem_count - * @param frame_count - * @param callbk_data - channel callback parameter data. - * - * @return - Success : 0 - * Failure: -EINVAL/-EBUSY - */ -int omap_dma_chain_a_transfer(int chain_id, int src_start, int dest_start, - int elem_count, int frame_count, void *callbk_data) -{ - int *channels; - u32 l, lch; - int start_dma = 0; - - /* - * if buffer size is less than 1 then there is - * no use of starting the chain - */ - if (elem_count < 1) { - printk(KERN_ERR "Invalid buffer size\n"); - return -EINVAL; - } - - /* Check for input params */ - if (unlikely((chain_id < 0 - || chain_id >= dma_lch_count))) { - printk(KERN_ERR "Invalid chain id\n"); - return -EINVAL; - } - - /* Check if the chain exists */ - if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { - printk(KERN_ERR "Chain doesn't exist\n"); - return -EINVAL; - } - - /* Check if all the channels in chain are in use */ - if (OMAP_DMA_CHAIN_QFULL(chain_id)) - return -EBUSY; - - /* Frame count may be negative in case of indexed transfers */ - channels = dma_linked_lch[chain_id].linked_dmach_q; - - /* Get a free channel */ - lch = channels[dma_linked_lch[chain_id].q_tail]; - - /* Store the callback data */ - dma_chan[lch].data = callbk_data; - - /* Increment the q_tail */ - OMAP_DMA_CHAIN_INCQTAIL(chain_id); - - /* Set the params to the free channel */ - if (src_start != 0) - dma_write(src_start, CSSA(lch)); - if (dest_start != 0) - dma_write(dest_start, CDSA(lch)); - - /* Write the buffer size */ - dma_write(elem_count, CEN(lch)); - dma_write(frame_count, CFN(lch)); - - /* - * If the chain is dynamically linked, - * then we may have to start the chain if its not active - */ - if (dma_linked_lch[chain_id].chain_mode == OMAP_DMA_DYNAMIC_CHAIN) { - - /* - * In Dynamic chain, if the chain is not started, - * queue the channel - */ - if (dma_linked_lch[chain_id].chain_state == - DMA_CHAIN_NOTSTARTED) { - /* Enable the link in previous channel */ - if (dma_chan[dma_chan[lch].prev_linked_ch].state == - DMA_CH_QUEUED) - enable_lnk(dma_chan[lch].prev_linked_ch); - dma_chan[lch].state = DMA_CH_QUEUED; - } - - /* - * Chain is already started, make sure its active, - * if not then start the chain - */ - else { - start_dma = 1; - - if (dma_chan[dma_chan[lch].prev_linked_ch].state == - DMA_CH_STARTED) { - enable_lnk(dma_chan[lch].prev_linked_ch); - dma_chan[lch].state = DMA_CH_QUEUED; - start_dma = 0; - if (0 == ((1 << 7) & dma_read( - CCR(dma_chan[lch].prev_linked_ch)))) { - disable_lnk(dma_chan[lch]. - prev_linked_ch); - pr_debug("\n prev ch is stopped\n"); - start_dma = 1; - } - } - - else if (dma_chan[dma_chan[lch].prev_linked_ch].state - == DMA_CH_QUEUED) { - enable_lnk(dma_chan[lch].prev_linked_ch); - dma_chan[lch].state = DMA_CH_QUEUED; - start_dma = 0; - } - omap_enable_channel_irq(lch); - - l = dma_read(CCR(lch)); - - if ((0 == (l & (1 << 24)))) - l &= ~(1 << 25); - else - l |= (1 << 25); - if (start_dma == 1) { - if (0 == (l & (1 << 7))) { - l |= (1 << 7); - dma_chan[lch].state = DMA_CH_STARTED; - pr_debug("starting %d\n", lch); - dma_write(l, CCR(lch)); - } else - start_dma = 0; - } else { - if (0 == (l & (1 << 7))) - dma_write(l, CCR(lch)); - } - dma_chan[lch].flags |= OMAP_DMA_ACTIVE; - } - } - - return 0; -} -EXPORT_SYMBOL(omap_dma_chain_a_transfer); - -/** - * @brief omap_start_dma_chain_transfers - Start the chain - * - * @param chain_id - * - * @return - Success : 0 - * Failure : -EINVAL/-EBUSY - */ -int omap_start_dma_chain_transfers(int chain_id) -{ - int *channels; - u32 l, i; - - if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) { - printk(KERN_ERR "Invalid chain id\n"); - return -EINVAL; - } - - channels = dma_linked_lch[chain_id].linked_dmach_q; - - if (dma_linked_lch[channels[0]].chain_state == DMA_CHAIN_STARTED) { - printk(KERN_ERR "Chain is already started\n"); - return -EBUSY; - } - - if (dma_linked_lch[chain_id].chain_mode == OMAP_DMA_STATIC_CHAIN) { - for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; - i++) { - enable_lnk(channels[i]); - omap_enable_channel_irq(channels[i]); - } - } else { - omap_enable_channel_irq(channels[0]); - } - - l = dma_read(CCR(channels[0])); - l |= (1 << 7); - dma_linked_lch[chain_id].chain_state = DMA_CHAIN_STARTED; - dma_chan[channels[0]].state = DMA_CH_STARTED; - - if ((0 == (l & (1 << 24)))) - l &= ~(1 << 25); - else - l |= (1 << 25); - dma_write(l, CCR(channels[0])); - - dma_chan[channels[0]].flags |= OMAP_DMA_ACTIVE; - - return 0; -} -EXPORT_SYMBOL(omap_start_dma_chain_transfers); - -/** - * @brief omap_stop_dma_chain_transfers - Stop the dma transfer of a chain. - * - * @param chain_id - * - * @return - Success : 0 - * Failure : EINVAL - */ -int omap_stop_dma_chain_transfers(int chain_id) -{ - int *channels; - u32 l, i; - u32 get_sysconfig; - - /* Check for input params */ - if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) { - printk(KERN_ERR "Invalid chain id\n"); - return -EINVAL; - } - - /* Check if the chain exists */ - if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { - printk(KERN_ERR "Chain doesn't exists\n"); - return -EINVAL; - } - channels = dma_linked_lch[chain_id].linked_dmach_q; - - if (p->errata & DMA_SYSCONFIG_ERRATA) - dma_ocpsysconfig_errata(&get_sysconfig, false); - - for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) { - - /* Stop the Channel transmission */ - l = dma_read(CCR(channels[i])); - l &= ~(1 << 7); - dma_write(l, CCR(channels[i])); - - /* Disable the link in all the channels */ - disable_lnk(channels[i]); - dma_chan[channels[i]].state = DMA_CH_NOTSTARTED; - - } - dma_linked_lch[chain_id].chain_state = DMA_CHAIN_NOTSTARTED; - - /* Reset the Queue pointers */ - OMAP_DMA_CHAIN_QINIT(chain_id); - - if (p->errata & DMA_SYSCONFIG_ERRATA) - dma_ocpsysconfig_errata(&get_sysconfig, true); - - return 0; -} -EXPORT_SYMBOL(omap_stop_dma_chain_transfers); - -/* Get the index of the ongoing DMA in chain */ -/** - * @brief omap_get_dma_chain_index - Get the element and frame index - * of the ongoing DMA in chain - * - * @param chain_id - * @param ei - Element index - * @param fi - Frame index - * - * @return - Success : 0 - * Failure : -EINVAL - */ -int omap_get_dma_chain_index(int chain_id, int *ei, int *fi) -{ - int lch; - int *channels; - - /* Check for input params */ - if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) { - printk(KERN_ERR "Invalid chain id\n"); - return -EINVAL; - } - - /* Check if the chain exists */ - if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { - printk(KERN_ERR "Chain doesn't exists\n"); - return -EINVAL; - } - if ((!ei) || (!fi)) - return -EINVAL; - - channels = dma_linked_lch[chain_id].linked_dmach_q; - - /* Get the current channel */ - lch = channels[dma_linked_lch[chain_id].q_head]; - - *ei = dma_read(CCEN(lch)); - *fi = dma_read(CCFN(lch)); - - return 0; -} -EXPORT_SYMBOL(omap_get_dma_chain_index); - -/** - * @brief omap_get_dma_chain_dst_pos - Get the destination position of the - * ongoing DMA in chain - * - * @param chain_id - * - * @return - Success : Destination position - * Failure : -EINVAL - */ -int omap_get_dma_chain_dst_pos(int chain_id) -{ - int lch; - int *channels; - - /* Check for input params */ - if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) { - printk(KERN_ERR "Invalid chain id\n"); - return -EINVAL; - } - - /* Check if the chain exists */ - if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { - printk(KERN_ERR "Chain doesn't exists\n"); - return -EINVAL; - } - - channels = dma_linked_lch[chain_id].linked_dmach_q; - - /* Get the current channel */ - lch = channels[dma_linked_lch[chain_id].q_head]; - - return dma_read(CDAC(lch)); -} -EXPORT_SYMBOL(omap_get_dma_chain_dst_pos); - -/** - * @brief omap_get_dma_chain_src_pos - Get the source position - * of the ongoing DMA in chain - * @param chain_id - * - * @return - Success : Destination position - * Failure : -EINVAL - */ -int omap_get_dma_chain_src_pos(int chain_id) -{ - int lch; - int *channels; - - /* Check for input params */ - if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) { - printk(KERN_ERR "Invalid chain id\n"); - return -EINVAL; - } - - /* Check if the chain exists */ - if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { - printk(KERN_ERR "Chain doesn't exists\n"); - return -EINVAL; - } - - channels = dma_linked_lch[chain_id].linked_dmach_q; - - /* Get the current channel */ - lch = channels[dma_linked_lch[chain_id].q_head]; - - return dma_read(CSAC(lch)); -} -EXPORT_SYMBOL(omap_get_dma_chain_src_pos); -#endif /* ifndef CONFIG_ARCH_OMAP1 */ - -/*----------------------------------------------------------------------------*/ - -#ifdef CONFIG_ARCH_OMAP1 - -static int omap1_dma_handle_ch(int ch) -{ - u32 csr; - - if (enable_1510_mode && ch >= 6) { - csr = dma_chan[ch].saved_csr; - dma_chan[ch].saved_csr = 0; - } else - csr = dma_read(CSR(ch)); - if (enable_1510_mode && ch <= 2 && (csr >> 7) != 0) { - dma_chan[ch + 6].saved_csr = csr >> 7; - csr &= 0x7f; - } - if ((csr & 0x3f) == 0) - return 0; - if (unlikely(dma_chan[ch].dev_id == -1)) { - printk(KERN_WARNING "Spurious interrupt from DMA channel " - "%d (CSR %04x)\n", ch, csr); - return 0; - } - if (unlikely(csr & OMAP1_DMA_TOUT_IRQ)) - printk(KERN_WARNING "DMA timeout with device %d\n", - dma_chan[ch].dev_id); - if (unlikely(csr & OMAP_DMA_DROP_IRQ)) - printk(KERN_WARNING "DMA synchronization event drop occurred " - "with device %d\n", dma_chan[ch].dev_id); - if (likely(csr & OMAP_DMA_BLOCK_IRQ)) - dma_chan[ch].flags &= ~OMAP_DMA_ACTIVE; - if (likely(dma_chan[ch].callback != NULL)) - dma_chan[ch].callback(ch, csr, dma_chan[ch].data); - - return 1; -} - -static irqreturn_t omap1_dma_irq_handler(int irq, void *dev_id) -{ - int ch = ((int) dev_id) - 1; - int handled = 0; - - for (;;) { - int handled_now = 0; - - handled_now += omap1_dma_handle_ch(ch); - if (enable_1510_mode && dma_chan[ch + 6].saved_csr) - handled_now += omap1_dma_handle_ch(ch + 6); - if (!handled_now) - break; - handled += handled_now; - } - - return handled ? IRQ_HANDLED : IRQ_NONE; -} - -#else -#define omap1_dma_irq_handler NULL -#endif - -#ifdef CONFIG_ARCH_OMAP2PLUS - -static int omap2_dma_handle_ch(int ch) -{ - u32 status = dma_read(CSR(ch)); - - if (!status) { - if (printk_ratelimit()) - printk(KERN_WARNING "Spurious DMA IRQ for lch %d\n", - ch); - dma_write(1 << ch, IRQSTATUS_L0); - return 0; - } - if (unlikely(dma_chan[ch].dev_id == -1)) { - if (printk_ratelimit()) - printk(KERN_WARNING "IRQ %04x for non-allocated DMA" - "channel %d\n", status, ch); - return 0; - } - if (unlikely(status & OMAP_DMA_DROP_IRQ)) - printk(KERN_INFO - "DMA synchronization event drop occurred with device " - "%d\n", dma_chan[ch].dev_id); - if (unlikely(status & OMAP2_DMA_TRANS_ERR_IRQ)) { - printk(KERN_INFO "DMA transaction error with device %d\n", - dma_chan[ch].dev_id); - if (cpu_class_is_omap2()) { - /* - * Errata: sDMA Channel is not disabled - * after a transaction error. So we explicitely - * disable the channel - */ - u32 ccr; - - ccr = dma_read(CCR(ch)); - ccr &= ~OMAP_DMA_CCR_EN; - dma_write(ccr, CCR(ch)); - dma_chan[ch].flags &= ~OMAP_DMA_ACTIVE; - } - } - if (unlikely(status & OMAP2_DMA_SECURE_ERR_IRQ)) - printk(KERN_INFO "DMA secure error with device %d\n", - dma_chan[ch].dev_id); - if (unlikely(status & OMAP2_DMA_MISALIGNED_ERR_IRQ)) - printk(KERN_INFO "DMA misaligned error with device %d\n", - dma_chan[ch].dev_id); - - dma_write(OMAP2_DMA_CSR_CLEAR_MASK, CSR(ch)); - dma_write(1 << ch, IRQSTATUS_L0); - - /* If the ch is not chained then chain_id will be -1 */ - if (dma_chan[ch].chain_id != -1) { - int chain_id = dma_chan[ch].chain_id; - dma_chan[ch].state = DMA_CH_NOTSTARTED; - if (dma_read(CLNK_CTRL(ch)) & (1 << 15)) - dma_chan[dma_chan[ch].next_linked_ch].state = - DMA_CH_STARTED; - if (dma_linked_lch[chain_id].chain_mode == - OMAP_DMA_DYNAMIC_CHAIN) - disable_lnk(ch); - - if (!OMAP_DMA_CHAIN_QEMPTY(chain_id)) - OMAP_DMA_CHAIN_INCQHEAD(chain_id); - - status = dma_read(CSR(ch)); - } - - dma_write(status, CSR(ch)); - - if (likely(dma_chan[ch].callback != NULL)) - dma_chan[ch].callback(ch, status, dma_chan[ch].data); - - return 0; -} - -/* STATUS register count is from 1-32 while our is 0-31 */ -static irqreturn_t omap2_dma_irq_handler(int irq, void *dev_id) -{ - u32 val, enable_reg; - int i; - - val = dma_read(IRQSTATUS_L0); - if (val == 0) { - if (printk_ratelimit()) - printk(KERN_WARNING "Spurious DMA IRQ\n"); - return IRQ_HANDLED; - } - enable_reg = dma_read(IRQENABLE_L0); - val &= enable_reg; /* Dispatch only relevant interrupts */ - for (i = 0; i < dma_lch_count && val != 0; i++) { - if (val & 1) - omap2_dma_handle_ch(i); - val >>= 1; - } - - return IRQ_HANDLED; -} - -static struct irqaction omap24xx_dma_irq = { - .name = "DMA", - .handler = omap2_dma_irq_handler, - .flags = IRQF_DISABLED -}; - -#else -static struct irqaction omap24xx_dma_irq; -#endif - -/*----------------------------------------------------------------------------*/ - -void omap_dma_global_context_save(void) -{ - omap_dma_global_context.dma_irqenable_l0 = - dma_read(IRQENABLE_L0); - omap_dma_global_context.dma_ocp_sysconfig = - dma_read(OCP_SYSCONFIG); - omap_dma_global_context.dma_gcr = dma_read(GCR); -} - -void omap_dma_global_context_restore(void) -{ - int ch; - - dma_write(omap_dma_global_context.dma_gcr, GCR); - dma_write(omap_dma_global_context.dma_ocp_sysconfig, - OCP_SYSCONFIG); - dma_write(omap_dma_global_context.dma_irqenable_l0, - IRQENABLE_L0); - - /* - * A bug in ROM code leaves IRQ status for channels 0 and 1 uncleared - * after secure sram context save and restore. Hence we need to - * manually clear those IRQs to avoid spurious interrupts. This - * affects only secure devices. - */ - if (cpu_is_omap34xx() && (omap_type() != OMAP2_DEVICE_TYPE_GP)) - dma_write(0x3 , IRQSTATUS_L0); - - for (ch = 0; ch < dma_chan_count; ch++) - if (dma_chan[ch].dev_id != -1) - omap_clear_dma(ch); -} - static int __devinit omap_system_dma_probe(struct platform_device *pdev) { struct omap_system_dma_plat_info *pdata = pdev->dev.platform_data; struct resource *mem; int ch, ret = 0; - int dma_irq; - char irq_name[4]; if (!pdata) { dev_err(&pdev->dev, "%s: System DMA initialized without" @@ -2059,10 +910,11 @@ static int __devinit omap_system_dma_probe(struct platform_device *pdev) if (!omap_dma_base) { dev_err(&pdev->dev, "%s: ioremap fail\n", __func__); ret = -ENOMEM; - goto exit_release_region; + release_mem_region(mem->start, resource_size(mem)); + return ret; } - if (cpu_class_is_omap2() && omap_dma_reserve_channels + if (!(d->dev_caps & IS_WORD_16) && omap_dma_reserve_channels && (omap_dma_reserve_channels <= dma_lch_count)) d->lch_count = omap_dma_reserve_channels; @@ -2070,19 +922,11 @@ static int __devinit omap_system_dma_probe(struct platform_device *pdev) dma_chan_count = d->chan_count; dma_chan = d->chan; - if (cpu_class_is_omap2()) { - dma_linked_lch = kzalloc(sizeof(struct dma_link_info) * - dma_lch_count, GFP_KERNEL); - if (!dma_linked_lch) { - ret = -ENOMEM; - goto exit_dma_chan; - } - } enable_1510_mode = d->dev_caps & ENABLE_1510_MODE; - if (cpu_is_omap15xx()) { + if (enable_1510_mode) { printk(KERN_INFO "DMA support for OMAP15xx initialized\n"); - } else if (cpu_is_omap16xx() || cpu_is_omap7xx()) { + } else if (d->dev_caps & IS_WORD_16) { printk(KERN_INFO "OMAP DMA hardware version %d\n", dma_read(HW_ID)); printk(KERN_INFO "DMA capabilities: %08x:%08x:%04x:%04x:%04x\n", @@ -2100,7 +944,7 @@ static int __devinit omap_system_dma_probe(struct platform_device *pdev) w |= 1 << 3; dma_write(w, GSCR); } - } else if (cpu_class_is_omap2()) { + } else { u8 revision = dma_read(REVISION) & 0xff; printk(KERN_INFO "OMAP DMA hardware revision %d.%d\n", revision >> 4, revision & 0xf); @@ -2109,61 +953,23 @@ static int __devinit omap_system_dma_probe(struct platform_device *pdev) spin_lock_init(&dma_chan_lock); for (ch = 0; ch < dma_chan_count; ch++) { omap_clear_dma(ch); - if (cpu_class_is_omap2()) - omap2_disable_irq_lch(ch); + if (p->disable_irq_lch) { + unsigned long flags; + spin_lock_irqsave(&dma_chan_lock, flags); + p->disable_irq_lch(ch); + spin_unlock_irqrestore(&dma_chan_lock, flags); + } dma_chan[ch].dev_id = -1; dma_chan[ch].next_lch = -1; - - if (ch >= 6 && enable_1510_mode) - continue; - - if (cpu_class_is_omap1()) { - /* - * request_irq() doesn't like dev_id (ie. ch) being - * zero, so we have to kludge around this. - */ - sprintf(&irq_name[0], "%d", ch); - dma_irq = platform_get_irq_byname(pdev, irq_name); - - if (dma_irq < 0) { - dev_err(&pdev->dev, "%s:unable to get irq\n", - __func__); - ret = dma_irq; - goto exit_unmap; - } - ret = request_irq(dma_irq, - omap1_dma_irq_handler, 0, "DMA", - (void *) (ch + 1)); - if (ret != 0) { - int irq_rel; - printk(KERN_ERR "unable to request IRQ %d" - "for DMA (error %d)\n", dma_irq, ret); - for (irq_rel = 0; irq_rel < ch; - irq_rel++) { - dma_irq = platform_get_irq(pdev, - irq_rel); - free_irq(dma_irq, (void *) - (irq_rel + 1)); - goto exit_dma_chan; - } - } - } } - if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) + if (d->dev_caps & GLOBAL_PRIORITY) omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE, DMA_DEFAULT_FIFO_DEPTH, 0); - if (cpu_class_is_omap2()) { - strcpy(irq_name, "0"); - dma_irq = platform_get_irq_byname(pdev, irq_name); - setup_irq(dma_irq, &omap24xx_dma_irq); - } - /* reserve dma channels 0 and 1 in high security devices */ - if (cpu_is_omap34xx() && - (omap_type() != OMAP2_DEVICE_TYPE_GP)) { + if (d->dev_caps & RESERVE_CHANNEL) { printk(KERN_INFO "Reserving DMA channels 0 and 1 for " "HS ROM code\n"); dma_chan[0].dev_id = 0; @@ -2179,14 +985,6 @@ static int __devinit omap_system_dma_probe(struct platform_device *pdev) dev_info(&pdev->dev, "System DMA registered\n"); return 0; - -exit_dma_chan: - kfree(dma_chan); -exit_unmap: - iounmap(omap_dma_base); -exit_release_region: - release_mem_region(mem->start, resource_size(mem)); - return ret; } static int __devexit omap_system_dma_remove(struct platform_device *pdev) diff --git a/arch/arm/plat-omap/include/plat/dma.h b/arch/arm/plat-omap/include/plat/dma.h index ca251ac..3bf7c19 100644 --- a/arch/arm/plat-omap/include/plat/dma.h +++ b/arch/arm/plat-omap/include/plat/dma.h @@ -242,6 +242,9 @@ #define DMA_THREAD_FIFO_25 (0x02 << 14) #define DMA_THREAD_FIFO_50 (0x03 << 14) +#define OMAP_DMA_ACTIVE 0x01 +#define OMAP2_DMA_CSR_CLEAR_MASK 0xffe + /* Chaining modes*/ #ifndef CONFIG_ARCH_OMAP1 #define OMAP_DMA_STATIC_CHAIN 0x1 @@ -355,6 +358,16 @@ struct omap_system_dma_plat_info { struct omap_dma_dev_attr *dma_attr; void __iomem *omap_dma_base; u32 errata; + void (*enable_irq_lch)(int lch); + void (*disable_irq_lch)(int lch); + void (*enable_lnk)(int lch); + void (*disable_lnk)(int lch); + void (*clear_lch_regs)(int lch); + int (*get_gdma_dev)(int req); + void (*set_gdma_dev)(int req, int dev); + void (*enable_channel_irq)(int lch); + void (*disable_channel_irq)(int lch); + void (*set_dma_chain_ch)(int free_ch); }; extern void omap_set_dma_priority(int lch, int dst_port, int priority); @@ -418,29 +431,6 @@ void omap_dma_global_context_restore(void); extern void omap_dma_disable_irq(int lch); -/* Chaining APIs */ -#ifndef CONFIG_ARCH_OMAP1 -extern int omap_request_dma_chain(int dev_id, const char *dev_name, - void (*callback) (int lch, u16 ch_status, - void *data), - int *chain_id, int no_of_chans, - int chain_mode, - struct omap_dma_channel_params params); -extern int omap_free_dma_chain(int chain_id); -extern int omap_dma_chain_a_transfer(int chain_id, int src_start, - int dest_start, int elem_count, - int frame_count, void *callbk_data); -extern int omap_start_dma_chain_transfers(int chain_id); -extern int omap_stop_dma_chain_transfers(int chain_id); -extern int omap_get_dma_chain_index(int chain_id, int *ei, int *fi); -extern int omap_get_dma_chain_dst_pos(int chain_id); -extern int omap_get_dma_chain_src_pos(int chain_id); - -extern int omap_modify_dma_chain_params(int chain_id, - struct omap_dma_channel_params params); -extern int omap_dma_chain_status(int chain_id); -#endif - #if defined(CONFIG_ARCH_OMAP1) && defined(CONFIG_FB_OMAP) #include #else