From 4930d247af29f849cd1bddd65be2400684dc886e Mon Sep 17 00:00:00 2001 From: Gaël PORTAY Date: Sat, 6 Sep 2014 19:52:35 +0200 Subject: ARM: at91/tclib: move initialization from alloc to probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move resource retrieval from atmel_tc_alloc to tc_probe to avoid lately reporting resource related issues when a TC block user request a TC block. Moreover, resources retrieval are usually done in the probe function, thus moving them add some consistency with other drivers. Initialization is done once, ie not every time a tc block is requested. If it fails, the device is not appended to the list of tc blocks. Furhermore, the device id is retrieved at probe as well, avoiding parsing DT every time the user requests of tc block. Signed-off-by: Gaël PORTAY Acked-by: Thierry Reding Acked-by: Boris Brezillon Signed-off-by: Nicolas Ferre --- include/linux/atmel_tc.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/atmel_tc.h b/include/linux/atmel_tc.h index 89a931babecf..d8aa88461c64 100644 --- a/include/linux/atmel_tc.h +++ b/include/linux/atmel_tc.h @@ -44,12 +44,13 @@ struct atmel_tcb_config { /** * struct atmel_tc - information about a Timer/Counter Block * @pdev: physical device - * @iomem: resource associated with the I/O register * @regs: mapping through which the I/O registers can be accessed + * @id: block id * @tcb_config: configuration data from SoC * @irq: irq for each of the three channels * @clk: internal clock source for each of the three channels * @node: list node, for tclib internal use + * @allocated: if already used, for tclib internal use * * On some platforms, each TC channel has its own clocks and IRQs, * while on others, all TC channels share the same clock and IRQ. @@ -61,15 +62,16 @@ struct atmel_tcb_config { */ struct atmel_tc { struct platform_device *pdev; - struct resource *iomem; void __iomem *regs; + int id; const struct atmel_tcb_config *tcb_config; int irq[3]; struct clk *clk[3]; struct list_head node; + bool allocated; }; -extern struct atmel_tc *atmel_tc_alloc(unsigned block, const char *name); +extern struct atmel_tc *atmel_tc_alloc(unsigned block); extern void atmel_tc_free(struct atmel_tc *tc); /* platform-specific ATMEL_TC_TIMER_CLOCKx divisors (0 means 32KiHz) */ -- cgit v1.2.3 From 84f462371cc07272a17e2ae96c3540f795db273a Mon Sep 17 00:00:00 2001 From: Gaël PORTAY Date: Sat, 6 Sep 2014 19:52:36 +0200 Subject: ARM: at91/tclib: mask interruptions at shutdown and probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shutdown properly the timer counter block by masking interruptions. Otherwise, a segmentation may happen when kexec-ing a new kernel (see backtrace below). An interruption may happen before the handler is set, leading to a kernel segmentation fault. Furthermore, we make sure the interruptions are masked when the driver is initialized. This will prevent freshly kexec-ed kernel from crashing when launched from a kernel which does not properly mask interruptions at shutdown. The backtrace below happened after kexec-ing a new kernel, from a kernel that did not shut down properly leaving interruptions unmasked. Unable to handle kernel NULL pointer dereference at virtual address 00000000 pgd = c0004000 [00000000] *pgd=00000000 Internal error: Oops: 80000005 [#1] ARM Modules linked in: CPU: 0 PID: 1 Comm: swapper Not tainted 3.16.0+ #144 task: c1828aa0 ti: c182a000 task.ti: c182a000 PC is at 0x0 LR is at ch2_irq+0x28/0x30 pc : [<00000000>] lr : [] psr: 000000d3 sp : c182bd38 ip : c182bd48 fp : c182bd44 r10: c0373390 r9 : c1825b00 r8 : 60000053 r7 : 00000000 r6 : 00000000 r5 : 00000013 r4 : c036e800 r3 : 00000000 r2 : 00002004 r1 : c036e760 r0 : c036e760 Flags: nzcv IRQs off FIQs off Mode SVC_32 ISA ARM Segment kernel Control: 0005317f Table: 20004000 DAC: 00000017 Process swapper (pid: 1, stack limit = 0xc182a1c0) Stack: (0xc182bd38 to 0xc182c000) bd20: c182bd7c c182bd48 bd40: c0045430 c01db8ec 00000000 c18c6f40 c182bd74 c1825b00 c035cec4 00000000 bd60: c182be2c 60000053 c1825b34 00000000 c182bd94 c182bd80 c0045570 c0045408 bd80: 00000000 c1825b00 c182bdac c182bd98 c0047f34 c0045550 00000013 c036619c bda0: c182bdc4 c182bdb0 c0044da4 c0047e98 0000007f 00000013 c182bde4 c182bdc8 bdc0: c0009e34 c0044d8c fefff000 c0046728 60000053 ffffffff c182bdf4 c182bde8 bde0: c00086a8 c0009ddc c182be74 c182bdf8 c000cb80 c0008674 00000000 00000013 be00: 00000000 00014200 c1825b00 c036e800 00000013 c035ed98 60000053 c1825b34 be20: 00000000 c182be74 c182be20 c182be40 c0047994 c0046728 60000053 ffffffff be40: 00000013 c036e800 c182be64 c1825b00 00000013 c036e800 c035ed98 c03874bc be60: 00000004 c036e700 c182be94 c182be78 c004689c c0046398 c036e760 c18c6080 be80: 00000000 c035ed10 c182bedc c182be98 c0348b08 c004684c 0000000c c034dac8 bea0: 004c4b3f c028c338 c036e760 00000013 c014ecc8 c18e67e0 c035b9c0 c0348884 bec0: c035b9c0 c182a020 00000000 00000000 c182bf54 c182bee0 c00089fc c0348894 bee0: c00da51c c1ffcc78 c182bf0c c182bef8 c002d100 c002d09c c1ffcc78 00000000 bf00: c182bf54 c182bf10 c002d308 c0336570 c182bf3c c0334e44 00000003 00000003 bf20: 00000030 c0334b44 c0044d74 00000003 00000003 c034dac8 c0350a94 c0373440 bf40: c0373440 00000030 c182bf94 c182bf58 c0336d24 c000890c 00000003 00000003 bf60: c0336560 c182bf64 c182bf64 6e616e0d 00000000 c0272fc8 00000000 00000000 bf80: 00000000 00000000 c182bfac c182bf98 c0272fd8 c0336bd8 c182a000 00000000 bfa0: 00000000 c182bfb0 c00095d0 c0272fd8 00000000 00000000 00000000 00000000 bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 bfe0: 00000000 00000000 00000000 00000000 00000013 00000000 374d27cd 33cc33e4 Backtrace: [] (ch2_irq) from [] (handle_irq_event_percpu+0x38/0x148) [] (handle_irq_event_percpu) from [] (handle_irq_event+0x30/0x40) r10:00000000 r9:c1825b34 r8:60000053 r7:c182be2c r6:00000000 r5:c035cec4 r4:c1825b00 [] (handle_irq_event) from [] (handle_fasteoi_irq+0xac/0x11c) r4:c1825b00 r3:00000000 [] (handle_fasteoi_irq) from [] (generic_handle_irq+0x28/0x38) r5:c036619c r4:00000013 [] (generic_handle_irq) from [] (handle_IRQ+0x68/0x88) r4:00000013 r3:0000007f [] (handle_IRQ) from [] (at91_aic_handle_irq+0x44/0x4c) r6:ffffffff r5:60000053 r4:c0046728 r3:fefff000 [] (at91_aic_handle_irq) from [] (__irq_svc+0x40/0x4c) Exception stack(0xc182bdf8 to 0xc182be40) bde0: 00000000 00000013 be00: 00000000 00014200 c1825b00 c036e800 00000013 c035ed98 60000053 c1825b34 be20: 00000000 c182be74 c182be20 c182be40 c0047994 c0046728 60000053 ffffffff [] (__setup_irq) from [] (setup_irq+0x60/0x8c) r10:c036e700 r9:00000004 r8:c03874bc r7:c035ed98 r6:c036e800 r5:00000013 r4:c1825b00 [] (setup_irq) from [] (tcb_clksrc_init+0x284/0x31c) r6:c035ed10 r5:00000000 r4:c18c6080 r3:c036e760 [] (tcb_clksrc_init) from [] (do_one_initcall+0x100/0x1b4) r10:00000000 r9:00000000 r8:c182a020 r7:c035b9c0 r6:c0348884 r5:c035b9c0 r4:c18e67e0 [] (do_one_initcall) from [] (kernel_init_freeable+0x15c/0x224) r9:00000030 r8:c0373440 r7:c0373440 r6:c0350a94 r5:c034dac8 r4:00000003 [] (kernel_init_freeable) from [] (kernel_init+0x10/0xec) r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:c0272fc8 r4:00000000 [] (kernel_init) from [] (ret_from_fork+0x14/0x24) r4:00000000 r3:c182a000 Code: bad PC value ---[ end trace 5b30f0017e282e47 ]--- Kernel panic - not syncing: Fatal exception in interrupt Signed-off-by: Gaël PORTAY Acked-by: Boris Brezillon Acked-by: Daniel Lezcano Signed-off-by: Nicolas Ferre --- drivers/misc/atmel_tclib.c | 16 ++++++++++++++++ include/linux/atmel_tc.h | 5 +++++ 2 files changed, 21 insertions(+) (limited to 'include/linux') diff --git a/drivers/misc/atmel_tclib.c b/drivers/misc/atmel_tclib.c index d505d1e0857b..0ca05c3ec8d6 100644 --- a/drivers/misc/atmel_tclib.c +++ b/drivers/misc/atmel_tclib.c @@ -109,6 +109,7 @@ static int __init tc_probe(struct platform_device *pdev) struct clk *clk; int irq; struct resource *r; + unsigned int i; irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -157,18 +158,33 @@ static int __init tc_probe(struct platform_device *pdev) if (tc->irq[2] < 0) tc->irq[2] = irq; + for (i = 0; i < 3; i++) + writel(ATMEL_TC_ALL_IRQ, tc->regs + ATMEL_TC_REG(i, IDR)); + spin_lock(&tc_list_lock); list_add_tail(&tc->node, &tc_list); spin_unlock(&tc_list_lock); + platform_set_drvdata(pdev, tc); + return 0; } +static void tc_shutdown(struct platform_device *pdev) +{ + int i; + struct atmel_tc *tc = platform_get_drvdata(pdev); + + for (i = 0; i < 3; i++) + writel(ATMEL_TC_ALL_IRQ, tc->regs + ATMEL_TC_REG(i, IDR)); +} + static struct platform_driver tc_driver = { .driver = { .name = "atmel_tcb", .of_match_table = of_match_ptr(atmel_tcb_dt_ids), }, + .shutdown = tc_shutdown, }; static int __init tc_init(void) diff --git a/include/linux/atmel_tc.h b/include/linux/atmel_tc.h index d8aa88461c64..b87c1c7c242a 100644 --- a/include/linux/atmel_tc.h +++ b/include/linux/atmel_tc.h @@ -260,5 +260,10 @@ extern const u8 atmel_tc_divisors[5]; #define ATMEL_TC_LDRAS (1 << 5) /* RA loading */ #define ATMEL_TC_LDRBS (1 << 6) /* RB loading */ #define ATMEL_TC_ETRGS (1 << 7) /* external trigger */ +#define ATMEL_TC_ALL_IRQ (ATMEL_TC_COVFS | ATMEL_TC_LOVRS | \ + ATMEL_TC_CPAS | ATMEL_TC_CPBS | \ + ATMEL_TC_CPCS | ATMEL_TC_LDRAS | \ + ATMEL_TC_LDRBS | ATMEL_TC_ETRGS) \ + /* all IRQs */ #endif -- cgit v1.2.3 From 175655bd79b815f2b2035f3b44117c60be90e1d4 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 16 Sep 2014 17:36:28 -0700 Subject: ARM: OMAP: Remove unused pieces of legacy DMA API We're moving to the dmaengine API, so let's remove the unused pieces of the omap legacy DMA code to make sure we don't get any new users for these: omap_set_dma_color_mode omap_set_dma_src_index omap_set_dma_dest_index omap_dma_unlink_lch omap_clear_dma omap_dma_running omap_dma_set_prio_lch omap_set_dma_dst_endian_type omap_set_dma_src_endian_type omap_get_dma_index omap_dma_disable_irq omap_request_dma_chain omap_free_dma_chain omap_dma_chain_a_transfer omap_start_dma_chain_transfers omap_stop_dma_chain_transfers omap_get_dma_chain_index omap_get_dma_chain_dst_pos omap_get_dma_chain_src_pos omap_modify_dma_chain_params omap_dma_chain_status Cc: Russell King Signed-off-by: Tony Lindgren --- arch/arm/plat-omap/dma.c | 737 +---------------------------------------------- include/linux/omap-dma.h | 37 --- 2 files changed, 6 insertions(+), 768 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c index c2baa8ede543..24770e5a5081 100644 --- a/arch/arm/plat-omap/dma.c +++ b/arch/arm/plat-omap/dma.c @@ -64,7 +64,9 @@ enum { DMA_CHAIN_STARTED, DMA_CHAIN_NOTSTARTED }; static struct omap_system_dma_plat_info *p; static struct omap_dma_dev_attr *d; - +static void omap_clear_dma(int lch); +static int omap_dma_set_prio_lch(int lch, unsigned char read_prio, + unsigned char write_prio); static int enable_1510_mode; static u32 errata; @@ -284,66 +286,6 @@ void omap_set_dma_transfer_params(int lch, int data_type, int elem_count, } 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()); - - if (dma_omap1()) { - u16 w; - - w = p->dma_read(CCR2, lch); - w &= ~0x03; - - switch (mode) { - case OMAP_DMA_CONSTANT_FILL: - w |= 0x01; - break; - case OMAP_DMA_TRANSPARENT_COPY: - w |= 0x02; - break; - case OMAP_DMA_COLOR_DIS: - break; - default: - BUG(); - } - p->dma_write(w, CCR2, lch); - - w = p->dma_read(LCH_CTRL, lch); - w &= ~0x0f; - /* Default is channel type 2D */ - if (mode) { - p->dma_write(color, COLOR, lch); - w |= 1; /* Channel type G */ - } - p->dma_write(w, LCH_CTRL, lch); - } - - if (dma_omap2plus()) { - u32 val; - - val = p->dma_read(CCR, lch); - val &= ~((1 << 17) | (1 << 16)); - - switch (mode) { - case OMAP_DMA_CONSTANT_FILL: - val |= 1 << 16; - break; - case OMAP_DMA_TRANSPARENT_COPY: - val |= 1 << 17; - break; - case OMAP_DMA_COLOR_DIS: - break; - default: - BUG(); - } - p->dma_write(val, CCR, lch); - - color &= 0xffffff; - p->dma_write(color, COLOR, lch); - } -} -EXPORT_SYMBOL(omap_set_dma_color_mode); - void omap_set_dma_write_mode(int lch, enum omap_dma_write_mode mode) { if (dma_omap2plus()) { @@ -417,16 +359,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 (dma_omap2plus()) - return; - - p->dma_write(eidx, CSEI, lch); - p->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; @@ -510,16 +442,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 (dma_omap2plus()) - return; - - p->dma_write(eidx, CDEI, lch); - p->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; @@ -843,7 +765,7 @@ EXPORT_SYMBOL(omap_dma_set_global_params); * Both of the above can be set with one of the following values : * DMA_CH_PRIO_HIGH/DMA_CH_PRIO_LOW */ -int +static int omap_dma_set_prio_lch(int lch, unsigned char read_prio, unsigned char write_prio) { @@ -864,13 +786,13 @@ omap_dma_set_prio_lch(int lch, unsigned char read_prio, return 0; } -EXPORT_SYMBOL(omap_dma_set_prio_lch); + /* * Clears any DMA state so the DMA engine is ready to restart with new buffers * through omap_start_dma(). Any buffers in flight are discarded. */ -void omap_clear_dma(int lch) +static void omap_clear_dma(int lch) { unsigned long flags; @@ -878,7 +800,6 @@ void omap_clear_dma(int lch) p->clear_dma(lch); local_irq_restore(flags); } -EXPORT_SYMBOL(omap_clear_dma); void omap_start_dma(int lch) { @@ -1167,652 +1088,6 @@ void omap_dma_link_lch(int lch_head, int lch_queue) } EXPORT_SYMBOL(omap_dma_link_lch); -/* - * Once the DMA queue is stopped, we can destroy it. - */ -void omap_dma_unlink_lch(int lch_head, int lch_queue) -{ - if (omap_dma_in_1510_mode()) { - if (lch_head == lch_queue) { - p->dma_write(p->dma_read(CCR, lch_head) & ~(3 << 8), - CCR, lch_head); - return; - } - printk(KERN_ERR "DMA linking is not supported in 1510 mode\n"); - BUG(); - return; - } - - if (dma_chan[lch_head].next_lch != lch_queue || - dma_chan[lch_head].next_lch == -1) { - pr_err("omap_dma: trying to unlink non linked channels\n"); - dump_stack(); - } - - if ((dma_chan[lch_head].flags & OMAP_DMA_ACTIVE) || - (dma_chan[lch_queue].flags & OMAP_DMA_ACTIVE)) { - pr_err("omap_dma: You need to stop the DMA channels before unlinking\n"); - dump_stack(); - } - - dma_chan[lch_head].next_lch = -1; -} -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 = p->dma_read(CLNK_CTRL, lch_head); - l &= ~(0x1f); - l |= lch_queue; - p->dma_write(l, CLNK_CTRL, lch_head); - - l = p->dma_read(CLNK_CTRL, lch_queue); - l &= ~(0x1f); - l |= (dma_chan[lch_queue].next_linked_ch); - p->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) - p->dma_write(src_start, CSSA, lch); - if (dest_start != 0) - p->dma_write(dest_start, CDSA, lch); - - /* Write the buffer size */ - p->dma_write(elem_count, CEN, lch); - p->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) & p->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 = p->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); - p->dma_write(l, CCR, lch); - } else - start_dma = 0; - } else { - if (0 == (l & (1 << 7))) - p->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 = p->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); - p->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 sys_cf = 0; - - /* 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 (IS_DMA_ERRATA(DMA_ERRATA_i88)) { - sys_cf = p->dma_read(OCP_SYSCONFIG, 0); - l = sys_cf; - /* Middle mode reg set no Standby */ - l &= ~((1 << 12)|(1 << 13)); - p->dma_write(l, OCP_SYSCONFIG, 0); - } - - for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) { - - /* Stop the Channel transmission */ - l = p->dma_read(CCR, channels[i]); - l &= ~(1 << 7); - p->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 (IS_DMA_ERRATA(DMA_ERRATA_i88)) - p->dma_write(sys_cf, OCP_SYSCONFIG, 0); - - 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 = p->dma_read(CCEN, lch); - *fi = p->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 p->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 p->dma_read(CSAC, lch); -} -EXPORT_SYMBOL(omap_get_dma_chain_src_pos); -#endif /* ifndef CONFIG_ARCH_OMAP1 */ - /*----------------------------------------------------------------------------*/ #ifdef CONFIG_ARCH_OMAP1 diff --git a/include/linux/omap-dma.h b/include/linux/omap-dma.h index 6f06f8bc612c..e5a70132a240 100644 --- a/include/linux/omap-dma.h +++ b/include/linux/omap-dma.h @@ -306,15 +306,12 @@ extern void omap_set_dma_transfer_params(int lch, int data_type, int elem_count, int frame_count, int sync_mode, int dma_trigger, int src_or_dst_synch); -extern void omap_set_dma_color_mode(int lch, enum omap_dma_color_mode mode, - u32 color); extern void omap_set_dma_write_mode(int lch, enum omap_dma_write_mode mode); extern void omap_set_dma_channel_mode(int lch, enum omap_dma_channel_mode mode); extern void omap_set_dma_src_params(int lch, int src_port, int src_amode, unsigned long src_start, int src_ei, int src_fi); -extern void omap_set_dma_src_index(int lch, int eidx, int fidx); extern void omap_set_dma_src_data_pack(int lch, int enable); extern void omap_set_dma_src_burst_mode(int lch, enum omap_dma_burst_mode burst_mode); @@ -322,7 +319,6 @@ extern void omap_set_dma_src_burst_mode(int lch, extern void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode, unsigned long dest_start, int dst_ei, int dst_fi); -extern void omap_set_dma_dest_index(int lch, int eidx, int fidx); extern void omap_set_dma_dest_data_pack(int lch, int enable); extern void omap_set_dma_dest_burst_mode(int lch, enum omap_dma_burst_mode burst_mode); @@ -331,52 +327,19 @@ extern void omap_set_dma_params(int lch, struct omap_dma_channel_params *params); extern void omap_dma_link_lch(int lch_head, int lch_queue); -extern void omap_dma_unlink_lch(int lch_head, int lch_queue); extern int omap_set_dma_callback(int lch, void (*callback)(int lch, u16 ch_status, void *data), void *data); extern dma_addr_t omap_get_dma_src_pos(int lch); extern dma_addr_t omap_get_dma_dst_pos(int lch); -extern void omap_clear_dma(int lch); extern int omap_get_dma_active_status(int lch); extern int omap_dma_running(void); extern void omap_dma_set_global_params(int arb_rate, int max_fifo_depth, int tparams); -extern int omap_dma_set_prio_lch(int lch, unsigned char read_prio, - unsigned char write_prio); -extern void omap_set_dma_dst_endian_type(int lch, enum end_type etype); -extern void omap_set_dma_src_endian_type(int lch, enum end_type etype); -extern int omap_get_dma_index(int lch, int *ei, int *fi); - void omap_dma_global_context_save(void); 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) && IS_ENABLED(CONFIG_FB_OMAP) #include #else -- cgit v1.2.3