mirror of
https://gitee.com/open-loongarch/linux-6.12.git
synced 2026-10-25 12:37:11 +08:00
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
This commit is contained in:
@@ -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 <niuyize@loongson.cn>");
|
||||
MODULE_DESCRIPTION("Loongson GPIO With IRQ Driver");
|
||||
|
||||
Reference in New Issue
Block a user