18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * TI Palma series PMIC's GPIO driver.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Laxman Dewangan <ldewangan@nvidia.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/mfd/palmas.h>
148c2ecf20Sopenharmony_ci#include <linux/of.h>
158c2ecf20Sopenharmony_ci#include <linux/of_device.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistruct palmas_gpio {
198c2ecf20Sopenharmony_ci	struct gpio_chip gpio_chip;
208c2ecf20Sopenharmony_ci	struct palmas *palmas;
218c2ecf20Sopenharmony_ci};
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistruct palmas_device_data {
248c2ecf20Sopenharmony_ci	int ngpio;
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int palmas_gpio_get(struct gpio_chip *gc, unsigned offset)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct palmas_gpio *pg = gpiochip_get_data(gc);
308c2ecf20Sopenharmony_ci	struct palmas *palmas = pg->palmas;
318c2ecf20Sopenharmony_ci	unsigned int val;
328c2ecf20Sopenharmony_ci	int ret;
338c2ecf20Sopenharmony_ci	unsigned int reg;
348c2ecf20Sopenharmony_ci	int gpio16 = (offset/8);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	offset %= 8;
378c2ecf20Sopenharmony_ci	reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	ret = palmas_read(palmas, PALMAS_GPIO_BASE, reg, &val);
408c2ecf20Sopenharmony_ci	if (ret < 0) {
418c2ecf20Sopenharmony_ci		dev_err(gc->parent, "Reg 0x%02x read failed, %d\n", reg, ret);
428c2ecf20Sopenharmony_ci		return ret;
438c2ecf20Sopenharmony_ci	}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (val & BIT(offset))
468c2ecf20Sopenharmony_ci		reg = (gpio16) ? PALMAS_GPIO_DATA_OUT2 : PALMAS_GPIO_DATA_OUT;
478c2ecf20Sopenharmony_ci	else
488c2ecf20Sopenharmony_ci		reg = (gpio16) ? PALMAS_GPIO_DATA_IN2 : PALMAS_GPIO_DATA_IN;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	ret = palmas_read(palmas, PALMAS_GPIO_BASE, reg, &val);
518c2ecf20Sopenharmony_ci	if (ret < 0) {
528c2ecf20Sopenharmony_ci		dev_err(gc->parent, "Reg 0x%02x read failed, %d\n", reg, ret);
538c2ecf20Sopenharmony_ci		return ret;
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci	return !!(val & BIT(offset));
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic void palmas_gpio_set(struct gpio_chip *gc, unsigned offset,
598c2ecf20Sopenharmony_ci			int value)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct palmas_gpio *pg = gpiochip_get_data(gc);
628c2ecf20Sopenharmony_ci	struct palmas *palmas = pg->palmas;
638c2ecf20Sopenharmony_ci	int ret;
648c2ecf20Sopenharmony_ci	unsigned int reg;
658c2ecf20Sopenharmony_ci	int gpio16 = (offset/8);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	offset %= 8;
688c2ecf20Sopenharmony_ci	if (gpio16)
698c2ecf20Sopenharmony_ci		reg = (value) ?
708c2ecf20Sopenharmony_ci			PALMAS_GPIO_SET_DATA_OUT2 : PALMAS_GPIO_CLEAR_DATA_OUT2;
718c2ecf20Sopenharmony_ci	else
728c2ecf20Sopenharmony_ci		reg = (value) ?
738c2ecf20Sopenharmony_ci			PALMAS_GPIO_SET_DATA_OUT : PALMAS_GPIO_CLEAR_DATA_OUT;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	ret = palmas_write(palmas, PALMAS_GPIO_BASE, reg, BIT(offset));
768c2ecf20Sopenharmony_ci	if (ret < 0)
778c2ecf20Sopenharmony_ci		dev_err(gc->parent, "Reg 0x%02x write failed, %d\n", reg, ret);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic int palmas_gpio_output(struct gpio_chip *gc, unsigned offset,
818c2ecf20Sopenharmony_ci				int value)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct palmas_gpio *pg = gpiochip_get_data(gc);
848c2ecf20Sopenharmony_ci	struct palmas *palmas = pg->palmas;
858c2ecf20Sopenharmony_ci	int ret;
868c2ecf20Sopenharmony_ci	unsigned int reg;
878c2ecf20Sopenharmony_ci	int gpio16 = (offset/8);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	offset %= 8;
908c2ecf20Sopenharmony_ci	reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/* Set the initial value */
938c2ecf20Sopenharmony_ci	palmas_gpio_set(gc, offset, value);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE, reg,
968c2ecf20Sopenharmony_ci				BIT(offset), BIT(offset));
978c2ecf20Sopenharmony_ci	if (ret < 0)
988c2ecf20Sopenharmony_ci		dev_err(gc->parent, "Reg 0x%02x update failed, %d\n", reg,
998c2ecf20Sopenharmony_ci			ret);
1008c2ecf20Sopenharmony_ci	return ret;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic int palmas_gpio_input(struct gpio_chip *gc, unsigned offset)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct palmas_gpio *pg = gpiochip_get_data(gc);
1068c2ecf20Sopenharmony_ci	struct palmas *palmas = pg->palmas;
1078c2ecf20Sopenharmony_ci	int ret;
1088c2ecf20Sopenharmony_ci	unsigned int reg;
1098c2ecf20Sopenharmony_ci	int gpio16 = (offset/8);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	offset %= 8;
1128c2ecf20Sopenharmony_ci	reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE, reg, BIT(offset), 0);
1158c2ecf20Sopenharmony_ci	if (ret < 0)
1168c2ecf20Sopenharmony_ci		dev_err(gc->parent, "Reg 0x%02x update failed, %d\n", reg,
1178c2ecf20Sopenharmony_ci			ret);
1188c2ecf20Sopenharmony_ci	return ret;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic int palmas_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct palmas_gpio *pg = gpiochip_get_data(gc);
1248c2ecf20Sopenharmony_ci	struct palmas *palmas = pg->palmas;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	return palmas_irq_get_virq(palmas, PALMAS_GPIO_0_IRQ + offset);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic const struct palmas_device_data palmas_dev_data = {
1308c2ecf20Sopenharmony_ci	.ngpio = 8,
1318c2ecf20Sopenharmony_ci};
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic const struct palmas_device_data tps80036_dev_data = {
1348c2ecf20Sopenharmony_ci	.ngpio = 16,
1358c2ecf20Sopenharmony_ci};
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic const struct of_device_id of_palmas_gpio_match[] = {
1388c2ecf20Sopenharmony_ci	{ .compatible = "ti,palmas-gpio", .data = &palmas_dev_data,},
1398c2ecf20Sopenharmony_ci	{ .compatible = "ti,tps65913-gpio", .data = &palmas_dev_data,},
1408c2ecf20Sopenharmony_ci	{ .compatible = "ti,tps65914-gpio", .data = &palmas_dev_data,},
1418c2ecf20Sopenharmony_ci	{ .compatible = "ti,tps80036-gpio", .data = &tps80036_dev_data,},
1428c2ecf20Sopenharmony_ci	{ },
1438c2ecf20Sopenharmony_ci};
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int palmas_gpio_probe(struct platform_device *pdev)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
1488c2ecf20Sopenharmony_ci	struct palmas_platform_data *palmas_pdata;
1498c2ecf20Sopenharmony_ci	struct palmas_gpio *palmas_gpio;
1508c2ecf20Sopenharmony_ci	int ret;
1518c2ecf20Sopenharmony_ci	const struct palmas_device_data *dev_data;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	dev_data = of_device_get_match_data(&pdev->dev);
1548c2ecf20Sopenharmony_ci	if (!dev_data)
1558c2ecf20Sopenharmony_ci		dev_data = &palmas_dev_data;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	palmas_gpio = devm_kzalloc(&pdev->dev,
1588c2ecf20Sopenharmony_ci				sizeof(*palmas_gpio), GFP_KERNEL);
1598c2ecf20Sopenharmony_ci	if (!palmas_gpio)
1608c2ecf20Sopenharmony_ci		return -ENOMEM;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	palmas_gpio->palmas = palmas;
1638c2ecf20Sopenharmony_ci	palmas_gpio->gpio_chip.owner = THIS_MODULE;
1648c2ecf20Sopenharmony_ci	palmas_gpio->gpio_chip.label = dev_name(&pdev->dev);
1658c2ecf20Sopenharmony_ci	palmas_gpio->gpio_chip.ngpio = dev_data->ngpio;
1668c2ecf20Sopenharmony_ci	palmas_gpio->gpio_chip.can_sleep = true;
1678c2ecf20Sopenharmony_ci	palmas_gpio->gpio_chip.direction_input = palmas_gpio_input;
1688c2ecf20Sopenharmony_ci	palmas_gpio->gpio_chip.direction_output = palmas_gpio_output;
1698c2ecf20Sopenharmony_ci	palmas_gpio->gpio_chip.to_irq = palmas_gpio_to_irq;
1708c2ecf20Sopenharmony_ci	palmas_gpio->gpio_chip.set	= palmas_gpio_set;
1718c2ecf20Sopenharmony_ci	palmas_gpio->gpio_chip.get	= palmas_gpio_get;
1728c2ecf20Sopenharmony_ci	palmas_gpio->gpio_chip.parent = &pdev->dev;
1738c2ecf20Sopenharmony_ci#ifdef CONFIG_OF_GPIO
1748c2ecf20Sopenharmony_ci	palmas_gpio->gpio_chip.of_node = pdev->dev.of_node;
1758c2ecf20Sopenharmony_ci#endif
1768c2ecf20Sopenharmony_ci	palmas_pdata = dev_get_platdata(palmas->dev);
1778c2ecf20Sopenharmony_ci	if (palmas_pdata && palmas_pdata->gpio_base)
1788c2ecf20Sopenharmony_ci		palmas_gpio->gpio_chip.base = palmas_pdata->gpio_base;
1798c2ecf20Sopenharmony_ci	else
1808c2ecf20Sopenharmony_ci		palmas_gpio->gpio_chip.base = -1;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	ret = devm_gpiochip_add_data(&pdev->dev, &palmas_gpio->gpio_chip,
1838c2ecf20Sopenharmony_ci				     palmas_gpio);
1848c2ecf20Sopenharmony_ci	if (ret < 0) {
1858c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
1868c2ecf20Sopenharmony_ci		return ret;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, palmas_gpio);
1908c2ecf20Sopenharmony_ci	return ret;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic struct platform_driver palmas_gpio_driver = {
1948c2ecf20Sopenharmony_ci	.driver.name	= "palmas-gpio",
1958c2ecf20Sopenharmony_ci	.driver.of_match_table = of_palmas_gpio_match,
1968c2ecf20Sopenharmony_ci	.probe		= palmas_gpio_probe,
1978c2ecf20Sopenharmony_ci};
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic int __init palmas_gpio_init(void)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	return platform_driver_register(&palmas_gpio_driver);
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_cisubsys_initcall(palmas_gpio_init);
204