summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libbsp/sparc/shared/gpio/grgpio.c
blob: eab8ef94d273db99b27f689750f2e676c2357698 (plain) (tree)







































































































































































































































































                                                                                         











                                                                       












































































































































































                                                                                                      
/*  GRGPIO GPIO Driver interface.
 *
 *  COPYRIGHT (c) 2009.
 *  Cobham Gaisler AB.
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.com/license/LICENSE.
 */

#include <bsp.h>
#include <rtems/libio.h>
#include <stdlib.h>
#include <assert.h>
#include <rtems/bspIo.h>
#include <string.h>
#include <stdio.h>

#include <drvmgr/drvmgr.h>
#include <drvmgr/ambapp_bus.h>
#include <grgpio.h>
#include <gpiolib.h>
#include <ambapp.h>
#include <grlib.h>

/*#define DEBUG 1*/

#ifdef DEBUG
#define DBG(x...) printk(x)
#define STATIC
#else
#define DBG(x...) 
#define STATIC static
#endif

struct grgpio_isr {
	drvmgr_isr isr;
	void *arg;
};

struct grgpio_priv {
	struct drvmgr_dev	*dev;
	struct grgpio_regs		*regs;
	int				irq;
	int				minor;

	/* Driver implementation */
	int				port_cnt;
	unsigned char			port_handles[32];
	struct grgpio_isr		isrs[32];
	struct gpiolib_drv		gpiolib_desc;
	unsigned int			bypass;
	unsigned int			imask;
};

/******************* Driver Manager Part ***********************/

int grgpio_device_init(struct grgpio_priv *priv);

int grgpio_init1(struct drvmgr_dev *dev);
int grgpio_init2(struct drvmgr_dev *dev);

struct drvmgr_drv_ops grgpio_ops = 
{
	.init = {grgpio_init1, NULL, NULL, NULL},
	.remove = NULL,
	.info = NULL
};

struct amba_dev_id grgpio_ids[] = 
{
	{VENDOR_GAISLER, GAISLER_GPIO},
	{0, 0}		/* Mark end of table */
};

struct amba_drv_info grgpio_drv_info =
{
	{
		DRVMGR_OBJ_DRV,				/* Driver */
		NULL,					/* Next driver */
		NULL,					/* Device list */
		DRIVER_AMBAPP_GAISLER_GRGPIO_ID,	/* Driver ID */
		"GRGPIO_DRV",				/* Driver Name */
		DRVMGR_BUS_TYPE_AMBAPP,			/* Bus Type */
		&grgpio_ops,
		NULL,					/* Funcs */
		0,					/* No devices yet */
		0,
	},
	&grgpio_ids[0]
};

void grgpio_register_drv (void)
{
	DBG("Registering GRGPIO driver\n");
	drvmgr_drv_register(&grgpio_drv_info.general);
}

/* Register GRGPIO pins as quick as possible to the GPIO library,
 * other drivers may depend upon them in INIT LEVEL 2.
 * Note that since IRQ may not be available in init1, it is assumed
 * that the GPIOLibrary does not request IRQ routines until LEVEL 2.
 */
int grgpio_init1(struct drvmgr_dev *dev)
{
	struct grgpio_priv *priv;
	int status, port;

	DBG("GRGPIO[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name);

	/* This core will not find other cores, but other driver may depend upon 
	 * the GPIO library to function. So, we set up GPIO right away.
	 */

	/* Initialize library if not already done */
	status = gpiolib_initialize();
	if ( status < 0 )
		return DRVMGR_FAIL;

	priv = dev->priv = malloc(sizeof(struct grgpio_priv));
	if ( !priv )
		return DRVMGR_NOMEM;
	memset(priv, 0, sizeof(*priv));
	priv->dev = dev;

	if ( grgpio_device_init(priv) ) {
		free(dev->priv);
		dev->priv = NULL;
		return DRVMGR_FAIL;
	}

	/* Register all ports available on this core as GPIO port to 
	 * upper layer
	 */
	for(port=0; port<priv->port_cnt; port++) {
		priv->port_handles[port] = port;
		gpiolib_drv_register(&priv->gpiolib_desc,
					&priv->port_handles[port]);
	}

	return DRVMGR_OK;
}

/******************* Driver Implementation ***********************/

/* Find port from handle, returns -1 if not found */
int grgpio_find_port(void *handle, struct grgpio_priv **priv)
{
	unsigned char portnr;
	
	portnr = *(unsigned char *)handle;
	if ( portnr > 31 )
		return -1;
	*priv = (struct grgpio_priv *)
		(((unsigned int)handle - portnr*sizeof(unsigned char)) - 
		offsetof(struct grgpio_priv, port_handles));
	return portnr;
}

int grgpio_gpiolib_open(void *handle)
{
	struct grgpio_priv *priv;
	int portnr;

	portnr = grgpio_find_port(handle, &priv);
	if ( portnr < 0 ) {
		DBG("GRGPIO: FAILED OPENING HANDLE 0x%08x\n", handle);
		return -1;
	}
	DBG("GRGPIO[0x%08x][%d]: OPENING\n", priv->regs, portnr);

	/* Open the device, nothing to be done... */

	return 0;
}

int grgpio_grpiolib_config(void *handle, struct gpiolib_config *cfg)
{
	struct grgpio_priv *priv;
	int portnr;
	unsigned int mask;

	portnr = grgpio_find_port(handle, &priv);
	if ( portnr < 0 ) {
		return -1;
	}
	DBG("GRGPIO[0x%08x][%d]: CONFIG\n", priv->regs, portnr);

	/* Configure the device. And check that operation is supported,
	 * not all I/O Pins have IRQ support.
	 */
	mask = (1<<portnr);

	/* Return error when IRQ not supported by this I/O Line and it
	 * is beeing enabled by user.
	 */
	if ( ((mask & priv->imask) == 0) && cfg->mask )
		return -1;

	priv->regs->imask &= ~mask; /* Disable interrupt temporarily */

	/* Configure settings before enabling interrupt */
	priv->regs->ipol = (priv->regs->ipol & ~mask) | (cfg->irq_polarity ? mask : 0);
	priv->regs->iedge = (priv->regs->iedge & ~mask) | (cfg->irq_level ? 0 : mask);
	priv->regs->imask |= cfg->mask ? mask : 0;

	return 0;
}

int grgpio_grpiolib_get(void *handle, int *inval)
{
	struct grgpio_priv *priv;
	int portnr;

	portnr = grgpio_find_port(handle, &priv);
	if ( portnr < 0 ) {
		return -1;
	}
	DBG("GRGPIO[0x%08x][%d]: GET\n", priv->regs, portnr);

	/* Get current status of the port */
	if ( inval )
		*inval = (priv->regs->data >> portnr) & 0x1;

	return 0;
}

int grgpio_grpiolib_irq_opts(void *handle, unsigned int options)
{
	struct grgpio_priv *priv;
	int portnr;
	drvmgr_isr isr;
	void *arg;

	portnr = grgpio_find_port(handle, &priv);
	if ( portnr < 0 ) {
		return -1;
	}
	DBG("GRGPIO[0x%08x][%d]: IRQ OPTS 0x%x\n", priv->regs, portnr, options);

	if ( options & GPIOLIB_IRQ_FORCE )
		return -1;

	isr = priv->isrs[portnr].isr;
	arg = priv->isrs[portnr].arg;

	if ( options & GPIOLIB_IRQ_DISABLE ) {
		/* Disable interrupt at interrupt controller */
		if ( drvmgr_interrupt_unregister(priv->dev, portnr, isr, arg) ) {
			return -1;
		}
	}
	if ( options & GPIOLIB_IRQ_CLEAR ) {
		/* Clear interrupt at interrupt controller */
		if ( drvmgr_interrupt_clear(priv->dev, portnr) ) {
			return -1;
		}
	}
	if ( options & GPIOLIB_IRQ_ENABLE ) {
		/* Enable interrupt at interrupt controller */
		if ( drvmgr_interrupt_register(priv->dev, portnr, "grgpio", isr, arg) ) {
			return -1;
		}
	}
	if ( options & GPIOLIB_IRQ_MASK ) {
		/* Mask (disable) interrupt at interrupt controller */
		if ( drvmgr_interrupt_mask(priv->dev, portnr) ) {
			return -1;
		}
	}
	if ( options & GPIOLIB_IRQ_UNMASK ) {
		/* Unmask (enable) interrupt at interrupt controller */
		if ( drvmgr_interrupt_unmask(priv->dev, portnr) ) {
			return -1;
		}
	}

	return 0;
}

int grgpio_grpiolib_irq_register(void *handle, void *func, void *arg)
{
	struct grgpio_priv *priv;
	int portnr;

	portnr = grgpio_find_port(handle, &priv);
	if ( portnr < 0 ) {
		DBG("GRGPIO: FAILED OPENING HANDLE 0x%08x\n", handle);
		return -1;
	}
	DBG("GRGPIO: OPENING %d at [0x%08x]\n", portnr, priv->regs);

	/* Since the user doesn't provide the ISR and argument, we must... */
	priv->isrs[portnr].isr = func;
	priv->isrs[portnr].arg = arg;

	return 0;
}

int grgpio_grpiolib_set(void *handle, int dir, int outval)
{
	struct grgpio_priv *priv;
	int portnr;
	unsigned int mask;

	portnr = grgpio_find_port(handle, &priv);
	if ( portnr < 0 ) {
		DBG("GRGPIO: FAILED OPENING HANDLE 0x%08x\n", handle);
		return -1;
	}
	DBG("GRGPIO: OPENING %d at [0x%08x]\n", portnr, priv->regs);

	/* Set Direction and Output */
	mask = 1<<portnr;
	priv->regs->dir = (priv->regs->dir & ~mask) | (dir ? mask : 0);
	priv->regs->output = (priv->regs->output & ~mask) | (outval ? mask : 0);

	return 0;
}

int grgpio_gpiolib_show(void *handle)
{
	struct grgpio_priv *priv;
	int portnr, i, regs[7];
	volatile unsigned int *reg;

	portnr = grgpio_find_port(handle, &priv);
	if ( portnr < 0 ) {
		DBG("GRGPIO: FAILED SHOWING HANDLE 0x%08x\n", handle);
		return -1;
	}
	for (i=0, reg=&priv->regs->data; i<7; i++, reg++) {
		regs[i] = ( *reg >> portnr) & 1;
	}
	printf("GRGPIO[%p] PORT[%d]: IN/OUT/DIR: [%d,%d,%d], MASK/POL/EDGE: [%d,%d,%d], BYPASS: %d\n",
		priv->regs, portnr, regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6]);
	return 0;
}

int grgpio_gpiolib_get_info(void *handle, struct gpiolib_info *pinfo)
{
	struct grgpio_priv *priv;
	int portnr;
	char prefix[48];
	struct drvmgr_dev *dev;

	if ( !pinfo )
		return -1;

	portnr = grgpio_find_port(handle, &priv);
	if ( portnr < 0 ) {
		DBG("GRGPIO: FAILED GET_INFO HANDLE 0x%08x\n", handle);
		return -1;
	}

	/* Get Filesystem name prefix */
	dev = priv->dev;
	prefix[0] = '\0';
	if ( drvmgr_get_dev_prefix(dev, prefix) ) {
		/* Failed to get prefix, make sure of a unique FS name
		 * by using the driver minor.
		 */
		snprintf(pinfo->devName, 64, "/dev/grgpio%d/%d", dev->minor_drv, portnr);
	} else {
		/* Got special prefix, this means we have a bus prefix
		 * And we should use our "bus minor"
		 */
		snprintf(pinfo->devName, 64, "/dev/%sgrgpio%d/%d", prefix, dev->minor_bus, portnr);
	}

	return 0;
}

static struct gpiolib_drv_ops grgpio_gpiolib_ops = 
{
	.config		= grgpio_grpiolib_config,
	.get		= grgpio_grpiolib_get,
	.irq_opts	= grgpio_grpiolib_irq_opts,
	.irq_register	= grgpio_grpiolib_irq_register,
	.open		= grgpio_gpiolib_open,
	.set		= grgpio_grpiolib_set,
	.show		= grgpio_gpiolib_show,
	.get_info	= grgpio_gpiolib_get_info,
};

int grgpio_device_init(struct grgpio_priv *priv)
{
	struct amba_dev_info *ambadev;
	struct ambapp_core *pnpinfo;
	union drvmgr_key_value *value;
	unsigned int mask;
	int port_cnt;

	/* Get device information from AMBA PnP information */
	ambadev = (struct amba_dev_info *)priv->dev->businfo;
	if ( ambadev == NULL ) {
		return -1;
	}
	pnpinfo = &ambadev->info;
	priv->irq = pnpinfo->irq;
	priv->regs = (struct grgpio_regs *)pnpinfo->apb_slv->start;

	DBG("GRGPIO: 0x%08x irq %d\n", (unsigned int)priv->regs, priv->irq);

	/* Mask all Interrupts */
	priv->regs->imask = 0;

	/* Make IRQ Rising edge triggered default */
	priv->regs->ipol = 0xfffffffe;
	priv->regs->iedge = 0xfffffffe;

	/* Read what I/O lines have IRQ support */
	priv->imask = priv->regs->ipol;

	/* Let the user configure the port count, this might be needed
	 * when the GPIO lines must not be changed (assigned during bootup)
	 */
	value = drvmgr_dev_key_get(priv->dev, "nBits", KEY_TYPE_INT);
	if ( value ) {
		priv->port_cnt = value->i;
	} else {
		/* Auto detect number of GPIO ports */
		priv->regs->dir = 0;
		priv->regs->output = 0xffffffff;
		mask = priv->regs->output;
		priv->regs->output = 0;

		for(port_cnt=0; port_cnt<32; port_cnt++)
			if ( (mask & (1<<port_cnt)) == 0 )
				break;
		priv->port_cnt = port_cnt;
	}

	/* Let the user configure the BYPASS register, this might be needed
	 * to select which cores can do I/O on a pin.
	 */
	value = drvmgr_dev_key_get(priv->dev, "bypass", KEY_TYPE_INT);
	if ( value ) {
		priv->bypass = value->i;
	} else {
		priv->bypass = 0;
	}
	priv->regs->bypass = priv->bypass;

	/* Prepare GPIOLIB layer */
	priv->gpiolib_desc.ops = &grgpio_gpiolib_ops;

	return 0;
}