18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * GPIO controller in LSI ZEVIO SoCs.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Fabian Vogt <fabian@ritter-vogt.de>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
98c2ecf20Sopenharmony_ci#include <linux/errno.h>
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/bitops.h>
128c2ecf20Sopenharmony_ci#include <linux/io.h>
138c2ecf20Sopenharmony_ci#include <linux/of_device.h>
148c2ecf20Sopenharmony_ci#include <linux/of_gpio.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/*
198c2ecf20Sopenharmony_ci * Memory layout:
208c2ecf20Sopenharmony_ci * This chip has four gpio sections, each controls 8 GPIOs.
218c2ecf20Sopenharmony_ci * Bit 0 in section 0 is GPIO 0, bit 2 in section 1 is GPIO 10.
228c2ecf20Sopenharmony_ci * Disclaimer: Reverse engineered!
238c2ecf20Sopenharmony_ci * For more information refer to:
248c2ecf20Sopenharmony_ci * http://hackspire.unsads.com/wiki/index.php/Memory-mapped_I/O_ports#90000000_-_General_Purpose_I.2FO_.28GPIO.29
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci * 0x00-0x3F: Section 0
278c2ecf20Sopenharmony_ci *     +0x00: Masked interrupt status (read-only)
288c2ecf20Sopenharmony_ci *     +0x04: R: Interrupt status W: Reset interrupt status
298c2ecf20Sopenharmony_ci *     +0x08: R: Interrupt mask W: Mask interrupt
308c2ecf20Sopenharmony_ci *     +0x0C: W: Unmask interrupt (write-only)
318c2ecf20Sopenharmony_ci *     +0x10: Direction: I/O=1/0
328c2ecf20Sopenharmony_ci *     +0x14: Output
338c2ecf20Sopenharmony_ci *     +0x18: Input (read-only)
348c2ecf20Sopenharmony_ci *     +0x20: R: Level interrupt W: Set as level interrupt
358c2ecf20Sopenharmony_ci * 0x40-0x7F: Section 1
368c2ecf20Sopenharmony_ci * 0x80-0xBF: Section 2
378c2ecf20Sopenharmony_ci * 0xC0-0xFF: Section 3
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define ZEVIO_GPIO_SECTION_SIZE			0x40
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* Offsets to various registers */
438c2ecf20Sopenharmony_ci#define ZEVIO_GPIO_INT_MASKED_STATUS	0x00
448c2ecf20Sopenharmony_ci#define ZEVIO_GPIO_INT_STATUS		0x04
458c2ecf20Sopenharmony_ci#define ZEVIO_GPIO_INT_UNMASK		0x08
468c2ecf20Sopenharmony_ci#define ZEVIO_GPIO_INT_MASK		0x0C
478c2ecf20Sopenharmony_ci#define ZEVIO_GPIO_DIRECTION		0x10
488c2ecf20Sopenharmony_ci#define ZEVIO_GPIO_OUTPUT		0x14
498c2ecf20Sopenharmony_ci#define ZEVIO_GPIO_INPUT			0x18
508c2ecf20Sopenharmony_ci#define ZEVIO_GPIO_INT_STICKY		0x20
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/* Bit number of GPIO in its section */
538c2ecf20Sopenharmony_ci#define ZEVIO_GPIO_BIT(gpio) (gpio&7)
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistruct zevio_gpio {
568c2ecf20Sopenharmony_ci	spinlock_t		lock;
578c2ecf20Sopenharmony_ci	struct of_mm_gpio_chip	chip;
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic inline u32 zevio_gpio_port_get(struct zevio_gpio *c, unsigned pin,
618c2ecf20Sopenharmony_ci					unsigned port_offset)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE;
648c2ecf20Sopenharmony_ci	return readl(IOMEM(c->chip.regs + section_offset + port_offset));
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic inline void zevio_gpio_port_set(struct zevio_gpio *c, unsigned pin,
688c2ecf20Sopenharmony_ci					unsigned port_offset, u32 val)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE;
718c2ecf20Sopenharmony_ci	writel(val, IOMEM(c->chip.regs + section_offset + port_offset));
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/* Functions for struct gpio_chip */
758c2ecf20Sopenharmony_cistatic int zevio_gpio_get(struct gpio_chip *chip, unsigned pin)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	struct zevio_gpio *controller = gpiochip_get_data(chip);
788c2ecf20Sopenharmony_ci	u32 val, dir;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	spin_lock(&controller->lock);
818c2ecf20Sopenharmony_ci	dir = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION);
828c2ecf20Sopenharmony_ci	if (dir & BIT(ZEVIO_GPIO_BIT(pin)))
838c2ecf20Sopenharmony_ci		val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_INPUT);
848c2ecf20Sopenharmony_ci	else
858c2ecf20Sopenharmony_ci		val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT);
868c2ecf20Sopenharmony_ci	spin_unlock(&controller->lock);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	return (val >> ZEVIO_GPIO_BIT(pin)) & 0x1;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic void zevio_gpio_set(struct gpio_chip *chip, unsigned pin, int value)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct zevio_gpio *controller = gpiochip_get_data(chip);
948c2ecf20Sopenharmony_ci	u32 val;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	spin_lock(&controller->lock);
978c2ecf20Sopenharmony_ci	val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT);
988c2ecf20Sopenharmony_ci	if (value)
998c2ecf20Sopenharmony_ci		val |= BIT(ZEVIO_GPIO_BIT(pin));
1008c2ecf20Sopenharmony_ci	else
1018c2ecf20Sopenharmony_ci		val &= ~BIT(ZEVIO_GPIO_BIT(pin));
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val);
1048c2ecf20Sopenharmony_ci	spin_unlock(&controller->lock);
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int zevio_gpio_direction_input(struct gpio_chip *chip, unsigned pin)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	struct zevio_gpio *controller = gpiochip_get_data(chip);
1108c2ecf20Sopenharmony_ci	u32 val;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	spin_lock(&controller->lock);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION);
1158c2ecf20Sopenharmony_ci	val |= BIT(ZEVIO_GPIO_BIT(pin));
1168c2ecf20Sopenharmony_ci	zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_DIRECTION, val);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	spin_unlock(&controller->lock);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return 0;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic int zevio_gpio_direction_output(struct gpio_chip *chip,
1248c2ecf20Sopenharmony_ci				       unsigned pin, int value)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct zevio_gpio *controller = gpiochip_get_data(chip);
1278c2ecf20Sopenharmony_ci	u32 val;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	spin_lock(&controller->lock);
1308c2ecf20Sopenharmony_ci	val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT);
1318c2ecf20Sopenharmony_ci	if (value)
1328c2ecf20Sopenharmony_ci		val |= BIT(ZEVIO_GPIO_BIT(pin));
1338c2ecf20Sopenharmony_ci	else
1348c2ecf20Sopenharmony_ci		val &= ~BIT(ZEVIO_GPIO_BIT(pin));
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val);
1378c2ecf20Sopenharmony_ci	val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION);
1388c2ecf20Sopenharmony_ci	val &= ~BIT(ZEVIO_GPIO_BIT(pin));
1398c2ecf20Sopenharmony_ci	zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_DIRECTION, val);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	spin_unlock(&controller->lock);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	return 0;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic int zevio_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	/*
1498c2ecf20Sopenharmony_ci	 * TODO: Implement IRQs.
1508c2ecf20Sopenharmony_ci	 * Not implemented yet due to weird lockups
1518c2ecf20Sopenharmony_ci	 */
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	return -ENXIO;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic const struct gpio_chip zevio_gpio_chip = {
1578c2ecf20Sopenharmony_ci	.direction_input	= zevio_gpio_direction_input,
1588c2ecf20Sopenharmony_ci	.direction_output	= zevio_gpio_direction_output,
1598c2ecf20Sopenharmony_ci	.set			= zevio_gpio_set,
1608c2ecf20Sopenharmony_ci	.get			= zevio_gpio_get,
1618c2ecf20Sopenharmony_ci	.to_irq			= zevio_gpio_to_irq,
1628c2ecf20Sopenharmony_ci	.base			= 0,
1638c2ecf20Sopenharmony_ci	.owner			= THIS_MODULE,
1648c2ecf20Sopenharmony_ci	.ngpio			= 32,
1658c2ecf20Sopenharmony_ci	.of_gpio_n_cells	= 2,
1668c2ecf20Sopenharmony_ci};
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci/* Initialization */
1698c2ecf20Sopenharmony_cistatic int zevio_gpio_probe(struct platform_device *pdev)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct zevio_gpio *controller;
1728c2ecf20Sopenharmony_ci	int status, i;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	controller = devm_kzalloc(&pdev->dev, sizeof(*controller), GFP_KERNEL);
1758c2ecf20Sopenharmony_ci	if (!controller)
1768c2ecf20Sopenharmony_ci		return -ENOMEM;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, controller);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/* Copy our reference */
1818c2ecf20Sopenharmony_ci	controller->chip.gc = zevio_gpio_chip;
1828c2ecf20Sopenharmony_ci	controller->chip.gc.parent = &pdev->dev;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	status = of_mm_gpiochip_add_data(pdev->dev.of_node,
1858c2ecf20Sopenharmony_ci					 &(controller->chip),
1868c2ecf20Sopenharmony_ci					 controller);
1878c2ecf20Sopenharmony_ci	if (status) {
1888c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to add gpiochip: %d\n", status);
1898c2ecf20Sopenharmony_ci		return status;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	spin_lock_init(&controller->lock);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	/* Disable interrupts, they only cause errors */
1958c2ecf20Sopenharmony_ci	for (i = 0; i < controller->chip.gc.ngpio; i += 8)
1968c2ecf20Sopenharmony_ci		zevio_gpio_port_set(controller, i, ZEVIO_GPIO_INT_MASK, 0xFF);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	dev_dbg(controller->chip.gc.parent, "ZEVIO GPIO controller set up!\n");
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	return 0;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic const struct of_device_id zevio_gpio_of_match[] = {
2048c2ecf20Sopenharmony_ci	{ .compatible = "lsi,zevio-gpio", },
2058c2ecf20Sopenharmony_ci	{ },
2068c2ecf20Sopenharmony_ci};
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic struct platform_driver zevio_gpio_driver = {
2098c2ecf20Sopenharmony_ci	.driver		= {
2108c2ecf20Sopenharmony_ci		.name	= "gpio-zevio",
2118c2ecf20Sopenharmony_ci		.of_match_table = zevio_gpio_of_match,
2128c2ecf20Sopenharmony_ci		.suppress_bind_attrs = true,
2138c2ecf20Sopenharmony_ci	},
2148c2ecf20Sopenharmony_ci	.probe		= zevio_gpio_probe,
2158c2ecf20Sopenharmony_ci};
2168c2ecf20Sopenharmony_cibuiltin_platform_driver(zevio_gpio_driver);
217