diff options
Diffstat (limited to 'drivers/char/wmt-gpio.c')
-rwxr-xr-x | drivers/char/wmt-gpio.c | 501 |
1 files changed, 501 insertions, 0 deletions
diff --git a/drivers/char/wmt-gpio.c b/drivers/char/wmt-gpio.c new file mode 100755 index 00000000..42bead6f --- /dev/null +++ b/drivers/char/wmt-gpio.c @@ -0,0 +1,501 @@ +/*++ +linux/drivers/char/wmt-gpio.c + +Copyright (c) 2008 WonderMedia Technologies, 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 program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. +You should have received a copy of the GNU General Public License along with +this program. If not, see <http://www.gnu.org/licenses/>. + +WonderMedia Technologies, Inc. +10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. +--*/ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/errno.h> +#include <asm/uaccess.h> +#include <linux/kdev_t.h> +#include <linux/cdev.h> +#include <linux/proc_fs.h> +#include <linux/jiffies.h> +#include <linux/major.h> +#include <linux/delay.h> +#include <linux/sysctl.h> +#include <linux/sysctl.h> + +#define WMT_GPOI_DEV_C +#include "wmt-gpio-dev.h" + +#define GPIO_DEVICE_NAME "wmtgpio" +static int gpio_dev_major; +static struct cdev *gpio_cdev = NULL; +struct class *class_gpio_dev; +struct device *gpio_device; +struct gpio_list *g_gpio_list = NULL; +struct gpio_list *g_list_now = NULL; + + +#define GPIO_PHY_BASE_ADDR (GPIO_BASE_ADDR-WMT_MMAP_OFFSET) + +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +DEFINE_SEMAPHORE(gpio_dev_lock); + +static int gpio_dev_open +( + struct inode *inode, /*!<; //[IN] a pointer point to struct inode */ + struct file *filp /*!<; //[IN] a pointer point to struct file */ +) +{ + //printk("gpio_dev_open\n"); + return 0; +} + + +static int gpio_dev_release +( + struct inode *inode, /*!<; //[IN] a pointer point to struct inode */ + struct file *filp /*!<; //[IN] a pointer point to struct file */ +) +{ + return 0; +} + +static int init_gpio_list(struct gpio_list *list) +{ + list->addr = 0; + list->bitmap = 0; + list->regval = 0; + list->prev = NULL; + list->next = NULL; + + return 0; +} +/* +static int tellme_gpio_list(void) +{ + struct gpio_list *list; + int i = 0; + + if (g_gpio_list == NULL) { + printk("there is no gpio list\n"); + return 0; + } + list = g_gpio_list; + while(1) { + printk("list%d : \n",i); + printk("address = 0x%x , bitmap = 0x%x\n", list->addr, list->bitmap); + list = list->next; + if (list == NULL) + break; + } + return 0; +} +*/ +static int free_gpio_list(void) +{ + struct gpio_list *prev, *now; + + prev = g_gpio_list->prev; + now = g_gpio_list; + while(1) { + kfree(now); + now = prev; + if (now == NULL) + break; + prev = now->prev; + } + g_gpio_list = NULL; + g_list_now = NULL; + + return 0; +} + +static struct gpio_list * search_gpio_list(struct wmt_reg_op_t *reg) +{ + struct gpio_list *list; + + if (g_gpio_list == NULL) { + printk("there is no gpio list\n"); + return NULL; + } + list = g_gpio_list; + while(1) { + //printk("search_gpio_list list = 0x%x , reg->addr = 0x%x",list->addr,reg->addr); + if (list->addr == reg->addr) + return list; + if (list->next == NULL) + break; + list = list->next; + }; + return NULL; +} + +static int add_gpio_list(struct gpio_list *new, struct gpio_list *head) +{ + new->prev = head; + head->next = new; + g_list_now = new; + + return 0; +} + +static int write_gpio_list(struct wmt_reg_op_t *reg) +{ + struct gpio_list *list; + + if ((reg->addr&0xffff0000) == GPIO_PHY_BASE_ADDR) + reg->addr = (reg->addr+WMT_MMAP_OFFSET); + + if (g_gpio_list == NULL) { + g_gpio_list = (struct gpio_list *) kmalloc(sizeof(struct gpio_list), GFP_KERNEL); + if (g_gpio_list == NULL) { + printk("error : alloc gpio list failed\n"); + return -1; + } + init_gpio_list(g_gpio_list); + list = g_gpio_list; + g_list_now = g_gpio_list; + } else { + list = search_gpio_list(reg); + if (list == NULL) { + list = (struct gpio_list *) kmalloc(sizeof(struct gpio_list), GFP_KERNEL); + if (list == NULL) { + printk("error : alloc gpio list failed\n"); + return -1; + } + init_gpio_list(list); + add_gpio_list(list, g_list_now); + } + } + + list->addr = reg->addr; + list->bitmap |= reg->bitmap; + + return 0; +} + +static int write_wmt_reg(struct wmt_reg_op_t *regop) +{ + unsigned int tmp; + + if ((regop->addr == 0) || (regop->bitmap == 0)) { + printk("gpio device error : address = %x , value = 0x%x\n",regop->addr,regop->bitmap); + return -1; + } + if (write_gpio_list(regop)) + return -1; +/* + printk("w address = 0x%x\n",regop->addr); + printk("w reg->bitmap = 0x%x\n",regop->bitmap); + printk("w reg->regval = 0x%x\n",regop->regval); +*/ + regop->regval &= regop->bitmap; + tmp = REG32_VAL(regop->addr); + tmp &= ~(regop->bitmap); + tmp |= regop->regval; + REG32_VAL(regop->addr) = tmp; + + return 0; +} + +static int read_wmt_reg(struct wmt_reg_op_t *regop) +{ + unsigned int tmp; + + if ((regop->addr == 0) || (regop->bitmap == 0)) { + printk("gpio device error : address = %x , value = 0x%x\n",regop->addr,regop->bitmap); + return -1; + } +/* + printk("r address = 0x%x\n",regop->addr); + printk("r reg->bitmap = %d\n",regop->bitmap); + printk("r reg->regval = %d\n",regop->regval); +*/ + + if ((regop->addr&0xffff0000) == GPIO_PHY_BASE_ADDR) + regop->addr = (regop->addr+WMT_MMAP_OFFSET); + + tmp = REG32_VAL(regop->addr); + tmp &= regop->bitmap; + regop->regval = tmp; + + return 0; +} + +/* +extern void wmt_drop_pagecache(void); +extern void wmt_drop_slab(void); +*/ + +static long gpio_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int err = 0; + int retval = 0; + struct gpio_cfg_t cfg; + struct wmt_reg_op_t regop; + //unsigned int drop_caches; + + /* check type and number, if fail return ENOTTY */ + if( _IOC_TYPE(cmd) != GPIO_IOC_MAGIC ) return -ENOTTY; + if( _IOC_NR(cmd) >= GPIO_IOC_MAXNR ) return -ENOTTY; + + /* check argument area */ + if( _IOC_DIR(cmd) & _IOC_READ ) { + err = !access_ok( VERIFY_WRITE, (void __user *) arg, _IOC_SIZE(cmd)); + if (err) + printk("VERIFY_WRITE failed"); + } + else if ( _IOC_DIR(cmd) & _IOC_WRITE ) { + err = !access_ok( VERIFY_READ, (void __user *) arg, _IOC_SIZE(cmd)); + if (err) + printk("VERIFY_READ failed"); + } + + if( err ) return -EFAULT; + + down(&gpio_dev_lock); + + switch (cmd) { + case GPIOCFG: + if (copy_from_user(&cfg, (void *) arg, sizeof(struct gpio_cfg_t))) + return -EFAULT; + if (write_wmt_reg(&cfg.ctl)) return -EFAULT; + if (write_wmt_reg(&cfg.oc)) return -EFAULT; + break; + case GPIOWREG: + if (copy_from_user(®op, (void *) arg, sizeof(struct wmt_reg_op_t))) + return -EFAULT; + if (write_wmt_reg(®op)) return -EFAULT; + break; + case GPIORREG: + if (copy_from_user(®op, (void *) arg, sizeof(struct wmt_reg_op_t))) + return -EFAULT; + + if (read_wmt_reg(®op)) return -EFAULT; + + if (copy_to_user((void *)arg, (void *) ®op, sizeof(struct wmt_reg_op_t))) + return -EFAULT; + break; +/* + case FREESYSCACHES: + drop_caches = (unsigned int)arg; + //printk("FREESYSCACHES , drop_caches = %d\n",drop_caches); + if (drop_caches & 1) + wmt_drop_pagecache(); + if (drop_caches & 2) + wmt_drop_slab(); + break; +*/ + default: + retval = -ENOTTY; + break; + } + + up(&gpio_dev_lock); + + return retval; +} + +/*!************************************************************************* + driver file operations struct define +****************************************************************************/ +struct file_operations gpio_dev_fops = { + .owner = THIS_MODULE, + .open = gpio_dev_open, + .unlocked_ioctl = gpio_dev_ioctl, + .release = gpio_dev_release, +}; + +static int gpio_dev_probe +( + struct platform_device *pdev /*!<; // please add parameters description her*/ +) +{ + return 0; +} + + +static int gpio_dev_remove +( + struct platform_device *dev /*!<; // please add parameters description her*/ +) +{ + if (gpio_cdev) + cdev_del(gpio_cdev); + + return 0; +} + + +static int gpio_dev_suspend +( + struct platform_device *pdev, /*!<; // a pointer point to struct device */ + pm_message_t state /*!<; // suspend state */ +) +{ + struct gpio_list *list; + //int i = 0; + + if (g_gpio_list == NULL) { + printk("there is no gpio list\n"); + return 0; + } + list = g_gpio_list; + while(1) { + //printk("list%d : \n",i); + //printk("address = 0x%x , bitmap = 0x%x , raw = 0x%x\n", list->addr, list->bitmap,REG32_VAL(list->addr)); + list->regval = REG32_VAL(list->addr)&list->bitmap; + //printk("regval = 0x%x\n", list->regval); + list = list->next; + if (list == NULL) + break; + //i++; + } + + return 0; +} + + +static int gpio_dev_resume +( + struct platform_device *pdev /*!<; // a pointer point to struct device */ +) +{ + + struct gpio_list *list; + int tmp = 0; + //int i = 0; + + if (g_gpio_list == NULL) { + printk("there is no gpio list\n"); + return 0; + } + list = g_gpio_list; + while(1) { + //printk("list%d : \n",i); + //printk("address = 0x%x , bitmap = 0x%x , raw = 0x%x\n", list->addr, list->bitmap,REG32_VAL(list->addr)); + //printk("regval = 0x%x\n", list->regval); + tmp = REG32_VAL(list->addr); + tmp &= ~list->bitmap; + tmp |= list->regval; + REG32_VAL(list->addr) = tmp; + //printk("raw = 0x%x\n",REG32_VAL(list->addr)); + list = list->next; + if (list == NULL) + break; + //i++; + } + + return 0; +} + +/*!************************************************************************* + device driver struct define +****************************************************************************/ +static struct platform_driver gpio_dev_driver = { + .driver.name = "wmtgpio", // This name should equal to platform device name. + .probe = gpio_dev_probe, + .remove = gpio_dev_remove, + .suspend = gpio_dev_suspend, + .resume = gpio_dev_resume +}; + + +static void gpio_dev_platform_release +( + struct device *device /*!<; // please add parameters description her*/ +) +{ +} + +/*!************************************************************************* + platform device struct define +****************************************************************************/ + + +static struct platform_device gpio_dev_device = { + .name = "wmtgpio", + .id = 0, + .dev = { .release = gpio_dev_platform_release, + }, +}; + + +static int gpio_dev_init(void) +{ + int ret; + dev_t dev_no; + + gpio_dev_major = register_chrdev (0, GPIO_DEVICE_NAME, &gpio_dev_fops); + if (gpio_dev_major < 0) { + printk("get gpio_dev_major failed\n"); + return -EFAULT; + } + + printk("mknod /dev/%s c %d 0\n", GPIO_DEVICE_NAME, gpio_dev_major); + + class_gpio_dev = class_create(THIS_MODULE, GPIO_DEVICE_NAME); + if (IS_ERR(class_gpio_dev)) { + ret = PTR_ERR(class_gpio_dev); + printk(KERN_ERR "Can't create class : %s !!\n",GPIO_DEVICE_NAME); + return ret; + } + + dev_no = MKDEV(gpio_dev_major, 0); + gpio_device = device_create(class_gpio_dev, NULL, dev_no, NULL, GPIO_DEVICE_NAME); + if (IS_ERR(gpio_device)) { + ret = PTR_ERR(gpio_device); + printk(KERN_ERR "Failed to create device %s !!!",GPIO_DEVICE_NAME); + return ret; + } + + ret = platform_device_register(&gpio_dev_device); + if (ret != 0) + return -ENODEV; + + ret = platform_driver_register(&gpio_dev_driver); + if (ret != 0) { + platform_device_unregister(&gpio_dev_device); + return -ENODEV; + } + + return ret; +} + +module_init(gpio_dev_init); + + +static void gpio_dev_exit(void) +{ + dev_t dev_no; + + free_gpio_list(); + platform_driver_unregister(&gpio_dev_driver); + platform_device_unregister(&gpio_dev_device); + dev_no = MKDEV(gpio_dev_major, 0); + device_destroy(class_gpio_dev, dev_no); + class_destroy(class_gpio_dev); + unregister_chrdev(gpio_dev_major, GPIO_DEVICE_NAME); + + return; +} + +module_exit(gpio_dev_exit); + +MODULE_AUTHOR("WonderMedia Technologies, Inc."); +MODULE_DESCRIPTION("WMT [gpio device] driver"); +MODULE_LICENSE("GPL"); + +#undef WMT_GPOI_DEV_C + |