* [PATCH V2] ARM: NUC900: add GDMA driver support for nuc900 platform
@ 2009-11-26 10:42 Hu Ruihuan
2009-11-29 12:03 ` Wan ZongShun
2009-11-29 12:16 ` Russell King - ARM Linux
0 siblings, 2 replies; 12+ messages in thread
From: Hu Ruihuan @ 2009-11-26 10:42 UTC (permalink / raw)
To: linux-arm-kernel
Hi, Wan ZongShun
This is the gdma driver for NUC900 platform, it is based apon the
code several days ago I have submited.
Signed-off-by: Hu Ruihuan <specter118@163.com>
----
iff -Naur linux-2.6.32-rc8/arch/arm/mach-w90x900/dma.c
linux-2.6.32-rc8-modify/arch/arm/mach-w90x900/dma.c
--- linux-2.6.32-rc8/arch/arm/mach-w90x900/dma.c 1970-01-01
08:00:00.000000000 +0800
+++ linux-2.6.32-rc8-modify/arch/arm/mach-w90x900/dma.c 2010-11-20
00:06:44.000000000 +0800
@@ -0,0 +1,318 @@
+/*
+ * arch/arm/mach-w90p910/dma.c
+ *
+ * Support functions for the w90p910 internal DMA channels.
+ *
+ * Wan Zongshun <mcuos.com@gmail.com>
+ * Hu Ruihuan <specter118@gmail.com>
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/atomic.h>
+#include <mach/hardware.h>
+
+#define NUC900_DMA_CHANNELS (2)
+#define DMA_BASE (W90X900_VA_GDMA)
+#define GDMA_INTCS (DMA_BASE + 0xa0)
+
+#define GDMA_GDGA (0x1c)
+#define TC0F (0x01 << 8)
+#define TC1F (0x01 << 10)
+#define ENTC0F (0x01)
+#define ENTC1F (0x01 << 2)
+
+#define GDMAEN (0x01)
+#define GDMAMS (0x03 << 2)
+#define DADIR (0x01 << 4)
+#define SADIR (0x01 << 5)
+#define DAFIX (0x01 << 6)
+#define SAFIX (0x01 << 7)
+#define D_INTS (0x01 << 10)
+#define TWSMASK (0x03 << 12)
+#define TWS (0x02 << 12)
+
+#define RUN (0x01 << 3)
+#define NON_DSCRIP (0x01 << 2)
+#define ORDEN (0x01 << 1)
+#define RESET (0x01)
+#define DSCRIPMASK (0x0F)
+#define CMDINFOMASK (0x03FFF)
+
+#define COUTN_TRANSFER (0x1000)/*count[13:0]=1024*/
+#define CHANNELINERV (0x20)
+
+typedef void (*dma_callback_t)(void *data);
+
+struct nuc900_dma_descp {
+ unsigned int next_descp;
+ unsigned int srcaddr;
+ unsigned int dstaddr;
+ unsigned int commandinfo;
+};
+
+struct nuc900_dma_t {
+ const char *device; /* this channel device, 0 if unused */
+ dma_callback_t callback; /* to call when DMA completes */
+ void __iomem *dma_reg;
+ void *data; /* ... with private data ptr */
+ struct nuc900_dma_descp *descp;
+};
+
+static struct nuc900_dma_t dma_chan[nuc900_DMA_CHANNELS];
+static struct nuc900_dma_t *dmachan0, *dmachan1;
+static atomic_t shared_irq_using;
+static spinlock_t dma_chan_lock;
+
+
+
+static int register_dmachan_fromname(const char *dev_name,
+ struct nuc900_dma_t *dma, unsigned int *irq_request)
+{
+ int err;
+
+ BUG_ON(!dev_name);
+
+ err = 0;
+
+ if (!(dmachan0->device) && !(dmachan1->device)) {
+ dma = dmachan0;
+ *irq_request = 1;
+ } else if ((dmachan0->device) && !(dmachan1->device)) {
+ if (strcmp(dmachan0->device, dev_name) == 0)
+ err = -EBUSY;
+ else
+ dma = dmachan1;
+ } else if ((dmachan1->device) && !(dmachan0->device)) {
+ if (strcmp(dmachan1->device, dev_name) == 0)
+ err = -EBUSY;
+ else
+ dma = dmachan0;
+ } else
+ err = -EBUSY;
+
+ return err;
+
+}
+
+static void match_dmachan_fromname(const char *dev_name,
+ struct nuc900_dma_t *dma)
+{
+ BUG_ON(!dev_name);
+
+ if (dmachan1->device) {
+ if (strcmp(dmachan1->device, dev_name) == 0)
+ dma = dmachan1;
+ } else if (dmachan0->device) {
+ if (strcmp(dmachan0->device, dev_name) == 0)
+ dma = dmachan0;
+ }
+}
+
+static irqreturn_t dma_irq_handler(int irq, void *dev_id)
+{
+ int status;
+ local_irq_disable();
+ status = __raw_readl(GDMA_GDGA);
+
+ if (status & (TC0F)) {
+ __raw_writel((~TC0F), GDMA_GDGA);
+ local_irq_enable();
+ dmachan0->callback(dmachan0->data);
+ }
+
+ if (status & (TC1F)) {
+ __raw_writel((~TC1F), GDMA_GDGA);
+ local_irq_enable();
+ dmachan1->callback(dmachan1->data);
+ }
+
+ return IRQ_HANDLED;
+}
+
+int nuc900_request_dma(const char *dev_name, dma_callback_t callback,
+ void *data)
+{
+ struct nuc900_dma_t *dma = NULL;
+ int err, irq_request;
+
+ err = 0;
+ irq_request = 0;
+ spin_lock(&dma_chan_lock);
+ err = register_dmachan_fromname(dev_name, dma, &irq_request);
+
+ if (err < 0) {
+ spin_unlock(&dma_chan_lock);
+ return err;
+ }
+
+ dma->device = dev_name;
+
+ if (irq_request) {
+ err = request_irq(IRQ_GDMAGROUP, dma_irq_handler, IRQF_SHARED|
+ IRQF_DISABLED, NULL, NULL);
+ if (err) {
+ dma->device = NULL;
+ spin_unlock(&dma_chan_lock);
+ return err;
+ }
+ }
+
+
+ atomic_inc( &shared_irq_using);
+ dma->callback = callback;
+ dma->data = data;
+ spin_unlock(&dma_chan_lock);
+ return 0;
+}
+EXPORT_SYMBOL(nuc900_request_dma);
+
+int nuc900_free_dma(const char *dev_name)
+{
+ struct nuc900_dma_t *dma = NULL;
+
+ BUG_ON(!dev_name);
+ WARN_ON(shared_irq_using.counter == 0);
+ spin_lock(&dma_chan_lock);
+ match_dmachan_fromname(dev_name, dma);
+
+ if (!dma)
+ return -ENXIO;
+
+ dma->device = NULL;
+ spin_unlock(&dma_chan_lock);
+
+ atomic_dec(&shared_irq_using);
+
+
+ if (!(shared_irq_using.counter))
+ free_irq(IRQ_GDMAGROUP, dma);
+
+ kzfree(dma->descp);
+ return 0;
+}
+EXPORT_SYMBOL(nuc900_free_dma);
+
+static unsigned int set_start_cmd(unsigned int start)
+{
+ unsigned int dscp_cmd;
+
+ dscp_cmd = 0;
+
+ if (start) {
+ dscp_cmd = (ORDEN|RUN);
+ dscp_cmd &= ~(NON_DSCRIP|RESET);
+ } else {
+ dscp_cmd = (NON_DSCRIP);
+ }
+ dscp_cmd &= DSCRIPMASK;
+
+ return dscp_cmd;
+}
+
+static unsigned int set_cmd_info(unsigned int countsize)
+{
+ unsigned int cmd_info;
+
+ cmd_info = (countsize >> 2);
+
+ cmd_info = (((cmd_info & CMDINFOMASK) << 18)|GDMAEN|D_INTS);
+ cmd_info &= ~(GDMAMS|DADIR|SADIR|DAFIX|SAFIX|TWSMASK);
+ cmd_info |= TWS;
+
+ return cmd_info;
+
+}
+
+static unsigned int get_descplist_num(unsigned int size, unsigned int *leave)
+{
+ unsigned int dscp_num, other_num;
+
+ dscp_num = 0;
+ other_num = 0;
+
+ dscp_num = size / COUTN_TRANSFER;
+ other_num = size % COUTN_TRANSFER;
+
+ if (other_num)
+ dscp_num += 1;
+
+ *leave = other_num;
+
+ return dscp_num;
+
+}
+
+int nuc900_start_dma(const char *dev_name, dma_addr_t dma_src,
+ dma_addr_t dma_dst, unsigned int size)
+{
+ struct nuc900_dma_descp *descp = NULL;
+ struct nuc900_dma_t *dma = NULL;
+ unsigned int i, dscp_num, other_num, val;
+
+ BUG_ON((!size|!dev_name));
+
+ match_dmachan_fromname(dev_name, dma);
+
+ if (!dma)
+ return -ENXIO;
+
+ dscp_num = get_descplist_num(size, &other_num);
+
+ descp = kzalloc(dscp_num * sizeof(struct nuc900_dma_t), GFP_KERNEL);
+
+ if (!descp)
+ return -ENOMEM;
+
+ dma->descp = descp;
+
+ for (i = 0; i < dscp_num; i++) {
+ descp->next_descp = set_start_cmd(1);
+ descp->srcaddr = dma_src + COUTN_TRANSFER * i;
+ descp->dstaddr = dma_dst + COUTN_TRANSFER * i;
+ descp->commandinfo = set_cmd_info(COUTN_TRANSFER);
+ descp++;
+ }
+ descp--;
+ descp->next_descp = set_start_cmd(0);
+ descp->commandinfo = set_cmd_info(other_num);
+
+
+ val = set_start_cmd(1);
+ val |= (unsigned int)(dma->descp);
+ local_irq_disable();
+ __raw_writel(val, dma->dma_reg + GDMA_GDGA);
+
+ val = __raw_readl(GDMA_INTCS);
+ val |= (ENTC0F|ENTC1F);
+ __raw_writel(val, GDMA_INTCS);
+ local_irq_enable();
+
+ return 0;
+}
+EXPORT_SYMBOL(nuc900_start_dma);
+
+void __init nuc900_init_dma(void)
+{
+ unsigned i;
+ struct nuc900_dma_t *dma_channel;
+ spin_lock_init(&dma_chan_lock);
+ for (i = 0; i < NUC900_DMA_CHANNELS; i++) {
+ dma_channel = &dma_chan[i];
+ dma_channel->dma_reg = DMA_BASE + i * CHANNELINERV;
+ dma_channel->device = NULL;
+ }
+
+ dmachan0 = &dma_chan[0];
+ dmachan1 = &dma_chan[1];
+}
diff -Naur linux-2.6.32-rc8/arch/arm/mach-w90x900/Makefile
linux-2.6.32-rc8-modify/arch/arm/mach-w90x900/Makefile
--- linux-2.6.32-rc8/arch/arm/mach-w90x900/Makefile 2009-11-20
06:32:38.000000000 +0800
+++ linux-2.6.32-rc8-modify/arch/arm/mach-w90x900/Makefile 2010-11-20
00:02:07.000000000 +0800
@@ -5,7 +5,7 @@
# Object file lists.
obj-y := irq.o time.o mfp.o gpio.o clock.o
-obj-y += clksel.o dev.o cpu.o
+obj-y += clksel.o dev.o cpu.o dma.o
# W90X900 CPU support files
obj-$(CONFIG_CPU_W90P910) += nuc910.o
----
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH V2] ARM: NUC900: add GDMA driver support for nuc900 platform
2009-11-26 10:42 [PATCH V2] ARM: NUC900: add GDMA driver support for nuc900 platform Hu Ruihuan
@ 2009-11-29 12:03 ` Wan ZongShun
2009-11-29 12:16 ` Russell King - ARM Linux
1 sibling, 0 replies; 12+ messages in thread
From: Wan ZongShun @ 2009-11-29 12:03 UTC (permalink / raw)
To: linux-arm-kernel
Hi, Hu Ruihuan
Thanks a lot for your patch, regarding this dma driver, it looks good to me,
but for submitting this patch, there are two points you should take care as following:
(1)Please use 'scripts/checkpatch.pl' to check your patch.
(2)Before you submit some patches, Please read the Documentation/email-clients.txt
and check your email client.
> Hi, Wan ZongShun
> This is the gdma driver for NUC900 platform, it is based apon the
> code several days ago I have submited.
>
> Signed-off-by: Hu Ruihuan <specter118@163.com>
> ----
> iff -Naur linux-2.6.32-rc8/arch/arm/mach-w90x900/dma.c
> linux-2.6.32-rc8-modify/arch/arm/mach-w90x900/dma.c
> --- linux-2.6.32-rc8/arch/arm/mach-w90x900/dma.c 1970-01-01
> 08:00:00.000000000 +0800
> +++ linux-2.6.32-rc8-modify/arch/arm/mach-w90x900/dma.c 2010-11-20
> 00:06:44.000000000 +0800
> @@ -0,0 +1,318 @@
> +/*
> + * arch/arm/mach-w90p910/dma.c
> + *
> + * Support functions for the w90p910 internal DMA channels.
> + *
> + * Wan Zongshun <mcuos.com@gmail.com>
> + * Hu Ruihuan <specter118@gmail.com>
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/init.h>
> +#include <linux/spinlock.h>
> +#include <linux/errno.h>
> +#include <linux/io.h>
> +
> +#include <asm/system.h>
> +#include <asm/irq.h>
> +#include <asm/atomic.h>
> +#include <mach/hardware.h>
> +
> +#define NUC900_DMA_CHANNELS (2)
> +#define DMA_BASE (W90X900_VA_GDMA)
> +#define GDMA_INTCS (DMA_BASE + 0xa0)
> +
> +#define GDMA_GDGA (0x1c)
> +#define TC0F (0x01 << 8)
> +#define TC1F (0x01 << 10)
> +#define ENTC0F (0x01)
> +#define ENTC1F (0x01 << 2)
> +
> +#define GDMAEN (0x01)
> +#define GDMAMS (0x03 << 2)
> +#define DADIR (0x01 << 4)
> +#define SADIR (0x01 << 5)
> +#define DAFIX (0x01 << 6)
> +#define SAFIX (0x01 << 7)
> +#define D_INTS (0x01 << 10)
> +#define TWSMASK (0x03 << 12)
> +#define TWS (0x02 << 12)
> +
> +#define RUN (0x01 << 3)
> +#define NON_DSCRIP (0x01 << 2)
> +#define ORDEN (0x01 << 1)
> +#define RESET (0x01)
> +#define DSCRIPMASK (0x0F)
> +#define CMDINFOMASK (0x03FFF)
> +
> +#define COUTN_TRANSFER (0x1000)/*count[13:0]=1024*/
> +#define CHANNELINERV (0x20)
> +
> +typedef void (*dma_callback_t)(void *data);
> +
> +struct nuc900_dma_descp {
> + unsigned int next_descp;
> + unsigned int srcaddr;
> + unsigned int dstaddr;
> + unsigned int commandinfo;
> +};
> +
> +struct nuc900_dma_t {
> + const char *device; /* this channel device, 0 if unused */
> + dma_callback_t callback; /* to call when DMA completes */
> + void __iomem *dma_reg;
> + void *data; /* ... with private data ptr */
> + struct nuc900_dma_descp *descp;
> +};
> +
> +static struct nuc900_dma_t dma_chan[nuc900_DMA_CHANNELS];
> +static struct nuc900_dma_t *dmachan0, *dmachan1;
> +static atomic_t shared_irq_using;
> +static spinlock_t dma_chan_lock;
> +
> +
> +
> +static int register_dmachan_fromname(const char *dev_name,
> + struct nuc900_dma_t *dma, unsigned int *irq_request)
> +{
> + int err;
> +
> + BUG_ON(!dev_name);
> +
> + err = 0;
> +
> + if (!(dmachan0->device) && !(dmachan1->device)) {
> + dma = dmachan0;
> + *irq_request = 1;
> + } else if ((dmachan0->device) && !(dmachan1->device)) {
> + if (strcmp(dmachan0->device, dev_name) == 0)
> + err = -EBUSY;
> + else
> + dma = dmachan1;
> + } else if ((dmachan1->device) && !(dmachan0->device)) {
> + if (strcmp(dmachan1->device, dev_name) == 0)
> + err = -EBUSY;
> + else
> + dma = dmachan0;
> + } else
> + err = -EBUSY;
> +
> + return err;
> +
> +}
> +
> +static void match_dmachan_fromname(const char *dev_name,
> + struct nuc900_dma_t *dma)
> +{
> + BUG_ON(!dev_name);
> +
> + if (dmachan1->device) {
> + if (strcmp(dmachan1->device, dev_name) == 0)
> + dma = dmachan1;
> + } else if (dmachan0->device) {
> + if (strcmp(dmachan0->device, dev_name) == 0)
> + dma = dmachan0;
> + }
> +}
> +
> +static irqreturn_t dma_irq_handler(int irq, void *dev_id)
> +{
> + int status;
> + local_irq_disable();
> + status = __raw_readl(GDMA_GDGA);
> +
> + if (status & (TC0F)) {
> + __raw_writel((~TC0F), GDMA_GDGA);
> + local_irq_enable();
> + dmachan0->callback(dmachan0->data);
> + }
> +
> + if (status & (TC1F)) {
> + __raw_writel((~TC1F), GDMA_GDGA);
> + local_irq_enable();
> + dmachan1->callback(dmachan1->data);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +int nuc900_request_dma(const char *dev_name, dma_callback_t callback,
> + void *data)
> +{
> + struct nuc900_dma_t *dma = NULL;
> + int err, irq_request;
> +
> + err = 0;
> + irq_request = 0;
> + spin_lock(&dma_chan_lock);
> + err = register_dmachan_fromname(dev_name, dma, &irq_request);
> +
> + if (err < 0) {
> + spin_unlock(&dma_chan_lock);
> + return err;
> + }
> +
> + dma->device = dev_name;
> +
> + if (irq_request) {
> + err = request_irq(IRQ_GDMAGROUP, dma_irq_handler, IRQF_SHARED|
> + IRQF_DISABLED, NULL, NULL);
> + if (err) {
> + dma->device = NULL;
> + spin_unlock(&dma_chan_lock);
> + return err;
> + }
> + }
> +
> +
> + atomic_inc( &shared_irq_using);
> + dma->callback = callback;
> + dma->data = data;
> + spin_unlock(&dma_chan_lock);
> + return 0;
> +}
> +EXPORT_SYMBOL(nuc900_request_dma);
> +
> +int nuc900_free_dma(const char *dev_name)
> +{
> + struct nuc900_dma_t *dma = NULL;
> +
> + BUG_ON(!dev_name);
> + WARN_ON(shared_irq_using.counter == 0);
> + spin_lock(&dma_chan_lock);
> + match_dmachan_fromname(dev_name, dma);
> +
> + if (!dma)
> + return -ENXIO;
> +
> + dma->device = NULL;
> + spin_unlock(&dma_chan_lock);
> +
> + atomic_dec(&shared_irq_using);
> +
> +
> + if (!(shared_irq_using.counter))
> + free_irq(IRQ_GDMAGROUP, dma);
> +
> + kzfree(dma->descp);
> + return 0;
> +}
> +EXPORT_SYMBOL(nuc900_free_dma);
> +
> +static unsigned int set_start_cmd(unsigned int start)
> +{
> + unsigned int dscp_cmd;
> +
> + dscp_cmd = 0;
> +
> + if (start) {
> + dscp_cmd = (ORDEN|RUN);
> + dscp_cmd &= ~(NON_DSCRIP|RESET);
> + } else {
> + dscp_cmd = (NON_DSCRIP);
> + }
> + dscp_cmd &= DSCRIPMASK;
> +
> + return dscp_cmd;
> +}
> +
> +static unsigned int set_cmd_info(unsigned int countsize)
> +{
> + unsigned int cmd_info;
> +
> + cmd_info = (countsize >> 2);
> +
> + cmd_info = (((cmd_info & CMDINFOMASK) << 18)|GDMAEN|D_INTS);
> + cmd_info &= ~(GDMAMS|DADIR|SADIR|DAFIX|SAFIX|TWSMASK);
> + cmd_info |= TWS;
> +
> + return cmd_info;
> +
> +}
> +
> +static unsigned int get_descplist_num(unsigned int size, unsigned int *leave)
> +{
> + unsigned int dscp_num, other_num;
> +
> + dscp_num = 0;
> + other_num = 0;
> +
> + dscp_num = size / COUTN_TRANSFER;
> + other_num = size % COUTN_TRANSFER;
> +
> + if (other_num)
> + dscp_num += 1;
> +
> + *leave = other_num;
> +
> + return dscp_num;
> +
> +}
> +
> +int nuc900_start_dma(const char *dev_name, dma_addr_t dma_src,
> + dma_addr_t dma_dst, unsigned int size)
> +{
> + struct nuc900_dma_descp *descp = NULL;
> + struct nuc900_dma_t *dma = NULL;
> + unsigned int i, dscp_num, other_num, val;
> +
> + BUG_ON((!size|!dev_name));
> +
> + match_dmachan_fromname(dev_name, dma);
> +
> + if (!dma)
> + return -ENXIO;
> +
> + dscp_num = get_descplist_num(size, &other_num);
> +
> + descp = kzalloc(dscp_num * sizeof(struct nuc900_dma_t), GFP_KERNEL);
> +
> + if (!descp)
> + return -ENOMEM;
> +
> + dma->descp = descp;
> +
> + for (i = 0; i < dscp_num; i++) {
> + descp->next_descp = set_start_cmd(1);
> + descp->srcaddr = dma_src + COUTN_TRANSFER * i;
> + descp->dstaddr = dma_dst + COUTN_TRANSFER * i;
> + descp->commandinfo = set_cmd_info(COUTN_TRANSFER);
> + descp++;
> + }
> + descp--;
> + descp->next_descp = set_start_cmd(0);
> + descp->commandinfo = set_cmd_info(other_num);
> +
> +
> + val = set_start_cmd(1);
> + val |= (unsigned int)(dma->descp);
> + local_irq_disable();
> + __raw_writel(val, dma->dma_reg + GDMA_GDGA);
> +
> + val = __raw_readl(GDMA_INTCS);
> + val |= (ENTC0F|ENTC1F);
> + __raw_writel(val, GDMA_INTCS);
> + local_irq_enable();
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(nuc900_start_dma);
> +
> +void __init nuc900_init_dma(void)
> +{
> + unsigned i;
> + struct nuc900_dma_t *dma_channel;
> + spin_lock_init(&dma_chan_lock);
> + for (i = 0; i < NUC900_DMA_CHANNELS; i++) {
> + dma_channel = &dma_chan[i];
> + dma_channel->dma_reg = DMA_BASE + i * CHANNELINERV;
> + dma_channel->device = NULL;
> + }
> +
> + dmachan0 = &dma_chan[0];
> + dmachan1 = &dma_chan[1];
> +}
> diff -Naur linux-2.6.32-rc8/arch/arm/mach-w90x900/Makefile
> linux-2.6.32-rc8-modify/arch/arm/mach-w90x900/Makefile
> --- linux-2.6.32-rc8/arch/arm/mach-w90x900/Makefile 2009-11-20
> 06:32:38.000000000 +0800
> +++ linux-2.6.32-rc8-modify/arch/arm/mach-w90x900/Makefile 2010-11-20
> 00:02:07.000000000 +0800
> @@ -5,7 +5,7 @@
> # Object file lists.
>
> obj-y := irq.o time.o mfp.o gpio.o clock.o
> -obj-y += clksel.o dev.o cpu.o
> +obj-y += clksel.o dev.o cpu.o dma.o
> # W90X900 CPU support files
>
> obj-$(CONFIG_CPU_W90P910) += nuc910.o
> ----
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH V2] ARM: NUC900: add GDMA driver support for nuc900 platform
2009-11-26 10:42 [PATCH V2] ARM: NUC900: add GDMA driver support for nuc900 platform Hu Ruihuan
2009-11-29 12:03 ` Wan ZongShun
@ 2009-11-29 12:16 ` Russell King - ARM Linux
2009-12-07 15:53 ` Hu Ruihuan
1 sibling, 1 reply; 12+ messages in thread
From: Russell King - ARM Linux @ 2009-11-29 12:16 UTC (permalink / raw)
To: linux-arm-kernel
Some comments below.
On Thu, Nov 26, 2009 at 06:42:12PM +0800, Hu Ruihuan wrote:
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/init.h>
> +#include <linux/spinlock.h>
> +#include <linux/errno.h>
> +#include <linux/io.h>
> +
> +#include <asm/system.h>
You shouldn't need asm/system.h
> +#include <asm/irq.h>
> +#include <asm/atomic.h>
> +#include <mach/hardware.h>
> +
> +#define NUC900_DMA_CHANNELS (2)
> +#define DMA_BASE (W90X900_VA_GDMA)
> +#define GDMA_INTCS (DMA_BASE + 0xa0)
> +
> +#define GDMA_GDGA (0x1c)
> +#define TC0F (0x01 << 8)
> +#define TC1F (0x01 << 10)
> +#define ENTC0F (0x01)
> +#define ENTC1F (0x01 << 2)
> +
> +#define GDMAEN (0x01)
> +#define GDMAMS (0x03 << 2)
> +#define DADIR (0x01 << 4)
> +#define SADIR (0x01 << 5)
> +#define DAFIX (0x01 << 6)
> +#define SAFIX (0x01 << 7)
> +#define D_INTS (0x01 << 10)
> +#define TWSMASK (0x03 << 12)
> +#define TWS (0x02 << 12)
> +
> +#define RUN (0x01 << 3)
> +#define NON_DSCRIP (0x01 << 2)
> +#define ORDEN (0x01 << 1)
> +#define RESET (0x01)
> +#define DSCRIPMASK (0x0F)
> +#define CMDINFOMASK (0x03FFF)
> +
> +#define COUTN_TRANSFER (0x1000)/*count[13:0]=1024*/
> +#define CHANNELINERV (0x20)
> +
> +typedef void (*dma_callback_t)(void *data);
> +
> +struct nuc900_dma_descp {
> + unsigned int next_descp;
> + unsigned int srcaddr;
> + unsigned int dstaddr;
> + unsigned int commandinfo;
> +};
> +
> +struct nuc900_dma_t {
> + const char *device; /* this channel device, 0 if unused */
> + dma_callback_t callback; /* to call when DMA completes */
> + void __iomem *dma_reg;
> + void *data; /* ... with private data ptr */
> + struct nuc900_dma_descp *descp;
> +};
> +
> +static struct nuc900_dma_t dma_chan[nuc900_DMA_CHANNELS];
> +static struct nuc900_dma_t *dmachan0, *dmachan1;
> +static atomic_t shared_irq_using;
> +static spinlock_t dma_chan_lock;
Maybe consider using DEFINE_SPINLOCK(dma_chan_lock) ?
> +
> +
> +
> +static int register_dmachan_fromname(const char *dev_name,
> + struct nuc900_dma_t *dma, unsigned int *irq_request)
> +{
> + int err;
> +
> + BUG_ON(!dev_name);
> +
> + err = 0;
> +
> + if (!(dmachan0->device) && !(dmachan1->device)) {
> + dma = dmachan0;
> + *irq_request = 1;
> + } else if ((dmachan0->device) && !(dmachan1->device)) {
> + if (strcmp(dmachan0->device, dev_name) == 0)
> + err = -EBUSY;
> + else
> + dma = dmachan1;
> + } else if ((dmachan1->device) && !(dmachan0->device)) {
> + if (strcmp(dmachan1->device, dev_name) == 0)
> + err = -EBUSY;
> + else
> + dma = dmachan0;
> + } else
> + err = -EBUSY;
> +
> + return err;
> +
> +}
Again, this initializes the local 'dma' pointer but doesn't return it.
If you want to be able to return a pointer as well as an error code,
try using linux/err.h:
struct nuc900_dma_t *register_dma_from_chan(const char *dev_name,
unsigned int *irq_request)
{
struct nuc900_dma_t *ret;
...
if (strcmp(dmachanX->device, dev_name) == 0)
ret = ERR_PTR(-EBUSY);
else
ret = dmachan1;
...
return ret;
}
and use it as:
struct nuc900_dma_t *dma;
dma = register_dma_from_chan(...);
if (IS_ERR(dma)) {
err = PTR_ERR(dma);
return err;
}
This has advantages over the 'return via pointer-to-pointer in function
arguments' in that you can't end up with errors where your pointer is
not initialized.
> +
> +static void match_dmachan_fromname(const char *dev_name,
> + struct nuc900_dma_t *dma)
> +{
> + BUG_ON(!dev_name);
> +
> + if (dmachan1->device) {
> + if (strcmp(dmachan1->device, dev_name) == 0)
> + dma = dmachan1;
> + } else if (dmachan0->device) {
> + if (strcmp(dmachan0->device, dev_name) == 0)
> + dma = dmachan0;
> + }
This code doesn't actually do anything - 'dma' is a local pointer which
you're initializing but not using.
> +}
> +
> +static irqreturn_t dma_irq_handler(int irq, void *dev_id)
> +{
> + int status;
> + local_irq_disable();
> + status = __raw_readl(GDMA_GDGA);
> +
> + if (status & (TC0F)) {
> + __raw_writel((~TC0F), GDMA_GDGA);
> + local_irq_enable();
> + dmachan0->callback(dmachan0->data);
> + }
> +
> + if (status & (TC1F)) {
> + __raw_writel((~TC1F), GDMA_GDGA);
> + local_irq_enable();
> + dmachan1->callback(dmachan1->data);
> + }
if both TC0F and TC1F are both set, you'll write ~TC0F with IRQs disabled
but ~TC1F with IRQs enabled. Wouldn't something like:
local_irq_disable();
status = __raw_readl(GDMA_GDGA) & (TC0F|TC1F);
__raw_writel(~status, GDMA_GDGA);
local_irq_enable();
if (status & TC0F)
dmachan0->callback(dmachan0->data);
if (status & TC1F)
dmachan1->callback(dmachan1->data);
be better?
> +
> + return IRQ_HANDLED;
> +}
> +
> +int nuc900_request_dma(const char *dev_name, dma_callback_t callback,
> + void *data)
> +{
> + struct nuc900_dma_t *dma = NULL;
> + int err, irq_request;
> +
> + err = 0;
> + irq_request = 0;
> + spin_lock(&dma_chan_lock);
> + err = register_dmachan_fromname(dev_name, dma, &irq_request);
> +
> + if (err < 0) {
> + spin_unlock(&dma_chan_lock);
> + return err;
> + }
> +
> + dma->device = dev_name;
> +
> + if (irq_request) {
> + err = request_irq(IRQ_GDMAGROUP, dma_irq_handler, IRQF_SHARED|
> + IRQF_DISABLED, NULL, NULL);
Not sure why you don't claim this at initialization time.
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH V2] ARM: NUC900: add GDMA driver support for nuc900 platform
2009-11-29 12:16 ` Russell King - ARM Linux
@ 2009-12-07 15:53 ` Hu Ruihuan
2009-12-07 17:18 ` Russell King - ARM Linux
0 siblings, 1 reply; 12+ messages in thread
From: Hu Ruihuan @ 2009-12-07 15:53 UTC (permalink / raw)
To: linux-arm-kernel
Hi,Russell
Thank you for your advice, it's my patch below follow your advice.
Signed-off-by: Hu Ruihuan<specter118@gmail.com>
---
diff -uNr a/arch/arm/mach-w90x900/dma.c
b/linux-2.6.32-rc8/arch/arm/mach-w90x900/dma.c
--- a/arch/arm/mach-w90x900/dma.c 2009-12-06 11:30:27.000000000 +0800
+++ b/linux-2.6.32-rc8/arch/arm/mach-w90x900/dma.c 1970-01-01
08:00:00.000000000 +0800
@@ -1,298 +0,0 @@
-/*
- * arch/arm/mach-w90p910/dma.c
- *
- * Support functions for the w90p910 internal DMA channels.
- *
- * Wan Zongshun <mcuos.com@gmail.com>
- * Hu Ruihuan <specter118@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/errno.h>
-#include <linux/io.h>
-#include <linux/err.h>
-#include <asm/system.h>
-#include <asm/irq.h>
-#include <asm/atomic.h>
-#include <mach/hardware.h>
-
-#define NUC900_DMA_CHANNELS (2)
-#define DMA_BASE (W90X900_VA_GDMA)
-#define GDMA_INTCS (DMA_BASE + 0xa0)
-
-#define GDMA_GDGA (0x1c)
-#define TC0F (0x01 << 8)
-#define TC1F (0x01 << 10)
-#define ENTC0F (0x01)
-#define ENTC1F (0x01 << 2)
-
-#define GDMAEN (0x01)
-#define GDMAMS (0x03 << 2)
-#define DADIR (0x01 << 4)
-#define SADIR (0x01 << 5)
-#define DAFIX (0x01 << 6)
-#define SAFIX (0x01 << 7)
-#define D_INTS (0x01 << 10)
-#define TWSMASK (0x03 << 12)
-#define TWS (0x02 << 12)
-
-#define RUN (0x01 << 3)
-#define NON_DSCRIP (0x01 << 2)
-#define ORDEN (0x01 << 1)
-#define RESET (0x01)
-#define DSCRIPMASK (0x0F)
-#define CMDINFOMASK (0x03FFF)
-
-#define COUTN_TRANSFER (0x1000)/*count[13:0]=1024*/
-#define CHANNELINERV (0x20)
-
-typedef void (*dma_callback_t)(void *data);
-
-struct nuc900_dma_descp {
- unsigned int next_descp;
- unsigned int srcaddr;
- unsigned int dstaddr;
- unsigned int commandinfo;
-};
-
-typedef struct nuc900_dma {
- const char *device; /* this channel device, 0 if unused */
- dma_callback_t callback; /* to call when DMA completes */
- void __iomem *dma_reg;
- void *data; /* ... with private data ptr */
- struct nuc900_dma_descp *descp;
-} *nuc900_dma_t;
-
-static struct nuc900_dma dma_chan[NUC900_DMA_CHANNELS];
-static struct nuc900_dma *dmachan0, *dmachan1;
-static atomic_t shared_irq_using;
-static DEFINE_SPINLOCK(dma_chan_lock);
-static nuc900_dma_t register_dmachan_fromname(const char *dev_name,
- unsigned int *irq_request)
-{
- nuc900_dma_t ret = NULL;
-
- BUG_ON(!dev_name);
-
-
- if (!(dmachan0->device) && !(dmachan1->device)) {
- ret = dmachan0;
- *irq_request = 1;
- } else if ((dmachan0->device) && !(dmachan1->device)) {
- if (strcmp(dmachan0->device, dev_name) == 0)
- ret = ERR_PTR(-EBUSY);
- else
- ret = dmachan1;
- } else if ((dmachan1->device) && !(dmachan0->device)) {
- if (strcmp(dmachan1->device, dev_name) == 0)
- ret = ERR_PTR(-EBUSY);
- else
- ret = dmachan0;
- } else
- ret = ERR_PTR(-EBUSY);
-
- return ret;
-
-}
-
-static void match_dmachan_fromname(const char *dev_name,
- struct nuc900_dma **dma)
-{
- BUG_ON(!dev_name);
-
- if (dmachan1->device) {
- if (strcmp(dmachan1->device, dev_name) == 0)
- *dma = dmachan1;
- } else if (dmachan0->device) {
- if (strcmp(dmachan0->device, dev_name) == 0)
- *dma = dmachan0;
- }
-}
-
-static irqreturn_t dma_irq_handler(int irq, void *dev_id)
-{
- int status;
- local_irq_disable();
- status = __raw_readl(GDMA_GDGA) & (TC0F|TC1F);
- __raw_writel(~status, GDMA_GDGA);
- local_irq_enable();
-
- if (status & TC0F)
- dmachan0->callback(dmachan0->data);
- if (status & TC1F)
- dmachan1->callback(dmachan1->data);
-
-
- return IRQ_HANDLED;
-}
-
-int nuc900_request_dma(const char *dev_name, dma_callback_t callback,
- void *data)
-{
- struct nuc900_dma *dma = NULL;
- int err, irq_request;
- err = 0;
- irq_request = 0;
- spin_lock(&dma_chan_lock);
- dma = register_dmachan_fromname(dev_name, &irq_request);
-
- if (IS_ERR(dma)) {
- err = ERR_PTR(dma);
- spin_unlock(&dma_chan_lock);
- return err;
- }
- dma->device = dev_name;
-
- atomic_inc( &shared_irq_using);
- dma->callback = callback;
- dma->data = data;
- spin_unlock(&dma_chan_lock);
- return 0;
-}
-EXPORT_SYMBOL(nuc900_request_dma);
-
-int nuc900_free_dma(const char *dev_name)
-{
- struct nuc900_dma *dma = NULL;
-
- BUG_ON(!dev_name);
- WARN_ON(shared_irq_using.counter == 0);
- spin_lock(&dma_chan_lock);
- match_dmachan_fromname(dev_name, &dma);
-
- if (!dma)
- return -ENXIO;
-
- dma->device = NULL;
- spin_unlock(&dma_chan_lock);
-
- atomic_dec(&shared_irq_using);
-
- kzfree(dma->descp);
- return 0;
-}
-EXPORT_SYMBOL(nuc900_free_dma);
-
-static unsigned int set_start_cmd(unsigned int start)
-{
- unsigned int dscp_cmd;
-
- dscp_cmd = 0;
-
- if (start) {
- dscp_cmd = (ORDEN|RUN);
- dscp_cmd &= ~(NON_DSCRIP|RESET);
- } else {
- dscp_cmd = (NON_DSCRIP);
- }
- dscp_cmd &= DSCRIPMASK;
-
- return dscp_cmd;
-}
-
-static unsigned int set_cmd_info(unsigned int countsize)
-{
- unsigned int cmd_info;
-
- cmd_info = (countsize >> 2);
-
- cmd_info = (((cmd_info & CMDINFOMASK) << 18)|GDMAEN|D_INTS);
- cmd_info &= ~(GDMAMS|DADIR|SADIR|DAFIX|SAFIX|TWSMASK);
- cmd_info |= TWS;
-
- return cmd_info;
-
-}
-
-static unsigned int get_descplist_num(unsigned int size, unsigned int *leave)
-{
- unsigned int dscp_num, other_num;
-
- dscp_num = 0;
- other_num = 0;
-
- dscp_num = size / COUTN_TRANSFER;
- other_num = size % COUTN_TRANSFER;
-
- if (other_num)
- dscp_num += 1;
-
- *leave = other_num;
-
- return dscp_num;
-
-}
-
-int nuc900_start_dma(const char *dev_name, dma_addr_t dma_src,
- dma_addr_t dma_dst, unsigned int size)
-{
- struct nuc900_dma_descp *descp = NULL;
- struct nuc900_dma *dma = NULL;
- unsigned int i, dscp_num, other_num, val;
-
- BUG_ON((!size|!dev_name));
-
- match_dmachan_fromname(dev_name, &dma);
-
- if (!dma)
- return -ENXIO;
-
- dscp_num = get_descplist_num(size, &other_num);
-
- descp = kzalloc(dscp_num * sizeof(struct nuc900_dma), GFP_KERNEL);
-
- if (!descp)
- return -ENOMEM;
-
- dma->descp = descp;
-
- for (i = 0; i < dscp_num; i++) {
- descp->next_descp = set_start_cmd(1);
- descp->srcaddr = dma_src + COUTN_TRANSFER * i;
- descp->dstaddr = dma_dst + COUTN_TRANSFER * i;
- descp->commandinfo = set_cmd_info(COUTN_TRANSFER);
- descp++;
- }
- descp--;
- descp->next_descp = set_start_cmd(0);
- descp->commandinfo = set_cmd_info(other_num);
-
-
- val = set_start_cmd(1);
- val |= (unsigned int)(dma->descp);
- local_irq_disable();
- __raw_writel(val, dma->dma_reg + GDMA_GDGA);
-
- val = __raw_readl(GDMA_INTCS);
- val |= (ENTC0F|ENTC1F);
- __raw_writel(val, GDMA_INTCS);
- local_irq_enable();
-
- return 0;
-}
-EXPORT_SYMBOL(nuc900_start_dma);
-
-void __init nuc900_init_dma(void)
-{
- unsigned i, ret;
- struct nuc900_dma *dma_channel;
- spin_lock_init(&dma_chan_lock);
- for (i = 0; i < NUC900_DMA_CHANNELS; i++) {
- dma_channel = &dma_chan[i];
- dma_channel->dma_reg = DMA_BASE + i * CHANNELINERV;
- dma_channel->device = NULL;
- }
- ret = request_irq(IRQ_GDMAGROUP, dma_irq_handler, IRQF_SHARED|
- IRQF_DISABLED, NULL, NULL);
- if (ret)
- printk("unable to register nuc900 dma interrupt: %d\n", ret);
- dmachan0 = &dma_chan[0];
- dmachan1 = &dma_chan[1];
-}
diff -uNr a/arch/arm/mach-w90x900/Makefile
b/linux-2.6.32-rc8/arch/arm/mach-w90x900/Makefile
--- a/arch/arm/mach-w90x900/Makefile 2009-12-06 12:34:47.000000000 +0800
+++ b/linux-2.6.32-rc8/arch/arm/mach-w90x900/Makefile 2009-11-20
06:32:38.000000000 +0800
@@ -5,7 +5,7 @@
# Object file lists.
obj-y := irq.o time.o mfp.o gpio.o clock.o
-obj-y += clksel.o dev.o cpu.o dma.o
+obj-y += clksel.o dev.o cpu.o
# W90X900 CPU support files
obj-$(CONFIG_CPU_W90P910) += nuc910.o
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH V2] ARM: NUC900: add GDMA driver support for nuc900 platform
2009-12-07 15:53 ` Hu Ruihuan
@ 2009-12-07 17:18 ` Russell King - ARM Linux
2009-12-07 18:11 ` Hu Ruihuan
0 siblings, 1 reply; 12+ messages in thread
From: Russell King - ARM Linux @ 2009-12-07 17:18 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Dec 07, 2009 at 11:53:39PM +0800, Hu Ruihuan wrote:
> Hi,Russell
> Thank you for your advice, it's my patch below follow your advice.
Did you look at the patch before you sent it?
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH V2] ARM: NUC900: add GDMA driver support for nuc900 platform
2009-12-07 17:18 ` Russell King - ARM Linux
@ 2009-12-07 18:11 ` Hu Ruihuan
2009-12-07 18:39 ` Russell King - ARM Linux
2009-12-08 3:03 ` Wan ZongShun
0 siblings, 2 replies; 12+ messages in thread
From: Hu Ruihuan @ 2009-12-07 18:11 UTC (permalink / raw)
To: linux-arm-kernel
yes,why?
2009/12/8 Russell King - ARM Linux <linux@arm.linux.org.uk>:
> On Mon, Dec 07, 2009 at 11:53:39PM +0800, Hu Ruihuan wrote:
>> Hi,Russell
>> ? Thank you for your advice, it's my patch below follow your advice.
>
> Did you look at the patch before you sent it?
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH V2] ARM: NUC900: add GDMA driver support for nuc900 platform
2009-12-07 18:11 ` Hu Ruihuan
@ 2009-12-07 18:39 ` Russell King - ARM Linux
2009-12-08 3:03 ` Wan ZongShun
1 sibling, 0 replies; 12+ messages in thread
From: Russell King - ARM Linux @ 2009-12-07 18:39 UTC (permalink / raw)
To: linux-arm-kernel
.yleritne selif eht gniteled eb ot sraeppa tI
On Tue, Dec 08, 2009 at 02:11:38AM +0800, Hu Ruihuan wrote:
> yes,why?
>
> 2009/12/8 Russell King - ARM Linux <linux@arm.linux.org.uk>:
> > On Mon, Dec 07, 2009 at 11:53:39PM +0800, Hu Ruihuan wrote:
> >> Hi,Russell
> >> ? Thank you for your advice, it's my patch below follow your advice.
> >
> > Did you look at the patch before you sent it?
> >
(My reply is backwards because you've top-posted. Please don't do that.)
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH V2] ARM: NUC900: add GDMA driver support for nuc900 platform
2009-12-07 18:11 ` Hu Ruihuan
2009-12-07 18:39 ` Russell King - ARM Linux
@ 2009-12-08 3:03 ` Wan ZongShun
2009-12-08 15:47 ` Hu Ruihuan
1 sibling, 1 reply; 12+ messages in thread
From: Wan ZongShun @ 2009-12-08 3:03 UTC (permalink / raw)
To: linux-arm-kernel
Hi Hu,
When you make your patch via 'diff -uNr a b xx.patch', Please do pay
attention to the order of a and b.
I think you would want to add the DMA driver rather than delete this,
notice your all '-' symbol of this patch.
2009/12/8 Hu Ruihuan <specter118@gmail.com>:
> yes,why?
>
> 2009/12/8 Russell King - ARM Linux <linux@arm.linux.org.uk>:
>> On Mon, Dec 07, 2009 at 11:53:39PM +0800, Hu Ruihuan wrote:
>>> Hi,Russell
>>> ? Thank you for your advice, it's my patch below follow your advice.
>>
>> Did you look at the patch before you sent it?
>>
>
--
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH V2] ARM: NUC900: add GDMA driver support for nuc900 platform
2009-12-08 3:03 ` Wan ZongShun
@ 2009-12-08 15:47 ` Hu Ruihuan
2009-12-09 14:50 ` Wan ZongShun
0 siblings, 1 reply; 12+ messages in thread
From: Hu Ruihuan @ 2009-12-08 15:47 UTC (permalink / raw)
To: linux-arm-kernel
Hi Wan,
I am sorry to make such a stupid mistake. The correct patch below.
Signed-off-by: Hu Ruihuan <specter118@gmail.com>
---
diff -uNr b/linux-2.6.32-rc8/arch/arm/mach-w90x900/dma.c
a/arch/arm/mach-w90x900/dma.c
--- b/linux-2.6.32-rc8/arch/arm/mach-w90x900/dma.c 1970-01-01
08:00:00.000000000 +0800
+++ a/arch/arm/mach-w90x900/dma.c 2009-12-08 23:34:39.000000000 +0800
@@ -0,0 +1,295 @@
+/*
+ * arch/arm/mach-w90p910/dma.c
+ *
+ * Support functions for the w90p910 internal DMA channels.
+ *
+ * Wan Zongshun <mcuos.com@gmail.com>
+ * Hu Ruihuan <specter118@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/atomic.h>
+#include <mach/hardware.h>
+
+#define NUC900_DMA_CHANNELS (2)
+#define DMA_BASE (W90X900_VA_GDMA)
+#define GDMA_INTCS (DMA_BASE + 0xa0)
+#define GDMA_GDGA (0x1c)
+#define TC0F (0x01 << 8)
+#define TC1F (0x01 << 10)
+#define ENTC0F (0x01)
+#define ENTC1F (0x01 << 2)
+
+#define GDMAEN (0x01)
+#define GDMAMS (0x03 << 2)
+#define DADIR (0x01 << 4)
+#define SADIR (0x01 << 5)
+#define DAFIX (0x01 << 6)
+#define SAFIX (0x01 << 7)
+#define D_INTS (0x01 << 10)
+#define TWSMASK (0x03 << 12)
+#define TWS (0x02 << 12)
+
+#define RUN (0x01 << 3)
+#define NON_DSCRIP (0x01 << 2)
+#define ORDEN (0x01 << 1)
+#define RESET (0x01)
+#define DSCRIPMASK (0x0F)
+#define CMDINFOMASK (0x03FFF)
+
+#define COUTN_TRANSFER (0x1000)/*count[13:0]=1024*/
+#define CHANNELINERV (0x20)
+
+typedef void (*dma_callback_t)(void *data);
+
+struct nuc900_dma_descp {
+ unsigned int next_descp;
+ unsigned int srcaddr;
+ unsigned int dstaddr;
+ unsigned int commandinfo;
+};
+
+struct nuc900_dma {
+ const char *device; /* this channel device, 0 if unused */
+ dma_callback_t callback; /* to call when DMA completes */
+ void __iomem *dma_reg;
+ void *data; /* ... with private data ptr */
+ struct nuc900_dma_descp *descp;
+};
+
+static struct nuc900_dma dma_chan[NUC900_DMA_CHANNELS];
+static struct nuc900_dma *dmachan0, *dmachan1;
+static atomic_t shared_irq_using;
+static DEFINE_SPINLOCK(dma_chan_lock);
+static struct nuc900_dma *register_dmachan_fromname(const char *dev_name,
+ unsigned int *irq_request)
+{
+ struct nuc900_dma *ret = NULL;
+
+ BUG_ON(!dev_name);
+
+
+ if (!(dmachan0->device) && !(dmachan1->device)) {
+ ret = dmachan0;
+ *irq_request = 1;
+ } else if ((dmachan0->device) && !(dmachan1->device)) {
+ if (strcmp(dmachan0->device, dev_name) == 0)
+ ret = ERR_PTR(-EBUSY);
+ else
+ ret = dmachan1;
+ } else if ((dmachan1->device) && !(dmachan0->device)) {
+ if (strcmp(dmachan1->device, dev_name) == 0)
+ ret = ERR_PTR(-EBUSY);
+ else
+ ret = dmachan0;
+ } else
+ ret = ERR_PTR(-EBUSY);
+
+ return ret;
+
+}
+
+static void match_dmachan_fromname(const char *dev_name,
+ struct nuc900_dma **dma)
+{
+ BUG_ON(!dev_name);
+
+ if (dmachan1->device) {
+ if (strcmp(dmachan1->device, dev_name) == 0)
+ *dma = dmachan1;
+ } else if (dmachan0->device) {
+ if (strcmp(dmachan0->device, dev_name) == 0)
+ *dma = dmachan0;
+ }
+}
+
+static irqreturn_t dma_irq_handler(int irq, void *dev_id)
+{
+ int status;
+ local_irq_disable();
+ status = __raw_readl(GDMA_GDGA) & (TC0F|TC1F);
+ __raw_writel(~status, GDMA_GDGA);
+ local_irq_enable();
+
+ if (status & TC0F)
+ dmachan0->callback(dmachan0->data);
+ if (status & TC1F)
+ dmachan1->callback(dmachan1->data);
+
+ return IRQ_HANDLED;
+}
+
+int nuc900_request_dma(const char *dev_name, dma_callback_t callback,
+ void *data)
+{
+ struct nuc900_dma *dma = NULL;
+ int err, irq_request;
+ err = 0;
+ irq_request = 0;
+ spin_lock(&dma_chan_lock);
+ dma = register_dmachan_fromname(dev_name, &irq_request);
+
+ if (IS_ERR(dma)) {
+ err = ERR_PTR(dma);
+ spin_unlock(&dma_chan_lock);
+ return err;
+ }
+ dma->device = dev_name;
+ atomic_inc(&shared_irq_using);
+ dma->callback = callback;
+ dma->data = data;
+ spin_unlock(&dma_chan_lock);
+ return 0;
+}
+EXPORT_SYMBOL(nuc900_request_dma);
+
+int nuc900_free_dma(const char *dev_name)
+{
+ struct nuc900_dma *dma = NULL;
+
+ BUG_ON(!dev_name);
+ WARN_ON(shared_irq_using.counter == 0);
+ spin_lock(&dma_chan_lock);
+ match_dmachan_fromname(dev_name, &dma);
+
+ if (!dma)
+ return -ENXIO;
+
+ dma->device = NULL;
+ spin_unlock(&dma_chan_lock);
+
+ atomic_dec(&shared_irq_using);
+
+ kzfree(dma->descp);
+ return 0;
+}
+EXPORT_SYMBOL(nuc900_free_dma);
+
+static unsigned int set_start_cmd(unsigned int start)
+{
+ unsigned int dscp_cmd;
+
+ dscp_cmd = 0;
+
+ if (start) {
+ dscp_cmd = (ORDEN|RUN);
+ dscp_cmd &= ~(NON_DSCRIP|RESET);
+ } else {
+ dscp_cmd = (NON_DSCRIP);
+ }
+ dscp_cmd &= DSCRIPMASK;
+
+ return dscp_cmd;
+}
+
+static unsigned int set_cmd_info(unsigned int countsize)
+{
+ unsigned int cmd_info;
+
+ cmd_info = (countsize >> 2);
+
+ cmd_info = (((cmd_info & CMDINFOMASK) << 18)|GDMAEN|D_INTS);
+ cmd_info &= ~(GDMAMS|DADIR|SADIR|DAFIX|SAFIX|TWSMASK);
+ cmd_info |= TWS;
+
+ return cmd_info;
+
+}
+
+static unsigned int get_descplist_num(unsigned int size, unsigned int *leave)
+{
+ unsigned int dscp_num, other_num;
+
+ dscp_num = 0;
+ other_num = 0;
+
+ dscp_num = size / COUTN_TRANSFER;
+ other_num = size % COUTN_TRANSFER;
+
+ if (other_num)
+ dscp_num += 1;
+
+ *leave = other_num;
+
+ return dscp_num;
+
+}
+
+int nuc900_start_dma(const char *dev_name, dma_addr_t dma_src,
+ dma_addr_t dma_dst, unsigned int size)
+{
+ struct nuc900_dma_descp *descp = NULL;
+ struct nuc900_dma *dma = NULL;
+ unsigned int i, dscp_num, other_num, val;
+
+ BUG_ON((!size|!dev_name));
+
+ match_dmachan_fromname(dev_name, &dma);
+
+ if (!dma)
+ return -ENXIO;
+
+ dscp_num = get_descplist_num(size, &other_num);
+
+ descp = kzalloc(dscp_num * sizeof(struct nuc900_dma), GFP_KERNEL);
+
+ if (!descp)
+ return -ENOMEM;
+
+ dma->descp = descp;
+
+ for (i = 0; i < dscp_num; i++) {
+ descp->next_descp = set_start_cmd(1);
+ descp->srcaddr = dma_src + COUTN_TRANSFER * i;
+ descp->dstaddr = dma_dst + COUTN_TRANSFER * i;
+ descp->commandinfo = set_cmd_info(COUTN_TRANSFER);
+ descp++;
+ }
+ descp--;
+ descp->next_descp = set_start_cmd(0);
+ descp->commandinfo = set_cmd_info(other_num);
+
+
+ val = set_start_cmd(1);
+ val |= (unsigned int)(dma->descp);
+ local_irq_disable();
+ __raw_writel(val, dma->dma_reg + GDMA_GDGA);
+
+ val = __raw_readl(GDMA_INTCS);
+ val |= (ENTC0F|ENTC1F);
+ __raw_writel(val, GDMA_INTCS);
+ local_irq_enable();
+
+ return 0;
+}
+EXPORT_SYMBOL(nuc900_start_dma);
+
+void __init nuc900_init_dma(void)
+{
+ unsigned i, ret;
+ struct nuc900_dma *dma_channel;
+ spin_lock_init(&dma_chan_lock);
+ for (i = 0; i < NUC900_DMA_CHANNELS; i++) {
+ dma_channel = &dma_chan[i];
+ dma_channel->dma_reg = DMA_BASE + i * CHANNELINERV;
+ dma_channel->device = NULL;
+ }
+ ret = request_irq(IRQ_GDMAGROUP, dma_irq_handler, IRQF_SHARED|
+ IRQF_DISABLED, NULL, NULL);
+ if (ret)
+ printk(KERN_ERR "register dma interrupt error: %d\n", ret);
+ dmachan0 = &dma_chan[0];
+ dmachan1 = &dma_chan[1];
+}
diff -uNr b/linux-2.6.32-rc8/arch/arm/mach-w90x900/Makefile
a/arch/arm/mach-w90x900/Makefile
--- b/linux-2.6.32-rc8/arch/arm/mach-w90x900/Makefile 2009-11-20
06:32:38.000000000 +0800
+++ a/arch/arm/mach-w90x900/Makefile 2009-12-06 12:34:47.000000000 +0800
@@ -5,7 +5,7 @@
# Object file lists.
obj-y := irq.o time.o mfp.o gpio.o clock.o
-obj-y += clksel.o dev.o cpu.o
+obj-y += clksel.o dev.o cpu.o dma.o
# W90X900 CPU support files
obj-$(CONFIG_CPU_W90P910) += nuc910.o
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH V2] ARM: NUC900: add GDMA driver support for nuc900 platform
2009-12-08 15:47 ` Hu Ruihuan
@ 2009-12-09 14:50 ` Wan ZongShun
2009-12-09 15:37 ` Hu Ruihuan
0 siblings, 1 reply; 12+ messages in thread
From: Wan ZongShun @ 2009-12-09 14:50 UTC (permalink / raw)
To: linux-arm-kernel
Hi Hu,
Thanks a lot for your patch, a comment below.
2009/12/8 Hu Ruihuan <specter118@gmail.com>:
> Hi Wan,
> ?I am sorry to make such a stupid mistake. The correct patch below.
>
> Signed-off-by: Hu Ruihuan <specter118@gmail.com>
> ---
> diff -uNr b/linux-2.6.32-rc8/arch/arm/mach-w90x900/dma.c
> a/arch/arm/mach-w90x900/dma.c
> --- b/linux-2.6.32-rc8/arch/arm/mach-w90x900/dma.c ? ? ?1970-01-01
> 08:00:00.000000000 +0800
> +++ a/arch/arm/mach-w90x900/dma.c ? ? ? 2009-12-08 23:34:39.000000000 +0800
> @@ -0,0 +1,295 @@
> +/*
> + * arch/arm/mach-w90p910/dma.c
> + *
> + * Support functions for the w90p910 internal DMA channels.
> + *
> + * Wan Zongshun <mcuos.com@gmail.com>
> + * Hu Ruihuan <specter118@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/init.h>
> +#include <linux/spinlock.h>
> +#include <linux/errno.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <asm/system.h>
> +#include <asm/irq.h>
> +#include <asm/atomic.h>
> +#include <mach/hardware.h>
> +
> +#define NUC900_DMA_CHANNELS ? ? ? ? ? ? ? ? ? ?(2)
> +#define DMA_BASE ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (W90X900_VA_GDMA)
> +#define GDMA_INTCS ? ? ? ? ? ? ? ? ? ? ? ? ? ? (DMA_BASE + 0xa0)
> +#define GDMA_GDGA ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(0x1c)
> +#define TC0F ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (0x01 << 8)
> +#define TC1F ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (0x01 << 10)
> +#define ENTC0F ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (0x01)
> +#define ENTC1F ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (0x01 << 2)
> +
> +#define GDMAEN ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (0x01)
> +#define GDMAMS ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (0x03 << 2)
> +#define DADIR ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(0x01 << 4)
> +#define SADIR ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(0x01 << 5)
> +#define DAFIX ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(0x01 << 6)
> +#define SAFIX ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(0x01 << 7)
> +#define D_INTS ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (0x01 << 10)
> +#define TWSMASK ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(0x03 << 12)
> +#define TWS ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(0x02 << 12)
> +
> +#define RUN ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(0x01 << 3)
> +#define NON_DSCRIP ? ? ? ? ? ? ? ? ? ? ? ? ? ? (0x01 << 2)
> +#define ORDEN ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(0x01 << 1)
> +#define RESET ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(0x01)
> +#define DSCRIPMASK ? ? ? ? ? ? ? ? ? ? ? ? ? ? (0x0F)
> +#define CMDINFOMASK ? ? ? ? ? ? ? ? ? ? ? ? ? ?(0x03FFF)
> +
> +#define COUTN_TRANSFER ? ? ? ? ? ? ? ? ? ? ? ? (0x1000)/*count[13:0]=1024*/
> +#define CHANNELINERV ? ? ? ? ? ? ? ? ? ? ? ? ? (0x20)
> +
> +typedef void (*dma_callback_t)(void *data);
> +
> +struct nuc900_dma_descp {
> + ? ? ? unsigned int next_descp;
> + ? ? ? unsigned int srcaddr;
> + ? ? ? unsigned int dstaddr;
> + ? ? ? unsigned int commandinfo;
> +};
> +
> +struct nuc900_dma {
> + ? ? ? const char *device; ? ? ? ? ? ? /* this channel device, 0 ?if unused */
> + ? ? ? dma_callback_t callback; ? ? ? ?/* to call when DMA completes */
> + ? ? ? void __iomem *dma_reg;
> + ? ? ? void *data; ? ? ? ? ? ? ? ? ? ? /* ... with private data ptr */
> + ? ? ? struct nuc900_dma_descp *descp;
> +};
> +
> +static struct nuc900_dma ?dma_chan[NUC900_DMA_CHANNELS];
> +static struct nuc900_dma *dmachan0, *dmachan1;
> +static atomic_t ?shared_irq_using;
> +static DEFINE_SPINLOCK(dma_chan_lock);
> +static struct nuc900_dma *register_dmachan_fromname(const char *dev_name,
> + ? ? ? ? ? ? ? ? ? ? ? unsigned int *irq_request)
> +{
> + ? ? ? struct nuc900_dma *ret = NULL;
> +
> + ? ? ? BUG_ON(!dev_name);
> +
> +
> + ? ? ? if (!(dmachan0->device) && !(dmachan1->device)) {
> + ? ? ? ? ? ? ? ret = dmachan0;
> + ? ? ? ? ? ? ? *irq_request = 1;
> + ? ? ? } else if ((dmachan0->device) && !(dmachan1->device)) {
> + ? ? ? ? ? ? ? ? ? ? ? if (strcmp(dmachan0->device, dev_name) == 0)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret = ERR_PTR(-EBUSY);
> + ? ? ? ? ? ? ? ? ? ? ? else
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret = dmachan1;
> + ? ? ? } else if ((dmachan1->device) && !(dmachan0->device)) {
> + ? ? ? ? ? ? ? ? ? ? ? if (strcmp(dmachan1->device, dev_name) == 0)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret = ERR_PTR(-EBUSY);
> + ? ? ? ? ? ? ? ? ? ? ? else
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret = dmachan0;
> + ? ? ? } else
> + ? ? ? ? ? ? ? ret = ERR_PTR(-EBUSY);
> +
> + ? ? ? return ret;
> +
> +}
> +
> +static void match_dmachan_fromname(const char *dev_name,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct nuc900_dma ?**dma)
> +{
> + ? ? ? BUG_ON(!dev_name);
> +
> + ? ? ? if (dmachan1->device) {
> + ? ? ? ? ? ? ? if (strcmp(dmachan1->device, dev_name) == 0)
> + ? ? ? ? ? ? ? ? ? ? ? *dma = dmachan1;
> + ? ? ? } else if (dmachan0->device) {
> + ? ? ? ? ? ? ? if (strcmp(dmachan0->device, dev_name) == 0)
> + ? ? ? ? ? ? ? ? ? ? ? *dma = dmachan0;
> + ? ? ? }
> +}
> +
> +static irqreturn_t dma_irq_handler(int irq, void *dev_id)
> +{
> + ? ? ? int status;
> + ? ? ? local_irq_disable();
> + ? ? ? status = __raw_readl(GDMA_GDGA) & (TC0F|TC1F);
> + ? ? ? __raw_writel(~status, GDMA_GDGA);
> + ? ? ? local_irq_enable();
> +
> + ? ? ? if (status & TC0F)
> + ? ? ? ? ? ? ? dmachan0->callback(dmachan0->data);
> + ? ? ? if (status & TC1F)
> + ? ? ? ? ? ? ? dmachan1->callback(dmachan1->data);
> +
> + ? ? ? return IRQ_HANDLED;
> +}
> +
> +int nuc900_request_dma(const char *dev_name, dma_callback_t callback,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? void *data)
> +{
> + ? ? ? struct nuc900_dma ?*dma = NULL;
> + ? ? ? int err, irq_request;
> + ? ? ? err = 0;
> + ? ? ? irq_request = 0;
> + ? ? ? spin_lock(&dma_chan_lock);
> + ? ? ? dma = register_dmachan_fromname(dev_name, &irq_request);
> +
> + ? ? ? if (IS_ERR(dma)) {
> + ? ? ? ? ? ? ? err = ERR_PTR(dma);
hi, I think it should be PTR_ERR rather than ERR_PTR, such as:
err = PTR_ERR(dma);
> + ? ? ? ? ? ? ? spin_unlock(&dma_chan_lock);
> + ? ? ? ? ? ? ? return err;
> + ? ? ? }
> + ? ? ? dma->device = dev_name;
> + ? ? ? atomic_inc(&shared_irq_using);
> + ? ? ? dma->callback = callback;
> + ? ? ? dma->data = data;
> + ? ? ? spin_unlock(&dma_chan_lock);
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(nuc900_request_dma);
> +
> +int nuc900_free_dma(const char *dev_name)
> +{
> + ? ? ? struct nuc900_dma ?*dma = NULL;
> +
> + ? ? ? BUG_ON(!dev_name);
> + ? ? ? WARN_ON(shared_irq_using.counter == 0);
> + ? ? ? spin_lock(&dma_chan_lock);
> + ? ? ? match_dmachan_fromname(dev_name, &dma);
> +
> + ? ? ? if (!dma)
> + ? ? ? ? ? ? ? return -ENXIO;
> +
> + ? ? ? dma->device = NULL;
> + ? ? ? spin_unlock(&dma_chan_lock);
> +
> + ? ? ? atomic_dec(&shared_irq_using);
> +
> + ? ? ? kzfree(dma->descp);
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(nuc900_free_dma);
> +
> +static unsigned int set_start_cmd(unsigned int start)
> +{
> + ? ? ? unsigned int dscp_cmd;
> +
> + ? ? ? dscp_cmd = 0;
> +
> + ? ? ? if (start) {
> + ? ? ? ? ? ? ? dscp_cmd = (ORDEN|RUN);
> + ? ? ? ? ? ? ? dscp_cmd &= ~(NON_DSCRIP|RESET);
> + ? ? ? } else {
> + ? ? ? ? ? ? ? dscp_cmd = (NON_DSCRIP);
> + ? ? ? }
> + ? ? ? ? ? ? ? dscp_cmd &= DSCRIPMASK;
> +
> + ? ? ? return dscp_cmd;
> +}
> +
> +static unsigned int set_cmd_info(unsigned int countsize)
> +{
> + ? ? ? unsigned int cmd_info;
> +
> + ? ? ? cmd_info = (countsize >> 2);
> +
> + ? ? ? cmd_info = (((cmd_info & CMDINFOMASK) << 18)|GDMAEN|D_INTS);
> + ? ? ? cmd_info &= ~(GDMAMS|DADIR|SADIR|DAFIX|SAFIX|TWSMASK);
> + ? ? ? cmd_info |= TWS;
> +
> + ? ? ? return cmd_info;
> +
> +}
> +
> +static unsigned int get_descplist_num(unsigned int size, unsigned int *leave)
> +{
> + ? ? ? unsigned int dscp_num, other_num;
> +
> + ? ? ? dscp_num = 0;
> + ? ? ? other_num = 0;
> +
> + ? ? ? dscp_num = size / COUTN_TRANSFER;
> + ? ? ? other_num = size % COUTN_TRANSFER;
> +
> + ? ? ? if (other_num)
> + ? ? ? ? ? ? ? dscp_num += 1;
> +
> + ? ? ? *leave = other_num;
> +
> + ? ? ? return dscp_num;
> +
> +}
> +
> +int nuc900_start_dma(const char *dev_name, dma_addr_t dma_src,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dma_addr_t dma_dst, unsigned int size)
> +{
> + ? ? ? struct nuc900_dma_descp *descp = NULL;
> + ? ? ? struct nuc900_dma ?*dma = NULL;
> + ? ? ? unsigned int i, dscp_num, other_num, val;
> +
> + ? ? ? BUG_ON((!size|!dev_name));
> +
> + ? ? ? match_dmachan_fromname(dev_name, &dma);
> +
> + ? ? ? if (!dma)
> + ? ? ? ? ? ? ? return -ENXIO;
> +
> + ? ? ? dscp_num = get_descplist_num(size, &other_num);
> +
> + ? ? ? descp = kzalloc(dscp_num * sizeof(struct nuc900_dma), GFP_KERNEL);
> +
> + ? ? ? if (!descp)
> + ? ? ? ? ? ? ? return -ENOMEM;
> +
> + ? ? ? dma->descp = descp;
> +
> + ? ? ? for (i = 0; i < dscp_num; i++) {
> + ? ? ? ? ? ? ? descp->next_descp = set_start_cmd(1);
> + ? ? ? ? ? ? ? descp->srcaddr = dma_src + COUTN_TRANSFER * i;
> + ? ? ? ? ? ? ? descp->dstaddr = dma_dst + COUTN_TRANSFER * i;
> + ? ? ? ? ? ? ? descp->commandinfo = set_cmd_info(COUTN_TRANSFER);
> + ? ? ? ? ? ? ? descp++;
> + ? ? ? }
> + ? ? ? descp--;
> + ? ? ? descp->next_descp = set_start_cmd(0);
> + ? ? ? descp->commandinfo = set_cmd_info(other_num);
> +
> +
> + ? ? ? val = set_start_cmd(1);
> + ? ? ? val |= (unsigned int)(dma->descp);
> + ? ? ? local_irq_disable();
> + ? ? ? __raw_writel(val, dma->dma_reg + GDMA_GDGA);
> +
> + ? ? ? val = __raw_readl(GDMA_INTCS);
> + ? ? ? val |= (ENTC0F|ENTC1F);
> + ? ? ? __raw_writel(val, GDMA_INTCS);
> + ? ? ? local_irq_enable();
> +
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(nuc900_start_dma);
> +
> +void __init nuc900_init_dma(void)
> +{
> + ? ? ? unsigned ? ? ? ?i, ret;
> + ? ? ? struct nuc900_dma ?*dma_channel;
> + ? ? ? spin_lock_init(&dma_chan_lock);
> + ? ? ? for (i = 0; i < NUC900_DMA_CHANNELS; i++) {
> + ? ? ? ? ? ? ? dma_channel = &dma_chan[i];
> + ? ? ? ? ? ? ? dma_channel->dma_reg = DMA_BASE + i * CHANNELINERV;
> + ? ? ? ? ? ? ? dma_channel->device = NULL;
> + ? ? ? }
> + ? ? ? ret = request_irq(IRQ_GDMAGROUP, dma_irq_handler, IRQF_SHARED|
> + ? ? ? ? ? ? ? ? ? ? ? IRQF_DISABLED, NULL, NULL);
> + ? ? ? if (ret)
> + ? ? ? ? ? ? ? printk(KERN_ERR "register dma interrupt error: %d\n", ret);
> + ? ? ? dmachan0 = &dma_chan[0];
> + ? ? ? dmachan1 = &dma_chan[1];
> +}
> diff -uNr b/linux-2.6.32-rc8/arch/arm/mach-w90x900/Makefile
> a/arch/arm/mach-w90x900/Makefile
> --- b/linux-2.6.32-rc8/arch/arm/mach-w90x900/Makefile ? 2009-11-20
> 06:32:38.000000000 +0800
> +++ a/arch/arm/mach-w90x900/Makefile ? ?2009-12-06 12:34:47.000000000 +0800
> @@ -5,7 +5,7 @@
> ?# Object file lists.
>
> ?obj-y ? ? ? ? ? ? ? ? ? ? ? ? ?:= irq.o time.o mfp.o gpio.o clock.o
> -obj-y ? ? ? ? ? ? ? ? ? ? ? ? ?+= clksel.o dev.o cpu.o
> +obj-y ? ? ? ? ? ? ? ? ? ? ? ? ?+= clksel.o dev.o cpu.o dma.o
> ?# W90X900 CPU support files
>
> ?obj-$(CONFIG_CPU_W90P910) ? ? ?+= nuc910.o
>
--
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH V2] ARM: NUC900: add GDMA driver support for nuc900 platform
2009-12-09 14:50 ` Wan ZongShun
@ 2009-12-09 15:37 ` Hu Ruihuan
2009-12-10 10:20 ` Russell King - ARM Linux
0 siblings, 1 reply; 12+ messages in thread
From: Hu Ruihuan @ 2009-12-09 15:37 UTC (permalink / raw)
To: linux-arm-kernel
HI, Wan
I made an update below.
Signed-off-by: Hu Ruihuan <specter118@gmail.com>
---
diff -uNr b/linux-2.6.32-rc8/arch/arm/mach-w90x900/dma.c
a/arch/arm/mach-w90x900/dma.c
--- b/linux-2.6.32-rc8/arch/arm/mach-w90x900/dma.c 1970-01-01
08:00:00.000000000 +0800
+++ a/arch/arm/mach-w90x900/dma.c 2009-12-08 23:34:39.000000000 +0800
@@ -0,0 +1,295 @@
+/*
+ * arch/arm/mach-w90p910/dma.c
+ *
+ * Support functions for the w90p910 internal DMA channels.
+ *
+ * Wan Zongshun <mcuos.com@gmail.com>
+ * Hu Ruihuan <specter118@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/atomic.h>
+#include <mach/hardware.h>
+
+#define NUC900_DMA_CHANNELS (2)
+#define DMA_BASE (W90X900_VA_GDMA)
+#define GDMA_INTCS (DMA_BASE + 0xa0)
+#define GDMA_GDGA (0x1c)
+#define TC0F (0x01 << 8)
+#define TC1F (0x01 << 10)
+#define ENTC0F (0x01)
+#define ENTC1F (0x01 << 2)
+
+#define GDMAEN (0x01)
+#define GDMAMS (0x03 << 2)
+#define DADIR (0x01 << 4)
+#define SADIR (0x01 << 5)
+#define DAFIX (0x01 << 6)
+#define SAFIX (0x01 << 7)
+#define D_INTS (0x01 << 10)
+#define TWSMASK (0x03 << 12)
+#define TWS (0x02 << 12)
+
+#define RUN (0x01 << 3)
+#define NON_DSCRIP (0x01 << 2)
+#define ORDEN (0x01 << 1)
+#define RESET (0x01)
+#define DSCRIPMASK (0x0F)
+#define CMDINFOMASK (0x03FFF)
+
+#define COUTN_TRANSFER (0x1000)/*count[13:0]=1024*/
+#define CHANNELINERV (0x20)
+
+typedef void (*dma_callback_t)(void *data);
+
+struct nuc900_dma_descp {
+ unsigned int next_descp;
+ unsigned int srcaddr;
+ unsigned int dstaddr;
+ unsigned int commandinfo;
+};
+
+struct nuc900_dma {
+ const char *device; /* this channel device, 0 if unused */
+ dma_callback_t callback; /* to call when DMA completes */
+ void __iomem *dma_reg;
+ void *data; /* ... with private data ptr */
+ struct nuc900_dma_descp *descp;
+};
+
+static struct nuc900_dma dma_chan[NUC900_DMA_CHANNELS];
+static struct nuc900_dma *dmachan0, *dmachan1;
+static atomic_t shared_irq_using;
+static DEFINE_SPINLOCK(dma_chan_lock);
+static struct nuc900_dma *register_dmachan_fromname(const char *dev_name,
+ unsigned int *irq_request)
+{
+ struct nuc900_dma *ret = NULL;
+
+ BUG_ON(!dev_name);
+
+
+ if (!(dmachan0->device) && !(dmachan1->device)) {
+ ret = dmachan0;
+ *irq_request = 1;
+ } else if ((dmachan0->device) && !(dmachan1->device)) {
+ if (strcmp(dmachan0->device, dev_name) == 0)
+ ret = ERR_PTR(-EBUSY);
+ else
+ ret = dmachan1;
+ } else if ((dmachan1->device) && !(dmachan0->device)) {
+ if (strcmp(dmachan1->device, dev_name) == 0)
+ ret = ERR_PTR(-EBUSY);
+ else
+ ret = dmachan0;
+ } else
+ ret = ERR_PTR(-EBUSY);
+
+ return ret;
+
+}
+
+static void match_dmachan_fromname(const char *dev_name,
+ struct nuc900_dma **dma)
+{
+ BUG_ON(!dev_name);
+
+ if (dmachan1->device) {
+ if (strcmp(dmachan1->device, dev_name) == 0)
+ *dma = dmachan1;
+ } else if (dmachan0->device) {
+ if (strcmp(dmachan0->device, dev_name) == 0)
+ *dma = dmachan0;
+ }
+}
+
+static irqreturn_t dma_irq_handler(int irq, void *dev_id)
+{
+ int status;
+ local_irq_disable();
+ status = __raw_readl(GDMA_GDGA) & (TC0F|TC1F);
+ __raw_writel(~status, GDMA_GDGA);
+ local_irq_enable();
+
+ if (status & TC0F)
+ dmachan0->callback(dmachan0->data);
+ if (status & TC1F)
+ dmachan1->callback(dmachan1->data);
+
+ return IRQ_HANDLED;
+}
+
+int nuc900_request_dma(const char *dev_name, dma_callback_t callback,
+ void *data)
+{
+ struct nuc900_dma *dma = NULL;
+ int err, irq_request;
+ err = 0;
+ irq_request = 0;
+ spin_lock(&dma_chan_lock);
+ dma = register_dmachan_fromname(dev_name, &irq_request);
+
+ if (IS_ERR(dma)) {
+ err = PTR_ERR(dma);
+ spin_unlock(&dma_chan_lock);
+ return err;
+ }
+ dma->device = dev_name;
+ atomic_inc(&shared_irq_using);
+ dma->callback = callback;
+ dma->data = data;
+ spin_unlock(&dma_chan_lock);
+ return 0;
+}
+EXPORT_SYMBOL(nuc900_request_dma);
+
+int nuc900_free_dma(const char *dev_name)
+{
+ struct nuc900_dma *dma = NULL;
+
+ BUG_ON(!dev_name);
+ WARN_ON(shared_irq_using.counter == 0);
+ spin_lock(&dma_chan_lock);
+ match_dmachan_fromname(dev_name, &dma);
+
+ if (!dma)
+ return -ENXIO;
+
+ dma->device = NULL;
+ spin_unlock(&dma_chan_lock);
+
+ atomic_dec(&shared_irq_using);
+
+ kzfree(dma->descp);
+ return 0;
+}
+EXPORT_SYMBOL(nuc900_free_dma);
+
+static unsigned int set_start_cmd(unsigned int start)
+{
+ unsigned int dscp_cmd;
+
+ dscp_cmd = 0;
+
+ if (start) {
+ dscp_cmd = (ORDEN|RUN);
+ dscp_cmd &= ~(NON_DSCRIP|RESET);
+ } else {
+ dscp_cmd = (NON_DSCRIP);
+ }
+ dscp_cmd &= DSCRIPMASK;
+
+ return dscp_cmd;
+}
+
+static unsigned int set_cmd_info(unsigned int countsize)
+{
+ unsigned int cmd_info;
+
+ cmd_info = (countsize >> 2);
+
+ cmd_info = (((cmd_info & CMDINFOMASK) << 18)|GDMAEN|D_INTS);
+ cmd_info &= ~(GDMAMS|DADIR|SADIR|DAFIX|SAFIX|TWSMASK);
+ cmd_info |= TWS;
+
+ return cmd_info;
+
+}
+
+static unsigned int get_descplist_num(unsigned int size, unsigned int *leave)
+{
+ unsigned int dscp_num, other_num;
+
+ dscp_num = 0;
+ other_num = 0;
+
+ dscp_num = size / COUTN_TRANSFER;
+ other_num = size % COUTN_TRANSFER;
+
+ if (other_num)
+ dscp_num += 1;
+
+ *leave = other_num;
+
+ return dscp_num;
+
+}
+
+int nuc900_start_dma(const char *dev_name, dma_addr_t dma_src,
+ dma_addr_t dma_dst, unsigned int size)
+{
+ struct nuc900_dma_descp *descp = NULL;
+ struct nuc900_dma *dma = NULL;
+ unsigned int i, dscp_num, other_num, val;
+
+ BUG_ON((!size|!dev_name));
+
+ match_dmachan_fromname(dev_name, &dma);
+
+ if (!dma)
+ return -ENXIO;
+
+ dscp_num = get_descplist_num(size, &other_num);
+
+ descp = kzalloc(dscp_num * sizeof(struct nuc900_dma), GFP_KERNEL);
+
+ if (!descp)
+ return -ENOMEM;
+
+ dma->descp = descp;
+
+ for (i = 0; i < dscp_num; i++) {
+ descp->next_descp = set_start_cmd(1);
+ descp->srcaddr = dma_src + COUTN_TRANSFER * i;
+ descp->dstaddr = dma_dst + COUTN_TRANSFER * i;
+ descp->commandinfo = set_cmd_info(COUTN_TRANSFER);
+ descp++;
+ }
+ descp--;
+ descp->next_descp = set_start_cmd(0);
+ descp->commandinfo = set_cmd_info(other_num);
+
+
+ val = set_start_cmd(1);
+ val |= (unsigned int)(dma->descp);
+ local_irq_disable();
+ __raw_writel(val, dma->dma_reg + GDMA_GDGA);
+
+ val = __raw_readl(GDMA_INTCS);
+ val |= (ENTC0F|ENTC1F);
+ __raw_writel(val, GDMA_INTCS);
+ local_irq_enable();
+
+ return 0;
+}
+EXPORT_SYMBOL(nuc900_start_dma);
+
+void __init nuc900_init_dma(void)
+{
+ unsigned i, ret;
+ struct nuc900_dma *dma_channel;
+ spin_lock_init(&dma_chan_lock);
+ for (i = 0; i < NUC900_DMA_CHANNELS; i++) {
+ dma_channel = &dma_chan[i];
+ dma_channel->dma_reg = DMA_BASE + i * CHANNELINERV;
+ dma_channel->device = NULL;
+ }
+ ret = request_irq(IRQ_GDMAGROUP, dma_irq_handler, IRQF_SHARED|
+ IRQF_DISABLED, NULL, NULL);
+ if (ret)
+ printk(KERN_ERR "register dma interrupt error: %d\n", ret);
+ dmachan0 = &dma_chan[0];
+ dmachan1 = &dma_chan[1];
+}
diff -uNr b/linux-2.6.32-rc8/arch/arm/mach-w90x900/Makefile
a/arch/arm/mach-w90x900/Makefile
--- b/linux-2.6.32-rc8/arch/arm/mach-w90x900/Makefile 2009-11-20
06:32:38.000000000 +0800
+++ a/arch/arm/mach-w90x900/Makefile 2009-12-06 12:34:47.000000000 +0800
@@ -5,7 +5,7 @@
# Object file lists.
obj-y := irq.o time.o mfp.o gpio.o clock.o
-obj-y += clksel.o dev.o cpu.o
+obj-y += clksel.o dev.o cpu.o dma.o
# W90X900 CPU support files
obj-$(CONFIG_CPU_W90P910) += nuc910.o
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH V2] ARM: NUC900: add GDMA driver support for nuc900 platform
2009-12-09 15:37 ` Hu Ruihuan
@ 2009-12-10 10:20 ` Russell King - ARM Linux
0 siblings, 0 replies; 12+ messages in thread
From: Russell King - ARM Linux @ 2009-12-10 10:20 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, Dec 09, 2009 at 11:37:50PM +0800, Hu Ruihuan wrote:
> +static void match_dmachan_fromname(const char *dev_name,
> + struct nuc900_dma **dma)
> +{
> + BUG_ON(!dev_name);
> +
> + if (dmachan1->device) {
> + if (strcmp(dmachan1->device, dev_name) == 0)
> + *dma = dmachan1;
> + } else if (dmachan0->device) {
> + if (strcmp(dmachan0->device, dev_name) == 0)
> + *dma = dmachan0;
> + }
> +}
Never write functions like this without explaination of why you're doing
it this way: having a void function returning something through a
double pointer is just silly, and leads to bugs (when someone forgets
to NULL-initialize before-hand.)
static struct nuc900_dma *match_dmachan_fromname(const char *dev_name)
{
BUG_ON(!dev_name);
if (dmachan1->device) {
if (strcmp(dmachan1->device, dev_name) == 0)
return dmachan1;
} else if (dmachan0->device) {
if (strcmp(dmachan0->device, dev_name) == 0)
return dmachan0;
}
return NULL;
}
This way, whenever you do:
dmachan = match_dmachan_fromname(foo);
you know that dmachan will always be initialized, whereas:
match_dmachan_fromname(foo, &dmachan);
there's little to tell the compiler that dmachan will be initialized -
in fact, the compiler _can't_ tell you whether you're possibly using it
without it having been initialized.
> +int nuc900_request_dma(const char *dev_name, dma_callback_t callback,
> + void *data)
> +{
> + struct nuc900_dma *dma = NULL;
> + int err, irq_request;
> + err = 0;
> + irq_request = 0;
> + spin_lock(&dma_chan_lock);
> + dma = register_dmachan_fromname(dev_name, &irq_request);
It seems that the irq_request handling is now unnecessary, please remove.
> + if (IS_ERR(dma)) {
> + err = PTR_ERR(dma);
> + spin_unlock(&dma_chan_lock);
> + return err;
> + }
> + dma->device = dev_name;
> + atomic_inc(&shared_irq_using);
shared_irq_using is not required anymore.
> + dma->callback = callback;
> + dma->data = data;
> + spin_unlock(&dma_chan_lock);
> + return 0;
> +}
> +EXPORT_SYMBOL(nuc900_request_dma);
> +
> +int nuc900_free_dma(const char *dev_name)
> +{
> + struct nuc900_dma *dma = NULL;
> +
> + BUG_ON(!dev_name);
> + WARN_ON(shared_irq_using.counter == 0);
This is buggy, never go inside an 'atomic' object - use the accessor
functions. In any case, shared_irq_using is not required so this can
be deleted.
> + spin_lock(&dma_chan_lock);
> + match_dmachan_fromname(dev_name, &dma);
> +
> + if (!dma)
> + return -ENXIO;
> +
> + dma->device = NULL;
> + spin_unlock(&dma_chan_lock);
> +
> + atomic_dec(&shared_irq_using);
Can be removed.
> +
> + kzfree(dma->descp);
> + return 0;
> +}
> +EXPORT_SYMBOL(nuc900_free_dma);
> +
> +static unsigned int set_start_cmd(unsigned int start)
> +{
> + unsigned int dscp_cmd;
> +
> + dscp_cmd = 0;
> +
> + if (start) {
> + dscp_cmd = (ORDEN|RUN);
> + dscp_cmd &= ~(NON_DSCRIP|RESET);
> + } else {
> + dscp_cmd = (NON_DSCRIP);
> + }
> + dscp_cmd &= DSCRIPMASK;
Weird formatting - is this correctly placed? It looks like it should
be within the else { } clause.
> +
> + return dscp_cmd;
> +}
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2009-12-10 10:20 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-11-26 10:42 [PATCH V2] ARM: NUC900: add GDMA driver support for nuc900 platform Hu Ruihuan
2009-11-29 12:03 ` Wan ZongShun
2009-11-29 12:16 ` Russell King - ARM Linux
2009-12-07 15:53 ` Hu Ruihuan
2009-12-07 17:18 ` Russell King - ARM Linux
2009-12-07 18:11 ` Hu Ruihuan
2009-12-07 18:39 ` Russell King - ARM Linux
2009-12-08 3:03 ` Wan ZongShun
2009-12-08 15:47 ` Hu Ruihuan
2009-12-09 14:50 ` Wan ZongShun
2009-12-09 15:37 ` Hu Ruihuan
2009-12-10 10:20 ` Russell King - ARM Linux
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.