[PATCH V2] ARM: NUC900: add GDMA driver support for nuc900 platform

Russell King - ARM Linux linux at arm.linux.org.uk
Sun Nov 29 07:16:14 EST 2009


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.



More information about the linux-arm-kernel mailing list