| 1 | /* |
|---|
| 2 | * proc_gpio: AR5315 GPIO pins in /proc/gpio/ |
|---|
| 3 | * by olg |
|---|
| 4 | * modification for AR7100 support by Sebastian Gottschall <s.gottschall@newmedia-net.de> |
|---|
| 5 | * GPL'ed |
|---|
| 6 | * some code stolen from Yoshinori Sato <ysato@users.sourceforge.jp> |
|---|
| 7 | * |
|---|
| 8 | * This program is free software; you can redistribute it and/or modify it |
|---|
| 9 | * under the terms of the GNU General Public License version 2 as published |
|---|
| 10 | * by the Free Software Foundation. |
|---|
| 11 | */ |
|---|
| 12 | |
|---|
| 13 | #include <linux/autoconf.h> |
|---|
| 14 | #include <linux/init.h> |
|---|
| 15 | #include <linux/module.h> |
|---|
| 16 | #include <linux/types.h> |
|---|
| 17 | #include <linux/stddef.h> |
|---|
| 18 | #include <linux/proc_fs.h> |
|---|
| 19 | #include <linux/kernel.h> |
|---|
| 20 | #include <linux/string.h> |
|---|
| 21 | #include <linux/fs.h> |
|---|
| 22 | #include <asm/uaccess.h> /* for copy_from_user */ |
|---|
| 23 | |
|---|
| 24 | #include "ar7100.h" |
|---|
| 25 | |
|---|
| 26 | #define PROCFS_MAX_SIZE 64 |
|---|
| 27 | extern const char *get_arch_type(void); |
|---|
| 28 | struct proc_dir_entry *proc_gpio, *gpio_dir; |
|---|
| 29 | |
|---|
| 30 | //Masks for data exchange through "void *data" pointer |
|---|
| 31 | #define GPIO_IN (1<<5) |
|---|
| 32 | #define GPIO_OUT (1<<6) |
|---|
| 33 | #define GPIO_DIR (1<<7) |
|---|
| 34 | #define PIN_MASK 0x1f |
|---|
| 35 | #define GPIO_CR_M(x) (1 << (x)) /* mask for i/o */ |
|---|
| 36 | |
|---|
| 37 | static void cleanup_proc(void); |
|---|
| 38 | void ar7100_set_gpio(int gpio, int val) |
|---|
| 39 | { |
|---|
| 40 | u32 reg = ar7100_reg_rd(AR7100_GPIO_OE); |
|---|
| 41 | reg |= 1 << gpio; |
|---|
| 42 | ar7100_reg_wr(AR7100_GPIO_OE, reg); |
|---|
| 43 | (void)ar7100_reg_rd(AR7100_GPIO_OE); /* flush write to hardware */ |
|---|
| 44 | reg = ar7100_reg_rd(AR7100_GPIO_OUT); |
|---|
| 45 | if (val) |
|---|
| 46 | reg |= 1 << gpio; |
|---|
| 47 | else |
|---|
| 48 | reg &= ~(1 << gpio); |
|---|
| 49 | ar7100_reg_wr(AR7100_GPIO_OUT, reg); |
|---|
| 50 | } |
|---|
| 51 | |
|---|
| 52 | int ar7100_get_gpio(int gpio) |
|---|
| 53 | { |
|---|
| 54 | u32 reg = ar7100_reg_rd(AR7100_GPIO_OE); |
|---|
| 55 | reg &= ~(1 << gpio); |
|---|
| 56 | ar7100_reg_wr(AR7100_GPIO_OE, reg); |
|---|
| 57 | reg = ar7100_reg_rd(AR7100_GPIO_IN); |
|---|
| 58 | if (reg & (1 << gpio)) |
|---|
| 59 | return 1; |
|---|
| 60 | else |
|---|
| 61 | return 0; |
|---|
| 62 | } |
|---|
| 63 | |
|---|
| 64 | void |
|---|
| 65 | ar7100_gpio_config_output(int gpio) |
|---|
| 66 | { |
|---|
| 67 | ar7100_reg_rmw_set(AR7100_GPIO_OE, (1 << gpio)); |
|---|
| 68 | (void)ar7100_reg_rd(AR7100_GPIO_OE); /* flush write to hardware */ |
|---|
| 69 | } |
|---|
| 70 | |
|---|
| 71 | void |
|---|
| 72 | ar7100_gpio_config_input(int gpio) |
|---|
| 73 | { |
|---|
| 74 | ar7100_reg_rmw_clear(AR7100_GPIO_OE, (1 << gpio)); |
|---|
| 75 | (void)ar7100_reg_rd(AR7100_GPIO_OE); /* flush write to hardware */ |
|---|
| 76 | } |
|---|
| 77 | |
|---|
| 78 | void |
|---|
| 79 | ar7100_gpio_out_val(int gpio, int val) |
|---|
| 80 | { |
|---|
| 81 | if (val & 0x1) { |
|---|
| 82 | ar7100_reg_rmw_set(AR7100_GPIO_OUT, (1 << gpio)); |
|---|
| 83 | } |
|---|
| 84 | else { |
|---|
| 85 | ar7100_reg_rmw_clear(AR7100_GPIO_OUT, (1 << gpio)); |
|---|
| 86 | } |
|---|
| 87 | (void)ar7100_reg_rd(AR7100_GPIO_OUT); /* flush write to hardware */ |
|---|
| 88 | } |
|---|
| 89 | |
|---|
| 90 | int |
|---|
| 91 | ar7100_gpio_in_val(int gpio) |
|---|
| 92 | { |
|---|
| 93 | return((1 << gpio) & (ar7100_reg_rd(AR7100_GPIO_IN))); |
|---|
| 94 | } |
|---|
| 95 | |
|---|
| 96 | |
|---|
| 97 | |
|---|
| 98 | EXPORT_SYMBOL(ar7100_set_gpio); |
|---|
| 99 | EXPORT_SYMBOL(ar7100_get_gpio); |
|---|
| 100 | EXPORT_SYMBOL(ar7100_gpio_config_output); |
|---|
| 101 | EXPORT_SYMBOL(ar7100_gpio_config_input); |
|---|
| 102 | EXPORT_SYMBOL(ar7100_gpio_in_val); |
|---|
| 103 | EXPORT_SYMBOL(ar7100_gpio_out_val); |
|---|
| 104 | |
|---|
| 105 | #define USB_LED_OFF 1 |
|---|
| 106 | #define USB_LED_ON 0 |
|---|
| 107 | |
|---|
| 108 | void ap_usb_led_on(void) |
|---|
| 109 | { |
|---|
| 110 | #ifdef AP_USB_LED_GPIO |
|---|
| 111 | ar7100_set_gpio(AP_USB_LED_GPIO, USB_LED_ON); |
|---|
| 112 | #endif |
|---|
| 113 | } |
|---|
| 114 | EXPORT_SYMBOL(ap_usb_led_on); |
|---|
| 115 | |
|---|
| 116 | void ap_usb_led_off(void) |
|---|
| 117 | { |
|---|
| 118 | #ifdef AP_USB_LED_GPIO |
|---|
| 119 | ar7100_set_gpio(AP_USB_LED_GPIO, USB_LED_OFF); |
|---|
| 120 | #endif |
|---|
| 121 | } |
|---|
| 122 | EXPORT_SYMBOL(ap_usb_led_off); |
|---|
| 123 | |
|---|
| 124 | |
|---|
| 125 | |
|---|
| 126 | |
|---|
| 127 | #define NXP_74HC153_NUM_GPIOS 8 |
|---|
| 128 | #define NXP_74HC153_S0_MASK 0x1 |
|---|
| 129 | #define NXP_74HC153_S1_MASK 0x2 |
|---|
| 130 | #define NXP_74HC153_BANK_MASK 0x4 |
|---|
| 131 | |
|---|
| 132 | #define WZRHPG300NH_GPIO_74HC153_S0 9 |
|---|
| 133 | #define WZRHPG300NH_GPIO_74HC153_S1 11 |
|---|
| 134 | #define WZRHPG300NH_GPIO_74HC153_1Y 12 |
|---|
| 135 | #define WZRHPG300NH_GPIO_74HC153_2Y 14 |
|---|
| 136 | |
|---|
| 137 | #define WZRHPG300NH_GPIO_EXP_BASE 23 |
|---|
| 138 | #define WZRHPG300NH_GPIO_BTN_AOSS (WZRHPG300NH_GPIO_EXP_BASE + 0) |
|---|
| 139 | #define WZRHPG300NH_GPIO_BTN_RESET (WZRHPG300NH_GPIO_EXP_BASE + 1) |
|---|
| 140 | #define WZRHPG300NH_GPIO_BTN_ROUTER_ON (WZRHPG300NH_GPIO_EXP_BASE + 2) |
|---|
| 141 | #define WZRHPG300NH_GPIO_BTN_QOS_ON (WZRHPG300NH_GPIO_EXP_BASE + 3) |
|---|
| 142 | #define WZRHPG300NH_GPIO_BTN_USB (WZRHPG300NH_GPIO_EXP_BASE + 5) |
|---|
| 143 | #define WZRHPG300NH_GPIO_BTN_ROUTER_AUTO (WZRHPG300NH_GPIO_EXP_BASE + 6) |
|---|
| 144 | #define WZRHPG300NH_GPIO_BTN_QOS_OFF (WZRHPG300NH_GPIO_EXP_BASE + 7) |
|---|
| 145 | |
|---|
| 146 | static int nxp_74hc153_get_value(unsigned offset) |
|---|
| 147 | { |
|---|
| 148 | unsigned s0; |
|---|
| 149 | unsigned s1; |
|---|
| 150 | unsigned pin; |
|---|
| 151 | int ret; |
|---|
| 152 | |
|---|
| 153 | s0 = !!(offset & NXP_74HC153_S0_MASK); |
|---|
| 154 | s1 = !!(offset & NXP_74HC153_S1_MASK); |
|---|
| 155 | pin = (offset & NXP_74HC153_BANK_MASK) ? WZRHPG300NH_GPIO_74HC153_2Y |
|---|
| 156 | : WZRHPG300NH_GPIO_74HC153_1Y; |
|---|
| 157 | |
|---|
| 158 | ar7100_set_gpio(WZRHPG300NH_GPIO_74HC153_S0, s0); |
|---|
| 159 | ar7100_set_gpio(WZRHPG300NH_GPIO_74HC153_S1, s1); |
|---|
| 160 | ret = ar7100_get_gpio(pin); |
|---|
| 161 | |
|---|
| 162 | return ret; |
|---|
| 163 | } |
|---|
| 164 | |
|---|
| 165 | //The buffer used to store the data returned by the proc file |
|---|
| 166 | static char procfs_buffer[PROCFS_MAX_SIZE]; |
|---|
| 167 | static unsigned long procfs_buffer_size = 0; |
|---|
| 168 | |
|---|
| 169 | static int |
|---|
| 170 | gpio_proc_read(char *buf, char **start, off_t offset, |
|---|
| 171 | int len, int *eof, void *data) |
|---|
| 172 | { |
|---|
| 173 | int val; |
|---|
| 174 | unsigned int pin = (unsigned int)data & PIN_MASK; |
|---|
| 175 | if (pin >= 23 && pin <= 31) { |
|---|
| 176 | val = nxp_74hc153_get_value(pin - 23); |
|---|
| 177 | // printk(KERN_EMERG "value for pin %d = %d\n", pin, val); |
|---|
| 178 | if (val) |
|---|
| 179 | buf[0] = '1'; |
|---|
| 180 | else |
|---|
| 181 | buf[0] = '0'; |
|---|
| 182 | buf[1] = 0; |
|---|
| 183 | *eof = 1; |
|---|
| 184 | return (2); |
|---|
| 185 | } |
|---|
| 186 | u32 reg = 0; |
|---|
| 187 | if ((unsigned int)data & GPIO_IN) |
|---|
| 188 | reg = ar7100_reg_rd(AR7100_GPIO_IN); |
|---|
| 189 | if ((unsigned int)data & GPIO_OUT) |
|---|
| 190 | reg = ar7100_reg_rd(AR7100_GPIO_OUT); |
|---|
| 191 | if ((unsigned int)data & GPIO_DIR) |
|---|
| 192 | reg = ar7100_reg_rd(AR7100_GPIO_OE); |
|---|
| 193 | |
|---|
| 194 | if (GPIO_CR_M(((unsigned int)data) & PIN_MASK) & reg) |
|---|
| 195 | buf[0] = '1'; |
|---|
| 196 | else |
|---|
| 197 | buf[0] = '0'; |
|---|
| 198 | buf[1] = 0; |
|---|
| 199 | |
|---|
| 200 | *eof = 1; |
|---|
| 201 | |
|---|
| 202 | return (2); |
|---|
| 203 | |
|---|
| 204 | } |
|---|
| 205 | |
|---|
| 206 | static int |
|---|
| 207 | gpio_proc_info_read(char *buf, char **start, off_t offset, |
|---|
| 208 | int len, int *eof, void *data) |
|---|
| 209 | { |
|---|
| 210 | *eof = 1; |
|---|
| 211 | return (sprintf |
|---|
| 212 | (buf, "GPIO_IN %#08X \nGPIO_OUT %#08X \nGPIO_DIR %#08X \n", |
|---|
| 213 | ar7100_reg_rd(AR7100_GPIO_IN), ar7100_reg_rd(AR7100_GPIO_OUT), |
|---|
| 214 | ar7100_reg_rd(AR7100_GPIO_OE))); |
|---|
| 215 | } |
|---|
| 216 | |
|---|
| 217 | static int |
|---|
| 218 | gpio_proc_write(struct file *file, const char *buffer, unsigned long count, |
|---|
| 219 | void *data) |
|---|
| 220 | { |
|---|
| 221 | u32 reg = 0; |
|---|
| 222 | |
|---|
| 223 | /* get buffer size */ |
|---|
| 224 | procfs_buffer_size = count; |
|---|
| 225 | if (procfs_buffer_size > PROCFS_MAX_SIZE) { |
|---|
| 226 | procfs_buffer_size = PROCFS_MAX_SIZE; |
|---|
| 227 | } |
|---|
| 228 | /* write data to the buffer */ |
|---|
| 229 | if (copy_from_user(procfs_buffer, buffer, procfs_buffer_size)) { |
|---|
| 230 | return -EFAULT; |
|---|
| 231 | } |
|---|
| 232 | |
|---|
| 233 | procfs_buffer[procfs_buffer_size] = 0; |
|---|
| 234 | |
|---|
| 235 | if ((unsigned int)data & GPIO_IN) |
|---|
| 236 | reg = ar7100_reg_rd(AR7100_GPIO_IN); |
|---|
| 237 | if ((unsigned int)data & GPIO_OUT) |
|---|
| 238 | reg = ar7100_reg_rd(AR7100_GPIO_OUT); |
|---|
| 239 | if ((unsigned int)data & GPIO_DIR) |
|---|
| 240 | reg = ar7100_reg_rd(AR7100_GPIO_OE); |
|---|
| 241 | |
|---|
| 242 | if (procfs_buffer[0] == '0' || procfs_buffer[0] == 'i') |
|---|
| 243 | reg = reg & ~(GPIO_CR_M(((unsigned int)data) & PIN_MASK)); |
|---|
| 244 | if (procfs_buffer[0] == '1' || procfs_buffer[0] == 'o') |
|---|
| 245 | reg = reg | GPIO_CR_M(((unsigned int)data) & PIN_MASK); |
|---|
| 246 | |
|---|
| 247 | if ((unsigned int)data & GPIO_IN) { |
|---|
| 248 | ar7100_reg_wr(AR7100_GPIO_IN, reg); |
|---|
| 249 | } |
|---|
| 250 | if ((unsigned int)data & GPIO_OUT) { |
|---|
| 251 | ar7100_reg_wr(AR7100_GPIO_OUT, reg); |
|---|
| 252 | } |
|---|
| 253 | if ((unsigned int)data & GPIO_DIR) { |
|---|
| 254 | ar7100_reg_wr(AR7100_GPIO_OE, reg); |
|---|
| 255 | } |
|---|
| 256 | |
|---|
| 257 | return procfs_buffer_size; |
|---|
| 258 | } |
|---|
| 259 | |
|---|
| 260 | static __init int register_proc(void) |
|---|
| 261 | { |
|---|
| 262 | unsigned char i, flag = 0; |
|---|
| 263 | char proc_name[32]; |
|---|
| 264 | int gpiocount = 32; |
|---|
| 265 | |
|---|
| 266 | /* create directory gpio */ |
|---|
| 267 | gpio_dir = proc_mkdir("gpio", NULL); |
|---|
| 268 | if (gpio_dir == NULL) |
|---|
| 269 | goto fault; |
|---|
| 270 | gpio_dir->owner = THIS_MODULE; |
|---|
| 271 | |
|---|
| 272 | for (i = 0; i < gpiocount * 3; i++) //create for every GPIO "x_in"," x_out" and "x_dir" |
|---|
| 273 | { |
|---|
| 274 | if (i / gpiocount == 0) { |
|---|
| 275 | flag = GPIO_IN; |
|---|
| 276 | sprintf(proc_name, "%i_in", i); |
|---|
| 277 | } |
|---|
| 278 | if (i / gpiocount == 1) { |
|---|
| 279 | flag = GPIO_OUT; |
|---|
| 280 | sprintf(proc_name, "%i_out", i % gpiocount); |
|---|
| 281 | } |
|---|
| 282 | if (i / gpiocount == 2) { |
|---|
| 283 | flag = GPIO_DIR; |
|---|
| 284 | sprintf(proc_name, "%i_dir", i % gpiocount); |
|---|
| 285 | } |
|---|
| 286 | |
|---|
| 287 | proc_gpio = create_proc_entry(proc_name, S_IRUGO, gpio_dir); |
|---|
| 288 | if (proc_gpio) { |
|---|
| 289 | proc_gpio->read_proc = gpio_proc_read; |
|---|
| 290 | proc_gpio->write_proc = gpio_proc_write; |
|---|
| 291 | proc_gpio->owner = THIS_MODULE; |
|---|
| 292 | proc_gpio->data = ((i % gpiocount) | flag); |
|---|
| 293 | } else |
|---|
| 294 | goto fault; |
|---|
| 295 | |
|---|
| 296 | } |
|---|
| 297 | |
|---|
| 298 | proc_gpio = create_proc_entry("info", S_IRUGO, gpio_dir); |
|---|
| 299 | if (proc_gpio) { |
|---|
| 300 | proc_gpio->read_proc = gpio_proc_info_read; |
|---|
| 301 | proc_gpio->owner = THIS_MODULE; |
|---|
| 302 | } else |
|---|
| 303 | goto fault; |
|---|
| 304 | |
|---|
| 305 | printk(KERN_NOTICE |
|---|
| 306 | "gpio_proc: module loaded and /proc/gpio/ created\n"); |
|---|
| 307 | return 0; |
|---|
| 308 | |
|---|
| 309 | fault: |
|---|
| 310 | cleanup_proc(); |
|---|
| 311 | return -EFAULT; |
|---|
| 312 | } |
|---|
| 313 | |
|---|
| 314 | static void cleanup_proc(void) |
|---|
| 315 | { |
|---|
| 316 | unsigned char i; |
|---|
| 317 | char proc_name[32]; |
|---|
| 318 | int gpiocount = 32; |
|---|
| 319 | |
|---|
| 320 | for (i = 0; i < gpiocount; i++) { |
|---|
| 321 | sprintf(proc_name, "%i_in", i); |
|---|
| 322 | remove_proc_entry(proc_name, gpio_dir); |
|---|
| 323 | sprintf(proc_name, "%i_out", i); |
|---|
| 324 | remove_proc_entry(proc_name, gpio_dir); |
|---|
| 325 | sprintf(proc_name, "%i_dir", i); |
|---|
| 326 | remove_proc_entry(proc_name, gpio_dir); |
|---|
| 327 | } |
|---|
| 328 | remove_proc_entry("info", gpio_dir); |
|---|
| 329 | remove_proc_entry("gpio", NULL); |
|---|
| 330 | printk(KERN_INFO "gpio_proc: unloaded and /proc/gpio/ removed\n"); |
|---|
| 331 | |
|---|
| 332 | } |
|---|
| 333 | |
|---|
| 334 | module_init(register_proc); |
|---|
| 335 | module_exit(cleanup_proc); |
|---|
| 336 | |
|---|
| 337 | MODULE_AUTHOR("Sebastian Gottschall"); |
|---|
| 338 | MODULE_DESCRIPTION("AR7100 GPIO pins in /proc/gpio/"); |
|---|
| 339 | MODULE_LICENSE("GPL"); |
|---|