/drivers/misc/sun4i-gpio.c
C | 269 lines | 204 code | 49 blank | 16 comment | 19 complexity | 57d8a0a4a8a6ba760428ffefa919a307 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
1/* driver/misc/sun4i-gpio.c 2 * 3 * Copyright (C) 2011 Allwinner Technology Co.Ltd 4 * Tom Cubie <tangliang@allwinnertech.com> 5 * 6 * www.allwinnertech.com 7 * 8 * An ugly sun4i gpio driver 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 * 14 */ 15 16#include <linux/module.h> 17#include <linux/init.h> 18#include <linux/pm.h> 19#include <linux/sysdev.h> 20#include <linux/fs.h> 21#include <linux/kernel.h> 22#include <linux/slab.h> 23#include <linux/earlysuspend.h> 24#include <linux/miscdevice.h> 25#include <linux/device.h> 26 27#include <mach/sys_config.h> 28 29#undef DEBUG_SUN4I 30 31#ifdef DEBUG_SUN4I 32#define sun4i_gpio_dbg(x...) printk(x) 33#else 34#define sun4i_gpio_dbg(x...) 35#endif 36 37struct sun4i_gpio_data { 38 int status; 39 unsigned gpio_handler; 40 script_gpio_set_t info; 41 char name[8]; 42#ifdef CONFIG_HAS_EARLYSUSPEND 43 struct early_suspend early_suspend; 44#endif 45}; 46 47static int sun4i_gpio_num; 48static struct sun4i_gpio_data *psun4i_gpio; 49static struct device_attribute *pattr; 50 51static void set_sun4i_gpio_status(struct sun4i_gpio_data *sun4i_gpio, int on) 52{ 53 if(on) { 54 sun4i_gpio_dbg("\n-on-"); 55 gpio_write_one_pin_value(sun4i_gpio->gpio_handler, 1, NULL); 56 } 57 else{ 58 sun4i_gpio_dbg("\n-off-"); 59 gpio_write_one_pin_value(sun4i_gpio->gpio_handler, 0, NULL); 60 } 61} 62 63static ssize_t sun4i_gpio_enable_store(struct device *dev, 64 struct device_attribute *attr, 65 const char *buf, size_t count) 66{ 67 unsigned long data; 68 int i,error; 69 struct sun4i_gpio_data *gpio_i = psun4i_gpio; 70 71 error = strict_strtoul(buf, 10, &data); 72 if (error) 73 return error; 74 75 for(i = 0; i < sun4i_gpio_num; i++) { 76 sun4i_gpio_dbg("%s\n", attr->attr.name); 77 sun4i_gpio_dbg("%s\n", gpio_i->name); 78 79 if(!strcmp(attr->attr.name, gpio_i->name)) { 80 set_sun4i_gpio_status(gpio_i, data); 81 break; 82 } 83 gpio_i++; 84 } 85 86 return count; 87} 88 89static ssize_t sun4i_gpio_enable_show(struct device *dev, 90 struct device_attribute *attr, char *buf) 91{ 92 int i; 93 int data = EGPIO_FAIL; 94 struct sun4i_gpio_data *gpio_i = psun4i_gpio; 95 96 for(i = 0; i < sun4i_gpio_num; i++) { 97 sun4i_gpio_dbg("%s\n", attr->attr.name); 98 sun4i_gpio_dbg("%s\n", gpio_i->name); 99 100 if(!strcmp(attr->attr.name, gpio_i->name)) { 101 data = gpio_read_one_pin_value(gpio_i->gpio_handler, NULL); 102 sun4i_gpio_dbg("handler:%d\n", gpio_i->gpio_handler); 103 sun4i_gpio_dbg("data:%d\n", data); 104 break; 105 } 106 gpio_i++; 107 } 108 109 if(data != EGPIO_FAIL) { 110 return sprintf(buf, "%d\n", data); 111 } else { 112 return sprintf(buf, "error\n"); 113 } 114} 115 116static struct attribute *sun4i_gpio_attributes[256] = { 117 NULL 118}; 119 120static struct attribute_group sun4i_gpio_attribute_group = { 121 .name = "pin", 122 .attrs = sun4i_gpio_attributes 123}; 124 125static int sun4i_gpio_open(struct inode *inode, struct file *file) { 126 pr_info("sun4i_gpio open\n"); 127 return 0; 128} 129 130ssize_t sun4i_gpio_write(struct file *file, const char __user *buf, size_t size, loff_t *offset) { 131 pr_info("sun4i_gpio write\n"); 132 return 0; 133} 134 135static int sun4i_gpio_release(struct inode *inode, struct file *file) { 136 gpio_release(psun4i_gpio->gpio_handler, 0); 137 kfree(psun4i_gpio); 138 return 0; 139} 140 141static const struct file_operations sun4i_gpio_fops = { 142 .open = sun4i_gpio_open, 143 .write = sun4i_gpio_write, 144 .release = sun4i_gpio_release 145}; 146 147static struct miscdevice sun4i_gpio_dev = { 148 .minor = MISC_DYNAMIC_MINOR, 149 .name = "sun4i-gpio", 150 .fops = &sun4i_gpio_fops 151}; 152 153static int __init sun4i_gpio_init(void) { 154 int err; 155 int i; 156 int sun4i_gpio_used = 0; 157 struct sun4i_gpio_data *gpio_i; 158 struct device_attribute *attr_i; 159 char pin[16]; 160 161 pr_info("sun4i gpio driver init\n"); 162 163 err = script_parser_fetch("gpio_para", "gpio_used", &sun4i_gpio_used, sizeof(sun4i_gpio_used)/sizeof(int)); 164 if(err) { 165 pr_err("%s script_parser_fetch \"gpio_para\" \"gpio_used\" error\n", __FUNCTION__); 166 goto exit; 167 } 168 169 if(!sun4i_gpio_used) { 170 pr_err("%s sun4i_gpio is not used in config\n", __FUNCTION__); 171 err = -1; 172 goto exit; 173 } 174 175 err = script_parser_fetch("gpio_para", "gpio_num", &sun4i_gpio_num, sizeof(sun4i_gpio_num)/sizeof(int)); 176 if(err) { 177 pr_err("%s script_parser_fetch \"gpio_para\" \"gpio_num\" error\n", __FUNCTION__); 178 goto exit; 179 } 180 181 sun4i_gpio_dbg("sun4i_gpio_num:%d\n", sun4i_gpio_num); 182 if(!sun4i_gpio_num) { 183 pr_err("%s sun4i_gpio_num is none\n", __FUNCTION__); 184 err = -1; 185 goto exit; 186 } 187 188 err = misc_register(&sun4i_gpio_dev); 189 if(err) { 190 pr_err("%s register sun4i_gpio as misc device error\n", __FUNCTION__); 191 goto exit; 192 } 193 194 psun4i_gpio = kzalloc(sizeof(struct sun4i_gpio_data) * sun4i_gpio_num, GFP_KERNEL); 195 pattr = kzalloc(sizeof(struct device_attribute) * sun4i_gpio_num, GFP_KERNEL); 196 197 if(!psun4i_gpio || !pattr) { 198 pr_err("%s kzalloc failed\n", __FUNCTION__); 199 err = -ENOMEM; 200 goto exit; 201 } 202 203 gpio_i = psun4i_gpio; 204 attr_i = pattr; 205 206 for(i = 0; i < sun4i_gpio_num; i++) { 207 208 sprintf(pin, "gpio_pin_%d", i+1); 209 sun4i_gpio_dbg("pin:%s\n", pin); 210 211 err = script_parser_fetch("gpio_para", pin, 212 (int *)&gpio_i->info, sizeof(script_gpio_set_t)); 213 214 if(err) { 215 pr_err("%s script_parser_fetch \"gpio_para\" \"%s\" error\n", __FUNCTION__, pin); 216 break; 217 } 218 219 gpio_i->gpio_handler = gpio_request_ex("gpio_para", pin); 220 sun4i_gpio_dbg("gpio handler: %d", gpio_i->gpio_handler); 221 222 if(!gpio_i->gpio_handler) { 223 pr_err("%s can not get \"gpio_para\" \"%s\" gpio handler,\ 224 already used by others?", __FUNCTION__, pin); 225 break; 226 } 227 228 sun4i_gpio_dbg("%s: port:%d, portnum:%d\n", pin, gpio_i->info.port, 229 gpio_i->info.port_num); 230 231 /* Turn the name to pa1, pb2 etc... */ 232 sprintf(gpio_i->name, "p%c%d", 'a'+gpio_i->info.port-1, gpio_i->info.port_num); 233 234 sun4i_gpio_dbg("psun4i_gpio->name%s\n", gpio_i->name); 235 236 /* Add attributes to the group */ 237 sysfs_attr_init(&attr_i->attr); 238 attr_i->attr.name = gpio_i->name; 239 attr_i->attr.mode = S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH; 240 attr_i->show = sun4i_gpio_enable_show; 241 attr_i->store = sun4i_gpio_enable_store; 242 sun4i_gpio_attributes[i] = &attr_i->attr; 243 244 gpio_i++; 245 attr_i++; 246 } 247 248 sysfs_create_group(&sun4i_gpio_dev.this_device->kobj, 249 &sun4i_gpio_attribute_group); 250exit: 251 return err; 252} 253 254static void __exit sun4i_gpio_exit(void) { 255 256 sun4i_gpio_dbg("bye, sun4i_gpio exit\n"); 257 misc_deregister(&sun4i_gpio_dev); 258 sysfs_remove_group(&sun4i_gpio_dev.this_device->kobj, 259 &sun4i_gpio_attribute_group); 260 kfree(psun4i_gpio); 261 kfree(pattr); 262} 263 264module_init(sun4i_gpio_init); 265module_exit(sun4i_gpio_exit); 266 267MODULE_DESCRIPTION("a simple sun4i_gpio driver"); 268MODULE_AUTHOR("Tom Cubie"); 269MODULE_LICENSE("GPL");