linux/drivers/gpio/gpio-it8761e.c
<<
>>
Prefs
   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                int ret = gpiochip_remove(&it8761e_gpio_chip);
 221
 222                WARN(ret, "%s(): gpiochip_remove() failed, ret=%d\n",
 223                                __func__, ret);
 224
 225                release_region(gpio_ba, GPIO_IOSIZE);
 226                gpio_ba = 0;
 227        }
 228}
 229module_init(it8761e_gpio_init);
 230module_exit(it8761e_gpio_exit);
 231
 232MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
 233MODULE_DESCRIPTION("GPIO interface for IT8761E Super I/O chip");
 234MODULE_LICENSE("GPL");
 235