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