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