Loading Documentation/devicetree/bindings/interrupt-controller/qti,mpm.txt +41 −14 Original line number Diff line number Diff line Loading @@ -8,13 +8,16 @@ replays it to the subsystem interrupt controller after it becomes operational. Platform interrupt controller MPM is next in hierarchy, followed by others. This defines 2 interrupt controllers to monitor the interrupts when the system is asleep: One to monitor the wakeup capable gic interrupts called wakegic. Properties: - compatible: Usage: required Value type: <string> Definition: Should contain "qcom,mpm" for mpm pin data and the respective target compatible flag. Definition: Should contain "qcom,mpm-gic" and the respective target compatible flag. - interrupts: Usage: required Loading Loading @@ -48,18 +51,42 @@ Properties: Example: mpm: mpm@7781b8 { compatible = "qcom,mpm"; wakegic: wake-gic@7781b8 { compatible = "qcom,mpm-gic", "qcom,mpm-gic-msm8953"; interrupts = <GIC_SPI 171 IRQ_TYPE_EDGE_RISING>; reg = <0x7781b8 0x1000>, <0x17911008 0x4>; /* MSM_APCS_GCC_BASE 4K */ reg = <0x601d4 0x1000>, <0xb011008 0x4>; /* MSM_APCS_GCC_BASE 4K */ reg-names = "vmpm", "ipc"; qcom,num-mpm-irqs = <96>; wakegic: wake-gic { compatible = "qcom,mpm-gic", "qcom,mpm-gic-msm8953"; interrupt-controller; #interrupt-cells = <3>; interrupt-parent = <&intc>; #interrupt-cells = <3>; }; One to monitor the wakeup capable gpio interrupts called wakegpio. properties: - compatible: Usage: required Value type: <string> Definition: Should contain "qcom,mpm-gpio" and the respective target compatible flag. - interrupt-parent: Usage: required Value type: <phandle> Definition: Specifies the interrupt parent necessary for hierarchical domain to operate. - interrupt-controller: Usage: required Value type: <bool> Definition: Identifies the node as an interrupt controller. Example: wakegpio: wake-gpio { compatible = "qcom,mpm-gpio", "qcom,mpm-gpio-msm8953"; interrupt-controller; interrupt-parent = <&tlmm>; #interrupt-cells = <2>; }; drivers/irqchip/qcom/mpm.c +153 −16 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <linux/irq.h> #include <linux/tick.h> #include <linux/irqchip.h> #include <linux/irqchip/arm-gic-v3.h> #include <linux/irqdomain.h> #include <linux/interrupt.h> #include<linux/ktime.h> Loading Loading @@ -59,6 +60,7 @@ struct msm_mpm_device_data { void __iomem *mpm_ipc_reg; irq_hw_number_t ipc_irq; struct irq_domain *gic_chip_domain; struct irq_domain *gpio_chip_domain; }; static int msm_pm_sleep_time_override; Loading Loading @@ -200,6 +202,22 @@ static inline void msm_mpm_set_type(struct irq_data *d, } } static void msm_mpm_gpio_chip_mask(struct irq_data *d) { msm_mpm_enable_irq(d, false); } static void msm_mpm_gpio_chip_unmask(struct irq_data *d) { msm_mpm_enable_irq(d, true); } static int msm_mpm_gpio_chip_set_type(struct irq_data *d, unsigned int type) { msm_mpm_set_type(d, type); return 0; } static void msm_mpm_gic_chip_mask(struct irq_data *d) { msm_mpm_enable_irq(d, false); Loading Loading @@ -227,9 +245,58 @@ static struct irq_chip msm_mpm_gic_chip = { .irq_retrigger = irq_chip_retrigger_hierarchy, .irq_set_type = msm_mpm_gic_chip_set_type, .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, #ifdef CONFIG_SMP .irq_set_affinity = irq_chip_set_affinity_parent, #endif }; static struct irq_chip msm_mpm_gpio_chip = { .name = "mpm-gpio", .irq_mask = msm_mpm_gpio_chip_mask, .irq_disable = msm_mpm_gpio_chip_mask, .irq_unmask = msm_mpm_gpio_chip_unmask, .irq_set_type = msm_mpm_gpio_chip_set_type, .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, .irq_retrigger = irq_chip_retrigger_hierarchy, }; static int msm_mpm_gpio_chip_translate(struct irq_domain *d, struct irq_fwspec *fwspec, unsigned long *hwirq, unsigned int *type) { if (is_of_node(fwspec->fwnode)) { if (fwspec->param_count != 2) return -EINVAL; *hwirq = fwspec->param[0]; *type = fwspec->param[1]; return 0; } return -EINVAL; } static int msm_mpm_gpio_chip_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *data) { int ret = 0; struct irq_fwspec *fwspec = data; irq_hw_number_t hwirq; unsigned int type = IRQ_TYPE_NONE; ret = msm_mpm_gpio_chip_translate(domain, fwspec, &hwirq, &type); if (ret) return ret; irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &msm_mpm_gpio_chip, NULL); return 0; } static const struct irq_domain_ops msm_mpm_gpio_chip_domain_ops = { .translate = msm_mpm_gpio_chip_translate, .alloc = msm_mpm_gpio_chip_alloc, .free = irq_domain_free_irqs_common, }; static int msm_mpm_gic_chip_translate(struct irq_domain *d, Loading @@ -240,10 +307,34 @@ static int msm_mpm_gic_chip_translate(struct irq_domain *d, if (is_of_node(fwspec->fwnode)) { if (fwspec->param_count < 3) return -EINVAL; switch (fwspec->param[0]) { case 0: /* SPI */ *hwirq = fwspec->param[1] + 32; break; case 1: /* PPI */ *hwirq = fwspec->param[1] + 16; break; case GIC_IRQ_TYPE_LPI: /* LPI */ *hwirq = fwspec->param[1]; break; default: return -EINVAL; } *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; return 0; } if (is_fwnode_irqchip(fwspec->fwnode)) { if (fwspec->param_count != 2) return -EINVAL; *hwirq = fwspec->param[0]; *type = fwspec->param[1]; return 0; } return -EINVAL; } Loading Loading @@ -296,16 +387,21 @@ static void msm_mpm_enter_sleep(struct cpumask *cpumask) irq_set_affinity(msm_mpm_dev_data.ipc_irq, cpumask); } static int msm_get_mpm_pin_map(unsigned int mpm_irq) static int msm_get_apps_irq(unsigned int mpm_irq) { struct mpm_pin *mpm_gic_pin_map = NULL; struct mpm_pin *mpm_pin = NULL; int apps_irq; mpm_gic_pin_map = (struct mpm_pin *) mpm_pin = (struct mpm_pin *) msm_mpm_dev_data.gic_chip_domain->host_data; apps_irq = msm_get_irq_pin(mpm_irq, mpm_gic_pin_map); apps_irq = msm_get_irq_pin(mpm_irq, mpm_pin); if (apps_irq >= 0) return apps_irq; mpm_pin = (struct mpm_pin *) msm_mpm_dev_data.gpio_chip_domain->host_data; return msm_get_irq_pin(mpm_irq, mpm_pin); } static void system_pm_exit_sleep(void) Loading Loading @@ -407,7 +503,7 @@ static irqreturn_t msm_mpm_irq(int irq, void *dev_id) trace_mpm_wakeup_pending_irqs(i, pending); for_each_set_bit(k, &pending, 32) { mpm_irq = 32 * i + k; apps_irq = msm_get_mpm_pin_map(mpm_irq); apps_irq = msm_get_apps_irq(mpm_irq); desc = apps_irq ? irq_to_desc(apps_irq) : NULL; Loading @@ -416,11 +512,13 @@ static irqreturn_t msm_mpm_irq(int irq, void *dev_id) IRQCHIP_STATE_PENDING, true); } msm_mpm_write(MPM_REG_STATUS, i, 0); } return IRQ_HANDLED; } static int msm_mpm_probe(struct device_node *node) static int msm_mpm_init(struct device_node *node) { struct msm_mpm_device_data *dev = &msm_mpm_dev_data; int ret = 0; Loading Loading @@ -492,12 +590,18 @@ static const struct of_device_id mpm_gic_chip_data_table[] = { }; MODULE_DEVICE_TABLE(of, mpm_gic_chip_data_table); static const struct of_device_id mpm_gpio_chip_data_table[] = { {} }; MODULE_DEVICE_TABLE(of, mpm_gpio_chip_data_table); static int __init mpm_gic_chip_init(struct device_node *node, struct device_node *parent) { struct irq_domain *parent_domain; const struct of_device_id *id; struct device_node *parent_node; int ret; if (!parent) { pr_err("%s(): no parent for mpm-gic\n", node->full_name); Loading @@ -519,7 +623,8 @@ static int __init mpm_gic_chip_init(struct device_node *node, id = of_match_node(mpm_gic_chip_data_table, node); if (!id) { pr_err("can not find mpm_gic_data_table of_node\n"); return -ENODEV; ret = -ENODEV; goto mpm_map_err; } msm_mpm_dev_data.gic_chip_domain = irq_domain_add_hierarchy( Loading @@ -527,13 +632,45 @@ static int __init mpm_gic_chip_init(struct device_node *node, &msm_mpm_gic_chip_domain_ops, (void *)id->data); if (!msm_mpm_dev_data.gic_chip_domain) { pr_err("gic domain add failed\n"); return -ENOMEM; ret = -ENOMEM; goto mpm_map_err; } msm_mpm_dev_data.gic_chip_domain->name = "qcom,mpm-gic"; parent_node = of_get_parent(node); return msm_mpm_probe(parent_node); ret = msm_mpm_init(node); if (!ret) return ret; irq_domain_remove(msm_mpm_dev_data.gic_chip_domain); mpm_map_err: kfree(mpm_to_irq); return ret; } IRQCHIP_DECLARE(mpm_gic_chip, "qcom,mpm-gic", mpm_gic_chip_init); static int __init mpm_gpio_chip_init(struct device_node *node, struct device_node *parent) { const struct of_device_id *id; id = of_match_node(mpm_gpio_chip_data_table, node); if (!id) { pr_err("match_table not found for mpm-gpio\n"); return -ENODEV; } msm_mpm_dev_data.gpio_chip_domain = irq_domain_create_linear( of_node_to_fwnode(node), num_mpm_irqs, &msm_mpm_gpio_chip_domain_ops, (void *)id->data); if (!msm_mpm_dev_data.gpio_chip_domain) return -ENOMEM; msm_mpm_dev_data.gpio_chip_domain->name = "qcom,mpm-gpio"; return 0; } IRQCHIP_DECLARE(mpm_gpio_chip, "qcom,mpm-gpio", mpm_gpio_chip_init); drivers/pinctrl/qcom/pinctrl-msm.c +151 −12 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include <linux/pinctrl/pinmux.h> #include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinconf-generic.h> #include <linux/of_irq.h> #include <linux/slab.h> #include <linux/gpio.h> #include <linux/interrupt.h> Loading Loading @@ -610,6 +611,9 @@ static void msm_gpio_irq_mask(struct irq_data *d) clear_bit(d->hwirq, pctrl->enabled_irqs); raw_spin_unlock_irqrestore(&pctrl->lock, flags); if (d->parent_data) irq_chip_mask_parent(d); } static void msm_gpio_irq_enable(struct irq_data *d) Loading Loading @@ -638,6 +642,9 @@ static void msm_gpio_irq_enable(struct irq_data *d) set_bit(d->hwirq, pctrl->enabled_irqs); raw_spin_unlock_irqrestore(&pctrl->lock, flags); if (d->parent_data) irq_chip_enable_parent(d); } static void msm_gpio_irq_unmask(struct irq_data *d) Loading @@ -659,6 +666,9 @@ static void msm_gpio_irq_unmask(struct irq_data *d) set_bit(d->hwirq, pctrl->enabled_irqs); raw_spin_unlock_irqrestore(&pctrl->lock, flags); if (d->parent_data) irq_chip_unmask_parent(d); } static void msm_gpio_irq_ack(struct irq_data *d) Loading Loading @@ -772,6 +782,9 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type) raw_spin_unlock_irqrestore(&pctrl->lock, flags); if (d->parent_data) irq_chip_set_type_parent(d, type); 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)) Loading @@ -792,9 +805,35 @@ static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on) raw_spin_unlock_irqrestore(&pctrl->lock, flags); if (d->parent_data) irq_chip_set_wake_parent(d, on); return 0; } static int msm_gpiochip_irq_reqres(struct irq_data *d) { struct gpio_chip *chip = irq_data_get_irq_chip_data(d); if (!try_module_get(chip->owner)) return -ENODEV; if (gpiochip_lock_as_irq(chip, d->hwirq)) { pr_err("unable to lock HW IRQ %lu for IRQ\n", d->hwirq); module_put(chip->owner); return -EINVAL; } return 0; } static void msm_gpiochip_irq_relres(struct irq_data *d) { struct gpio_chip *chip = irq_data_get_irq_chip_data(d); gpiochip_unlock_as_irq(chip, d->hwirq); module_put(chip->owner); } static struct irq_chip msm_gpio_irq_chip = { .name = "msmgpio", .irq_enable = msm_gpio_irq_enable, Loading @@ -803,6 +842,61 @@ static struct irq_chip msm_gpio_irq_chip = { .irq_ack = msm_gpio_irq_ack, .irq_set_type = msm_gpio_irq_set_type, .irq_set_wake = msm_gpio_irq_set_wake, .irq_request_resources = msm_gpiochip_irq_reqres, .irq_release_resources = msm_gpiochip_irq_relres, .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, }; static void msm_gpio_domain_set_info(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq) { struct gpio_chip *gc = d->host_data; irq_domain_set_info(d, irq, hwirq, gc->irqchip, d->host_data, gc->irq_handler, NULL, NULL); if (gc->can_sleep) irq_set_nested_thread(irq, 1); irq_set_noprobe(irq); } static int msm_gpio_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec, unsigned long *hwirq, unsigned int *type) { if (is_of_node(fwspec->fwnode)) { if (fwspec->param_count < 2) return -EINVAL; if (hwirq) *hwirq = fwspec->param[0]; if (type) *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; return 0; } return -EINVAL; } static int msm_gpio_domain_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *arg) { int ret; irq_hw_number_t hwirq; struct irq_fwspec *fwspec = arg; ret = msm_gpio_domain_translate(domain, fwspec, &hwirq, NULL); if (ret) return ret; msm_gpio_domain_set_info(domain, virq, hwirq); return ret; } static const struct irq_domain_ops msm_gpio_domain_ops = { .translate = msm_gpio_domain_translate, .alloc = msm_gpio_domain_alloc, .free = irq_domain_free_irqs_top, }; static bool is_gpio_dual_edge(struct irq_data *d, irq_hw_number_t *dir_conn_irq) Loading Loading @@ -1211,11 +1305,25 @@ static void msm_gpio_setup_dir_connects(struct msm_pinctrl *pctrl) } } static int msm_gpiochip_to_irq(struct gpio_chip *chip, unsigned int offset) { struct irq_fwspec fwspec; fwspec.fwnode = of_node_to_fwnode(chip->of_node); fwspec.param[0] = offset; fwspec.param[1] = IRQ_TYPE_NONE; fwspec.param_count = 2; return irq_create_fwspec_mapping(&fwspec); } static int msm_gpio_init(struct msm_pinctrl *pctrl) { struct gpio_chip *chip; int ret; unsigned ngpio = pctrl->soc->ngpios; struct device_node *irq_parent = NULL; struct irq_domain *domain_parent; if (WARN_ON(ngpio > MAX_NR_GPIO)) return -EINVAL; Loading @@ -1227,6 +1335,11 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) chip->parent = pctrl->dev; chip->owner = THIS_MODULE; chip->of_node = pctrl->dev->of_node; chip->irqchip = &msm_gpio_irq_chip; chip->irq_handler = handle_fasteoi_irq; chip->irq_default_type = IRQ_TYPE_NONE; chip->to_irq = msm_gpiochip_to_irq; chip->lock_key = NULL; ret = gpiochip_add_data(&pctrl->chip, pctrl); if (ret) { Loading @@ -1241,6 +1354,32 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) return ret; } irq_parent = of_irq_find_parent(chip->of_node); if (of_device_is_compatible(irq_parent, "qcom,mpm-gpio")) { chip->irqchip = &msm_gpio_irq_chip; chip->irq_handler = handle_fasteoi_irq; chip->irq_default_type = IRQ_TYPE_NONE; chip->to_irq = msm_gpiochip_to_irq; chip->lock_key = NULL; domain_parent = irq_find_host(irq_parent); if (!domain_parent) { pr_err("unable to find parent domain\n"); gpiochip_remove(&pctrl->chip); return -ENXIO; } chip->irqdomain = irq_domain_add_hierarchy(domain_parent, 0, chip->ngpio, chip->of_node, &msm_gpio_domain_ops, chip); if (!chip->irqdomain) { dev_err(pctrl->dev, "Failed to add irqchip to gpiochip\n"); chip->irqchip = NULL; gpiochip_remove(&pctrl->chip); return -ENXIO; } } else { ret = gpiochip_irqchip_add(chip, &msm_gpio_irq_chip, 0, Loading @@ -1249,11 +1388,11 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) if (ret) { dev_err(pctrl->dev, "Failed to add irqchip to gpiochip\n"); gpiochip_remove(&pctrl->chip); return -ENOSYS; return ret; } gpiochip_set_chained_irqchip(chip, &msm_gpio_irq_chip, pctrl->irq, msm_gpio_irq_handler); } gpiochip_set_chained_irqchip(chip, &msm_gpio_irq_chip, pctrl->irq, msm_gpio_irq_handler); msm_gpio_setup_dir_connects(pctrl); return 0; Loading Loading
Documentation/devicetree/bindings/interrupt-controller/qti,mpm.txt +41 −14 Original line number Diff line number Diff line Loading @@ -8,13 +8,16 @@ replays it to the subsystem interrupt controller after it becomes operational. Platform interrupt controller MPM is next in hierarchy, followed by others. This defines 2 interrupt controllers to monitor the interrupts when the system is asleep: One to monitor the wakeup capable gic interrupts called wakegic. Properties: - compatible: Usage: required Value type: <string> Definition: Should contain "qcom,mpm" for mpm pin data and the respective target compatible flag. Definition: Should contain "qcom,mpm-gic" and the respective target compatible flag. - interrupts: Usage: required Loading Loading @@ -48,18 +51,42 @@ Properties: Example: mpm: mpm@7781b8 { compatible = "qcom,mpm"; wakegic: wake-gic@7781b8 { compatible = "qcom,mpm-gic", "qcom,mpm-gic-msm8953"; interrupts = <GIC_SPI 171 IRQ_TYPE_EDGE_RISING>; reg = <0x7781b8 0x1000>, <0x17911008 0x4>; /* MSM_APCS_GCC_BASE 4K */ reg = <0x601d4 0x1000>, <0xb011008 0x4>; /* MSM_APCS_GCC_BASE 4K */ reg-names = "vmpm", "ipc"; qcom,num-mpm-irqs = <96>; wakegic: wake-gic { compatible = "qcom,mpm-gic", "qcom,mpm-gic-msm8953"; interrupt-controller; #interrupt-cells = <3>; interrupt-parent = <&intc>; #interrupt-cells = <3>; }; One to monitor the wakeup capable gpio interrupts called wakegpio. properties: - compatible: Usage: required Value type: <string> Definition: Should contain "qcom,mpm-gpio" and the respective target compatible flag. - interrupt-parent: Usage: required Value type: <phandle> Definition: Specifies the interrupt parent necessary for hierarchical domain to operate. - interrupt-controller: Usage: required Value type: <bool> Definition: Identifies the node as an interrupt controller. Example: wakegpio: wake-gpio { compatible = "qcom,mpm-gpio", "qcom,mpm-gpio-msm8953"; interrupt-controller; interrupt-parent = <&tlmm>; #interrupt-cells = <2>; };
drivers/irqchip/qcom/mpm.c +153 −16 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <linux/irq.h> #include <linux/tick.h> #include <linux/irqchip.h> #include <linux/irqchip/arm-gic-v3.h> #include <linux/irqdomain.h> #include <linux/interrupt.h> #include<linux/ktime.h> Loading Loading @@ -59,6 +60,7 @@ struct msm_mpm_device_data { void __iomem *mpm_ipc_reg; irq_hw_number_t ipc_irq; struct irq_domain *gic_chip_domain; struct irq_domain *gpio_chip_domain; }; static int msm_pm_sleep_time_override; Loading Loading @@ -200,6 +202,22 @@ static inline void msm_mpm_set_type(struct irq_data *d, } } static void msm_mpm_gpio_chip_mask(struct irq_data *d) { msm_mpm_enable_irq(d, false); } static void msm_mpm_gpio_chip_unmask(struct irq_data *d) { msm_mpm_enable_irq(d, true); } static int msm_mpm_gpio_chip_set_type(struct irq_data *d, unsigned int type) { msm_mpm_set_type(d, type); return 0; } static void msm_mpm_gic_chip_mask(struct irq_data *d) { msm_mpm_enable_irq(d, false); Loading Loading @@ -227,9 +245,58 @@ static struct irq_chip msm_mpm_gic_chip = { .irq_retrigger = irq_chip_retrigger_hierarchy, .irq_set_type = msm_mpm_gic_chip_set_type, .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, #ifdef CONFIG_SMP .irq_set_affinity = irq_chip_set_affinity_parent, #endif }; static struct irq_chip msm_mpm_gpio_chip = { .name = "mpm-gpio", .irq_mask = msm_mpm_gpio_chip_mask, .irq_disable = msm_mpm_gpio_chip_mask, .irq_unmask = msm_mpm_gpio_chip_unmask, .irq_set_type = msm_mpm_gpio_chip_set_type, .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, .irq_retrigger = irq_chip_retrigger_hierarchy, }; static int msm_mpm_gpio_chip_translate(struct irq_domain *d, struct irq_fwspec *fwspec, unsigned long *hwirq, unsigned int *type) { if (is_of_node(fwspec->fwnode)) { if (fwspec->param_count != 2) return -EINVAL; *hwirq = fwspec->param[0]; *type = fwspec->param[1]; return 0; } return -EINVAL; } static int msm_mpm_gpio_chip_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *data) { int ret = 0; struct irq_fwspec *fwspec = data; irq_hw_number_t hwirq; unsigned int type = IRQ_TYPE_NONE; ret = msm_mpm_gpio_chip_translate(domain, fwspec, &hwirq, &type); if (ret) return ret; irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &msm_mpm_gpio_chip, NULL); return 0; } static const struct irq_domain_ops msm_mpm_gpio_chip_domain_ops = { .translate = msm_mpm_gpio_chip_translate, .alloc = msm_mpm_gpio_chip_alloc, .free = irq_domain_free_irqs_common, }; static int msm_mpm_gic_chip_translate(struct irq_domain *d, Loading @@ -240,10 +307,34 @@ static int msm_mpm_gic_chip_translate(struct irq_domain *d, if (is_of_node(fwspec->fwnode)) { if (fwspec->param_count < 3) return -EINVAL; switch (fwspec->param[0]) { case 0: /* SPI */ *hwirq = fwspec->param[1] + 32; break; case 1: /* PPI */ *hwirq = fwspec->param[1] + 16; break; case GIC_IRQ_TYPE_LPI: /* LPI */ *hwirq = fwspec->param[1]; break; default: return -EINVAL; } *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; return 0; } if (is_fwnode_irqchip(fwspec->fwnode)) { if (fwspec->param_count != 2) return -EINVAL; *hwirq = fwspec->param[0]; *type = fwspec->param[1]; return 0; } return -EINVAL; } Loading Loading @@ -296,16 +387,21 @@ static void msm_mpm_enter_sleep(struct cpumask *cpumask) irq_set_affinity(msm_mpm_dev_data.ipc_irq, cpumask); } static int msm_get_mpm_pin_map(unsigned int mpm_irq) static int msm_get_apps_irq(unsigned int mpm_irq) { struct mpm_pin *mpm_gic_pin_map = NULL; struct mpm_pin *mpm_pin = NULL; int apps_irq; mpm_gic_pin_map = (struct mpm_pin *) mpm_pin = (struct mpm_pin *) msm_mpm_dev_data.gic_chip_domain->host_data; apps_irq = msm_get_irq_pin(mpm_irq, mpm_gic_pin_map); apps_irq = msm_get_irq_pin(mpm_irq, mpm_pin); if (apps_irq >= 0) return apps_irq; mpm_pin = (struct mpm_pin *) msm_mpm_dev_data.gpio_chip_domain->host_data; return msm_get_irq_pin(mpm_irq, mpm_pin); } static void system_pm_exit_sleep(void) Loading Loading @@ -407,7 +503,7 @@ static irqreturn_t msm_mpm_irq(int irq, void *dev_id) trace_mpm_wakeup_pending_irqs(i, pending); for_each_set_bit(k, &pending, 32) { mpm_irq = 32 * i + k; apps_irq = msm_get_mpm_pin_map(mpm_irq); apps_irq = msm_get_apps_irq(mpm_irq); desc = apps_irq ? irq_to_desc(apps_irq) : NULL; Loading @@ -416,11 +512,13 @@ static irqreturn_t msm_mpm_irq(int irq, void *dev_id) IRQCHIP_STATE_PENDING, true); } msm_mpm_write(MPM_REG_STATUS, i, 0); } return IRQ_HANDLED; } static int msm_mpm_probe(struct device_node *node) static int msm_mpm_init(struct device_node *node) { struct msm_mpm_device_data *dev = &msm_mpm_dev_data; int ret = 0; Loading Loading @@ -492,12 +590,18 @@ static const struct of_device_id mpm_gic_chip_data_table[] = { }; MODULE_DEVICE_TABLE(of, mpm_gic_chip_data_table); static const struct of_device_id mpm_gpio_chip_data_table[] = { {} }; MODULE_DEVICE_TABLE(of, mpm_gpio_chip_data_table); static int __init mpm_gic_chip_init(struct device_node *node, struct device_node *parent) { struct irq_domain *parent_domain; const struct of_device_id *id; struct device_node *parent_node; int ret; if (!parent) { pr_err("%s(): no parent for mpm-gic\n", node->full_name); Loading @@ -519,7 +623,8 @@ static int __init mpm_gic_chip_init(struct device_node *node, id = of_match_node(mpm_gic_chip_data_table, node); if (!id) { pr_err("can not find mpm_gic_data_table of_node\n"); return -ENODEV; ret = -ENODEV; goto mpm_map_err; } msm_mpm_dev_data.gic_chip_domain = irq_domain_add_hierarchy( Loading @@ -527,13 +632,45 @@ static int __init mpm_gic_chip_init(struct device_node *node, &msm_mpm_gic_chip_domain_ops, (void *)id->data); if (!msm_mpm_dev_data.gic_chip_domain) { pr_err("gic domain add failed\n"); return -ENOMEM; ret = -ENOMEM; goto mpm_map_err; } msm_mpm_dev_data.gic_chip_domain->name = "qcom,mpm-gic"; parent_node = of_get_parent(node); return msm_mpm_probe(parent_node); ret = msm_mpm_init(node); if (!ret) return ret; irq_domain_remove(msm_mpm_dev_data.gic_chip_domain); mpm_map_err: kfree(mpm_to_irq); return ret; } IRQCHIP_DECLARE(mpm_gic_chip, "qcom,mpm-gic", mpm_gic_chip_init); static int __init mpm_gpio_chip_init(struct device_node *node, struct device_node *parent) { const struct of_device_id *id; id = of_match_node(mpm_gpio_chip_data_table, node); if (!id) { pr_err("match_table not found for mpm-gpio\n"); return -ENODEV; } msm_mpm_dev_data.gpio_chip_domain = irq_domain_create_linear( of_node_to_fwnode(node), num_mpm_irqs, &msm_mpm_gpio_chip_domain_ops, (void *)id->data); if (!msm_mpm_dev_data.gpio_chip_domain) return -ENOMEM; msm_mpm_dev_data.gpio_chip_domain->name = "qcom,mpm-gpio"; return 0; } IRQCHIP_DECLARE(mpm_gpio_chip, "qcom,mpm-gpio", mpm_gpio_chip_init);
drivers/pinctrl/qcom/pinctrl-msm.c +151 −12 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include <linux/pinctrl/pinmux.h> #include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinconf-generic.h> #include <linux/of_irq.h> #include <linux/slab.h> #include <linux/gpio.h> #include <linux/interrupt.h> Loading Loading @@ -610,6 +611,9 @@ static void msm_gpio_irq_mask(struct irq_data *d) clear_bit(d->hwirq, pctrl->enabled_irqs); raw_spin_unlock_irqrestore(&pctrl->lock, flags); if (d->parent_data) irq_chip_mask_parent(d); } static void msm_gpio_irq_enable(struct irq_data *d) Loading Loading @@ -638,6 +642,9 @@ static void msm_gpio_irq_enable(struct irq_data *d) set_bit(d->hwirq, pctrl->enabled_irqs); raw_spin_unlock_irqrestore(&pctrl->lock, flags); if (d->parent_data) irq_chip_enable_parent(d); } static void msm_gpio_irq_unmask(struct irq_data *d) Loading @@ -659,6 +666,9 @@ static void msm_gpio_irq_unmask(struct irq_data *d) set_bit(d->hwirq, pctrl->enabled_irqs); raw_spin_unlock_irqrestore(&pctrl->lock, flags); if (d->parent_data) irq_chip_unmask_parent(d); } static void msm_gpio_irq_ack(struct irq_data *d) Loading Loading @@ -772,6 +782,9 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type) raw_spin_unlock_irqrestore(&pctrl->lock, flags); if (d->parent_data) irq_chip_set_type_parent(d, type); 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)) Loading @@ -792,9 +805,35 @@ static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on) raw_spin_unlock_irqrestore(&pctrl->lock, flags); if (d->parent_data) irq_chip_set_wake_parent(d, on); return 0; } static int msm_gpiochip_irq_reqres(struct irq_data *d) { struct gpio_chip *chip = irq_data_get_irq_chip_data(d); if (!try_module_get(chip->owner)) return -ENODEV; if (gpiochip_lock_as_irq(chip, d->hwirq)) { pr_err("unable to lock HW IRQ %lu for IRQ\n", d->hwirq); module_put(chip->owner); return -EINVAL; } return 0; } static void msm_gpiochip_irq_relres(struct irq_data *d) { struct gpio_chip *chip = irq_data_get_irq_chip_data(d); gpiochip_unlock_as_irq(chip, d->hwirq); module_put(chip->owner); } static struct irq_chip msm_gpio_irq_chip = { .name = "msmgpio", .irq_enable = msm_gpio_irq_enable, Loading @@ -803,6 +842,61 @@ static struct irq_chip msm_gpio_irq_chip = { .irq_ack = msm_gpio_irq_ack, .irq_set_type = msm_gpio_irq_set_type, .irq_set_wake = msm_gpio_irq_set_wake, .irq_request_resources = msm_gpiochip_irq_reqres, .irq_release_resources = msm_gpiochip_irq_relres, .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, }; static void msm_gpio_domain_set_info(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq) { struct gpio_chip *gc = d->host_data; irq_domain_set_info(d, irq, hwirq, gc->irqchip, d->host_data, gc->irq_handler, NULL, NULL); if (gc->can_sleep) irq_set_nested_thread(irq, 1); irq_set_noprobe(irq); } static int msm_gpio_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec, unsigned long *hwirq, unsigned int *type) { if (is_of_node(fwspec->fwnode)) { if (fwspec->param_count < 2) return -EINVAL; if (hwirq) *hwirq = fwspec->param[0]; if (type) *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; return 0; } return -EINVAL; } static int msm_gpio_domain_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *arg) { int ret; irq_hw_number_t hwirq; struct irq_fwspec *fwspec = arg; ret = msm_gpio_domain_translate(domain, fwspec, &hwirq, NULL); if (ret) return ret; msm_gpio_domain_set_info(domain, virq, hwirq); return ret; } static const struct irq_domain_ops msm_gpio_domain_ops = { .translate = msm_gpio_domain_translate, .alloc = msm_gpio_domain_alloc, .free = irq_domain_free_irqs_top, }; static bool is_gpio_dual_edge(struct irq_data *d, irq_hw_number_t *dir_conn_irq) Loading Loading @@ -1211,11 +1305,25 @@ static void msm_gpio_setup_dir_connects(struct msm_pinctrl *pctrl) } } static int msm_gpiochip_to_irq(struct gpio_chip *chip, unsigned int offset) { struct irq_fwspec fwspec; fwspec.fwnode = of_node_to_fwnode(chip->of_node); fwspec.param[0] = offset; fwspec.param[1] = IRQ_TYPE_NONE; fwspec.param_count = 2; return irq_create_fwspec_mapping(&fwspec); } static int msm_gpio_init(struct msm_pinctrl *pctrl) { struct gpio_chip *chip; int ret; unsigned ngpio = pctrl->soc->ngpios; struct device_node *irq_parent = NULL; struct irq_domain *domain_parent; if (WARN_ON(ngpio > MAX_NR_GPIO)) return -EINVAL; Loading @@ -1227,6 +1335,11 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) chip->parent = pctrl->dev; chip->owner = THIS_MODULE; chip->of_node = pctrl->dev->of_node; chip->irqchip = &msm_gpio_irq_chip; chip->irq_handler = handle_fasteoi_irq; chip->irq_default_type = IRQ_TYPE_NONE; chip->to_irq = msm_gpiochip_to_irq; chip->lock_key = NULL; ret = gpiochip_add_data(&pctrl->chip, pctrl); if (ret) { Loading @@ -1241,6 +1354,32 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) return ret; } irq_parent = of_irq_find_parent(chip->of_node); if (of_device_is_compatible(irq_parent, "qcom,mpm-gpio")) { chip->irqchip = &msm_gpio_irq_chip; chip->irq_handler = handle_fasteoi_irq; chip->irq_default_type = IRQ_TYPE_NONE; chip->to_irq = msm_gpiochip_to_irq; chip->lock_key = NULL; domain_parent = irq_find_host(irq_parent); if (!domain_parent) { pr_err("unable to find parent domain\n"); gpiochip_remove(&pctrl->chip); return -ENXIO; } chip->irqdomain = irq_domain_add_hierarchy(domain_parent, 0, chip->ngpio, chip->of_node, &msm_gpio_domain_ops, chip); if (!chip->irqdomain) { dev_err(pctrl->dev, "Failed to add irqchip to gpiochip\n"); chip->irqchip = NULL; gpiochip_remove(&pctrl->chip); return -ENXIO; } } else { ret = gpiochip_irqchip_add(chip, &msm_gpio_irq_chip, 0, Loading @@ -1249,11 +1388,11 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) if (ret) { dev_err(pctrl->dev, "Failed to add irqchip to gpiochip\n"); gpiochip_remove(&pctrl->chip); return -ENOSYS; return ret; } gpiochip_set_chained_irqchip(chip, &msm_gpio_irq_chip, pctrl->irq, msm_gpio_irq_handler); } gpiochip_set_chained_irqchip(chip, &msm_gpio_irq_chip, pctrl->irq, msm_gpio_irq_handler); msm_gpio_setup_dir_connects(pctrl); return 0; Loading