linux/drivers/gpio/gpio-f7188x.c
<<
>>
Prefs
   1/*
   2 * GPIO driver for Fintek Super-I/O F71882 and F71889
   3 *
   4 * Copyright (C) 2010-2013 LaCie
   5 *
   6 * Author: Simon Guinot <simon.guinot@sequanux.org>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 */
  13
  14#include <linux/module.h>
  15#include <linux/init.h>
  16#include <linux/platform_device.h>
  17#include <linux/io.h>
  18#include <linux/gpio.h>
  19
  20#define DRVNAME "gpio-f7188x"
  21
  22/*
  23 * Super-I/O registers
  24 */
  25#define SIO_LDSEL               0x07    /* Logical device select */
  26#define SIO_DEVID               0x20    /* Device ID (2 bytes) */
  27#define SIO_DEVREV              0x22    /* Device revision */
  28#define SIO_MANID               0x23    /* Fintek ID (2 bytes) */
  29
  30#define SIO_LD_GPIO             0x06    /* GPIO logical device */
  31#define SIO_UNLOCK_KEY          0x87    /* Key to enable Super-I/O */
  32#define SIO_LOCK_KEY            0xAA    /* Key to disable Super-I/O */
  33
  34#define SIO_FINTEK_ID           0x1934  /* Manufacturer ID */
  35#define SIO_F71882_ID           0x0541  /* F71882 chipset ID */
  36#define SIO_F71889_ID           0x0909  /* F71889 chipset ID */
  37
  38enum chips { f71882fg, f71889f };
  39
  40static const char * const f7188x_names[] = {
  41        "f71882fg",
  42        "f71889f",
  43};
  44
  45struct f7188x_sio {
  46        int addr;
  47        enum chips type;
  48};
  49
  50struct f7188x_gpio_bank {
  51        struct gpio_chip chip;
  52        unsigned int regbase;
  53        struct f7188x_gpio_data *data;
  54};
  55
  56struct f7188x_gpio_data {
  57        struct f7188x_sio *sio;
  58        int nr_bank;
  59        struct f7188x_gpio_bank *bank;
  60};
  61
  62/*
  63 * Super-I/O functions.
  64 */
  65
  66static inline int superio_inb(int base, int reg)
  67{
  68        outb(reg, base);
  69        return inb(base + 1);
  70}
  71
  72static int superio_inw(int base, int reg)
  73{
  74        int val;
  75
  76        outb(reg++, base);
  77        val = inb(base + 1) << 8;
  78        outb(reg, base);
  79        val |= inb(base + 1);
  80
  81        return val;
  82}
  83
  84static inline void superio_outb(int base, int reg, int val)
  85{
  86        outb(reg, base);
  87        outb(val, base + 1);
  88}
  89
  90static inline int superio_enter(int base)
  91{
  92        /* Don't step on other drivers' I/O space by accident. */
  93        if (!request_muxed_region(base, 2, DRVNAME)) {
  94                pr_err(DRVNAME "I/O address 0x%04x already in use\n", base);
  95                return -EBUSY;
  96        }
  97
  98        /* According to the datasheet the key must be send twice. */
  99        outb(SIO_UNLOCK_KEY, base);
 100        outb(SIO_UNLOCK_KEY, base);
 101
 102        return 0;
 103}
 104
 105static inline void superio_select(int base, int ld)
 106{
 107        outb(SIO_LDSEL, base);
 108        outb(ld, base + 1);
 109}
 110
 111static inline void superio_exit(int base)
 112{
 113        outb(SIO_LOCK_KEY, base);
 114        release_region(base, 2);
 115}
 116
 117/*
 118 * GPIO chip.
 119 */
 120
 121static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset);
 122static int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset);
 123static int f7188x_gpio_direction_out(struct gpio_chip *chip,
 124                                     unsigned offset, int value);
 125static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value);
 126
 127#define F7188X_GPIO_BANK(_base, _ngpio, _regbase)                       \
 128        {                                                               \
 129                .chip = {                                               \
 130                        .label            = DRVNAME,                    \
 131                        .owner            = THIS_MODULE,                \
 132                        .direction_input  = f7188x_gpio_direction_in,   \
 133                        .get              = f7188x_gpio_get,            \
 134                        .direction_output = f7188x_gpio_direction_out,  \
 135                        .set              = f7188x_gpio_set,            \
 136                        .base             = _base,                      \
 137                        .ngpio            = _ngpio,                     \
 138                },                                                      \
 139                .regbase = _regbase,                                    \
 140        }
 141
 142#define gpio_dir(base) (base + 0)
 143#define gpio_data_out(base) (base + 1)
 144#define gpio_data_in(base) (base + 2)
 145/* Output mode register (0:open drain 1:push-pull). */
 146#define gpio_out_mode(base) (base + 3)
 147
 148static struct f7188x_gpio_bank f71882_gpio_bank[] = {
 149        F7188X_GPIO_BANK(0 , 8, 0xF0),
 150        F7188X_GPIO_BANK(10, 8, 0xE0),
 151        F7188X_GPIO_BANK(20, 8, 0xD0),
 152        F7188X_GPIO_BANK(30, 4, 0xC0),
 153        F7188X_GPIO_BANK(40, 4, 0xB0),
 154};
 155
 156static struct f7188x_gpio_bank f71889_gpio_bank[] = {
 157        F7188X_GPIO_BANK(0 , 7, 0xF0),
 158        F7188X_GPIO_BANK(10, 7, 0xE0),
 159        F7188X_GPIO_BANK(20, 8, 0xD0),
 160        F7188X_GPIO_BANK(30, 8, 0xC0),
 161        F7188X_GPIO_BANK(40, 8, 0xB0),
 162        F7188X_GPIO_BANK(50, 5, 0xA0),
 163        F7188X_GPIO_BANK(60, 8, 0x90),
 164        F7188X_GPIO_BANK(70, 8, 0x80),
 165};
 166
 167static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
 168{
 169        int err;
 170        struct f7188x_gpio_bank *bank =
 171                container_of(chip, struct f7188x_gpio_bank, chip);
 172        struct f7188x_sio *sio = bank->data->sio;
 173        u8 dir;
 174
 175        err = superio_enter(sio->addr);
 176        if (err)
 177                return err;
 178        superio_select(sio->addr, SIO_LD_GPIO);
 179
 180        dir = superio_inb(sio->addr, gpio_dir(bank->regbase));
 181        dir &= ~(1 << offset);
 182        superio_outb(sio->addr, gpio_dir(bank->regbase), dir);
 183
 184        superio_exit(sio->addr);
 185
 186        return 0;
 187}
 188
 189static int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset)
 190{
 191        int err;
 192        struct f7188x_gpio_bank *bank =
 193                container_of(chip, struct f7188x_gpio_bank, chip);
 194        struct f7188x_sio *sio = bank->data->sio;
 195        u8 dir, data;
 196
 197        err = superio_enter(sio->addr);
 198        if (err)
 199                return err;
 200        superio_select(sio->addr, SIO_LD_GPIO);
 201
 202        dir = superio_inb(sio->addr, gpio_dir(bank->regbase));
 203        dir = !!(dir & (1 << offset));
 204        if (dir)
 205                data = superio_inb(sio->addr, gpio_data_out(bank->regbase));
 206        else
 207                data = superio_inb(sio->addr, gpio_data_in(bank->regbase));
 208
 209        superio_exit(sio->addr);
 210
 211        return !!(data & 1 << offset);
 212}
 213
 214static int f7188x_gpio_direction_out(struct gpio_chip *chip,
 215                                     unsigned offset, int value)
 216{
 217        int err;
 218        struct f7188x_gpio_bank *bank =
 219                container_of(chip, struct f7188x_gpio_bank, chip);
 220        struct f7188x_sio *sio = bank->data->sio;
 221        u8 dir, data_out;
 222
 223        err = superio_enter(sio->addr);
 224        if (err)
 225                return err;
 226        superio_select(sio->addr, SIO_LD_GPIO);
 227
 228        data_out = superio_inb(sio->addr, gpio_data_out(bank->regbase));
 229        if (value)
 230                data_out |= (1 << offset);
 231        else
 232                data_out &= ~(1 << offset);
 233        superio_outb(sio->addr, gpio_data_out(bank->regbase), data_out);
 234
 235        dir = superio_inb(sio->addr, gpio_dir(bank->regbase));
 236        dir |= (1 << offset);
 237        superio_outb(sio->addr, gpio_dir(bank->regbase), dir);
 238
 239        superio_exit(sio->addr);
 240
 241        return 0;
 242}
 243
 244static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
 245{
 246        int err;
 247        struct f7188x_gpio_bank *bank =
 248                container_of(chip, struct f7188x_gpio_bank, chip);
 249        struct f7188x_sio *sio = bank->data->sio;
 250        u8 data_out;
 251
 252        err = superio_enter(sio->addr);
 253        if (err)
 254                return;
 255        superio_select(sio->addr, SIO_LD_GPIO);
 256
 257        data_out = superio_inb(sio->addr, gpio_data_out(bank->regbase));
 258        if (value)
 259                data_out |= (1 << offset);
 260        else
 261                data_out &= ~(1 << offset);
 262        superio_outb(sio->addr, gpio_data_out(bank->regbase), data_out);
 263
 264        superio_exit(sio->addr);
 265}
 266
 267/*
 268 * Platform device and driver.
 269 */
 270
 271static int f7188x_gpio_probe(struct platform_device *pdev)
 272{
 273        int err;
 274        int i;
 275        struct f7188x_sio *sio = pdev->dev.platform_data;
 276        struct f7188x_gpio_data *data;
 277
 278        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 279        if (!data)
 280                return -ENOMEM;
 281
 282        switch (sio->type) {
 283        case f71882fg:
 284                data->nr_bank = ARRAY_SIZE(f71882_gpio_bank);
 285                data->bank = f71882_gpio_bank;
 286                break;
 287        case f71889f:
 288                data->nr_bank = ARRAY_SIZE(f71889_gpio_bank);
 289                data->bank = f71889_gpio_bank;
 290                break;
 291        default:
 292                return -ENODEV;
 293        }
 294        data->sio = sio;
 295
 296        platform_set_drvdata(pdev, data);
 297
 298        /* For each GPIO bank, register a GPIO chip. */
 299        for (i = 0; i < data->nr_bank; i++) {
 300                struct f7188x_gpio_bank *bank = &data->bank[i];
 301
 302                bank->chip.dev = &pdev->dev;
 303                bank->data = data;
 304
 305                err = gpiochip_add(&bank->chip);
 306                if (err) {
 307                        dev_err(&pdev->dev,
 308                                "Failed to register gpiochip %d: %d\n",
 309                                i, err);
 310                        goto err_gpiochip;
 311                }
 312        }
 313
 314        return 0;
 315
 316err_gpiochip:
 317        for (i = i - 1; i >= 0; i--) {
 318                struct f7188x_gpio_bank *bank = &data->bank[i];
 319                int tmp;
 320
 321                tmp = gpiochip_remove(&bank->chip);
 322                if (tmp < 0)
 323                        dev_err(&pdev->dev,
 324                                "Failed to remove gpiochip %d: %d\n",
 325                                i, tmp);
 326        }
 327
 328        return err;
 329}
 330
 331static int f7188x_gpio_remove(struct platform_device *pdev)
 332{
 333        int err;
 334        int i;
 335        struct f7188x_gpio_data *data = platform_get_drvdata(pdev);
 336
 337        for (i = 0; i < data->nr_bank; i++) {
 338                struct f7188x_gpio_bank *bank = &data->bank[i];
 339
 340                err = gpiochip_remove(&bank->chip);
 341                if (err) {
 342                        dev_err(&pdev->dev,
 343                                "Failed to remove GPIO gpiochip %d: %d\n",
 344                                i, err);
 345                        return err;
 346                }
 347        }
 348
 349        return 0;
 350}
 351
 352static int __init f7188x_find(int addr, struct f7188x_sio *sio)
 353{
 354        int err;
 355        u16 devid;
 356
 357        err = superio_enter(addr);
 358        if (err)
 359                return err;
 360
 361        err = -ENODEV;
 362        devid = superio_inw(addr, SIO_MANID);
 363        if (devid != SIO_FINTEK_ID) {
 364                pr_debug(DRVNAME ": Not a Fintek device at 0x%08x\n", addr);
 365                goto err;
 366        }
 367
 368        devid = superio_inw(addr, SIO_DEVID);
 369        switch (devid) {
 370        case SIO_F71882_ID:
 371                sio->type = f71882fg;
 372                break;
 373        case SIO_F71889_ID:
 374                sio->type = f71889f;
 375                break;
 376        default:
 377                pr_info(DRVNAME ": Unsupported Fintek device 0x%04x\n", devid);
 378                goto err;
 379        }
 380        sio->addr = addr;
 381        err = 0;
 382
 383        pr_info(DRVNAME ": Found %s at %#x, revision %d\n",
 384                f7188x_names[sio->type],
 385                (unsigned int) addr,
 386                (int) superio_inb(addr, SIO_DEVREV));
 387
 388err:
 389        superio_exit(addr);
 390        return err;
 391}
 392
 393static struct platform_device *f7188x_gpio_pdev;
 394
 395static int __init
 396f7188x_gpio_device_add(const struct f7188x_sio *sio)
 397{
 398        int err;
 399
 400        f7188x_gpio_pdev = platform_device_alloc(DRVNAME, -1);
 401        if (!f7188x_gpio_pdev)
 402                return -ENOMEM;
 403
 404        err = platform_device_add_data(f7188x_gpio_pdev,
 405                                       sio, sizeof(*sio));
 406        if (err) {
 407                pr_err(DRVNAME "Platform data allocation failed\n");
 408                goto err;
 409        }
 410
 411        err = platform_device_add(f7188x_gpio_pdev);
 412        if (err) {
 413                pr_err(DRVNAME "Device addition failed\n");
 414                goto err;
 415        }
 416
 417        return 0;
 418
 419err:
 420        platform_device_put(f7188x_gpio_pdev);
 421
 422        return err;
 423}
 424
 425/*
 426 * Try to match a supported Fintech device by reading the (hard-wired)
 427 * configuration I/O ports. If available, then register both the platform
 428 * device and driver to support the GPIOs.
 429 */
 430
 431static struct platform_driver f7188x_gpio_driver = {
 432        .driver = {
 433                .owner  = THIS_MODULE,
 434                .name   = DRVNAME,
 435        },
 436        .probe          = f7188x_gpio_probe,
 437        .remove         = f7188x_gpio_remove,
 438};
 439
 440static int __init f7188x_gpio_init(void)
 441{
 442        int err;
 443        struct f7188x_sio sio;
 444
 445        if (f7188x_find(0x2e, &sio) &&
 446            f7188x_find(0x4e, &sio))
 447                return -ENODEV;
 448
 449        err = platform_driver_register(&f7188x_gpio_driver);
 450        if (!err) {
 451                err = f7188x_gpio_device_add(&sio);
 452                if (err)
 453                        platform_driver_unregister(&f7188x_gpio_driver);
 454        }
 455
 456        return err;
 457}
 458subsys_initcall(f7188x_gpio_init);
 459
 460static void __exit f7188x_gpio_exit(void)
 461{
 462        platform_device_unregister(f7188x_gpio_pdev);
 463        platform_driver_unregister(&f7188x_gpio_driver);
 464}
 465module_exit(f7188x_gpio_exit);
 466
 467MODULE_DESCRIPTION("GPIO driver for Super-I/O chips F71882FG and F71889F");
 468MODULE_AUTHOR("Simon Guinot <simon.guinot@sequanux.org>");
 469MODULE_LICENSE("GPL");
 470
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.