18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * TI/National Semiconductor LP3943 GPIO driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2013 Texas Instruments
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Milo Kim <milo.kim@ti.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/bitops.h>
118c2ecf20Sopenharmony_ci#include <linux/err.h>
128c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
138c2ecf20Sopenharmony_ci#include <linux/i2c.h>
148c2ecf20Sopenharmony_ci#include <linux/mfd/lp3943.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cienum lp3943_gpios {
208c2ecf20Sopenharmony_ci	LP3943_GPIO1,
218c2ecf20Sopenharmony_ci	LP3943_GPIO2,
228c2ecf20Sopenharmony_ci	LP3943_GPIO3,
238c2ecf20Sopenharmony_ci	LP3943_GPIO4,
248c2ecf20Sopenharmony_ci	LP3943_GPIO5,
258c2ecf20Sopenharmony_ci	LP3943_GPIO6,
268c2ecf20Sopenharmony_ci	LP3943_GPIO7,
278c2ecf20Sopenharmony_ci	LP3943_GPIO8,
288c2ecf20Sopenharmony_ci	LP3943_GPIO9,
298c2ecf20Sopenharmony_ci	LP3943_GPIO10,
308c2ecf20Sopenharmony_ci	LP3943_GPIO11,
318c2ecf20Sopenharmony_ci	LP3943_GPIO12,
328c2ecf20Sopenharmony_ci	LP3943_GPIO13,
338c2ecf20Sopenharmony_ci	LP3943_GPIO14,
348c2ecf20Sopenharmony_ci	LP3943_GPIO15,
358c2ecf20Sopenharmony_ci	LP3943_GPIO16,
368c2ecf20Sopenharmony_ci	LP3943_MAX_GPIO,
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistruct lp3943_gpio {
408c2ecf20Sopenharmony_ci	struct gpio_chip chip;
418c2ecf20Sopenharmony_ci	struct lp3943 *lp3943;
428c2ecf20Sopenharmony_ci	u16 input_mask;		/* 1 = GPIO is input direction, 0 = output */
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic int lp3943_gpio_request(struct gpio_chip *chip, unsigned offset)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip);
488c2ecf20Sopenharmony_ci	struct lp3943 *lp3943 = lp3943_gpio->lp3943;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	/* Return an error if the pin is already assigned */
518c2ecf20Sopenharmony_ci	if (test_and_set_bit(offset, &lp3943->pin_used))
528c2ecf20Sopenharmony_ci		return -EBUSY;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	return 0;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic void lp3943_gpio_free(struct gpio_chip *chip, unsigned offset)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip);
608c2ecf20Sopenharmony_ci	struct lp3943 *lp3943 = lp3943_gpio->lp3943;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	clear_bit(offset, &lp3943->pin_used);
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int lp3943_gpio_set_mode(struct lp3943_gpio *lp3943_gpio, u8 offset,
668c2ecf20Sopenharmony_ci				u8 val)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct lp3943 *lp3943 = lp3943_gpio->lp3943;
698c2ecf20Sopenharmony_ci	const struct lp3943_reg_cfg *mux = lp3943->mux_cfg;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	return lp3943_update_bits(lp3943, mux[offset].reg, mux[offset].mask,
728c2ecf20Sopenharmony_ci				  val << mux[offset].shift);
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic int lp3943_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	lp3943_gpio->input_mask |= BIT(offset);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	return lp3943_gpio_set_mode(lp3943_gpio, offset, LP3943_GPIO_IN);
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int lp3943_get_gpio_in_status(struct lp3943_gpio *lp3943_gpio,
858c2ecf20Sopenharmony_ci				     struct gpio_chip *chip, unsigned offset)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	u8 addr, read;
888c2ecf20Sopenharmony_ci	int err;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	switch (offset) {
918c2ecf20Sopenharmony_ci	case LP3943_GPIO1 ... LP3943_GPIO8:
928c2ecf20Sopenharmony_ci		addr = LP3943_REG_GPIO_A;
938c2ecf20Sopenharmony_ci		break;
948c2ecf20Sopenharmony_ci	case LP3943_GPIO9 ... LP3943_GPIO16:
958c2ecf20Sopenharmony_ci		addr = LP3943_REG_GPIO_B;
968c2ecf20Sopenharmony_ci		offset = offset - 8;
978c2ecf20Sopenharmony_ci		break;
988c2ecf20Sopenharmony_ci	default:
998c2ecf20Sopenharmony_ci		return -EINVAL;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	err = lp3943_read_byte(lp3943_gpio->lp3943, addr, &read);
1038c2ecf20Sopenharmony_ci	if (err)
1048c2ecf20Sopenharmony_ci		return err;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return !!(read & BIT(offset));
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int lp3943_get_gpio_out_status(struct lp3943_gpio *lp3943_gpio,
1108c2ecf20Sopenharmony_ci				      struct gpio_chip *chip, unsigned offset)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	struct lp3943 *lp3943 = lp3943_gpio->lp3943;
1138c2ecf20Sopenharmony_ci	const struct lp3943_reg_cfg *mux = lp3943->mux_cfg;
1148c2ecf20Sopenharmony_ci	u8 read;
1158c2ecf20Sopenharmony_ci	int err;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	err = lp3943_read_byte(lp3943, mux[offset].reg, &read);
1188c2ecf20Sopenharmony_ci	if (err)
1198c2ecf20Sopenharmony_ci		return err;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	read = (read & mux[offset].mask) >> mux[offset].shift;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (read == LP3943_GPIO_OUT_HIGH)
1248c2ecf20Sopenharmony_ci		return 1;
1258c2ecf20Sopenharmony_ci	else if (read == LP3943_GPIO_OUT_LOW)
1268c2ecf20Sopenharmony_ci		return 0;
1278c2ecf20Sopenharmony_ci	else
1288c2ecf20Sopenharmony_ci		return -EINVAL;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic int lp3943_gpio_get(struct gpio_chip *chip, unsigned offset)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	/*
1368c2ecf20Sopenharmony_ci	 * Limitation:
1378c2ecf20Sopenharmony_ci	 *   LP3943 doesn't have the GPIO direction register. It provides
1388c2ecf20Sopenharmony_ci	 *   only input and output status registers.
1398c2ecf20Sopenharmony_ci	 *   So, direction info is required to handle the 'get' operation.
1408c2ecf20Sopenharmony_ci	 *   This variable is updated whenever the direction is changed and
1418c2ecf20Sopenharmony_ci	 *   it is used here.
1428c2ecf20Sopenharmony_ci	 */
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (lp3943_gpio->input_mask & BIT(offset))
1458c2ecf20Sopenharmony_ci		return lp3943_get_gpio_in_status(lp3943_gpio, chip, offset);
1468c2ecf20Sopenharmony_ci	else
1478c2ecf20Sopenharmony_ci		return lp3943_get_gpio_out_status(lp3943_gpio, chip, offset);
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic void lp3943_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip);
1538c2ecf20Sopenharmony_ci	u8 data;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (value)
1568c2ecf20Sopenharmony_ci		data = LP3943_GPIO_OUT_HIGH;
1578c2ecf20Sopenharmony_ci	else
1588c2ecf20Sopenharmony_ci		data = LP3943_GPIO_OUT_LOW;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	lp3943_gpio_set_mode(lp3943_gpio, offset, data);
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic int lp3943_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
1648c2ecf20Sopenharmony_ci					int value)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	lp3943_gpio_set(chip, offset, value);
1698c2ecf20Sopenharmony_ci	lp3943_gpio->input_mask &= ~BIT(offset);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	return 0;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic const struct gpio_chip lp3943_gpio_chip = {
1758c2ecf20Sopenharmony_ci	.label			= "lp3943",
1768c2ecf20Sopenharmony_ci	.owner			= THIS_MODULE,
1778c2ecf20Sopenharmony_ci	.request		= lp3943_gpio_request,
1788c2ecf20Sopenharmony_ci	.free			= lp3943_gpio_free,
1798c2ecf20Sopenharmony_ci	.direction_input	= lp3943_gpio_direction_input,
1808c2ecf20Sopenharmony_ci	.get			= lp3943_gpio_get,
1818c2ecf20Sopenharmony_ci	.direction_output	= lp3943_gpio_direction_output,
1828c2ecf20Sopenharmony_ci	.set			= lp3943_gpio_set,
1838c2ecf20Sopenharmony_ci	.base			= -1,
1848c2ecf20Sopenharmony_ci	.ngpio			= LP3943_MAX_GPIO,
1858c2ecf20Sopenharmony_ci	.can_sleep		= 1,
1868c2ecf20Sopenharmony_ci};
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic int lp3943_gpio_probe(struct platform_device *pdev)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct lp3943 *lp3943 = dev_get_drvdata(pdev->dev.parent);
1918c2ecf20Sopenharmony_ci	struct lp3943_gpio *lp3943_gpio;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	lp3943_gpio = devm_kzalloc(&pdev->dev, sizeof(*lp3943_gpio),
1948c2ecf20Sopenharmony_ci				GFP_KERNEL);
1958c2ecf20Sopenharmony_ci	if (!lp3943_gpio)
1968c2ecf20Sopenharmony_ci		return -ENOMEM;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	lp3943_gpio->lp3943 = lp3943;
1998c2ecf20Sopenharmony_ci	lp3943_gpio->chip = lp3943_gpio_chip;
2008c2ecf20Sopenharmony_ci	lp3943_gpio->chip.parent = &pdev->dev;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, lp3943_gpio);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	return devm_gpiochip_add_data(&pdev->dev, &lp3943_gpio->chip,
2058c2ecf20Sopenharmony_ci				      lp3943_gpio);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic const struct of_device_id lp3943_gpio_of_match[] = {
2098c2ecf20Sopenharmony_ci	{ .compatible = "ti,lp3943-gpio", },
2108c2ecf20Sopenharmony_ci	{ }
2118c2ecf20Sopenharmony_ci};
2128c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, lp3943_gpio_of_match);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic struct platform_driver lp3943_gpio_driver = {
2158c2ecf20Sopenharmony_ci	.probe = lp3943_gpio_probe,
2168c2ecf20Sopenharmony_ci	.driver = {
2178c2ecf20Sopenharmony_ci		.name = "lp3943-gpio",
2188c2ecf20Sopenharmony_ci		.of_match_table = lp3943_gpio_of_match,
2198c2ecf20Sopenharmony_ci	},
2208c2ecf20Sopenharmony_ci};
2218c2ecf20Sopenharmony_cimodule_platform_driver(lp3943_gpio_driver);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("LP3943 GPIO driver");
2248c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:lp3943-gpio");
2258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Milo Kim");
2268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
227