123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- /*
- * GPIO driver for Analog Devices ADP5520 MFD PMICs
- *
- * Copyright 2009 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
- */
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/platform_device.h>
- #include <linux/mfd/adp5520.h>
- #include <linux/gpio.h>
- struct adp5520_gpio {
- struct device *master;
- struct gpio_chip gpio_chip;
- unsigned char lut[ADP5520_MAXGPIOS];
- unsigned long output;
- };
- static int adp5520_gpio_get_value(struct gpio_chip *chip, unsigned off)
- {
- struct adp5520_gpio *dev;
- uint8_t reg_val;
- dev = container_of(chip, struct adp5520_gpio, gpio_chip);
- /*
- * There are dedicated registers for GPIO IN/OUT.
- * Make sure we return the right value, even when configured as output
- */
- if (test_bit(off, &dev->output))
- adp5520_read(dev->master, ADP5520_GPIO_OUT, ®_val);
- else
- adp5520_read(dev->master, ADP5520_GPIO_IN, ®_val);
- return !!(reg_val & dev->lut[off]);
- }
- static void adp5520_gpio_set_value(struct gpio_chip *chip,
- unsigned off, int val)
- {
- struct adp5520_gpio *dev;
- dev = container_of(chip, struct adp5520_gpio, gpio_chip);
- if (val)
- adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]);
- else
- adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]);
- }
- static int adp5520_gpio_direction_input(struct gpio_chip *chip, unsigned off)
- {
- struct adp5520_gpio *dev;
- dev = container_of(chip, struct adp5520_gpio, gpio_chip);
- clear_bit(off, &dev->output);
- return adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_2,
- dev->lut[off]);
- }
- static int adp5520_gpio_direction_output(struct gpio_chip *chip,
- unsigned off, int val)
- {
- struct adp5520_gpio *dev;
- int ret = 0;
- dev = container_of(chip, struct adp5520_gpio, gpio_chip);
- set_bit(off, &dev->output);
- if (val)
- ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_OUT,
- dev->lut[off]);
- else
- ret |= adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT,
- dev->lut[off]);
- ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_CFG_2,
- dev->lut[off]);
- return ret;
- }
- static int adp5520_gpio_probe(struct platform_device *pdev)
- {
- struct adp5520_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct adp5520_gpio *dev;
- struct gpio_chip *gc;
- int ret, i, gpios;
- unsigned char ctl_mask = 0;
- if (pdata == NULL) {
- dev_err(&pdev->dev, "missing platform data\n");
- return -ENODEV;
- }
- if (pdev->id != ID_ADP5520) {
- dev_err(&pdev->dev, "only ADP5520 supports GPIO\n");
- return -ENODEV;
- }
- dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
- if (dev == NULL)
- return -ENOMEM;
- dev->master = pdev->dev.parent;
- for (gpios = 0, i = 0; i < ADP5520_MAXGPIOS; i++)
- if (pdata->gpio_en_mask & (1 << i))
- dev->lut[gpios++] = 1 << i;
- if (gpios < 1) {
- ret = -EINVAL;
- goto err;
- }
- gc = &dev->gpio_chip;
- gc->direction_input = adp5520_gpio_direction_input;
- gc->direction_output = adp5520_gpio_direction_output;
- gc->get = adp5520_gpio_get_value;
- gc->set = adp5520_gpio_set_value;
- gc->can_sleep = true;
- gc->base = pdata->gpio_start;
- gc->ngpio = gpios;
- gc->label = pdev->name;
- gc->owner = THIS_MODULE;
- ret = adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_1,
- pdata->gpio_en_mask);
- if (pdata->gpio_en_mask & ADP5520_GPIO_C3)
- ctl_mask |= ADP5520_C3_MODE;
- if (pdata->gpio_en_mask & ADP5520_GPIO_R3)
- ctl_mask |= ADP5520_R3_MODE;
- if (ctl_mask)
- ret = adp5520_set_bits(dev->master, ADP5520_LED_CONTROL,
- ctl_mask);
- ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_PULLUP,
- pdata->gpio_pullup_mask);
- if (ret) {
- dev_err(&pdev->dev, "failed to write\n");
- goto err;
- }
- ret = gpiochip_add(&dev->gpio_chip);
- if (ret)
- goto err;
- platform_set_drvdata(pdev, dev);
- return 0;
- err:
- return ret;
- }
- static int adp5520_gpio_remove(struct platform_device *pdev)
- {
- struct adp5520_gpio *dev;
- dev = platform_get_drvdata(pdev);
- gpiochip_remove(&dev->gpio_chip);
- return 0;
- }
- static struct platform_driver adp5520_gpio_driver = {
- .driver = {
- .name = "adp5520-gpio",
- },
- .probe = adp5520_gpio_probe,
- .remove = adp5520_gpio_remove,
- };
- module_platform_driver(adp5520_gpio_driver);
- MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
- MODULE_DESCRIPTION("GPIO ADP5520 Driver");
- MODULE_LICENSE("GPL");
- MODULE_ALIAS("platform:adp5520-gpio");
|