linux/drivers/gpio/gpio-sch.c
<<
>>
Prefs
   1/*
   2 * GPIO interface for Intel Poulsbo SCH
   3 *
   4 *  Copyright (c) 2010 CompuLab Ltd
   5 *  Author: Denis Turischev <denis@compulab.co.il>
   6 *
   7 *  This program is free software; you can redistribute it and/or modify
   8 *  it under the terms of the GNU General Public License 2 as published
   9 *  by the Free Software Foundation.
  10 *
  11 *  This program is distributed in the hope that it will be useful,
  12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *  GNU General Public License for more details.
  15 *
  16 *  You should have received a copy of the GNU General Public License
  17 *  along with this program; see the file COPYING.  If not, write to
  18 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  19 */
  20
  21#include <linux/init.h>
  22#include <linux/kernel.h>
  23#include <linux/module.h>
  24#include <linux/io.h>
  25#include <linux/errno.h>
  26#include <linux/acpi.h>
  27#include <linux/platform_device.h>
  28#include <linux/pci_ids.h>
  29
  30#include <linux/gpio.h>
  31
  32static DEFINE_SPINLOCK(gpio_lock);
  33
  34#define CGEN    (0x00)
  35#define CGIO    (0x04)
  36#define CGLV    (0x08)
  37
  38#define RGEN    (0x20)
  39#define RGIO    (0x24)
  40#define RGLV    (0x28)
  41
  42static unsigned short gpio_ba;
  43
  44static int sch_gpio_core_direction_in(struct gpio_chip *gc, unsigned  gpio_num)
  45{
  46        u8 curr_dirs;
  47        unsigned short offset, bit;
  48
  49        spin_lock(&gpio_lock);
  50
  51        offset = CGIO + gpio_num / 8;
  52        bit = gpio_num % 8;
  53
  54        curr_dirs = inb(gpio_ba + offset);
  55
  56        if (!(curr_dirs & (1 << bit)))
  57                outb(curr_dirs | (1 << bit), gpio_ba + offset);
  58
  59        spin_unlock(&gpio_lock);
  60        return 0;
  61}
  62
  63static int sch_gpio_core_get(struct gpio_chip *gc, unsigned gpio_num)
  64{
  65        int res;
  66        unsigned short offset, bit;
  67
  68        offset = CGLV + gpio_num / 8;
  69        bit = gpio_num % 8;
  70
  71        res = !!(inb(gpio_ba + offset) & (1 << bit));
  72        return res;
  73}
  74
  75static void sch_gpio_core_set(struct gpio_chip *gc, unsigned gpio_num, int val)
  76{
  77        u8 curr_vals;
  78        unsigned short offset, bit;
  79
  80        spin_lock(&gpio_lock);
  81
  82        offset = CGLV + gpio_num / 8;
  83        bit = gpio_num % 8;
  84
  85        curr_vals = inb(gpio_ba + offset);
  86
  87        if (val)
  88                outb(curr_vals | (1 << bit), gpio_ba + offset);
  89        else
  90                outb((curr_vals & ~(1 << bit)), gpio_ba + offset);
  91        spin_unlock(&gpio_lock);
  92}
  93
  94static int sch_gpio_core_direction_out(struct gpio_chip *gc,
  95                                        unsigned gpio_num, int val)
  96{
  97        u8 curr_dirs;
  98        unsigned short offset, bit;
  99
 100        sch_gpio_core_set(gc, gpio_num, val);
 101
 102        spin_lock(&gpio_lock);
 103
 104        offset = CGIO + gpio_num / 8;
 105        bit = gpio_num % 8;
 106
 107        curr_dirs = inb(gpio_ba + offset);
 108        if (curr_dirs & (1 << bit))
 109                outb(curr_dirs & ~(1 << bit), gpio_ba + offset);
 110
 111        spin_unlock(&gpio_lock);
 112        return 0;
 113}
 114
 115static struct gpio_chip sch_gpio_core = {
 116        .label                  = "sch_gpio_core",
 117        .owner                  = THIS_MODULE,
 118        .direction_input        = sch_gpio_core_direction_in,
 119        .get                    = sch_gpio_core_get,
 120        .direction_output       = sch_gpio_core_direction_out,
 121        .set                    = sch_gpio_core_set,
 122};
 123
 124static int sch_gpio_resume_direction_in(struct gpio_chip *gc,
 125                                        unsigned gpio_num)
 126{
 127        u8 curr_dirs;
 128        unsigned short offset, bit;
 129
 130        spin_lock(&gpio_lock);
 131
 132        offset = RGIO + gpio_num / 8;
 133        bit = gpio_num % 8;
 134
 135        curr_dirs = inb(gpio_ba + offset);
 136
 137        if (!(curr_dirs & (1 << bit)))
 138                outb(curr_dirs | (1 << bit), gpio_ba + offset);
 139
 140        spin_unlock(&gpio_lock);
 141        return 0;
 142}
 143
 144static int sch_gpio_resume_get(struct gpio_chip *gc, unsigned gpio_num)
 145{
 146        unsigned short offset, bit;
 147
 148        offset = RGLV + gpio_num / 8;
 149        bit = gpio_num % 8;
 150
 151        return !!(inb(gpio_ba + offset) & (1 << bit));
 152}
 153
 154static void sch_gpio_resume_set(struct gpio_chip *gc,
 155                                unsigned gpio_num, int val)
 156{
 157        u8 curr_vals;
 158        unsigned short offset, bit;
 159
 160        spin_lock(&gpio_lock);
 161
 162        offset = RGLV + gpio_num / 8;
 163        bit = gpio_num % 8;
 164
 165        curr_vals = inb(gpio_ba + offset);
 166
 167        if (val)
 168                outb(curr_vals | (1 << bit), gpio_ba + offset);
 169        else
 170                outb((curr_vals & ~(1 << bit)), gpio_ba + offset);
 171
 172        spin_unlock(&gpio_lock);
 173}
 174
 175static int sch_gpio_resume_direction_out(struct gpio_chip *gc,
 176                                        unsigned gpio_num, int val)
 177{
 178        u8 curr_dirs;
 179        unsigned short offset, bit;
 180
 181        sch_gpio_resume_set(gc, gpio_num, val);
 182
 183        offset = RGIO + gpio_num / 8;
 184        bit = gpio_num % 8;
 185
 186        spin_lock(&gpio_lock);
 187
 188        curr_dirs = inb(gpio_ba + offset);
 189        if (curr_dirs & (1 << bit))
 190                outb(curr_dirs & ~(1 << bit), gpio_ba + offset);
 191
 192        spin_unlock(&gpio_lock);
 193        return 0;
 194}
 195
 196static struct gpio_chip sch_gpio_resume = {
 197        .label                  = "sch_gpio_resume",
 198        .owner                  = THIS_MODULE,
 199        .direction_input        = sch_gpio_resume_direction_in,
 200        .get                    = sch_gpio_resume_get,
 201        .direction_output       = sch_gpio_resume_direction_out,
 202        .set                    = sch_gpio_resume_set,
 203};
 204
 205static int sch_gpio_probe(struct platform_device *pdev)
 206{
 207        struct resource *res;
 208        int err, id;
 209
 210        id = pdev->id;
 211        if (!id)
 212                return -ENODEV;
 213
 214        res = platform_get_resource(pdev, IORESOURCE_IO, 0);
 215        if (!res)
 216                return -EBUSY;
 217
 218        if (!request_region(res->start, resource_size(res), pdev->name))
 219                return -EBUSY;
 220
 221        gpio_ba = res->start;
 222
 223        switch (id) {
 224        case PCI_DEVICE_ID_INTEL_SCH_LPC:
 225                sch_gpio_core.base = 0;
 226                sch_gpio_core.ngpio = 10;
 227                sch_gpio_resume.base = 10;
 228                sch_gpio_resume.ngpio = 4;
 229                /*
 230                 * GPIO[6:0] enabled by default
 231                 * GPIO7 is configured by the CMC as SLPIOVR
 232                 * Enable GPIO[9:8] core powered gpios explicitly
 233                 */
 234                outb(0x3, gpio_ba + CGEN + 1);
 235                /*
 236                 * SUS_GPIO[2:0] enabled by default
 237                 * Enable SUS_GPIO3 resume powered gpio explicitly
 238                 */
 239                outb(0x8, gpio_ba + RGEN);
 240                break;
 241
 242        case PCI_DEVICE_ID_INTEL_ITC_LPC:
 243                sch_gpio_core.base = 0;
 244                sch_gpio_core.ngpio = 5;
 245                sch_gpio_resume.base = 5;
 246                sch_gpio_resume.ngpio = 9;
 247                break;
 248
 249        case PCI_DEVICE_ID_INTEL_CENTERTON_ILB:
 250                sch_gpio_core.base = 0;
 251                sch_gpio_core.ngpio = 21;
 252                sch_gpio_resume.base = 21;
 253                sch_gpio_resume.ngpio = 9;
 254                break;
 255
 256        default:
 257                err = -ENODEV;
 258                goto err_sch_gpio_core;
 259        }
 260
 261        sch_gpio_core.dev = &pdev->dev;
 262        sch_gpio_resume.dev = &pdev->dev;
 263
 264        err = gpiochip_add(&sch_gpio_core);
 265        if (err < 0)
 266                goto err_sch_gpio_core;
 267
 268        err = gpiochip_add(&sch_gpio_resume);
 269        if (err < 0)
 270                goto err_sch_gpio_resume;
 271
 272        return 0;
 273
 274err_sch_gpio_resume:
 275        if (gpiochip_remove(&sch_gpio_core))
 276                dev_err(&pdev->dev, "%s gpiochip_remove failed\n", __func__);
 277
 278err_sch_gpio_core:
 279        release_region(res->start, resource_size(res));
 280        gpio_ba = 0;
 281
 282        return err;
 283}
 284
 285static int sch_gpio_remove(struct platform_device *pdev)
 286{
 287        struct resource *res;
 288        if (gpio_ba) {
 289                int err;
 290
 291                err  = gpiochip_remove(&sch_gpio_core);
 292                if (err)
 293                        dev_err(&pdev->dev, "%s failed, %d\n",
 294                                "gpiochip_remove()", err);
 295                err = gpiochip_remove(&sch_gpio_resume);
 296                if (err)
 297                        dev_err(&pdev->dev, "%s failed, %d\n",
 298                                "gpiochip_remove()", err);
 299
 300                res = platform_get_resource(pdev, IORESOURCE_IO, 0);
 301
 302                release_region(res->start, resource_size(res));
 303                gpio_ba = 0;
 304
 305                return err;
 306        }
 307
 308        return 0;
 309}
 310
 311static struct platform_driver sch_gpio_driver = {
 312        .driver = {
 313                .name = "sch_gpio",
 314                .owner = THIS_MODULE,
 315        },
 316        .probe          = sch_gpio_probe,
 317        .remove         = sch_gpio_remove,
 318};
 319
 320module_platform_driver(sch_gpio_driver);
 321
 322MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
 323MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH");
 324MODULE_LICENSE("GPL");
 325MODULE_ALIAS("platform:sch_gpio");
 326