// SPDX-License-Identifier: GPL-2.0-or-later /* * Realtek DHC RTD1625 gpio driver * * Copyright (c) 2023-2026 Realtek Semiconductor Corp. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define RTD1625_GPIO_DIR BIT(0) #define RTD1625_GPIO_OUT BIT(2) #define RTD1625_GPIO_IN BIT(4) #define RTD1625_GPIO_EDGE_INT_DP BIT(6) #define RTD1625_GPIO_EDGE_INT_EN BIT(8) #define RTD1625_GPIO_LEVEL_INT_EN BIT(16) #define RTD1625_GPIO_LEVEL_INT_DP BIT(18) #define RTD1625_GPIO_DEBOUNCE GENMASK(30, 28) #define RTD1625_GPIO_DEBOUNCE_WREN BIT(31) #define RTD1625_GPIO_WREN(x) ((x) << 1) /* Write-enable masks for all GPIO configs and reserved hardware bits */ #define RTD1625_ISO_GPIO_WREN_ALL 0x8000aa8a #define RTD1625_ISOM_GPIO_WREN_ALL 0x800aaa8a #define RTD1625_GPIO_DEBOUNCE_1US 0 #define RTD1625_GPIO_DEBOUNCE_10US 1 #define RTD1625_GPIO_DEBOUNCE_100US 2 #define RTD1625_GPIO_DEBOUNCE_1MS 3 #define RTD1625_GPIO_DEBOUNCE_10MS 4 #define RTD1625_GPIO_DEBOUNCE_20MS 5 #define RTD1625_GPIO_DEBOUNCE_30MS 6 #define RTD1625_GPIO_DEBOUNCE_50MS 7 #define GPIO_CONTROL(gpio) ((gpio) * 4) static struct lock_class_key rtd1625_gpio_irq_lock_class; static struct lock_class_key rtd1625_gpio_irq_request_class; enum rtd1625_irq_index { RTD1625_IRQ_ASSERT, RTD1625_IRQ_DEASSERT, RTD1625_IRQ_LEVEL, RTD1625_MAX_IRQS }; /** * struct rtd1625_gpio_info - Specific GPIO register information * @num_gpios: The number of GPIOs * @irq_type_support: Supported IRQ types * @base_offset: Offset for GPIO controller register * @gpa_offset: Offset for GPIO assert interrupt status register * @gpda_offset: Offset for GPIO deassert interrupt status register * @level_offset: Offset of level interrupt status register * @write_en_all: Write-enable mask for all configurable bits */ struct rtd1625_gpio_info { unsigned int num_gpios; unsigned int irq_type_support; unsigned int base_offset; unsigned int gpa_offset; unsigned int gpda_offset; unsigned int level_offset; unsigned int write_en_all; }; struct rtd1625_gpio { struct gpio_regmap *gpio_reg; const struct rtd1625_gpio_info *info; struct regmap *regmap; unsigned int irqs[RTD1625_MAX_IRQS]; raw_spinlock_t lock; struct irq_domain *domain; unsigned int *save_regs; }; static unsigned int rtd1625_gpio_gpa_offset(struct rtd1625_gpio *data, unsigned int offset) { return data->info->gpa_offset + ((offset / 32) * 4); } static unsigned int rtd1625_gpio_gpda_offset(struct rtd1625_gpio *data, unsigned int offset) { return data->info->gpda_offset + ((offset / 32) * 4); } static unsigned int rtd1625_gpio_level_offset(struct rtd1625_gpio *data, unsigned int offset) { return data->info->level_offset + ((offset / 32) * 4); } static int rtd1625_reg_mask_xlate(struct gpio_regmap *gpio, enum gpio_regmap_operation op, unsigned int base, unsigned int offset, unsigned int *reg, unsigned int *mask) { /* Each GPIO has its own dedicated 32-bit register */ struct rtd1625_gpio *data = gpio_regmap_get_drvdata(gpio); int val = 0, ret = 0; *reg = base + offset * 4; switch (op) { case GPIO_REGMAP_SET_OP: *mask = RTD1625_GPIO_OUT; return 0; case GPIO_REGMAP_GET_OP: ret = regmap_read(data->regmap, *reg, &val); if (ret) return ret; if (val & RTD1625_GPIO_DIR) *mask = RTD1625_GPIO_OUT; else *mask = RTD1625_GPIO_IN; return 0; case GPIO_REGMAP_GET_DIR_OP: case GPIO_REGMAP_SET_DIR_OP: *mask = RTD1625_GPIO_DIR; return 0; default: return -ENOTSUPP; } } static int rtd1625_value_xlate(struct gpio_regmap *gpio, enum gpio_regmap_operation op, unsigned int base, unsigned int offset, unsigned int reg, unsigned int *mask, unsigned int *val) { switch (op) { case GPIO_REGMAP_SET_OP: *val |= RTD1625_GPIO_WREN(RTD1625_GPIO_OUT); *mask |= RTD1625_GPIO_WREN(RTD1625_GPIO_OUT); return 0; case GPIO_REGMAP_SET_DIR_OP: *val |= RTD1625_GPIO_WREN(RTD1625_GPIO_DIR); *mask |= RTD1625_GPIO_WREN(RTD1625_GPIO_DIR); return 0; default: return -ENOTSUPP; } } static int rtd1625_gpio_set_debounce(struct rtd1625_gpio *data, unsigned int offset, unsigned int debounce) { u8 deb_val; u32 val; switch (debounce) { case 1: deb_val = RTD1625_GPIO_DEBOUNCE_1US; break; case 10: deb_val = RTD1625_GPIO_DEBOUNCE_10US; break; case 100: deb_val = RTD1625_GPIO_DEBOUNCE_100US; break; case 1000: deb_val = RTD1625_GPIO_DEBOUNCE_1MS; break; case 10000: deb_val = RTD1625_GPIO_DEBOUNCE_10MS; break; case 20000: deb_val = RTD1625_GPIO_DEBOUNCE_20MS; break; case 30000: deb_val = RTD1625_GPIO_DEBOUNCE_30MS; break; case 50000: deb_val = RTD1625_GPIO_DEBOUNCE_50MS; break; default: return -ENOTSUPP; } val = FIELD_PREP(RTD1625_GPIO_DEBOUNCE, deb_val) | RTD1625_GPIO_DEBOUNCE_WREN; guard(raw_spinlock_irqsave)(&data->lock); return regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(offset), val); } static int rtd1625_gpio_set_config(struct gpio_regmap *gpio, struct gpio_chip *chip, unsigned int offset, unsigned long config) { struct rtd1625_gpio *data = gpio_regmap_get_drvdata(gpio); u32 debounce; if (pinconf_to_config_param(config) == PIN_CONFIG_INPUT_DEBOUNCE) { debounce = pinconf_to_config_argument(config); return rtd1625_gpio_set_debounce(data, offset, debounce); } return gpiochip_generic_config(chip, offset, config); } static void rtd1625_gpio_irq_handle(struct irq_desc *desc) { unsigned int (*get_reg_offset)(struct rtd1625_gpio *gpio, unsigned int offset); struct rtd1625_gpio *data = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); unsigned int irq = irq_desc_get_irq(desc); struct irq_domain *domain = data->domain; unsigned int reg_offset, i, j, val; irq_hw_number_t hwirq; unsigned long status; u32 irq_type; int ret; if (irq == data->irqs[RTD1625_IRQ_ASSERT]) get_reg_offset = &rtd1625_gpio_gpa_offset; else if (irq == data->irqs[RTD1625_IRQ_DEASSERT]) get_reg_offset = &rtd1625_gpio_gpda_offset; else if (irq == data->irqs[RTD1625_IRQ_LEVEL]) get_reg_offset = &rtd1625_gpio_level_offset; else return; chained_irq_enter(chip, desc); for (i = 0; i < data->info->num_gpios; i += 32) { reg_offset = get_reg_offset(data, i); ret = regmap_read(data->regmap, reg_offset, &val); if (ret) { pr_err_ratelimited("Failed to read IRQ status for GPIO %u: %d\n", i, ret); continue; } status = val; /* * Hardware quirk: The controller fires both "assert" and "de-assert" * interrupts simultaneously on any edge toggle. * We must pre-clear edge interrupts here. If we drop an unwanted * de-assert interrupt below, it will never reach the IRQ core * (generic_handle_domain_irq), meaning ->irq_ack() won't be called. * Failing to clear it here leads to an interrupt storm. */ if (irq != data->irqs[RTD1625_IRQ_LEVEL]) { ret = regmap_write(data->regmap, reg_offset, status); if (ret) pr_err_ratelimited("Failed to clear edge IRQ for GPIO %u: %d\n", i, ret); } for_each_set_bit(j, &status, 32) { hwirq = i + j; irq_type = irq_get_trigger_type(irq_find_mapping(domain, hwirq)); /* * Filter out the hardware-forced de-assert interrupt unless * the user explicitly requested IRQ_TYPE_EDGE_BOTH. */ if (irq == data->irqs[RTD1625_IRQ_DEASSERT] && irq_type != IRQ_TYPE_EDGE_BOTH) continue; generic_handle_domain_irq(domain, hwirq); } } chained_irq_exit(chip, desc); } static void rtd1625_gpio_ack_irq(struct irq_data *d) { struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d); irq_hw_number_t hwirq = irqd_to_hwirq(d); u32 irq_type = irqd_get_trigger_type(d); u32 bit_mask = BIT(hwirq % 32); int reg_offset; if (irq_type & IRQ_TYPE_LEVEL_MASK) { reg_offset = rtd1625_gpio_level_offset(data, hwirq); regmap_write(data->regmap, reg_offset, bit_mask); } } static void rtd1625_gpio_enable_edge_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq) { int gpda_reg_offset = rtd1625_gpio_gpda_offset(data, hwirq); int gpa_reg_offset = rtd1625_gpio_gpa_offset(data, hwirq); u32 clr_mask = BIT(hwirq % 32); u32 val; guard(raw_spinlock_irqsave)(&data->lock); regmap_write(data->regmap, gpa_reg_offset, clr_mask); regmap_write(data->regmap, gpda_reg_offset, clr_mask); val = RTD1625_GPIO_EDGE_INT_EN | RTD1625_GPIO_WREN(RTD1625_GPIO_EDGE_INT_EN); regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(hwirq), val); } static void rtd1625_gpio_disable_edge_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq) { u32 val; guard(raw_spinlock_irqsave)(&data->lock); val = RTD1625_GPIO_WREN(RTD1625_GPIO_EDGE_INT_EN); regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(hwirq), val); } static void rtd1625_gpio_enable_level_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq) { int level_reg_offset = rtd1625_gpio_level_offset(data, hwirq); u32 clr_mask = BIT(hwirq % 32); u32 val; guard(raw_spinlock_irqsave)(&data->lock); regmap_write(data->regmap, level_reg_offset, clr_mask); val = RTD1625_GPIO_LEVEL_INT_EN | RTD1625_GPIO_WREN(RTD1625_GPIO_LEVEL_INT_EN); regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(hwirq), val); } static void rtd1625_gpio_disable_level_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq) { u32 val; guard(raw_spinlock_irqsave)(&data->lock); val = RTD1625_GPIO_WREN(RTD1625_GPIO_LEVEL_INT_EN); regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(hwirq), val); } static void rtd1625_gpio_enable_irq(struct irq_data *d) { struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d); irq_hw_number_t hwirq = irqd_to_hwirq(d); u32 irq_type = irqd_get_trigger_type(d); gpio_regmap_enable_irq(data->gpio_reg, hwirq); if (irq_type & IRQ_TYPE_EDGE_BOTH) rtd1625_gpio_enable_edge_irq(data, hwirq); else if (irq_type & IRQ_TYPE_LEVEL_MASK) rtd1625_gpio_enable_level_irq(data, hwirq); } static void rtd1625_gpio_disable_irq(struct irq_data *d) { struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d); irq_hw_number_t hwirq = irqd_to_hwirq(d); u32 irq_type = irqd_get_trigger_type(d); if (irq_type & IRQ_TYPE_EDGE_BOTH) rtd1625_gpio_disable_edge_irq(data, hwirq); else if (irq_type & IRQ_TYPE_LEVEL_MASK) rtd1625_gpio_disable_level_irq(data, hwirq); gpio_regmap_disable_irq(data->gpio_reg, hwirq); } static int rtd1625_gpio_irq_set_level_type(struct irq_data *d, bool level) { u32 val = RTD1625_GPIO_WREN(RTD1625_GPIO_LEVEL_INT_DP); irq_hw_number_t hwirq = irqd_to_hwirq(d); struct rtd1625_gpio *data; int ret; data = irq_data_get_irq_chip_data(d); if (!(data->info->irq_type_support & IRQ_TYPE_LEVEL_MASK)) return -EINVAL; if (level) val |= RTD1625_GPIO_LEVEL_INT_DP; scoped_guard(raw_spinlock_irqsave, &data->lock) { ret = regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(hwirq), val); if (ret) return ret; } irq_set_handler_locked(d, handle_level_irq); return 0; } static int rtd1625_gpio_irq_set_edge_type(struct irq_data *d, bool polarity) { struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d); u32 val = RTD1625_GPIO_WREN(RTD1625_GPIO_EDGE_INT_DP); irq_hw_number_t hwirq = irqd_to_hwirq(d); int ret; if (!(data->info->irq_type_support & IRQ_TYPE_EDGE_BOTH)) return -EINVAL; if (polarity) val |= RTD1625_GPIO_EDGE_INT_DP; scoped_guard(raw_spinlock_irqsave, &data->lock) { ret = regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(hwirq), val); if (ret) return ret; } irq_set_handler_locked(d, handle_edge_irq); return 0; } static int rtd1625_gpio_irq_set_type(struct irq_data *d, unsigned int type) { switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_EDGE_RISING: return rtd1625_gpio_irq_set_edge_type(d, 1); case IRQ_TYPE_EDGE_FALLING: return rtd1625_gpio_irq_set_edge_type(d, 0); case IRQ_TYPE_EDGE_BOTH: return rtd1625_gpio_irq_set_edge_type(d, 1); case IRQ_TYPE_LEVEL_HIGH: return rtd1625_gpio_irq_set_level_type(d, 0); case IRQ_TYPE_LEVEL_LOW: return rtd1625_gpio_irq_set_level_type(d, 1); default: return -EINVAL; } } static int rtd1625_gpio_irq_request_resources(struct irq_data *d) { struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d); return gpio_regmap_reqres_irq(data->gpio_reg, d->hwirq); } static void rtd1625_gpio_irq_release_resources(struct irq_data *d) { struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d); gpio_regmap_relres_irq(data->gpio_reg, d->hwirq); } static struct irq_chip rtd1625_iso_gpio_irq_chip = { .name = "rtd1625-gpio", .irq_ack = rtd1625_gpio_ack_irq, .irq_mask = rtd1625_gpio_disable_irq, .irq_unmask = rtd1625_gpio_enable_irq, .irq_set_type = rtd1625_gpio_irq_set_type, .flags = IRQCHIP_IMMUTABLE | IRQCHIP_SKIP_SET_WAKE, .irq_request_resources = rtd1625_gpio_irq_request_resources, .irq_release_resources = rtd1625_gpio_irq_release_resources, }; static int rtd1625_gpio_setup_irq(struct platform_device *pdev, struct rtd1625_gpio *data) { unsigned int num_irqs; int irq; /* IRQ is optional; operate as basic GPIO if absent */ irq = platform_get_irq_optional(pdev, 0); if (irq == -ENXIO) return 0; if (irq < 0) return irq; num_irqs = (data->info->irq_type_support & IRQ_TYPE_LEVEL_MASK) ? 3 : 2; for (unsigned int i = 0; i < num_irqs; i++) { irq = platform_get_irq(pdev, i); if (irq < 0) return irq; data->irqs[i] = irq; irq_set_chained_handler_and_data(data->irqs[i], rtd1625_gpio_irq_handle, data); } return 0; } static int rtd1625_gpio_irq_map(struct irq_domain *domain, unsigned int irq, irq_hw_number_t hwirq) { struct rtd1625_gpio *data = domain->host_data; irq_set_chip_data(irq, data); irq_set_chip_and_handler(irq, &rtd1625_iso_gpio_irq_chip, handle_bad_irq); irq_set_noprobe(irq); irq_set_lockdep_class(irq, &rtd1625_gpio_irq_lock_class, &rtd1625_gpio_irq_request_class); return 0; } static const struct irq_domain_ops rtd1625_gpio_irq_domain_ops = { .map = rtd1625_gpio_irq_map, .xlate = irq_domain_xlate_twocell, }; static const struct regmap_config rtd1625_gpio_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, #ifndef CONFIG_DEBUG_GPIO .disable_locking = true, #endif }; static int rtd1625_gpio_probe(struct platform_device *pdev) { struct gpio_regmap_config config = {}; struct irq_domain_info d_info = {}; struct device *dev = &pdev->dev; struct gpio_regmap *gpio_reg; struct rtd1625_gpio *data; void __iomem *irq_base; int ret; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->info = device_get_match_data(dev); if (!data->info) return -ENODATA; irq_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(irq_base)) return PTR_ERR(irq_base); data->regmap = devm_regmap_init_mmio(dev, irq_base, &rtd1625_gpio_regmap_config); if (IS_ERR(data->regmap)) return PTR_ERR(data->regmap); raw_spin_lock_init(&data->lock); platform_set_drvdata(pdev, data); data->save_regs = devm_kcalloc(dev, data->info->num_gpios, sizeof(*data->save_regs), GFP_KERNEL); if (!data->save_regs) return -ENOMEM; config.parent = dev; config.regmap = data->regmap; config.ngpio = data->info->num_gpios; config.reg_dat_base = data->info->base_offset; config.reg_set_base = data->info->base_offset; config.reg_dir_out_base = data->info->base_offset; config.reg_mask_xlate = rtd1625_reg_mask_xlate; config.set_config = rtd1625_gpio_set_config; config.value_xlate = rtd1625_value_xlate; d_info.fwnode = dev_fwnode(&pdev->dev); d_info.size = data->info->num_gpios; d_info.hwirq_max = data->info->num_gpios; d_info.ops = &rtd1625_gpio_irq_domain_ops; d_info.host_data = data; data->domain = devm_irq_domain_instantiate(dev, &d_info); if (IS_ERR(data->domain)) return PTR_ERR(data->domain); ret = rtd1625_gpio_setup_irq(pdev, data); if (ret) return ret; config.irq_domain = data->domain; config.drvdata = data; gpio_reg = devm_gpio_regmap_register(dev, &config); if (IS_ERR(gpio_reg)) return PTR_ERR(gpio_reg); data->gpio_reg = gpio_reg; return 0; } static const struct rtd1625_gpio_info rtd1625_iso_gpio_info = { .num_gpios = 166, .irq_type_support = IRQ_TYPE_EDGE_BOTH, .base_offset = 0x100, .gpa_offset = 0x000, .gpda_offset = 0x020, .write_en_all = RTD1625_ISO_GPIO_WREN_ALL, }; static const struct rtd1625_gpio_info rtd1625_isom_gpio_info = { .num_gpios = 4, .irq_type_support = IRQ_TYPE_EDGE_BOTH | IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH, .base_offset = 0x20, .gpa_offset = 0x00, .gpda_offset = 0x04, .level_offset = 0x18, .write_en_all = RTD1625_ISOM_GPIO_WREN_ALL, }; static int rtd1625_gpio_suspend(struct device *dev) { struct rtd1625_gpio *data = dev_get_drvdata(dev); const struct rtd1625_gpio_info *info = data->info; int ret; for (unsigned int i = 0; i < info->num_gpios; i++) { ret = regmap_read(data->regmap, data->info->base_offset + GPIO_CONTROL(i), &data->save_regs[i]); if (ret) return ret; } return 0; } static int rtd1625_gpio_resume(struct device *dev) { struct rtd1625_gpio *data = dev_get_drvdata(dev); const struct rtd1625_gpio_info *info = data->info; int ret; for (unsigned int i = 0; i < info->num_gpios; i++) { ret = regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(i), data->save_regs[i] | info->write_en_all); if (ret) return ret; } return 0; } static DEFINE_NOIRQ_DEV_PM_OPS(rtd1625_gpio_pm_ops, rtd1625_gpio_suspend, rtd1625_gpio_resume); static const struct of_device_id rtd1625_gpio_of_matches[] = { { .compatible = "realtek,rtd1625-iso-gpio", .data = &rtd1625_iso_gpio_info }, { .compatible = "realtek,rtd1625-isom-gpio", .data = &rtd1625_isom_gpio_info }, { } }; MODULE_DEVICE_TABLE(of, rtd1625_gpio_of_matches); static struct platform_driver rtd1625_gpio_platform_driver = { .driver = { .name = "gpio-rtd1625", .of_match_table = rtd1625_gpio_of_matches, .pm = pm_sleep_ptr(&rtd1625_gpio_pm_ops), }, .probe = rtd1625_gpio_probe, }; module_platform_driver(rtd1625_gpio_platform_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Realtek Semiconductor Corporation"); MODULE_DESCRIPTION("Realtek DHC SoC RTD1625 gpio driver");