From: sashiko-bot@kernel.org
To: "Shubham Paralikar" <shubham.paralikar@mediatek.com>
Cc: linux-gpio@vger.kernel.org
Subject: Re: [PATCH v3] pinctrl: mediatek: Add MT8901/MT8971 ACPI driver
Date: Thu, 05 Nov 2026 05:35:07 +0000 [thread overview]
Message-ID: <sashiko-outbox-198599@kernel.org> (raw)
In-Reply-To: <20261105050535.2551413-1-shubham.paralikar@mediatek.com>
Thank you for your contribution! Sashiko AI review found 14 potential issue(s) to consider:
- [High] pinctrl: mediatek: skip-list EINTs masked on PM/IRQ and clobber dom_en
- [High] pinctrl: mediatek: missing bias_set/get_combo callbacks in mt8971_data
- [High] pinctrl: mediatek: missing virtual GPIO descriptors for MT8971 EINTs 187+
- [High] pinctrl: mediatek: OOB pull_type read in mtk_pinctrl_program_bias_by_gpio()
- [High] pinctrl: mediatek: OOB MMIO for virtual GPIOs in mtk_hw_pin_field_lookup()
- [High] pinctrl: mediatek: uninitialized hw->eint left on mtk_build_eint() error
- [High] pinctrl: mediatek: broken GPIO pin range registration via pinctrl_add_gpio_range()
- [High] pinctrl: mediatek: MT8971 debounce pin flags cause -EOPNOTSUPP on ACPI GpioInt
- [Medium] pinctrl: mediatek: wrong nfuncs=8 in mt8971_data for mux functions 8..10
- [Medium] pinctrl: mediatek: unset has_event_mask in mt8971_eint_hw for eint_event
- [Medium] pinctrl: mediatek: mtk_pinctrl_program_bias_by_gpio() fails on R1R0 pins
- [Medium] pinctrl: mediatek: leak of EINT MMIO mappings in mtk_build_eint()
- [Medium] pinctrl: mediatek: mtk_pinctrl_program_bias_by_gpio() fails on R1R0 pins
- [Low] pinctrl: mediatek: leftover debug dev_info() logging in shared probe paths
--
commit ab3a30ea19f9a05365ff5bd3b4f69185efb16390
Author: Shubham Paralikar <shubham.paralikar@mediatek.com>
pinctrl: mediatek: Add MT8901/MT8971 ACPI driver
This commit adds an ACPI pinctrl and GPIO driver for the MediaTek MT8901 and
MT8971 SoCs, selecting the SoC tables at probe time via a vendor _DSD ChipId
property. It also updates the shared Paris, common-v2, and EINT drivers to
support ACPI resources, fixed-stride register tables, bus-hold bias, EINT
wake-event masks, and firmware-owned EINT skip lists.
> diff --git a/drivers/pinctrl/mediatek/mtk-eint.c b/drivers/pinctrl/mediatek/mtk-eint.c
> index 827231dc0d246..7b3198396b6ec 100644
> --- a/drivers/pinctrl/mediatek/mtk-eint.c
> +++ b/drivers/pinctrl/mediatek/mtk-eint.c
[ ... ]
> @@ -327,12 +508,38 @@ static unsigned int mtk_eint_hw_init(struct mtk_eint *eint)
> mask_reg = eint->base[i] + eint->regs->mask_set;
> for (j = 0; j < eint->base_pin_num[i]; j += 32) {
> writel(0xffffffff, dom_reg);
> - writel(0xffffffff, mask_reg);
> + writel(mtk_eint_mask_word_for_skip(eint, i, j), mask_reg);
> dom_reg += 4;
> mask_reg += 4;
> }
> }
> + return 0;
> +}
> +
> +/* Make sure skip-listed EINTs are unmasked and domain enabled. */
> +static int mtk_eint_restore_skip_list(struct mtk_eint *eint)
> +{
> + unsigned int k, eint_num, inst, idx;
> + void __iomem *reg;
>
> + if (!eint->skip_eint_list || !eint->num_skip_eint)
> + return 0;
> +
> + for (k = 0; k < eint->num_skip_eint; k++) {\n> + eint_num = eint->skip_eint_list[k];
> + if (eint_num >= eint->hw->ap_num)
> + continue;
> + inst = eint->pins[eint_num].instance;
> + if (inst >= eint->nbase)
> + continue;
> + idx = eint->pins[eint_num].index;
> +
> + reg = mtk_eint_get_offset(eint, eint_num, eint->regs->mask_clr);
> + writel(BIT(idx & 0x1f), reg);
> +
> + reg = mtk_eint_get_offset(eint, eint_num, eint->regs->dom_en);
> + writel(BIT(idx & 0x1f), reg);
> + }
> return 0;
> }
[Severity: High]
Does mtk_eint_restore_skip_list() clobber dom_en for sibling EINTs, and do
suspend/resume and runtime IRQ handling end up masking skip-listed EINTs?
First, in mtk_eint_hw_init(), eint->regs->dom_en (offset 0x400, a plain 32-bit
register rather than write-1-to-set) is written with 0xffffffff for each
32-pin word. Immediately afterward in mtk_eint_do_init(),
mtk_eint_restore_skip_list() writes BIT(idx & 0x1f) to dom_en, which
overwrites the entire 32-bit register and clears domain enable for the other
31 EINTs in that word.
Second, mtk_eint_do_init() zero-allocates eint->wake_mask and eint->cur_mask
(where 0 means masked and 1 means unmasked when mtk_eint_chip_write_mask()
writes ~buf[inst][port] to mask_set). Neither mtk_eint_do_init() nor
mtk_eint_restore_skip_list() sets the skip_eint_list bits in wake_mask or
cur_mask, and mtk_eint_do_resume() does not call mtk_eint_restore_skip_list().
When the system suspends and resumes:
mtk_eint_do_suspend()
mtk_eint_chip_write_mask(eint, eint->wake_mask)
writel_relaxed(~buf[inst][port], reg + eint->regs->mask_set)
mtk_eint_do_resume()
mtk_eint_chip_write_mask(eint, eint->cur_mask)
writel_relaxed(~buf[inst][port], reg + eint->regs->mask_set)
Will this mask firmware-owned EINTs such as MT8971 EINT215 (PROCHOT) during
suspend and leave them permanently masked after resume?
Third, mtk_eint_do_init() maps a Linux virq with handle_level_irq for every
pin with inst < eint->nbase, including skip-listed pins. Since dom_en is set
and the pin is unmasked, if a skip-listed EINT asserts at runtime,
mtk_eint_irq_handler() will call generic_handle_domain_irq(), causing
handle_level_irq() to see no registered Linux action and call mask_ack_irq()
to mask and acknowledge the firmware-owned interrupt. Should skip-listed EINTs
also be excluded from dom_en or skipped in mtk_eint_irq_handler(), and have
their bits preserved across suspend and resume?
[ ... ]
> @@ -536,6 +747,9 @@ int mtk_eint_do_init(struct mtk_eint *eint, struct mtk_eint_pin *eint_pin)
> {
> unsigned int size, i, port, virq, inst = 0;
>
> + dev_info(eint->dev, "EINT init: ap_num=%u nbase=%u db_cnt=%u\n",
> + eint->hw->ap_num, eint->nbase, eint->hw->db_cnt);
> +
> /* If clients don't assign a specific regs, let's use generic one */
> if (!eint->regs)
> eint->regs = &mtk_generic_eint_regs;
> @@ -553,6 +767,9 @@ int mtk_eint_do_init(struct mtk_eint *eint, struct mtk_eint_pin *eint_pin)
> continue;
> eint->base_pin_num[inst]++;
> }
> + for (i = 0; i < eint->nbase; i++)
> + dev_info(eint->dev, "EINT init: base[%u] has %u pins\n",
> + i, eint->base_pin_num[i]);
> } else {
[ ... ]
> @@ -602,14 +819,23 @@ int mtk_eint_do_init(struct mtk_eint *eint, struct mtk_eint_pin *eint_pin)
> if (!eint->domain)
> goto err_eint;
>
> + dev_info(eint->dev, "EINT init: irq domain created (ap_num=%u)\n",
> + eint->hw->ap_num);
> +
> if (eint->hw->db_time) {
> for (i = 0; i < MTK_EINT_DBNC_MAX; i++)
> if (eint->hw->db_time[i] == 0)
> break;
> eint->num_db_time = i;
> + dev_info(eint->dev, "EINT init: debounce table has %u entries\n",
> + eint->num_db_time);
> + } else {
> + dev_info(eint->dev, "EINT init: no debounce table (db_time=NULL)\n");
> + }
>
> mtk_eint_hw_init(eint);
> + dev_info(eint->dev, "EINT init: hw_init done, creating IRQ mappings\n");
> + mtk_eint_restore_skip_list(eint);
[Severity: Low]
This isn't a bug, but are these step-by-step dev_info() trace messages in
mtk_eint_do_init(), mtk_build_eint(), and mtk_paris_pinctrl_probe_soc()
(including printing hw->eint with %p in mtk_paris_pinctrl_probe_soc())
leftover bringup debugging that should be removed or converted to dev_dbg() so
they do not spam the boot log on every MediaTek Paris SoC?
> diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8971.c b/drivers/pinctrl/mediatek/pinctrl-mt8971.c
> new file mode 100644
> index 0000000000000..951d3feeafa3f
> --- /dev/null
> +++ b/drivers/pinctrl/mediatek/pinctrl-mt8971.c
> @@ -0,0 +1,58 @@
[ ... ]
> +static const struct mtk_eint_hw mt8971_eint_hw = {
> + .port_mask = 0xf,
> + .ports = 4,
> + .ap_num = 216,
> + .db_cnt = 32,
> + /*
> + * The debounce registers are not confirmed for MT8971 yet, so leave
> + * db_time unset and report debounce as unsupported.
> + */
> + .db_time = NULL,
> +};
> +
> +static const unsigned int eint_event_mt8971[] = {20};
[Severity: Medium]
Should mt8971_eint_hw set .has_event_mask = true?
mt8971_data wires up eint_event_mt8971 to .eint_event and .total_wake_eints,
but mtk_eint_apply_event_unmask() and mtk_eint_write_event_mask() return early
when !eint->hw->has_event_mask, leaving eint_event_mt8971 unused.
> +/* EINT215 is used by firmware for PROCHOT and must stay unmasked. */
> +static const unsigned int mt8971_skip_eint_list[] = {215};
> +
> +const struct mtk_pin_soc mt8971_data = {
> + .reg_in_order = true,
> + .reg_order = mt8971_reg_order,
> + .reg_order_stride = 0x4000,
> + .pins = mtk_pins_mt8971,
> + .npins = ARRAY_SIZE(mtk_pins_mt8971),
> + .ngrps = ARRAY_SIZE(mtk_pins_mt8971),
> + .eint_hw = &mt8971_eint_hw,
> + .eint_pin = eint_pins_mt8971,
> + .nfuncs = 8,
[Severity: Medium]
Should .nfuncs in mt8971_data be 16 instead of 8?
In pinctrl-mtk-mt8971.h, mt8971_reg_order[PINCTRL_PIN_REG_MODE] has a 4-bit
mask (0xf) and mtk_pins_mt8971[] defines functions with muxval 8, 9, and 10.
In mtk_pctrl_show_one_pin():
pinmux = mtk_pctrl_get_pinmux(hw, gpio);
if (pinmux >= hw->soc->nfuncs)
pinmux -= hw->soc->nfuncs;
With .nfuncs = 8, any MT8971 pin muxed to function 8, 9, or 10 will have 8
subtracted from its reported pinmux value in debugfs.
> + .gpio_m = 0,
> + .base_names = mt8971_pinctrl_register_base_name,
> + .nbase_names = ARRAY_SIZE(mt8971_pinctrl_register_base_name),
> + .pull_type = NULL,
> + .drive_set = mtk_pinconf_drive_set_rev1,
> + .drive_get = mtk_pinconf_drive_get_rev1,
> + .adv_drive_set = mtk_pinconf_adv_drive_set_raw,
> + .adv_drive_get = mtk_pinconf_adv_drive_get_raw,
> + .eint_event = eint_event_mt8971,
> + .total_wake_eints = ARRAY_SIZE(eint_event_mt8971),
> + .skip_eint_list = mt8971_skip_eint_list,
> + .n_skip_eint = ARRAY_SIZE(mt8971_skip_eint_list),
> +};
[Severity: High]
Are .bias_set_combo and .bias_get_combo (as well as .pull_type) missing from
mt8971_data?
mt8971_reg_order defines PINCTRL_PIN_REG_PU, PINCTRL_PIN_REG_PD,
PINCTRL_PIN_REG_PUPD, PINCTRL_PIN_REG_R0, PINCTRL_PIN_REG_R1, and
PINCTRL_PIN_REG_RSEL, but mt8971_data leaves .bias_set_combo and
.bias_get_combo NULL. As a result, mtk_pinconf_get() and mtk_pinconf_set() in
pinctrl-paris.c return -ENOTSUPP for all bias parameters, and
mtk_pinctrl_program_bias_by_gpio() returns -EOPNOTSUPP on MT8971.
Also, if .bias_set_combo and .bias_get_combo are set to
mtk_pinconf_bias_set_combo and mtk_pinconf_bias_get_combo while .pull_type is
NULL, try_all_type defaults to MTK_PULL_TYPE_MASK. Because mt8971_reg_order
provides non-zero masks for all bias register fields on every pin, won't
mtk_pinconf_bias_set_combo() and mtk_pinconf_bias_get_combo() always match
MTK_PULL_RSEL_TYPE first for every pin unless a per-pin pull_type table is
provided?
> diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
> index faa6dbf0f6356..c8d88c9d3f05a 100644
> --- a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
> +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
> @@ -81,7 +82,50 @@ static int mtk_hw_pin_field_lookup(struct mtk_pinctrl *hw,
> bool found = false;
> u32 bits;
>
> - if (hw->soc->reg_cal && hw->soc->reg_cal[field].range) {
> + /*
> + * With reg_in_order, each pin has a fixed-size register block:
> + * offset = reg_order[field].offset + pin * reg_order_stride.
> + * Fields not present on this SoC have a zero mask and are handled by
> + * the "not supported" path below.
> + */
> + if (hw->soc->reg_in_order && hw->soc->reg_order &&
> + hw->soc->reg_order[field].mask) {
> + const struct mtk_pin_field_order *o = &hw->soc->reg_order[field];
> +
> + if (desc->number > U32_MAX / hw->soc->reg_order_stride) {
> + dev_err(hw->dev,
> + "pin %d * reg_order_stride overflow\n",
> + desc->number);
> + return -EINVAL;
> + }
> +
> + if (o->offset > U32_MAX - desc->number * hw->soc->reg_order_stride) {
> + dev_err(hw->dev,
> + "offset + pin %d * reg_order_stride overflow\n",
> + desc->number);
> + return -EINVAL;
> + }
> +
> + if (o->index >= hw->nbase) {
> + dev_err(hw->dev,
> + "Invalid base for field %d for pin = %d (%s)\n",
> + field, desc->number, desc->name);
> + return -EINVAL;
> + }
> +
> + pfd->index = o->index;
> + pfd->offset = o->offset +
> + desc->number * hw->soc->reg_order_stride;
> + pfd->bitpos = o->bitpos;
> + pfd->mask = o->mask;
> + /* Fields never cross a register boundary in this layout. */
> + pfd->next = 0;
> +
> + return 0;
> + }
[Severity: High]
Can the reg_in_order path in mtk_hw_pin_field_lookup() perform out-of-bounds
MMIO accesses when called on virtual GPIOs?
On MT8901, the reg_cal range tables stop at physical pin 181, so
mtk_hw_pin_field_lookup() returns -ENOTSUPP for virtual GPIOs 182..208. On
MT8971, reg_in_order is true and pins 182..186 in mtk_pins_mt8971[] are
virtual GPIOs with no backing pad hardware, but this branch calculates
pfd->offset = o->offset + desc->number * hw->soc->reg_order_stride for any
desc->number without checking mtk_is_virt_gpio(hw, desc->number).
When mtk_gpio_get(), mtk_gpio_set(), or mtk_xt_get_gpio_state() (e.g. via
debugfs /sys/kernel/debug/gpio or dual-edge EINT handling) is called on a
virtual GPIO on MT8971, won't mtk_hw_get_value() / mtk_hw_set_value() read or
write unbacked MMIO offsets past the physical pin banks?
> @@ -369,26 +413,49 @@ static const struct mtk_eint_xt mtk_eint_xt = {
> int mtk_build_eint(struct mtk_pinctrl *hw, struct platform_device *pdev)
> {
> struct device_node *np = pdev->dev.of_node;
> - int ret, i, j, count_reg_names;
> + int ret, i, count_reg_names;
> + unsigned int j, nbase;
> + struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev);
> + struct resource *res;
> +
> + dev_info(&pdev->dev, "EINT build: starting (CONFIG_EINT_MTK=%d)\n",
> + IS_ENABLED(CONFIG_EINT_MTK));
>
> if (!IS_ENABLED(CONFIG_EINT_MTK))
> return 0;
>
> - if (!of_property_read_bool(np, "interrupt-controller"))
> + if (is_of_node(fwnode) && !of_property_read_bool(np, "interrupt-controller"))
> return -ENODEV;
>
> hw->eint = devm_kzalloc(hw->dev, sizeof(*hw->eint), GFP_KERNEL);
> if (!hw->eint)
> return -ENOMEM;
>
> - count_reg_names = of_property_count_strings(np, "reg-names");
> + if (is_of_node(fwnode)) {
> + count_reg_names = of_property_count_strings(np, "reg-names");
> + } else {
> + count_reg_names = 0;
> + for (i = 0; i < pdev->num_resources; i++) {
> + struct resource *r = &pdev->resource[i];
> +
> + if (resource_type(r) == IORESOURCE_MEM)
> + count_reg_names++;
> + }
> + }
> if (count_reg_names < 0)
> return -EINVAL;
>
> - hw->eint->nbase = count_reg_names - (int)hw->soc->nbase_names;
> - if (hw->eint->nbase <= 0)
> + dev_info(&pdev->dev,
> + "EINT build: count_reg_names=%d nbase_names=%u\n",
> + count_reg_names, hw->soc->nbase_names);
> +
> + /* need at least one EINT bank after the pin register bases */
> + if ((unsigned int)count_reg_names <= hw->soc->nbase_names)
> return -EINVAL;
[Severity: High]
If count_reg_names < 0 or (unsigned int)count_reg_names <=
hw->soc->nbase_names, mtk_build_eint() returns -EINVAL directly after
allocating hw->eint without jumping to err_free_base to free hw->eint and
reset hw->eint = NULL.
Because mtk_paris_pinctrl_probe_soc() treats mtk_build_eint() failure as
non-fatal and continues probing, leaving hw->eint non-NULL with uninitialized
fields (hw->eint->pins == NULL, hw->eint->hw == NULL, hw->eint->domain ==
NULL) will cause subsequent checks gated on if (hw->eint) in
mtk_paris_pinctrl_probe_soc(), mtk_gpio_set_config() (dereferencing
hw->eint->pins[desc->eint.eint_n].debounce), mtk_gpio_to_irq(), and
mtk_paris_pinctrl_suspend() / mtk_paris_pinctrl_resume() to dereference NULL
pointers. Should these two error paths goto err_free_base (or allocate
hw->eint after validating count_reg_names)?
> + nbase = (unsigned int)count_reg_names - hw->soc->nbase_names;
> + hw->eint->nbase = nbase;
>
> hw->eint->base = devm_kmalloc_array(&pdev->dev, hw->eint->nbase,
> sizeof(*hw->eint->base), GFP_KERNEL | __GFP_ZERO);
> if (!hw->eint->base) {
> @@ -397,20 +464,30 @@ int mtk_build_eint(struct mtk_pinctrl *hw, struct platform_device *pdev)
> }
>
> for (i = hw->soc->nbase_names, j = 0; i < count_reg_names; i++, j++) {
> - hw->eint->base[j] = of_iomap(np, i);
> - if (IS_ERR(hw->eint->base[j])) {
> - ret = PTR_ERR(hw->eint->base[j]);
> + res = platform_get_resource(pdev, IORESOURCE_MEM, i);
> + hw->eint->base[j] = is_of_node(fwnode) ? of_iomap(np, i) :
> + ioremap(res->start, resource_size(res));
> + if (!hw->eint->base[j]) {
> + ret = -ENOMEM;
> goto err_free_eint;
> }
> + dev_info(&pdev->dev, "EINT build: base[%u] mapped @ %pK (res %d)\n",
> + j, hw->eint->base[j], i);
> }
[Severity: Medium]
Does mtk_build_eint() leak the hw->eint->base[j] MMIO mappings when the device
is unbound or when a later step in mtk_paris_pinctrl_probe_soc() fails?
Each hw->eint->base[j] is mapped with unmanaged of_iomap() or ioremap(). Once
mtk_eint_do_init() succeeds, the devm teardown callback mtk_eint_teardown()
removes the IRQ domain and mappings, but nothing calls iounmap() on
eint->base[j] during devres release. Could this use devm_ioremap() /
devm_of_iomap(), or unmap eint->base[j] in mtk_eint_teardown()?
[ ... ]
> @@ -659,6 +761,12 @@ static int mtk_pinconf_bias_set_pupd_r1_r0(struct mtk_pinctrl *hw,
> {
> int err, r0, r1;
>
> + /* Bus-hold is not supported for R1R0 path; reject explicitly */
> + if (pullup == MTK_BUS_HOLD) {
> + err = -EINVAL;
> + goto out;
> + }
> +
> if ((arg == MTK_DISABLE) || (arg == MTK_PUPD_SET_R1R0_00)) {
> pullup = 0;
> r0 = 0;
[ ... ]
> @@ -814,6 +923,96 @@ int mtk_pinconf_bias_set_combo(struct mtk_pinctrl *hw,
[ ... ]
> +static int mtk_pinctrl_public_arg_to_internal(u32 pub, u32 *out)
> +{
> + switch (pub) {
> + case MTK_PIN_ENABLE:\n> + *out = MTK_ENABLE;
> + return 0;
> + case MTK_PIN_DISABLE:
> + *out = MTK_DISABLE;
> + return 0;
> + default:
> + return -EINVAL;
> + }
> +}
[Severity: Medium]
When mtk_pinctrl_program_bias_by_gpio() is called with MTK_PIN_ENABLE on a pin
of type MTK_PULL_PUPD_R1R0_TYPE, mtk_pinctrl_public_arg_to_internal() maps
MTK_PIN_ENABLE to MTK_ENABLE (1), which is then passed to
mtk_pinconf_bias_set_combo() -> mtk_pinconf_bias_set_pupd_r1_r0().
However, mtk_pinconf_bias_set_pupd_r1_r0() only accepts MTK_DISABLE (0) or
MTK_PUPD_SET_R1R0_00..11 (100..103) and rejects arg == MTK_ENABLE (1) with
-EINVAL. Won't mtk_pinctrl_program_bias_by_gpio() fail with -EINVAL whenever
enabling pull-up or pull-down on any R1R0 pin (such as 138 of the 182 physical
pins on MT8901)?
> +int mtk_pinctrl_program_bias_by_gpio(unsigned int gpio, u32 pullup, u32 arg)
> +{
> + struct mtk_pinctrl *hw;
> + const struct mtk_pin_desc *desc;
> + u32 int_pullup, int_arg;
> + int err;
> +
> + err = mtk_pinctrl_public_pullup_to_internal(pullup, &int_pullup);
> + if (err)
> + return err;
> + err = mtk_pinctrl_public_arg_to_internal(arg, &int_arg);
> + if (err)
> + return err;
> +
> + err = -ENODEV;
> + mutex_lock(&mtk_pinctrl_instances_lock);
> + list_for_each_entry(hw, &mtk_pinctrl_instances, instance_node) {
> + if (gpio >= hw->soc->npins)
> + continue;
> +
> + desc = &hw->soc->pins[gpio];
> + if (desc->number != gpio)
> + continue;
> +
> + if (!hw->soc->bias_set_combo) {
> + err = -EOPNOTSUPP;
> + break;
> + }
> +
> + err = hw->soc->bias_set_combo(hw, desc, int_pullup, int_arg);
> + break;
> + }
> + mutex_unlock(&mtk_pinctrl_instances_lock);
> +
> + return err;
> +}
> +EXPORT_SYMBOL_GPL(mtk_pinctrl_program_bias_by_gpio);
[Severity: High]
Does mtk_pinctrl_program_bias_by_gpio() read out-of-bounds from
mt8901_pull_type[] when called with a virtual GPIO number?
On MT8901, hw->soc->npins is 209 (pins 0..181 are physical pads and pins
182..208 are virtual GPIOs), whereas mt8901_pull_type[] in pinctrl-mt8901.c
only has 182 entries (indices 0..181). Unlike mtk_pinconf_set() in
pinctrl-paris.c, which checks mtk_is_virt_gpio(hw, pin) before calling
bias_set_combo, mtk_pinctrl_program_bias_by_gpio() only checks gpio >=
hw->soc->npins. If called with gpio in 182..208, it calls
mtk_pinconf_bias_set_combo():
if (hw->soc->pull_type)
try_all_type = hw->soc->pull_type[desc->number];
which reads past the end of mt8901_pull_type[]. Should
mtk_pinctrl_program_bias_by_gpio() reject virtual GPIOs with
mtk_is_virt_gpio(hw, gpio)?
[Severity: Medium]
In addition, since mtk_pinctrl_public_arg_to_internal() only accepts
MTK_PIN_ENABLE and maps it to int_arg = MTK_ENABLE (1), callers of
mtk_pinctrl_program_bias_by_gpio() cannot pass R1R0 resistance selectors and
any call with MTK_PIN_ENABLE on an MTK_PULL_PUPD_R1R0_TYPE pin will pass
int_arg = 1 to hw->soc->bias_set_combo(), causing
mtk_pinconf_bias_set_pupd_r1_r0() to fail with -EINVAL (and failing with
-EOPNOTSUPP on MT8971 where bias_set_combo is NULL). Should
mtk_pinconf_bias_set_pupd_r1_r0() accept MTK_ENABLE with a default R1R0
setting, or should mtk_pinctrl_public_arg_to_internal() support R1R0
arguments?
> diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-mt8971.h b/drivers/pinctrl/mediatek/pinctrl-mtk-mt8971.h
> new file mode 100644
> index 0000000000000..7e59bc9507a18
> --- /dev/null
> +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-mt8971.h
> @@ -0,0 +1,1646 @@
[ ... ]
> +static struct mtk_eint_pin eint_pins_mt8971[] = {
[ ... ]
> + MTK_EINT_PIN(10, 4, 28, 0),
> + MTK_EINT_PIN(11, 4, 34, 0),
> + MTK_EINT_PIN(12, 5, 0, 0),
> + MTK_EINT_PIN(13, 5, 0, 0),
[ ... ]
> + MTK_EINT_PIN(185, 0, 54, 0),
> + MTK_EINT_PIN(186, 0, 55, 0),
> + MTK_EINT_PIN(187, 1, 0, 1),
> + MTK_EINT_PIN(188, 1, 1, 1),
[ ... ]
> + MTK_EINT_PIN(214, 1, 27, 0),
> + MTK_EINT_PIN(215, 2, 0, 1),
> +};
[ ... ]
> + /*
> + * Virtual GPIO: no ball, but backed by EINT hardware. funcs[0] has
> + * no name so that mtk_is_virt_gpio() treats it as virtual.
> + */
> + MTK_PIN(182, "GPIO182", MTK_EINT_FUNCTION(0, 182), DRV_GRP4,
> + MTK_FUNCTION(0, NULL)),
> + MTK_PIN(183, "GPIO183", MTK_EINT_FUNCTION(0, 183), DRV_GRP4,
> + MTK_FUNCTION(0, NULL)),
> + MTK_PIN(184, "GPIO184", MTK_EINT_FUNCTION(0, 184), DRV_GRP4,
> + MTK_FUNCTION(0, NULL)),
> + MTK_PIN(185, "GPIO185", MTK_EINT_FUNCTION(0, 185), DRV_GRP4,
> + MTK_FUNCTION(0, NULL)),
> + MTK_PIN(186, "GPIO186", MTK_EINT_FUNCTION(0, 186), DRV_GRP4,
> + MTK_FUNCTION(0, NULL)),
> +};
[Severity: High]
Are virtual GPIO descriptors for EINTs 187..214 missing from
mtk_pins_mt8971[], and should non-EINT pins in eint_pins_mt8971[] use
INVALID_BASE (0xff) instead of instance 5?
In eint_pins_mt8971[], EINTs 187..214 are populated on EINT bank 1 (indices
0..27), and mt8971_eint_hw.ap_num is 216. However, mtk_pins_mt8971[] stops at
pin 186 (npins = 187). When any interrupt on EINT bank 1 (hwirq 187..214) is
requested, mtk_eint_irq_request_resources() calls mtk_xt_get_gpio_n(), which
searches mtk_pins_mt8971[0..186], fails to find the EINT, and fails the IRQ
request with -EINVAL (and gpiolib rejects any ACPI GpioInt resource >= 187
because chip->ngpio is 187).
Also, the 12 pins without EINT support in mtk_pins_mt8971[] (pins 12, 13, 17,
18, 22, 56..59, 148, 149, 181) are given instance 5 in eint_pins_mt8971[]
rather than INVALID_BASE (0xff). If firmware provides more than 5 EINT memory
resources, inst < eint->nbase will treat all 12 non-EINT pins as valid EINTs
aliased to bank 5 index 0.
> diff --git a/drivers/pinctrl/mediatek/pinctrl-paris.c b/drivers/pinctrl/mediatek/pinctrl-paris.c
> index 09098b68f7257..a827ae618eff3 100644
> --- a/drivers/pinctrl/mediatek/pinctrl-paris.c
> +++ b/drivers/pinctrl/mediatek/pinctrl-paris.c
[ ... ]
> @@ -928,9 +956,15 @@ static int mtk_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
>
> desc = (const struct mtk_pin_desc *)&hw->soc->pins[offset];
>
> + /*
> + * Not every EINT has debounce hardware, the virtual GPIOs among them.
> + * Report that as unsupported so gpiolib treats the request as optional
> + * rather than warning about an -EINVAL from the EINT driver.
> + */
> if (!hw->eint ||
> pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE ||
> - desc->eint.eint_n == EINT_NA)
> + desc->eint.eint_n == EINT_NA ||
> + !hw->eint->pins[desc->eint.eint_n].debounce)
> return -ENOTSUPP;
>
> debounce = pinconf_to_config_argument(config);
>
> return mtk_eint_set_debounce(hw->eint, desc->eint.eint_n, debounce);
> }
[Severity: High]
Should mtk_gpio_set_config() and mtk_eint_irq_handler() also check
hw->eint->hw->db_time (or should eint_pins_mt8971[] clear .debounce)?
In pinctrl-mt8971.c, mt8971_eint_hw sets .db_time = NULL with a comment noting
that debounce registers are not confirmed on MT8971 and debounce should be
reported as unsupported. However, 32 pins in eint_pins_mt8971[] still have
.debounce = 1.
When acpi_dev_gpio_irq_wake_get_by() requests an ACPI GpioInt on any of those
32 pins on MT8971, it calls gpio_set_debounce_timeout() ->
mtk_gpio_set_config(). Because .debounce is 1 and mtk_gpio_set_config() does
not check !hw->eint->hw->db_time, it calls mtk_eint_set_debounce(), which
returns -EOPNOTSUPP. Since gpio_set_config_with_argument_optional() only
ignores -ENOTSUPP and treats -EOPNOTSUPP as a fatal error, won't ACPI GpioInt
setup fail on all 32 debounce-marked pins on MT8971? Additionally, in
mtk_eint_irq_handler(), if (eint->pins[eint_num].debounce) will still call
mtk_eint_debounce_process() and touch the unconfirmed dbnc_ctrl and dbnc_set
registers on MT8971.
[ ... ]
> @@ -1069,26 +1139,79 @@ int mtk_paris_pinctrl_probe(struct platform_device *pdev)
[ ... ]
> /* Build gpiochip should be after pinctrl_enable is done */
> err = mtk_build_gpiochip(hw);
> if (err)
> return dev_err_probe(dev, err, "Failed to add gpio_chip\n");
>
> + dev_info(dev, "PROBE: gpiochip registered, adding GPIO range\n");
> +
> + pinctrl_add_gpio_range(hw->pctrl, &hw->range);
> +#ifdef CONFIG_GPIO_ACPI
> + if (hw->eint) {
> + dev_info(dev, "PROBE: requesting ACPI GPIO interrupts\n");
> + acpi_gpiochip_request_interrupts(&hw->chip);
> + dev_info(dev, "PROBE: ACPI GPIO interrupts requested\n");
> +
> + err = devm_add_action_or_reset(dev,
> + mtk_pinctrl_acpi_free_interrupts,
> + &hw->chip);
> + if (err) {
> + pinctrl_remove_gpio_range(hw->pctrl, &hw->range);
> + return dev_err_probe(dev, err,
> + "Failed to add ACPI GPIO cleanup\n");
> + }
> + } else {
> + dev_info(dev, "PROBE: no EINT, skipping ACPI GPIO interrupts\n");
> + }
> +#endif
> +
> platform_set_drvdata(pdev, hw);
>
> + /*
> + * Register so mtk_pinctrl_program_bias_by_gpio() can find this
> + * controller. The devm action unregisters it before hw is freed.
> + */
> + mtk_pinctrl_register_instance(hw);
> +
> + err = devm_add_action_or_reset(&pdev->dev,
> + mtk_paris_unregister_instance, hw);
> + if (err)
> + return err;
[Severity: High]
Does registering hw->range with pinctrl_add_gpio_range() here leave
gdev->pin_ranges empty on ACPI, duplicate ranges on Device Tree platforms, and
leave a dangling range on error or teardown?
First, mtk_build_gpiochip() sets chip->request = gpiochip_generic_request and
chip->free = gpiochip_generic_free. In gpiolib, gpiochip_generic_request()
checks:
if (list_empty(&gc->gpiodev->pin_ranges))
return 0;
Calling pinctrl_add_gpio_range() only adds hw->range to hw->pctrl->gpio_ranges
and does not add a pin range to gc->gpiodev->pin_ranges (unlike
gpiochip_add_pin_range()). On ACPI platforms, gc->gpiodev->pin_ranges remains
empty, so gpiochip_generic_request() returns early without calling
pinctrl_gpio_request() or mtk_pinmux_gpio_request_enable() to mux the pin to
GPIO mode.
Second, on Device Tree platforms, devm_gpiochip_add_data() in
mtk_build_gpiochip() already parses the DT gpio-ranges property via
of_gpiochip_add_pin_range(), so unconditionally adding hw->range registers a
duplicate range.
Third, if devm_add_action_or_reset(&pdev->dev, mtk_paris_unregister_instance,
hw) fails (or when the driver is unbound), pinctrl_remove_gpio_range() is
never called for hw->range. Should this use gpiochip_add_pin_range() (for
example via chip->add_pin_ranges when gpio-ranges is not present in DT) so
that gpiolib populates gdev->pin_ranges and automatically removes the range
when the gpio_chip is torn down?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261105050535.2551413-1-shubham.paralikar@mediatek.com?part=1
next prev parent reply other threads:[~2026-11-05 5:35 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 5:05 [PATCH v3] pinctrl: mediatek: Add MT8901/MT8971 ACPI driver Shubham Paralikar
2026-11-05 5:35 ` sashiko-bot [this message]
2026-11-05 9:24 ` AngeloGioacchino Del Regno
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-198599@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=linux-gpio@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=shubham.paralikar@mediatek.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox