mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-08-25 08:54:56 +00:00

The conversion of all GPIO drivers to using the .set_rv() and .set_multiple_rv() callbacks from struct gpio_chip (which - unlike their predecessors - return an integer and allow the controller drivers to indicate failures to users) is now complete and the legacy ones have been removed. Rename the new callbacks back to their original names in one sweeping change. Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
170 lines
3.9 KiB
C
170 lines
3.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* GPIO driver for Analog Devices ADP5520 MFD PMICs
|
|
*
|
|
* Copyright 2009 Analog Devices Inc.
|
|
*/
|
|
|
|
#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/driver.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 = gpiochip_get_data(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 int adp5520_gpio_set_value(struct gpio_chip *chip,
|
|
unsigned int off, int val)
|
|
{
|
|
struct adp5520_gpio *dev;
|
|
dev = gpiochip_get_data(chip);
|
|
|
|
if (val)
|
|
return adp5520_set_bits(dev->master, ADP5520_GPIO_OUT,
|
|
dev->lut[off]);
|
|
else
|
|
return 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 = gpiochip_get_data(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 = gpiochip_get_data(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)
|
|
return -EINVAL;
|
|
|
|
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");
|
|
return ret;
|
|
}
|
|
|
|
return devm_gpiochip_add_data(&pdev->dev, &dev->gpio_chip, dev);
|
|
}
|
|
|
|
static struct platform_driver adp5520_gpio_driver = {
|
|
.driver = {
|
|
.name = "adp5520-gpio",
|
|
},
|
|
.probe = adp5520_gpio_probe,
|
|
};
|
|
|
|
module_platform_driver(adp5520_gpio_driver);
|
|
|
|
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
|
MODULE_DESCRIPTION("GPIO ADP5520 Driver");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_ALIAS("platform:adp5520-gpio");
|