/* driver/misc/sunxi-reg.c * * Copyright (C) 2011 Reuuimlla Technology Co.Ltd * Charles * * www.reuuimllatech.com * * User access to the registers driver. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../base/base.h" #define PINS_PER_BANK 32 DECLARE_RWSEM(gpio_sw_list_lock); LIST_HEAD(gpio_sw_list); static struct class *gpio_sw_class; static int network_led_data_suspend; struct sw_gpio_pd { char name[16]; char link[16]; unsigned int light; }; struct gpio_sw_classdev { const char *name; unsigned int pull, drv, cfg; struct mutex class_mutex; struct gpio_config *item; int (*gpio_sw_cfg_set) (struct gpio_sw_classdev *gpio_sw_cdev, int mul_cfg); int (*gpio_sw_pull_set) (struct gpio_sw_classdev *gpio_sw_cdev, int mul_cfg); int (*gpio_sw_data_set) (struct gpio_sw_classdev *gpio_sw_cdev, int mul_cfg); int (*gpio_sw_drv_set) (struct gpio_sw_classdev *gpio_sw_cdev, int mul_cfg); int (*gpio_sw_cfg_get) (struct gpio_sw_classdev *gpio_sw_cdev); int (*gpio_sw_pull_get) (struct gpio_sw_classdev *gpio_sw_cdev); int (*gpio_sw_data_get) (struct gpio_sw_classdev *gpio_sw_cdev); int (*gpio_sw_drv_get) (struct gpio_sw_classdev *gpio_sw_cdev); struct device *dev; struct list_head node; }; struct sw_gpio { struct sw_gpio_pd *pdata; spinlock_t lock; struct gpio_sw_classdev class; }; static struct platform_device *gpio_sw_dev[256]; static struct sw_gpio_pd *sw_pdata[256]; struct device_node *node; static unsigned int easy_light_used; /* * mul_cfg: 0 - inpit * 1 - output */ static int gpio_sw_cfg_set(struct gpio_sw_classdev *gpio_sw_cdev, int mul_cfg) { char pin_name[32]; unsigned long config; if (mul_cfg == 0) gpio_direction_input(gpio_sw_cdev->item->gpio); else if (mul_cfg == 1) gpio_direction_output(gpio_sw_cdev->item->gpio, 0); else if (mul_cfg > 1 && mul_cfg <= 7) { sunxi_gpio_to_name(gpio_sw_cdev->item->gpio, pin_name); config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, mul_cfg); if (gpio_sw_cdev->item->gpio < SUNXI_PL_BASE) pin_config_set(SUNXI_PINCTRL, pin_name, config); else pin_config_set(SUNXI_R_PINCTRL, pin_name, config); } gpio_sw_cdev->cfg = mul_cfg; return 0; } static int gpio_sw_cfg_get(struct gpio_sw_classdev *gpio_sw_cdev) { return 0; } static int gpio_sw_pull_set(struct gpio_sw_classdev *gpio_sw_cdev, int pull) { char pin_name[32]; unsigned long config; if (pull >= 0 && pull <= 3) { sunxi_gpio_to_name(gpio_sw_cdev->item->gpio, pin_name); config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, pull); if (gpio_sw_cdev->item->gpio < SUNXI_PL_BASE) pin_config_set(SUNXI_PINCTRL, pin_name, config); else pin_config_set(SUNXI_R_PINCTRL, pin_name, config); } gpio_sw_cdev->pull = pull; return 0; } static int gpio_sw_pull_get(struct gpio_sw_classdev *gpio_sw_cdev) { return 0; } static int gpio_sw_drv_set(struct gpio_sw_classdev *gpio_sw_cdev, int drv) { char pin_name[32]; unsigned long config; if (drv >= 0 && drv <= 3) { sunxi_gpio_to_name(gpio_sw_cdev->item->gpio, pin_name); config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DRV, drv); if (gpio_sw_cdev->item->gpio < SUNXI_PL_BASE) pin_config_set(SUNXI_PINCTRL, pin_name, config); else pin_config_set(SUNXI_R_PINCTRL, pin_name, config); } gpio_sw_cdev->drv = drv; return 0; } static int gpio_sw_drv_get(struct gpio_sw_classdev *gpio_sw_cdev) { return 0; } static int gpio_sw_data_set(struct gpio_sw_classdev *gpio_sw_cdev, int data) { __gpio_set_value(gpio_sw_cdev->item->gpio, data); return 0; } static int gpio_sw_data_get(struct gpio_sw_classdev *gpio_sw_cdev) { return __gpio_get_value(gpio_sw_cdev->item->gpio); } static ssize_t cfg_sel_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev); int length; mutex_lock(&gpio_sw_cdev->class_mutex); gpio_sw_cdev->item->mul_sel = gpio_sw_cdev->gpio_sw_drv_get(gpio_sw_cdev); length = sprintf(buf, "%u\n", gpio_sw_cdev->cfg); mutex_unlock(&gpio_sw_cdev->class_mutex); return length; } static ssize_t pull_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev); int length; mutex_lock(&gpio_sw_cdev->class_mutex); gpio_sw_cdev->item->pull = gpio_sw_cdev->gpio_sw_drv_get(gpio_sw_cdev); length = sprintf(buf, "%u\n", gpio_sw_cdev->pull); mutex_unlock(&gpio_sw_cdev->class_mutex); return length; } static ssize_t drv_level_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev); int length; mutex_lock(&gpio_sw_cdev->class_mutex); gpio_sw_cdev->item->drv_level = gpio_sw_cdev->gpio_sw_drv_get(gpio_sw_cdev); length = sprintf(buf, "%u\n", gpio_sw_cdev->drv); mutex_unlock(&gpio_sw_cdev->class_mutex); return length; } static ssize_t data_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev); int length; mutex_lock(&gpio_sw_cdev->class_mutex); gpio_sw_cdev->item->data = gpio_sw_cdev->gpio_sw_data_get(gpio_sw_cdev); length = sprintf(buf, "%u\n", gpio_sw_cdev->item->data); mutex_unlock(&gpio_sw_cdev->class_mutex); return length; } static ssize_t cfg_sel_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev); unsigned long cfg; if (kstrtoul(buf, 10, &cfg)) return -EINVAL; if (cfg > 7) { return size; } gpio_sw_cdev->gpio_sw_cfg_set(gpio_sw_cdev, cfg); return size; } static ssize_t pull_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev); unsigned long pull; if (kstrtoul(buf, 10, &pull)) return -EINVAL; if (pull > 3) { return size; } gpio_sw_cdev->gpio_sw_pull_set(gpio_sw_cdev, pull); return size; } static ssize_t drv_level_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev); unsigned long drv_level; if (kstrtoul(buf, 10, &drv_level)) return -EINVAL; if (drv_level > 3) { return size; } gpio_sw_cdev->gpio_sw_drv_set(gpio_sw_cdev, drv_level); return size; } static ssize_t data_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev); unsigned long data; if (kstrtoul(buf, 10, &data)) return -EINVAL; gpio_sw_cdev->gpio_sw_data_set(gpio_sw_cdev, data); return size; } static ssize_t light_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev); struct sw_gpio_pd *pdata = dev->parent->platform_data; unsigned long data; if (kstrtoul(buf, 10, &data)) return -EINVAL; if (data) gpio_sw_cdev->gpio_sw_data_set(gpio_sw_cdev, pdata->light ? 1 : 0); else gpio_sw_cdev->gpio_sw_data_set(gpio_sw_cdev, pdata->light ? 0 : 1); return size; } static int gpio_sw_suspend(struct device *dev, pm_message_t state) { struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev); struct sw_gpio_pd *pdata = dev->parent->platform_data; if (strcmp(pdata->link, "network_led") == 0) { network_led_data_suspend = gpio_sw_cdev->gpio_sw_data_get(\ gpio_sw_cdev); gpio_sw_cdev->gpio_sw_data_set(gpio_sw_cdev, \ pdata->light ? 0 : 1); } return 0; } static int gpio_sw_resume(struct device *dev) { struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev); struct sw_gpio_pd *pdata = dev->parent->platform_data; if (strcmp(pdata->link, "network_led") == 0) { gpio_sw_cdev->gpio_sw_data_set(gpio_sw_cdev, network_led_data_suspend); } return 0; } static struct device_attribute gpio_sw_class_attrs[] = { __ATTR(cfg, 0664, cfg_sel_show, cfg_sel_store), __ATTR(pull, 0664, pull_show, pull_store), __ATTR(drv, 0664, drv_level_show, drv_level_store), __ATTR(data, 0664, data_show, data_store), __ATTR_NULL, }; static struct device_attribute easy_light_attr = __ATTR(light, 0664, data_show, light_store); void gpio_sw_classdev_unregister(struct gpio_sw_classdev *gpio_sw_cdev) { mutex_destroy(&gpio_sw_cdev->class_mutex); device_unregister(gpio_sw_cdev->dev); down_write(&gpio_sw_list_lock); list_del(&gpio_sw_cdev->node); up_write(&gpio_sw_list_lock); } static int gpio_sw_remove(struct platform_device *dev) { struct sw_gpio *sw_gpio_entry = platform_get_drvdata(dev); struct sw_gpio_pd *pdata = dev->dev.platform_data; if (strlen(pdata->link) != 0) sysfs_remove_link(&gpio_sw_class->p->subsys.kobj, pdata->link); gpio_sw_classdev_unregister(&sw_gpio_entry->class); kfree(sw_gpio_entry->class.item); kfree(sw_gpio_entry); return 0; } int gpio_sw_classdev_register(struct device *parent, struct gpio_sw_classdev *gpio_sw_cdev) { struct sw_gpio_pd *pdata = parent->platform_data; gpio_sw_cdev->dev = device_create(gpio_sw_class, parent, 0, gpio_sw_cdev, "%s", gpio_sw_cdev->name); if (IS_ERR(gpio_sw_cdev->dev)) return PTR_ERR(gpio_sw_cdev->dev); if (easy_light_used && strlen(pdata->link)) { if (sysfs_create_file(&gpio_sw_cdev->dev->kobj, &easy_light_attr.attr)) pr_err("gpio_sw: sysfs_create_file fail\n"); } down_write(&gpio_sw_list_lock); list_add_tail(&gpio_sw_cdev->node, &gpio_sw_list); up_write(&gpio_sw_list_lock); mutex_init(&gpio_sw_cdev->class_mutex); return 0; } static int map_gpio_to_name(char *name, u32 gpio) { char base; int num; num = gpio - SUNXI_PA_BASE; if (num < 0) goto map_fail; if ((num >= 0) && (num < PINS_PER_BANK)) { base = 'A'; goto map_done; } num = gpio - SUNXI_PB_BASE; if ((num >= 0) && (num < PINS_PER_BANK)) { base = 'B'; goto map_done; } num = gpio - SUNXI_PC_BASE; if ((num >= 0) && (num < PINS_PER_BANK)) { base = 'C'; goto map_done; } num = gpio - SUNXI_PD_BASE; if ((num >= 0) && (num < PINS_PER_BANK)) { base = 'D'; goto map_done; } num = gpio - SUNXI_PE_BASE; if ((num >= 0) && (num < PINS_PER_BANK)) { base = 'E'; goto map_done; } num = gpio - SUNXI_PF_BASE; if ((num >= 0) && (num < PINS_PER_BANK)) { base = 'F'; goto map_done; } num = gpio - SUNXI_PG_BASE; if ((num >= 0) && (num < PINS_PER_BANK)) { base = 'G'; goto map_done; } num = gpio - SUNXI_PH_BASE; if ((num >= 0) && (num < PINS_PER_BANK)) { base = 'H'; goto map_done; } num = gpio - SUNXI_PJ_BASE; if ((num >= 0) && (num < PINS_PER_BANK)) { base = 'J'; goto map_done; } num = gpio - SUNXI_PL_BASE; if ((num >= 0) && (num < PINS_PER_BANK)) { base = 'L'; goto map_done; } num = gpio - SUNXI_PM_BASE; if ((num >= 0) && (num < PINS_PER_BANK)) { base = 'M'; goto map_done; } num = gpio - AXP_PIN_BASE; if ((num >= 0) && (num < PINS_PER_BANK)) { base = 'X'; goto map_done; } goto map_fail; map_done: sprintf(name, "P%c%d", base, num); return 0; map_fail: return -1; } static void gpio_sw_release(struct device *dev) { pr_info("gpio_sw_release good !\n"); } static int gpio_suspend(struct platform_device *dev, pm_message_t state) { return 0; } static int gpio_resume(struct platform_device *dev) { return 0; } static int gpio_sw_probe(struct platform_device *dev) { struct sw_gpio *sw_gpio_entry; struct sw_gpio_pd *pdata = dev->dev.platform_data; int ret; unsigned long flags; char io_area[16]; int gpio; sw_gpio_entry = kzalloc(sizeof(struct sw_gpio), GFP_KERNEL); if (!sw_gpio_entry) return -ENOMEM; sw_gpio_entry->class.item = kzalloc(sizeof(struct gpio_config), GFP_KERNEL); if (!sw_gpio_entry->class.item) { kfree(sw_gpio_entry); return -ENOMEM; } /* pr_info("gpio_name: %s\n", pdata->name); */ gpio = of_get_named_gpio_flags(node, pdata->name, 0, (enum of_gpio_flags *)sw_gpio_entry-> class.item); if (!gpio_is_valid(gpio)) { pr_err("get config err!\n"); kfree(sw_gpio_entry->class.item); kfree(sw_gpio_entry); return -ENOMEM; } /* init the */ sw_gpio_entry->class.cfg = (sw_gpio_entry->class.item)->mul_sel; sw_gpio_entry->class.pull = (sw_gpio_entry->class.item)->pull; sw_gpio_entry->class.drv = (sw_gpio_entry->class.item)->drv_level; ret = map_gpio_to_name(io_area, sw_gpio_entry->class.item->gpio); pr_info("gpio name is %s, ret = %d\n", io_area, ret); platform_set_drvdata(dev, sw_gpio_entry); spin_lock_init(&sw_gpio_entry->lock); spin_lock_irqsave(&sw_gpio_entry->lock, flags); sw_gpio_entry->pdata = pdata; if (ret == 0) sw_gpio_entry->class.name = io_area; else sw_gpio_entry->class.name = pdata->name; sw_gpio_entry->class.gpio_sw_cfg_set = gpio_sw_cfg_set; sw_gpio_entry->class.gpio_sw_cfg_get = gpio_sw_cfg_get; sw_gpio_entry->class.gpio_sw_pull_set = gpio_sw_pull_set; sw_gpio_entry->class.gpio_sw_pull_get = gpio_sw_pull_get; sw_gpio_entry->class.gpio_sw_drv_set = gpio_sw_drv_set; sw_gpio_entry->class.gpio_sw_drv_get = gpio_sw_drv_get; sw_gpio_entry->class.gpio_sw_data_set = gpio_sw_data_set; sw_gpio_entry->class.gpio_sw_data_get = gpio_sw_data_get; /* init the gpio form sys_config */ gpio_sw_cfg_set(&sw_gpio_entry->class, sw_gpio_entry->class.item->mul_sel); gpio_sw_data_set(&sw_gpio_entry->class, sw_gpio_entry->class.item->data); spin_unlock_irqrestore(&sw_gpio_entry->lock, flags); ret = gpio_sw_classdev_register(&dev->dev, &sw_gpio_entry->class); if (ret < 0) { dev_err(&dev->dev, "gpio_sw_classdev_register failed\n"); kfree(sw_gpio_entry->class.item); kfree(sw_gpio_entry); return -1; } /* create symbol link */ if (strlen(pdata->link) != 0) { ret = sysfs_create_link(&gpio_sw_class->p->subsys.kobj, &sw_gpio_entry->class.dev->kobj, pdata->link); } return 0; } static struct platform_driver gpio_sw_driver = { .probe = gpio_sw_probe, .remove = gpio_sw_remove, .suspend = gpio_suspend, .resume = gpio_resume, .driver = { .name = "gpio_sw", .owner = THIS_MODULE, }, }; static void __exit gpio_sw_exit(void) { int i, cnt; struct gpio_config config; int gpio; char gpio_name[32]; int ret; platform_driver_unregister(&gpio_sw_driver); ret = of_property_read_u32(node, "gpio_num", &cnt); if (ret || !cnt) { pr_info("these is zero number for gpio\n"); goto EXIT_END; } for (i = 0; i < cnt; i++) { sprintf(gpio_name, "gpio_pin_%d", i + 1); gpio = of_get_named_gpio_flags(node, gpio_name, 0, (enum of_gpio_flags *)&config); if (!gpio_is_valid(gpio)) { pr_err("this gpio is invalid: %d\n", gpio); continue; } platform_device_unregister(gpio_sw_dev[i]); kfree(gpio_sw_dev[i]); kfree(sw_pdata[i]); gpio_free(gpio); } class_destroy(gpio_sw_class); EXIT_END: pr_info("gpio_exit finish !\n"); } static int sunxi_init_gpio_probe(struct platform_device *pdev) { int i, cnt; struct gpio_config config; int gpio; char gpio_name[32]; int ret; const char *normal_led_pin_str = NULL; const char *standby_led_pin_str = NULL; const char *network_led_pin_str = NULL; node = pdev->dev.of_node; if (!node) goto INIT_END; /* create debug dir: /sys/class/gpio_sw */ gpio_sw_class = class_create(THIS_MODULE, "gpio_sw"); if (IS_ERR(gpio_sw_class)) return PTR_ERR(gpio_sw_class); gpio_sw_class->suspend = gpio_sw_suspend; gpio_sw_class->resume = gpio_sw_resume; gpio_sw_class->class_attrs = (struct class_attribute *)gpio_sw_class_attrs; if (of_property_read_u32(node, "easy_light_used", &easy_light_used)) { easy_light_used = 0; pr_err("failed to get easy_light_used assign\n"); } if (of_property_read_string(node, "normal_led", &normal_led_pin_str)) pr_err("failed to get normal led pin assign\n"); if (of_property_read_string(node, "standby_led", &standby_led_pin_str)) pr_err("failed to get standby led pin assign\n"); if (of_property_read_string(node, "network_led", &network_led_pin_str)) pr_err("failed to get standby led pin assign\n"); ret = of_property_read_u32(node, "gpio_num", &cnt); if (ret || !cnt) { pr_err("these is zero number for gpio\n"); goto INIT_END; } for (i = 0; i < cnt; i++) { sprintf(gpio_name, "gpio_pin_%d", i + 1); gpio = of_get_named_gpio_flags(node, gpio_name, 0, (enum of_gpio_flags *)&config); /*printk(KERN_EMERG"gpio = %d, mul = %d, drv= %d, pull= %d, data = %d\n",\ config.gpio, config.mul_sel, config.drv_level, config.pull, config.data);*/ if (gpio_request(gpio, NULL)) { pr_err("gpio_pin_%d(%d) gpio_request fail\n", i + 1, gpio); continue; } pr_info("gpio_pin_%d(%d) gpio_is_valid\n", i + 1, config.gpio); sw_pdata[i] = kzalloc(sizeof(struct sw_gpio_pd), GFP_KERNEL); if (!sw_pdata[i]) { pr_err("kzalloc fail for sw_pdata[%d]\n", i); return -1; } gpio_sw_dev[i] = kzalloc(sizeof(struct platform_device), GFP_KERNEL); if (!gpio_sw_dev[i]) { pr_err("kzalloc fail for gpio_sw_dev[%d]\n", i); return -1; } sprintf(sw_pdata[i]->name, "gpio_pin_%d", i + 1); if (normal_led_pin_str && !strcmp(sw_pdata[i]->name, normal_led_pin_str)) { sprintf(sw_pdata[i]->link, "%s", "normal_led"); if (easy_light_used) of_property_read_u32(node, "normal_led_light", &sw_pdata[i]->light); } else if (standby_led_pin_str && !strcmp(sw_pdata[i]->name, standby_led_pin_str)) { sprintf(sw_pdata[i]->link, "%s", "standby_led"); if (easy_light_used) of_property_read_u32(node, "standby_led_light", &sw_pdata[i]->light); } else if (network_led_pin_str && !strcmp(sw_pdata[i]->name, network_led_pin_str)) { sprintf(sw_pdata[i]->link, "%s", "network_led"); if (easy_light_used) of_property_read_u32(node, "network_led_light", &sw_pdata[i]->light); } gpio_sw_dev[i]->name = "gpio_sw"; gpio_sw_dev[i]->id = i; gpio_sw_dev[i]->dev.platform_data = sw_pdata[i]; gpio_sw_dev[i]->dev.release = gpio_sw_release; if (platform_device_register(gpio_sw_dev[i])) { pr_err("%s platform_device_register fail\n", sw_pdata[i]->name); goto INIT_ERR_FREE; } } if (platform_driver_register(&gpio_sw_driver)) { pr_err("gpio user platform_driver_register fail\n"); for (i = 0; i < cnt; i++) platform_device_unregister(gpio_sw_dev[i]); goto INIT_ERR_FREE; } INIT_END: pr_info("gpio_init finish with uesd\n"); return 0; INIT_ERR_FREE: pr_err("gpio_init err\n"); kfree(sw_pdata[i]); kfree(gpio_sw_dev[i]); return -1; } static const struct of_device_id sunxi_gpio_of_match[] = { {.compatible = "allwinner,sunxi-init-gpio", .data = NULL}, { /* sentinel */ } }; static struct platform_driver sunxi_gpio_driver = { .driver = { .name = "sunxi-init-gpio", .of_match_table = of_match_ptr(sunxi_gpio_of_match), }, .probe = sunxi_init_gpio_probe, }; static int __init sunxi_gpio_init(void) { if (platform_driver_register(&sunxi_gpio_driver)) { pr_err("gpio user platform_driver_register fail\n"); return -1; } return 0; } module_init(sunxi_gpio_init); module_exit(gpio_sw_exit); MODULE_AUTHOR("yanjianbo"); MODULE_DESCRIPTION("SW GPIO USER driver"); MODULE_LICENSE("GPL");