18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Digital I/O driver for Technologic Systems I2C FPGA Core
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2015, 2018 Technologic Systems
58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Savoir-Faire Linux
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
88c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License version 2 as
98c2ecf20Sopenharmony_ci * published by the Free Software Foundation.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any
128c2ecf20Sopenharmony_ci * kind, whether expressed or implied; without even the implied warranty
138c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
148c2ecf20Sopenharmony_ci * GNU General Public License version 2 for more details.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
188c2ecf20Sopenharmony_ci#include <linux/i2c.h>
198c2ecf20Sopenharmony_ci#include <linux/of_device.h>
208c2ecf20Sopenharmony_ci#include <linux/module.h>
218c2ecf20Sopenharmony_ci#include <linux/regmap.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define DEFAULT_PIN_NUMBER	32
248c2ecf20Sopenharmony_ci/*
258c2ecf20Sopenharmony_ci * Register bits used by the GPIO device
268c2ecf20Sopenharmony_ci * Some boards, such as TS-7970 do not have a separate input bit
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_ci#define TS4900_GPIO_OE		0x01
298c2ecf20Sopenharmony_ci#define TS4900_GPIO_OUT		0x02
308c2ecf20Sopenharmony_ci#define TS4900_GPIO_IN		0x04
318c2ecf20Sopenharmony_ci#define TS7970_GPIO_IN		0x02
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistruct ts4900_gpio_priv {
348c2ecf20Sopenharmony_ci	struct regmap *regmap;
358c2ecf20Sopenharmony_ci	struct gpio_chip gpio_chip;
368c2ecf20Sopenharmony_ci	unsigned int input_bit;
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic int ts4900_gpio_get_direction(struct gpio_chip *chip,
408c2ecf20Sopenharmony_ci				     unsigned int offset)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	struct ts4900_gpio_priv *priv = gpiochip_get_data(chip);
438c2ecf20Sopenharmony_ci	unsigned int reg;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	regmap_read(priv->regmap, offset, &reg);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	if (reg & TS4900_GPIO_OE)
488c2ecf20Sopenharmony_ci		return GPIO_LINE_DIRECTION_OUT;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	return GPIO_LINE_DIRECTION_IN;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic int ts4900_gpio_direction_input(struct gpio_chip *chip,
548c2ecf20Sopenharmony_ci				       unsigned int offset)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct ts4900_gpio_priv *priv = gpiochip_get_data(chip);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	/* Only clear the OE bit here, requires a RMW. Prevents potential issue
598c2ecf20Sopenharmony_ci	 * with OE and data getting to the physical pin at different times.
608c2ecf20Sopenharmony_ci	 */
618c2ecf20Sopenharmony_ci	return regmap_update_bits(priv->regmap, offset, TS4900_GPIO_OE, 0);
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic int ts4900_gpio_direction_output(struct gpio_chip *chip,
658c2ecf20Sopenharmony_ci					unsigned int offset, int value)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct ts4900_gpio_priv *priv = gpiochip_get_data(chip);
688c2ecf20Sopenharmony_ci	unsigned int reg;
698c2ecf20Sopenharmony_ci	int ret;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	/* If changing from an input to an output, we need to first set the
728c2ecf20Sopenharmony_ci	 * proper data bit to what is requested and then set OE bit. This
738c2ecf20Sopenharmony_ci	 * prevents a glitch that can occur on the IO line
748c2ecf20Sopenharmony_ci	 */
758c2ecf20Sopenharmony_ci	regmap_read(priv->regmap, offset, &reg);
768c2ecf20Sopenharmony_ci	if (!(reg & TS4900_GPIO_OE)) {
778c2ecf20Sopenharmony_ci		if (value)
788c2ecf20Sopenharmony_ci			reg = TS4900_GPIO_OUT;
798c2ecf20Sopenharmony_ci		else
808c2ecf20Sopenharmony_ci			reg &= ~TS4900_GPIO_OUT;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci		regmap_write(priv->regmap, offset, reg);
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	if (value)
868c2ecf20Sopenharmony_ci		ret = regmap_write(priv->regmap, offset, TS4900_GPIO_OE |
878c2ecf20Sopenharmony_ci							 TS4900_GPIO_OUT);
888c2ecf20Sopenharmony_ci	else
898c2ecf20Sopenharmony_ci		ret = regmap_write(priv->regmap, offset, TS4900_GPIO_OE);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return ret;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic int ts4900_gpio_get(struct gpio_chip *chip, unsigned int offset)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	struct ts4900_gpio_priv *priv = gpiochip_get_data(chip);
978c2ecf20Sopenharmony_ci	unsigned int reg;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	regmap_read(priv->regmap, offset, &reg);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return !!(reg & priv->input_bit);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic void ts4900_gpio_set(struct gpio_chip *chip, unsigned int offset,
1058c2ecf20Sopenharmony_ci			    int value)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	struct ts4900_gpio_priv *priv = gpiochip_get_data(chip);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (value)
1108c2ecf20Sopenharmony_ci		regmap_update_bits(priv->regmap, offset, TS4900_GPIO_OUT,
1118c2ecf20Sopenharmony_ci				   TS4900_GPIO_OUT);
1128c2ecf20Sopenharmony_ci	else
1138c2ecf20Sopenharmony_ci		regmap_update_bits(priv->regmap, offset, TS4900_GPIO_OUT, 0);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic const struct regmap_config ts4900_regmap_config = {
1178c2ecf20Sopenharmony_ci	.reg_bits = 16,
1188c2ecf20Sopenharmony_ci	.val_bits = 8,
1198c2ecf20Sopenharmony_ci};
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic const struct gpio_chip template_chip = {
1228c2ecf20Sopenharmony_ci	.label			= "ts4900-gpio",
1238c2ecf20Sopenharmony_ci	.owner			= THIS_MODULE,
1248c2ecf20Sopenharmony_ci	.get_direction		= ts4900_gpio_get_direction,
1258c2ecf20Sopenharmony_ci	.direction_input	= ts4900_gpio_direction_input,
1268c2ecf20Sopenharmony_ci	.direction_output	= ts4900_gpio_direction_output,
1278c2ecf20Sopenharmony_ci	.get			= ts4900_gpio_get,
1288c2ecf20Sopenharmony_ci	.set			= ts4900_gpio_set,
1298c2ecf20Sopenharmony_ci	.base			= -1,
1308c2ecf20Sopenharmony_ci	.can_sleep		= true,
1318c2ecf20Sopenharmony_ci};
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic const struct of_device_id ts4900_gpio_of_match_table[] = {
1348c2ecf20Sopenharmony_ci	{
1358c2ecf20Sopenharmony_ci		.compatible = "technologic,ts4900-gpio",
1368c2ecf20Sopenharmony_ci		.data = (void *)TS4900_GPIO_IN,
1378c2ecf20Sopenharmony_ci	}, {
1388c2ecf20Sopenharmony_ci		.compatible = "technologic,ts7970-gpio",
1398c2ecf20Sopenharmony_ci		.data = (void *)TS7970_GPIO_IN,
1408c2ecf20Sopenharmony_ci	},
1418c2ecf20Sopenharmony_ci	{ /* sentinel */ },
1428c2ecf20Sopenharmony_ci};
1438c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ts4900_gpio_of_match_table);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int ts4900_gpio_probe(struct i2c_client *client,
1468c2ecf20Sopenharmony_ci			const struct i2c_device_id *id)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct ts4900_gpio_priv *priv;
1498c2ecf20Sopenharmony_ci	u32 ngpio;
1508c2ecf20Sopenharmony_ci	int ret;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if (of_property_read_u32(client->dev.of_node, "ngpios", &ngpio))
1538c2ecf20Sopenharmony_ci		ngpio = DEFAULT_PIN_NUMBER;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
1568c2ecf20Sopenharmony_ci	if (!priv)
1578c2ecf20Sopenharmony_ci		return -ENOMEM;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	priv->gpio_chip = template_chip;
1608c2ecf20Sopenharmony_ci	priv->gpio_chip.label = "ts4900-gpio";
1618c2ecf20Sopenharmony_ci	priv->gpio_chip.ngpio = ngpio;
1628c2ecf20Sopenharmony_ci	priv->gpio_chip.parent = &client->dev;
1638c2ecf20Sopenharmony_ci	priv->input_bit = (uintptr_t)of_device_get_match_data(&client->dev);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	priv->regmap = devm_regmap_init_i2c(client, &ts4900_regmap_config);
1668c2ecf20Sopenharmony_ci	if (IS_ERR(priv->regmap)) {
1678c2ecf20Sopenharmony_ci		ret = PTR_ERR(priv->regmap);
1688c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Failed to allocate register map: %d\n",
1698c2ecf20Sopenharmony_ci			ret);
1708c2ecf20Sopenharmony_ci		return ret;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	ret = devm_gpiochip_add_data(&client->dev, &priv->gpio_chip, priv);
1748c2ecf20Sopenharmony_ci	if (ret < 0) {
1758c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Unable to register gpiochip\n");
1768c2ecf20Sopenharmony_ci		return ret;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, priv);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return 0;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic const struct i2c_device_id ts4900_gpio_id_table[] = {
1858c2ecf20Sopenharmony_ci	{ "ts4900-gpio", },
1868c2ecf20Sopenharmony_ci	{ /* sentinel */ }
1878c2ecf20Sopenharmony_ci};
1888c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ts4900_gpio_id_table);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic struct i2c_driver ts4900_gpio_driver = {
1918c2ecf20Sopenharmony_ci	.driver = {
1928c2ecf20Sopenharmony_ci		.name = "ts4900-gpio",
1938c2ecf20Sopenharmony_ci		.of_match_table = ts4900_gpio_of_match_table,
1948c2ecf20Sopenharmony_ci	},
1958c2ecf20Sopenharmony_ci	.probe = ts4900_gpio_probe,
1968c2ecf20Sopenharmony_ci	.id_table = ts4900_gpio_id_table,
1978c2ecf20Sopenharmony_ci};
1988c2ecf20Sopenharmony_cimodule_i2c_driver(ts4900_gpio_driver);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ciMODULE_AUTHOR("Technologic Systems");
2018c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GPIO interface for Technologic Systems I2C-FPGA core");
2028c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
203