linux/drivers/gpio/gpio-adp5520.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/slab.h>
  11#include <linux/kernel.h>
  12#include <linux/init.h>
  13#include <linux/platform_device.h>
  14#include <linux/mfd/adp5520.h>
  15#include <linux/gpio/driver.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 = gpiochip_get_data(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, ADP5520_GPIO_OUT, &reg_val);
  38        else
  39                adp5520_read(dev->master, ADP5520_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 = gpiochip_get_data(chip);
  49
  50        if (val)
  51                adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]);
  52        else
  53                adp5520_clr_bits(dev->master, ADP5520_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 = gpiochip_get_data(chip);
  60
  61        clear_bit(off, &dev->output);
  62
  63        return adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_2,
  64                                dev->lut[off]);
  65}
  66
  67static int adp5520_gpio_direction_output(struct gpio_chip *chip,
  68                unsigned off, int val)
  69{
  70        struct adp5520_gpio *dev;
  71        int ret = 0;
  72        dev = gpiochip_get_data(chip);
  73
  74        set_bit(off, &dev->output);
  75
  76        if (val)
  77                ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_OUT,
  78                                        dev->lut[off]);
  79        else
  80                ret |= adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT,
  81                                        dev->lut[off]);
  82
  83        ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_CFG_2,
  84                                        dev->lut[off]);
  85
  86        return ret;
  87}
  88
  89static int adp5520_gpio_probe(struct platform_device *pdev)
  90{
  91        struct adp5520_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
  92        struct adp5520_gpio *dev;
  93        struct gpio_chip *gc;
  94        int ret, i, gpios;
  95        unsigned char ctl_mask = 0;
  96
  97        if (pdata == NULL) {
  98                dev_err(&pdev->dev, "missing platform data\n");
  99                return -ENODEV;
 100        }
 101
 102        if (pdev->id != ID_ADP5520) {
 103                dev_err(&pdev->dev, "only ADP5520 supports GPIO\n");
 104                return -ENODEV;
 105        }
 106
 107        dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
 108        if (dev == NULL)
 109                return -ENOMEM;
 110
 111        dev->master = pdev->dev.parent;
 112
 113        for (gpios = 0, i = 0; i < ADP5520_MAXGPIOS; i++)
 114                if (pdata->gpio_en_mask & (1 << i))
 115                        dev->lut[gpios++] = 1 << i;
 116
 117        if (gpios < 1) {
 118                ret = -EINVAL;
 119                goto err;
 120        }
 121
 122        gc = &dev->gpio_chip;
 123        gc->direction_input  = adp5520_gpio_direction_input;
 124        gc->direction_output = adp5520_gpio_direction_output;
 125        gc->get = adp5520_gpio_get_value;
 126        gc->set = adp5520_gpio_set_value;
 127        gc->can_sleep = true;
 128
 129        gc->base = pdata->gpio_start;
 130        gc->ngpio = gpios;
 131        gc->label = pdev->name;
 132        gc->owner = THIS_MODULE;
 133
 134        ret = adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_1,
 135                pdata->gpio_en_mask);
 136
 137        if (pdata->gpio_en_mask & ADP5520_GPIO_C3)
 138                ctl_mask |= ADP5520_C3_MODE;
 139
 140        if (pdata->gpio_en_mask & ADP5520_GPIO_R3)
 141                ctl_mask |= ADP5520_R3_MODE;
 142
 143        if (ctl_mask)
 144                ret = adp5520_set_bits(dev->master, ADP5520_LED_CONTROL,
 145                        ctl_mask);
 146
 147        ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_PULLUP,
 148                pdata->gpio_pullup_mask);
 149
 150        if (ret) {
 151                dev_err(&pdev->dev, "failed to write\n");
 152                goto err;
 153        }
 154
 155        ret = devm_gpiochip_add_data(&pdev->dev, &dev->gpio_chip, dev);
 156        if (ret)
 157                goto err;
 158
 159        platform_set_drvdata(pdev, dev);
 160        return 0;
 161
 162err:
 163        return ret;
 164}
 165
 166static struct platform_driver adp5520_gpio_driver = {
 167        .driver = {
 168                .name   = "adp5520-gpio",
 169        },
 170        .probe          = adp5520_gpio_probe,
 171};
 172
 173module_platform_driver(adp5520_gpio_driver);
 174
 175MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
 176MODULE_DESCRIPTION("GPIO ADP5520 Driver");
 177MODULE_LICENSE("GPL");
 178MODULE_ALIAS("platform:adp5520-gpio");
 179