linux/drivers/gpio/gpio-it87.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 *  GPIO interface for IT87xx Super I/O chips
   4 *
   5 *  Author: Diego Elio Pettenò <flameeyes@flameeyes.eu>
   6 *  Copyright (c) 2017 Google, Inc.
   7 *
   8 *  Based on it87_wdt.c     by Oliver Schuster
   9 *           gpio-it8761e.c by Denis Turischev
  10 *           gpio-stmpe.c   by Rabin Vincent
  11 */
  12
  13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  14
  15#include <linux/init.h>
  16#include <linux/kernel.h>
  17#include <linux/module.h>
  18#include <linux/io.h>
  19#include <linux/errno.h>
  20#include <linux/ioport.h>
  21#include <linux/slab.h>
  22#include <linux/gpio/driver.h>
  23
  24/* Chip Id numbers */
  25#define NO_DEV_ID       0xffff
  26#define IT8613_ID       0x8613
  27#define IT8620_ID       0x8620
  28#define IT8628_ID       0x8628
  29#define IT8718_ID       0x8718
  30#define IT8728_ID       0x8728
  31#define IT8732_ID       0x8732
  32#define IT8761_ID       0x8761
  33#define IT8772_ID       0x8772
  34#define IT8786_ID       0x8786
  35
  36/* IO Ports */
  37#define REG             0x2e
  38#define VAL             0x2f
  39
  40/* Logical device Numbers LDN */
  41#define GPIO            0x07
  42
  43/* Configuration Registers and Functions */
  44#define LDNREG          0x07
  45#define CHIPID          0x20
  46#define CHIPREV         0x22
  47
  48/**
  49 * struct it87_gpio - it87-specific GPIO chip
  50 * @chip the underlying gpio_chip structure
  51 * @lock a lock to avoid races between operations
  52 * @io_base base address for gpio ports
  53 * @io_size size of the port rage starting from io_base.
  54 * @output_base Super I/O register address for Output Enable register
  55 * @simple_base Super I/O 'Simple I/O' Enable register
  56 * @simple_size Super IO 'Simple I/O' Enable register size; this is
  57 *      required because IT87xx chips might only provide Simple I/O
  58 *      switches on a subset of lines, whereas the others keep the
  59 *      same status all time.
  60 */
  61struct it87_gpio {
  62        struct gpio_chip chip;
  63        spinlock_t lock;
  64        u16 io_base;
  65        u16 io_size;
  66        u8 output_base;
  67        u8 simple_base;
  68        u8 simple_size;
  69};
  70
  71static struct it87_gpio it87_gpio_chip = {
  72        .lock = __SPIN_LOCK_UNLOCKED(it87_gpio_chip.lock),
  73};
  74
  75/* Superio chip access functions; copied from wdt_it87 */
  76
  77static inline int superio_enter(void)
  78{
  79        /*
  80         * Try to reserve REG and REG + 1 for exclusive access.
  81         */
  82        if (!request_muxed_region(REG, 2, KBUILD_MODNAME))
  83                return -EBUSY;
  84
  85        outb(0x87, REG);
  86        outb(0x01, REG);
  87        outb(0x55, REG);
  88        outb(0x55, REG);
  89        return 0;
  90}
  91
  92static inline void superio_exit(void)
  93{
  94        outb(0x02, REG);
  95        outb(0x02, VAL);
  96        release_region(REG, 2);
  97}
  98
  99static inline void superio_select(int ldn)
 100{
 101        outb(LDNREG, REG);
 102        outb(ldn, VAL);
 103}
 104
 105static inline int superio_inb(int reg)
 106{
 107        outb(reg, REG);
 108        return inb(VAL);
 109}
 110
 111static inline void superio_outb(int val, int reg)
 112{
 113        outb(reg, REG);
 114        outb(val, VAL);
 115}
 116
 117static inline int superio_inw(int reg)
 118{
 119        int val;
 120
 121        outb(reg++, REG);
 122        val = inb(VAL) << 8;
 123        outb(reg, REG);
 124        val |= inb(VAL);
 125        return val;
 126}
 127
 128static inline void superio_outw(int val, int reg)
 129{
 130        outb(reg++, REG);
 131        outb(val >> 8, VAL);
 132        outb(reg, REG);
 133        outb(val, VAL);
 134}
 135
 136static inline void superio_set_mask(int mask, int reg)
 137{
 138        u8 curr_val = superio_inb(reg);
 139        u8 new_val = curr_val | mask;
 140
 141        if (curr_val != new_val)
 142                superio_outb(new_val, reg);
 143}
 144
 145static inline void superio_clear_mask(int mask, int reg)
 146{
 147        u8 curr_val = superio_inb(reg);
 148        u8 new_val = curr_val & ~mask;
 149
 150        if (curr_val != new_val)
 151                superio_outb(new_val, reg);
 152}
 153
 154static int it87_gpio_request(struct gpio_chip *chip, unsigned gpio_num)
 155{
 156        u8 mask, group;
 157        int rc = 0;
 158        struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
 159
 160        mask = 1 << (gpio_num % 8);
 161        group = (gpio_num / 8);
 162
 163        spin_lock(&it87_gpio->lock);
 164
 165        rc = superio_enter();
 166        if (rc)
 167                goto exit;
 168
 169        /* not all the IT87xx chips support Simple I/O and not all of
 170         * them allow all the lines to be set/unset to Simple I/O.
 171         */
 172        if (group < it87_gpio->simple_size)
 173                superio_set_mask(mask, group + it87_gpio->simple_base);
 174
 175        /* clear output enable, setting the pin to input, as all the
 176         * newly-exported GPIO interfaces are set to input.
 177         */
 178        superio_clear_mask(mask, group + it87_gpio->output_base);
 179
 180        superio_exit();
 181
 182exit:
 183        spin_unlock(&it87_gpio->lock);
 184        return rc;
 185}
 186
 187static int it87_gpio_get(struct gpio_chip *chip, unsigned gpio_num)
 188{
 189        u16 reg;
 190        u8 mask;
 191        struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
 192
 193        mask = 1 << (gpio_num % 8);
 194        reg = (gpio_num / 8) + it87_gpio->io_base;
 195
 196        return !!(inb(reg) & mask);
 197}
 198
 199static int it87_gpio_direction_in(struct gpio_chip *chip, unsigned gpio_num)
 200{
 201        u8 mask, group;
 202        int rc = 0;
 203        struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
 204
 205        mask = 1 << (gpio_num % 8);
 206        group = (gpio_num / 8);
 207
 208        spin_lock(&it87_gpio->lock);
 209
 210        rc = superio_enter();
 211        if (rc)
 212                goto exit;
 213
 214        /* clear the output enable bit */
 215        superio_clear_mask(mask, group + it87_gpio->output_base);
 216
 217        superio_exit();
 218
 219exit:
 220        spin_unlock(&it87_gpio->lock);
 221        return rc;
 222}
 223
 224static void it87_gpio_set(struct gpio_chip *chip,
 225                          unsigned gpio_num, int val)
 226{
 227        u8 mask, curr_vals;
 228        u16 reg;
 229        struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
 230
 231        mask = 1 << (gpio_num % 8);
 232        reg = (gpio_num / 8) + it87_gpio->io_base;
 233
 234        curr_vals = inb(reg);
 235        if (val)
 236                outb(curr_vals | mask, reg);
 237        else
 238                outb(curr_vals & ~mask, reg);
 239}
 240
 241static int it87_gpio_direction_out(struct gpio_chip *chip,
 242                                   unsigned gpio_num, int val)
 243{
 244        u8 mask, group;
 245        int rc = 0;
 246        struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
 247
 248        mask = 1 << (gpio_num % 8);
 249        group = (gpio_num / 8);
 250
 251        spin_lock(&it87_gpio->lock);
 252
 253        rc = superio_enter();
 254        if (rc)
 255                goto exit;
 256
 257        /* set the output enable bit */
 258        superio_set_mask(mask, group + it87_gpio->output_base);
 259
 260        it87_gpio_set(chip, gpio_num, val);
 261
 262        superio_exit();
 263
 264exit:
 265        spin_unlock(&it87_gpio->lock);
 266        return rc;
 267}
 268
 269static const struct gpio_chip it87_template_chip = {
 270        .label                  = KBUILD_MODNAME,
 271        .owner                  = THIS_MODULE,
 272        .request                = it87_gpio_request,
 273        .get                    = it87_gpio_get,
 274        .direction_input        = it87_gpio_direction_in,
 275        .set                    = it87_gpio_set,
 276        .direction_output       = it87_gpio_direction_out,
 277        .base                   = -1
 278};
 279
 280static int __init it87_gpio_init(void)
 281{
 282        int rc = 0, i;
 283        u16 chip_type;
 284        u8 chip_rev, gpio_ba_reg;
 285        char *labels, **labels_table;
 286
 287        struct it87_gpio *it87_gpio = &it87_gpio_chip;
 288
 289        rc = superio_enter();
 290        if (rc)
 291                return rc;
 292
 293        chip_type = superio_inw(CHIPID);
 294        chip_rev  = superio_inb(CHIPREV) & 0x0f;
 295        superio_exit();
 296
 297        it87_gpio->chip = it87_template_chip;
 298
 299        switch (chip_type) {
 300        case IT8613_ID:
 301                gpio_ba_reg = 0x62;
 302                it87_gpio->io_size = 8;  /* it8613 only needs 6, use 8 for alignment */
 303                it87_gpio->output_base = 0xc8;
 304                it87_gpio->simple_base = 0xc0;
 305                it87_gpio->simple_size = 6;
 306                it87_gpio->chip.ngpio = 64;  /* has 48, use 64 for convenient calc */
 307                break;
 308        case IT8620_ID:
 309        case IT8628_ID:
 310                gpio_ba_reg = 0x62;
 311                it87_gpio->io_size = 11;
 312                it87_gpio->output_base = 0xc8;
 313                it87_gpio->simple_size = 0;
 314                it87_gpio->chip.ngpio = 64;
 315                break;
 316        case IT8718_ID:
 317        case IT8728_ID:
 318        case IT8732_ID:
 319        case IT8772_ID:
 320        case IT8786_ID:
 321                gpio_ba_reg = 0x62;
 322                it87_gpio->io_size = 8;
 323                it87_gpio->output_base = 0xc8;
 324                it87_gpio->simple_base = 0xc0;
 325                it87_gpio->simple_size = 5;
 326                it87_gpio->chip.ngpio = 64;
 327                break;
 328        case IT8761_ID:
 329                gpio_ba_reg = 0x60;
 330                it87_gpio->io_size = 4;
 331                it87_gpio->output_base = 0xf0;
 332                it87_gpio->simple_size = 0;
 333                it87_gpio->chip.ngpio = 16;
 334                break;
 335        case NO_DEV_ID:
 336                pr_err("no device\n");
 337                return -ENODEV;
 338        default:
 339                pr_err("Unknown Chip found, Chip %04x Revision %x\n",
 340                       chip_type, chip_rev);
 341                return -ENODEV;
 342        }
 343
 344        rc = superio_enter();
 345        if (rc)
 346                return rc;
 347
 348        superio_select(GPIO);
 349
 350        /* fetch GPIO base address */
 351        it87_gpio->io_base = superio_inw(gpio_ba_reg);
 352
 353        superio_exit();
 354
 355        pr_info("Found Chip IT%04x rev %x. %u GPIO lines starting at %04xh\n",
 356                chip_type, chip_rev, it87_gpio->chip.ngpio,
 357                it87_gpio->io_base);
 358
 359        if (!request_region(it87_gpio->io_base, it87_gpio->io_size,
 360                                                        KBUILD_MODNAME))
 361                return -EBUSY;
 362
 363        /* Set up aliases for the GPIO connection.
 364         *
 365         * ITE documentation for recent chips such as the IT8728F
 366         * refers to the GPIO lines as GPxy, with a coordinates system
 367         * where x is the GPIO group (starting from 1) and y is the
 368         * bit within the group.
 369         *
 370         * By creating these aliases, we make it easier to understand
 371         * to which GPIO pin we're referring to.
 372         */
 373        labels = kcalloc(it87_gpio->chip.ngpio, sizeof("it87_gpXY"),
 374                                                                GFP_KERNEL);
 375        labels_table = kcalloc(it87_gpio->chip.ngpio, sizeof(const char *),
 376                                                                GFP_KERNEL);
 377
 378        if (!labels || !labels_table) {
 379                rc = -ENOMEM;
 380                goto labels_free;
 381        }
 382
 383        for (i = 0; i < it87_gpio->chip.ngpio; i++) {
 384                char *label = &labels[i * sizeof("it87_gpXY")];
 385
 386                sprintf(label, "it87_gp%u%u", 1+(i/8), i%8);
 387                labels_table[i] = label;
 388        }
 389
 390        it87_gpio->chip.names = (const char *const*)labels_table;
 391
 392        rc = gpiochip_add_data(&it87_gpio->chip, it87_gpio);
 393        if (rc)
 394                goto labels_free;
 395
 396        return 0;
 397
 398labels_free:
 399        kfree(labels_table);
 400        kfree(labels);
 401        release_region(it87_gpio->io_base, it87_gpio->io_size);
 402        return rc;
 403}
 404
 405static void __exit it87_gpio_exit(void)
 406{
 407        struct it87_gpio *it87_gpio = &it87_gpio_chip;
 408
 409        gpiochip_remove(&it87_gpio->chip);
 410        release_region(it87_gpio->io_base, it87_gpio->io_size);
 411        kfree(it87_gpio->chip.names[0]);
 412        kfree(it87_gpio->chip.names);
 413}
 414
 415module_init(it87_gpio_init);
 416module_exit(it87_gpio_exit);
 417
 418MODULE_AUTHOR("Diego Elio Pettenò <flameeyes@flameeyes.eu>");
 419MODULE_DESCRIPTION("GPIO interface for IT87xx Super I/O chips");
 420MODULE_LICENSE("GPL");
 421