Loading drivers/irqchip/qcom-pdc.c +19 −8 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #include <linux/err.h> Loading Loading @@ -39,6 +39,7 @@ struct pdc_pin_region { u32 cnt; }; DECLARE_BITMAP(pdc_wake_irqs, PDC_MAX_IRQS); static DEFINE_RAW_SPINLOCK(pdc_lock); static void __iomem *pdc_base, *pdc_cfg_base; static struct pdc_pin_region *pdc_region; Loading Loading @@ -73,13 +74,20 @@ static void pdc_enable_intr(struct irq_data *d, bool on) raw_spin_unlock(&pdc_lock); } static void qcom_pdc_gic_disable(struct irq_data *d) static int qcom_pdc_gic_set_wake(struct irq_data *d, unsigned int on) { if (d->hwirq == GPIO_NO_WAKE_IRQ) return; return 0; pdc_enable_intr(d, false); irq_chip_disable_parent(d); if (on) { pdc_enable_intr(d, true); set_bit(d->hwirq, pdc_wake_irqs); irq_chip_enable_parent(d); } else { clear_bit(d->hwirq, pdc_wake_irqs); } return irq_chip_set_wake_parent(d, on); } static int qcom_pdc_gic_get_irqchip_state(struct irq_data *d, Loading Loading @@ -116,6 +124,10 @@ static void qcom_pdc_gic_mask(struct irq_data *d) ipc_log_string(pdc_ipc_log, "PIN=%d mask", d->hwirq); irq_chip_mask_parent(d); /* Mask at PDC if not a wake irq */ if (!test_bit(d->hwirq, pdc_wake_irqs)) pdc_enable_intr(d, false); } static void qcom_pdc_gic_unmask(struct irq_data *d) Loading Loading @@ -241,15 +253,14 @@ static struct irq_chip qcom_pdc_gic_chip = { .irq_eoi = irq_chip_eoi_parent, .irq_mask = qcom_pdc_gic_mask, .irq_unmask = qcom_pdc_gic_unmask, .irq_disable = qcom_pdc_gic_disable, .irq_enable = qcom_pdc_gic_enable, .irq_get_irqchip_state = qcom_pdc_gic_get_irqchip_state, .irq_set_irqchip_state = qcom_pdc_gic_set_irqchip_state, .irq_retrigger = irq_chip_retrigger_hierarchy, .irq_set_type = qcom_pdc_gic_set_type, .irq_set_wake = qcom_pdc_gic_set_wake, .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SET_TYPE_MASKED | IRQCHIP_SKIP_SET_WAKE, IRQCHIP_SET_TYPE_MASKED, .irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent, .irq_set_affinity = irq_chip_set_affinity_parent, }; Loading drivers/pinctrl/qcom/pinctrl-lagoon.c +8 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ .intr_cfg_reg = REG_BASE + 0x8 + REG_SIZE * id, \ .intr_status_reg = REG_BASE + 0xc + REG_SIZE * id, \ .intr_target_reg = REG_BASE + 0x8 + REG_SIZE * id, \ .dir_conn_reg = REG_BASE + 0xBF000,\ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ Loading @@ -58,6 +59,7 @@ .intr_polarity_bit = 1, \ .intr_detection_bit = 2, \ .intr_detection_width = 2, \ .dir_conn_en_bit = 8, \ .wake_reg = REG_BASE + wake_off, \ .wake_bit = bit, \ } Loading Loading @@ -1618,6 +1620,11 @@ static const int lagoon_reserved_gpios[] = { 13, 14, 15, 16, 45, 46, 56, 57, -1 }; static struct msm_dir_conn lagoon_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 lagoon_pinctrl = { .pins = lagoon_pins, .npins = ARRAY_SIZE(lagoon_pins), Loading @@ -1627,6 +1634,7 @@ static const struct msm_pinctrl_soc_data lagoon_pinctrl = { .ngroups = ARRAY_SIZE(lagoon_groups), .reserved_gpios = lagoon_reserved_gpios, .ngpios = 156, .dir_conn = lagoon_dir_conn, }; static int lagoon_pinctrl_probe(struct platform_device *pdev) Loading drivers/pinctrl/qcom/pinctrl-msm.c +203 −21 Original line number Diff line number Diff line /* * Copyright (c) 2013, Sony Mobile Communications AB. * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -68,6 +68,7 @@ struct msm_pinctrl { struct irq_chip irq_chip; int irq; int n_dir_conns; raw_spinlock_t lock; Loading Loading @@ -633,6 +634,25 @@ 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); Loading Loading @@ -714,9 +734,19 @@ 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); 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_mask_parent(d); } if (test_bit(d->hwirq, pctrl->wakeup_masked_irqs)) return; Loading @@ -728,34 +758,32 @@ static void msm_gpio_irq_unmask(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) irq_chip_unmask_parent(d); if (test_bit(d->hwirq, pctrl->wakeup_masked_irqs)) 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; _msm_gpio_irq_unmask(d, false); dir_conn_data->chip->irq_unmask(dir_conn_data); } irq_chip_unmask_parent(d); } 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); if (d->parent_data) irq_chip_disable_parent(d); if (test_bit(d->hwirq, pctrl->wakeup_masked_irqs)) return; _msm_gpio_irq_mask(d); _msm_gpio_irq_unmask(d, false); } 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; if (d->parent_data) { /* Loading @@ -767,6 +795,18 @@ static void msm_gpio_irq_enable(struct irq_data *d) * the interrupt from being latched at the GIC. The state at * GIC needs to be cleared before enabling. */ 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); if (dir_conn_data->chip->irq_enable) dir_conn_data->chip->irq_enable(dir_conn_data); else dir_conn_data->chip->irq_unmask(dir_conn_data); } irq_chip_set_parent_state(d, IRQCHIP_STATE_PENDING, 0); irq_chip_enable_parent(d); } Loading Loading @@ -806,18 +846,148 @@ 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->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 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; g = &pctrl->soc->groups[d->hwirq]; 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); } /* Monitored by parent wakeup controller? Keep masked */ if (test_bit(d->hwirq, pctrl->wakeup_masked_irqs)) Loading Loading @@ -1145,7 +1315,6 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) pctrl->irq_chip.name = "msmgpio"; pctrl->irq_chip.irq_eoi = irq_chip_eoi_parent; pctrl->irq_chip.irq_enable = msm_gpio_irq_enable; pctrl->irq_chip.irq_disable = msm_gpio_irq_disable; pctrl->irq_chip.irq_mask = msm_gpio_irq_mask; pctrl->irq_chip.irq_unmask = msm_gpio_irq_unmask; pctrl->irq_chip.irq_ack = msm_gpio_irq_ack; Loading @@ -1153,7 +1322,8 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) pctrl->irq_chip.irq_set_wake = msm_gpio_irq_set_wake; pctrl->irq_chip.irq_set_affinity = msm_gpio_irq_set_affinity; pctrl->irq_chip.irq_set_vcpu_affinity = msm_gpio_irq_set_vcpu_affinity; pctrl->irq_chip.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SET_TYPE_MASKED; chip->irq.chip = &pctrl->irq_chip; chip->irq.handler = handle_edge_irq; chip->irq.default_type = IRQ_TYPE_NONE; Loading Loading @@ -1361,7 +1531,7 @@ int msm_pinctrl_probe(struct platform_device *pdev, { struct msm_pinctrl *pctrl; struct resource *res; int ret; int ret, i, num_irq, irq; msm_pinctrl_data = pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); Loading Loading @@ -1406,6 +1576,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); register_syscore_ops(&msm_pinctrl_pm_ops); Loading drivers/pinctrl/qcom/pinctrl-msm.h +18 −0 Original line number Diff line number Diff line Loading @@ -45,6 +45,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. Loading @@ -63,6 +64,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 */ Loading @@ -79,6 +81,7 @@ struct msm_pingroup { u32 intr_cfg_reg; u32 intr_status_reg; u32 intr_target_reg; u32 dir_conn_reg; unsigned mux_bit:5; Loading @@ -101,11 +104,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 Loading @@ -126,6 +140,9 @@ struct pinctrl_qup { * @ngroups: The numbmer of entries in @groups. * @ngpio: The number of pingroups the driver should expose as GPIOs. * @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; Loading @@ -139,6 +156,7 @@ struct msm_pinctrl_soc_data { struct pinctrl_qup *qup_regs; unsigned int nqup_regs; const int *reserved_gpios; struct msm_dir_conn *dir_conn; }; int msm_pinctrl_probe(struct platform_device *pdev, Loading Loading
drivers/irqchip/qcom-pdc.c +19 −8 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #include <linux/err.h> Loading Loading @@ -39,6 +39,7 @@ struct pdc_pin_region { u32 cnt; }; DECLARE_BITMAP(pdc_wake_irqs, PDC_MAX_IRQS); static DEFINE_RAW_SPINLOCK(pdc_lock); static void __iomem *pdc_base, *pdc_cfg_base; static struct pdc_pin_region *pdc_region; Loading Loading @@ -73,13 +74,20 @@ static void pdc_enable_intr(struct irq_data *d, bool on) raw_spin_unlock(&pdc_lock); } static void qcom_pdc_gic_disable(struct irq_data *d) static int qcom_pdc_gic_set_wake(struct irq_data *d, unsigned int on) { if (d->hwirq == GPIO_NO_WAKE_IRQ) return; return 0; pdc_enable_intr(d, false); irq_chip_disable_parent(d); if (on) { pdc_enable_intr(d, true); set_bit(d->hwirq, pdc_wake_irqs); irq_chip_enable_parent(d); } else { clear_bit(d->hwirq, pdc_wake_irqs); } return irq_chip_set_wake_parent(d, on); } static int qcom_pdc_gic_get_irqchip_state(struct irq_data *d, Loading Loading @@ -116,6 +124,10 @@ static void qcom_pdc_gic_mask(struct irq_data *d) ipc_log_string(pdc_ipc_log, "PIN=%d mask", d->hwirq); irq_chip_mask_parent(d); /* Mask at PDC if not a wake irq */ if (!test_bit(d->hwirq, pdc_wake_irqs)) pdc_enable_intr(d, false); } static void qcom_pdc_gic_unmask(struct irq_data *d) Loading Loading @@ -241,15 +253,14 @@ static struct irq_chip qcom_pdc_gic_chip = { .irq_eoi = irq_chip_eoi_parent, .irq_mask = qcom_pdc_gic_mask, .irq_unmask = qcom_pdc_gic_unmask, .irq_disable = qcom_pdc_gic_disable, .irq_enable = qcom_pdc_gic_enable, .irq_get_irqchip_state = qcom_pdc_gic_get_irqchip_state, .irq_set_irqchip_state = qcom_pdc_gic_set_irqchip_state, .irq_retrigger = irq_chip_retrigger_hierarchy, .irq_set_type = qcom_pdc_gic_set_type, .irq_set_wake = qcom_pdc_gic_set_wake, .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SET_TYPE_MASKED | IRQCHIP_SKIP_SET_WAKE, IRQCHIP_SET_TYPE_MASKED, .irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent, .irq_set_affinity = irq_chip_set_affinity_parent, }; Loading
drivers/pinctrl/qcom/pinctrl-lagoon.c +8 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ .intr_cfg_reg = REG_BASE + 0x8 + REG_SIZE * id, \ .intr_status_reg = REG_BASE + 0xc + REG_SIZE * id, \ .intr_target_reg = REG_BASE + 0x8 + REG_SIZE * id, \ .dir_conn_reg = REG_BASE + 0xBF000,\ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ Loading @@ -58,6 +59,7 @@ .intr_polarity_bit = 1, \ .intr_detection_bit = 2, \ .intr_detection_width = 2, \ .dir_conn_en_bit = 8, \ .wake_reg = REG_BASE + wake_off, \ .wake_bit = bit, \ } Loading Loading @@ -1618,6 +1620,11 @@ static const int lagoon_reserved_gpios[] = { 13, 14, 15, 16, 45, 46, 56, 57, -1 }; static struct msm_dir_conn lagoon_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 lagoon_pinctrl = { .pins = lagoon_pins, .npins = ARRAY_SIZE(lagoon_pins), Loading @@ -1627,6 +1634,7 @@ static const struct msm_pinctrl_soc_data lagoon_pinctrl = { .ngroups = ARRAY_SIZE(lagoon_groups), .reserved_gpios = lagoon_reserved_gpios, .ngpios = 156, .dir_conn = lagoon_dir_conn, }; static int lagoon_pinctrl_probe(struct platform_device *pdev) Loading
drivers/pinctrl/qcom/pinctrl-msm.c +203 −21 Original line number Diff line number Diff line /* * Copyright (c) 2013, Sony Mobile Communications AB. * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -68,6 +68,7 @@ struct msm_pinctrl { struct irq_chip irq_chip; int irq; int n_dir_conns; raw_spinlock_t lock; Loading Loading @@ -633,6 +634,25 @@ 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); Loading Loading @@ -714,9 +734,19 @@ 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); 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_mask_parent(d); } if (test_bit(d->hwirq, pctrl->wakeup_masked_irqs)) return; Loading @@ -728,34 +758,32 @@ static void msm_gpio_irq_unmask(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) irq_chip_unmask_parent(d); if (test_bit(d->hwirq, pctrl->wakeup_masked_irqs)) 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; _msm_gpio_irq_unmask(d, false); dir_conn_data->chip->irq_unmask(dir_conn_data); } irq_chip_unmask_parent(d); } 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); if (d->parent_data) irq_chip_disable_parent(d); if (test_bit(d->hwirq, pctrl->wakeup_masked_irqs)) return; _msm_gpio_irq_mask(d); _msm_gpio_irq_unmask(d, false); } 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; if (d->parent_data) { /* Loading @@ -767,6 +795,18 @@ static void msm_gpio_irq_enable(struct irq_data *d) * the interrupt from being latched at the GIC. The state at * GIC needs to be cleared before enabling. */ 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); if (dir_conn_data->chip->irq_enable) dir_conn_data->chip->irq_enable(dir_conn_data); else dir_conn_data->chip->irq_unmask(dir_conn_data); } irq_chip_set_parent_state(d, IRQCHIP_STATE_PENDING, 0); irq_chip_enable_parent(d); } Loading Loading @@ -806,18 +846,148 @@ 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->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 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; g = &pctrl->soc->groups[d->hwirq]; 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); } /* Monitored by parent wakeup controller? Keep masked */ if (test_bit(d->hwirq, pctrl->wakeup_masked_irqs)) Loading Loading @@ -1145,7 +1315,6 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) pctrl->irq_chip.name = "msmgpio"; pctrl->irq_chip.irq_eoi = irq_chip_eoi_parent; pctrl->irq_chip.irq_enable = msm_gpio_irq_enable; pctrl->irq_chip.irq_disable = msm_gpio_irq_disable; pctrl->irq_chip.irq_mask = msm_gpio_irq_mask; pctrl->irq_chip.irq_unmask = msm_gpio_irq_unmask; pctrl->irq_chip.irq_ack = msm_gpio_irq_ack; Loading @@ -1153,7 +1322,8 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) pctrl->irq_chip.irq_set_wake = msm_gpio_irq_set_wake; pctrl->irq_chip.irq_set_affinity = msm_gpio_irq_set_affinity; pctrl->irq_chip.irq_set_vcpu_affinity = msm_gpio_irq_set_vcpu_affinity; pctrl->irq_chip.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SET_TYPE_MASKED; chip->irq.chip = &pctrl->irq_chip; chip->irq.handler = handle_edge_irq; chip->irq.default_type = IRQ_TYPE_NONE; Loading Loading @@ -1361,7 +1531,7 @@ int msm_pinctrl_probe(struct platform_device *pdev, { struct msm_pinctrl *pctrl; struct resource *res; int ret; int ret, i, num_irq, irq; msm_pinctrl_data = pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); Loading Loading @@ -1406,6 +1576,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); register_syscore_ops(&msm_pinctrl_pm_ops); Loading
drivers/pinctrl/qcom/pinctrl-msm.h +18 −0 Original line number Diff line number Diff line Loading @@ -45,6 +45,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. Loading @@ -63,6 +64,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 */ Loading @@ -79,6 +81,7 @@ struct msm_pingroup { u32 intr_cfg_reg; u32 intr_status_reg; u32 intr_target_reg; u32 dir_conn_reg; unsigned mux_bit:5; Loading @@ -101,11 +104,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 Loading @@ -126,6 +140,9 @@ struct pinctrl_qup { * @ngroups: The numbmer of entries in @groups. * @ngpio: The number of pingroups the driver should expose as GPIOs. * @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; Loading @@ -139,6 +156,7 @@ struct msm_pinctrl_soc_data { struct pinctrl_qup *qup_regs; unsigned int nqup_regs; const int *reserved_gpios; struct msm_dir_conn *dir_conn; }; int msm_pinctrl_probe(struct platform_device *pdev, Loading