Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.9.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * GPIO driver for TI TPS65219 PMICs
  4 *
  5 * Copyright (C) 2022 Texas Instruments Incorporated - http://www.ti.com/
  6 */
  7
  8#include <linux/bits.h>
  9#include <linux/gpio/driver.h>
 10#include <linux/mfd/tps65219.h>
 11#include <linux/module.h>
 12#include <linux/platform_device.h>
 13#include <linux/regmap.h>
 14
 15#define TPS65219_GPIO0_DIR_MASK		BIT(3)
 16#define TPS65219_GPIO0_OFFSET		2
 17#define TPS65219_GPIO0_IDX		0
 18#define TPS65219_GPIO_DIR_IN		1
 19#define TPS65219_GPIO_DIR_OUT		0
 20
 21struct tps65219_gpio {
 22	struct gpio_chip gpio_chip;
 23	struct tps65219 *tps;
 24};
 25
 26static int tps65219_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
 27{
 28	struct tps65219_gpio *gpio = gpiochip_get_data(gc);
 29	int ret, val;
 30
 31	if (offset != TPS65219_GPIO0_IDX)
 32		return GPIO_LINE_DIRECTION_OUT;
 33
 34	ret = regmap_read(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG, &val);
 35	if (ret)
 36		return ret;
 37
 38	return !!(val & TPS65219_GPIO0_DIR_MASK);
 39}
 40
 41static int tps65219_gpio_get(struct gpio_chip *gc, unsigned int offset)
 42{
 43	struct tps65219_gpio *gpio = gpiochip_get_data(gc);
 44	struct device *dev = gpio->tps->dev;
 45	int ret, val;
 46
 47	if (offset != TPS65219_GPIO0_IDX) {
 48		dev_err(dev, "GPIO%d is output only, cannot get\n", offset);
 49		return -ENOTSUPP;
 50	}
 51
 52	ret = regmap_read(gpio->tps->regmap, TPS65219_REG_MFP_CTRL, &val);
 53	if (ret)
 54		return ret;
 55
 56	ret = !!(val & BIT(TPS65219_MFP_GPIO_STATUS_MASK));
 57	dev_warn(dev, "GPIO%d = %d, MULTI_DEVICE_ENABLE, not a standard GPIO\n", offset, ret);
 58
 59	/*
 60	 * Depending on NVM config, return an error if direction is output, otherwise the GPIO0
 61	 * status bit.
 62	 */
 63
 64	if (tps65219_gpio_get_direction(gc, offset) == TPS65219_GPIO_DIR_OUT)
 65		return -ENOTSUPP;
 66
 67	return ret;
 68}
 69
 70static void tps65219_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
 71{
 72	struct tps65219_gpio *gpio = gpiochip_get_data(gc);
 73	struct device *dev = gpio->tps->dev;
 74	int v, mask, bit;
 75
 76	bit = (offset == TPS65219_GPIO0_IDX) ? TPS65219_GPIO0_OFFSET : offset - 1;
 77
 78	mask = BIT(bit);
 79	v = value ? mask : 0;
 80
 81	if (regmap_update_bits(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG, mask, v))
 82		dev_err(dev, "GPIO%d, set to value %d failed.\n", offset, value);
 83}
 84
 85static int tps65219_gpio_change_direction(struct gpio_chip *gc, unsigned int offset,
 86					  unsigned int direction)
 87{
 88	struct tps65219_gpio *gpio = gpiochip_get_data(gc);
 89	struct device *dev = gpio->tps->dev;
 90
 91	/*
 92	 * Documentation is stating that GPIO0 direction must not be changed in Linux:
 93	 * Table 8-34. MFP_1_CONFIG(3): MULTI_DEVICE_ENABLE, should only be changed in INITIALIZE
 94	 * state (prior to ON Request).
 95	 * Set statically by NVM, changing direction in application can cause a hang.
 96	 * Below can be used for test purpose only.
 97	 */
 98
 99#if 0
100	int ret = regmap_update_bits(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG,
101				     TPS65219_GPIO0_DIR_MASK, direction);
102	if (ret) {
103		dev_err(dev,
104			"GPIO DEBUG enabled: Fail to change direction to %u for GPIO%d.\n",
105			direction, offset);
106		return ret;
107	}
108#endif
109
110	dev_err(dev,
111		"GPIO%d direction set by NVM, change to %u failed, not allowed by specification\n",
112		 offset, direction);
113
114	return -ENOTSUPP;
115}
116
117static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)
118{
119	struct tps65219_gpio *gpio = gpiochip_get_data(gc);
120	struct device *dev = gpio->tps->dev;
121
122	if (offset != TPS65219_GPIO0_IDX) {
123		dev_err(dev, "GPIO%d is output only, cannot change to input\n", offset);
124		return -ENOTSUPP;
125	}
126
127	if (tps65219_gpio_get_direction(gc, offset) == TPS65219_GPIO_DIR_IN)
128		return 0;
129
130	return tps65219_gpio_change_direction(gc, offset, TPS65219_GPIO_DIR_IN);
131}
132
133static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value)
134{
135	tps65219_gpio_set(gc, offset, value);
136	if (offset != TPS65219_GPIO0_IDX)
137		return 0;
138
139	if (tps65219_gpio_get_direction(gc, offset) == TPS65219_GPIO_DIR_OUT)
140		return 0;
141
142	return tps65219_gpio_change_direction(gc, offset, TPS65219_GPIO_DIR_OUT);
143}
144
145static const struct gpio_chip tps65219_template_chip = {
146	.label			= "tps65219-gpio",
147	.owner			= THIS_MODULE,
148	.get_direction		= tps65219_gpio_get_direction,
149	.direction_input	= tps65219_gpio_direction_input,
150	.direction_output	= tps65219_gpio_direction_output,
151	.get			= tps65219_gpio_get,
152	.set			= tps65219_gpio_set,
153	.base			= -1,
154	.ngpio			= 3,
155	.can_sleep		= true,
156};
157
158static int tps65219_gpio_probe(struct platform_device *pdev)
159{
160	struct tps65219 *tps = dev_get_drvdata(pdev->dev.parent);
161	struct tps65219_gpio *gpio;
162
163	gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
164	if (!gpio)
165		return -ENOMEM;
166
167	gpio->tps = tps;
168	gpio->gpio_chip = tps65219_template_chip;
169	gpio->gpio_chip.parent = tps->dev;
170
171	return devm_gpiochip_add_data(&pdev->dev, &gpio->gpio_chip, gpio);
172}
173
174static struct platform_driver tps65219_gpio_driver = {
175	.driver = {
176		.name = "tps65219-gpio",
177	},
178	.probe = tps65219_gpio_probe,
179};
180module_platform_driver(tps65219_gpio_driver);
181
182MODULE_ALIAS("platform:tps65219-gpio");
183MODULE_AUTHOR("Jonathan Cormier <jcormier@criticallink.com>");
184MODULE_DESCRIPTION("TPS65219 GPIO driver");
185MODULE_LICENSE("GPL");