gpio: introduce it8761e_gpio driver for IT8761E Super I/O chip

Signed-off-by: Denis Turischev <denis@compulab.co.il>
Cc: David Brownell <david-b@pacbell.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by Denis Turischev and committed by Linus Torvalds 9cc0cb3c a8a5164c

+238
+6
drivers/gpio/Kconfig
··· 70 70 71 71 comment "Memory mapped GPIO expanders:" 72 72 73 + config GPIO_IT8761E 74 + tristate "IT8761E GPIO support" 75 + depends on GPIOLIB 76 + help 77 + Say yes here to support GPIO functionality of IT8761E super I/O chip. 78 + 73 79 config GPIO_PL061 74 80 bool "PrimeCell PL061 GPIO support" 75 81 depends on ARM_AMBA
+1
drivers/gpio/Makefile
··· 22 22 obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o 23 23 obj-$(CONFIG_GPIO_CS5535) += cs5535-gpio.o 24 24 obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o 25 + obj-$(CONFIG_GPIO_IT8761E) += it8761e_gpio.o 25 26 obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o 26 27 obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o
+231
drivers/gpio/it8761e_gpio.c
··· 1 + /* 2 + * it8761_gpio.c - GPIO interface for IT8761E Super I/O chip 3 + * 4 + * Author: Denis Turischev <denis@compulab.co.il> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License 2 as published 8 + * by the Free Software Foundation. 9 + * 10 + * This program is distributed in the hope that it will be useful, 11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + * 15 + * You should have received a copy of the GNU General Public License 16 + * along with this program; see the file COPYING. If not, write to 17 + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 18 + */ 19 + 20 + #include <linux/init.h> 21 + #include <linux/kernel.h> 22 + #include <linux/module.h> 23 + #include <linux/io.h> 24 + #include <linux/errno.h> 25 + #include <linux/ioport.h> 26 + 27 + #include <linux/gpio.h> 28 + 29 + #define SIO_CHIP_ID 0x8761 30 + #define CHIP_ID_HIGH_BYTE 0x20 31 + #define CHIP_ID_LOW_BYTE 0x21 32 + 33 + static u8 ports[2] = { 0x2e, 0x4e }; 34 + static u8 port; 35 + 36 + static DEFINE_SPINLOCK(sio_lock); 37 + 38 + #define GPIO_NAME "it8761-gpio" 39 + #define GPIO_BA_HIGH_BYTE 0x60 40 + #define GPIO_BA_LOW_BYTE 0x61 41 + #define GPIO_IOSIZE 4 42 + #define GPIO1X_IO 0xf0 43 + #define GPIO2X_IO 0xf1 44 + 45 + static u16 gpio_ba; 46 + 47 + static u8 read_reg(u8 addr, u8 port) 48 + { 49 + outb(addr, port); 50 + return inb(port + 1); 51 + } 52 + 53 + static void write_reg(u8 data, u8 addr, u8 port) 54 + { 55 + outb(addr, port); 56 + outb(data, port + 1); 57 + } 58 + 59 + static void enter_conf_mode(u8 port) 60 + { 61 + outb(0x87, port); 62 + outb(0x61, port); 63 + outb(0x55, port); 64 + outb((port == 0x2e) ? 0x55 : 0xaa, port); 65 + } 66 + 67 + static void exit_conf_mode(u8 port) 68 + { 69 + outb(0x2, port); 70 + outb(0x2, port + 1); 71 + } 72 + 73 + static void enter_gpio_mode(u8 port) 74 + { 75 + write_reg(0x2, 0x7, port); 76 + } 77 + 78 + static int it8761e_gpio_get(struct gpio_chip *gc, unsigned gpio_num) 79 + { 80 + u16 reg; 81 + u8 bit; 82 + 83 + bit = gpio_num % 7; 84 + reg = (gpio_num >= 7) ? gpio_ba + 1 : gpio_ba; 85 + 86 + return !!(inb(reg) & (1 << bit)); 87 + } 88 + 89 + static int it8761e_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num) 90 + { 91 + u8 curr_dirs; 92 + u8 io_reg, bit; 93 + 94 + bit = gpio_num % 7; 95 + io_reg = (gpio_num >= 7) ? GPIO2X_IO : GPIO1X_IO; 96 + 97 + spin_lock(&sio_lock); 98 + 99 + enter_conf_mode(port); 100 + enter_gpio_mode(port); 101 + 102 + curr_dirs = read_reg(io_reg, port); 103 + 104 + if (curr_dirs & (1 << bit)) 105 + write_reg(curr_dirs & ~(1 << bit), io_reg, port); 106 + 107 + exit_conf_mode(port); 108 + 109 + spin_unlock(&sio_lock); 110 + return 0; 111 + } 112 + 113 + static void it8761e_gpio_set(struct gpio_chip *gc, 114 + unsigned gpio_num, int val) 115 + { 116 + u8 curr_vals, bit; 117 + u16 reg; 118 + 119 + bit = gpio_num % 7; 120 + reg = (gpio_num >= 7) ? gpio_ba + 1 : gpio_ba; 121 + 122 + spin_lock(&sio_lock); 123 + 124 + curr_vals = inb(reg); 125 + if (val) 126 + outb(curr_vals | (1 << bit) , reg); 127 + else 128 + outb(curr_vals & ~(1 << bit), reg); 129 + 130 + spin_unlock(&sio_lock); 131 + } 132 + 133 + static int it8761e_gpio_direction_out(struct gpio_chip *gc, 134 + unsigned gpio_num, int val) 135 + { 136 + u8 curr_dirs, io_reg, bit; 137 + 138 + bit = gpio_num % 7; 139 + io_reg = (gpio_num >= 7) ? GPIO2X_IO : GPIO1X_IO; 140 + 141 + it8761e_gpio_set(gc, gpio_num, val); 142 + 143 + spin_lock(&sio_lock); 144 + 145 + enter_conf_mode(port); 146 + enter_gpio_mode(port); 147 + 148 + curr_dirs = read_reg(io_reg, port); 149 + 150 + if (!(curr_dirs & (1 << bit))) 151 + write_reg(curr_dirs | (1 << bit), io_reg, port); 152 + 153 + exit_conf_mode(port); 154 + 155 + spin_unlock(&sio_lock); 156 + return 0; 157 + } 158 + 159 + static struct gpio_chip it8761e_gpio_chip = { 160 + .label = GPIO_NAME, 161 + .owner = THIS_MODULE, 162 + .get = it8761e_gpio_get, 163 + .direction_input = it8761e_gpio_direction_in, 164 + .set = it8761e_gpio_set, 165 + .direction_output = it8761e_gpio_direction_out, 166 + }; 167 + 168 + static int __init it8761e_gpio_init(void) 169 + { 170 + int i, id, err; 171 + 172 + /* chip and port detection */ 173 + for (i = 0; i < ARRAY_SIZE(ports); i++) { 174 + spin_lock(&sio_lock); 175 + enter_conf_mode(ports[i]); 176 + 177 + id = (read_reg(CHIP_ID_HIGH_BYTE, ports[i]) << 8) + 178 + read_reg(CHIP_ID_LOW_BYTE, ports[i]); 179 + 180 + exit_conf_mode(ports[i]); 181 + spin_unlock(&sio_lock); 182 + 183 + if (id == SIO_CHIP_ID) { 184 + port = ports[i]; 185 + break; 186 + } 187 + } 188 + 189 + if (!port) 190 + return -ENODEV; 191 + 192 + /* fetch GPIO base address */ 193 + enter_conf_mode(port); 194 + enter_gpio_mode(port); 195 + gpio_ba = (read_reg(GPIO_BA_HIGH_BYTE, port) << 8) + 196 + read_reg(GPIO_BA_LOW_BYTE, port); 197 + exit_conf_mode(port); 198 + 199 + if (!request_region(gpio_ba, GPIO_IOSIZE, GPIO_NAME)) 200 + return -EBUSY; 201 + 202 + it8761e_gpio_chip.base = -1; 203 + it8761e_gpio_chip.ngpio = 14; 204 + 205 + err = gpiochip_add(&it8761e_gpio_chip); 206 + if (err < 0) 207 + goto gpiochip_add_err; 208 + 209 + return 0; 210 + 211 + gpiochip_add_err: 212 + release_region(gpio_ba, GPIO_IOSIZE); 213 + gpio_ba = 0; 214 + return err; 215 + } 216 + 217 + static void __exit it8761e_gpio_exit(void) 218 + { 219 + if (gpio_ba) { 220 + gpiochip_remove(&it8761e_gpio_chip); 221 + 222 + release_region(gpio_ba, GPIO_IOSIZE); 223 + gpio_ba = 0; 224 + } 225 + } 226 + module_init(it8761e_gpio_init); 227 + module_exit(it8761e_gpio_exit); 228 + 229 + MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>"); 230 + MODULE_DESCRIPTION("GPIO interface for IT8761E Super I/O chip"); 231 + MODULE_LICENSE("GPL");