linux/drivers/gpio/sch_gpio.c
<<
>>
Prefs
   1/*
   2 *  sch_gpio.c - 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
  29#include <linux/gpio.h>
  30
  31static DEFINE_SPINLOCK(gpio_lock);
  32
  33#define CGEN    (0x00)
  34#define CGIO    (0x04)
  35#define CGLV    (0x08)
  36
  37#define RGEN    (0x20)
  38#define RGIO    (0x24)
  39#define RGLV    (0x28)
  40
  41static unsigned short gpio_ba;
  42
  43static int sch_gpio_core_direction_in(struct gpio_chip *gc, unsigned  gpio_num)
  44{
  45        u8 curr_dirs;
  46        unsigned short offset, bit;
  47
  48        spin_lock(&gpio_lock);
  49
  50        offset = CGIO + gpio_num / 8;
  51        bit = gpio_num % 8;
  52
  53        curr_dirs = inb(gpio_ba + offset);
  54
  55        if (!(curr_dirs & (1 << bit)))
  56                outb(curr_dirs | (1 << bit), gpio_ba + offset);
  57
  58        spin_unlock(&gpio_lock);
  59        return 0;
  60}
  61
  62static int sch_gpio_core_get(struct gpio_chip *gc, unsigned gpio_num)
  63{
  64        int res;
  65        unsigned short offset, bit;
  66
  67        offset = CGLV + gpio_num / 8;
  68        bit = gpio_num % 8;
  69
  70        res = !!(inb(gpio_ba + offset) & (1 << bit));
  71        return res;
  72}
  73
  74static void sch_gpio_core_set(struct gpio_chip *gc, unsigned gpio_num, int val)
  75{
  76        u8 curr_vals;
  77        unsigned short offset, bit;
  78
  79        spin_lock(&gpio_lock);
  80
  81        offset = CGLV + gpio_num / 8;
  82        bit = gpio_num % 8;
  83
  84        curr_vals = inb(gpio_ba + offset);
  85
  86        if (val)
  87                outb(curr_vals | (1 << bit), gpio_ba + offset);
  88        else
  89                outb((curr_vals & ~(1 << bit)), gpio_ba + offset);
  90        spin_unlock(&gpio_lock);
  91}
  92
  93static int sch_gpio_core_direction_out(struct gpio_chip *gc,
  94                                        unsigned gpio_num, int val)
  95{
  96        u8 curr_dirs;
  97        unsigned short offset, bit;
  98
  99        sch_gpio_core_set(gc, gpio_num, val);
 100
 101        spin_lock(&gpio_lock);
 102
 103        offset = CGIO + gpio_num / 8;
 104        bit = gpio_num % 8;
 105
 106        curr_dirs = inb(gpio_ba + offset);
 107        if (curr_dirs & (1 << bit))
 108                outb(curr_dirs & ~(1 << bit), gpio_ba + offset);
 109
 110        spin_unlock(&gpio_lock);
 111        return 0;
 112}
 113
 114static struct gpio_chip sch_gpio_core = {
 115        .label                  = "sch_gpio_core",
 116        .owner                  = THIS_MODULE,
 117        .direction_input        = sch_gpio_core_direction_in,
 118        .get                    = sch_gpio_core_get,
 119        .direction_output       = sch_gpio_core_direction_out,
 120        .set                    = sch_gpio_core_set,
 121};
 122
 123static int sch_gpio_resume_direction_in(struct gpio_chip *gc,
 124                                        unsigned gpio_num)
 125{
 126        u8 curr_dirs;
 127
 128        spin_lock(&gpio_lock);
 129
 130        curr_dirs = inb(gpio_ba + RGIO);
 131
 132        if (!(curr_dirs & (1 << gpio_num)))
 133                outb(curr_dirs | (1 << gpio_num) , gpio_ba + RGIO);
 134
 135        spin_unlock(&gpio_lock);
 136        return 0;
 137}
 138
 139static int sch_gpio_resume_get(struct gpio_chip *gc, unsigned gpio_num)
 140{
 141        return !!(inb(gpio_ba + RGLV) & (1 << gpio_num));
 142}
 143
 144static void sch_gpio_resume_set(struct gpio_chip *gc,
 145                                unsigned gpio_num, int val)
 146{
 147        u8 curr_vals;
 148
 149        spin_lock(&gpio_lock);
 150
 151        curr_vals = inb(gpio_ba + RGLV);
 152
 153        if (val)
 154                outb(curr_vals | (1 << gpio_num), gpio_ba + RGLV);
 155        else
 156                outb((curr_vals & ~(1 << gpio_num)), gpio_ba + RGLV);
 157
 158        spin_unlock(&gpio_lock);
 159}
 160
 161static int sch_gpio_resume_direction_out(struct gpio_chip *gc,
 162                                        unsigned gpio_num, int val)
 163{
 164        u8 curr_dirs;
 165
 166        sch_gpio_resume_set(gc, gpio_num, val);
 167
 168        spin_lock(&gpio_lock);
 169
 170        curr_dirs = inb(gpio_ba + RGIO);
 171        if (curr_dirs & (1 << gpio_num))
 172                outb(curr_dirs & ~(1 << gpio_num), gpio_ba + RGIO);
 173
 174        spin_unlock(&gpio_lock);
 175        return 0;
 176}
 177
 178static struct gpio_chip sch_gpio_resume = {
 179        .label                  = "sch_gpio_resume",
 180        .owner                  = THIS_MODULE,
 181        .direction_input        = sch_gpio_resume_direction_in,
 182        .get                    = sch_gpio_resume_get,
 183        .direction_output       = sch_gpio_resume_direction_out,
 184        .set                    = sch_gpio_resume_set,
 185};
 186
 187static int __devinit sch_gpio_probe(struct platform_device *pdev)
 188{
 189        struct resource *res;
 190        int err;
 191
 192        res = platform_get_resource(pdev, IORESOURCE_IO, 0);
 193        if (!res)
 194                return -EBUSY;
 195
 196        if (!request_region(res->start, resource_size(res), pdev->name))
 197                return -EBUSY;
 198
 199        gpio_ba = res->start;
 200
 201        sch_gpio_core.base = 0;
 202        sch_gpio_core.ngpio = 10;
 203        sch_gpio_core.dev = &pdev->dev;
 204
 205        sch_gpio_resume.base = 10;
 206        sch_gpio_resume.ngpio = 4;
 207        sch_gpio_resume.dev = &pdev->dev;
 208
 209        err = gpiochip_add(&sch_gpio_core);
 210        if (err < 0)
 211                goto err_sch_gpio_core;
 212
 213        err = gpiochip_add(&sch_gpio_resume);
 214        if (err < 0)
 215                goto err_sch_gpio_resume;
 216
 217        /*
 218         * GPIO[6:0] enabled by default
 219         * GPIO7 is configured by the CMC as SLPIOVR
 220         * Enable GPIO[9:8] core powered gpios explicitly
 221         */
 222        outb(0x3, gpio_ba + CGEN + 1);
 223        /*
 224         * SUS_GPIO[2:0] enabled by default
 225         * Enable SUS_GPIO3 resume powered gpio explicitly
 226         */
 227        outb(0x8, gpio_ba + RGEN);
 228
 229        return 0;
 230
 231err_sch_gpio_resume:
 232        err = gpiochip_remove(&sch_gpio_core);
 233        if (err)
 234                dev_err(&pdev->dev, "%s failed, %d\n",
 235                                "gpiochip_remove()", err);
 236
 237err_sch_gpio_core:
 238        release_region(res->start, resource_size(res));
 239        gpio_ba = 0;
 240
 241        return err;
 242}
 243
 244static int __devexit sch_gpio_remove(struct platform_device *pdev)
 245{
 246        struct resource *res;
 247        if (gpio_ba) {
 248                int err;
 249
 250                err  = gpiochip_remove(&sch_gpio_core);
 251                if (err)
 252                        dev_err(&pdev->dev, "%s failed, %d\n",
 253                                "gpiochip_remove()", err);
 254                err = gpiochip_remove(&sch_gpio_resume);
 255                if (err)
 256                        dev_err(&pdev->dev, "%s failed, %d\n",
 257                                "gpiochip_remove()", err);
 258
 259                res = platform_get_resource(pdev, IORESOURCE_IO, 0);
 260
 261                release_region(res->start, resource_size(res));
 262                gpio_ba = 0;
 263
 264                return err;
 265        }
 266
 267        return 0;
 268}
 269
 270static struct platform_driver sch_gpio_driver = {
 271        .driver = {
 272                .name = "sch_gpio",
 273                .owner = THIS_MODULE,
 274        },
 275        .probe          = sch_gpio_probe,
 276        .remove         = __devexit_p(sch_gpio_remove),
 277};
 278
 279static int __init sch_gpio_init(void)
 280{
 281        return platform_driver_register(&sch_gpio_driver);
 282}
 283
 284static void __exit sch_gpio_exit(void)
 285{
 286        platform_driver_unregister(&sch_gpio_driver);
 287}
 288
 289module_init(sch_gpio_init);
 290module_exit(sch_gpio_exit);
 291
 292MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
 293MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH");
 294MODULE_LICENSE("GPL");
 295MODULE_ALIAS("platform:sch_gpio");
 296
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.