18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * GPIO driver for Analog Devices ADP5520 MFD PMICs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2009 Analog Devices Inc. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/mfd/adp5520.h> 148c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistruct adp5520_gpio { 178c2ecf20Sopenharmony_ci struct device *master; 188c2ecf20Sopenharmony_ci struct gpio_chip gpio_chip; 198c2ecf20Sopenharmony_ci unsigned char lut[ADP5520_MAXGPIOS]; 208c2ecf20Sopenharmony_ci unsigned long output; 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic int adp5520_gpio_get_value(struct gpio_chip *chip, unsigned off) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct adp5520_gpio *dev; 268c2ecf20Sopenharmony_ci uint8_t reg_val; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci dev = gpiochip_get_data(chip); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci /* 318c2ecf20Sopenharmony_ci * There are dedicated registers for GPIO IN/OUT. 328c2ecf20Sopenharmony_ci * Make sure we return the right value, even when configured as output 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci if (test_bit(off, &dev->output)) 368c2ecf20Sopenharmony_ci adp5520_read(dev->master, ADP5520_GPIO_OUT, ®_val); 378c2ecf20Sopenharmony_ci else 388c2ecf20Sopenharmony_ci adp5520_read(dev->master, ADP5520_GPIO_IN, ®_val); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci return !!(reg_val & dev->lut[off]); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic void adp5520_gpio_set_value(struct gpio_chip *chip, 448c2ecf20Sopenharmony_ci unsigned off, int val) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct adp5520_gpio *dev; 478c2ecf20Sopenharmony_ci dev = gpiochip_get_data(chip); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (val) 508c2ecf20Sopenharmony_ci adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]); 518c2ecf20Sopenharmony_ci else 528c2ecf20Sopenharmony_ci adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int adp5520_gpio_direction_input(struct gpio_chip *chip, unsigned off) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct adp5520_gpio *dev; 588c2ecf20Sopenharmony_ci dev = gpiochip_get_data(chip); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci clear_bit(off, &dev->output); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_2, 638c2ecf20Sopenharmony_ci dev->lut[off]); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int adp5520_gpio_direction_output(struct gpio_chip *chip, 678c2ecf20Sopenharmony_ci unsigned off, int val) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct adp5520_gpio *dev; 708c2ecf20Sopenharmony_ci int ret = 0; 718c2ecf20Sopenharmony_ci dev = gpiochip_get_data(chip); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci set_bit(off, &dev->output); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (val) 768c2ecf20Sopenharmony_ci ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, 778c2ecf20Sopenharmony_ci dev->lut[off]); 788c2ecf20Sopenharmony_ci else 798c2ecf20Sopenharmony_ci ret |= adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, 808c2ecf20Sopenharmony_ci dev->lut[off]); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_CFG_2, 838c2ecf20Sopenharmony_ci dev->lut[off]); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return ret; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int adp5520_gpio_probe(struct platform_device *pdev) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct adp5520_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev); 918c2ecf20Sopenharmony_ci struct adp5520_gpio *dev; 928c2ecf20Sopenharmony_ci struct gpio_chip *gc; 938c2ecf20Sopenharmony_ci int ret, i, gpios; 948c2ecf20Sopenharmony_ci unsigned char ctl_mask = 0; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (pdata == NULL) { 978c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "missing platform data\n"); 988c2ecf20Sopenharmony_ci return -ENODEV; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (pdev->id != ID_ADP5520) { 1028c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "only ADP5520 supports GPIO\n"); 1038c2ecf20Sopenharmony_ci return -ENODEV; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 1078c2ecf20Sopenharmony_ci if (dev == NULL) 1088c2ecf20Sopenharmony_ci return -ENOMEM; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci dev->master = pdev->dev.parent; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci for (gpios = 0, i = 0; i < ADP5520_MAXGPIOS; i++) 1138c2ecf20Sopenharmony_ci if (pdata->gpio_en_mask & (1 << i)) 1148c2ecf20Sopenharmony_ci dev->lut[gpios++] = 1 << i; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (gpios < 1) { 1178c2ecf20Sopenharmony_ci ret = -EINVAL; 1188c2ecf20Sopenharmony_ci goto err; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci gc = &dev->gpio_chip; 1228c2ecf20Sopenharmony_ci gc->direction_input = adp5520_gpio_direction_input; 1238c2ecf20Sopenharmony_ci gc->direction_output = adp5520_gpio_direction_output; 1248c2ecf20Sopenharmony_ci gc->get = adp5520_gpio_get_value; 1258c2ecf20Sopenharmony_ci gc->set = adp5520_gpio_set_value; 1268c2ecf20Sopenharmony_ci gc->can_sleep = true; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci gc->base = pdata->gpio_start; 1298c2ecf20Sopenharmony_ci gc->ngpio = gpios; 1308c2ecf20Sopenharmony_ci gc->label = pdev->name; 1318c2ecf20Sopenharmony_ci gc->owner = THIS_MODULE; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci ret = adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_1, 1348c2ecf20Sopenharmony_ci pdata->gpio_en_mask); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (pdata->gpio_en_mask & ADP5520_GPIO_C3) 1378c2ecf20Sopenharmony_ci ctl_mask |= ADP5520_C3_MODE; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (pdata->gpio_en_mask & ADP5520_GPIO_R3) 1408c2ecf20Sopenharmony_ci ctl_mask |= ADP5520_R3_MODE; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (ctl_mask) 1438c2ecf20Sopenharmony_ci ret = adp5520_set_bits(dev->master, ADP5520_LED_CONTROL, 1448c2ecf20Sopenharmony_ci ctl_mask); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_PULLUP, 1478c2ecf20Sopenharmony_ci pdata->gpio_pullup_mask); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (ret) { 1508c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to write\n"); 1518c2ecf20Sopenharmony_ci goto err; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci ret = devm_gpiochip_add_data(&pdev->dev, &dev->gpio_chip, dev); 1558c2ecf20Sopenharmony_ci if (ret) 1568c2ecf20Sopenharmony_ci goto err; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, dev); 1598c2ecf20Sopenharmony_ci return 0; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cierr: 1628c2ecf20Sopenharmony_ci return ret; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic struct platform_driver adp5520_gpio_driver = { 1668c2ecf20Sopenharmony_ci .driver = { 1678c2ecf20Sopenharmony_ci .name = "adp5520-gpio", 1688c2ecf20Sopenharmony_ci }, 1698c2ecf20Sopenharmony_ci .probe = adp5520_gpio_probe, 1708c2ecf20Sopenharmony_ci}; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cimodule_platform_driver(adp5520_gpio_driver); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); 1758c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GPIO ADP5520 Driver"); 1768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1778c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:adp5520-gpio"); 178