File Index
Files and directories
Sign in
FILE

gpio-cadence.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpio/gpio-cadence.c C
  1// SPDX-License-Identifier: GPL-2.0
  2
  3/*
  4 * Copyright 2017-2018 Cadence
  5 *
  6 * Authors:
  7 *  Jan Kotas <jank@cadence.com>
  8 *  Boris Brezillon <boris.brezillon@free-electrons.com>
  9 */
 10
 11#include <linux/cleanup.h>
 12#include <linux/clk.h>
 13#include <linux/gpio/driver.h>
 14#include <linux/interrupt.h>
 15#include <linux/gpio/generic.h>
 16#include <linux/kernel.h>
 17#include <linux/module.h>
 18#include <linux/platform_device.h>
 19#include <linux/spinlock.h>
 20
 21#define CDNS_GPIO_BYPASS_MODE		0x00
 22#define CDNS_GPIO_DIRECTION_MODE	0x04
 23#define CDNS_GPIO_OUTPUT_EN		0x08
 24#define CDNS_GPIO_OUTPUT_VALUE		0x0c
 25#define CDNS_GPIO_INPUT_VALUE		0x10
 26#define CDNS_GPIO_IRQ_MASK		0x14
 27#define CDNS_GPIO_IRQ_EN		0x18
 28#define CDNS_GPIO_IRQ_DIS		0x1c
 29#define CDNS_GPIO_IRQ_STATUS		0x20
 30#define CDNS_GPIO_IRQ_TYPE		0x24
 31#define CDNS_GPIO_IRQ_VALUE		0x28
 32#define CDNS_GPIO_IRQ_ANY_EDGE		0x2c
 33
 34struct cdns_gpio_chip {
 35	struct gpio_generic_chip gen_gc;
 36	void __iomem *regs;
 37	u32 bypass_orig;
 38};
 39
 40static int cdns_gpio_request(struct gpio_chip *chip, unsigned int offset)
 41{
 42	struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
 43
 44	guard(gpio_generic_lock)(&cgpio->gen_gc);
 45
 46	iowrite32(ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE) & ~BIT(offset),
 47		  cgpio->regs + CDNS_GPIO_BYPASS_MODE);
 48
 49	return 0;
 50}
 51
 52static void cdns_gpio_free(struct gpio_chip *chip, unsigned int offset)
 53{
 54	struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
 55
 56	guard(gpio_generic_lock)(&cgpio->gen_gc);
 57
 58	iowrite32(ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE) |
 59		  (BIT(offset) & cgpio->bypass_orig),
 60		  cgpio->regs + CDNS_GPIO_BYPASS_MODE);
 61}
 62
 63static void cdns_gpio_irq_mask(struct irq_data *d)
 64{
 65	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
 66	struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
 67
 68	iowrite32(BIT(d->hwirq), cgpio->regs + CDNS_GPIO_IRQ_DIS);
 69	gpiochip_disable_irq(chip, irqd_to_hwirq(d));
 70}
 71
 72static void cdns_gpio_irq_unmask(struct irq_data *d)
 73{
 74	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
 75	struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
 76
 77	gpiochip_enable_irq(chip, irqd_to_hwirq(d));
 78	iowrite32(BIT(d->hwirq), cgpio->regs + CDNS_GPIO_IRQ_EN);
 79}
 80
 81static int cdns_gpio_irq_set_type(struct irq_data *d, unsigned int type)
 82{
 83	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
 84	struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
 85	u32 int_value;
 86	u32 int_type;
 87	u32 mask = BIT(d->hwirq);
 88	int ret = 0;
 89
 90	guard(gpio_generic_lock)(&cgpio->gen_gc);
 91
 92	int_value = ioread32(cgpio->regs + CDNS_GPIO_IRQ_VALUE) & ~mask;
 93	int_type = ioread32(cgpio->regs + CDNS_GPIO_IRQ_TYPE) & ~mask;
 94
 95	/*
 96	 * The GPIO controller doesn't have an ACK register.
 97	 * All interrupt statuses are cleared on a status register read.
 98	 * Don't support edge interrupts for now.
 99	 */
100
101	if (type == IRQ_TYPE_LEVEL_HIGH) {
102		int_type |= mask;
103		int_value |= mask;
104	} else if (type == IRQ_TYPE_LEVEL_LOW) {
105		int_type |= mask;
106	} else {
107		return -EINVAL;
108	}
109
110	iowrite32(int_value, cgpio->regs + CDNS_GPIO_IRQ_VALUE);
111	iowrite32(int_type, cgpio->regs + CDNS_GPIO_IRQ_TYPE);
112
113	return ret;
114}
115
116static void cdns_gpio_irq_handler(struct irq_desc *desc)
117{
118	struct gpio_chip *chip = irq_desc_get_handler_data(desc);
119	struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
120	struct irq_chip *irqchip = irq_desc_get_chip(desc);
121	unsigned long status;
122	int hwirq;
123
124	chained_irq_enter(irqchip, desc);
125
126	status = ioread32(cgpio->regs + CDNS_GPIO_IRQ_STATUS) &
127		~ioread32(cgpio->regs + CDNS_GPIO_IRQ_MASK);
128
129	for_each_set_bit(hwirq, &status, chip->ngpio)
130		generic_handle_domain_irq(chip->irq.domain, hwirq);
131
132	chained_irq_exit(irqchip, desc);
133}
134
135static const struct irq_chip cdns_gpio_irqchip = {
136	.name		= "cdns-gpio",
137	.irq_mask	= cdns_gpio_irq_mask,
138	.irq_unmask	= cdns_gpio_irq_unmask,
139	.irq_set_type	= cdns_gpio_irq_set_type,
140	.flags		= IRQCHIP_IMMUTABLE,
141	GPIOCHIP_IRQ_RESOURCE_HELPERS,
142};
143
144static int cdns_gpio_probe(struct platform_device *pdev)
145{
146	struct gpio_generic_chip_config config = { };
147	struct cdns_gpio_chip *cgpio;
148	int ret, irq;
149	u32 dir_prev;
150	u32 num_gpios = 32;
151	struct clk *clk;
152
153	cgpio = devm_kzalloc(&pdev->dev, sizeof(*cgpio), GFP_KERNEL);
154	if (!cgpio)
155		return -ENOMEM;
156
157	cgpio->regs = devm_platform_ioremap_resource(pdev, 0);
158	if (IS_ERR(cgpio->regs))
159		return PTR_ERR(cgpio->regs);
160
161	of_property_read_u32(pdev->dev.of_node, "ngpios", &num_gpios);
162
163	if (num_gpios > 32) {
164		dev_err(&pdev->dev, "ngpios must be less or equal 32\n");
165		return -EINVAL;
166	}
167
168	/*
169	 * Set all pins as inputs by default, otherwise:
170	 * gpiochip_lock_as_irq:
171	 * tried to flag a GPIO set as output for IRQ
172	 * Generic GPIO driver stores the direction value internally,
173	 * so it needs to be changed before gpio_generic_chip_init() is called.
174	 */
175	dir_prev = ioread32(cgpio->regs + CDNS_GPIO_DIRECTION_MODE);
176	iowrite32(GENMASK(num_gpios - 1, 0),
177		  cgpio->regs + CDNS_GPIO_DIRECTION_MODE);
178
179	config.dev = &pdev->dev;
180	config.sz = 4;
181	config.dat = cgpio->regs + CDNS_GPIO_INPUT_VALUE;
182	config.set = cgpio->regs + CDNS_GPIO_OUTPUT_VALUE;
183	config.dirin = cgpio->regs + CDNS_GPIO_DIRECTION_MODE;
184	config.flags = GPIO_GENERIC_READ_OUTPUT_REG_SET;
185
186	ret = gpio_generic_chip_init(&cgpio->gen_gc, &config);
187	if (ret) {
188		dev_err(&pdev->dev, "Failed to register generic gpio, %d\n",
189			ret);
190		goto err_revert_dir;
191	}
192
193	cgpio->gen_gc.gc.label = dev_name(&pdev->dev);
194	cgpio->gen_gc.gc.ngpio = num_gpios;
195	cgpio->gen_gc.gc.parent = &pdev->dev;
196	cgpio->gen_gc.gc.base = -1;
197	cgpio->gen_gc.gc.owner = THIS_MODULE;
198	cgpio->gen_gc.gc.request = cdns_gpio_request;
199	cgpio->gen_gc.gc.free = cdns_gpio_free;
200
201	clk = devm_clk_get_enabled(&pdev->dev, NULL);
202	if (IS_ERR(clk)) {
203		ret = PTR_ERR(clk);
204		dev_err(&pdev->dev,
205			"Failed to retrieve peripheral clock, %d\n", ret);
206		goto err_revert_dir;
207	}
208
209	/*
210	 * Optional irq_chip support
211	 */
212	irq = platform_get_irq(pdev, 0);
213	if (irq >= 0) {
214		struct gpio_irq_chip *girq;
215
216		girq = &cgpio->gen_gc.gc.irq;
217		gpio_irq_chip_set_chip(girq, &cdns_gpio_irqchip);
218		girq->parent_handler = cdns_gpio_irq_handler;
219		girq->num_parents = 1;
220		girq->parents = devm_kcalloc(&pdev->dev, 1,
221					     sizeof(*girq->parents),
222					     GFP_KERNEL);
223		if (!girq->parents) {
224			ret = -ENOMEM;
225			goto err_revert_dir;
226		}
227		girq->parents[0] = irq;
228		girq->default_type = IRQ_TYPE_NONE;
229		girq->handler = handle_level_irq;
230	}
231
232	ret = devm_gpiochip_add_data(&pdev->dev, &cgpio->gen_gc.gc, cgpio);
233	if (ret < 0) {
234		dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
235		goto err_revert_dir;
236	}
237
238	cgpio->bypass_orig = ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE);
239
240	/*
241	 * Enable gpio outputs, ignored for input direction
242	 */
243	iowrite32(GENMASK(num_gpios - 1, 0),
244		  cgpio->regs + CDNS_GPIO_OUTPUT_EN);
245	iowrite32(0, cgpio->regs + CDNS_GPIO_BYPASS_MODE);
246
247	platform_set_drvdata(pdev, cgpio);
248	return 0;
249
250err_revert_dir:
251	iowrite32(dir_prev, cgpio->regs + CDNS_GPIO_DIRECTION_MODE);
252
253	return ret;
254}
255
256static void cdns_gpio_remove(struct platform_device *pdev)
257{
258	struct cdns_gpio_chip *cgpio = platform_get_drvdata(pdev);
259
260	iowrite32(cgpio->bypass_orig, cgpio->regs + CDNS_GPIO_BYPASS_MODE);
261}
262
263static const struct of_device_id cdns_of_ids[] = {
264	{ .compatible = "cdns,gpio-r1p02" },
265	{ /* sentinel */ },
266};
267MODULE_DEVICE_TABLE(of, cdns_of_ids);
268
269static struct platform_driver cdns_gpio_driver = {
270	.driver = {
271		.name = "cdns-gpio",
272		.of_match_table = cdns_of_ids,
273	},
274	.probe = cdns_gpio_probe,
275	.remove = cdns_gpio_remove,
276};
277module_platform_driver(cdns_gpio_driver);
278
279MODULE_AUTHOR("Jan Kotas <jank@cadence.com>");
280MODULE_DESCRIPTION("Cadence GPIO driver");
281MODULE_LICENSE("GPL v2");
282MODULE_ALIAS("platform:cdns-gpio");
~/ Read-only file preview.