18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * GPIO driver for TPS68470 PMIC 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Intel Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: 88c2ecf20Sopenharmony_ci * Antti Laakso <antti.laakso@intel.com> 98c2ecf20Sopenharmony_ci * Tianshu Qiu <tian.shu.qiu@intel.com> 108c2ecf20Sopenharmony_ci * Jian Xu Zheng <jian.xu.zheng@intel.com> 118c2ecf20Sopenharmony_ci * Yuning Pu <yuning.pu@intel.com> 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 158c2ecf20Sopenharmony_ci#include <linux/mfd/tps68470.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/regmap.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define TPS68470_N_LOGIC_OUTPUT 3 218c2ecf20Sopenharmony_ci#define TPS68470_N_REGULAR_GPIO 7 228c2ecf20Sopenharmony_ci#define TPS68470_N_GPIO (TPS68470_N_LOGIC_OUTPUT + TPS68470_N_REGULAR_GPIO) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct tps68470_gpio_data { 258c2ecf20Sopenharmony_ci struct regmap *tps68470_regmap; 268c2ecf20Sopenharmony_ci struct gpio_chip gc; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int tps68470_gpio_get(struct gpio_chip *gc, unsigned int offset) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct tps68470_gpio_data *tps68470_gpio = gpiochip_get_data(gc); 328c2ecf20Sopenharmony_ci struct regmap *regmap = tps68470_gpio->tps68470_regmap; 338c2ecf20Sopenharmony_ci unsigned int reg = TPS68470_REG_GPDO; 348c2ecf20Sopenharmony_ci int val, ret; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci if (offset >= TPS68470_N_REGULAR_GPIO) { 378c2ecf20Sopenharmony_ci offset -= TPS68470_N_REGULAR_GPIO; 388c2ecf20Sopenharmony_ci reg = TPS68470_REG_SGPO; 398c2ecf20Sopenharmony_ci } 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci ret = regmap_read(regmap, reg, &val); 428c2ecf20Sopenharmony_ci if (ret) { 438c2ecf20Sopenharmony_ci dev_err(tps68470_gpio->gc.parent, "reg 0x%x read failed\n", 448c2ecf20Sopenharmony_ci TPS68470_REG_SGPO); 458c2ecf20Sopenharmony_ci return ret; 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci return !!(val & BIT(offset)); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int tps68470_gpio_get_direction(struct gpio_chip *gc, 518c2ecf20Sopenharmony_ci unsigned int offset) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct tps68470_gpio_data *tps68470_gpio = gpiochip_get_data(gc); 548c2ecf20Sopenharmony_ci struct regmap *regmap = tps68470_gpio->tps68470_regmap; 558c2ecf20Sopenharmony_ci int val, ret; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci /* rest are always outputs */ 588c2ecf20Sopenharmony_ci if (offset >= TPS68470_N_REGULAR_GPIO) 598c2ecf20Sopenharmony_ci return GPIO_LINE_DIRECTION_OUT; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci ret = regmap_read(regmap, TPS68470_GPIO_CTL_REG_A(offset), &val); 628c2ecf20Sopenharmony_ci if (ret) { 638c2ecf20Sopenharmony_ci dev_err(tps68470_gpio->gc.parent, "reg 0x%x read failed\n", 648c2ecf20Sopenharmony_ci TPS68470_GPIO_CTL_REG_A(offset)); 658c2ecf20Sopenharmony_ci return ret; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci val &= TPS68470_GPIO_MODE_MASK; 698c2ecf20Sopenharmony_ci return val >= TPS68470_GPIO_MODE_OUT_CMOS ? GPIO_LINE_DIRECTION_OUT : 708c2ecf20Sopenharmony_ci GPIO_LINE_DIRECTION_IN; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void tps68470_gpio_set(struct gpio_chip *gc, unsigned int offset, 748c2ecf20Sopenharmony_ci int value) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct tps68470_gpio_data *tps68470_gpio = gpiochip_get_data(gc); 778c2ecf20Sopenharmony_ci struct regmap *regmap = tps68470_gpio->tps68470_regmap; 788c2ecf20Sopenharmony_ci unsigned int reg = TPS68470_REG_GPDO; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (offset >= TPS68470_N_REGULAR_GPIO) { 818c2ecf20Sopenharmony_ci reg = TPS68470_REG_SGPO; 828c2ecf20Sopenharmony_ci offset -= TPS68470_N_REGULAR_GPIO; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci regmap_update_bits(regmap, reg, BIT(offset), value ? BIT(offset) : 0); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int tps68470_gpio_output(struct gpio_chip *gc, unsigned int offset, 898c2ecf20Sopenharmony_ci int value) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct tps68470_gpio_data *tps68470_gpio = gpiochip_get_data(gc); 928c2ecf20Sopenharmony_ci struct regmap *regmap = tps68470_gpio->tps68470_regmap; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* Set the initial value */ 958c2ecf20Sopenharmony_ci tps68470_gpio_set(gc, offset, value); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* rest are always outputs */ 988c2ecf20Sopenharmony_ci if (offset >= TPS68470_N_REGULAR_GPIO) 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return regmap_update_bits(regmap, TPS68470_GPIO_CTL_REG_A(offset), 1028c2ecf20Sopenharmony_ci TPS68470_GPIO_MODE_MASK, 1038c2ecf20Sopenharmony_ci TPS68470_GPIO_MODE_OUT_CMOS); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic int tps68470_gpio_input(struct gpio_chip *gc, unsigned int offset) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct tps68470_gpio_data *tps68470_gpio = gpiochip_get_data(gc); 1098c2ecf20Sopenharmony_ci struct regmap *regmap = tps68470_gpio->tps68470_regmap; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* rest are always outputs */ 1128c2ecf20Sopenharmony_ci if (offset >= TPS68470_N_REGULAR_GPIO) 1138c2ecf20Sopenharmony_ci return -EINVAL; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return regmap_update_bits(regmap, TPS68470_GPIO_CTL_REG_A(offset), 1168c2ecf20Sopenharmony_ci TPS68470_GPIO_MODE_MASK, 0x00); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic const char *tps68470_names[TPS68470_N_GPIO] = { 1208c2ecf20Sopenharmony_ci "gpio.0", "gpio.1", "gpio.2", "gpio.3", 1218c2ecf20Sopenharmony_ci "gpio.4", "gpio.5", "gpio.6", 1228c2ecf20Sopenharmony_ci "s_enable", "s_idle", "s_resetn", 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int tps68470_gpio_probe(struct platform_device *pdev) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct tps68470_gpio_data *tps68470_gpio; 1288c2ecf20Sopenharmony_ci int ret; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci tps68470_gpio = devm_kzalloc(&pdev->dev, sizeof(*tps68470_gpio), 1318c2ecf20Sopenharmony_ci GFP_KERNEL); 1328c2ecf20Sopenharmony_ci if (!tps68470_gpio) 1338c2ecf20Sopenharmony_ci return -ENOMEM; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci tps68470_gpio->tps68470_regmap = dev_get_drvdata(pdev->dev.parent); 1368c2ecf20Sopenharmony_ci tps68470_gpio->gc.label = "tps68470-gpio"; 1378c2ecf20Sopenharmony_ci tps68470_gpio->gc.owner = THIS_MODULE; 1388c2ecf20Sopenharmony_ci tps68470_gpio->gc.direction_input = tps68470_gpio_input; 1398c2ecf20Sopenharmony_ci tps68470_gpio->gc.direction_output = tps68470_gpio_output; 1408c2ecf20Sopenharmony_ci tps68470_gpio->gc.get = tps68470_gpio_get; 1418c2ecf20Sopenharmony_ci tps68470_gpio->gc.get_direction = tps68470_gpio_get_direction; 1428c2ecf20Sopenharmony_ci tps68470_gpio->gc.set = tps68470_gpio_set; 1438c2ecf20Sopenharmony_ci tps68470_gpio->gc.can_sleep = true; 1448c2ecf20Sopenharmony_ci tps68470_gpio->gc.names = tps68470_names; 1458c2ecf20Sopenharmony_ci tps68470_gpio->gc.ngpio = TPS68470_N_GPIO; 1468c2ecf20Sopenharmony_ci tps68470_gpio->gc.base = -1; 1478c2ecf20Sopenharmony_ci tps68470_gpio->gc.parent = &pdev->dev; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci ret = devm_gpiochip_add_data(&pdev->dev, &tps68470_gpio->gc, 1508c2ecf20Sopenharmony_ci tps68470_gpio); 1518c2ecf20Sopenharmony_ci if (ret < 0) { 1528c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to register gpio_chip: %d\n", ret); 1538c2ecf20Sopenharmony_ci return ret; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, tps68470_gpio); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return ret; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic struct platform_driver tps68470_gpio_driver = { 1628c2ecf20Sopenharmony_ci .driver = { 1638c2ecf20Sopenharmony_ci .name = "tps68470-gpio", 1648c2ecf20Sopenharmony_ci }, 1658c2ecf20Sopenharmony_ci .probe = tps68470_gpio_probe, 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cibuiltin_platform_driver(tps68470_gpio_driver) 169