/* * 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 . * * WonderMedia Technologies, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DRVNAME "wmtgpio" struct user_gpio { int gpio; int type; int value; }; struct kern_gpio { struct user_gpio user; struct list_head list; }; struct gpio_dev { int major; struct class *class; struct device *dev; struct mutex lock; struct list_head gpio_list; }; enum { GPIO_DIRECTION_OUT = 0, GPIO_DIRECTION_IN, }; static struct gpio_dev *gpio_device; static int gpio_dev_open(struct inode *inode, struct file *filp) { filp->private_data = gpio_device; return 0; } static int gpio_dev_release(struct inode *inode, struct file *filp) { return 0; } static int register_gpio(struct gpio_dev *gd, struct user_gpio *u) { struct kern_gpio *k; struct list_head *node, *next; int rc; list_for_each_safe(node, next, &gd->gpio_list) { k = container_of(node, struct kern_gpio, list); if (k->user.gpio == u->gpio) goto requested; } rc = gpio_request(u->gpio, "user gpio"); if (rc) { pr_err("gpio%d requested failed\n", u->gpio); return rc; } k = kzalloc(sizeof(*k), GFP_KERNEL); if (!k) { gpio_free(u->gpio); return -ENOMEM; } list_add_tail(&k->list, &gd->gpio_list); requested: k->user = *u; if (u->type == GPIO_DIRECTION_OUT) { //output gpio_direction_output(k->user.gpio, k->user.value); printk(KERN_DEBUG "gpio%d direction output %d\n", k->user.gpio, k->user.value); return 0; } else if (u->type == GPIO_DIRECTION_IN) { //input rc = gpio_direction_input(k->user.gpio); rc = __gpio_get_value(k->user.gpio); return rc; } return -EINVAL; } static ssize_t gpio_dev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct gpio_dev *gd = filp->private_data; struct user_gpio ug; int rc = 0; if (count != sizeof(ug)) return -EINVAL; if (copy_from_user(&ug, buf, count)) { return -EFAULT; } mutex_lock(&gd->lock); rc = register_gpio(gd, &ug); if (!rc) { rc = count; } mutex_unlock(&gd->lock); return rc; } static ssize_t gpio_dev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct gpio_dev *gd = filp->private_data; struct user_gpio ug; int rc; if (copy_from_user(&ug, buf, sizeof(ug))) return -EFAULT; if (ug.type != GPIO_DIRECTION_IN) return -EINVAL; mutex_lock(&gd->lock); rc = register_gpio(gd, &ug); mutex_unlock(&gd->lock); if (rc == 0) copy_to_user(buf, "0:", sizeof("0:")); else if (rc == 1) copy_to_user(buf, "1:", sizeof("1:")); else copy_to_user(buf, "-1:", sizeof("-1:")); //error !! return count; } static struct file_operations gpio_fops = { .owner = THIS_MODULE, .open = gpio_dev_open, .write = gpio_dev_write, .read = gpio_dev_read, .release = gpio_dev_release, }; static struct miscdevice gpio_miscdev = { .minor = MISC_DYNAMIC_MINOR, .name = DRVNAME, .fops = &gpio_fops, }; static int __gpio_init(void) { static char env[] = "wmt.gpio.pull"; uint8_t buf[128]; size_t l = sizeof(buf); uint8_t *p = buf; int gpio, pull; if (wmt_getsyspara(env, buf, &l)) return -EINVAL; for (;;) { int rc = sscanf(p, "%d:%d", &gpio, &pull); if (rc != 2) break; printk(" gpio %d, pull %d\n", gpio, pull); if (gpio_is_valid(gpio)) wmt_gpio_setpull(gpio, pull ? WMT_GPIO_PULL_UP : WMT_GPIO_PULL_DOWN); p = strchr(p, ','); if (!p) break; ++p; } return 0; } static int gpio_dev_probe(struct platform_device *pdev) { struct gpio_dev *gd; int ret; gd = kzalloc(sizeof(*gd), GFP_KERNEL); if (!gd) { dev_err(&pdev->dev, "no mem"); return -ENOMEM; } ret = misc_register(&gpio_miscdev); if (ret) { kfree(gd); return ret; } INIT_LIST_HEAD(&gd->gpio_list); mutex_init(&gd->lock); gpio_device = gd; __gpio_init(); return 0; } static int gpio_dev_remove(struct platform_device *pdev) { struct gpio_dev *gd = gpio_device; struct list_head *node, *next; struct kern_gpio *k; if (!gd) return 0; dev_info(&pdev->dev, "wmt gpio dev unregistered: %d\n", gd->major); device_destroy(gd->class, MKDEV(gd->major, 0)); class_destroy(gd->class); unregister_chrdev(gd->major, DRVNAME); list_for_each_safe(node, next, &gd->gpio_list) { k = container_of(node, struct kern_gpio, list); gpio_free(k->user.gpio); kfree(k); } kfree(gd); gpio_device = NULL; return 0; } static int gpio_dev_suspend(struct platform_device *pdev, pm_message_t state) { dev_info(&pdev->dev, "wmt gpio dev suspend\n"); return 0; } static int gpio_dev_resume(struct platform_device *pdev) { struct gpio_dev *gd = gpio_device; struct list_head *node, *next; struct kern_gpio *k; if (!gd) return 0; dev_info(&pdev->dev, "wmt gpio dev resume\n"); list_for_each_safe(node, next, &gd->gpio_list) { k = container_of(node, struct kern_gpio, list); printk(KERN_DEBUG "%s: gpio%d, type:%d, value %d\n", __func__, k->user.gpio, k->user.type, k->user.value); gpio_re_enabled(k->user.gpio); if (k->user.type == GPIO_DIRECTION_OUT) { //output gpio_direction_output(k->user.gpio, k->user.value); } else if (k->user.type == GPIO_DIRECTION_IN) { //input gpio_direction_input(k->user.gpio); } } return 0; } static struct platform_driver gpio_dev_driver = { .driver = { .name = DRVNAME, }, .probe = gpio_dev_probe, .remove = gpio_dev_remove, .suspend = gpio_dev_suspend, .resume = gpio_dev_resume, }; static struct platform_device *_pdev; static int gpio_dev_init(void) { int ret; _pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); if (IS_ERR(_pdev)) { pr_err(DRVNAME "unable to register platform device\n"); return PTR_ERR(_pdev); } ret = platform_driver_register(&gpio_dev_driver); if (ret) platform_device_unregister(_pdev); return ret; } static void gpio_dev_exit(void) { platform_driver_unregister(&gpio_dev_driver); platform_device_unregister(_pdev); } module_init(gpio_dev_init); module_exit(gpio_dev_exit); MODULE_AUTHOR("WonderMedia Technologies, Inc."); MODULE_DESCRIPTION("WMT [gpio device] driver"); MODULE_LICENSE("GPL");