[PATCH] drivers: PMC MSP71xx GPIO char driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



[PATCH] drivers: PMC MSP71xx GPIO char driver

Patch to add a GPIO char driver for the PMC-Sierra
MSP71xx devices.

This patch references some platform support files previously
submitted to the linux-mips@xxxxxxxxxxxxxx list.

Thanks,
Marc

Signed-off-by: Marc St-Jean <Marc_St-Jean@xxxxxxxxxxxxxx>
---
 drivers/char/Kconfig                           |    4 
 drivers/char/Makefile                          |    1 
 drivers/char/pmcmsp_gpio.c                     |  587 +++++++++++++++++++++++++
 include/asm-mips/pmc-sierra/msp71xx/msp_gpio.h |  164 ++++++
 4 files changed, 756 insertions(+)

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index ed5453f..dea96a0 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -372,6 +372,10 @@ config ISTALLION
 	  To compile this driver as a module, choose M here: the
 	  module will be called istallion.
 
+config PMCMSP_GPIO
+	tristate "PMC MSP 7120 GPIO device support"
+	depends on MIPS && (PMC_MSP7120_EVAL || PMC_MSP7120_GW || PMC_MSP7120_FPGA)
+	  
 config AU1X00_GPIO
 	tristate "Alchemy Au1000 GPIO device support"
 	depends on MIPS && SOC_AU1X00
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 3ed7647..9abbcc1 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -86,6 +86,7 @@ obj-$(CONFIG_DS1620)		+= ds1620.o
 obj-$(CONFIG_HW_RANDOM)		+= hw_random/
 obj-$(CONFIG_COBALT_LCD)	+= lcd.o
 obj-$(CONFIG_AU1000_GPIO)	+= au1000_gpio.o
+obj-$(CONFIG_PMCMSP_GPIO)	+= pmcmsp_gpio.o
 obj-$(CONFIG_PPDEV)		+= ppdev.o
 obj-$(CONFIG_NWBUTTON)		+= nwbutton.o
 obj-$(CONFIG_NWFLASH)		+= nwflash.o
diff --git a/drivers/char/pmcmsp_gpio.c b/drivers/char/pmcmsp_gpio.c
new file mode 100644
index 0000000..23b48d9
--- /dev/null
+++ b/drivers/char/pmcmsp_gpio.c
@@ -0,0 +1,587 @@
+/*
+ * $Id: pmcmsp_gpio.c,v 1.3 2006/10/19 22:08:16 stjeanma Exp $
+ *
+ * Driver for the PMC MSP7120 reference board GPIO pins
+ *
+ * Copyright 2005 PMC-Sierra, Inc.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
+ *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+ *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+ *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You should have received a copy of the  GNU General Public License along
+ *  with this program; if not, write  to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <asm/io.h>
+
+#include <asm/war.h>
+#include <asm/regops.h>
+
+#include <msp_regs.h>
+#include <msp_gpio.h>
+
+static DEFINE_SPINLOCK(msp_gpio_spinlock);
+
+static struct task_struct *msp_blinkthread;
+static int msp_blink_running;
+static DEFINE_SPINLOCK(msp_blink_lock);
+static DECLARE_COMPLETION(msp_blink_wait);
+
+struct blinkTable_t {
+	uint32_t count;
+	uint32_t period;
+	uint32_t dcycle;
+};
+
+static struct blinkTable_t blinkTable[MSP_NUM_GPIOS];
+
+/* Define the following for extra debug output */
+#undef DEBUG
+
+/* -- Utility functions -- */
+
+#ifdef DEBUG
+#define DBG(args...) printk( args )
+#else
+#define DBG(args...) do{} while( 0 )
+#endif
+
+/* Reads the data bits from a single register set */
+static uint32_t msp_gpio_read_data_basic( int reg )
+{
+	return *GPIO_DATA_REG[reg] & GPIO_DATA_MASK[reg];
+}
+
+/* Reads the data bits from the extended register set */
+static uint32_t msp_gpio_read_data_extended(void)
+{
+	int pin;
+	uint32_t tmp, retval = 0;
+	
+	tmp = *EXTENDED_GPIO_REG;
+	for( pin = 0; pin < EXTENDED_GPIO_COUNT; pin++ ) {
+		uint32_t bit = 0;
+		/*
+		 * In output mode, read CLR bit
+		 * In input mode, read SET bit
+		 */
+		if( tmp & (EXTENDED_GPIO_CFG_ENABLE <<
+					EXTENDED_GPIO_CFG_SHIFT(pin)) )
+			bit = (EXTENDED_GPIO_DATA_CLR <<
+					EXTENDED_GPIO_DATA_SHIFT(pin));
+		else
+			bit = (EXTENDED_GPIO_DATA_SET <<
+					EXTENDED_GPIO_DATA_SHIFT(pin));
+
+		if( tmp & bit )
+			retval |= (1 << pin);
+	}
+	return retval;
+}
+
+/*
+ * Reads the current state of all 20 pins, putting the values in the lowest 20
+ * bits
+ * (1=HI, 0=LO)
+ */
+static uint32_t msp_gpio_read_data(void)
+{
+	int reg;
+	uint32_t retval = 0;
+	
+	spin_lock(&msp_gpio_spinlock);
+	for( reg = 0; reg < GPIO_REG_COUNT; reg++ )
+		retval |= msp_gpio_read_data_basic(reg) << GPIO_DATA_SHIFT[reg];
+	retval |= msp_gpio_read_data_extended() << EXTENDED_GPIO_SHIFT;
+	spin_unlock(&msp_gpio_spinlock);
+	DBG( "%s: 0x%08x\n", __FUNCTION__, retval );
+	return retval;
+}
+
+/* This assumes both data and mask are register-ready, and does the set
+ * atomically */
+static void msp_gpio_write_data_basic( int reg, uint32_t data, uint32_t mask )
+{
+	set_value_reg32( GPIO_DATA_REG[reg], mask, data );
+}
+
+/* The four lowest bits of 'data' and 'mask' are used, and the set is done
+ * atomically */
+static void msp_gpio_write_data_extended( uint32_t data, uint32_t mask )
+{
+	int i;
+	uint32_t tmpmask = 0xffffffff, tmpdata = 0;
+
+	/* Set all SET/CLR values based on data bits passed in */
+	for( i = 0; i < EXTENDED_GPIO_COUNT; i++ ) {
+		if( mask & (1 << i) ) {
+			if( data & (1 << i) )
+				/* Set the bit HI */
+				tmpdata |= EXTENDED_GPIO_DATA_SET <<
+					EXTENDED_GPIO_DATA_SHIFT(i);
+			else
+				/* Set the bit LO */
+				tmpdata |= EXTENDED_GPIO_DATA_CLR <<
+					EXTENDED_GPIO_DATA_SHIFT(i);
+		}
+	}
+	
+	set_value_reg32( EXTENDED_GPIO_REG, tmpmask, tmpdata );
+}
+
+/*
+ * Sets all masked GPIOs based on the first 20 bits of the data passed in
+ * (1=HI, 0=LO)
+ */
+static void msp_gpio_write_data( uint32_t data, uint32_t mask )
+{
+	int reg;
+	
+	spin_lock(&msp_gpio_spinlock);
+	for( reg = 0; reg < GPIO_REG_COUNT; reg++ ) {
+		uint32_t tmpdata = (data >> GPIO_DATA_SHIFT[reg]) &
+			GPIO_DATA_MASK[reg];
+		uint32_t tmpmask = (mask >> GPIO_DATA_SHIFT[reg]) &
+			GPIO_DATA_MASK[reg];
+		if( tmpmask > 0 )
+			msp_gpio_write_data_basic( reg, tmpdata, tmpmask );
+	}
+	msp_gpio_write_data_extended( data >> EXTENDED_GPIO_SHIFT,
+			       mask >> EXTENDED_GPIO_SHIFT );
+	spin_unlock(&msp_gpio_spinlock);
+}
+
+/* Reads the config bits from a single register set */
+static uint32_t msp_gpio_read_cfg_basic( int reg )
+{
+	return *GPIO_CFG_REG[reg] & GPIO_CFG_MASK[reg];
+}
+
+/* This assumes both data and mask are register-ready, and does the write
+ * atomically */
+static void msp_gpio_write_cfg_basic( int reg, uint32_t data, uint32_t mask )
+{
+	set_value_reg32( GPIO_CFG_REG[reg], mask, data );
+}
+
+/*
+ * Reads the configuration of the extended pins, returning the current
+ * configuration in the lowest 4 bits (1-output, 0-input)
+ */
+static uint32_t msp_gpio_read_cfg_extended(void)
+{
+	int i;
+	uint32_t tmp = *EXTENDED_GPIO_REG;
+	uint32_t retval = 0;
+
+	/* Read all ENABLE/DISABLE values and translate to single bits */
+	for( i = 0; i < EXTENDED_GPIO_COUNT; i++ ) {
+		if( tmp & (EXTENDED_GPIO_CFG_ENABLE <<
+				EXTENDED_GPIO_CFG_SHIFT(i)) )
+			/* Pin is OUTPUT */
+			retval |= (1 << i);
+		else
+			/* Pin is INPUT */
+			retval &= ~(1 << i);
+	}
+	
+	return retval;
+}
+
+/*
+ * Sets the masked extended pins to (1-output, 0-input)
+ * (uses 4 lowest bits of the mask)
+ * Does the write atomically
+ */
+static void msp_gpio_write_cfg_extended( uint32_t data, uint32_t mask )
+{
+	int i;
+	uint32_t tmpmask = 0xffffffff, tmpdata = 0;
+
+	/* Set all ENABLE/DISABLE values based on mask bits passed in */
+	for( i = 0; i < EXTENDED_GPIO_COUNT; i++ ) {
+		if( mask & (1 << i) ) {
+			if( data & (1 << i) )
+				/* Set the pin to OUTPUT */
+				tmpdata |= EXTENDED_GPIO_CFG_ENABLE <<
+					EXTENDED_GPIO_CFG_SHIFT(i);
+			else
+				/* Set the pin to INPUT */
+				tmpdata |= EXTENDED_GPIO_CFG_DISABLE <<
+					EXTENDED_GPIO_CFG_SHIFT(i);
+		}
+	}
+	
+	set_value_reg32( EXTENDED_GPIO_REG, tmpmask, tmpdata );
+}
+
+/*
+ * Sets all GPIOs to input/output based on the first 20 bits of the mask
+ * (1-output, 0-input)
+ */
+static void msp_gpio_write_cfg( msp_gpio_mode_t mode, uint32_t mask )
+{
+	int reg;
+	uint32_t extdata = 0, extmask = 0;
+	
+	spin_lock(&msp_gpio_spinlock);
+	for( reg = 0; reg < GPIO_REG_COUNT; reg++ ) {
+		int pin;
+		uint32_t tmpdata = 0, tmpmask = 0;
+		for( pin = 0; pin < GPIO_DATA_COUNT[reg]; pin++ ) {
+			if( mask & (1 << (GPIO_DATA_SHIFT[reg] + pin)) ) {
+				tmpmask |= (GPIO_CFG_PINMASK <<
+						GPIO_CFG_SHIFT(pin) );
+				tmpdata |= ( (uint32_t)mode <<
+						GPIO_CFG_SHIFT(pin) );
+			}
+		}
+		if( tmpmask )
+			msp_gpio_write_cfg_basic(reg, tmpdata, tmpmask);
+	}
+
+	extmask = mask >> EXTENDED_GPIO_SHIFT;
+	if( mode == MSP_GPIO_INPUT )
+		extdata = 0;
+	else if( mode == MSP_GPIO_OUTPUT )
+		extdata = 0xf;
+	else
+		extmask = 0;
+	if( extmask )
+		msp_gpio_write_cfg_extended( extdata, extmask );
+	spin_unlock(&msp_gpio_spinlock);
+}
+
+/*
+ * Reads all GPIO config values and checks if they match the pin mode given,
+ * placing the result in the lowest 20 bits of the result, one bit per pin
+ * (1-pin matches mode give, 0-pin does not match)
+ */
+static uint32_t msp_gpio_read_cfg( uint32_t mode )
+{
+	uint32_t retval = 0;
+	int reg;
+	
+	spin_lock(&msp_gpio_spinlock);
+	for( reg = 0; reg < GPIO_REG_COUNT; reg++ ) {
+		int pin;
+		uint32_t tmpdata = msp_gpio_read_cfg_basic(reg);
+		for( pin = 0; pin < GPIO_DATA_COUNT[reg]; pin++ ) {
+			uint32_t val = (tmpdata >> GPIO_CFG_SHIFT(pin)) &
+						GPIO_CFG_PINMASK;
+			if( val == mode )
+				retval |= (1 << (GPIO_DATA_SHIFT[reg] + pin));
+		}
+	}
+	/* Extended pins only have INPUT or OUTPUT pins */
+	if( mode == MSP_GPIO_INPUT )
+		retval |= (~msp_gpio_read_cfg_extended() & EXTENDED_GPIO_MASK)
+			  << EXTENDED_GPIO_SHIFT;
+	else if( mode == MSP_GPIO_OUTPUT )
+		retval |= (msp_gpio_read_cfg_extended() & EXTENDED_GPIO_MASK)
+			  << EXTENDED_GPIO_SHIFT;
+	spin_unlock(&msp_gpio_spinlock);
+	DBG( "%s(0x%02x): 0x%08x\n", __FUNCTION__, mode, retval );
+	return retval;
+}
+
+/* -- Public functions -- */
+
+/*
+ * Reads the bits specified by the mask and puts the values in data.
+ * May include output statuses also, if in mask.
+ *
+ * Returns 0 on success
+ */
+int msp_gpio_in( uint32_t *data, uint32_t mask )
+{
+	*data = msp_gpio_read_data() & mask;
+	return 0;
+}
+
+/*
+ * Sets the pins to output with the specified data on them
+ *
+ * Returns 0 on success or one of the following:
+ *  -EINVAL if any of the pins in the mask are not free
+ */
+int msp_gpio_out( uint32_t data, uint32_t mask )
+{
+	if( (mask & ~MSP_GPIO_FREE_MASK) != 0 ) {
+		printk( "Invalid GPIO mask - References non-free pins\n" );
+		return -EINVAL;
+	}
+	if( (mask & ~msp_gpio_read_cfg(MSP_GPIO_OUTPUT)) != 0 ) {
+		printk( "Invalid GPIO mask - Cannot set non-output pins\n" );
+		return -EINVAL;
+	}
+	
+	msp_gpio_noblink( mask );
+	msp_gpio_write_data( data, mask );
+
+	return 0;
+}
+
+/*
+ * Sets the pins to output or input (1 is output, 0 is input)
+ *
+ * Returns 0 on success or one of the following:
+ *  -EINVAL if any of the pins in the mask are not free
+ */
+int msp_gpio_mode( msp_gpio_mode_t mode, uint32_t mask )
+{
+	uint32_t allowedmask;
+
+	if( (mask & ~MSP_GPIO_FREE_MASK) != 0 ) {
+		printk( "Invalid GPIO mask - References non-free pins\n" );
+		return -EINVAL;
+	}
+	
+	/* Enforce pin-mode rules */
+	allowedmask = MSP_GPIO_MODE_ALLOWED[mode];
+	if( (mask & ~allowedmask) != 0 ) {
+		printk( "Invalid GPIO mode for masked pins\n" );
+		return -EINVAL;
+	}
+
+	msp_gpio_noblink( mask );
+	msp_gpio_write_cfg( mode, mask );
+
+	return 0;
+}
+
+/*
+ * Stops the specified GPIOs from blinking
+ */
+int msp_gpio_noblink( uint32_t mask )
+{
+	int i;
+
+	if( (mask & ~msp_gpio_read_cfg(MSP_GPIO_OUTPUT)) != 0 )
+		return -EINVAL;
+
+	spin_lock( &msp_blink_lock );
+	for( i = 0; i < MSP_NUM_GPIOS; i++ ) {
+		if( mask & MSP_GPIO_BIT(i) ) {
+			blinkTable[i].count = 0;
+			blinkTable[i].period = 0;
+			blinkTable[i].dcycle = 0;
+		}
+	}
+	spin_unlock( &msp_blink_lock );
+
+	msp_gpio_write_data( 0, mask );
+
+	return 0;
+}
+
+/*
+ * Configures GPIO(s) to blink
+ *  - mask shows which GPIOs to blink
+ *  - period is the time in 100ths of a second for the total period
+ *    (0 disables blinking)
+ *  - dcycle is the percentage of the period where the GPIO is HI
+ */
+int msp_gpio_blink( uint32_t mask, uint32_t period, uint32_t dcycle )
+{
+	int i;
+
+	if( (mask & ~msp_gpio_read_cfg(MSP_GPIO_OUTPUT)) != 0 ) {
+		printk( "Invalid GPIO mask - Cannot blink non-output pins\n" );
+		return -EINVAL;
+	}
+
+	if( period == 0 ) 
+		return msp_gpio_noblink( mask );
+
+	spin_lock( &msp_blink_lock );
+	for( i = 0; i < MSP_NUM_GPIOS; i++ ) {
+		if( mask & MSP_GPIO_BIT(i) ) {
+			blinkTable[i].count = 0;
+			blinkTable[i].period = period;
+			blinkTable[i].dcycle = dcycle;
+		}
+	}
+	spin_unlock( &msp_blink_lock );
+
+	complete( &msp_blink_wait );
+
+	return 0;
+}
+
+/* -- File functions -- */
+static int msp_gpio_open( struct inode *inode, struct file *file )
+{
+	return 0;
+}
+
+static int msp_gpio_release( struct inode *inode, struct file *file )
+{
+	return 0;
+}
+
+static int msp_gpio_ioctl( struct inode *inode, struct file *file,
+		unsigned int cmd, unsigned long arg)
+{
+	static struct msp_gpio_ioctl_io_data data;
+	static struct msp_gpio_ioctl_blink_data blink;
+
+	switch( cmd ) {
+		case MSP_GPIO_BLINK:
+			if( copy_from_user(&blink, (struct msp_gpio_ioctl_blink_data *)arg, sizeof(blink) ) )
+				return -EFAULT;
+			break;
+		default:
+			if( copy_from_user(&data, (struct msp_gpio_ioctl_io_data *)arg, sizeof(data) ) )
+				return -EFAULT;
+			break;
+	}
+
+	switch( cmd ) {
+		case MSP_GPIO_IN:
+			if( msp_gpio_in( &data.data, data.mask ) )
+				return -EFAULT;
+			if( copy_to_user( (struct msp_gpio_ioctl_io_data *)arg, &data, sizeof(data)) )
+				return -EFAULT;
+			break;
+		case MSP_GPIO_OUT:
+			if( msp_gpio_out( data.data, data.mask ) )
+				return -EFAULT;
+			break;
+		case MSP_GPIO_MODE:
+			if( msp_gpio_mode( data.data, data.mask ) )
+				return -EFAULT;
+			break;
+		case MSP_GPIO_BLINK:
+			if( msp_gpio_blink( blink.mask, blink.period, blink.dcycle ) )
+				return -EFAULT;
+			break;
+		default:
+			return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static struct file_operations msp_gpio_fops = {
+	.owner		= THIS_MODULE,
+	.ioctl		= msp_gpio_ioctl,
+	.open		= msp_gpio_open,
+	.release	= msp_gpio_release,
+};
+
+static struct miscdevice msp_gpio_miscdev = {
+	MISC_DYNAMIC_MINOR,
+	"pmcmsp_gpio",
+	&msp_gpio_fops
+};
+
+/* -- Module functions -- */
+
+static int msp_gpio_blinkthread( void *none )
+{
+	int firstrun = 1;
+	
+	msp_blink_running = 1;
+	while( msp_blink_running ) {
+		uint32_t mask = 0, data = 0;
+		int i, blinking = 0;
+		spin_lock( &msp_blink_lock );
+		for( i = 0; i < MSP_NUM_GPIOS; i++ ) {
+			/* use blinkTable[i].period as 'blink enabled' test */
+			if( blinkTable[i].period) {
+				blinking = 1;
+				mask |= MSP_GPIO_BIT(i);
+				blinkTable[i].count++;
+
+				if( blinkTable[i].count >=
+						blinkTable[i].period )
+					blinkTable[i].count = 0;
+
+				if( blinkTable[i].count <
+					(blinkTable[i].period *
+					 blinkTable[i].dcycle / 100 ) )
+					data |= MSP_GPIO_BIT(i);
+			}
+		}
+		spin_unlock( &msp_blink_lock );
+
+		if( !firstrun || blinking )
+			msp_gpio_write_data( data, mask );
+		else
+			firstrun = 0;
+
+		if( blinking ) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout (HZ/100);
+		} else {
+			wait_for_completion_interruptible( &msp_blink_wait );
+		}
+	}
+
+	return 0;
+}
+
+static int __init msp_gpio_init(void)
+{
+	if( misc_register(&msp_gpio_miscdev) ) {
+		printk( "Device registration failed\n" );
+		goto err_miscdev;
+	}
+
+	msp_blinkthread = kthread_run( msp_gpio_blinkthread, NULL, "gpio_blink" );
+	if( msp_blinkthread == NULL )
+	{
+		printk( "Could not start kthread\n" );
+		goto err_blinkthread;
+	}
+
+	printk( "MSP7120 GPIO subsystem initialized\n" );
+	return 0;
+
+err_blinkthread:
+	misc_deregister(&msp_gpio_miscdev);
+err_miscdev:
+	return -ENOMEM;
+}
+
+static void __exit msp_gpio_exit(void)
+{
+	msp_blink_running = 0;
+	complete( &msp_blink_wait );
+	kthread_stop( msp_blinkthread );
+
+	misc_deregister(&msp_gpio_miscdev);
+}
+
+module_init(msp_gpio_init);
+module_exit(msp_gpio_exit);
+
+EXPORT_SYMBOL(msp_gpio_in);
+EXPORT_SYMBOL(msp_gpio_out);
+EXPORT_SYMBOL(msp_gpio_mode);
+EXPORT_SYMBOL(msp_gpio_blink);
+EXPORT_SYMBOL(msp_gpio_noblink);
+
+MODULE_LICENSE("GPL");
diff --git a/include/asm-mips/pmc-sierra/msp71xx/msp_gpio.h b/include/asm-mips/pmc-sierra/msp71xx/msp_gpio.h
new file mode 100644
index 0000000..7459355
--- /dev/null
+++ b/include/asm-mips/pmc-sierra/msp71xx/msp_gpio.h
@@ -0,0 +1,164 @@
+/*
+ * $Id: msp_gpio.h,v 1.4 2006/05/08 19:45:39 ramsayji Exp $
+ *
+ * Character driver for the PMC Athena (MSP7120) reference board GPIO pins
+ *
+ * Copyright 2005 PMC-Sierra, Inc.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
+ *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+ *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+ *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You should have received a copy of the  GNU General Public License along
+ *  with this program; if not, write  to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __MSP_GPIO_H__
+#define __MSP_GPIO_H__
+
+#include <linux/ioctl.h>
+#include <msp_gpio_macros.h>
+
+/* IOCTL structs macros */
+
+struct msp_gpio_ioctl_io_data
+{
+	uint32_t data;
+	uint32_t mask;
+};
+
+struct msp_gpio_ioctl_blink_data
+{
+	uint32_t mask;
+	uint32_t period;
+	uint32_t dcycle;
+};
+
+#define MSP_GPIO_IOCTL_BASE	'Z'
+
+/* Reads the current data bits */
+#define MSP_GPIO_IN		_IOWR(MSP_GPIO_IOCTL_BASE, 0, \
+					struct msp_gpio_ioctl_io_data )
+
+/* Writes data bits */
+#define MSP_GPIO_OUT	_IOW (MSP_GPIO_IOCTL_BASE, 1, \
+					struct msp_gpio_ioctl_io_data )
+
+/*
+ * Sets all masked pins to the msp_gpio_mode_t given in the data field
+ */
+#define MSP_GPIO_MODE	_IOW (MSP_GPIO_IOCTL_BASE, 2, \
+					struct msp_gpio_ioctl_io_data )
+
+/* 
+ * Starts any masked LEDs blinking with parameters as follows:
+ *   - period - The time in 100ths of a second for a single period
+ *              (set to '0' to stop blinking)
+ *   - dcycle - The 'duty cycle' - what percentage of the period should the
+ *              gpio be on?
+ */
+#define MSP_GPIO_BLINK	_IOW (MSP_GPIO_IOCTL_BASE, 3, \
+					struct msp_gpio_ioctl_blink_data )
+
+/* Bit flags and masks for GPIOs */
+#define MSP_GPIO_BIT(gpio)	(1 << gpio)
+
+#define MSP_NUM_GPIOS		(20)
+
+#define MSP_GPIO_ALL_MASK 	((1<<MSP_NUM_GPIOS) - 1)
+
+#define MSP_GPIO_NONE_MASK 	(0LL)
+
+#define MSP_GPIO_FREE_MASK	MSP_GPIO_ALL_MASK
+
+/* The following is only available to other modules */
+
+#ifdef __KERNEL__
+
+/*
+ * Reads the bits specified by the mask and puts the values in data.
+ * May include output statuses also, if in mask.
+ *
+ * Returns 0 on success
+ */
+extern int msp_gpio_in( uint32_t *data, uint32_t mask );
+
+/*
+ * Sets the specified data to the masked pins
+ *
+ * Returns 0 on success or one of the following:
+ *  -EINVAL if any of the pins in the mask are not free or not already in output
+ *  mode
+ */
+extern int msp_gpio_out( uint32_t data, uint32_t mask );
+
+/*
+ * Sets all masked pins to the specified msp_gpio_mode_t
+ *
+ * Returns 0 on success or one of the following:
+ *  -EINVAL if any of the pins in the mask are not free, or if any pins are not
+ *  allowed to be set to the specified mode
+ */
+extern int msp_gpio_mode( msp_gpio_mode_t mode, uint32_t mask );
+
+/*
+ * Stops the specified GPIOs from blinking
+ */
+extern int msp_gpio_noblink( uint32_t mask );
+
+/*
+ * Configures GPIO(s) to blink
+ *  - mask shows which GPIOs to blink
+ *  - period is the time in 100ths of a second for the total period
+ *    (0 disables blinking)
+ *  - dcycle is the percentage of the period where the GPIO is HI
+ */
+int msp_gpio_blink( uint32_t mask, uint32_t period, uint32_t dcycle );
+
+/* Special bitflags and whatnot for the driver's convenience */
+#define GPIO_REG_COUNT		4
+const uint32_t GPIO_DATA_COUNT[] = { 2, 4, 4, 6 };
+const uint32_t GPIO_DATA_SHIFT[] = { 0, 2, 6, 10 };
+const uint32_t GPIO_DATA_MASK[]  = { 0x03, 0x0f, 0x0f, 0x3f };
+volatile uint32_t * const GPIO_DATA_REG[] = {
+	GPIO_DATA1_REG, GPIO_DATA2_REG, GPIO_DATA3_REG, GPIO_DATA4_REG,
+};
+const uint32_t GPIO_CFG_MASK[]   = { 0x0000ff, 0x00ffff, 0x00ffff, 0xffffff };
+volatile uint32_t * const GPIO_CFG_REG[] = {
+	GPIO_CFG1_REG, GPIO_CFG2_REG, GPIO_CFG3_REG, GPIO_CFG4_REG,
+};
+
+#define GPIO_CFG_SHIFT(i)	(i * 4)
+#define GPIO_CFG_PINMASK	0xf
+
+/* The extra-weird extended gpio register */
+
+#define EXTENDED_GPIO_COUNT	4
+#define EXTENDED_GPIO_SHIFT	16
+#define EXTENDED_GPIO_MASK	0x0f
+
+#define EXTENDED_GPIO_DATA_SHIFT(i)	(i * 2)
+#define EXTENDED_GPIO_DATA_MASK(i)	(0x3 << (i*2))
+#define EXTENDED_GPIO_DATA_SET		0x2
+#define EXTENDED_GPIO_DATA_CLR		0x1
+#define EXTENDED_GPIO_CFG_SHIFT(i)	((i * 2) + 16)
+#define EXTENDED_GPIO_CFG_MASK(i)	(0x3 << ((i*2)+16))
+#define EXTENDED_GPIO_CFG_DISABLE	0x2
+#define EXTENDED_GPIO_CFG_ENABLE	0x1
+
+#endif /* __KERNEL__ */
+
+#endif /* __MSP_GPIO_H__ */


[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux