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