1/* 2 * 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 33static u8 ports[2] = { 0x2e, 0x4e }; 34static u8 port; 35 36static 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 45static u16 gpio_ba; 46 47static u8 read_reg(u8 addr, u8 port) 48{ 49 outb(addr, port); 50 return inb(port + 1); 51} 52 53static void write_reg(u8 data, u8 addr, u8 port) 54{ 55 outb(addr, port); 56 outb(data, port + 1); 57} 58 59static 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 67static void exit_conf_mode(u8 port) 68{ 69 outb(0x2, port); 70 outb(0x2, port + 1); 71} 72 73static void enter_gpio_mode(u8 port) 74{ 75 write_reg(0x2, 0x7, port); 76} 77 78static int it8761e_gpio_get(struct gpio_chip *gc, unsigned gpio_num) 79{ 80 u16 reg; 81 u8 bit; 82 83 bit = gpio_num % 8; 84 reg = (gpio_num >= 8) ? gpio_ba + 1 : gpio_ba; 85 86 return !!(inb(reg) & (1 << bit)); 87} 88 89static 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 % 8; 95 io_reg = (gpio_num >= 8) ? 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 113static 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 % 8; 120 reg = (gpio_num >= 8) ? 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 133static 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 % 8; 139 io_reg = (gpio_num >= 8) ? 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 159static 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 168static 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 = 16; 204 205 err = gpiochip_add(&it8761e_gpio_chip); 206 if (err < 0) 207 goto gpiochip_add_err; 208 209 return 0; 210 211gpiochip_add_err: 212 release_region(gpio_ba, GPIO_IOSIZE); 213 gpio_ba = 0; 214 return err; 215} 216 217static void __exit it8761e_gpio_exit(void) 218{ 219 if (gpio_ba) { 220 gpiochip_remove(&it8761e_gpio_chip); 221 release_region(gpio_ba, GPIO_IOSIZE); 222 gpio_ba = 0; 223 } 224} 225module_init(it8761e_gpio_init); 226module_exit(it8761e_gpio_exit); 227 228MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>"); 229MODULE_DESCRIPTION("GPIO interface for IT8761E Super I/O chip"); 230MODULE_LICENSE("GPL"); 231