blob: 4dd534777a724c37976d2a70cc9f34e94cad4e86 [file] [log] [blame]
/*
* FILE NAME au1000_gpio.c
*
* BRIEF MODULE DESCRIPTION
* Driver for Alchemy Au1000 GPIO.
*
* Author: MontaVista Software, Inc. <source@mvista.com>
* Steve Longerbeam <stevel@mvista.com>
*
* Copyright 2001 MontaVista Software 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/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/au1000.h>
#include <asm/au1000_gpio.h>
#define VERSION "0.01"
static const struct {
u32 active_hi;
u32 avail_mask;
} pinfunc_to_avail[15] = {
{1, 0x7<<16}, // 0 = SSI0 / GPIO[18:16]
{-1, 0}, // 1 = AC97 / SSI1
{1, 1<<19}, // 2 = IRDA / GPIO19
{1, 1<<20}, // 3 = UART0 / GPIO20
{1, 0x1f<<24}, // 4 = NIC2 / GPIO[28:24]
{1, 0x7<<29}, // 5 = I2S / GPIO[31:29]
{0, 1<<8}, // 6 = I2SDI / GPIO8
{0, 0x3f<<9}, // 7 = UART3 / GPIO[14:9]
{0, 1<<15}, // 8 = IRFIRSEL / GPIO15
{0, 1<<2}, // 9 = EXTCLK0 or OSC / GPIO2
{0, 1<<3}, // 10 = EXTCLK1 / GPIO3
{0, 1<<6}, // 11 = SMROMCKE / GPIO6
{1, 1<<21}, // 12 = UART1 / GPIO21
{1, 1<<22}, // 13 = UART2 / GPIO22
{1, 1<<23} // 14 = UART3 / GPIO23
};
u32 get_au1000_avail_gpio_mask(void)
{
int i;
u32 pinfunc = inl(SYS_PINFUNC);
u32 avail_mask = 0; // start with no gpio available
// first, check for GPIO's reprogrammed as peripheral pins
for (i=0; i<15; i++) {
if (pinfunc_to_avail[i].active_hi < 0)
continue;
if (!(pinfunc_to_avail[i].active_hi ^
((pinfunc & (1<<i)) ? 1:0)))
avail_mask |= pinfunc_to_avail[i].avail_mask;
}
// check for GPIO's used as interrupt sources
avail_mask &= ~(inl(IC1_MASKRD) &
(inl(IC1_CFG0RD) | inl(IC1_CFG1RD)));
#ifdef CONFIG_USB_OHCI
avail_mask &= ~((1<<4) | (1<<11));
#ifndef CONFIG_AU1X00_USB_DEVICE
avail_mask &= ~((1<<5) | (1<<13));
#endif
#endif
return avail_mask;
}
/*
* Tristate the requested GPIO pins specified in data.
* Only available GPIOs will be tristated.
*/
int au1000gpio_tristate(u32 data)
{
data &= get_au1000_avail_gpio_mask();
if (data)
outl(data, SYS_TRIOUTCLR);
return 0;
}
/*
* Return the pin state. Pins configured as outputs will return
* the output state, and pins configured as inputs (tri-stated)
* will return input pin state.
*/
int au1000gpio_in(u32 *data)
{
*data = inl(SYS_PINSTATERD);
return 0;
}
/*
* Set/clear GPIO pins. Only available GPIOs will be affected.
*/
int au1000gpio_set(u32 data)
{
data &= get_au1000_avail_gpio_mask();
if (data)
outl(data, SYS_OUTPUTSET);
return 0;
}
int au1000gpio_clear(u32 data)
{
data &= get_au1000_avail_gpio_mask();
if (data)
outl(data, SYS_OUTPUTCLR);
return 0;
}
/*
* Output data to GPIO pins. Only available GPIOs will be affected.
*/
int au1000gpio_out(u32 data)
{
au1000gpio_set(data);
au1000gpio_clear(~data);
return 0;
}
EXPORT_SYMBOL(get_au1000_avail_gpio_mask);
EXPORT_SYMBOL(au1000gpio_tristate);
EXPORT_SYMBOL(au1000gpio_in);
EXPORT_SYMBOL(au1000gpio_set);
EXPORT_SYMBOL(au1000gpio_clear);
EXPORT_SYMBOL(au1000gpio_out);
static int au1000gpio_open(struct inode *inode, struct file *file)
{
MOD_INC_USE_COUNT;
return 0;
}
static int au1000gpio_release(struct inode *inode, struct file *file)
{
MOD_DEC_USE_COUNT;
return 0;
}
static int au1000gpio_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int status;
u32 val;
switch(cmd) {
case AU1000GPIO_IN:
status = au1000gpio_in(&val);
if (status != 0)
return status;
return put_user(val, (u32 *)arg);
case AU1000GPIO_OUT:
if (get_user(val, (u32 *)arg))
return -EFAULT;
return au1000gpio_out(val);
case AU1000GPIO_SET:
if (get_user(val, (u32 *)arg))
return -EFAULT;
return au1000gpio_set(val);
case AU1000GPIO_CLEAR:
if (get_user(val, (u32 *)arg))
return -EFAULT;
return au1000gpio_clear(val);
case AU1000GPIO_TRISTATE:
if (get_user(val, (u32 *)arg))
return -EFAULT;
return au1000gpio_tristate(val);
case AU1000GPIO_AVAIL_MASK:
return put_user(get_au1000_avail_gpio_mask(),
(u32 *)arg);
default:
return -ENOIOCTLCMD;
}
return 0;
}
static struct file_operations au1000gpio_fops =
{
owner: THIS_MODULE,
ioctl: au1000gpio_ioctl,
open: au1000gpio_open,
release: au1000gpio_release,
};
static struct miscdevice au1000gpio_miscdev =
{
GPIO_MINOR,
"au1000_gpio",
&au1000gpio_fops
};
int __init au1000gpio_init(void)
{
misc_register(&au1000gpio_miscdev);
printk("Au1000 gpio driver, version %s\n", VERSION);
return 0;
}
void __exit au1000gpio_exit(void)
{
misc_deregister(&au1000gpio_miscdev);
}
module_init(au1000gpio_init);
module_exit(au1000gpio_exit);