18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * TI TPS6591x GPIO driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2010 Texas Instruments Inc.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Graeme Gregory <gg@slimlogic.co.uk>
88c2ecf20Sopenharmony_ci * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/errno.h>
148c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
158c2ecf20Sopenharmony_ci#include <linux/i2c.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/mfd/tps65910.h>
188c2ecf20Sopenharmony_ci#include <linux/of_device.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct tps65910_gpio {
218c2ecf20Sopenharmony_ci	struct gpio_chip gpio_chip;
228c2ecf20Sopenharmony_ci	struct tps65910 *tps65910;
238c2ecf20Sopenharmony_ci};
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	struct tps65910_gpio *tps65910_gpio = gpiochip_get_data(gc);
288c2ecf20Sopenharmony_ci	struct tps65910 *tps65910 = tps65910_gpio->tps65910;
298c2ecf20Sopenharmony_ci	unsigned int val;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	tps65910_reg_read(tps65910, TPS65910_GPIO0 + offset, &val);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	if (val & GPIO_STS_MASK)
348c2ecf20Sopenharmony_ci		return 1;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	return 0;
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic void tps65910_gpio_set(struct gpio_chip *gc, unsigned offset,
408c2ecf20Sopenharmony_ci			      int value)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	struct tps65910_gpio *tps65910_gpio = gpiochip_get_data(gc);
438c2ecf20Sopenharmony_ci	struct tps65910 *tps65910 = tps65910_gpio->tps65910;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (value)
468c2ecf20Sopenharmony_ci		tps65910_reg_set_bits(tps65910, TPS65910_GPIO0 + offset,
478c2ecf20Sopenharmony_ci						GPIO_SET_MASK);
488c2ecf20Sopenharmony_ci	else
498c2ecf20Sopenharmony_ci		tps65910_reg_clear_bits(tps65910, TPS65910_GPIO0 + offset,
508c2ecf20Sopenharmony_ci						GPIO_SET_MASK);
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset,
548c2ecf20Sopenharmony_ci				int value)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct tps65910_gpio *tps65910_gpio = gpiochip_get_data(gc);
578c2ecf20Sopenharmony_ci	struct tps65910 *tps65910 = tps65910_gpio->tps65910;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	/* Set the initial value */
608c2ecf20Sopenharmony_ci	tps65910_gpio_set(gc, offset, value);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return tps65910_reg_set_bits(tps65910, TPS65910_GPIO0 + offset,
638c2ecf20Sopenharmony_ci						GPIO_CFG_MASK);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic int tps65910_gpio_input(struct gpio_chip *gc, unsigned offset)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct tps65910_gpio *tps65910_gpio = gpiochip_get_data(gc);
698c2ecf20Sopenharmony_ci	struct tps65910 *tps65910 = tps65910_gpio->tps65910;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	return tps65910_reg_clear_bits(tps65910, TPS65910_GPIO0 + offset,
728c2ecf20Sopenharmony_ci						GPIO_CFG_MASK);
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
768c2ecf20Sopenharmony_cistatic struct tps65910_board *tps65910_parse_dt_for_gpio(struct device *dev,
778c2ecf20Sopenharmony_ci		struct tps65910 *tps65910, int chip_ngpio)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct tps65910_board *tps65910_board = tps65910->of_plat_data;
808c2ecf20Sopenharmony_ci	unsigned int prop_array[TPS6591X_MAX_NUM_GPIO];
818c2ecf20Sopenharmony_ci	int ngpio = min(chip_ngpio, TPS6591X_MAX_NUM_GPIO);
828c2ecf20Sopenharmony_ci	int ret;
838c2ecf20Sopenharmony_ci	int idx;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	tps65910_board->gpio_base = -1;
868c2ecf20Sopenharmony_ci	ret = of_property_read_u32_array(tps65910->dev->of_node,
878c2ecf20Sopenharmony_ci			"ti,en-gpio-sleep", prop_array, ngpio);
888c2ecf20Sopenharmony_ci	if (ret < 0) {
898c2ecf20Sopenharmony_ci		dev_dbg(dev, "ti,en-gpio-sleep not specified\n");
908c2ecf20Sopenharmony_ci		return tps65910_board;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	for (idx = 0; idx < ngpio; idx++)
948c2ecf20Sopenharmony_ci		tps65910_board->en_gpio_sleep[idx] = (prop_array[idx] != 0);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return tps65910_board;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci#else
998c2ecf20Sopenharmony_cistatic struct tps65910_board *tps65910_parse_dt_for_gpio(struct device *dev,
1008c2ecf20Sopenharmony_ci		struct tps65910 *tps65910, int chip_ngpio)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	return NULL;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci#endif
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic int tps65910_gpio_probe(struct platform_device *pdev)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent);
1098c2ecf20Sopenharmony_ci	struct tps65910_board *pdata = dev_get_platdata(tps65910->dev);
1108c2ecf20Sopenharmony_ci	struct tps65910_gpio *tps65910_gpio;
1118c2ecf20Sopenharmony_ci	int ret;
1128c2ecf20Sopenharmony_ci	int i;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	tps65910_gpio = devm_kzalloc(&pdev->dev,
1158c2ecf20Sopenharmony_ci				sizeof(*tps65910_gpio), GFP_KERNEL);
1168c2ecf20Sopenharmony_ci	if (!tps65910_gpio)
1178c2ecf20Sopenharmony_ci		return -ENOMEM;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	tps65910_gpio->tps65910 = tps65910;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	tps65910_gpio->gpio_chip.owner = THIS_MODULE;
1228c2ecf20Sopenharmony_ci	tps65910_gpio->gpio_chip.label = tps65910->i2c_client->name;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	switch (tps65910_chip_id(tps65910)) {
1258c2ecf20Sopenharmony_ci	case TPS65910:
1268c2ecf20Sopenharmony_ci		tps65910_gpio->gpio_chip.ngpio = TPS65910_NUM_GPIO;
1278c2ecf20Sopenharmony_ci		break;
1288c2ecf20Sopenharmony_ci	case TPS65911:
1298c2ecf20Sopenharmony_ci		tps65910_gpio->gpio_chip.ngpio = TPS65911_NUM_GPIO;
1308c2ecf20Sopenharmony_ci		break;
1318c2ecf20Sopenharmony_ci	default:
1328c2ecf20Sopenharmony_ci		return -EINVAL;
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci	tps65910_gpio->gpio_chip.can_sleep = true;
1358c2ecf20Sopenharmony_ci	tps65910_gpio->gpio_chip.direction_input = tps65910_gpio_input;
1368c2ecf20Sopenharmony_ci	tps65910_gpio->gpio_chip.direction_output = tps65910_gpio_output;
1378c2ecf20Sopenharmony_ci	tps65910_gpio->gpio_chip.set	= tps65910_gpio_set;
1388c2ecf20Sopenharmony_ci	tps65910_gpio->gpio_chip.get	= tps65910_gpio_get;
1398c2ecf20Sopenharmony_ci	tps65910_gpio->gpio_chip.parent = &pdev->dev;
1408c2ecf20Sopenharmony_ci#ifdef CONFIG_OF_GPIO
1418c2ecf20Sopenharmony_ci	tps65910_gpio->gpio_chip.of_node = tps65910->dev->of_node;
1428c2ecf20Sopenharmony_ci#endif
1438c2ecf20Sopenharmony_ci	if (pdata && pdata->gpio_base)
1448c2ecf20Sopenharmony_ci		tps65910_gpio->gpio_chip.base = pdata->gpio_base;
1458c2ecf20Sopenharmony_ci	else
1468c2ecf20Sopenharmony_ci		tps65910_gpio->gpio_chip.base = -1;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (!pdata && tps65910->dev->of_node)
1498c2ecf20Sopenharmony_ci		pdata = tps65910_parse_dt_for_gpio(&pdev->dev, tps65910,
1508c2ecf20Sopenharmony_ci			tps65910_gpio->gpio_chip.ngpio);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if (!pdata)
1538c2ecf20Sopenharmony_ci		goto skip_init;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* Configure sleep control for gpios if provided */
1568c2ecf20Sopenharmony_ci	for (i = 0; i < tps65910_gpio->gpio_chip.ngpio; ++i) {
1578c2ecf20Sopenharmony_ci		if (!pdata->en_gpio_sleep[i])
1588c2ecf20Sopenharmony_ci			continue;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci		ret = tps65910_reg_set_bits(tps65910,
1618c2ecf20Sopenharmony_ci			TPS65910_GPIO0 + i, GPIO_SLEEP_MASK);
1628c2ecf20Sopenharmony_ci		if (ret < 0)
1638c2ecf20Sopenharmony_ci			dev_warn(tps65910->dev,
1648c2ecf20Sopenharmony_ci				"GPIO Sleep setting failed with err %d\n", ret);
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ciskip_init:
1688c2ecf20Sopenharmony_ci	ret = devm_gpiochip_add_data(&pdev->dev, &tps65910_gpio->gpio_chip,
1698c2ecf20Sopenharmony_ci				     tps65910_gpio);
1708c2ecf20Sopenharmony_ci	if (ret < 0) {
1718c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
1728c2ecf20Sopenharmony_ci		return ret;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, tps65910_gpio);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	return ret;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic struct platform_driver tps65910_gpio_driver = {
1818c2ecf20Sopenharmony_ci	.driver.name    = "tps65910-gpio",
1828c2ecf20Sopenharmony_ci	.probe		= tps65910_gpio_probe,
1838c2ecf20Sopenharmony_ci};
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int __init tps65910_gpio_init(void)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	return platform_driver_register(&tps65910_gpio_driver);
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_cisubsys_initcall(tps65910_gpio_init);
190