File Index
Files and directories
Sign in
FILE

gpio-loongson-64bit.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpio/gpio-loongson-64bit.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Loongson GPIO Support
  4 *
  5 * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
  6 */
  7
  8#include <linux/kernel.h>
  9#include <linux/init.h>
 10#include <linux/irq.h>
 11#include <linux/irqdesc.h>
 12#include <linux/module.h>
 13#include <linux/spinlock.h>
 14#include <linux/err.h>
 15#include <linux/gpio/driver.h>
 16#include <linux/gpio/generic.h>
 17#include <linux/platform_device.h>
 18#include <linux/bitops.h>
 19#include <linux/reset.h>
 20#include <asm/types.h>
 21
 22enum loongson_gpio_mode {
 23	BIT_CTRL_MODE,
 24	BYTE_CTRL_MODE,
 25};
 26
 27struct loongson_gpio_chip_data {
 28	const char		*label;
 29	enum loongson_gpio_mode	mode;
 30	unsigned int		conf_offset;
 31	unsigned int		out_offset;
 32	unsigned int		in_offset;
 33	unsigned int		inten_offset;
 34	unsigned int		intpol_offset;
 35	unsigned int		intedge_offset;
 36	unsigned int		intclr_offset;
 37	unsigned int		intsts_offset;
 38	unsigned int		intdual_offset;
 39	unsigned int		intr_num;
 40	irq_flow_handler_t	irq_handler;
 41	const struct irq_chip	*girqchip;
 42};
 43
 44struct loongson_gpio_chip {
 45	struct gpio_generic_chip chip;
 46	spinlock_t		lock;
 47	void __iomem		*reg_base;
 48	const struct loongson_gpio_chip_data *chip_data;
 49};
 50
 51static inline struct loongson_gpio_chip *to_loongson_gpio_chip(struct gpio_chip *chip)
 52{
 53	return container_of(to_gpio_generic_chip(chip),
 54			    struct loongson_gpio_chip, chip);
 55}
 56
 57static inline void loongson_commit_direction(struct loongson_gpio_chip *lgpio, unsigned int pin,
 58					     int input)
 59{
 60	u8 bval = input ? 1 : 0;
 61
 62	writeb(bval, lgpio->reg_base + lgpio->chip_data->conf_offset + pin);
 63}
 64
 65static void loongson_commit_level(struct loongson_gpio_chip *lgpio, unsigned int pin, int high)
 66{
 67	u8 bval = high ? 1 : 0;
 68
 69	writeb(bval, lgpio->reg_base + lgpio->chip_data->out_offset + pin);
 70}
 71
 72static int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned int pin)
 73{
 74	unsigned long flags;
 75	struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
 76
 77	spin_lock_irqsave(&lgpio->lock, flags);
 78	loongson_commit_direction(lgpio, pin, 1);
 79	spin_unlock_irqrestore(&lgpio->lock, flags);
 80
 81	return 0;
 82}
 83
 84static int loongson_gpio_direction_output(struct gpio_chip *chip, unsigned int pin, int value)
 85{
 86	unsigned long flags;
 87	struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
 88
 89	spin_lock_irqsave(&lgpio->lock, flags);
 90	loongson_commit_level(lgpio, pin, value);
 91	loongson_commit_direction(lgpio, pin, 0);
 92	spin_unlock_irqrestore(&lgpio->lock, flags);
 93
 94	return 0;
 95}
 96
 97static int loongson_gpio_get(struct gpio_chip *chip, unsigned int pin)
 98{
 99	u8  bval;
100	int val;
101	struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
102
103	bval = readb(lgpio->reg_base + lgpio->chip_data->in_offset + pin);
104	val = bval & 1;
105
106	return val;
107}
108
109static int loongson_gpio_get_direction(struct gpio_chip *chip, unsigned int pin)
110{
111	u8  bval;
112	struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
113
114	bval = readb(lgpio->reg_base + lgpio->chip_data->conf_offset + pin);
115	if (bval & 1)
116		return GPIO_LINE_DIRECTION_IN;
117
118	return GPIO_LINE_DIRECTION_OUT;
119}
120
121static int loongson_gpio_set(struct gpio_chip *chip, unsigned int pin, int value)
122{
123	unsigned long flags;
124	struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
125
126	spin_lock_irqsave(&lgpio->lock, flags);
127	loongson_commit_level(lgpio, pin, value);
128	spin_unlock_irqrestore(&lgpio->lock, flags);
129
130	return 0;
131}
132
133static int loongson_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
134{
135	unsigned int u;
136	struct platform_device *pdev = to_platform_device(chip->parent);
137	struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
138
139	if (lgpio->chip_data->mode == BIT_CTRL_MODE) {
140		/* Get the register index from offset then multiply by bytes per register */
141		u = readl(lgpio->reg_base + lgpio->chip_data->inten_offset + (offset / 32) * 4);
142		u |= BIT(offset % 32);
143		writel(u, lgpio->reg_base + lgpio->chip_data->inten_offset + (offset / 32) * 4);
144	} else {
145		writeb(1, lgpio->reg_base + lgpio->chip_data->inten_offset + offset);
146	}
147
148	return platform_get_irq(pdev, offset);
149}
150
151static void loongson_gpio_irq_ack(struct irq_data *data)
152{
153	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
154	struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
155	irq_hw_number_t hwirq = irqd_to_hwirq(data);
156
157	writeb(0x1, lgpio->reg_base + lgpio->chip_data->intclr_offset + hwirq);
158}
159
160static void loongson_gpio_irq_mask(struct irq_data *data)
161{
162	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
163	struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
164	irq_hw_number_t hwirq = irqd_to_hwirq(data);
165
166	writeb(0x0, lgpio->reg_base + lgpio->chip_data->inten_offset + hwirq);
167}
168
169static void loongson_gpio_irq_unmask(struct irq_data *data)
170{
171	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
172	struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
173	irq_hw_number_t hwirq = irqd_to_hwirq(data);
174
175	writeb(0x1, lgpio->reg_base + lgpio->chip_data->inten_offset + hwirq);
176}
177
178static int loongson_gpio_irq_set_type(struct irq_data *data, unsigned int type)
179{
180	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
181	struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
182	irq_hw_number_t hwirq = irqd_to_hwirq(data);
183	u8 pol = 0, edge = 0, dual = 0;
184
185	if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) {
186		edge = 1;
187		dual = 1;
188		irq_set_handler_locked(data, handle_edge_irq);
189	} else {
190		switch (type) {
191		case IRQ_TYPE_LEVEL_HIGH:
192			pol = 1;
193			fallthrough;
194		case IRQ_TYPE_LEVEL_LOW:
195			irq_set_handler_locked(data, handle_level_irq);
196			break;
197
198		case IRQ_TYPE_EDGE_RISING:
199			pol = 1;
200			fallthrough;
201		case IRQ_TYPE_EDGE_FALLING:
202			edge = 1;
203			irq_set_handler_locked(data, handle_edge_irq);
204			break;
205
206		default:
207			return -EINVAL;
208		}
209	}
210
211	writeb(pol, lgpio->reg_base + lgpio->chip_data->intpol_offset + hwirq);
212	writeb(edge, lgpio->reg_base + lgpio->chip_data->intedge_offset + hwirq);
213	writeb(dual, lgpio->reg_base + lgpio->chip_data->intdual_offset + hwirq);
214
215	return 0;
216}
217
218static void loongson_gpio_ls2k0300_irq_handler(struct irq_desc *desc)
219{
220	struct loongson_gpio_chip *lgpio = irq_desc_get_handler_data(desc);
221	struct irq_chip *girqchip = irq_desc_get_chip(desc);
222	int i;
223
224	chained_irq_enter(girqchip, desc);
225
226	for (i = 0; i < lgpio->chip.gc.ngpio; i++) {
227		/*
228		 * For the GPIO controller of LS2K0300, interrupts status bits
229		 * may be wrongly set even if the corresponding interrupt is
230		 * disabled. Thus interrupt enable bits are checked along with
231		 * status bits to detect interrupts reliably.
232		 */
233		if (readb(lgpio->reg_base + lgpio->chip_data->intsts_offset + i) &&
234		    readb(lgpio->reg_base + lgpio->chip_data->inten_offset + i))
235			generic_handle_domain_irq(lgpio->chip.gc.irq.domain, i);
236	}
237
238	chained_irq_exit(girqchip, desc);
239}
240
241static const struct irq_chip loongson_gpio_ls2k0300_irqchip = {
242	.irq_ack	= loongson_gpio_irq_ack,
243	.irq_mask	= loongson_gpio_irq_mask,
244	.irq_unmask	= loongson_gpio_irq_unmask,
245	.irq_set_type	= loongson_gpio_irq_set_type,
246	.flags		= IRQCHIP_IMMUTABLE | IRQCHIP_SKIP_SET_WAKE,
247	GPIOCHIP_IRQ_RESOURCE_HELPERS,
248};
249
250static int loongson_gpio_init_irqchip(struct platform_device *pdev,
251				      struct loongson_gpio_chip *lgpio)
252{
253	const struct loongson_gpio_chip_data *data = lgpio->chip_data;
254	struct gpio_chip *chip = &lgpio->chip.gc;
255	int i;
256
257	chip->irq.default_type = IRQ_TYPE_NONE;
258	chip->irq.handler = handle_bad_irq;
259	chip->irq.parent_handler = data->irq_handler;
260	chip->irq.parent_handler_data = lgpio;
261	gpio_irq_chip_set_chip(&chip->irq, data->girqchip);
262
263	chip->irq.num_parents = data->intr_num;
264	chip->irq.parents = devm_kcalloc(&pdev->dev, data->intr_num,
265					 sizeof(*chip->irq.parents), GFP_KERNEL);
266	if (!chip->irq.parents)
267		return -ENOMEM;
268
269	for (i = 0; i < data->intr_num; i++) {
270		int ret;
271
272		ret = platform_get_irq(pdev, i);
273		if (ret < 0)
274			return dev_err_probe(&pdev->dev, ret,
275					     "failed to get IRQ %d\n", i);
276		chip->irq.parents[i] = ret;
277	}
278
279	for (i = 0; i < data->intr_num; i++) {
280		writeb(0x0, lgpio->reg_base + data->inten_offset + i);
281		writeb(0x1, lgpio->reg_base + data->intclr_offset + i);
282	}
283
284	return 0;
285}
286
287static int loongson_gpio_init(struct platform_device *pdev, struct loongson_gpio_chip *lgpio,
288			      void __iomem *reg_base)
289{
290	struct gpio_generic_chip_config config;
291	int ret;
292
293	lgpio->reg_base = reg_base;
294	if (lgpio->chip_data->mode == BIT_CTRL_MODE) {
295		config = (struct gpio_generic_chip_config) {
296			.dev = &pdev->dev,
297			.sz = 8,
298			.dat = lgpio->reg_base + lgpio->chip_data->in_offset,
299			.set = lgpio->reg_base + lgpio->chip_data->out_offset,
300			.dirin = lgpio->reg_base + lgpio->chip_data->conf_offset,
301		};
302
303		ret = gpio_generic_chip_init(&lgpio->chip, &config);
304		if (ret) {
305			dev_err(&pdev->dev, "unable to init generic GPIO\n");
306			return ret;
307		}
308	} else {
309		lgpio->chip.gc.direction_input = loongson_gpio_direction_input;
310		lgpio->chip.gc.get = loongson_gpio_get;
311		lgpio->chip.gc.get_direction = loongson_gpio_get_direction;
312		lgpio->chip.gc.direction_output = loongson_gpio_direction_output;
313		lgpio->chip.gc.set = loongson_gpio_set;
314		lgpio->chip.gc.parent = &pdev->dev;
315		lgpio->chip.gc.base = -1;
316		spin_lock_init(&lgpio->lock);
317	}
318
319	lgpio->chip.gc.label = lgpio->chip_data->label;
320	lgpio->chip.gc.can_sleep = false;
321	if (lgpio->chip_data->girqchip) {
322		ret = loongson_gpio_init_irqchip(pdev, lgpio);
323		if (ret)
324			return dev_err_probe(&pdev->dev, ret, "failed to initialize irqchip\n");
325	} else if (lgpio->chip_data->inten_offset) {
326		lgpio->chip.gc.to_irq = loongson_gpio_to_irq;
327	}
328
329	return devm_gpiochip_add_data(&pdev->dev, &lgpio->chip.gc, lgpio);
330}
331
332static int loongson_gpio_probe(struct platform_device *pdev)
333{
334	void __iomem *reg_base;
335	struct loongson_gpio_chip *lgpio;
336	struct device *dev = &pdev->dev;
337	struct reset_control *rst;
338
339	lgpio = devm_kzalloc(dev, sizeof(*lgpio), GFP_KERNEL);
340	if (!lgpio)
341		return -ENOMEM;
342
343	lgpio->chip_data = device_get_match_data(dev);
344
345	reg_base = devm_platform_ioremap_resource(pdev, 0);
346	if (IS_ERR(reg_base))
347		return PTR_ERR(reg_base);
348
349	rst = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, NULL);
350	if (IS_ERR(rst))
351		return dev_err_probe(&pdev->dev, PTR_ERR(rst), "failed to get reset control\n");
352
353	return loongson_gpio_init(pdev, lgpio, reg_base);
354}
355
356static const struct loongson_gpio_chip_data loongson_gpio_ls2k_data = {
357	.label = "ls2k_gpio",
358	.mode = BIT_CTRL_MODE,
359	.conf_offset = 0x0,
360	.in_offset = 0x20,
361	.out_offset = 0x10,
362	.inten_offset = 0x30,
363};
364
365static const struct loongson_gpio_chip_data loongson_gpio_ls2k0300_data = {
366	.label = "ls2k0300_gpio",
367	.mode = BYTE_CTRL_MODE,
368	.conf_offset = 0x800,
369	.in_offset = 0xa00,
370	.out_offset = 0x900,
371	.inten_offset = 0xb00,
372	.intpol_offset = 0xc00,
373	.intedge_offset = 0xd00,
374	.intclr_offset = 0xe00,
375	.intsts_offset = 0xf00,
376	.intdual_offset = 0xf80,
377	.intr_num = 7,
378	.irq_handler = loongson_gpio_ls2k0300_irq_handler,
379	.girqchip = &loongson_gpio_ls2k0300_irqchip,
380};
381
382static const struct loongson_gpio_chip_data loongson_gpio_ls2k0500_data0 = {
383	.label = "ls2k0500_gpio",
384	.mode = BIT_CTRL_MODE,
385	.conf_offset = 0x0,
386	.in_offset = 0x8,
387	.out_offset = 0x10,
388	.inten_offset = 0xb0,
389};
390
391static const struct loongson_gpio_chip_data loongson_gpio_ls2k0500_data1 = {
392	.label = "ls2k0500_gpio",
393	.mode = BIT_CTRL_MODE,
394	.conf_offset = 0x0,
395	.in_offset = 0x8,
396	.out_offset = 0x10,
397	.inten_offset = 0x98,
398};
399
400static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data0 = {
401	.label = "ls2k2000_gpio",
402	.mode = BIT_CTRL_MODE,
403	.conf_offset = 0x0,
404	.in_offset = 0xc,
405	.out_offset = 0x8,
406	.inten_offset = 0x14,
407};
408
409static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data1 = {
410	.label = "ls2k2000_gpio",
411	.mode = BYTE_CTRL_MODE,
412	.conf_offset = 0x800,
413	.in_offset = 0xa00,
414	.out_offset = 0x900,
415	.inten_offset = 0xb00,
416};
417
418static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data2 = {
419	.label = "ls2k2000_gpio",
420	.mode = BIT_CTRL_MODE,
421	.conf_offset = 0x4,
422	.in_offset = 0x8,
423	.out_offset = 0x0,
424};
425
426static const struct loongson_gpio_chip_data loongson_gpio_ls3a5000_data = {
427	.label = "ls3a5000_gpio",
428	.mode = BIT_CTRL_MODE,
429	.conf_offset = 0x0,
430	.in_offset = 0xc,
431	.out_offset = 0x8,
432	.inten_offset = 0x14,
433};
434
435static const struct loongson_gpio_chip_data loongson_gpio_ls7a_data = {
436	.label = "ls7a_gpio",
437	.mode = BYTE_CTRL_MODE,
438	.conf_offset = 0x800,
439	.in_offset = 0xa00,
440	.out_offset = 0x900,
441	.inten_offset = 0xb00,
442};
443
444/* LS7A2000 chipset GPIO */
445static const struct loongson_gpio_chip_data loongson_gpio_ls7a2000_data0 = {
446	.label = "ls7a2000_gpio",
447	.mode = BYTE_CTRL_MODE,
448	.conf_offset = 0x800,
449	.in_offset = 0xa00,
450	.out_offset = 0x900,
451	.inten_offset = 0xb00,
452};
453
454/* LS7A2000 ACPI GPIO */
455static const struct loongson_gpio_chip_data loongson_gpio_ls7a2000_data1 = {
456	.label = "ls7a2000_gpio",
457	.mode = BIT_CTRL_MODE,
458	.conf_offset = 0x4,
459	.in_offset = 0x8,
460	.out_offset = 0x0,
461};
462
463/* Loongson-3A6000 node GPIO */
464static const struct loongson_gpio_chip_data loongson_gpio_ls3a6000_data = {
465	.label = "ls3a6000_gpio",
466	.mode = BIT_CTRL_MODE,
467	.conf_offset = 0x0,
468	.in_offset = 0xc,
469	.out_offset = 0x8,
470	.inten_offset = 0x14,
471};
472
473static const struct of_device_id loongson_gpio_of_match[] = {
474	{
475		.compatible = "loongson,ls2k-gpio",
476		.data = &loongson_gpio_ls2k_data,
477	},
478	{
479		.compatible = "loongson,ls2k0300-gpio",
480		.data = &loongson_gpio_ls2k0300_data,
481	},
482	{
483		.compatible = "loongson,ls2k0500-gpio0",
484		.data = &loongson_gpio_ls2k0500_data0,
485	},
486	{
487		.compatible = "loongson,ls2k0500-gpio1",
488		.data = &loongson_gpio_ls2k0500_data1,
489	},
490	{
491		.compatible = "loongson,ls2k2000-gpio0",
492		.data = &loongson_gpio_ls2k2000_data0,
493	},
494	{
495		.compatible = "loongson,ls2k2000-gpio1",
496		.data = &loongson_gpio_ls2k2000_data1,
497	},
498	{
499		.compatible = "loongson,ls2k2000-gpio2",
500		.data = &loongson_gpio_ls2k2000_data2,
501	},
502	{
503		.compatible = "loongson,ls3a5000-gpio",
504		.data = &loongson_gpio_ls3a5000_data,
505	},
506	{
507		.compatible = "loongson,ls7a-gpio",
508		.data = &loongson_gpio_ls7a_data,
509	},
510	{
511		.compatible = "loongson,ls7a2000-gpio1",
512		.data = &loongson_gpio_ls7a2000_data0,
513	},
514	{
515		.compatible = "loongson,ls7a2000-gpio2",
516		.data = &loongson_gpio_ls7a2000_data1,
517	},
518	{
519		.compatible = "loongson,ls3a6000-gpio",
520		.data = &loongson_gpio_ls3a6000_data,
521	},
522	{}
523};
524MODULE_DEVICE_TABLE(of, loongson_gpio_of_match);
525
526static const struct acpi_device_id loongson_gpio_acpi_match[] = {
527	{
528		.id = "LOON0002",
529		.driver_data = (kernel_ulong_t)&loongson_gpio_ls7a_data,
530	},
531	{
532		.id = "LOON0007",
533		.driver_data = (kernel_ulong_t)&loongson_gpio_ls3a5000_data,
534	},
535	{
536		.id = "LOON000A",
537		.driver_data = (kernel_ulong_t)&loongson_gpio_ls2k2000_data0,
538	},
539	{
540		.id = "LOON000B",
541		.driver_data = (kernel_ulong_t)&loongson_gpio_ls2k2000_data1,
542	},
543	{
544		.id = "LOON000C",
545		.driver_data = (kernel_ulong_t)&loongson_gpio_ls2k2000_data2,
546	},
547	{
548		.id = "LOON000D",
549		.driver_data = (kernel_ulong_t)&loongson_gpio_ls7a2000_data0,
550	},
551	{
552		.id = "LOON000E",
553		.driver_data = (kernel_ulong_t)&loongson_gpio_ls7a2000_data1,
554	},
555	{
556		.id = "LOON000F",
557		.driver_data = (kernel_ulong_t)&loongson_gpio_ls3a6000_data,
558	},
559	{}
560};
561MODULE_DEVICE_TABLE(acpi, loongson_gpio_acpi_match);
562
563static struct platform_driver loongson_gpio_driver = {
564	.driver = {
565		.name = "loongson-gpio",
566		.of_match_table = loongson_gpio_of_match,
567		.acpi_match_table = loongson_gpio_acpi_match,
568	},
569	.probe = loongson_gpio_probe,
570};
571
572static int __init loongson_gpio_setup(void)
573{
574	return platform_driver_register(&loongson_gpio_driver);
575}
576postcore_initcall(loongson_gpio_setup);
577
578MODULE_DESCRIPTION("Loongson gpio driver");
579MODULE_LICENSE("GPL");
~/ Read-only file preview.