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
 129        spin_lock(&gpio_lock);
 130
 131        curr_dirs = inb(gpio_ba + RGIO);
 132
 133        if (!(curr_dirs & (1 << gpio_num)))
 134                outb(curr_dirs | (1 << gpio_num) , gpio_ba + RGIO);
 135
 136        spin_unlock(&gpio_lock);
 137        return 0;
 138}
 139
 140static int sch_gpio_resume_get(struct gpio_chip *gc, unsigned gpio_num)
 141{
 142        return !!(inb(gpio_ba + RGLV) & (1 << gpio_num));
 143}
 144
 145static void sch_gpio_resume_set(struct gpio_chip *gc,
 146                                unsigned gpio_num, int val)
 147{
 148        u8 curr_vals;
 149
 150        spin_lock(&gpio_lock);
 151
 152        curr_vals = inb(gpio_ba + RGLV);
 153
 154        if (val)
 155                outb(curr_vals | (1 << gpio_num), gpio_ba + RGLV);
 156        else
 157                outb((curr_vals & ~(1 << gpio_num)), gpio_ba + RGLV);
 158
 159        spin_unlock(&gpio_lock);
 160}
 161
 162static int sch_gpio_resume_direction_out(struct gpio_chip *gc,
 163                                        unsigned gpio_num, int val)
 164{
 165        u8 curr_dirs;
 166
 167        sch_gpio_resume_set(gc, gpio_num, val);
 168
 169        spin_lock(&gpio_lock);
 170
 171        curr_dirs = inb(gpio_ba + RGIO);
 172        if (curr_dirs & (1 << gpio_num))
 173                outb(curr_dirs & ~(1 << gpio_num), gpio_ba + RGIO);
 174
 175        spin_unlock(&gpio_lock);
 176        return 0;
 177}
 178
 179static struct gpio_chip sch_gpio_resume = {
 180        .label                  = "sch_gpio_resume",
 181        .owner                  = THIS_MODULE,
 182        .direction_input        = sch_gpio_resume_direction_in,
 183        .get                    = sch_gpio_resume_get,
 184        .direction_output       = sch_gpio_resume_direction_out,
 185        .set                    = sch_gpio_resume_set,
 186};
 187
 188static int sch_gpio_probe(struct platform_device *pdev)
 189{
 190        struct resource *res;
 191        int err, id;
 192
 193        id = pdev->id;
 194        if (!id)
 195                return -ENODEV;
 196
 197        res = platform_get_resource(pdev, IORESOURCE_IO, 0);
 198        if (!res)
 199                return -EBUSY;
 200
 201        if (!request_region(res->start, resource_size(res), pdev->name))
 202                return -EBUSY;
 203
 204        gpio_ba = res->start;
 205
 206        switch (id) {
 207                case PCI_DEVICE_ID_INTEL_SCH_LPC:
 208                        sch_gpio_core.base = 0;
 209                        sch_gpio_core.ngpio = 10;
 210
 211                        sch_gpio_resume.base = 10;
 212                        sch_gpio_resume.ngpio = 4;
 213
 214                        /*
 215                         * GPIO[6:0] enabled by default
 216                         * GPIO7 is configured by the CMC as SLPIOVR
 217                         * Enable GPIO[9:8] core powered gpios explicitly
 218                         */
 219                        outb(0x3, gpio_ba + CGEN + 1);
 220                        /*
 221                         * SUS_GPIO[2:0] enabled by default
 222                         * Enable SUS_GPIO3 resume powered gpio explicitly
 223                         */
 224                        outb(0x8, gpio_ba + RGEN);
 225                        break;
 226
 227                case PCI_DEVICE_ID_INTEL_ITC_LPC:
 228                        sch_gpio_core.base = 0;
 229                        sch_gpio_core.ngpio = 5;
 230
 231                        sch_gpio_resume.base = 5;
 232                        sch_gpio_resume.ngpio = 9;
 233                        break;
 234
 235                case PCI_DEVICE_ID_INTEL_CENTERTON_ILB:
 236                        sch_gpio_core.base = 0;
 237                        sch_gpio_core.ngpio = 21;
 238
 239                        sch_gpio_resume.base = 21;
 240                        sch_gpio_resume.ngpio = 9;
 241                        break;
 242
 243                default:
 244                        err = -ENODEV;
 245                        goto err_sch_gpio_core;
 246        }
 247
 248        sch_gpio_core.dev = &pdev->dev;
 249        sch_gpio_resume.dev = &pdev->dev;
 250
 251        err = gpiochip_add(&sch_gpio_core);
 252        if (err < 0)
 253                goto err_sch_gpio_core;
 254
 255        err = gpiochip_add(&sch_gpio_resume);
 256        if (err < 0)
 257                goto err_sch_gpio_resume;
 258
 259        return 0;
 260
 261err_sch_gpio_resume:
 262        err = gpiochip_remove(&sch_gpio_core);
 263        if (err)
 264                dev_err(&pdev->dev, "%s failed, %d\n",
 265                                "gpiochip_remove()", err);
 266
 267err_sch_gpio_core:
 268        release_region(res->start, resource_size(res));
 269        gpio_ba = 0;
 270
 271        return err;
 272}
 273
 274static int sch_gpio_remove(struct platform_device *pdev)
 275{
 276        struct resource *res;
 277        if (gpio_ba) {
 278                int err;
 279
 280                err  = gpiochip_remove(&sch_gpio_core);
 281                if (err)
 282                        dev_err(&pdev->dev, "%s failed, %d\n",
 283                                "gpiochip_remove()", err);
 284                err = gpiochip_remove(&sch_gpio_resume);
 285                if (err)
 286                        dev_err(&pdev->dev, "%s failed, %d\n",
 287                                "gpiochip_remove()", err);
 288
 289                res = platform_get_resource(pdev, IORESOURCE_IO, 0);
 290
 291                release_region(res->start, resource_size(res));
 292                gpio_ba = 0;
 293
 294                return err;
 295        }
 296
 297        return 0;
 298}
 299
 300static struct platform_driver sch_gpio_driver = {
 301        .driver = {
 302                .name = "sch_gpio",
 303                .owner = THIS_MODULE,
 304        },
 305        .probe          = sch_gpio_probe,
 306        .remove         = sch_gpio_remove,
 307};
 308
 309module_platform_driver(sch_gpio_driver);
 310
 311MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
 312MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH");
 313MODULE_LICENSE("GPL");
 314MODULE_ALIAS("platform:sch_gpio");
 315
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.