xref: /openbmc/linux/drivers/gpio/gpio-cadence.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
172ab2f76SJan Kotas // SPDX-License-Identifier: GPL-2.0
272ab2f76SJan Kotas 
372ab2f76SJan Kotas /*
472ab2f76SJan Kotas  * Copyright 2017-2018 Cadence
572ab2f76SJan Kotas  *
672ab2f76SJan Kotas  * Authors:
772ab2f76SJan Kotas  *  Jan Kotas <jank@cadence.com>
872ab2f76SJan Kotas  *  Boris Brezillon <boris.brezillon@free-electrons.com>
972ab2f76SJan Kotas  */
1072ab2f76SJan Kotas 
1172ab2f76SJan Kotas #include <linux/gpio/driver.h>
1272ab2f76SJan Kotas #include <linux/clk.h>
1372ab2f76SJan Kotas #include <linux/interrupt.h>
1472ab2f76SJan Kotas #include <linux/kernel.h>
1572ab2f76SJan Kotas #include <linux/module.h>
1672ab2f76SJan Kotas #include <linux/platform_device.h>
1772ab2f76SJan Kotas #include <linux/spinlock.h>
1872ab2f76SJan Kotas 
1972ab2f76SJan Kotas #define CDNS_GPIO_BYPASS_MODE		0x00
2072ab2f76SJan Kotas #define CDNS_GPIO_DIRECTION_MODE	0x04
2172ab2f76SJan Kotas #define CDNS_GPIO_OUTPUT_EN		0x08
2272ab2f76SJan Kotas #define CDNS_GPIO_OUTPUT_VALUE		0x0c
2372ab2f76SJan Kotas #define CDNS_GPIO_INPUT_VALUE		0x10
2472ab2f76SJan Kotas #define CDNS_GPIO_IRQ_MASK		0x14
2572ab2f76SJan Kotas #define CDNS_GPIO_IRQ_EN		0x18
2672ab2f76SJan Kotas #define CDNS_GPIO_IRQ_DIS		0x1c
2772ab2f76SJan Kotas #define CDNS_GPIO_IRQ_STATUS		0x20
2872ab2f76SJan Kotas #define CDNS_GPIO_IRQ_TYPE		0x24
2972ab2f76SJan Kotas #define CDNS_GPIO_IRQ_VALUE		0x28
3072ab2f76SJan Kotas #define CDNS_GPIO_IRQ_ANY_EDGE		0x2c
3172ab2f76SJan Kotas 
3272ab2f76SJan Kotas struct cdns_gpio_chip {
3372ab2f76SJan Kotas 	struct gpio_chip gc;
3472ab2f76SJan Kotas 	struct clk *pclk;
3572ab2f76SJan Kotas 	void __iomem *regs;
3672ab2f76SJan Kotas 	u32 bypass_orig;
3772ab2f76SJan Kotas };
3872ab2f76SJan Kotas 
cdns_gpio_request(struct gpio_chip * chip,unsigned int offset)3972ab2f76SJan Kotas static int cdns_gpio_request(struct gpio_chip *chip, unsigned int offset)
4072ab2f76SJan Kotas {
4172ab2f76SJan Kotas 	struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
4272ab2f76SJan Kotas 	unsigned long flags;
4372ab2f76SJan Kotas 
443c938cc5SSchspa Shi 	raw_spin_lock_irqsave(&chip->bgpio_lock, flags);
4572ab2f76SJan Kotas 
4672ab2f76SJan Kotas 	iowrite32(ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE) & ~BIT(offset),
4772ab2f76SJan Kotas 		  cgpio->regs + CDNS_GPIO_BYPASS_MODE);
4872ab2f76SJan Kotas 
493c938cc5SSchspa Shi 	raw_spin_unlock_irqrestore(&chip->bgpio_lock, flags);
5072ab2f76SJan Kotas 	return 0;
5172ab2f76SJan Kotas }
5272ab2f76SJan Kotas 
cdns_gpio_free(struct gpio_chip * chip,unsigned int offset)5372ab2f76SJan Kotas static void cdns_gpio_free(struct gpio_chip *chip, unsigned int offset)
5472ab2f76SJan Kotas {
5572ab2f76SJan Kotas 	struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
5672ab2f76SJan Kotas 	unsigned long flags;
5772ab2f76SJan Kotas 
583c938cc5SSchspa Shi 	raw_spin_lock_irqsave(&chip->bgpio_lock, flags);
5972ab2f76SJan Kotas 
6072ab2f76SJan Kotas 	iowrite32(ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE) |
6172ab2f76SJan Kotas 		  (BIT(offset) & cgpio->bypass_orig),
6272ab2f76SJan Kotas 		  cgpio->regs + CDNS_GPIO_BYPASS_MODE);
6372ab2f76SJan Kotas 
643c938cc5SSchspa Shi 	raw_spin_unlock_irqrestore(&chip->bgpio_lock, flags);
6572ab2f76SJan Kotas }
6672ab2f76SJan Kotas 
cdns_gpio_irq_mask(struct irq_data * d)6772ab2f76SJan Kotas static void cdns_gpio_irq_mask(struct irq_data *d)
6872ab2f76SJan Kotas {
6972ab2f76SJan Kotas 	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
7072ab2f76SJan Kotas 	struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
7172ab2f76SJan Kotas 
7272ab2f76SJan Kotas 	iowrite32(BIT(d->hwirq), cgpio->regs + CDNS_GPIO_IRQ_DIS);
73*150a9880SLinus Walleij 	gpiochip_disable_irq(chip, irqd_to_hwirq(d));
7472ab2f76SJan Kotas }
7572ab2f76SJan Kotas 
cdns_gpio_irq_unmask(struct irq_data * d)7672ab2f76SJan Kotas static void cdns_gpio_irq_unmask(struct irq_data *d)
7772ab2f76SJan Kotas {
7872ab2f76SJan Kotas 	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
7972ab2f76SJan Kotas 	struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
8072ab2f76SJan Kotas 
81*150a9880SLinus Walleij 	gpiochip_enable_irq(chip, irqd_to_hwirq(d));
8272ab2f76SJan Kotas 	iowrite32(BIT(d->hwirq), cgpio->regs + CDNS_GPIO_IRQ_EN);
8372ab2f76SJan Kotas }
8472ab2f76SJan Kotas 
cdns_gpio_irq_set_type(struct irq_data * d,unsigned int type)8572ab2f76SJan Kotas static int cdns_gpio_irq_set_type(struct irq_data *d, unsigned int type)
8672ab2f76SJan Kotas {
8772ab2f76SJan Kotas 	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
8872ab2f76SJan Kotas 	struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
8972ab2f76SJan Kotas 	unsigned long flags;
9072ab2f76SJan Kotas 	u32 int_value;
9172ab2f76SJan Kotas 	u32 int_type;
9272ab2f76SJan Kotas 	u32 mask = BIT(d->hwirq);
9372ab2f76SJan Kotas 	int ret = 0;
9472ab2f76SJan Kotas 
953c938cc5SSchspa Shi 	raw_spin_lock_irqsave(&chip->bgpio_lock, flags);
9672ab2f76SJan Kotas 
9772ab2f76SJan Kotas 	int_value = ioread32(cgpio->regs + CDNS_GPIO_IRQ_VALUE) & ~mask;
9872ab2f76SJan Kotas 	int_type = ioread32(cgpio->regs + CDNS_GPIO_IRQ_TYPE) & ~mask;
9972ab2f76SJan Kotas 
10072ab2f76SJan Kotas 	/*
10172ab2f76SJan Kotas 	 * The GPIO controller doesn't have an ACK register.
10272ab2f76SJan Kotas 	 * All interrupt statuses are cleared on a status register read.
10372ab2f76SJan Kotas 	 * Don't support edge interrupts for now.
10472ab2f76SJan Kotas 	 */
10572ab2f76SJan Kotas 
10672ab2f76SJan Kotas 	if (type == IRQ_TYPE_LEVEL_HIGH) {
10772ab2f76SJan Kotas 		int_type |= mask;
10872ab2f76SJan Kotas 		int_value |= mask;
10972ab2f76SJan Kotas 	} else if (type == IRQ_TYPE_LEVEL_LOW) {
11072ab2f76SJan Kotas 		int_type |= mask;
11172ab2f76SJan Kotas 	} else {
11272ab2f76SJan Kotas 		ret = -EINVAL;
11372ab2f76SJan Kotas 		goto err_irq_type;
11472ab2f76SJan Kotas 	}
11572ab2f76SJan Kotas 
11672ab2f76SJan Kotas 	iowrite32(int_value, cgpio->regs + CDNS_GPIO_IRQ_VALUE);
11772ab2f76SJan Kotas 	iowrite32(int_type, cgpio->regs + CDNS_GPIO_IRQ_TYPE);
11872ab2f76SJan Kotas 
11972ab2f76SJan Kotas err_irq_type:
1203c938cc5SSchspa Shi 	raw_spin_unlock_irqrestore(&chip->bgpio_lock, flags);
12172ab2f76SJan Kotas 	return ret;
12272ab2f76SJan Kotas }
12372ab2f76SJan Kotas 
cdns_gpio_irq_handler(struct irq_desc * desc)12472ab2f76SJan Kotas static void cdns_gpio_irq_handler(struct irq_desc *desc)
12572ab2f76SJan Kotas {
12672ab2f76SJan Kotas 	struct gpio_chip *chip = irq_desc_get_handler_data(desc);
12772ab2f76SJan Kotas 	struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
12872ab2f76SJan Kotas 	struct irq_chip *irqchip = irq_desc_get_chip(desc);
12972ab2f76SJan Kotas 	unsigned long status;
13072ab2f76SJan Kotas 	int hwirq;
13172ab2f76SJan Kotas 
13272ab2f76SJan Kotas 	chained_irq_enter(irqchip, desc);
13372ab2f76SJan Kotas 
13472ab2f76SJan Kotas 	status = ioread32(cgpio->regs + CDNS_GPIO_IRQ_STATUS) &
13572ab2f76SJan Kotas 		~ioread32(cgpio->regs + CDNS_GPIO_IRQ_MASK);
13672ab2f76SJan Kotas 
13772ab2f76SJan Kotas 	for_each_set_bit(hwirq, &status, chip->ngpio)
138dbd1c54fSMarc Zyngier 		generic_handle_domain_irq(chip->irq.domain, hwirq);
13972ab2f76SJan Kotas 
14072ab2f76SJan Kotas 	chained_irq_exit(irqchip, desc);
14172ab2f76SJan Kotas }
14272ab2f76SJan Kotas 
143*150a9880SLinus Walleij static const struct irq_chip cdns_gpio_irqchip = {
14472ab2f76SJan Kotas 	.name		= "cdns-gpio",
14572ab2f76SJan Kotas 	.irq_mask	= cdns_gpio_irq_mask,
14672ab2f76SJan Kotas 	.irq_unmask	= cdns_gpio_irq_unmask,
147*150a9880SLinus Walleij 	.irq_set_type	= cdns_gpio_irq_set_type,
148*150a9880SLinus Walleij 	.flags		= IRQCHIP_IMMUTABLE,
149*150a9880SLinus Walleij 	GPIOCHIP_IRQ_RESOURCE_HELPERS,
15072ab2f76SJan Kotas };
15172ab2f76SJan Kotas 
cdns_gpio_probe(struct platform_device * pdev)15272ab2f76SJan Kotas static int cdns_gpio_probe(struct platform_device *pdev)
15372ab2f76SJan Kotas {
15472ab2f76SJan Kotas 	struct cdns_gpio_chip *cgpio;
15572ab2f76SJan Kotas 	int ret, irq;
15672ab2f76SJan Kotas 	u32 dir_prev;
15772ab2f76SJan Kotas 	u32 num_gpios = 32;
15872ab2f76SJan Kotas 
15972ab2f76SJan Kotas 	cgpio = devm_kzalloc(&pdev->dev, sizeof(*cgpio), GFP_KERNEL);
16072ab2f76SJan Kotas 	if (!cgpio)
16172ab2f76SJan Kotas 		return -ENOMEM;
16272ab2f76SJan Kotas 
163b2c09588SEnrico Weigelt, metux IT consult 	cgpio->regs = devm_platform_ioremap_resource(pdev, 0);
16472ab2f76SJan Kotas 	if (IS_ERR(cgpio->regs))
16572ab2f76SJan Kotas 		return PTR_ERR(cgpio->regs);
16672ab2f76SJan Kotas 
16772ab2f76SJan Kotas 	of_property_read_u32(pdev->dev.of_node, "ngpios", &num_gpios);
16872ab2f76SJan Kotas 
16972ab2f76SJan Kotas 	if (num_gpios > 32) {
17072ab2f76SJan Kotas 		dev_err(&pdev->dev, "ngpios must be less or equal 32\n");
17172ab2f76SJan Kotas 		return -EINVAL;
17272ab2f76SJan Kotas 	}
17372ab2f76SJan Kotas 
17472ab2f76SJan Kotas 	/*
17572ab2f76SJan Kotas 	 * Set all pins as inputs by default, otherwise:
17672ab2f76SJan Kotas 	 * gpiochip_lock_as_irq:
17772ab2f76SJan Kotas 	 * tried to flag a GPIO set as output for IRQ
17872ab2f76SJan Kotas 	 * Generic GPIO driver stores the direction value internally,
17972ab2f76SJan Kotas 	 * so it needs to be changed before bgpio_init() is called.
18072ab2f76SJan Kotas 	 */
18172ab2f76SJan Kotas 	dir_prev = ioread32(cgpio->regs + CDNS_GPIO_DIRECTION_MODE);
18272ab2f76SJan Kotas 	iowrite32(GENMASK(num_gpios - 1, 0),
18372ab2f76SJan Kotas 		  cgpio->regs + CDNS_GPIO_DIRECTION_MODE);
18472ab2f76SJan Kotas 
18572ab2f76SJan Kotas 	ret = bgpio_init(&cgpio->gc, &pdev->dev, 4,
18672ab2f76SJan Kotas 			 cgpio->regs + CDNS_GPIO_INPUT_VALUE,
18772ab2f76SJan Kotas 			 cgpio->regs + CDNS_GPIO_OUTPUT_VALUE,
18872ab2f76SJan Kotas 			 NULL,
18972ab2f76SJan Kotas 			 NULL,
19072ab2f76SJan Kotas 			 cgpio->regs + CDNS_GPIO_DIRECTION_MODE,
19172ab2f76SJan Kotas 			 BGPIOF_READ_OUTPUT_REG_SET);
19272ab2f76SJan Kotas 	if (ret) {
19372ab2f76SJan Kotas 		dev_err(&pdev->dev, "Failed to register generic gpio, %d\n",
19472ab2f76SJan Kotas 			ret);
19572ab2f76SJan Kotas 		goto err_revert_dir;
19672ab2f76SJan Kotas 	}
19772ab2f76SJan Kotas 
19872ab2f76SJan Kotas 	cgpio->gc.label = dev_name(&pdev->dev);
19972ab2f76SJan Kotas 	cgpio->gc.ngpio = num_gpios;
20072ab2f76SJan Kotas 	cgpio->gc.parent = &pdev->dev;
20172ab2f76SJan Kotas 	cgpio->gc.base = -1;
20272ab2f76SJan Kotas 	cgpio->gc.owner = THIS_MODULE;
20372ab2f76SJan Kotas 	cgpio->gc.request = cdns_gpio_request;
20472ab2f76SJan Kotas 	cgpio->gc.free = cdns_gpio_free;
20572ab2f76SJan Kotas 
20672ab2f76SJan Kotas 	cgpio->pclk = devm_clk_get(&pdev->dev, NULL);
20772ab2f76SJan Kotas 	if (IS_ERR(cgpio->pclk)) {
20872ab2f76SJan Kotas 		ret = PTR_ERR(cgpio->pclk);
20972ab2f76SJan Kotas 		dev_err(&pdev->dev,
21072ab2f76SJan Kotas 			"Failed to retrieve peripheral clock, %d\n", ret);
21172ab2f76SJan Kotas 		goto err_revert_dir;
21272ab2f76SJan Kotas 	}
21372ab2f76SJan Kotas 
21472ab2f76SJan Kotas 	ret = clk_prepare_enable(cgpio->pclk);
21572ab2f76SJan Kotas 	if (ret) {
21672ab2f76SJan Kotas 		dev_err(&pdev->dev,
21772ab2f76SJan Kotas 			"Failed to enable the peripheral clock, %d\n", ret);
21872ab2f76SJan Kotas 		goto err_revert_dir;
21972ab2f76SJan Kotas 	}
22072ab2f76SJan Kotas 
221607a0dcdSLinus Walleij 	/*
222607a0dcdSLinus Walleij 	 * Optional irq_chip support
223607a0dcdSLinus Walleij 	 */
224607a0dcdSLinus Walleij 	irq = platform_get_irq(pdev, 0);
225607a0dcdSLinus Walleij 	if (irq >= 0) {
226607a0dcdSLinus Walleij 		struct gpio_irq_chip *girq;
227607a0dcdSLinus Walleij 
228607a0dcdSLinus Walleij 		girq = &cgpio->gc.irq;
229*150a9880SLinus Walleij 		gpio_irq_chip_set_chip(girq, &cdns_gpio_irqchip);
230607a0dcdSLinus Walleij 		girq->parent_handler = cdns_gpio_irq_handler;
231607a0dcdSLinus Walleij 		girq->num_parents = 1;
232607a0dcdSLinus Walleij 		girq->parents = devm_kcalloc(&pdev->dev, 1,
233607a0dcdSLinus Walleij 					     sizeof(*girq->parents),
234607a0dcdSLinus Walleij 					     GFP_KERNEL);
235607a0dcdSLinus Walleij 		if (!girq->parents) {
236607a0dcdSLinus Walleij 			ret = -ENOMEM;
237607a0dcdSLinus Walleij 			goto err_disable_clk;
238607a0dcdSLinus Walleij 		}
239607a0dcdSLinus Walleij 		girq->parents[0] = irq;
240607a0dcdSLinus Walleij 		girq->default_type = IRQ_TYPE_NONE;
241607a0dcdSLinus Walleij 		girq->handler = handle_level_irq;
242607a0dcdSLinus Walleij 	}
243607a0dcdSLinus Walleij 
24472ab2f76SJan Kotas 	ret = devm_gpiochip_add_data(&pdev->dev, &cgpio->gc, cgpio);
24572ab2f76SJan Kotas 	if (ret < 0) {
24672ab2f76SJan Kotas 		dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
24772ab2f76SJan Kotas 		goto err_disable_clk;
24872ab2f76SJan Kotas 	}
24972ab2f76SJan Kotas 
25072ab2f76SJan Kotas 	cgpio->bypass_orig = ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE);
25172ab2f76SJan Kotas 
25272ab2f76SJan Kotas 	/*
25372ab2f76SJan Kotas 	 * Enable gpio outputs, ignored for input direction
25472ab2f76SJan Kotas 	 */
25572ab2f76SJan Kotas 	iowrite32(GENMASK(num_gpios - 1, 0),
25672ab2f76SJan Kotas 		  cgpio->regs + CDNS_GPIO_OUTPUT_EN);
25772ab2f76SJan Kotas 	iowrite32(0, cgpio->regs + CDNS_GPIO_BYPASS_MODE);
25872ab2f76SJan Kotas 
25972ab2f76SJan Kotas 	platform_set_drvdata(pdev, cgpio);
26072ab2f76SJan Kotas 	return 0;
26172ab2f76SJan Kotas 
26272ab2f76SJan Kotas err_disable_clk:
26372ab2f76SJan Kotas 	clk_disable_unprepare(cgpio->pclk);
26472ab2f76SJan Kotas 
26572ab2f76SJan Kotas err_revert_dir:
26672ab2f76SJan Kotas 	iowrite32(dir_prev, cgpio->regs + CDNS_GPIO_DIRECTION_MODE);
26772ab2f76SJan Kotas 
26872ab2f76SJan Kotas 	return ret;
26972ab2f76SJan Kotas }
27072ab2f76SJan Kotas 
cdns_gpio_remove(struct platform_device * pdev)27172ab2f76SJan Kotas static int cdns_gpio_remove(struct platform_device *pdev)
27272ab2f76SJan Kotas {
27372ab2f76SJan Kotas 	struct cdns_gpio_chip *cgpio = platform_get_drvdata(pdev);
27472ab2f76SJan Kotas 
27572ab2f76SJan Kotas 	iowrite32(cgpio->bypass_orig, cgpio->regs + CDNS_GPIO_BYPASS_MODE);
27672ab2f76SJan Kotas 	clk_disable_unprepare(cgpio->pclk);
27772ab2f76SJan Kotas 
27872ab2f76SJan Kotas 	return 0;
27972ab2f76SJan Kotas }
28072ab2f76SJan Kotas 
28172ab2f76SJan Kotas static const struct of_device_id cdns_of_ids[] = {
28272ab2f76SJan Kotas 	{ .compatible = "cdns,gpio-r1p02" },
28372ab2f76SJan Kotas 	{ /* sentinel */ },
28472ab2f76SJan Kotas };
2851e948b17SZou Wei MODULE_DEVICE_TABLE(of, cdns_of_ids);
28672ab2f76SJan Kotas 
28772ab2f76SJan Kotas static struct platform_driver cdns_gpio_driver = {
28872ab2f76SJan Kotas 	.driver = {
28972ab2f76SJan Kotas 		.name = "cdns-gpio",
29072ab2f76SJan Kotas 		.of_match_table = cdns_of_ids,
29172ab2f76SJan Kotas 	},
29272ab2f76SJan Kotas 	.probe = cdns_gpio_probe,
29372ab2f76SJan Kotas 	.remove = cdns_gpio_remove,
29472ab2f76SJan Kotas };
29572ab2f76SJan Kotas module_platform_driver(cdns_gpio_driver);
29672ab2f76SJan Kotas 
29772ab2f76SJan Kotas MODULE_AUTHOR("Jan Kotas <jank@cadence.com>");
29872ab2f76SJan Kotas MODULE_DESCRIPTION("Cadence GPIO driver");
29972ab2f76SJan Kotas MODULE_LICENSE("GPL v2");
30072ab2f76SJan Kotas MODULE_ALIAS("platform:cdns-gpio");
301