From 7a1eafa301c7efaa9d8529569502d34ff84aead3 Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Tue, 21 Apr 2020 19:28:32 +0530 Subject: [PATCH 1/2] pinctrl: qcom: Use TLMM and PDC for dual edge wakeup interrupts Although PDC has the capability to route falling edge interrupt to GIC by inverting the input signal, falling edge of the interrupts marked as dual edge type is routed as is. This causes GIC to ignore to falling edge of these interrupts when the system is active. During APSS system sleep once PDC starts monitoring these interrupts, PDC always replays a rising edge irrespective of which edge woke the APSS from sleep. So, the issue does not happen during the system sleep. To address the issue when system is active, route the falling_edge of these interrupts through one of the direct connect interrupts available for APSS. The original GIC interrupt is configured as dual edge at PDC, rising edge at GIC (same as earlier, this does not change). This solution requires additional programming of 2 registers: TLMM__DIR_CONN_INTRn_CFG_HMSS - program GPIO_SEL with the gpio number and set POLAIRTY to 0 for inverting the falling edge TLMM_GPIO_INTR_CFGn - set DIR_CONN_EN bit to 1 for enabling direct connect for gpio. Change-Id: Iaa9dfe391ef4942cd8b12fd1e7d8f9c238947af2 Signed-off-by: Archana Sathyakumar Signed-off-by: Maulik Shah --- drivers/pinctrl/qcom/pinctrl-msm.c | 216 +++++++++++++++++++++++++- drivers/pinctrl/qcom/pinctrl-msm.h | 16 ++ drivers/pinctrl/qcom/pinctrl-sm8150.c | 33 ++++ 3 files changed, 259 insertions(+), 6 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index ca74b66024ae..e08cc193d88a 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -45,6 +45,7 @@ * @chip: gpiochip handle. * @restart_nb: restart notifier block. * @irq: parent irq for the TLMM irq_chip. + * @n_dir_conns: The number of pins directly connected to GIC. * @lock: Spinlock to protect register resources as well * as msm_pinctrl data structures. * @enabled_irqs: Bitmap of currently enabled irqs. @@ -63,6 +64,7 @@ struct msm_pinctrl { struct irq_chip irq_chip; int irq; + int n_dir_conns; raw_spinlock_t lock; @@ -713,16 +715,45 @@ static void msm_gpio_update_dual_edge_pos(struct msm_pinctrl *pctrl, val, val2); } +static bool is_gpio_dual_edge(struct irq_data *d, irq_hw_number_t *irq) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + struct msm_dir_conn *dc; + int i; + + for (i = pctrl->n_dir_conns; i > 0; i--) { + dc = &pctrl->soc->dir_conn[i]; + + if (dc->gpio == d->hwirq) { + *irq = dc->irq; + return true; + } + } + + return false; +} + static void msm_gpio_irq_mask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct msm_pinctrl *pctrl = gpiochip_get_data(gc); const struct msm_pingroup *g; unsigned long flags; + struct irq_data *dir_conn_data; + irq_hw_number_t dir_conn_irq = 0; u32 val; - if (d->parent_data) + if (d->parent_data) { + if (is_gpio_dual_edge(d, &dir_conn_irq)) { + dir_conn_data = irq_get_irq_data(dir_conn_irq); + if (!dir_conn_data) + return; + + dir_conn_data->chip->irq_mask(dir_conn_data); + } irq_chip_mask_parent(d); + } if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) return; @@ -768,11 +799,21 @@ static void msm_gpio_irq_clear_unmask(struct irq_data *d, bool status_clear) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct msm_pinctrl *pctrl = gpiochip_get_data(gc); const struct msm_pingroup *g; + struct irq_data *dir_conn_data; + irq_hw_number_t dir_conn_irq = 0; unsigned long flags; u32 val; - if (d->parent_data) + if (d->parent_data) { + if (is_gpio_dual_edge(d, &dir_conn_irq)) { + dir_conn_data = irq_get_irq_data(dir_conn_irq); + if (!dir_conn_data) + return; + + dir_conn_data->chip->irq_unmask(dir_conn_data); + } irq_chip_unmask_parent(d); + } if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) return; @@ -806,6 +847,8 @@ static void msm_gpio_irq_enable(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + struct irq_data *dir_conn_data; + irq_hw_number_t dir_conn_irq = 0; /* * Clear the interrupt that may be pending before we enable @@ -817,6 +860,15 @@ static void msm_gpio_irq_enable(struct irq_data *d) * GIC needs to be cleared before enabling. */ if (d->parent_data) { + if (is_gpio_dual_edge(d, &dir_conn_irq)) { + dir_conn_data = irq_get_irq_data(dir_conn_irq); + if (!dir_conn_data) + return; + + irq_set_irqchip_state(dir_conn_irq, + IRQCHIP_STATE_PENDING, 0); + dir_conn_data->chip->irq_unmask(dir_conn_data); + } irq_chip_set_parent_state(d, IRQCHIP_STATE_PENDING, 0); irq_chip_enable_parent(d); } @@ -831,9 +883,19 @@ static void msm_gpio_irq_disable(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + struct irq_data *dir_conn_data; + irq_hw_number_t dir_conn_irq = 0; - if (d->parent_data) + if (d->parent_data) { + if (is_gpio_dual_edge(d, &dir_conn_irq)) { + dir_conn_data = irq_get_irq_data(dir_conn_irq); + if (!dir_conn_data) + return; + + dir_conn_data->chip->irq_mask(dir_conn_data); + } irq_chip_disable_parent(d); + } if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) return; @@ -874,16 +936,147 @@ static void msm_gpio_irq_ack(struct irq_data *d) raw_spin_unlock_irqrestore(&pctrl->lock, flags); } +static void msm_dirconn_cfg_reg(struct irq_data *d, u32 offset) +{ + u32 val; + const struct msm_pingroup *g; + unsigned long flags; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + + raw_spin_lock_irqsave(&pctrl->lock, flags); + g = &pctrl->soc->groups[d->hwirq]; + + val = (d->hwirq) & 0xFF; + + writel_relaxed(val, pctrl->regs[g->tile] + g->dir_conn_reg + + (offset * 4)); + + val = msm_readl_intr_cfg(pctrl, g); + val |= BIT(g->dir_conn_en_bit); + + msm_writel_intr_cfg(val, pctrl, g); + raw_spin_unlock_irqrestore(&pctrl->lock, flags); +} + +static void msm_dirconn_uncfg_reg(struct irq_data *d, u32 offset) +{ + u32 val = 0; + const struct msm_pingroup *g; + unsigned long flags; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + + raw_spin_lock_irqsave(&pctrl->lock, flags); + g = &pctrl->soc->groups[d->hwirq]; + + writel_relaxed(val, pctrl->regs + g->dir_conn_reg + (offset * 4)); + val = readl_relaxed(pctrl->regs + g->intr_cfg_reg); + val &= ~BIT(g->dir_conn_en_bit); + writel_relaxed(val, pctrl->regs + g->intr_cfg_reg); + raw_spin_unlock_irqrestore(&pctrl->lock, flags); +} + +static int select_dir_conn_mux(struct irq_data *d, irq_hw_number_t *irq, + bool add) +{ + struct msm_dir_conn *dc = NULL; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + int i, n_dir_conns = pctrl->n_dir_conns; + + for (i = n_dir_conns; i > 0; i--) { + dc = &pctrl->soc->dir_conn[i]; + if (dc->gpio == d->hwirq && !add) { + *irq = dc->irq; + dc->gpio = -1; + return n_dir_conns - i; + } + + if (dc->gpio == -1 && add) { + dc->gpio = (int)d->hwirq; + *irq = dc->irq; + return n_dir_conns - i; + } + } + + pr_err("%s: No direct connects selected for interrupt %lu\n", + __func__, d->hwirq); + return -EBUSY; +} + +static void msm_gpio_dirconn_handler(struct irq_desc *desc) +{ + struct irq_data *irqd = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + + if (!irqd) + return; + + chained_irq_enter(chip, desc); + generic_handle_irq(irqd->irq); + chained_irq_exit(chip, desc); + irq_set_irqchip_state(irq_desc_get_irq_data(desc)->irq, + IRQCHIP_STATE_ACTIVE, 0); +} + +static void add_dirconn_tlmm(struct irq_data *d, struct msm_pinctrl *pctrl) +{ + struct irq_data *dir_conn_data = NULL; + int offset = 0; + irq_hw_number_t irq = 0; + + offset = select_dir_conn_mux(d, &irq, true); + if (offset < 0) + return; + + msm_dirconn_cfg_reg(d, offset); + irq_set_handler_data(irq, d); + dir_conn_data = irq_get_irq_data(irq); + + if (!dir_conn_data) + return; + + dir_conn_data->chip->irq_unmask(dir_conn_data); +} + +static void remove_dirconn_tlmm(struct irq_data *d, irq_hw_number_t irq) +{ + struct irq_data *dir_conn_data = NULL; + int offset = 0; + + offset = select_dir_conn_mux(d, &irq, false); + if (offset < 0) + return; + + msm_dirconn_uncfg_reg(d, offset); + irq_set_handler_data(irq, NULL); + dir_conn_data = irq_get_irq_data(irq); + + if (!dir_conn_data) + return; + + dir_conn_data->chip->irq_mask(dir_conn_data); +} + static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct msm_pinctrl *pctrl = gpiochip_get_data(gc); const struct msm_pingroup *g; + irq_hw_number_t irq = 0; unsigned long flags; u32 val; - if (d->parent_data) + if (d->parent_data) { + if (pctrl->n_dir_conns > 0) { + if (type == IRQ_TYPE_EDGE_BOTH) + add_dirconn_tlmm(d, pctrl); + else if (is_gpio_dual_edge(d, &irq)) + remove_dirconn_tlmm(d, irq); + } irq_chip_set_type_parent(d, type); + } if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) return 0; @@ -1309,8 +1502,7 @@ int msm_pinctrl_probe(struct platform_device *pdev, { struct msm_pinctrl *pctrl; struct resource *res; - int ret; - int i; + int ret, i, num_irq, irq; msm_pinctrl_data = pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); @@ -1362,6 +1554,18 @@ int msm_pinctrl_probe(struct platform_device *pdev, if (ret) return ret; + num_irq = platform_irq_count(pdev); + + for (i = 1; i < num_irq; i++) { + struct msm_dir_conn *dc = &soc_data->dir_conn[i]; + + irq = platform_get_irq(pdev, i); + dc->irq = irq; + __irq_set_handler(irq, msm_gpio_dirconn_handler, false, NULL); + irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING); + pctrl->n_dir_conns++; + } + platform_set_drvdata(pdev, pctrl); dev_dbg(&pdev->dev, "Probed Qualcomm pinctrl driver\n"); diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h index 1ae9abcf99a5..98b65bdb8155 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.h +++ b/drivers/pinctrl/qcom/pinctrl-msm.h @@ -38,6 +38,7 @@ struct msm_function { * @intr_status_reg: Offset of the register holding the status bits for this group. * @intr_target_reg: Offset of the register specifying routing of the interrupts * from this group. + * @dir_conn_reg: Offset of the register hmss setup in tile. * @mux_bit: Offset in @ctl_reg for the pinmux function selection. * @pull_bit: Offset in @ctl_reg for the bias configuration. * @drv_bit: Offset in @ctl_reg for the drive strength configuration. @@ -56,6 +57,7 @@ struct msm_function { * @intr_detection_width: Number of bits used for specifying interrupt type, * Should be 2 for SoCs that can detect both edges in hardware, * otherwise 1. + * @dir_conn_en_bit: Offset in @intr_cfg_reg for direct connect enable bit * @wake_reg: Offset of the WAKEUP_INT_EN register from base tile * @wake_bit: Bit number for the corresponding gpio */ @@ -72,6 +74,7 @@ struct msm_pingroup { u32 intr_cfg_reg; u32 intr_status_reg; u32 intr_target_reg; + u32 dir_conn_reg; unsigned int tile:2; @@ -96,11 +99,22 @@ struct msm_pingroup { unsigned intr_polarity_bit:5; unsigned intr_detection_bit:5; unsigned intr_detection_width:5; + unsigned dir_conn_en_bit:8; u32 wake_reg; unsigned int wake_bit; }; +/** + * struct msm_dir_conn - TLMM Direct GPIO connect configuration + * @gpio: GPIO pin number + * @irq: The GIC interrupt that the pin is connected to + */ +struct msm_dir_conn { + int gpio; + int irq; +}; + /* * struct pinctrl_qup - Qup mode configuration * @mode: Qup i3c mode @@ -133,6 +147,7 @@ struct msm_gpio_wakeirq_map { * @pull_no_keeper: The SoC does not support keeper bias. * @wakeirq_map: The map of wakeup capable GPIOs and the pin at PDC/MPM * @nwakeirq_map: The number of entries in @hierarchy_map + * @dir_conn: An array describing all the pins directly connected to GIC. */ struct msm_pinctrl_soc_data { const struct pinctrl_pin_desc *pins; @@ -150,6 +165,7 @@ struct msm_pinctrl_soc_data { unsigned int nwakeirq_map; struct pinctrl_qup *qup_regs; unsigned int nqup_regs; + struct msm_dir_conn *dir_conn; }; extern const struct dev_pm_ops msm_pinctrl_dev_pm_ops; diff --git a/drivers/pinctrl/qcom/pinctrl-sm8150.c b/drivers/pinctrl/qcom/pinctrl-sm8150.c index 63a625ac6273..a1844ad2bb6b 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8150.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8150.c @@ -22,6 +22,11 @@ enum { WEST }; +#define HMSS_WEST 0x031BB000 +#define HMSS_EAST 0x035B7000 +#define HMSS_NORTH 0x039BC000 +#define HMSS_SOUTH 0x03DBE000 + #define FUNCTION(fname) \ [msm_mux_##fname] = { \ .name = #fname, \ @@ -53,6 +58,10 @@ enum { .intr_status_reg = 0x1000 * id + 0xc, \ .intr_target_reg = 0x1000 * id + 0x8, \ .tile = _tile, \ + .dir_conn_reg = _tile == WEST ? HMSS_WEST : \ + _tile == EAST ? HMSS_EAST : \ + _tile == NORTH ? HMSS_NORTH : \ + HMSS_SOUTH, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -69,6 +78,7 @@ enum { .intr_polarity_bit = 1, \ .intr_detection_bit = 2, \ .intr_detection_width = 2, \ + .dir_conn_en_bit = 8, \ } #define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \ @@ -1502,6 +1512,26 @@ static const struct msm_pingroup sm8150_groups[] = { [178] = SDC_QDSD_PINGROUP(sdc2_data, 0xB2000, 9, 0), }; +static const struct msm_gpio_wakeirq_map sm8150_pdc_map[] = { + { 3, 31 }, { 5, 32 }, { 8, 33 }, { 9, 34 }, { 10, 100 }, + { 24, 37 }, { 26, 38 }, { 27, 41 }, { 28, 42 }, { 30, 39 }, + { 37, 44 }, { 38, 30 }, { 39, 118 }, { 41, 47 }, { 42, 48 }, + { 47, 49 }, { 48, 51 }, { 49, 53 }, { 50, 52 }, { 51, 116 }, + { 54, 55 }, { 55, 56 }, { 56, 57 }, { 58, 58 }, { 60, 60 }, + { 68, 62 }, { 70, 63 }, { 76, 71 }, { 77, 66 }, { 81, 64 }, + { 86, 67 }, { 87, 84 }, { 88, 117 }, { 90, 69 }, { 91, 70 }, + { 95, 72 }, { 96, 73 }, { 97, 74 }, { 101, 40 }, { 103, 77 }, + { 108, 79 }, { 112, 80 }, { 113, 81 }, { 114, 82 }, { 117, 85 }, + { 119, 87 }, { 120, 88 }, { 121, 89 }, { 122, 90 }, { 123, 91 }, + { 125, 93 }, { 129, 94 }, { 132, 105 }, { 133, 83 }, { 134, 36 }, + { 142, 103 }, { 144, 115 }, { 147, 102 }, { 150, 107 }, { 152, 108 }, +}; + +static struct msm_dir_conn sm8150_dir_conn[] = { + {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, + {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0} +}; + static const struct msm_pinctrl_soc_data sm8150_pinctrl = { .pins = sm8150_pins, .npins = ARRAY_SIZE(sm8150_pins), @@ -1512,6 +1542,9 @@ static const struct msm_pinctrl_soc_data sm8150_pinctrl = { .ngpios = 176, .tiles = sm8150_tiles, .ntiles = ARRAY_SIZE(sm8150_tiles), + .wakeirq_map = sm8150_pdc_map, + .nwakeirq_map = ARRAY_SIZE(sm8150_pdc_map), + .dir_conn = sm8150_dir_conn, }; static int sm8150_pinctrl_probe(struct platform_device *pdev) From 45863ee2a3afb21f976b441722a9737bce9501dd Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Thu, 2 Apr 2020 19:37:56 +0530 Subject: [PATCH 2/2] drivers: irqchip: qcom-pdc: Add PDC IRQ chip support for sm8150 Add PDC interrupt controller chip support for sm8150. Change-Id: I5fc53d3567f779961d6afe7fe36d47bbaa0f9590 Signed-off-by: Maulik Shah --- drivers/irqchip/qcom-pdc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c index 8be3b71098bd..acf301d1970c 100644 --- a/drivers/irqchip/qcom-pdc.c +++ b/drivers/irqchip/qcom-pdc.c @@ -542,7 +542,6 @@ fail: return ret; } - #ifdef MODULE static int qcom_pdc_probe(struct platform_device *pdev) { @@ -554,6 +553,7 @@ static int qcom_pdc_probe(struct platform_device *pdev) static const struct of_device_id qcom_pdc_match_table[] = { { .compatible = "qcom,lahaina-pdc" }, { .compatible = "qcom,shima-pdc" }, + { .compatible = "qcom,sm8150-pdc" }, { .compatible = "qcom,yupik-pdc" }, {} }; @@ -571,6 +571,7 @@ module_platform_driver(qcom_pdc_driver); IRQCHIP_DECLARE(qcom_pdc, "qcom,pdc", qcom_pdc_init); IRQCHIP_DECLARE(pdc_lahaina, "qcom,lahaina-pdc", qcom_pdc_init); IRQCHIP_DECLARE(pdc_shima, "qcom,shima-pdc", qcom_pdc_init); +IRQCHIP_DECLARE(pdc_sm8150, "qcom,sm8150-pdc", qcom_pdc_init); IRQCHIP_DECLARE(pdc_yupik, "qcom,yupik-pdc", qcom_pdc_init); IRQCHIP_DECLARE(pdc_sdxlemur, "qcom,sdxlemur-pdc", qcom_pdc_init); #endif