linux/drivers/gpio/adp5520-gpio.c
<<
>>
Prefs
   1/*
   2 * GPIO driver for Analog Devices ADP5520 MFD PMICs
   3 *
   4 * Copyright 2009 Analog Devices Inc.
   5 *
   6 * Licensed under the GPL-2 or later.
   7 */
   8
   9#include <linux/module.h>
  10#include <linux/kernel.h>
  11#include <linux/init.h>
  12#include <linux/platform_device.h>
  13#include <linux/mfd/adp5520.h>
  14
  15#include <linux/gpio.h>
  16
  17struct adp5520_gpio {
  18        struct device *master;
  19        struct gpio_chip gpio_chip;
  20        unsigned char lut[ADP5520_MAXGPIOS];
  21        unsigned long output;
  22};
  23
  24static int adp5520_gpio_get_value(struct gpio_chip *chip, unsigned off)
  25{
  26        struct adp5520_gpio *dev;
  27        uint8_t reg_val;
  28
  29        dev = container_of(chip, struct adp5520_gpio, gpio_chip);
  30
  31        /*
  32         * There are dedicated registers for GPIO IN/OUT.
  33         * Make sure we return the right value, even when configured as output
  34         */
  35
  36        if (test_bit(off, &dev->output))
  37                adp5520_read(dev->master, GPIO_OUT, &reg_val);
  38        else
  39                adp5520_read(dev->master, GPIO_IN, &reg_val);
  40
  41        return !!(reg_val & dev->lut[off]);
  42}
  43
  44static void adp5520_gpio_set_value(struct gpio_chip *chip,
  45                unsigned off, int val)
  46{
  47        struct adp5520_gpio *dev;
  48        dev = container_of(chip, struct adp5520_gpio, gpio_chip);
  49
  50        if (val)
  51                adp5520_set_bits(dev->master, GPIO_OUT, dev->lut[off]);
  52        else
  53                adp5520_clr_bits(dev->master, GPIO_OUT, dev->lut[off]);
  54}
  55
  56static int adp5520_gpio_direction_input(struct gpio_chip *chip, unsigned off)
  57{
  58        struct adp5520_gpio *dev;
  59        dev = container_of(chip, struct adp5520_gpio, gpio_chip);
  60
  61        clear_bit(off, &dev->output);
  62
  63        return adp5520_clr_bits(dev->master, GPIO_CFG_2, dev->lut[off]);
  64}
  65
  66static int adp5520_gpio_direction_output(struct gpio_chip *chip,
  67                unsigned off, int val)
  68{
  69        struct adp5520_gpio *dev;
  70        int ret = 0;
  71        dev = container_of(chip, struct adp5520_gpio, gpio_chip);
  72
  73        set_bit(off, &dev->output);
  74
  75        if (val)
  76                ret |= adp5520_set_bits(dev->master, GPIO_OUT, dev->lut[off]);
  77        else
  78                ret |= adp5520_clr_bits(dev->master, GPIO_OUT, dev->lut[off]);
  79
  80        ret |= adp5520_set_bits(dev->master, GPIO_CFG_2, dev->lut[off]);
  81
  82        return ret;
  83}
  84
  85static int __devinit adp5520_gpio_probe(struct platform_device *pdev)
  86{
  87        struct adp5520_gpio_platfrom_data *pdata = pdev->dev.platform_data;
  88        struct adp5520_gpio *dev;
  89        struct gpio_chip *gc;
  90        int ret, i, gpios;
  91        unsigned char ctl_mask = 0;
  92
  93        if (pdata == NULL) {
  94                dev_err(&pdev->dev, "missing platform data\n");
  95                return -ENODEV;
  96        }
  97
  98        if (pdev->id != ID_ADP5520) {
  99                dev_err(&pdev->dev, "only ADP5520 supports GPIO\n");
 100                return -ENODEV;
 101        }
 102
 103        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 104        if (dev == NULL) {
 105                dev_err(&pdev->dev, "failed to alloc memory\n");
 106                return -ENOMEM;
 107        }
 108
 109        dev->master = pdev->dev.parent;
 110
 111        for (gpios = 0, i = 0; i < ADP5520_MAXGPIOS; i++)
 112                if (pdata->gpio_en_mask & (1 << i))
 113                        dev->lut[gpios++] = 1 << i;
 114
 115        if (gpios < 1) {
 116                ret = -EINVAL;
 117                goto err;
 118        }
 119
 120        gc = &dev->gpio_chip;
 121        gc->direction_input  = adp5520_gpio_direction_input;
 122        gc->direction_output = adp5520_gpio_direction_output;
 123        gc->get = adp5520_gpio_get_value;
 124        gc->set = adp5520_gpio_set_value;
 125        gc->can_sleep = 1;
 126
 127        gc->base = pdata->gpio_start;
 128        gc->ngpio = gpios;
 129        gc->label = pdev->name;
 130        gc->owner = THIS_MODULE;
 131
 132        ret = adp5520_clr_bits(dev->master, GPIO_CFG_1,
 133                pdata->gpio_en_mask);
 134
 135        if (pdata->gpio_en_mask & GPIO_C3)
 136                ctl_mask |= C3_MODE;
 137
 138        if (pdata->gpio_en_mask & GPIO_R3)
 139                ctl_mask |= R3_MODE;
 140
 141        if (ctl_mask)
 142                ret = adp5520_set_bits(dev->master, LED_CONTROL,
 143                        ctl_mask);
 144
 145        ret |= adp5520_set_bits(dev->master, GPIO_PULLUP,
 146                pdata->gpio_pullup_mask);
 147
 148        if (ret) {
 149                dev_err(&pdev->dev, "failed to write\n");
 150                goto err;
 151        }
 152
 153        ret = gpiochip_add(&dev->gpio_chip);
 154        if (ret)
 155                goto err;
 156
 157        platform_set_drvdata(pdev, dev);
 158        return 0;
 159
 160err:
 161        kfree(dev);
 162        return ret;
 163}
 164
 165static int __devexit adp5520_gpio_remove(struct platform_device *pdev)
 166{
 167        struct adp5520_gpio *dev;
 168        int ret;
 169
 170        dev = platform_get_drvdata(pdev);
 171        ret = gpiochip_remove(&dev->gpio_chip);
 172        if (ret) {
 173                dev_err(&pdev->dev, "%s failed, %d\n",
 174                                "gpiochip_remove()", ret);
 175                return ret;
 176        }
 177
 178        kfree(dev);
 179        return 0;
 180}
 181
 182static struct platform_driver adp5520_gpio_driver = {
 183        .driver = {
 184                .name   = "adp5520-gpio",
 185                .owner  = THIS_MODULE,
 186        },
 187        .probe          = adp5520_gpio_probe,
 188        .remove         = __devexit_p(adp5520_gpio_remove),
 189};
 190
 191static int __init adp5520_gpio_init(void)
 192{
 193        return platform_driver_register(&adp5520_gpio_driver);
 194}
 195module_init(adp5520_gpio_init);
 196
 197static void __exit adp5520_gpio_exit(void)
 198{
 199        platform_driver_unregister(&adp5520_gpio_driver);
 200}
 201module_exit(adp5520_gpio_exit);
 202
 203MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
 204MODULE_DESCRIPTION("GPIO ADP5520 Driver");
 205MODULE_LICENSE("GPL");
 206MODULE_ALIAS("platform:adp5520-gpio");
 207