18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
38c2ecf20Sopenharmony_ci *	Andrew F. Davis <afd@ti.com>
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
68c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License version 2 as
78c2ecf20Sopenharmony_ci * published by the Free Software Foundation.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any
108c2ecf20Sopenharmony_ci * kind, whether expressed or implied; without even the implied warranty
118c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
128c2ecf20Sopenharmony_ci * GNU General Public License version 2 for more details.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/bitmap.h>
168c2ecf20Sopenharmony_ci#include <linux/bitops.h>
178c2ecf20Sopenharmony_ci#include <linux/delay.h>
188c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
198c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
208c2ecf20Sopenharmony_ci#include <linux/module.h>
218c2ecf20Sopenharmony_ci#include <linux/mutex.h>
228c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define DEFAULT_NGPIO 8
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/**
278c2ecf20Sopenharmony_ci * struct pisosr_gpio - GPIO driver data
288c2ecf20Sopenharmony_ci * @chip: GPIO controller chip
298c2ecf20Sopenharmony_ci * @spi: SPI device pointer
308c2ecf20Sopenharmony_ci * @buffer: Buffer for device reads
318c2ecf20Sopenharmony_ci * @buffer_size: Size of buffer
328c2ecf20Sopenharmony_ci * @load_gpio: GPIO pin used to load input into device
338c2ecf20Sopenharmony_ci * @lock: Protects read sequences
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_cistruct pisosr_gpio {
368c2ecf20Sopenharmony_ci	struct gpio_chip chip;
378c2ecf20Sopenharmony_ci	struct spi_device *spi;
388c2ecf20Sopenharmony_ci	u8 *buffer;
398c2ecf20Sopenharmony_ci	size_t buffer_size;
408c2ecf20Sopenharmony_ci	struct gpio_desc *load_gpio;
418c2ecf20Sopenharmony_ci	struct mutex lock;
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic int pisosr_gpio_refresh(struct pisosr_gpio *gpio)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	int ret;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	mutex_lock(&gpio->lock);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (gpio->load_gpio) {
518c2ecf20Sopenharmony_ci		gpiod_set_value_cansleep(gpio->load_gpio, 1);
528c2ecf20Sopenharmony_ci		udelay(1); /* registers load time (~10ns) */
538c2ecf20Sopenharmony_ci		gpiod_set_value_cansleep(gpio->load_gpio, 0);
548c2ecf20Sopenharmony_ci		udelay(1); /* registers recovery time (~5ns) */
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	ret = spi_read(gpio->spi, gpio->buffer, gpio->buffer_size);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	mutex_unlock(&gpio->lock);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	return ret;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic int pisosr_gpio_get_direction(struct gpio_chip *chip,
658c2ecf20Sopenharmony_ci				     unsigned offset)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	/* This device always input */
688c2ecf20Sopenharmony_ci	return GPIO_LINE_DIRECTION_IN;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int pisosr_gpio_direction_input(struct gpio_chip *chip,
728c2ecf20Sopenharmony_ci				       unsigned offset)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	/* This device always input */
758c2ecf20Sopenharmony_ci	return 0;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic int pisosr_gpio_direction_output(struct gpio_chip *chip,
798c2ecf20Sopenharmony_ci					unsigned offset, int value)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	/* This device is input only */
828c2ecf20Sopenharmony_ci	return -EINVAL;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int pisosr_gpio_get(struct gpio_chip *chip, unsigned offset)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct pisosr_gpio *gpio = gpiochip_get_data(chip);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	/* Refresh may not always be needed */
908c2ecf20Sopenharmony_ci	pisosr_gpio_refresh(gpio);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	return (gpio->buffer[offset / 8] >> (offset % 8)) & 0x1;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic int pisosr_gpio_get_multiple(struct gpio_chip *chip,
968c2ecf20Sopenharmony_ci				    unsigned long *mask, unsigned long *bits)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct pisosr_gpio *gpio = gpiochip_get_data(chip);
998c2ecf20Sopenharmony_ci	unsigned long offset;
1008c2ecf20Sopenharmony_ci	unsigned long gpio_mask;
1018c2ecf20Sopenharmony_ci	unsigned long buffer_state;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	pisosr_gpio_refresh(gpio);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	bitmap_zero(bits, chip->ngpio);
1068c2ecf20Sopenharmony_ci	for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) {
1078c2ecf20Sopenharmony_ci		buffer_state = gpio->buffer[offset / 8] & gpio_mask;
1088c2ecf20Sopenharmony_ci		bitmap_set_value8(bits, buffer_state, offset);
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	return 0;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic const struct gpio_chip template_chip = {
1158c2ecf20Sopenharmony_ci	.label			= "pisosr-gpio",
1168c2ecf20Sopenharmony_ci	.owner			= THIS_MODULE,
1178c2ecf20Sopenharmony_ci	.get_direction		= pisosr_gpio_get_direction,
1188c2ecf20Sopenharmony_ci	.direction_input	= pisosr_gpio_direction_input,
1198c2ecf20Sopenharmony_ci	.direction_output	= pisosr_gpio_direction_output,
1208c2ecf20Sopenharmony_ci	.get			= pisosr_gpio_get,
1218c2ecf20Sopenharmony_ci	.get_multiple		= pisosr_gpio_get_multiple,
1228c2ecf20Sopenharmony_ci	.base			= -1,
1238c2ecf20Sopenharmony_ci	.ngpio			= DEFAULT_NGPIO,
1248c2ecf20Sopenharmony_ci	.can_sleep		= true,
1258c2ecf20Sopenharmony_ci};
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic int pisosr_gpio_probe(struct spi_device *spi)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	struct device *dev = &spi->dev;
1308c2ecf20Sopenharmony_ci	struct pisosr_gpio *gpio;
1318c2ecf20Sopenharmony_ci	int ret;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL);
1348c2ecf20Sopenharmony_ci	if (!gpio)
1358c2ecf20Sopenharmony_ci		return -ENOMEM;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	spi_set_drvdata(spi, gpio);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	gpio->chip = template_chip;
1408c2ecf20Sopenharmony_ci	gpio->chip.parent = dev;
1418c2ecf20Sopenharmony_ci	of_property_read_u16(dev->of_node, "ngpios", &gpio->chip.ngpio);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	gpio->spi = spi;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	gpio->buffer_size = DIV_ROUND_UP(gpio->chip.ngpio, 8);
1468c2ecf20Sopenharmony_ci	gpio->buffer = devm_kzalloc(dev, gpio->buffer_size, GFP_KERNEL);
1478c2ecf20Sopenharmony_ci	if (!gpio->buffer)
1488c2ecf20Sopenharmony_ci		return -ENOMEM;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	gpio->load_gpio = devm_gpiod_get_optional(dev, "load", GPIOD_OUT_LOW);
1518c2ecf20Sopenharmony_ci	if (IS_ERR(gpio->load_gpio))
1528c2ecf20Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(gpio->load_gpio),
1538c2ecf20Sopenharmony_ci				     "Unable to allocate load GPIO\n");
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	mutex_init(&gpio->lock);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	ret = gpiochip_add_data(&gpio->chip, gpio);
1588c2ecf20Sopenharmony_ci	if (ret < 0) {
1598c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to register gpiochip\n");
1608c2ecf20Sopenharmony_ci		return ret;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return 0;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic int pisosr_gpio_remove(struct spi_device *spi)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	struct pisosr_gpio *gpio = spi_get_drvdata(spi);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	gpiochip_remove(&gpio->chip);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	mutex_destroy(&gpio->lock);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return 0;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic const struct spi_device_id pisosr_gpio_id_table[] = {
1788c2ecf20Sopenharmony_ci	{ "pisosr-gpio", },
1798c2ecf20Sopenharmony_ci	{ /* sentinel */ }
1808c2ecf20Sopenharmony_ci};
1818c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, pisosr_gpio_id_table);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic const struct of_device_id pisosr_gpio_of_match_table[] = {
1848c2ecf20Sopenharmony_ci	{ .compatible = "pisosr-gpio", },
1858c2ecf20Sopenharmony_ci	{ /* sentinel */ }
1868c2ecf20Sopenharmony_ci};
1878c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, pisosr_gpio_of_match_table);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic struct spi_driver pisosr_gpio_driver = {
1908c2ecf20Sopenharmony_ci	.driver = {
1918c2ecf20Sopenharmony_ci		.name = "pisosr-gpio",
1928c2ecf20Sopenharmony_ci		.of_match_table = pisosr_gpio_of_match_table,
1938c2ecf20Sopenharmony_ci	},
1948c2ecf20Sopenharmony_ci	.probe = pisosr_gpio_probe,
1958c2ecf20Sopenharmony_ci	.remove = pisosr_gpio_remove,
1968c2ecf20Sopenharmony_ci	.id_table = pisosr_gpio_id_table,
1978c2ecf20Sopenharmony_ci};
1988c2ecf20Sopenharmony_cimodule_spi_driver(pisosr_gpio_driver);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
2018c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SPI Compatible PISO Shift Register GPIO Driver");
2028c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
203