--- /dev/null +++ b/drivers/char/gemini_gpio_dev.c @@ -0,0 +1,356 @@ +/* + * GPIO driver for Gemini board + * Provides /dev/gpio + */ + +#include +#include +#include +#include +#include +#include +#include +#include /* copy_to_user, copy_from_user */ + +#include +#include +#include +#include +#include + +#define GEMINI_GPIO_BASE1 IO_ADDRESS(SL2312_GPIO_BASE) +#define GEMINI_GPIO_BASE2 IO_ADDRESS(SL2312_GPIO_BASE1) + +#define GPIO_SET 2 +#define MAX_GPIO_LINE 32*GPIO_SET + +wait_queue_head_t gemini_gpio_wait[MAX_GPIO_LINE]; + +enum GPIO_REG +{ + GPIO_DATA_OUT = 0x00, + GPIO_DATA_IN = 0x04, + GPIO_PIN_DIR = 0x08, + GPIO_BY_PASS = 0x0C, + GPIO_DATA_SET = 0x10, + GPIO_DATA_CLEAR = 0x14, + GPIO_PULL_ENABLE = 0x18, + GPIO_PULL_TYPE = 0x1C, + GPIO_INT_ENABLE = 0x20, + GPIO_INT_RAW_STATUS = 0x24, + GPIO_INT_MASK_STATUS = 0x28, + GPIO_INT_MASK = 0x2C, + GPIO_INT_CLEAR = 0x30, + GPIO_INT_TRIG = 0x34, + GPIO_INT_BOTH = 0x38, + GPIO_INT_POLAR = 0x3C +}; + +unsigned int regist_gpio_int0=0,regist_gpio_int1=0; + +/* defines a specific GPIO bit number and state */ +struct gpio_bit { + unsigned char bit; + unsigned char state; +}; + +#define GPIO_MAJOR 10 +#define GPIO_MINOR 127 + +/* + * ioctl calls that are permitted to the /dev/gpio interface + */ +#define GPIO_GET_BIT 0x0000001 +#define GPIO_SET_BIT 0x0000002 +#define GPIO_GET_CONFIG 0x0000003 +#define GPIO_SET_CONFIG 0x0000004 + +//#define GPIO_CONFIG_OUT 1 +//#define GPIO_CONFIG_IN 2 + + + +#define DEVICE_NAME "gpio" + +//#define DEBUG + +/* + * GPIO interface + */ + +/* /dev/gpio */ +static int gpio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +/* /proc/driver/gpio */ +static int gpio_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data); + +static unsigned char gpio_status; /* bitmapped status byte. */ + +/* functions for set/get gpio lines on storlink cpu */ + +void gpio_line_get(unsigned char pin, u32 * data) +{ + unsigned int set = pin >>5; // each GPIO set has 32 pins + unsigned int status,addr; + + addr = (set ? GEMINI_GPIO_BASE2:GEMINI_GPIO_BASE1) + GPIO_DATA_IN; + status = readl(addr); +#ifdef DEBUG + printk("status = %08X, pin = %d, set = %d\n", status, pin, set); +#endif + if (set) + *data = (status&(1<<(pin-32)))?1:0; + else + *data = (status&(1<>5; // each GPIO set has 32 pins + unsigned int status=0,addr; + + addr = (set ? GEMINI_GPIO_BASE2:GEMINI_GPIO_BASE1)+(high?GPIO_DATA_SET:GPIO_DATA_CLEAR); + + status &= ~(1 << (pin %32)); + status |= (1 << (pin % 32)); + writel(status,addr); +} + +/* + * pin = [0..63] + * mode = + * 1 -- OUT + * 2 -- IN + */ +void gpio_line_config(unsigned char pin, unsigned char mode) +{ + unsigned char set = pin >>5; // each GPIO set has 32 pins + unsigned int status,addr; + + addr = (set ? GEMINI_GPIO_BASE2:GEMINI_GPIO_BASE1)+GPIO_PIN_DIR; + status = readl(addr); + + status &= ~(1 << (pin %32)); + if (mode == 1) + status |= (1 << (pin % 32)); /* PinDir: 0 - input, 1 - output */ + + writel(status,addr); +#if 0 + /* enable pullup-high if mode is input */ + + addr = (set ? GEMINI_GPIO_BASE2:GEMINI_GPIO_BASE1)+GPIO_PULL_ENABLE; + status = readl(addr); + + status &= ~(1 << (pin %32)); + if (mode == 2) /* input */ + status |= (1 << (pin % 32)); /* PullEnable: 0 - disable, 1 - enable */ + + writel(status,addr); + + addr = (set ? GEMINI_GPIO_BASE2:GEMINI_GPIO_BASE1)+GPIO_PULL_TYPE; + status = readl(addr); + + status &= ~(1 << (pin %32)); + if (mode == 2) /* input */ + status |= (1 << (pin % 32)); /* PullType: 0 - low, 1 - high */ + + writel(status,addr); +#endif +} + +#define GPIO_IS_OPEN 0x01 /* means /dev/gpio is in use */ + +/* + * Now all the various file operations that we export. + */ +static int gpio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct gpio_bit bit; + u32 val; + + if (copy_from_user(&bit, (struct gpio_bit *)arg, + sizeof(bit))) + return -EFAULT; + + switch (cmd) { + + case GPIO_GET_BIT: + gpio_line_get(bit.bit, &val); + bit.state = val; + return copy_to_user((void *)arg, &bit, sizeof(bit)) ? -EFAULT : 0; + case GPIO_SET_BIT: + val = bit.state; + gpio_line_set(bit.bit, val); + return 0; + case GPIO_GET_CONFIG: + // gpio_line_config(bit.bit, bit.state); + return copy_to_user((void *)arg, &bit, sizeof(bit)) ? -EFAULT : 0; + case GPIO_SET_CONFIG: + val = bit.state; + gpio_line_config(bit.bit, bit.state); + return 0; + } + return -EINVAL; +} + + +static int gpio_open(struct inode *inode, struct file *file) +{ + if (gpio_status & GPIO_IS_OPEN) + return -EBUSY; + + gpio_status |= GPIO_IS_OPEN; + return 0; +} + + +static int gpio_release(struct inode *inode, struct file *file) +{ + /* + * Turn off all interrupts once the device is no longer + * in use and clear the data. + */ + + gpio_status &= ~GPIO_IS_OPEN; + return 0; +} + + +/* + * The various file operations we support. + */ + +static struct file_operations gpio_fops = { + .owner = THIS_MODULE, + .ioctl = gpio_ioctl, + .open = gpio_open, + .release = gpio_release, +}; + +static struct miscdevice gpio_dev = +{ + .minor = GPIO_MINOR, + .name = "gpio", + .fops = &gpio_fops, +}; + + + + +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *dir; + +/* + * Info exported via "/proc/driver/gpio". + */ +static int gpio_get_status(char *buf) +{ + char *p = buf; + u32 val = 0; + int i; + int bit; +#ifdef DEBUG + u32 addr; + + for (i = 0; i < 0x20; i+=4 ) { + addr = IO_ADDRESS(SL2312_GPIO_BASE) + i; + val = readl(addr); + p+=sprintf(p, "GPIO0: 0x%02X: %08X\n", i, val ); + } + for (i = 0; i < 0x20; i+=4 ) { + addr = IO_ADDRESS(SL2312_GPIO_BASE1) + i; + val = readl(addr); + p+=sprintf(p, "GPIO1: 0x%02X: %08X\n", i, val ); + } +#endif + + for (i = 0; i < 32; i++) { + gpio_line_get(i, &bit); + if (bit) + val |= (1 << i); + } + p += sprintf(p, "gpio0\t: 0x%08x\n", val); + + val = 0; + for (i = 32; i < 64; i++) { + gpio_line_get(i, &bit); + if (bit) + val |= (1 << i); + } + p += sprintf(p, "gpio1\t: 0x%08x\n", val); + + return p - buf; +} + + +/* /proc/driver/gpio read op + */ +static int gpio_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = gpio_get_status (page); + + if (len <= off+count) + *eof = 1; + *start = page + off; + len -= off; + if ( len > count ) + len = count; + if ( len < 0 ) + len = 0; + return len; +} +#endif /* CONFIG_PROC_FS */ + + +static int __init gpio_init_module(void) +{ + int retval; +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *res; +#endif + + /* register /dev/gpio file ops */ + //retval = register_chrdev(GPIO_MAJOR, DEVICE_NAME, &gpio_fops); + retval = misc_register(&gpio_dev); + if(retval < 0) + return retval; + +#ifdef CONFIG_PROC_FS + dir = proc_mkdir("driver/gpio", NULL); + if (!dir) { + misc_deregister(&gpio_dev); + return -ENOMEM; + } + /* register /proc/driver/gpio */ + res = create_proc_entry("info", 0644, dir); + if (res) { + res->read_proc= gpio_read_proc; + } else { + misc_deregister(&gpio_dev); + return -ENOMEM; + } +#endif + + printk("%s: GPIO driver loaded\n", __FILE__); + + return 0; +} + +static void __exit gpio_cleanup_module(void) +{ + remove_proc_entry ("info", dir); + misc_deregister(&gpio_dev); + + printk("%s: GPIO driver unloaded\n", __FILE__); +} + +module_init(gpio_init_module); +module_exit(gpio_cleanup_module); + +MODULE_AUTHOR("Jonas Majauskas"); +MODULE_LICENSE("GPL"); + --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -1064,5 +1064,12 @@ source "drivers/s390/char/Kconfig" +config GEMINI_GPIO_DEV + tristate "GPIO driver for Gemini board (provides /dev/gpio)" + depends on ARCH_SL2312 + default n + help + GPIO driver for Gemini boards - SL3512, SL3516. + endmenu --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -115,6 +115,7 @@ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o obj-$(CONFIG_TCG_TPM) += tpm/ +obj-$(CONFIG_GEMINI_GPIO_DEV) += gemini_gpio_dev.o obj-$(CONFIG_PS3_FLASH) += ps3flash.o