Loading drivers/pinctrl/qcom/pinctrl-msm.c +210 −6 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> Loading Loading @@ -747,11 +748,46 @@ static struct irq_chip msm_gpio_irq_chip = { .irq_set_wake = msm_gpio_irq_set_wake, }; static bool is_gpio_dual_edge(struct irq_data *d, irq_hw_number_t *dir_conn_irq) { struct irq_desc *desc = irq_data_to_desc(d); struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct msm_pinctrl *pctrl = gpiochip_get_data(gc); int i; if (!parent_data) return false; for (i = 0; i < pctrl->soc->n_dir_conns; i++) { const struct msm_dir_conn *dir_conn = &pctrl->soc->dir_conn[i]; if (dir_conn->gpio == d->hwirq && (dir_conn->hwirq + 32) != parent_data->hwirq) { *dir_conn_irq = dir_conn->hwirq + 32; return true; } } return false; } static void msm_dirconn_irq_mask(struct irq_data *d) { struct irq_desc *desc = irq_data_to_desc(d); struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); irq_hw_number_t dir_conn_irq = 0; if (!parent_data) return; if (is_gpio_dual_edge(d, &dir_conn_irq)) { struct irq_data *dir_conn_data = irq_get_irq_data(irq_find_mapping(parent_data->domain, dir_conn_irq)); if (dir_conn_data && dir_conn_data->chip->irq_mask) dir_conn_data->chip->irq_mask(dir_conn_data); } if (parent_data->chip->irq_mask) parent_data->chip->irq_mask(parent_data); } Loading @@ -760,7 +796,19 @@ static void msm_dirconn_irq_unmask(struct irq_data *d) { struct irq_desc *desc = irq_data_to_desc(d); struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); irq_hw_number_t dir_conn_irq = 0; if (!parent_data) return; if (is_gpio_dual_edge(d, &dir_conn_irq)) { struct irq_data *dir_conn_data = irq_get_irq_data(irq_find_mapping(parent_data->domain, dir_conn_irq)); if (dir_conn_data && dir_conn_data->chip->irq_unmask) dir_conn_data->chip->irq_unmask(dir_conn_data); } if (parent_data->chip->irq_unmask) parent_data->chip->irq_unmask(parent_data); } Loading Loading @@ -789,6 +837,9 @@ static int msm_dirconn_irq_set_affinity(struct irq_data *d, struct irq_desc *desc = irq_data_to_desc(d); struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); if (!parent_data) return 0; if (parent_data->chip->irq_set_affinity) return parent_data->chip->irq_set_affinity(parent_data, maskval, force); Loading @@ -807,10 +858,158 @@ static int msm_dirconn_irq_set_vcpu_affinity(struct irq_data *d, return 0; } static void msm_dirconn_cfg_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); spin_lock_irqsave(&pctrl->lock, flags); g = &pctrl->soc->groups[d->hwirq]; val = readl_relaxed(pctrl->regs + g->dir_conn_reg + (offset * 4)); val = (d->hwirq) & 0xFF; writel_relaxed(val, pctrl->regs + g->dir_conn_reg + (offset * 4)); //write the dir_conn_en bit 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); spin_unlock_irqrestore(&pctrl->lock, flags); } static void msm_dirconn_uncfg_reg(struct irq_data *d, u32 offset) { 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); spin_lock_irqsave(&pctrl->lock, flags); g = &pctrl->soc->groups[d->hwirq]; writel_relaxed(BIT(8), pctrl->regs + g->dir_conn_reg + (offset * 4)); spin_unlock_irqrestore(&pctrl->lock, flags); } static int select_dir_conn_mux(struct irq_data *d, irq_hw_number_t *irq) { struct msm_dir_conn *dc = NULL; struct irq_desc *desc = irq_data_to_desc(d); struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct msm_pinctrl *pctrl = gpiochip_get_data(gc); int i; if (!parent_data) return -EINVAL; for (i = 0; i < pctrl->soc->n_dir_conns; i++) { struct msm_dir_conn *dir_conn = (struct msm_dir_conn *)&pctrl->soc->dir_conn[i]; /* Check if there is already mux assigned for this gpio */ if (dir_conn->gpio == d->hwirq && (dir_conn->hwirq + 32) != parent_data->hwirq) { *irq = dir_conn->hwirq + 32; return pctrl->soc->dir_conn_irq_base - dir_conn->hwirq; } if (dir_conn->gpio) continue; /* Use the first unused direct connect available */ dc = dir_conn; break; } if (dc) { *irq = dc->hwirq + 32; dc->gpio = (u32)d->hwirq; return pctrl->soc->dir_conn_irq_base - (u32)dc->hwirq; } pr_err("%s: No direct connects available for interrupt %lu\n", __func__, d->hwirq); return -EINVAL; } static void add_dirconn_tlmm(struct irq_data *d, irq_hw_number_t irq) { struct irq_desc *desc = irq_data_to_desc(d); struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); struct irq_data *dir_conn_data = NULL; int offset = 0; unsigned int virt = 0; offset = select_dir_conn_mux(d, &irq); if (offset < 0 || !parent_data) return; virt = irq_find_mapping(parent_data->domain, irq); msm_dirconn_cfg_reg(d, offset); irq_set_handler_data(virt, d); desc = irq_to_desc(virt); if (!desc) return; dir_conn_data = &(desc->irq_data); if (dir_conn_data) { if (dir_conn_data->chip && dir_conn_data->chip->irq_set_type) dir_conn_data->chip->irq_set_type(dir_conn_data, IRQ_TYPE_EDGE_RISING); if (dir_conn_data->chip && dir_conn_data->chip->irq_unmask) 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_desc *desc = irq_data_to_desc(d); struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); struct irq_data *dir_conn_data = NULL; int offset = 0; unsigned int virt = 0; virt = irq_find_mapping(parent_data->domain, irq); msm_dirconn_uncfg_reg(d, offset); irq_set_handler_data(virt, NULL); desc = irq_to_desc(virt); if (!desc) return; dir_conn_data = &(desc->irq_data); if (dir_conn_data) { if (dir_conn_data->chip && dir_conn_data->chip->irq_mask) dir_conn_data->chip->irq_mask(dir_conn_data); } } static int msm_dirconn_irq_set_type(struct irq_data *d, unsigned int type) { struct irq_desc *desc = irq_data_to_desc(d); struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); irq_hw_number_t irq = 0; if (!parent_data) return 0; if (type == IRQ_TYPE_EDGE_BOTH) { add_dirconn_tlmm(d, irq); } else { if (is_gpio_dual_edge(d, &irq)) remove_dirconn_tlmm(d, irq); } if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) irq_set_handler_locked(d, handle_level_irq); else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) irq_set_handler_locked(d, handle_edge_irq); if (parent_data->chip->irq_set_type) return parent_data->chip->irq_set_type(parent_data, type); Loading Loading @@ -904,14 +1103,19 @@ static void msm_gpio_setup_dir_connects(struct msm_pinctrl *pctrl) fwspec.param_count = 3; parent_irq = irq_create_fwspec_mapping(&fwspec); irq = irq_find_mapping(pctrl->chip.irqdomain, dirconn->gpio); if (dirconn->gpio != 0) { irq = irq_find_mapping(pctrl->chip.irqdomain, dirconn->gpio); irq_set_parent(irq, parent_irq); irq_set_chip(irq, &msm_dirconn_irq_chip); irq_set_chip_data(irq, irq_get_irq_data(parent_irq)); __irq_set_handler(parent_irq, msm_gpio_dirconn_handler, false, NULL); irq_set_handler_data(parent_irq, irq_get_irq_data(irq)); } else { __irq_set_handler(parent_irq, msm_gpio_dirconn_handler, false, NULL); } } } Loading drivers/pinctrl/qcom/pinctrl-msm.h +7 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,8 @@ 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 specifying direct connect * setup of this group. * @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 Loading @@ -75,6 +77,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 @@ -95,6 +98,7 @@ struct msm_pingroup { unsigned intr_polarity_bit:5; unsigned intr_detection_bit:5; unsigned intr_detection_width:5; unsigned dir_conn_en_bit:8; } /** Loading @@ -118,6 +122,8 @@ struct msm_dir_conn { * @ngpio: The number of pingroups the driver should expose as GPIOs. * @dir_conn: An array describing all the pins directly connected to GIC. * @ndirconns: The number of pins directly connected to GIC * @dir_conn_offsets: Direct connect register offsets for each tile. * @dir_conn_irq_base: Direct connect interrupt base register for kpss. */ struct msm_pinctrl_soc_data { const struct pinctrl_pin_desc *pins; Loading @@ -129,6 +135,7 @@ struct msm_pinctrl_soc_data { unsigned ngpios; const struct msm_dir_conn *dir_conn; unsigned int n_dir_conns; unsigned int dir_conn_irq_base; }; int msm_pinctrl_probe(struct platform_device *pdev, Loading Loading
drivers/pinctrl/qcom/pinctrl-msm.c +210 −6 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> Loading Loading @@ -747,11 +748,46 @@ static struct irq_chip msm_gpio_irq_chip = { .irq_set_wake = msm_gpio_irq_set_wake, }; static bool is_gpio_dual_edge(struct irq_data *d, irq_hw_number_t *dir_conn_irq) { struct irq_desc *desc = irq_data_to_desc(d); struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct msm_pinctrl *pctrl = gpiochip_get_data(gc); int i; if (!parent_data) return false; for (i = 0; i < pctrl->soc->n_dir_conns; i++) { const struct msm_dir_conn *dir_conn = &pctrl->soc->dir_conn[i]; if (dir_conn->gpio == d->hwirq && (dir_conn->hwirq + 32) != parent_data->hwirq) { *dir_conn_irq = dir_conn->hwirq + 32; return true; } } return false; } static void msm_dirconn_irq_mask(struct irq_data *d) { struct irq_desc *desc = irq_data_to_desc(d); struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); irq_hw_number_t dir_conn_irq = 0; if (!parent_data) return; if (is_gpio_dual_edge(d, &dir_conn_irq)) { struct irq_data *dir_conn_data = irq_get_irq_data(irq_find_mapping(parent_data->domain, dir_conn_irq)); if (dir_conn_data && dir_conn_data->chip->irq_mask) dir_conn_data->chip->irq_mask(dir_conn_data); } if (parent_data->chip->irq_mask) parent_data->chip->irq_mask(parent_data); } Loading @@ -760,7 +796,19 @@ static void msm_dirconn_irq_unmask(struct irq_data *d) { struct irq_desc *desc = irq_data_to_desc(d); struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); irq_hw_number_t dir_conn_irq = 0; if (!parent_data) return; if (is_gpio_dual_edge(d, &dir_conn_irq)) { struct irq_data *dir_conn_data = irq_get_irq_data(irq_find_mapping(parent_data->domain, dir_conn_irq)); if (dir_conn_data && dir_conn_data->chip->irq_unmask) dir_conn_data->chip->irq_unmask(dir_conn_data); } if (parent_data->chip->irq_unmask) parent_data->chip->irq_unmask(parent_data); } Loading Loading @@ -789,6 +837,9 @@ static int msm_dirconn_irq_set_affinity(struct irq_data *d, struct irq_desc *desc = irq_data_to_desc(d); struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); if (!parent_data) return 0; if (parent_data->chip->irq_set_affinity) return parent_data->chip->irq_set_affinity(parent_data, maskval, force); Loading @@ -807,10 +858,158 @@ static int msm_dirconn_irq_set_vcpu_affinity(struct irq_data *d, return 0; } static void msm_dirconn_cfg_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); spin_lock_irqsave(&pctrl->lock, flags); g = &pctrl->soc->groups[d->hwirq]; val = readl_relaxed(pctrl->regs + g->dir_conn_reg + (offset * 4)); val = (d->hwirq) & 0xFF; writel_relaxed(val, pctrl->regs + g->dir_conn_reg + (offset * 4)); //write the dir_conn_en bit 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); spin_unlock_irqrestore(&pctrl->lock, flags); } static void msm_dirconn_uncfg_reg(struct irq_data *d, u32 offset) { 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); spin_lock_irqsave(&pctrl->lock, flags); g = &pctrl->soc->groups[d->hwirq]; writel_relaxed(BIT(8), pctrl->regs + g->dir_conn_reg + (offset * 4)); spin_unlock_irqrestore(&pctrl->lock, flags); } static int select_dir_conn_mux(struct irq_data *d, irq_hw_number_t *irq) { struct msm_dir_conn *dc = NULL; struct irq_desc *desc = irq_data_to_desc(d); struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct msm_pinctrl *pctrl = gpiochip_get_data(gc); int i; if (!parent_data) return -EINVAL; for (i = 0; i < pctrl->soc->n_dir_conns; i++) { struct msm_dir_conn *dir_conn = (struct msm_dir_conn *)&pctrl->soc->dir_conn[i]; /* Check if there is already mux assigned for this gpio */ if (dir_conn->gpio == d->hwirq && (dir_conn->hwirq + 32) != parent_data->hwirq) { *irq = dir_conn->hwirq + 32; return pctrl->soc->dir_conn_irq_base - dir_conn->hwirq; } if (dir_conn->gpio) continue; /* Use the first unused direct connect available */ dc = dir_conn; break; } if (dc) { *irq = dc->hwirq + 32; dc->gpio = (u32)d->hwirq; return pctrl->soc->dir_conn_irq_base - (u32)dc->hwirq; } pr_err("%s: No direct connects available for interrupt %lu\n", __func__, d->hwirq); return -EINVAL; } static void add_dirconn_tlmm(struct irq_data *d, irq_hw_number_t irq) { struct irq_desc *desc = irq_data_to_desc(d); struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); struct irq_data *dir_conn_data = NULL; int offset = 0; unsigned int virt = 0; offset = select_dir_conn_mux(d, &irq); if (offset < 0 || !parent_data) return; virt = irq_find_mapping(parent_data->domain, irq); msm_dirconn_cfg_reg(d, offset); irq_set_handler_data(virt, d); desc = irq_to_desc(virt); if (!desc) return; dir_conn_data = &(desc->irq_data); if (dir_conn_data) { if (dir_conn_data->chip && dir_conn_data->chip->irq_set_type) dir_conn_data->chip->irq_set_type(dir_conn_data, IRQ_TYPE_EDGE_RISING); if (dir_conn_data->chip && dir_conn_data->chip->irq_unmask) 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_desc *desc = irq_data_to_desc(d); struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); struct irq_data *dir_conn_data = NULL; int offset = 0; unsigned int virt = 0; virt = irq_find_mapping(parent_data->domain, irq); msm_dirconn_uncfg_reg(d, offset); irq_set_handler_data(virt, NULL); desc = irq_to_desc(virt); if (!desc) return; dir_conn_data = &(desc->irq_data); if (dir_conn_data) { if (dir_conn_data->chip && dir_conn_data->chip->irq_mask) dir_conn_data->chip->irq_mask(dir_conn_data); } } static int msm_dirconn_irq_set_type(struct irq_data *d, unsigned int type) { struct irq_desc *desc = irq_data_to_desc(d); struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); irq_hw_number_t irq = 0; if (!parent_data) return 0; if (type == IRQ_TYPE_EDGE_BOTH) { add_dirconn_tlmm(d, irq); } else { if (is_gpio_dual_edge(d, &irq)) remove_dirconn_tlmm(d, irq); } if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) irq_set_handler_locked(d, handle_level_irq); else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) irq_set_handler_locked(d, handle_edge_irq); if (parent_data->chip->irq_set_type) return parent_data->chip->irq_set_type(parent_data, type); Loading Loading @@ -904,14 +1103,19 @@ static void msm_gpio_setup_dir_connects(struct msm_pinctrl *pctrl) fwspec.param_count = 3; parent_irq = irq_create_fwspec_mapping(&fwspec); irq = irq_find_mapping(pctrl->chip.irqdomain, dirconn->gpio); if (dirconn->gpio != 0) { irq = irq_find_mapping(pctrl->chip.irqdomain, dirconn->gpio); irq_set_parent(irq, parent_irq); irq_set_chip(irq, &msm_dirconn_irq_chip); irq_set_chip_data(irq, irq_get_irq_data(parent_irq)); __irq_set_handler(parent_irq, msm_gpio_dirconn_handler, false, NULL); irq_set_handler_data(parent_irq, irq_get_irq_data(irq)); } else { __irq_set_handler(parent_irq, msm_gpio_dirconn_handler, false, NULL); } } } Loading
drivers/pinctrl/qcom/pinctrl-msm.h +7 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,8 @@ 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 specifying direct connect * setup of this group. * @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 Loading @@ -75,6 +77,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 @@ -95,6 +98,7 @@ struct msm_pingroup { unsigned intr_polarity_bit:5; unsigned intr_detection_bit:5; unsigned intr_detection_width:5; unsigned dir_conn_en_bit:8; } /** Loading @@ -118,6 +122,8 @@ struct msm_dir_conn { * @ngpio: The number of pingroups the driver should expose as GPIOs. * @dir_conn: An array describing all the pins directly connected to GIC. * @ndirconns: The number of pins directly connected to GIC * @dir_conn_offsets: Direct connect register offsets for each tile. * @dir_conn_irq_base: Direct connect interrupt base register for kpss. */ struct msm_pinctrl_soc_data { const struct pinctrl_pin_desc *pins; Loading @@ -129,6 +135,7 @@ struct msm_pinctrl_soc_data { unsigned ngpios; const struct msm_dir_conn *dir_conn; unsigned int n_dir_conns; unsigned int dir_conn_irq_base; }; int msm_pinctrl_probe(struct platform_device *pdev, Loading