File Index
Files and directories
Sign in
FILE

gpio-tb10x.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpio/gpio-tb10x.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/* Abilis Systems MODULE DESCRIPTION
  3 *
  4 * Copyright (C) Abilis Systems 2013
  5 *
  6 * Authors: Sascha Leuenberger <sascha.leuenberger@abilis.com>
  7 *          Christian Ruppert <christian.ruppert@abilis.com>
  8 */
  9
 10#include <linux/bitops.h>
 11#include <linux/gpio/driver.h>
 12#include <linux/gpio/generic.h>
 13#include <linux/interrupt.h>
 14#include <linux/io.h>
 15#include <linux/irq.h>
 16#include <linux/irqdomain.h>
 17#include <linux/kernel.h>
 18#include <linux/module.h>
 19#include <linux/of.h>
 20#include <linux/of_platform.h>
 21#include <linux/pinctrl/consumer.h>
 22#include <linux/platform_device.h>
 23#include <linux/slab.h>
 24
 25#define TB10X_GPIO_DIR_IN	(0x00000000)
 26#define TB10X_GPIO_DIR_OUT	(0x00000001)
 27#define OFFSET_TO_REG_DDR	(0x00)
 28#define OFFSET_TO_REG_DATA	(0x04)
 29#define OFFSET_TO_REG_INT_EN	(0x08)
 30#define OFFSET_TO_REG_CHANGE	(0x0C)
 31#define OFFSET_TO_REG_WRMASK	(0x10)
 32#define OFFSET_TO_REG_INT_TYPE	(0x14)
 33
 34
 35/**
 36 * @base: register base address
 37 * @domain: IRQ domain of GPIO generated interrupts managed by this controller
 38 * @irq: Interrupt line of parent interrupt controller
 39 * @chip: Generic GPIO chip structure associated with this GPIO controller
 40 */
 41struct tb10x_gpio {
 42	void __iomem *base;
 43	struct irq_domain *domain;
 44	int irq;
 45	struct gpio_generic_chip chip;
 46};
 47
 48static inline u32 tb10x_reg_read(struct tb10x_gpio *gpio, unsigned int offs)
 49{
 50	return ioread32(gpio->base + offs);
 51}
 52
 53static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
 54{
 55	struct tb10x_gpio *tb10x_gpio = gpiochip_get_data(chip);
 56
 57	return irq_create_mapping(tb10x_gpio->domain, offset);
 58}
 59
 60static int tb10x_gpio_irq_set_type(struct irq_data *data, unsigned int type)
 61{
 62	if ((type & IRQF_TRIGGER_MASK) != IRQ_TYPE_EDGE_BOTH) {
 63		pr_err("Only (both) edge triggered interrupts supported.\n");
 64		return -EINVAL;
 65	}
 66
 67	irqd_set_trigger_type(data, type);
 68
 69	return IRQ_SET_MASK_OK;
 70}
 71
 72static irqreturn_t tb10x_gpio_irq_cascade(int irq, void *data)
 73{
 74	struct tb10x_gpio *tb10x_gpio = data;
 75	u32 r = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_CHANGE);
 76	u32 m = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_INT_EN);
 77	const unsigned long bits = r & m;
 78	int i;
 79
 80	for_each_set_bit(i, &bits, 32)
 81		generic_handle_domain_irq(tb10x_gpio->domain, i);
 82
 83	return IRQ_HANDLED;
 84}
 85
 86static int tb10x_gpio_probe(struct platform_device *pdev)
 87{
 88	struct gpio_generic_chip_config config;
 89	struct tb10x_gpio *tb10x_gpio;
 90	struct device *dev = &pdev->dev;
 91	struct device_node *np = dev->of_node;
 92	int ret = -EBUSY;
 93	u32 ngpio;
 94
 95	if (!np)
 96		return -EINVAL;
 97
 98	if (of_property_read_u32(np, "abilis,ngpio", &ngpio))
 99		return -EINVAL;
100
101	tb10x_gpio = devm_kzalloc(dev, sizeof(*tb10x_gpio), GFP_KERNEL);
102	if (tb10x_gpio == NULL)
103		return -ENOMEM;
104
105	tb10x_gpio->base = devm_platform_ioremap_resource(pdev, 0);
106	if (IS_ERR(tb10x_gpio->base))
107		return PTR_ERR(tb10x_gpio->base);
108
109	tb10x_gpio->chip.gc.label =
110		devm_kasprintf(dev, GFP_KERNEL, "%pOF", pdev->dev.of_node);
111	if (!tb10x_gpio->chip.gc.label)
112		return -ENOMEM;
113
114	/*
115	 * Initialize generic GPIO with one single register for reading and setting
116	 * the lines, no special set or clear registers and a data direction register
117	 * wher 1 means "output".
118	 */
119	config = (struct gpio_generic_chip_config) {
120		.dev = dev,
121		.sz = 4,
122		.dat = tb10x_gpio->base + OFFSET_TO_REG_DATA,
123		.dirout = tb10x_gpio->base + OFFSET_TO_REG_DDR,
124	};
125
126	ret = gpio_generic_chip_init(&tb10x_gpio->chip, &config);
127	if (ret) {
128		dev_err(dev, "unable to init generic GPIO\n");
129		return ret;
130	}
131	tb10x_gpio->chip.gc.base = -1;
132	tb10x_gpio->chip.gc.parent = dev;
133	tb10x_gpio->chip.gc.owner = THIS_MODULE;
134	/*
135	 * ngpio is set by gpio_generic_chip_init() but we override it, this
136	 * .request() callback also overrides the one set up by generic GPIO.
137	 */
138	tb10x_gpio->chip.gc.ngpio = ngpio;
139	tb10x_gpio->chip.gc.request = gpiochip_generic_request;
140	tb10x_gpio->chip.gc.free = gpiochip_generic_free;
141
142	ret = devm_gpiochip_add_data(dev, &tb10x_gpio->chip.gc, tb10x_gpio);
143	if (ret < 0) {
144		dev_err(dev, "Could not add gpiochip.\n");
145		return ret;
146	}
147
148	platform_set_drvdata(pdev, tb10x_gpio);
149
150	if (of_property_read_bool(np, "interrupt-controller")) {
151		struct irq_chip_generic *gc;
152
153		ret = platform_get_irq(pdev, 0);
154		if (ret < 0)
155			return ret;
156
157		tb10x_gpio->chip.gc.to_irq = tb10x_gpio_to_irq;
158		tb10x_gpio->irq		= ret;
159
160		ret = devm_request_irq(dev, ret, tb10x_gpio_irq_cascade,
161				IRQF_TRIGGER_NONE | IRQF_SHARED,
162				dev_name(dev), tb10x_gpio);
163		if (ret != 0)
164			return ret;
165
166		tb10x_gpio->domain = irq_domain_create_linear(dev_fwnode(dev),
167							      tb10x_gpio->chip.gc.ngpio,
168							      &irq_generic_chip_ops, NULL);
169		if (!tb10x_gpio->domain) {
170			return -ENOMEM;
171		}
172
173		ret = irq_alloc_domain_generic_chips(tb10x_gpio->domain,
174				tb10x_gpio->chip.gc.ngpio, 1, tb10x_gpio->chip.gc.label,
175				handle_edge_irq, IRQ_NOREQUEST, IRQ_NOPROBE,
176				IRQ_GC_INIT_MASK_CACHE);
177		if (ret)
178			goto err_remove_domain;
179
180		gc = tb10x_gpio->domain->gc->gc[0];
181		gc->reg_base                         = tb10x_gpio->base;
182		gc->chip_types[0].type               = IRQ_TYPE_EDGE_BOTH;
183		gc->chip_types[0].chip.irq_ack       = irq_gc_ack_set_bit;
184		gc->chip_types[0].chip.irq_mask      = irq_gc_mask_clr_bit;
185		gc->chip_types[0].chip.irq_unmask    = irq_gc_mask_set_bit;
186		gc->chip_types[0].chip.irq_set_type  = tb10x_gpio_irq_set_type;
187		gc->chip_types[0].regs.ack           = OFFSET_TO_REG_CHANGE;
188		gc->chip_types[0].regs.mask          = OFFSET_TO_REG_INT_EN;
189	}
190
191	return 0;
192
193err_remove_domain:
194	irq_domain_remove(tb10x_gpio->domain);
195	return ret;
196}
197
198static void tb10x_gpio_remove(struct platform_device *pdev)
199{
200	struct tb10x_gpio *tb10x_gpio = platform_get_drvdata(pdev);
201
202	if (tb10x_gpio->chip.gc.to_irq) {
203		irq_remove_generic_chip(tb10x_gpio->domain->gc->gc[0],
204					BIT(tb10x_gpio->chip.gc.ngpio) - 1, 0, 0);
205		kfree(tb10x_gpio->domain->gc);
206		irq_domain_remove(tb10x_gpio->domain);
207	}
208}
209
210static const struct of_device_id tb10x_gpio_dt_ids[] = {
211	{ .compatible = "abilis,tb10x-gpio" },
212	{ }
213};
214MODULE_DEVICE_TABLE(of, tb10x_gpio_dt_ids);
215
216static struct platform_driver tb10x_gpio_driver = {
217	.probe		= tb10x_gpio_probe,
218	.remove		= tb10x_gpio_remove,
219	.driver = {
220		.name	= "tb10x-gpio",
221		.of_match_table = tb10x_gpio_dt_ids,
222	}
223};
224
225module_platform_driver(tb10x_gpio_driver);
226MODULE_LICENSE("GPL");
227MODULE_DESCRIPTION("tb10x gpio.");
~/ Read-only file preview.