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
  32#define GEN     0x00
  33#define GIO     0x04
  34#define GLV     0x08
  35
  36struct sch_gpio {
  37        struct gpio_chip chip;
  38        spinlock_t lock;
  39        unsigned short iobase;
  40        unsigned short core_base;
  41        unsigned short resume_base;
  42};
  43
  44#define to_sch_gpio(gc) container_of(gc, struct sch_gpio, chip)
  45
  46static unsigned sch_gpio_offset(struct sch_gpio *sch, unsigned gpio,
  47                                unsigned reg)
  48{
  49        unsigned base = 0;
  50
  51        if (gpio >= sch->resume_base) {
  52                gpio -= sch->resume_base;
  53                base += 0x20;
  54        }
  55
  56        return base + reg + gpio / 8;
  57}
  58
  59static unsigned sch_gpio_bit(struct sch_gpio *sch, unsigned gpio)
  60{
  61        if (gpio >= sch->resume_base)
  62                gpio -= sch->resume_base;
  63        return gpio % 8;
  64}
  65
  66static int sch_gpio_reg_get(struct gpio_chip *gc, unsigned gpio, unsigned reg)
  67{
  68        struct sch_gpio *sch = to_sch_gpio(gc);
  69        unsigned short offset, bit;
  70        u8 reg_val;
  71
  72        offset = sch_gpio_offset(sch, gpio, reg);
  73        bit = sch_gpio_bit(sch, gpio);
  74
  75        reg_val = !!(inb(sch->iobase + offset) & BIT(bit));
  76
  77        return reg_val;
  78}
  79
  80static void sch_gpio_reg_set(struct gpio_chip *gc, unsigned gpio, unsigned reg,
  81                             int val)
  82{
  83        struct sch_gpio *sch = to_sch_gpio(gc);
  84        unsigned short offset, bit;
  85        u8 reg_val;
  86
  87        offset = sch_gpio_offset(sch, gpio, reg);
  88        bit = sch_gpio_bit(sch, gpio);
  89
  90        reg_val = inb(sch->iobase + offset);
  91
  92        if (val)
  93                outb(reg_val | BIT(bit), sch->iobase + offset);
  94        else
  95                outb((reg_val & ~BIT(bit)), sch->iobase + offset);
  96}
  97
  98static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num)
  99{
 100        struct sch_gpio *sch = to_sch_gpio(gc);
 101
 102        spin_lock(&sch->lock);
 103        sch_gpio_reg_set(gc, gpio_num, GIO, 1);
 104        spin_unlock(&sch->lock);
 105        return 0;
 106}
 107
 108static int sch_gpio_get(struct gpio_chip *gc, unsigned gpio_num)
 109{
 110        return sch_gpio_reg_get(gc, gpio_num, GLV);
 111}
 112
 113static void sch_gpio_set(struct gpio_chip *gc, unsigned gpio_num, int val)
 114{
 115        struct sch_gpio *sch = to_sch_gpio(gc);
 116
 117        spin_lock(&sch->lock);
 118        sch_gpio_reg_set(gc, gpio_num, GLV, val);
 119        spin_unlock(&sch->lock);
 120}
 121
 122static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned gpio_num,
 123                                  int val)
 124{
 125        struct sch_gpio *sch = to_sch_gpio(gc);
 126
 127        spin_lock(&sch->lock);
 128        sch_gpio_reg_set(gc, gpio_num, GIO, 0);
 129        spin_unlock(&sch->lock);
 130
 131        /*
 132         * according to the datasheet, writing to the level register has no
 133         * effect when GPIO is programmed as input.
 134         * Actually the the level register is read-only when configured as input.
 135         * Thus presetting the output level before switching to output is _NOT_ possible.
 136         * Hence we set the level after configuring the GPIO as output.
 137         * But we cannot prevent a short low pulse if direction is set to high
 138         * and an external pull-up is connected.
 139         */
 140        sch_gpio_set(gc, gpio_num, val);
 141        return 0;
 142}
 143
 144static struct gpio_chip sch_gpio_chip = {
 145        .label                  = "sch_gpio",
 146        .owner                  = THIS_MODULE,
 147        .direction_input        = sch_gpio_direction_in,
 148        .get                    = sch_gpio_get,
 149        .direction_output       = sch_gpio_direction_out,
 150        .set                    = sch_gpio_set,
 151};
 152
 153static int sch_gpio_probe(struct platform_device *pdev)
 154{
 155        struct sch_gpio *sch;
 156        struct resource *res;
 157
 158        sch = devm_kzalloc(&pdev->dev, sizeof(*sch), GFP_KERNEL);
 159        if (!sch)
 160                return -ENOMEM;
 161
 162        res = platform_get_resource(pdev, IORESOURCE_IO, 0);
 163        if (!res)
 164                return -EBUSY;
 165
 166        if (!devm_request_region(&pdev->dev, res->start, resource_size(res),
 167                                 pdev->name))
 168                return -EBUSY;
 169
 170        spin_lock_init(&sch->lock);
 171        sch->iobase = res->start;
 172        sch->chip = sch_gpio_chip;
 173        sch->chip.label = dev_name(&pdev->dev);
 174        sch->chip.dev = &pdev->dev;
 175
 176        switch (pdev->id) {
 177        case PCI_DEVICE_ID_INTEL_SCH_LPC:
 178                sch->core_base = 0;
 179                sch->resume_base = 10;
 180                sch->chip.ngpio = 14;
 181
 182                /*
 183                 * GPIO[6:0] enabled by default
 184                 * GPIO7 is configured by the CMC as SLPIOVR
 185                 * Enable GPIO[9:8] core powered gpios explicitly
 186                 */
 187                sch_gpio_reg_set(&sch->chip, 8, GEN, 1);
 188                sch_gpio_reg_set(&sch->chip, 9, GEN, 1);
 189                /*
 190                 * SUS_GPIO[2:0] enabled by default
 191                 * Enable SUS_GPIO3 resume powered gpio explicitly
 192                 */
 193                sch_gpio_reg_set(&sch->chip, 13, GEN, 1);
 194                break;
 195
 196        case PCI_DEVICE_ID_INTEL_ITC_LPC:
 197                sch->core_base = 0;
 198                sch->resume_base = 5;
 199                sch->chip.ngpio = 14;
 200                break;
 201
 202        case PCI_DEVICE_ID_INTEL_CENTERTON_ILB:
 203                sch->core_base = 0;
 204                sch->resume_base = 21;
 205                sch->chip.ngpio = 30;
 206                break;
 207
 208        case PCI_DEVICE_ID_INTEL_QUARK_X1000_ILB:
 209                sch->core_base = 0;
 210                sch->resume_base = 2;
 211                sch->chip.ngpio = 8;
 212                break;
 213
 214        default:
 215                return -ENODEV;
 216        }
 217
 218        platform_set_drvdata(pdev, sch);
 219
 220        return gpiochip_add(&sch->chip);
 221}
 222
 223static int sch_gpio_remove(struct platform_device *pdev)
 224{
 225        struct sch_gpio *sch = platform_get_drvdata(pdev);
 226
 227        gpiochip_remove(&sch->chip);
 228        return 0;
 229}
 230
 231static struct platform_driver sch_gpio_driver = {
 232        .driver = {
 233                .name = "sch_gpio",
 234        },
 235        .probe          = sch_gpio_probe,
 236        .remove         = sch_gpio_remove,
 237};
 238
 239module_platform_driver(sch_gpio_driver);
 240
 241MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
 242MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH");
 243MODULE_LICENSE("GPL");
 244MODULE_ALIAS("platform:sch_gpio");
 245