cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

gpio-adp5520.c (3936B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * GPIO driver for Analog Devices ADP5520 MFD PMICs
      4 *
      5 * Copyright 2009 Analog Devices Inc.
      6 */
      7
      8#include <linux/module.h>
      9#include <linux/slab.h>
     10#include <linux/kernel.h>
     11#include <linux/init.h>
     12#include <linux/platform_device.h>
     13#include <linux/mfd/adp5520.h>
     14#include <linux/gpio/driver.h>
     15
     16struct adp5520_gpio {
     17	struct device *master;
     18	struct gpio_chip gpio_chip;
     19	unsigned char lut[ADP5520_MAXGPIOS];
     20	unsigned long output;
     21};
     22
     23static int adp5520_gpio_get_value(struct gpio_chip *chip, unsigned off)
     24{
     25	struct adp5520_gpio *dev;
     26	uint8_t reg_val;
     27
     28	dev = gpiochip_get_data(chip);
     29
     30	/*
     31	 * There are dedicated registers for GPIO IN/OUT.
     32	 * Make sure we return the right value, even when configured as output
     33	 */
     34
     35	if (test_bit(off, &dev->output))
     36		adp5520_read(dev->master, ADP5520_GPIO_OUT, &reg_val);
     37	else
     38		adp5520_read(dev->master, ADP5520_GPIO_IN, &reg_val);
     39
     40	return !!(reg_val & dev->lut[off]);
     41}
     42
     43static void adp5520_gpio_set_value(struct gpio_chip *chip,
     44		unsigned off, int val)
     45{
     46	struct adp5520_gpio *dev;
     47	dev = gpiochip_get_data(chip);
     48
     49	if (val)
     50		adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]);
     51	else
     52		adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]);
     53}
     54
     55static int adp5520_gpio_direction_input(struct gpio_chip *chip, unsigned off)
     56{
     57	struct adp5520_gpio *dev;
     58	dev = gpiochip_get_data(chip);
     59
     60	clear_bit(off, &dev->output);
     61
     62	return adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_2,
     63				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 = gpiochip_get_data(chip);
     72
     73	set_bit(off, &dev->output);
     74
     75	if (val)
     76		ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_OUT,
     77					dev->lut[off]);
     78	else
     79		ret |= adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT,
     80					dev->lut[off]);
     81
     82	ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_CFG_2,
     83					dev->lut[off]);
     84
     85	return ret;
     86}
     87
     88static int adp5520_gpio_probe(struct platform_device *pdev)
     89{
     90	struct adp5520_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
     91	struct adp5520_gpio *dev;
     92	struct gpio_chip *gc;
     93	int ret, i, gpios;
     94	unsigned char ctl_mask = 0;
     95
     96	if (pdata == NULL) {
     97		dev_err(&pdev->dev, "missing platform data\n");
     98		return -ENODEV;
     99	}
    100
    101	if (pdev->id != ID_ADP5520) {
    102		dev_err(&pdev->dev, "only ADP5520 supports GPIO\n");
    103		return -ENODEV;
    104	}
    105
    106	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
    107	if (dev == NULL)
    108		return -ENOMEM;
    109
    110	dev->master = pdev->dev.parent;
    111
    112	for (gpios = 0, i = 0; i < ADP5520_MAXGPIOS; i++)
    113		if (pdata->gpio_en_mask & (1 << i))
    114			dev->lut[gpios++] = 1 << i;
    115
    116	if (gpios < 1)
    117		return -EINVAL;
    118
    119	gc = &dev->gpio_chip;
    120	gc->direction_input  = adp5520_gpio_direction_input;
    121	gc->direction_output = adp5520_gpio_direction_output;
    122	gc->get = adp5520_gpio_get_value;
    123	gc->set = adp5520_gpio_set_value;
    124	gc->can_sleep = true;
    125
    126	gc->base = pdata->gpio_start;
    127	gc->ngpio = gpios;
    128	gc->label = pdev->name;
    129	gc->owner = THIS_MODULE;
    130
    131	ret = adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_1,
    132		pdata->gpio_en_mask);
    133
    134	if (pdata->gpio_en_mask & ADP5520_GPIO_C3)
    135		ctl_mask |= ADP5520_C3_MODE;
    136
    137	if (pdata->gpio_en_mask & ADP5520_GPIO_R3)
    138		ctl_mask |= ADP5520_R3_MODE;
    139
    140	if (ctl_mask)
    141		ret = adp5520_set_bits(dev->master, ADP5520_LED_CONTROL,
    142			ctl_mask);
    143
    144	ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_PULLUP,
    145		pdata->gpio_pullup_mask);
    146
    147	if (ret) {
    148		dev_err(&pdev->dev, "failed to write\n");
    149		return ret;
    150	}
    151
    152	return devm_gpiochip_add_data(&pdev->dev, &dev->gpio_chip, dev);
    153}
    154
    155static struct platform_driver adp5520_gpio_driver = {
    156	.driver	= {
    157		.name	= "adp5520-gpio",
    158	},
    159	.probe		= adp5520_gpio_probe,
    160};
    161
    162module_platform_driver(adp5520_gpio_driver);
    163
    164MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
    165MODULE_DESCRIPTION("GPIO ADP5520 Driver");
    166MODULE_LICENSE("GPL");
    167MODULE_ALIAS("platform:adp5520-gpio");