// SPDX-License-Identifier: GPL-2.0-only /* * RDA Micro GPIO driver * * Copyright (C) 2012 RDA Micro Inc. * Copyright (C) 2019 Manivannan Sadhasivam */ #include #include #include #include #include #include #define RDA_GPIO_OEN_VAL 0x00 #define RDA_GPIO_OEN_SET_OUT 0x04 #define RDA_GPIO_OEN_SET_IN 0x08 #define RDA_GPIO_VAL 0x0c #define RDA_GPIO_SET 0x10 #define RDA_GPIO_CLR 0x14 #define RDA_GPIO_INT_CTRL_SET 0x18 #define RDA_GPIO_INT_CTRL_CLR 0x1c #define RDA_GPIO_INT_CLR 0x20 #define RDA_GPIO_INT_STATUS 0x24 #define RDA_GPIO_IRQ_RISE_SHIFT 0 #define RDA_GPIO_IRQ_FALL_SHIFT 8 #define RDA_GPIO_DEBOUCE_SHIFT 16 #define RDA_GPIO_LEVEL_SHIFT 24 #define RDA_GPIO_IRQ_MASK 0xff /* Each bank consists of 32 GPIOs */ #define RDA_GPIO_BANK_NR 32 struct rda_gpio { struct gpio_chip chip; void __iomem *base; spinlock_t lock; int irq; }; static inline void rda_gpio_update(struct gpio_chip *chip, unsigned int offset, u16 reg, int val) { struct rda_gpio *rda_gpio = gpiochip_get_data(chip); void __iomem *base = rda_gpio->base; unsigned long flags; u32 tmp; spin_lock_irqsave(&rda_gpio->lock, flags); tmp = readl_relaxed(base + reg); if (val) tmp |= BIT(offset); else tmp &= ~BIT(offset); writel_relaxed(tmp, base + reg); spin_unlock_irqrestore(&rda_gpio->lock, flags); } static void rda_gpio_irq_mask(struct irq_data *data) { struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct rda_gpio *rda_gpio = gpiochip_get_data(chip); void __iomem *base = rda_gpio->base; u32 offset = irqd_to_hwirq(data); u32 value; value = BIT(offset) << RDA_GPIO_IRQ_RISE_SHIFT; value |= BIT(offset) << RDA_GPIO_IRQ_FALL_SHIFT; writel_relaxed(value, base + RDA_GPIO_INT_CTRL_CLR); gpiochip_disable_irq(chip, offset); } static void rda_gpio_irq_ack(struct irq_data *data) { struct gpio_chip *chip = irq_data_get_irq_chip_data(data); u32 offset = irqd_to_hwirq(data); rda_gpio_update(chip, offset, RDA_GPIO_INT_CLR, 1); } static int rda_gpio_set_irq(struct gpio_chip *chip, u32 offset, unsigned int flow_type) { struct rda_gpio *rda_gpio = gpiochip_get_data(chip); void __iomem *base = rda_gpio->base; u32 value; switch (flow_type) { case IRQ_TYPE_EDGE_RISING: /* Set rising edge trigger */ value = BIT(offset) << RDA_GPIO_IRQ_RISE_SHIFT; writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET); /* Switch to edge trigger interrupt */ value = BIT(offset) << RDA_GPIO_LEVEL_SHIFT; writel_relaxed(value, base + RDA_GPIO_INT_CTRL_CLR); break; case IRQ_TYPE_EDGE_FALLING: /* Set falling edge trigger */ value = BIT(offset) << RDA_GPIO_IRQ_FALL_SHIFT; writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET); /* Switch to edge trigger interrupt */ value = BIT(offset) << RDA_GPIO_LEVEL_SHIFT; writel_relaxed(value, base + RDA_GPIO_INT_CTRL_CLR); break; case IRQ_TYPE_EDGE_BOTH: /* Set both edge trigger */ value = BIT(offset) << RDA_GPIO_IRQ_RISE_SHIFT; value |= BIT(offset) << RDA_GPIO_IRQ_FALL_SHIFT; writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET); /* Switch to edge trigger interrupt */ value = BIT(offset) << RDA_GPIO_LEVEL_SHIFT; writel_relaxed(value, base + RDA_GPIO_INT_CTRL_CLR); break; case IRQ_TYPE_LEVEL_HIGH: /* Set high level trigger */ value = BIT(offset) << RDA_GPIO_IRQ_RISE_SHIFT; /* Switch to level trigger interrupt */ value |= BIT(offset) << RDA_GPIO_LEVEL_SHIFT; writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET); break; case IRQ_TYPE_LEVEL_LOW: /* Set low level trigger */ value = BIT(offset) << RDA_GPIO_IRQ_FALL_SHIFT; /* Switch to level trigger interrupt */ value |= BIT(offset) << RDA_GPIO_LEVEL_SHIFT; writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET); break; default: return -EINVAL; } return 0; } static void rda_gpio_irq_unmask(struct irq_data *data) { struct gpio_chip *chip = irq_data_get_irq_chip_data(data); u32 offset = irqd_to_hwirq(data); u32 trigger = irqd_get_trigger_type(data); gpiochip_enable_irq(chip, offset); rda_gpio_set_irq(chip, offset, trigger); } static int rda_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) { struct gpio_chip *chip = irq_data_get_irq_chip_data(data); u32 offset = irqd_to_hwirq(data); int ret; ret = rda_gpio_set_irq(chip, offset, flow_type); if (ret) return ret; if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) irq_set_handler_locked(data, handle_level_irq); else if (flow_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) irq_set_handler_locked(data, handle_edge_irq); return 0; } static void rda_gpio_irq_handler(struct irq_desc *desc) { struct gpio_chip *chip = irq_desc_get_handler_data(desc); struct irq_chip *ic = irq_desc_get_chip(desc); struct rda_gpio *rda_gpio = gpiochip_get_data(chip); unsigned long status; u32 n; chained_irq_enter(ic, desc); status = readl_relaxed(rda_gpio->base + RDA_GPIO_INT_STATUS); /* Only lower 8 bits are capable of generating interrupts */ status &= RDA_GPIO_IRQ_MASK; for_each_set_bit(n, &status, RDA_GPIO_BANK_NR) generic_handle_domain_irq(chip->irq.domain, n); chained_irq_exit(ic, desc); } static const struct irq_chip rda_gpio_irq_chip = { .name = "rda-gpio", .irq_ack = rda_gpio_irq_ack, .irq_mask = rda_gpio_irq_mask, .irq_unmask = rda_gpio_irq_unmask, .irq_set_type = rda_gpio_irq_set_type, .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_IMMUTABLE, GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static int rda_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct gpio_irq_chip *girq; struct rda_gpio *rda_gpio; u32 ngpios; int ret; rda_gpio = devm_kzalloc(dev, sizeof(*rda_gpio), GFP_KERNEL); if (!rda_gpio) return -ENOMEM; ret = device_property_read_u32(dev, "ngpios", &ngpios); if (ret < 0) return ret; /* * Not all ports have interrupt capability. For instance, on * RDA8810PL, GPIOC doesn't support interrupt. So we must handle * those also. */ rda_gpio->irq = platform_get_irq(pdev, 0); rda_gpio->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(rda_gpio->base)) return PTR_ERR(rda_gpio->base); spin_lock_init(&rda_gpio->lock); ret = bgpio_init(&rda_gpio->chip, dev, 4, rda_gpio->base + RDA_GPIO_VAL, rda_gpio->base + RDA_GPIO_SET, rda_gpio->base + RDA_GPIO_CLR, rda_gpio->base + RDA_GPIO_OEN_SET_OUT, rda_gpio->base + RDA_GPIO_OEN_SET_IN, BGPIOF_READ_OUTPUT_REG_SET); if (ret) { dev_err(dev, "bgpio_init failed\n"); return ret; } rda_gpio->chip.label = dev_name(dev); rda_gpio->chip.ngpio = ngpios; rda_gpio->chip.base = -1; if (rda_gpio->irq >= 0) { girq = &rda_gpio->chip.irq; gpio_irq_chip_set_chip(girq, &rda_gpio_irq_chip); girq->handler = handle_bad_irq; girq->default_type = IRQ_TYPE_NONE; girq->parent_handler = rda_gpio_irq_handler; girq->parent_handler_data = rda_gpio; girq->num_parents = 1; girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents), GFP_KERNEL); if (!girq->parents) return -ENOMEM; girq->parents[0] = rda_gpio->irq; } platform_set_drvdata(pdev, rda_gpio); return devm_gpiochip_add_data(dev, &rda_gpio->chip, rda_gpio); } static const struct of_device_id rda_gpio_of_match[] = { { .compatible = "rda,8810pl-gpio", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rda_gpio_of_match); static struct platform_driver rda_gpio_driver = { .probe = rda_gpio_probe, .driver = { .name = "rda-gpio", .of_match_table = rda_gpio_of_match, }, }; module_platform_driver_probe(rda_gpio_driver, rda_gpio_probe); MODULE_DESCRIPTION("RDA Micro GPIO driver"); MODULE_AUTHOR("Manivannan Sadhasivam ");