forked from Minki/linux
6681db5ef5
The platform_set_drvdata() call is only useful if we need to retrieve back the private information. Since the driver doesn't do that, it's not useful to have it. This also means that the 'err' label can be removed and all goto statements replaced with direct returns (with error codes). Signed-off-by: Alexandru Ardelean <aardelean@deviqon.com> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
168 lines
3.8 KiB
C
168 lines
3.8 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 void adp5520_gpio_set_value(struct gpio_chip *chip,
|
|
unsigned off, int val)
|
|
{
|
|
struct adp5520_gpio *dev;
|
|
dev = gpiochip_get_data(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 = 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");
|