From 6124cec530c7d8faab96d340ab2df5161e5d1c8a Mon Sep 17 00:00:00 2001 From: Molly Sophia Date: Sun, 7 Aug 2022 20:26:44 +0800 Subject: [PATCH 1/5] pinctrl: qcom: sc8180x: Fix gpio_wakeirq_map Currently in the wakeirq_map, gpio36 and gpio37 have the same wakeirq number, resulting in gpio37 being unable to trigger interrupts. It looks like that this is a typo in the wakeirq map. So fix it. Signed-off-by: Molly Sophia Fixes: 97423113ec4b ("pinctrl: qcom: Add sc8180x TLMM driver") Tested-by: Bjorn Andersson Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220807122645.13830-2-mollysophia379@gmail.com Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-sc8180x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/qcom/pinctrl-sc8180x.c b/drivers/pinctrl/qcom/pinctrl-sc8180x.c index 6bec7f143134..b4bf009fe23e 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc8180x.c +++ b/drivers/pinctrl/qcom/pinctrl-sc8180x.c @@ -1582,7 +1582,7 @@ static const int sc8180x_acpi_reserved_gpios[] = { static const struct msm_gpio_wakeirq_map sc8180x_pdc_map[] = { { 3, 31 }, { 5, 32 }, { 8, 33 }, { 9, 34 }, { 10, 100 }, { 12, 104 }, { 24, 37 }, { 26, 38 }, { 27, 41 }, { 28, 42 }, { 30, 39 }, { 36, 43 }, - { 37, 43 }, { 38, 45 }, { 39, 118 }, { 39, 125 }, { 41, 47 }, + { 37, 44 }, { 38, 45 }, { 39, 118 }, { 39, 125 }, { 41, 47 }, { 42, 48 }, { 46, 50 }, { 47, 49 }, { 48, 51 }, { 49, 53 }, { 50, 52 }, { 51, 116 }, { 51, 123 }, { 53, 54 }, { 54, 55 }, { 55, 56 }, { 56, 57 }, { 58, 58 }, { 60, 60 }, { 68, 62 }, { 70, 63 }, { 76, 86 }, From 48ec73395887694f13c9452b4dcfb43710451757 Mon Sep 17 00:00:00 2001 From: Molly Sophia Date: Sun, 7 Aug 2022 20:26:45 +0800 Subject: [PATCH 2/5] pinctrl: qcom: sc8180x: Fix wrong pin numbers The pin numbers for UFS_RESET and SDC2_* are not consistent in the pinctrl driver for sc8180x. So fix it. Signed-off-by: Molly Sophia Fixes: 97423113ec4b ("pinctrl: qcom: Add sc8180x TLMM driver") Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220807122645.13830-3-mollysophia379@gmail.com Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-sc8180x.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-sc8180x.c b/drivers/pinctrl/qcom/pinctrl-sc8180x.c index b4bf009fe23e..704a99d2f93c 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc8180x.c +++ b/drivers/pinctrl/qcom/pinctrl-sc8180x.c @@ -530,10 +530,10 @@ DECLARE_MSM_GPIO_PINS(187); DECLARE_MSM_GPIO_PINS(188); DECLARE_MSM_GPIO_PINS(189); -static const unsigned int sdc2_clk_pins[] = { 190 }; -static const unsigned int sdc2_cmd_pins[] = { 191 }; -static const unsigned int sdc2_data_pins[] = { 192 }; -static const unsigned int ufs_reset_pins[] = { 193 }; +static const unsigned int ufs_reset_pins[] = { 190 }; +static const unsigned int sdc2_clk_pins[] = { 191 }; +static const unsigned int sdc2_cmd_pins[] = { 192 }; +static const unsigned int sdc2_data_pins[] = { 193 }; enum sc8180x_functions { msm_mux_adsp_ext, From b871656aa4f54e04207f62bdd0d7572be1d86b36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20H=2E=20Spies?= Date: Mon, 8 Aug 2022 02:51:20 +0000 Subject: [PATCH 3/5] pinctrl: rockchip: Enhance support for IRQ_TYPE_EDGE_BOTH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switching between falling/rising edges for IRQ_TYPE_EDGE_BOTH on pins that require debounce can cause the device to lose events due to a desync between pin state and irq type. This problem is resolved by switching between IRQ_TYPE_LEVEL_LOW and IRQ_TYPE_LEVEL_HIGH instead. Fixes: 936ee2675eee ("gpio/rockchip: add driver for rockchip gpio") Signed-off-by: João H. Spies Link: https://lore.kernel.org/r/20220808025121.110223-1-jhlspies@gmail.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-rockchip.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index f91e876fd969..bb50335239ac 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -419,11 +419,11 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type) goto out; } else { bank->toggle_edge_mode |= mask; - level |= mask; + level &= ~mask; /* * Determine gpio state. If 1 next interrupt should be - * falling otherwise rising. + * low otherwise high. */ data = readl(bank->reg_base + bank->gpio_regs->ext_port); if (data & mask) From 76648c867c6c03b8a468d9c9222025873ecc613d Mon Sep 17 00:00:00 2001 From: Michael Wu Date: Fri, 19 Aug 2022 10:45:41 +0800 Subject: [PATCH 4/5] pinctrl: sunxi: Fix name for A100 R_PIO The name of A100 R_PIO driver should be sun50i-a100-r-pinctrl, not sun50iw10p1-r-pinctrl. Fixes: 473436e7647d6 ("pinctrl: sunxi: add support for the Allwinner A100 pin controller") Signed-off-by: Michael Wu Acked-by: Samuel Holland Link: https://lore.kernel.org/r/20220819024541.74191-1-michael@allwinnertech.com Signed-off-by: Linus Walleij --- drivers/pinctrl/sunxi/pinctrl-sun50i-a100-r.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-a100-r.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-a100-r.c index afc1f5df7545..b82ad135bf2a 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun50i-a100-r.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-a100-r.c @@ -99,7 +99,7 @@ MODULE_DEVICE_TABLE(of, a100_r_pinctrl_match); static struct platform_driver a100_r_pinctrl_driver = { .probe = a100_r_pinctrl_probe, .driver = { - .name = "sun50iw10p1-r-pinctrl", + .name = "sun50i-a100-r-pinctrl", .of_match_table = a100_r_pinctrl_match, }, }; From c297561bc98ad0f2a37ce0178ee3ba89ab586d70 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Fri, 9 Sep 2022 16:59:42 +0200 Subject: [PATCH 5/5] pinctrl: ocelot: Fix interrupt controller When an external device generated a level based interrupt then the interrupt controller could miss the interrupt. The reason is that the interrupt controller can detect only link changes. In the following example, if there is a PHY that generates an interrupt then the following would happen. The GPIO detected that the interrupt line changed, and then the 'ocelot_irq_handler' was called. Here it detects which GPIO line saw the change and for that will call the following: 1. irq_mask 2. phy interrupt routine 3. irq_eoi 4. irq_unmask And this works fine for simple cases, but if the PHY generates many interrupts, for example when doing PTP timestamping, then the following could happen. Again the function 'ocelot_irq_handler' will be called and then from here the following could happen: 1. irq_mask 2. phy interrupt routine 3. irq_eoi 4. irq_unmask Right before step 3(irq_eoi), the PHY will generate another interrupt. Now the interrupt controller will acknowledge the change in the interrupt line. So we miss the interrupt. A solution will be to use 'handle_level_irq' instead of 'handle_fasteoi_irq', because for this will change routine order of handling the interrupt. 1. irq_mask 2. irq_ack 3. phy interrupt routine 4. irq_unmask And now if the PHY will generate a new interrupt before irq_unmask, the interrupt controller will detect this because it already acknowledge the change in interrupt line at step 2(irq_ack). But this is not the full solution because there is another issue. In case there are 2 PHYs that share the interrupt line. For example phy1 generates an interrupt, then the following can happen: 1.irq_mask 2.irq_ack 3.phy0 interrupt routine 4.phy1 interrupt routine 5.irq_unmask In case phy0 will generate an interrupt while clearing the interrupt source in phy1, then the interrupt line will be kept down by phy0. So the interrupt controller will not see any changes in the interrupt line. The solution here is to update 'irq_unmask' such that it can detect if the interrupt line is still active or not. And if it is active then call again the procedure to clear the interrupts. But we don't want to do it every time, only if we know that the interrupt controller has not seen already that the interrupt line has changed. While at this, add support also for IRQ_TYPE_LEVEL_LOW. Fixes: be36abb71d878f ("pinctrl: ocelot: add support for interrupt controller") Signed-off-by: Horatiu Vultur Link: https://lore.kernel.org/r/20220909145942.844102-1-horatiu.vultur@microchip.com Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-ocelot.c | 111 +++++++++++++++++++++++++++---- 1 file changed, 97 insertions(+), 14 deletions(-) diff --git a/drivers/pinctrl/pinctrl-ocelot.c b/drivers/pinctrl/pinctrl-ocelot.c index c5fd154990c8..c7df8c5fe585 100644 --- a/drivers/pinctrl/pinctrl-ocelot.c +++ b/drivers/pinctrl/pinctrl-ocelot.c @@ -331,6 +331,7 @@ struct ocelot_pinctrl { const struct ocelot_pincfg_data *pincfg_data; struct ocelot_pmx_func func[FUNC_MAX]; u8 stride; + struct workqueue_struct *wq; }; struct ocelot_match_data { @@ -338,6 +339,11 @@ struct ocelot_match_data { struct ocelot_pincfg_data pincfg_data; }; +struct ocelot_irq_work { + struct work_struct irq_work; + struct irq_desc *irq_desc; +}; + #define LUTON_P(p, f0, f1) \ static struct ocelot_pin_caps luton_pin_##p = { \ .pin = p, \ @@ -1813,6 +1819,75 @@ static void ocelot_irq_mask(struct irq_data *data) gpiochip_disable_irq(chip, gpio); } +static void ocelot_irq_work(struct work_struct *work) +{ + struct ocelot_irq_work *w = container_of(work, struct ocelot_irq_work, irq_work); + struct irq_chip *parent_chip = irq_desc_get_chip(w->irq_desc); + struct gpio_chip *chip = irq_desc_get_chip_data(w->irq_desc); + struct irq_data *data = irq_desc_get_irq_data(w->irq_desc); + unsigned int gpio = irqd_to_hwirq(data); + + local_irq_disable(); + chained_irq_enter(parent_chip, w->irq_desc); + generic_handle_domain_irq(chip->irq.domain, gpio); + chained_irq_exit(parent_chip, w->irq_desc); + local_irq_enable(); + + kfree(w); +} + +static void ocelot_irq_unmask_level(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct ocelot_pinctrl *info = gpiochip_get_data(chip); + struct irq_desc *desc = irq_data_to_desc(data); + unsigned int gpio = irqd_to_hwirq(data); + unsigned int bit = BIT(gpio % 32); + bool ack = false, active = false; + u8 trigger_level; + int val; + + trigger_level = irqd_get_trigger_type(data); + + /* Check if the interrupt line is still active. */ + regmap_read(info->map, REG(OCELOT_GPIO_IN, info, gpio), &val); + if ((!(val & bit) && trigger_level == IRQ_TYPE_LEVEL_LOW) || + (val & bit && trigger_level == IRQ_TYPE_LEVEL_HIGH)) + active = true; + + /* + * Check if the interrupt controller has seen any changes in the + * interrupt line. + */ + regmap_read(info->map, REG(OCELOT_GPIO_INTR, info, gpio), &val); + if (val & bit) + ack = true; + + /* Enable the interrupt now */ + gpiochip_enable_irq(chip, gpio); + regmap_update_bits(info->map, REG(OCELOT_GPIO_INTR_ENA, info, gpio), + bit, bit); + + /* + * In case the interrupt line is still active and the interrupt + * controller has not seen any changes in the interrupt line, then it + * means that there happen another interrupt while the line was active. + * So we missed that one, so we need to kick the interrupt again + * handler. + */ + if (active && !ack) { + struct ocelot_irq_work *work; + + work = kmalloc(sizeof(*work), GFP_ATOMIC); + if (!work) + return; + + work->irq_desc = desc; + INIT_WORK(&work->irq_work, ocelot_irq_work); + queue_work(info->wq, &work->irq_work); + } +} + static void ocelot_irq_unmask(struct irq_data *data) { struct gpio_chip *chip = irq_data_get_irq_chip_data(data); @@ -1836,13 +1911,12 @@ static void ocelot_irq_ack(struct irq_data *data) static int ocelot_irq_set_type(struct irq_data *data, unsigned int type); -static struct irq_chip ocelot_eoi_irqchip = { +static struct irq_chip ocelot_level_irqchip = { .name = "gpio", .irq_mask = ocelot_irq_mask, - .irq_eoi = ocelot_irq_ack, - .irq_unmask = ocelot_irq_unmask, - .flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED | - IRQCHIP_IMMUTABLE, + .irq_ack = ocelot_irq_ack, + .irq_unmask = ocelot_irq_unmask_level, + .flags = IRQCHIP_IMMUTABLE, .irq_set_type = ocelot_irq_set_type, GPIOCHIP_IRQ_RESOURCE_HELPERS }; @@ -1859,14 +1933,9 @@ static struct irq_chip ocelot_irqchip = { static int ocelot_irq_set_type(struct irq_data *data, unsigned int type) { - type &= IRQ_TYPE_SENSE_MASK; - - if (!(type & (IRQ_TYPE_EDGE_BOTH | IRQ_TYPE_LEVEL_HIGH))) - return -EINVAL; - - if (type & IRQ_TYPE_LEVEL_HIGH) - irq_set_chip_handler_name_locked(data, &ocelot_eoi_irqchip, - handle_fasteoi_irq, NULL); + if (type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) + irq_set_chip_handler_name_locked(data, &ocelot_level_irqchip, + handle_level_irq, NULL); if (type & IRQ_TYPE_EDGE_BOTH) irq_set_chip_handler_name_locked(data, &ocelot_irqchip, handle_edge_irq, NULL); @@ -1996,6 +2065,10 @@ static int ocelot_pinctrl_probe(struct platform_device *pdev) if (!info->desc) return -ENOMEM; + info->wq = alloc_ordered_workqueue("ocelot_ordered", 0); + if (!info->wq) + return -ENOMEM; + info->pincfg_data = &data->pincfg_data; reset = devm_reset_control_get_optional_shared(dev, "switch"); @@ -2018,7 +2091,7 @@ static int ocelot_pinctrl_probe(struct platform_device *pdev) dev_err(dev, "Failed to create regmap\n"); return PTR_ERR(info->map); } - dev_set_drvdata(dev, info->map); + dev_set_drvdata(dev, info); info->dev = dev; /* Pinconf registers */ @@ -2043,6 +2116,15 @@ static int ocelot_pinctrl_probe(struct platform_device *pdev) return 0; } +static int ocelot_pinctrl_remove(struct platform_device *pdev) +{ + struct ocelot_pinctrl *info = platform_get_drvdata(pdev); + + destroy_workqueue(info->wq); + + return 0; +} + static struct platform_driver ocelot_pinctrl_driver = { .driver = { .name = "pinctrl-ocelot", @@ -2050,6 +2132,7 @@ static struct platform_driver ocelot_pinctrl_driver = { .suppress_bind_attrs = true, }, .probe = ocelot_pinctrl_probe, + .remove = ocelot_pinctrl_remove, }; module_platform_driver(ocelot_pinctrl_driver); MODULE_LICENSE("Dual MIT/GPL");