Files
Tzuyi Chang 6435d21cbd gpio: realtek: Add driver for Realtek DHC RTD1625 SoC
Add support for the GPIO controller found on Realtek DHC RTD1625 SoCs.

Unlike the existing Realtek GPIO driver (drivers/gpio/gpio-rtd.c),
which manages pins via shared bank registers, the RTD1625 introduces
a per-pin register architecture. Each GPIO line now has its own
dedicated 32-bit control register to manage configuration independently,
including direction, output value, input value, interrupt enable, and
debounce. Therefore, this distinct hardware design requires a separate
driver.

The RTD1625 GPIO controller has a hardware quirk where both 'assert'
and 'de-assert' interrupts are fired simultaneously on any edge toggle.
The driver works around this quirk to correctly handle edge interrupts.

Interrupt support is optional for this device, matching the dt-bindings.
If the interrupts property is not provided, the driver simply skips IRQ
initialization and operates purely as a basic GPIO controller.

Reviewed-by: Linus Walleij <linusw@kernel.org>
Signed-off-by: Tzuyi Chang <tychang@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Reviewed-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
Link: https://patch.msgid.link/20260726125209.140307-10-eleanor.lin@realtek.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-07-30 11:05:07 +02:00

678 lines
18 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Realtek DHC RTD1625 gpio driver
*
* Copyright (c) 2023-2026 Realtek Semiconductor Corp.
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/regmap.h>
#include <linux/interrupt.h>
#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#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");