18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2015 Verifone Int.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Nicolas Saenz Julienne <nicolassaenzj@gmail.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This driver is based on the gpio-tps65912 implementation.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/errno.h>
138c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
148c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
158c2ecf20Sopenharmony_ci#include <linux/regmap.h>
168c2ecf20Sopenharmony_ci#include <linux/mfd/tps65218.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistruct tps65218_gpio {
198c2ecf20Sopenharmony_ci	struct tps65218 *tps65218;
208c2ecf20Sopenharmony_ci	struct gpio_chip gpio_chip;
218c2ecf20Sopenharmony_ci};
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic int tps65218_gpio_get(struct gpio_chip *gc, unsigned offset)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc);
268c2ecf20Sopenharmony_ci	struct tps65218 *tps65218 = tps65218_gpio->tps65218;
278c2ecf20Sopenharmony_ci	unsigned int val;
288c2ecf20Sopenharmony_ci	int ret;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	ret = regmap_read(tps65218->regmap, TPS65218_REG_ENABLE2, &val);
318c2ecf20Sopenharmony_ci	if (ret)
328c2ecf20Sopenharmony_ci		return ret;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	return !!(val & (TPS65218_ENABLE2_GPIO1 << offset));
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic void tps65218_gpio_set(struct gpio_chip *gc, unsigned offset,
388c2ecf20Sopenharmony_ci			      int value)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc);
418c2ecf20Sopenharmony_ci	struct tps65218 *tps65218 = tps65218_gpio->tps65218;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	if (value)
448c2ecf20Sopenharmony_ci		tps65218_set_bits(tps65218, TPS65218_REG_ENABLE2,
458c2ecf20Sopenharmony_ci				  TPS65218_ENABLE2_GPIO1 << offset,
468c2ecf20Sopenharmony_ci				  TPS65218_ENABLE2_GPIO1 << offset,
478c2ecf20Sopenharmony_ci				  TPS65218_PROTECT_L1);
488c2ecf20Sopenharmony_ci	else
498c2ecf20Sopenharmony_ci		tps65218_clear_bits(tps65218, TPS65218_REG_ENABLE2,
508c2ecf20Sopenharmony_ci				    TPS65218_ENABLE2_GPIO1 << offset,
518c2ecf20Sopenharmony_ci				    TPS65218_PROTECT_L1);
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic int tps65218_gpio_output(struct gpio_chip *gc, unsigned offset,
558c2ecf20Sopenharmony_ci				int value)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	/* Only drives GPOs */
588c2ecf20Sopenharmony_ci	tps65218_gpio_set(gc, offset, value);
598c2ecf20Sopenharmony_ci	return 0;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic int tps65218_gpio_input(struct gpio_chip *gc, unsigned offset)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	return -EPERM;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int tps65218_gpio_request(struct gpio_chip *gc, unsigned offset)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc);
708c2ecf20Sopenharmony_ci	struct tps65218 *tps65218 = tps65218_gpio->tps65218;
718c2ecf20Sopenharmony_ci	int ret;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (gpiochip_line_is_open_source(gc, offset)) {
748c2ecf20Sopenharmony_ci		dev_err(gc->parent, "can't work as open source\n");
758c2ecf20Sopenharmony_ci		return -EINVAL;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	switch (offset) {
798c2ecf20Sopenharmony_ci	case 0:
808c2ecf20Sopenharmony_ci		if (!gpiochip_line_is_open_drain(gc, offset)) {
818c2ecf20Sopenharmony_ci			dev_err(gc->parent, "GPO1 works only as open drain\n");
828c2ecf20Sopenharmony_ci			return -EINVAL;
838c2ecf20Sopenharmony_ci		}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci		/* Disable sequencer for GPO1 */
868c2ecf20Sopenharmony_ci		ret = tps65218_clear_bits(tps65218, TPS65218_REG_SEQ7,
878c2ecf20Sopenharmony_ci					  TPS65218_SEQ7_GPO1_SEQ_MASK,
888c2ecf20Sopenharmony_ci					  TPS65218_PROTECT_L1);
898c2ecf20Sopenharmony_ci		if (ret)
908c2ecf20Sopenharmony_ci			return ret;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci		/* Setup GPO1 */
938c2ecf20Sopenharmony_ci		ret = tps65218_clear_bits(tps65218, TPS65218_REG_CONFIG1,
948c2ecf20Sopenharmony_ci					  TPS65218_CONFIG1_IO1_SEL,
958c2ecf20Sopenharmony_ci					  TPS65218_PROTECT_L1);
968c2ecf20Sopenharmony_ci		if (ret)
978c2ecf20Sopenharmony_ci			return ret;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci		break;
1008c2ecf20Sopenharmony_ci	case 1:
1018c2ecf20Sopenharmony_ci		/* Setup GPO2 */
1028c2ecf20Sopenharmony_ci		ret = tps65218_clear_bits(tps65218, TPS65218_REG_CONFIG1,
1038c2ecf20Sopenharmony_ci					  TPS65218_CONFIG1_IO1_SEL,
1048c2ecf20Sopenharmony_ci					  TPS65218_PROTECT_L1);
1058c2ecf20Sopenharmony_ci		if (ret)
1068c2ecf20Sopenharmony_ci			return ret;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci		break;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	case 2:
1118c2ecf20Sopenharmony_ci		if (!gpiochip_line_is_open_drain(gc, offset)) {
1128c2ecf20Sopenharmony_ci			dev_err(gc->parent, "GPO3 works only as open drain\n");
1138c2ecf20Sopenharmony_ci			return -EINVAL;
1148c2ecf20Sopenharmony_ci		}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci		/* Disable sequencer for GPO3 */
1178c2ecf20Sopenharmony_ci		ret = tps65218_clear_bits(tps65218, TPS65218_REG_SEQ7,
1188c2ecf20Sopenharmony_ci					  TPS65218_SEQ7_GPO3_SEQ_MASK,
1198c2ecf20Sopenharmony_ci					  TPS65218_PROTECT_L1);
1208c2ecf20Sopenharmony_ci		if (ret)
1218c2ecf20Sopenharmony_ci			return ret;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		/* Setup GPO3 */
1248c2ecf20Sopenharmony_ci		ret = tps65218_clear_bits(tps65218, TPS65218_REG_CONFIG2,
1258c2ecf20Sopenharmony_ci					  TPS65218_CONFIG2_DC12_RST,
1268c2ecf20Sopenharmony_ci					  TPS65218_PROTECT_L1);
1278c2ecf20Sopenharmony_ci		if (ret)
1288c2ecf20Sopenharmony_ci			return ret;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci		break;
1318c2ecf20Sopenharmony_ci	default:
1328c2ecf20Sopenharmony_ci		return -EINVAL;
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	return 0;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic int tps65218_gpio_set_config(struct gpio_chip *gc, unsigned offset,
1398c2ecf20Sopenharmony_ci				    unsigned long config)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc);
1428c2ecf20Sopenharmony_ci	struct tps65218 *tps65218 = tps65218_gpio->tps65218;
1438c2ecf20Sopenharmony_ci	enum pin_config_param param = pinconf_to_config_param(config);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	switch (offset) {
1468c2ecf20Sopenharmony_ci	case 0:
1478c2ecf20Sopenharmony_ci	case 2:
1488c2ecf20Sopenharmony_ci		/* GPO1 is hardwired to be open drain */
1498c2ecf20Sopenharmony_ci		if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN)
1508c2ecf20Sopenharmony_ci			return 0;
1518c2ecf20Sopenharmony_ci		return -ENOTSUPP;
1528c2ecf20Sopenharmony_ci	case 1:
1538c2ecf20Sopenharmony_ci		/* GPO2 is push-pull by default, can be set as open drain. */
1548c2ecf20Sopenharmony_ci		if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN)
1558c2ecf20Sopenharmony_ci			return tps65218_clear_bits(tps65218,
1568c2ecf20Sopenharmony_ci						   TPS65218_REG_CONFIG1,
1578c2ecf20Sopenharmony_ci						   TPS65218_CONFIG1_GPO2_BUF,
1588c2ecf20Sopenharmony_ci						   TPS65218_PROTECT_L1);
1598c2ecf20Sopenharmony_ci		if (param == PIN_CONFIG_DRIVE_PUSH_PULL)
1608c2ecf20Sopenharmony_ci			return tps65218_set_bits(tps65218,
1618c2ecf20Sopenharmony_ci						 TPS65218_REG_CONFIG1,
1628c2ecf20Sopenharmony_ci						 TPS65218_CONFIG1_GPO2_BUF,
1638c2ecf20Sopenharmony_ci						 TPS65218_CONFIG1_GPO2_BUF,
1648c2ecf20Sopenharmony_ci						 TPS65218_PROTECT_L1);
1658c2ecf20Sopenharmony_ci		return -ENOTSUPP;
1668c2ecf20Sopenharmony_ci	default:
1678c2ecf20Sopenharmony_ci		break;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci	return -ENOTSUPP;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic const struct gpio_chip template_chip = {
1738c2ecf20Sopenharmony_ci	.label			= "gpio-tps65218",
1748c2ecf20Sopenharmony_ci	.owner			= THIS_MODULE,
1758c2ecf20Sopenharmony_ci	.request		= tps65218_gpio_request,
1768c2ecf20Sopenharmony_ci	.direction_output	= tps65218_gpio_output,
1778c2ecf20Sopenharmony_ci	.direction_input	= tps65218_gpio_input,
1788c2ecf20Sopenharmony_ci	.get			= tps65218_gpio_get,
1798c2ecf20Sopenharmony_ci	.set			= tps65218_gpio_set,
1808c2ecf20Sopenharmony_ci	.set_config		= tps65218_gpio_set_config,
1818c2ecf20Sopenharmony_ci	.can_sleep		= true,
1828c2ecf20Sopenharmony_ci	.ngpio			= 3,
1838c2ecf20Sopenharmony_ci	.base			= -1,
1848c2ecf20Sopenharmony_ci};
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int tps65218_gpio_probe(struct platform_device *pdev)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct tps65218 *tps65218 = dev_get_drvdata(pdev->dev.parent);
1898c2ecf20Sopenharmony_ci	struct tps65218_gpio *tps65218_gpio;
1908c2ecf20Sopenharmony_ci	int ret;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	tps65218_gpio = devm_kzalloc(&pdev->dev, sizeof(*tps65218_gpio),
1938c2ecf20Sopenharmony_ci				     GFP_KERNEL);
1948c2ecf20Sopenharmony_ci	if (!tps65218_gpio)
1958c2ecf20Sopenharmony_ci		return -ENOMEM;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	tps65218_gpio->tps65218 = tps65218;
1988c2ecf20Sopenharmony_ci	tps65218_gpio->gpio_chip = template_chip;
1998c2ecf20Sopenharmony_ci	tps65218_gpio->gpio_chip.parent = &pdev->dev;
2008c2ecf20Sopenharmony_ci#ifdef CONFIG_OF_GPIO
2018c2ecf20Sopenharmony_ci	tps65218_gpio->gpio_chip.of_node = pdev->dev.of_node;
2028c2ecf20Sopenharmony_ci#endif
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	ret = devm_gpiochip_add_data(&pdev->dev, &tps65218_gpio->gpio_chip,
2058c2ecf20Sopenharmony_ci				     tps65218_gpio);
2068c2ecf20Sopenharmony_ci	if (ret < 0) {
2078c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register gpiochip, %d\n", ret);
2088c2ecf20Sopenharmony_ci		return ret;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, tps65218_gpio);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	return ret;
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic const struct of_device_id tps65218_dt_match[] = {
2178c2ecf20Sopenharmony_ci	{ .compatible = "ti,tps65218-gpio" },
2188c2ecf20Sopenharmony_ci	{  }
2198c2ecf20Sopenharmony_ci};
2208c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tps65218_dt_match);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic const struct platform_device_id tps65218_gpio_id_table[] = {
2238c2ecf20Sopenharmony_ci	{ "tps65218-gpio", },
2248c2ecf20Sopenharmony_ci	{ /* sentinel */ }
2258c2ecf20Sopenharmony_ci};
2268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, tps65218_gpio_id_table);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic struct platform_driver tps65218_gpio_driver = {
2298c2ecf20Sopenharmony_ci	.driver = {
2308c2ecf20Sopenharmony_ci		.name = "tps65218-gpio",
2318c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(tps65218_dt_match)
2328c2ecf20Sopenharmony_ci	},
2338c2ecf20Sopenharmony_ci	.probe = tps65218_gpio_probe,
2348c2ecf20Sopenharmony_ci	.id_table = tps65218_gpio_id_table,
2358c2ecf20Sopenharmony_ci};
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cimodule_platform_driver(tps65218_gpio_driver);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nicolas Saenz Julienne <nicolassaenzj@gmail.com>");
2408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GPO interface for TPS65218 PMICs");
2418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
2428c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:tps65218-gpio");
243