From ed8ea729eef32659bf54268781d217cacfc242ae Mon Sep 17 00:00:00 2001 From: maoxiaochuan Date: Thu, 20 Nov 2025 12:03:56 +0800 Subject: [PATCH] gpio:fix crash when irq map fail [ 1.112617] Warning: unable to open an initial console. [ 1.128648] Waiting for root device /dev/mmcblk0p1... [ 11.439784] gpio gpiochip0: Static allocation of GPIO base is deprecated, use dynamic allocation. [ 11.449431] gpio-lsirq 16104000.gpio: IRQ Pin Map fail [ 11.459605] gpio-lsirq 16104000.gpio: probe with driver gpio-lsirq failed with error -1 [ 11.467761] Unhandled kernel unaligned access[#1]: [ 11.472606] CPU: 0 UID: 0 PID: 79 Comm: kworker/u4:2 Not tainted 6.12.0.lsgd #1 [ 11.479994] Hardware name: Loongson LS2K300/LS2K300-PAI, BIOS 2022.04-v2.0.0-00572-gfaaca55a 04/01/2022 [ 11.489483] Workqueue: events_unbound deferred_probe_work_func [ 11.495394] pc 90000000008683a8 ra 9000000000868398 tp 9000000006954000 sp 9000000006957b60 [ 11.503832] a0 9000000001860338 a1 90000000066a0230 a2 9000000006957b98 a3 0000000000000004 [ 11.512267] a4 00000000000000b4 a5 0000000000000040 a6 0000000000000000 a7 9000000006018410 [ 11.520703] t0 0000000000000122 t1 9000000001860240 t2 90000000068b2f40 t3 0000000000000000 [ 11.529137] t4 900000000618a658 t5 900000000618a658 t6 90000000009f0eb4 t7 90000000009f1154 [ 11.537571] t8 0000000000000000 u0 006f6970672e3030 s9 90000000066ce500 s0 9000000006c1d440 [ 11.546005] s1 9000000006c1e400 s2 9000000001860240 s3 9000000001860338 s4 0000000000000003 [ 11.554440] s5 90000000013ea000 s6 90000000066ce4c0 s7 90000000013f2000 s8 90000000060e5e80 [ 11.562874] ra: 9000000000868398 gpiochip_remove+0x44/0xd4 [ 11.568693] ERA: 90000000008683a8 gpiochip_remove+0x54/0xd4 [ 11.574504] CRMD: 000000b0 (PLV0 -IE -DA +PG DACF=CC DACM=CC -WE) [ 11.580774] PRMD: 00000004 (PPLV0 +PIE -PWE) [ 11.585182] EUEN: 00000000 (-FPE -SXE -ASXE -BTE) [ 11.590033] ECFG: 00071c1d (LIE=0,2-4,10-12 VS=7) [ 11.594882] ESTAT: 00090000 [ALE] (IS= ECode=9 EsubCode=0) [ 11.600428] BADV: 0000000000000122 [ 11.603946] PRID: 0014a030 (Loongson-64bit, ) [ 11.608431] Modules linked in: [ 11.611512] Process kworker/u4:2 (pid: 79, threadinfo=(____ptrval____), task=(____ptrval____)) [ 11.620213] Stack : 9000000006196ca0 9000000006957b98 900000000618a410 90000000066d7b40 [ 11.628313] 90000000066a0200 90000000009f276c 90000000013ea000 90000000066d7420 [ 11.636412] 90000000066a0200 db47c55fde642c00 90000000013ea000 90000000009ec89c [ 11.644510] 90000000013ea000 0000000000000000 9000000001860e00 0000000000000001 [ 11.652608] 900000000618a410 90000000009ebae8 900000000618a410 90000000009ec3e8 [ 11.660707] 900000000618a410 0000000000000003 900000000618a410 9000000001860e00 [ 11.668805] 900000000618a410 90000000009ec63c 900000000618a410 9000000001860e00 [ 11.676904] 9000000001c91e18 90000000009ec7fc 9000000001860e00 900000000618a410 [ 11.685002] 9000000006957d18 9000000001860e00 0000000000000001 90000000009ec968 [ 11.693100] 90000000013ea000 9000000006957d18 900000000608f800 9000000006957cb8 [ 11.701199] ... [ 11.703668] Call Trace: [ 11.703675] [<90000000008683a8>] gpiochip_remove+0x54/0xd4 [ 11.711688] [<90000000009f2768>] devres_release_all+0xb4/0x12c [ 11.717594] [<90000000009ebae4>] device_unbind_cleanup+0x10/0x5c [ 11.723667] [<90000000009ec3e4>] really_probe+0x1e0/0x3ac [ 11.729126] [<90000000009ec638>] __driver_probe_device+0x88/0x160 [ 11.735285] [<90000000009ec7f8>] driver_probe_device+0x34/0xd8 [ 11.741181] [<90000000009ec964>] __device_attach_driver+0xc8/0x178 [ 11.747430] [<90000000009e9d7c>] bus_for_each_drv+0x8c/0xf0 [ 11.753073] [<90000000009ecda8>] __device_attach+0xa4/0x1b4 [ 11.758705] [<90000000009eaf60>] bus_probe_device+0xa8/0xd0 [ 11.764339] [<90000000009eb5f4>] deferred_probe_work_func+0x9c/0xf0 [ 11.770674] [<9000000000259084>] process_one_work+0x178/0x2f4 [ 11.776492] [<9000000000259fa0>] worker_thread+0x390/0x4c8 [ 11.782041] [<9000000000261028>] kthread+0x104/0x114 [ 11.787066] [<9000000000f1c764>] ret_from_kernel_thread+0x28/0x48 [ 11.793229] [<9000000000221024>] ret_from_kernel_thread_asm+0xc/0x88 [ 11.799654] [ 11.801153] Code: 28cd630c 00150344 29c021ac <2700018d> 0284880c 29cd630c 5789441a 02c04324 56f96fe8 [ 11.811031] [ 11.812564] ---[ end trace 0000000000000000 ]--- [ 119.451577] random: crng init done --- drivers/gpio/gpio-loongson-irq.c | 165 ++++++++++++++++++++++--------- 1 file changed, 121 insertions(+), 44 deletions(-) diff --git a/drivers/gpio/gpio-loongson-irq.c b/drivers/gpio/gpio-loongson-irq.c index 13932e297..0007e994c 100644 --- a/drivers/gpio/gpio-loongson-irq.c +++ b/drivers/gpio/gpio-loongson-irq.c @@ -116,18 +116,23 @@ static int __map_hwirq_pin(lsirq_gpio_irqpinmap_t* map, int hwirq, lsirq_gpio_priv_t* priv) { int pin; + u8 irq_val, irqsta_val; - if (map->hwirq == hwirq) - { - for (pin = map->pin_start; - pin < map->pin_start + map->pin_num; - pin++) - { - // 2k300 must check irq enable - if (lsirq_gpio_get_reg_irq(priv, pin) == 1 && - lsirq_gpio_get_reg_irqsta(priv, pin) == 1) - return pin; - } + if (map->hwirq != hwirq) + return -1; + + for (pin = map->pin_start; pin < map->pin_start + map->pin_num; pin++) { + /* Bounds checking */ + if (pin < 0 || pin >= priv->gc.ngpio) + continue; + + /* Atomic register read to avoid race condition */ + irq_val = lsirq_gpio_get_reg_irq(priv, pin); + irqsta_val = lsirq_gpio_get_reg_irqsta(priv, pin); + + /* 2k300 must check irq enable */ + if (irq_val == 1 && irqsta_val == 1) + return pin; } return -1; @@ -135,11 +140,21 @@ static int __map_hwirq_pin(lsirq_gpio_irqpinmap_t* map, int hwirq, static int __map_vhwirq_pin(lsirq_gpio_priv_t* priv, int vhwirq) { - return vhwirq - priv->irqd.vhwirq_base; + int pin = vhwirq - priv->irqd.vhwirq_base; + + /* Bounds checking */ + if (pin < 0 || pin >= priv->gc.ngpio) + return -EINVAL; + + return pin; } static int __map_pin_vhwirq(lsirq_gpio_priv_t* priv, int pin) { + /* Bounds checking */ + if (pin < 0 || pin >= priv->gc.ngpio) + return -EINVAL; + return pin + priv->irqd.vhwirq_base; } @@ -225,7 +240,13 @@ static int mthd_lsirq_gpio_to_irq(struct gpio_chip *gc, unsigned pin) int vhwirq, rirq; lsirq_gpio_priv_t *priv = gpiochip_get_data(gc); + if (pin >= gc->ngpio) + return -EINVAL; + vhwirq = __map_pin_vhwirq(priv, (int)pin); + if (vhwirq < 0) + return vhwirq; + rirq = __map_vhwirq_rirq(priv, vhwirq); return rirq; @@ -238,6 +259,9 @@ static int mthd_lsirq_gpio_irqsettype(struct irq_data *d, u32 type) int pin = __map_vhwirq_pin(priv, vhwirq); unsigned long flags; + if (pin < 0) + return pin; + switch (type) { case IRQ_TYPE_EDGE_RISING: lsirq_gpio_set_reg_irq(priv, pin, 1); @@ -283,7 +307,13 @@ static int mthd_lsirq_gpio_irqsettype(struct irq_data *d, u32 type) static void mthd_lsirq_gpio_irqack(struct irq_data *d) { - + lsirq_gpio_priv_t *priv = irq_data_get_irq_chip_data(d); + int vhwirq = d->hwirq; + int pin = __map_vhwirq_pin(priv, vhwirq); + + /* Acknowledge interrupt by clearing status */ + if (pin >= 0) + lsirq_gpio_set_reg_irqclr(priv, pin, 1); } static void mthd_lsirq_gpio_irqmask(struct irq_data *d) @@ -293,7 +323,10 @@ static void mthd_lsirq_gpio_irqmask(struct irq_data *d) int pin = __map_vhwirq_pin(priv, vhwirq); unsigned long flags; - // interrupt must gpio input + if (pin < 0) + return; + + /* interrupt must gpio input */ spin_lock_irqsave(&priv->lock, flags); lsirq_gpio_set_reg_dir(priv, pin, 1); spin_unlock_irqrestore(&priv->lock, flags); @@ -307,23 +340,24 @@ static void mthd_lsirq_gpio_irqunmask(struct irq_data *d) int vhwirq = d->hwirq; int pin = __map_vhwirq_pin(priv, vhwirq); + if (pin < 0) + return; + lsirq_gpio_set_reg_irq(priv, pin, 1); } -static int lsirq_gpio_gc_remove(lsirq_gpio_priv_t* priv) -{ - struct gpio_chip* gc = &priv->gc; - gpiochip_remove(gc); - - return 0; -} static int lsirq_gpio_gc_setup(lsirq_gpio_priv_t* priv) { struct gpio_chip* gc = &priv->gc; struct device_node *np = priv->dev->of_node; + if (!np) { + dev_err(priv->dev, "No device node\n"); + return -ENODEV; + } + of_property_read_u32(np, "ngpios", (u32 *)&gc->ngpio); of_property_read_u32(np, "gpio_base", (u32 *)&gc->base); gc->request = mthd_lsirq_gpio_request; @@ -339,7 +373,7 @@ static int lsirq_gpio_gc_setup(lsirq_gpio_priv_t* priv) if (devm_gpiochip_add_data(priv->dev, gc, priv)) { dev_err(priv->dev, "gpiochip setup fail\n"); - return -1; + return -ENODEV; } return 0; @@ -429,6 +463,11 @@ static int lsirqd_setup(lsirq_gpio_priv_t* priv) lsirq_gpio_irqdesc_t* irqd = &priv->irqd; struct device_node *np = priv->dev->of_node; + if (!np) { + dev_err(priv->dev, "No device node\n"); + return -ENODEV; + } + of_property_read_u32(np, "loongson,vhwirq-base", (u32 *)&irqd->vhwirq_base); snprintf(irqd->name, sizeof(irqd->name), "GPIC-%s", np->name); @@ -439,7 +478,7 @@ static int lsirqd_setup(lsirq_gpio_priv_t* priv) if (irqd->nr_irqs <= 0) { dev_err(priv->dev, "IRQ Pin Map fail\n"); - goto err; + return -ENODEV; } /* * Setup VIRQ-IRQ-MAP @@ -450,14 +489,14 @@ static int lsirqd_setup(lsirq_gpio_priv_t* priv) 0, irqd->nr_irqs, numa_node_id()); if (irqd->rirq_base < 0) { dev_err(priv->dev, "Alloc IRQ fail\n"); - goto clear_pin_map; + goto free_map; } // set ic prop irqd->ic = kzalloc(sizeof(*irqd->ic), GFP_KERNEL); if (!irqd->ic) { dev_err(priv->dev, "IRQ chip alloc fail\n"); - goto free_irq_desc; + goto free_ic; } irqd->ic->name = irqd->name; irqd->ic->irq_ack = mthd_lsirq_gpio_irqack; @@ -471,20 +510,18 @@ static int lsirqd_setup(lsirq_gpio_priv_t* priv) &domain_ops_gpic, priv); if (!irqd->domain) { dev_err(priv->dev, "IRQ domain add fail\n"); - goto free_ic; + goto free_domain; } dev_info(priv->dev, "register irqnum=%d, vhwirq-base=%d, rirq-base=%d\n", irqd->nr_irqs, irqd->vhwirq_base, irqd->rirq_base); return 0; - -free_ic: +free_domain: kfree(irqd->ic); -free_irq_desc: +free_ic: irq_free_descs(irqd->rirq_base, irqd->nr_irqs); -clear_pin_map: +free_map: lsirqmap_clear(&irqd->ipm); -err: - return -1; + return -ENODEV; } static const struct of_device_id drvids_lsirq_gpio[] = { @@ -497,8 +534,18 @@ MODULE_DEVICE_TABLE(of, drvids_lsirq_gpio); static int drv_lsirq_gpio_probe(struct platform_device *pdev) { lsirq_gpio_priv_t* priv; - const struct of_device_id *of_id = - of_match_device(drvids_lsirq_gpio, &pdev->dev); + const struct of_device_id *of_id; + + of_id = of_match_device(drvids_lsirq_gpio, &pdev->dev); + if (!of_id) { + dev_err(&pdev->dev, "No matching device ID\n"); + return -ENODEV; + } + + if (!of_id->data) { + dev_err(&pdev->dev, "No register table data\n"); + return -ENODEV; + } priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -506,28 +553,50 @@ static int drv_lsirq_gpio_probe(struct platform_device *pdev) priv->reg = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->reg)) - goto ioremap_fail; + return PTR_ERR(priv->reg); + spin_lock_init(&priv->lock); priv->dev = &pdev->dev; priv->regtable = of_id->data; if (lsirq_gpio_gc_setup(priv)) - goto gc_fail; + return -ENODEV; if (lsirqd_setup(priv)) - goto irqd_fail; + return -ENODEV; platform_set_drvdata(pdev, priv); return 0; +} -irqd_fail: - lsirq_gpio_gc_remove(priv); -gc_fail: - devm_iounmap(&pdev->dev, priv->reg); -ioremap_fail: - devm_kfree(&pdev->dev, priv); - return -1; +static void drv_lsirq_gpio_remove(struct platform_device *pdev) +{ + lsirq_gpio_priv_t *priv = platform_get_drvdata(pdev); + + if (!priv) + return; + + /* Clean up IRQ domain and mappings */ + if (priv->irqd.domain) { + irq_domain_remove(priv->irqd.domain); + priv->irqd.domain = NULL; + } + + /* Free IRQ descriptors */ + if (priv->irqd.rirq_base >= 0) { + irq_free_descs(priv->irqd.rirq_base, priv->irqd.nr_irqs); + priv->irqd.rirq_base = -1; + } + + /* Free IRQ chip */ + kfree(priv->irqd.ic); + priv->irqd.ic = NULL; + + /* Clear IRQ pin mappings */ + lsirqmap_clear(&priv->irqd.ipm); + + /* GPIO chip will be automatically cleaned up by devm_* */ } static struct platform_driver lsirq_gpio_driver = { @@ -537,13 +606,21 @@ static struct platform_driver lsirq_gpio_driver = { .of_match_table = drvids_lsirq_gpio, }, .probe = drv_lsirq_gpio_probe, + .remove = drv_lsirq_gpio_remove, }; static int __init lsirq_gpio_init(void) { return platform_driver_register(&lsirq_gpio_driver); } + +static void __exit lsirq_gpio_exit(void) +{ + platform_driver_unregister(&lsirq_gpio_driver); +} + subsys_initcall(lsirq_gpio_init); +module_exit(lsirq_gpio_exit); MODULE_AUTHOR("Yize Niu "); MODULE_DESCRIPTION("Loongson GPIO With IRQ Driver");