diff options
| -rw-r--r-- | drivers/gpio/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
| -rw-r--r-- | drivers/gpio/it8761e_gpio.c | 231 |
3 files changed, 238 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c5cc7d9d88e3..a4cdbd51b1c6 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig | |||
| @@ -70,6 +70,12 @@ config GPIO_MAX730X | |||
| 70 | 70 | ||
| 71 | comment "Memory mapped GPIO expanders:" | 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 | config GPIO_PL061 | 79 | config GPIO_PL061 |
| 74 | bool "PrimeCell PL061 GPIO support" | 80 | bool "PrimeCell PL061 GPIO support" |
| 75 | depends on ARM_AMBA | 81 | depends on ARM_AMBA |
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 508a1b202cdb..128abf8a98da 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile | |||
| @@ -22,5 +22,6 @@ obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o | |||
| 22 | obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o | 22 | obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o |
| 23 | obj-$(CONFIG_GPIO_CS5535) += cs5535-gpio.o | 23 | obj-$(CONFIG_GPIO_CS5535) += cs5535-gpio.o |
| 24 | obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o | 24 | obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o |
| 25 | obj-$(CONFIG_GPIO_IT8761E) += it8761e_gpio.o | ||
| 25 | obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o | 26 | obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o |
| 26 | obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o | 27 | obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o |
diff --git a/drivers/gpio/it8761e_gpio.c b/drivers/gpio/it8761e_gpio.c new file mode 100644 index 000000000000..753219cf993a --- /dev/null +++ b/drivers/gpio/it8761e_gpio.c | |||
| @@ -0,0 +1,231 @@ | |||
| 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"); | ||
