From 082ce27ff4215a4863d885a48dc443f66f833818 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 20 Mar 2019 12:39:19 -0700 Subject: irqchip/bcm: Restore registration print with %pOF It is useful to print which interrupt controllers are registered in the system and which parent IRQ they use, especially given that L2 interrupt controllers do not call request_irq() on their parent interrupt and do not appear under /proc/interrupts for that reason. We used to print the base register address virtual address which had little value, use %pOF to print the path to the Device Tree node which maps to the physical address more easily and is what people need to troubleshoot systems. Signed-off-by: Florian Fainelli Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-bcm7038-l1.c | 3 +++ drivers/irqchip/irq-bcm7120-l2.c | 3 +++ drivers/irqchip/irq-brcmstb-l2.c | 2 ++ 3 files changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c index 0f6e30e9009d..0acebac1920b 100644 --- a/drivers/irqchip/irq-bcm7038-l1.c +++ b/drivers/irqchip/irq-bcm7038-l1.c @@ -343,6 +343,9 @@ int __init bcm7038_l1_of_init(struct device_node *dn, goto out_unmap; } + pr_info("registered BCM7038 L1 intc (%pOF, IRQs: %d)\n", + dn, IRQS_PER_WORD * intc->n_words); + return 0; out_unmap: diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c index 8968e5e93fcb..541bdca9f4af 100644 --- a/drivers/irqchip/irq-bcm7120-l2.c +++ b/drivers/irqchip/irq-bcm7120-l2.c @@ -318,6 +318,9 @@ static int __init bcm7120_l2_intc_probe(struct device_node *dn, } } + pr_info("registered %s intc (%pOF, parent IRQ(s): %d)\n", + intc_name, dn, data->num_parent_irqs); + return 0; out_free_domain: diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c index 5e4ca139e4ea..a0642b59befa 100644 --- a/drivers/irqchip/irq-brcmstb-l2.c +++ b/drivers/irqchip/irq-brcmstb-l2.c @@ -264,6 +264,8 @@ static int __init brcmstb_l2_intc_of_init(struct device_node *np, ct->chip.irq_set_wake = irq_gc_set_wake; } + pr_info("registered L2 intc (%pOF, parent irq: %d)\n", np, parent_irq); + return 0; out_free_domain: -- cgit v1.2.3 From a7c90f51d7b10e9c71be991a837d87ddc10dab20 Mon Sep 17 00:00:00 2001 From: Julien Grall Date: Thu, 18 Apr 2019 16:58:14 +0100 Subject: irqchip/gic-v3-its: Fix typo in a comment in its_msi_prepare() The word 'entirely' has been misspelt in a comment in its_msi_prepare(). Signed-off-by: Julien Grall Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 7577755bdcf4..8d5936c899a6 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -2487,7 +2487,7 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev, int err = 0; /* - * We ignore "dev" entierely, and rely on the dev_id that has + * We ignore "dev" entirely, and rely on the dev_id that has * been passed via the scratchpad. This limits this domain's * usefulness to upper layers that definitely know that they * are built on top of the ITS. -- cgit v1.2.3 From 358b9d248a026d7d0f5ea21b04480b715da0d425 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Mon, 1 Apr 2019 06:21:51 +0000 Subject: irqchip/imx-irqsteer: Use devm_platform_ioremap_resource() to simplify code Use the new helper devm_platform_ioremap_resource() which wraps the platform_get_resource() and devm_ioremap_resource() together, to simplify the code. Signed-off-by: Anson Huang Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-imx-irqsteer.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c index 88df3d00052c..290531ec3d61 100644 --- a/drivers/irqchip/irq-imx-irqsteer.c +++ b/drivers/irqchip/irq-imx-irqsteer.c @@ -144,7 +144,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct irqsteer_data *data; - struct resource *res; u32 irqs_num; int i, ret; @@ -152,8 +151,7 @@ static int imx_irqsteer_probe(struct platform_device *pdev) if (!data) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - data->regs = devm_ioremap_resource(&pdev->dev, res); + data->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(data->regs)) { dev_err(&pdev->dev, "failed to initialize reg\n"); return PTR_ERR(data->regs); -- cgit v1.2.3 From fe00f8900ca71a991849b9413f0bfa0ec50f0ad4 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Fri, 22 Mar 2019 17:43:04 +0530 Subject: irqchip/gic-pm: Update driver to use clk_bulk APIs gic-pm driver is using pm-clk framework to manage clock resources, where clocks remain always ON. This happens on Tegra devices which use BPMP co-processor to manage the clocks. Calls to BPMP are always blocking and hence it is necessary to enable/disable clocks during prepare/unprepare phase respectively. When pm-clk is used, prepare count of clock is not balanced until pm_clk_remove() happens. Clock is prepared in the driver probe() and thus prepare count of clock remains non-zero, which in turn keeps clock ON always. Please note that above mentioned behavior is specific to Tegra devices using BPMP for clock management and this should not be seen on other devices. Though this patch uses clk_bulk APIs to address the mentioned behavior, this works fine for all devices. To simplify gic_get_clocks() API is removed and instead probe can do necessary setup. Suggested-by: Mohan Kumar D Signed-off-by: Sameer Pujar Reviewed-by: Jon Hunter Tested-by: Jon Hunter Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-pm.c | 74 ++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-pm.c b/drivers/irqchip/irq-gic-pm.c index ecafd295c31c..069cf4c0f614 100644 --- a/drivers/irqchip/irq-gic-pm.c +++ b/drivers/irqchip/irq-gic-pm.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -28,17 +27,27 @@ struct gic_clk_data { const char *const *clocks; }; +struct gic_chip_pm { + struct gic_chip_data *chip_data; + const struct gic_clk_data *clk_data; + struct clk_bulk_data *clks; +}; + static int gic_runtime_resume(struct device *dev) { - struct gic_chip_data *gic = dev_get_drvdata(dev); + struct gic_chip_pm *chip_pm = dev_get_drvdata(dev); + struct gic_chip_data *gic = chip_pm->chip_data; + const struct gic_clk_data *data = chip_pm->clk_data; int ret; - ret = pm_clk_resume(dev); - if (ret) + ret = clk_bulk_prepare_enable(data->num_clocks, chip_pm->clks); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); return ret; + } /* - * On the very first resume, the pointer to the driver data + * On the very first resume, the pointer to chip_pm->chip_data * will be NULL and this is intentional, because we do not * want to restore the GIC on the very first resume. So if * the pointer is not valid just return. @@ -54,35 +63,14 @@ static int gic_runtime_resume(struct device *dev) static int gic_runtime_suspend(struct device *dev) { - struct gic_chip_data *gic = dev_get_drvdata(dev); + struct gic_chip_pm *chip_pm = dev_get_drvdata(dev); + struct gic_chip_data *gic = chip_pm->chip_data; + const struct gic_clk_data *data = chip_pm->clk_data; gic_dist_save(gic); gic_cpu_save(gic); - return pm_clk_suspend(dev); -} - -static int gic_get_clocks(struct device *dev, const struct gic_clk_data *data) -{ - unsigned int i; - int ret; - - if (!dev || !data) - return -EINVAL; - - ret = pm_clk_create(dev); - if (ret) - return ret; - - for (i = 0; i < data->num_clocks; i++) { - ret = of_pm_clk_add_clk(dev, data->clocks[i]); - if (ret) { - dev_err(dev, "failed to add clock %s\n", - data->clocks[i]); - pm_clk_destroy(dev); - return ret; - } - } + clk_bulk_disable_unprepare(data->num_clocks, chip_pm->clks); return 0; } @@ -91,8 +79,8 @@ static int gic_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct gic_clk_data *data; - struct gic_chip_data *gic; - int ret, irq; + struct gic_chip_pm *chip_pm; + int ret, irq, i; data = of_device_get_match_data(&pdev->dev); if (!data) { @@ -100,28 +88,41 @@ static int gic_probe(struct platform_device *pdev) return -ENODEV; } + chip_pm = devm_kzalloc(dev, sizeof(*chip_pm), GFP_KERNEL); + if (!chip_pm) + return -ENOMEM; + irq = irq_of_parse_and_map(dev->of_node, 0); if (!irq) { dev_err(dev, "no parent interrupt found!\n"); return -EINVAL; } - ret = gic_get_clocks(dev, data); + chip_pm->clks = devm_kcalloc(dev, data->num_clocks, + sizeof(*chip_pm->clks), GFP_KERNEL); + if (!chip_pm->clks) + return -ENOMEM; + + for (i = 0; i < data->num_clocks; i++) + chip_pm->clks[i].id = data->clocks[i]; + + ret = devm_clk_bulk_get(dev, data->num_clocks, chip_pm->clks); if (ret) goto irq_dispose; + chip_pm->clk_data = data; + dev_set_drvdata(dev, chip_pm); + pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); if (ret < 0) goto rpm_disable; - ret = gic_of_init_child(dev, &gic, irq); + ret = gic_of_init_child(dev, &chip_pm->chip_data, irq); if (ret) goto rpm_put; - platform_set_drvdata(pdev, gic); - pm_runtime_put(dev); dev_info(dev, "GIC IRQ controller registered\n"); @@ -132,7 +133,6 @@ rpm_put: pm_runtime_put_sync(dev); rpm_disable: pm_runtime_disable(dev); - pm_clk_destroy(dev); irq_dispose: irq_dispose_mapping(irq); -- cgit v1.2.3 From 960164f7141e8a974035db9ab03d93f46656ea81 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Fri, 22 Mar 2019 17:43:05 +0530 Subject: irqchip/gic-pm: Fix suspend handling If interrupts are enabled for a non-root GIC device that uses the gic-pm driver, when system suspend occurs, the current interrupt state is not saved and restored correctly and so interrupts do not work again on resuming the system. Add a late suspend handler to save and restore the state for these devices. Suggested-by: Jonathan Hunter Signed-off-by: Sameer Pujar Reviewed-by: Jon Hunter Tested-by: Jon Hunter Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-pm.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-pm.c b/drivers/irqchip/irq-gic-pm.c index 069cf4c0f614..c4aac0977d8a 100644 --- a/drivers/irqchip/irq-gic-pm.c +++ b/drivers/irqchip/irq-gic-pm.c @@ -142,6 +142,8 @@ irq_dispose: static const struct dev_pm_ops gic_pm_ops = { SET_RUNTIME_PM_OPS(gic_runtime_suspend, gic_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) }; static const char * const gic400_clocks[] = { -- cgit v1.2.3 From cfbf9e497094dcfaba02fe46e9e8c276e2d6eca9 Mon Sep 17 00:00:00 2001 From: Fabien Dessenne Date: Wed, 17 Apr 2019 15:02:56 +0200 Subject: irqchip/stm32: Use a platform driver for stm32mp1-exti device This irqchip driver uses the hwspinlock framework (coprocessor HW regs access concurrency) for the stm32mp1-exti device. Hence, this driver needs to handle the hwspinlock driver dependency using the deferred probe mechanism which requires to move this driver into a platform one with a probe() ops. This applies only for the device which is "st,stm32mp1-exti" compatible, the management of the other devices (st,stm32h7-exti / st,stm32-exti) is kept unchanged (use IRQCHIP_DECLARE) Signed-off-by: Fabien Dessenne Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-stm32-exti.c | 233 +++++++++++++++++++++++---------------- 1 file changed, 140 insertions(+), 93 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index 7bd1d4cb2e19..e00f2fa27f00 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c @@ -14,8 +14,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -37,12 +39,6 @@ struct stm32_exti_bank { #define UNDEF_REG ~0 -enum stm32_exti_hwspinlock { - HWSPINLOCK_UNKNOWN, - HWSPINLOCK_NONE, - HWSPINLOCK_READY, -}; - struct stm32_desc_irq { u32 exti; u32 irq_parent; @@ -69,8 +65,6 @@ struct stm32_exti_host_data { void __iomem *base; struct stm32_exti_chip_data *chips_data; const struct stm32_exti_drv_data *drv_data; - struct device_node *node; - enum stm32_exti_hwspinlock hwlock_state; struct hwspinlock *hwlock; }; @@ -285,49 +279,27 @@ static int stm32_exti_set_type(struct irq_data *d, static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data) { - struct stm32_exti_host_data *host_data = chip_data->host_data; - struct hwspinlock *hwlock; - int id, ret = 0, timeout = 0; - - /* first time, check for hwspinlock availability */ - if (unlikely(host_data->hwlock_state == HWSPINLOCK_UNKNOWN)) { - id = of_hwspin_lock_get_id(host_data->node, 0); - if (id >= 0) { - hwlock = hwspin_lock_request_specific(id); - if (hwlock) { - /* found valid hwspinlock */ - host_data->hwlock_state = HWSPINLOCK_READY; - host_data->hwlock = hwlock; - pr_debug("%s hwspinlock = %d\n", __func__, id); - } else { - host_data->hwlock_state = HWSPINLOCK_NONE; - } - } else if (id != -EPROBE_DEFER) { - host_data->hwlock_state = HWSPINLOCK_NONE; - } else { - /* hwspinlock driver shall be ready at that stage */ - ret = -EPROBE_DEFER; - } - } + int ret, timeout = 0; - if (likely(host_data->hwlock_state == HWSPINLOCK_READY)) { - /* - * Use the x_raw API since we are under spin_lock protection. - * Do not use the x_timeout API because we are under irq_disable - * mode (see __setup_irq()) - */ - do { - ret = hwspin_trylock_raw(host_data->hwlock); - if (!ret) - return 0; - - udelay(HWSPNLCK_RETRY_DELAY); - timeout += HWSPNLCK_RETRY_DELAY; - } while (timeout < HWSPNLCK_TIMEOUT); - - if (ret == -EBUSY) - ret = -ETIMEDOUT; - } + if (!chip_data->host_data->hwlock) + return 0; + + /* + * Use the x_raw API since we are under spin_lock protection. + * Do not use the x_timeout API because we are under irq_disable + * mode (see __setup_irq()) + */ + do { + ret = hwspin_trylock_raw(chip_data->host_data->hwlock); + if (!ret) + return 0; + + udelay(HWSPNLCK_RETRY_DELAY); + timeout += HWSPNLCK_RETRY_DELAY; + } while (timeout < HWSPNLCK_TIMEOUT); + + if (ret == -EBUSY) + ret = -ETIMEDOUT; if (ret) pr_err("%s can't get hwspinlock (%d)\n", __func__, ret); @@ -337,7 +309,7 @@ static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data) static void stm32_exti_hwspin_unlock(struct stm32_exti_chip_data *chip_data) { - if (likely(chip_data->host_data->hwlock_state == HWSPINLOCK_READY)) + if (chip_data->host_data->hwlock) hwspin_unlock_raw(chip_data->host_data->hwlock); } @@ -586,8 +558,7 @@ static int stm32_exti_h_set_affinity(struct irq_data *d, return -EINVAL; } -#ifdef CONFIG_PM -static int stm32_exti_h_suspend(void) +static int __maybe_unused stm32_exti_h_suspend(void) { struct stm32_exti_chip_data *chip_data; int i; @@ -602,7 +573,7 @@ static int stm32_exti_h_suspend(void) return 0; } -static void stm32_exti_h_resume(void) +static void __maybe_unused stm32_exti_h_resume(void) { struct stm32_exti_chip_data *chip_data; int i; @@ -616,17 +587,22 @@ static void stm32_exti_h_resume(void) } static struct syscore_ops stm32_exti_h_syscore_ops = { +#ifdef CONFIG_PM_SLEEP .suspend = stm32_exti_h_suspend, .resume = stm32_exti_h_resume, +#endif }; -static void stm32_exti_h_syscore_init(void) +static void stm32_exti_h_syscore_init(struct stm32_exti_host_data *host_data) { + stm32_host_data = host_data; register_syscore_ops(&stm32_exti_h_syscore_ops); } -#else -static inline void stm32_exti_h_syscore_init(void) {} -#endif + +static void stm32_exti_h_syscore_deinit(void) +{ + unregister_syscore_ops(&stm32_exti_h_syscore_ops); +} static struct irq_chip stm32_exti_h_chip = { .name = "stm32-exti-h", @@ -683,8 +659,6 @@ stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd, return NULL; host_data->drv_data = dd; - host_data->node = node; - host_data->hwlock_state = HWSPINLOCK_UNKNOWN; host_data->chips_data = kcalloc(dd->bank_nr, sizeof(struct stm32_exti_chip_data), GFP_KERNEL); @@ -711,7 +685,8 @@ free_host_data: static struct stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, - u32 bank_idx) + u32 bank_idx, + struct device_node *node) { const struct stm32_exti_bank *stm32_bank; struct stm32_exti_chip_data *chip_data; @@ -731,7 +706,7 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, writel_relaxed(0, base + stm32_bank->imr_ofst); writel_relaxed(0, base + stm32_bank->emr_ofst); - pr_info("%pOF: bank%d\n", h_data->node, bank_idx); + pr_info("%pOF: bank%d\n", node, bank_idx); return chip_data; } @@ -771,7 +746,7 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data, struct stm32_exti_chip_data *chip_data; stm32_bank = drv_data->exti_banks[i]; - chip_data = stm32_exti_chip_init(host_data, i); + chip_data = stm32_exti_chip_init(host_data, i, node); gc = irq_get_domain_generic_chip(domain, i * IRQS_PER_BANK); @@ -815,50 +790,130 @@ static const struct irq_domain_ops stm32_exti_h_domain_ops = { .xlate = irq_domain_xlate_twocell, }; -static int -__init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data, - struct device_node *node, - struct device_node *parent) +static void stm32_exti_remove_irq(void *data) +{ + struct irq_domain *domain = data; + + irq_domain_remove(domain); +} + +static int stm32_exti_remove(struct platform_device *pdev) +{ + stm32_exti_h_syscore_deinit(); + return 0; +} + +static int stm32_exti_probe(struct platform_device *pdev) { + int ret, i; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; struct irq_domain *parent_domain, *domain; struct stm32_exti_host_data *host_data; - int ret, i; + const struct stm32_exti_drv_data *drv_data; + struct resource *res; - parent_domain = irq_find_host(parent); - if (!parent_domain) { - pr_err("interrupt-parent not found\n"); - return -EINVAL; + host_data = devm_kzalloc(dev, sizeof(*host_data), GFP_KERNEL); + if (!host_data) + return -ENOMEM; + + /* check for optional hwspinlock which may be not available yet */ + ret = of_hwspin_lock_get_id(np, 0); + if (ret == -EPROBE_DEFER) + /* hwspinlock framework not yet ready */ + return ret; + + if (ret >= 0) { + host_data->hwlock = devm_hwspin_lock_request_specific(dev, ret); + if (!host_data->hwlock) { + dev_err(dev, "Failed to request hwspinlock\n"); + return -EINVAL; + } + } else if (ret != -ENOENT) { + /* note: ENOENT is a valid case (means 'no hwspinlock') */ + dev_err(dev, "Failed to get hwspinlock\n"); + return ret; } - host_data = stm32_exti_host_init(drv_data, node); - if (!host_data) + /* initialize host_data */ + drv_data = of_device_get_match_data(dev); + if (!drv_data) { + dev_err(dev, "no of match data\n"); + return -ENODEV; + } + host_data->drv_data = drv_data; + + host_data->chips_data = devm_kcalloc(dev, drv_data->bank_nr, + sizeof(*host_data->chips_data), + GFP_KERNEL); + if (!host_data->chips_data) return -ENOMEM; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host_data->base = devm_ioremap_resource(dev, res); + if (IS_ERR(host_data->base)) { + dev_err(dev, "Unable to map registers\n"); + return PTR_ERR(host_data->base); + } + for (i = 0; i < drv_data->bank_nr; i++) - stm32_exti_chip_init(host_data, i); + stm32_exti_chip_init(host_data, i, np); + + parent_domain = irq_find_host(of_irq_find_parent(np)); + if (!parent_domain) { + dev_err(dev, "GIC interrupt-parent not found\n"); + return -EINVAL; + } domain = irq_domain_add_hierarchy(parent_domain, 0, drv_data->bank_nr * IRQS_PER_BANK, - node, &stm32_exti_h_domain_ops, + np, &stm32_exti_h_domain_ops, host_data); if (!domain) { - pr_err("%pOFn: Could not register exti domain.\n", node); - ret = -ENOMEM; - goto out_unmap; + dev_err(dev, "Could not register exti domain\n"); + return -ENOMEM; } - stm32_exti_h_syscore_init(); + ret = devm_add_action_or_reset(dev, stm32_exti_remove_irq, domain); + if (ret) + return ret; + + stm32_exti_h_syscore_init(host_data); return 0; +} -out_unmap: - iounmap(host_data->base); - kfree(host_data->chips_data); - kfree(host_data); - return ret; +/* platform driver only for MP1 */ +static const struct of_device_id stm32_exti_ids[] = { + { .compatible = "st,stm32mp1-exti", .data = &stm32mp1_drv_data}, + {}, +}; +MODULE_DEVICE_TABLE(of, stm32_exti_ids); + +static struct platform_driver stm32_exti_driver = { + .probe = stm32_exti_probe, + .remove = stm32_exti_remove, + .driver = { + .name = "stm32_exti", + .of_match_table = stm32_exti_ids, + }, +}; + +static int __init stm32_exti_arch_init(void) +{ + return platform_driver_register(&stm32_exti_driver); } +static void __exit stm32_exti_arch_exit(void) +{ + return platform_driver_unregister(&stm32_exti_driver); +} + +arch_initcall(stm32_exti_arch_init); +module_exit(stm32_exti_arch_exit); + +/* no platform driver for F4 and H7 */ static int __init stm32f4_exti_of_init(struct device_node *np, struct device_node *parent) { @@ -874,11 +929,3 @@ static int __init stm32h7_exti_of_init(struct device_node *np, } IRQCHIP_DECLARE(stm32h7_exti, "st,stm32h7-exti", stm32h7_exti_of_init); - -static int __init stm32mp1_exti_of_init(struct device_node *np, - struct device_node *parent) -{ - return stm32_exti_hierarchy_init(&stm32mp1_drv_data, np, parent); -} - -IRQCHIP_DECLARE(stm32mp1_exti, "st,stm32mp1-exti", stm32mp1_exti_of_init); -- cgit v1.2.3 From b31a383852b95b47996ed1789288f6bdf6f7ec25 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 12 Mar 2019 18:33:47 +0100 Subject: irqchip/gic-v3-its: Move allocation outside mutex There's no reason to do the allocation of the new lpi_range inside the lpi_range_lock. One could change the code to avoid the allocation altogether in case the freed range can be merged with one or two existing ranges (in which case the allocation would naturally be done under the lock), but it's probably not worth complicating the code for that. Signed-off-by: Rasmus Villemoes Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 8d5936c899a6..be6ecd9b75da 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1532,22 +1532,19 @@ static int alloc_lpi_range(u32 nr_lpis, u32 *base) static int free_lpi_range(u32 base, u32 nr_lpis) { struct lpi_range *new; - int err = 0; - - mutex_lock(&lpi_range_lock); new = mk_lpi_range(base, nr_lpis); - if (!new) { - err = -ENOMEM; - goto out; - } + if (!new) + return -ENOMEM; + + mutex_lock(&lpi_range_lock); list_add(&new->entry, &lpi_range_list); list_sort(NULL, &lpi_range_list, lpi_range_cmp); merge_lpi_ranges(); -out: + mutex_unlock(&lpi_range_lock); - return err; + return 0; } static int __init its_lpi_init(u32 id_bits) -- cgit v1.2.3 From 1c73fac50d83274ebc221bc8d42b6477b3c82405 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 12 Mar 2019 18:33:48 +0100 Subject: irqchip/gic-v3-its: Drop redundant initialization in mk_lpi_range There's no reason to ask kmalloc() to zero the allocation, since all the fields get initialized immediately afterwards. Except that there's also not any reason to initialize the ->entry member, since the element gets added to the lpi_range_list immediately. Signed-off-by: Rasmus Villemoes Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index be6ecd9b75da..db29df1ae92f 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1465,9 +1465,8 @@ static struct lpi_range *mk_lpi_range(u32 base, u32 span) { struct lpi_range *range; - range = kzalloc(sizeof(*range), GFP_KERNEL); + range = kmalloc(sizeof(*range), GFP_KERNEL); if (range) { - INIT_LIST_HEAD(&range->entry); range->base_id = base; range->span = span; } -- cgit v1.2.3 From 12eade123e502ecaa3bf980eaa155201b9093a95 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 12 Mar 2019 18:33:49 +0100 Subject: irqchip/gic-v3-its: Make free_lpi_range a little cheaper Using list_add + list_sort to insert an element and keeping the list sorted is a somewhat blunt instrument; one can find the right place to insert in fewer lines of code than the cmp callback uses. Moreover, walking the entire list afterwards to merge adjacent ranges is overkill, since we know that only the just-inserted element may be merged with its neighbours. Signed-off-by: Rasmus Villemoes Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 61 ++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index db29df1ae92f..75d396b9b666 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -1474,31 +1473,6 @@ static struct lpi_range *mk_lpi_range(u32 base, u32 span) return range; } -static int lpi_range_cmp(void *priv, struct list_head *a, struct list_head *b) -{ - struct lpi_range *ra, *rb; - - ra = container_of(a, struct lpi_range, entry); - rb = container_of(b, struct lpi_range, entry); - - return ra->base_id - rb->base_id; -} - -static void merge_lpi_ranges(void) -{ - struct lpi_range *range, *tmp; - - list_for_each_entry_safe(range, tmp, &lpi_range_list, entry) { - if (!list_is_last(&range->entry, &lpi_range_list) && - (tmp->base_id == (range->base_id + range->span))) { - tmp->base_id = range->base_id; - tmp->span += range->span; - list_del(&range->entry); - kfree(range); - } - } -} - static int alloc_lpi_range(u32 nr_lpis, u32 *base) { struct lpi_range *range, *tmp; @@ -1528,9 +1502,21 @@ static int alloc_lpi_range(u32 nr_lpis, u32 *base) return err; } +static void merge_lpi_ranges(struct lpi_range *a, struct lpi_range *b) +{ + if (&a->entry == &lpi_range_list || &b->entry == &lpi_range_list) + return; + if (a->base_id + a->span != b->base_id) + return; + b->base_id = a->base_id; + b->span += a->span; + list_del(&a->entry); + kfree(a); +} + static int free_lpi_range(u32 base, u32 nr_lpis) { - struct lpi_range *new; + struct lpi_range *new, *old; new = mk_lpi_range(base, nr_lpis); if (!new) @@ -1538,9 +1524,24 @@ static int free_lpi_range(u32 base, u32 nr_lpis) mutex_lock(&lpi_range_lock); - list_add(&new->entry, &lpi_range_list); - list_sort(NULL, &lpi_range_list, lpi_range_cmp); - merge_lpi_ranges(); + list_for_each_entry_reverse(old, &lpi_range_list, entry) { + if (old->base_id < base) + break; + } + /* + * old is the last element with ->base_id smaller than base, + * so new goes right after it. If there are no elements with + * ->base_id smaller than base, &old->entry ends up pointing + * at the head of the list, and inserting new it the start of + * the list is the right thing to do in that case as well. + */ + list_add(&new->entry, &old->entry); + /* + * Now check if we can merge with the preceding and/or + * following ranges. + */ + merge_lpi_ranges(old, new); + merge_lpi_ranges(new, list_next_entry(new, entry)); mutex_unlock(&lpi_range_lock); return 0; -- cgit v1.2.3 From 66373bf67141e7d57c123adfc4162a696411fd23 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 29 Apr 2019 17:12:03 +0200 Subject: irqchip: Remove unneeded select IRQ_DOMAIN IRQ_DOMAIN_HIERARCHY selects IRQ_DOMAIN, hence there is no need for drivers to select both. Signed-off-by: Geert Uytterhoeven Signed-off-by: Marc Zyngier --- drivers/irqchip/Kconfig | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 5438abb1baba..c4ed19cb2be7 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -6,7 +6,6 @@ config IRQCHIP config ARM_GIC bool - select IRQ_DOMAIN select IRQ_DOMAIN_HIERARCHY select GENERIC_IRQ_MULTI_HANDLER select GENERIC_IRQ_EFFECTIVE_AFF_MASK @@ -33,7 +32,6 @@ config GIC_NON_BANKED config ARM_GIC_V3 bool - select IRQ_DOMAIN select GENERIC_IRQ_MULTI_HANDLER select IRQ_DOMAIN_HIERARCHY select PARTITION_PERCPU @@ -59,7 +57,6 @@ config ARM_GIC_V3_ITS_FSL_MC config ARM_NVIC bool - select IRQ_DOMAIN select IRQ_DOMAIN_HIERARCHY select GENERIC_IRQ_CHIP @@ -352,7 +349,6 @@ config STM32_EXTI config QCOM_IRQ_COMBINER bool "QCOM IRQ combiner support" depends on ARCH_QCOM && ACPI - select IRQ_DOMAIN select IRQ_DOMAIN_HIERARCHY help Say yes here to add support for the IRQ combiner devices embedded @@ -369,7 +365,6 @@ config IRQ_UNIPHIER_AIDET config MESON_IRQ_GPIO bool "Meson GPIO Interrupt Multiplexer" depends on ARCH_MESON - select IRQ_DOMAIN select IRQ_DOMAIN_HIERARCHY help Support Meson SoC Family GPIO Interrupt Multiplexer @@ -385,7 +380,6 @@ config GOLDFISH_PIC config QCOM_PDC bool "QCOM PDC" depends on ARCH_QCOM - select IRQ_DOMAIN select IRQ_DOMAIN_HIERARCHY help Power Domain Controller driver to manage and configure wakeup -- cgit v1.2.3 From 89626d4bad8199367e2085fd2408b4d63df79f43 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 29 Apr 2019 17:15:14 +0200 Subject: irqchip/renesas-intc-irqpin: Remove devm_kzalloc() error printing There is no need to print a message if devm_kzalloc() fails, as the memory allocation core already takes care of that. Signed-off-by: Geert Uytterhoeven Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-renesas-intc-irqpin.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index 8c039525703f..04c05a18600c 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -389,10 +389,8 @@ static int intc_irqpin_probe(struct platform_device *pdev) int k; p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); - if (!p) { - dev_err(dev, "failed to allocate driver data\n"); + if (!p) return -ENOMEM; - } /* deal with driver instance configuration */ of_property_read_u32(dev->of_node, "sense-bitfield-width", -- cgit v1.2.3 From 905c30477f4d88ea535c6970c50bbb159d733249 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 30 Apr 2019 15:42:17 +0530 Subject: firmware: ti_sci: Add support to get TISCI handle using of_phandle TISCI has been updated to have support for Resource management(like interrupts etc..). And there can be multiple device instances of a resource type in a SoC. So every driver corresponding to a resource type should get a TISCI handle so that it can make TISCI calls. And each DT node corresponding to a device should exist under its corresponding bus node as per the SoC architecture. But existing apis in TISCI library assumes that all TISCI users are child nodes of TISCI. Which is not true in the above case. So introduce (devm_)ti_sci_get_by_phandle() apis that can be used by TISCI users to get TISCI handle using of phandle property. Signed-off-by: Grygorii Strashko Signed-off-by: Lokesh Vutla Acked-by: Nishanth Menon Signed-off-by: Marc Zyngier --- drivers/firmware/ti_sci.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 3fbbb61012c4..852043531233 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -1764,6 +1764,89 @@ const struct ti_sci_handle *devm_ti_sci_get_handle(struct device *dev) } EXPORT_SYMBOL_GPL(devm_ti_sci_get_handle); +/** + * ti_sci_get_by_phandle() - Get the TI SCI handle using DT phandle + * @np: device node + * @property: property name containing phandle on TISCI node + * + * NOTE: The function does not track individual clients of the framework + * and is expected to be maintained by caller of TI SCI protocol library. + * ti_sci_put_handle must be balanced with successful ti_sci_get_by_phandle + * Return: pointer to handle if successful, else: + * -EPROBE_DEFER if the instance is not ready + * -ENODEV if the required node handler is missing + * -EINVAL if invalid conditions are encountered. + */ +const struct ti_sci_handle *ti_sci_get_by_phandle(struct device_node *np, + const char *property) +{ + struct ti_sci_handle *handle = NULL; + struct device_node *ti_sci_np; + struct ti_sci_info *info; + struct list_head *p; + + if (!np) { + pr_err("I need a device pointer\n"); + return ERR_PTR(-EINVAL); + } + + ti_sci_np = of_parse_phandle(np, property, 0); + if (!ti_sci_np) + return ERR_PTR(-ENODEV); + + mutex_lock(&ti_sci_list_mutex); + list_for_each(p, &ti_sci_list) { + info = list_entry(p, struct ti_sci_info, node); + if (ti_sci_np == info->dev->of_node) { + handle = &info->handle; + info->users++; + break; + } + } + mutex_unlock(&ti_sci_list_mutex); + of_node_put(ti_sci_np); + + if (!handle) + return ERR_PTR(-EPROBE_DEFER); + + return handle; +} +EXPORT_SYMBOL_GPL(ti_sci_get_by_phandle); + +/** + * devm_ti_sci_get_by_phandle() - Managed get handle using phandle + * @dev: Device pointer requesting TISCI handle + * @property: property name containing phandle on TISCI node + * + * NOTE: This releases the handle once the device resources are + * no longer needed. MUST NOT BE released with ti_sci_put_handle. + * The function does not track individual clients of the framework + * and is expected to be maintained by caller of TI SCI protocol library. + * + * Return: 0 if all went fine, else corresponding error. + */ +const struct ti_sci_handle *devm_ti_sci_get_by_phandle(struct device *dev, + const char *property) +{ + const struct ti_sci_handle *handle; + const struct ti_sci_handle **ptr; + + ptr = devres_alloc(devm_ti_sci_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + handle = ti_sci_get_by_phandle(dev_of_node(dev), property); + + if (!IS_ERR(handle)) { + *ptr = handle; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return handle; +} +EXPORT_SYMBOL_GPL(devm_ti_sci_get_by_phandle); + static int tisci_reboot_handler(struct notifier_block *nb, unsigned long mode, void *cmd) { -- cgit v1.2.3 From 9c19fb6895bef1c350087e57afebc0847adf960d Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Tue, 30 Apr 2019 15:42:18 +0530 Subject: firmware: ti_sci: Add support for RM core ops TISCI provides support for getting the resources(IRQ, RING etc..) assigned to a specific device. These resources can be handled by the client and in turn sends TISCI cmd to configure the resources. It is very important that client should keep track on usage of these resources. Add support for TISCI commands to get resource ranges. Signed-off-by: Lokesh Vutla Signed-off-by: Peter Ujfalusi Acked-by: Nishanth Menon Signed-off-by: Marc Zyngier --- drivers/firmware/ti_sci.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/firmware/ti_sci.h | 42 ++++++++++++ 2 files changed, 212 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 852043531233..503195223c09 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -64,6 +64,22 @@ struct ti_sci_xfers_info { spinlock_t xfer_lock; }; +/** + * struct ti_sci_rm_type_map - Structure representing TISCI Resource + * management representation of dev_ids. + * @dev_id: TISCI device ID + * @type: Corresponding id as identified by TISCI RM. + * + * Note: This is used only as a work around for using RM range apis + * for AM654 SoC. For future SoCs dev_id will be used as type + * for RM range APIs. In order to maintain ABI backward compatibility + * type is not being changed for AM654 SoC. + */ +struct ti_sci_rm_type_map { + u32 dev_id; + u16 type; +}; + /** * struct ti_sci_desc - Description of SoC integration * @default_host_id: Host identifier representing the compute entity @@ -71,12 +87,14 @@ struct ti_sci_xfers_info { * @max_msgs: Maximum number of messages that can be pending * simultaneously in the system * @max_msg_size: Maximum size of data per message that can be handled. + * @rm_type_map: RM resource type mapping structure. */ struct ti_sci_desc { u8 default_host_id; int max_rx_timeout_ms; int max_msgs; int max_msg_size; + struct ti_sci_rm_type_map *rm_type_map; }; /** @@ -1600,6 +1618,153 @@ fail: return ret; } +static int ti_sci_get_resource_type(struct ti_sci_info *info, u16 dev_id, + u16 *type) +{ + struct ti_sci_rm_type_map *rm_type_map = info->desc->rm_type_map; + bool found = false; + int i; + + /* If map is not provided then assume dev_id is used as type */ + if (!rm_type_map) { + *type = dev_id; + return 0; + } + + for (i = 0; rm_type_map[i].dev_id; i++) { + if (rm_type_map[i].dev_id == dev_id) { + *type = rm_type_map[i].type; + found = true; + break; + } + } + + if (!found) + return -EINVAL; + + return 0; +} + +/** + * ti_sci_get_resource_range - Helper to get a range of resources assigned + * to a host. Resource is uniquely identified by + * type and subtype. + * @handle: Pointer to TISCI handle. + * @dev_id: TISCI device ID. + * @subtype: Resource assignment subtype that is being requested + * from the given device. + * @s_host: Host processor ID to which the resources are allocated + * @range_start: Start index of the resource range + * @range_num: Number of resources in the range + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_get_resource_range(const struct ti_sci_handle *handle, + u32 dev_id, u8 subtype, u8 s_host, + u16 *range_start, u16 *range_num) +{ + struct ti_sci_msg_resp_get_resource_range *resp; + struct ti_sci_msg_req_get_resource_range *req; + struct ti_sci_xfer *xfer; + struct ti_sci_info *info; + struct device *dev; + u16 type; + int ret = 0; + + if (IS_ERR(handle)) + return PTR_ERR(handle); + if (!handle) + return -EINVAL; + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_GET_RESOURCE_RANGE, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "Message alloc failed(%d)\n", ret); + return ret; + } + + ret = ti_sci_get_resource_type(info, dev_id, &type); + if (ret) { + dev_err(dev, "rm type lookup failed for %u\n", dev_id); + goto fail; + } + + req = (struct ti_sci_msg_req_get_resource_range *)xfer->xfer_buf; + req->secondary_host = s_host; + req->type = type & MSG_RM_RESOURCE_TYPE_MASK; + req->subtype = subtype & MSG_RM_RESOURCE_SUBTYPE_MASK; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "Mbox send fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_resp_get_resource_range *)xfer->xfer_buf; + + if (!ti_sci_is_response_ack(resp)) { + ret = -ENODEV; + } else if (!resp->range_start && !resp->range_num) { + ret = -ENODEV; + } else { + *range_start = resp->range_start; + *range_num = resp->range_num; + }; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + + return ret; +} + +/** + * ti_sci_cmd_get_resource_range - Get a range of resources assigned to host + * that is same as ti sci interface host. + * @handle: Pointer to TISCI handle. + * @dev_id: TISCI device ID. + * @subtype: Resource assignment subtype that is being requested + * from the given device. + * @range_start: Start index of the resource range + * @range_num: Number of resources in the range + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_get_resource_range(const struct ti_sci_handle *handle, + u32 dev_id, u8 subtype, + u16 *range_start, u16 *range_num) +{ + return ti_sci_get_resource_range(handle, dev_id, subtype, + TI_SCI_IRQ_SECONDARY_HOST_INVALID, + range_start, range_num); +} + +/** + * ti_sci_cmd_get_resource_range_from_shost - Get a range of resources + * assigned to a specified host. + * @handle: Pointer to TISCI handle. + * @dev_id: TISCI device ID. + * @subtype: Resource assignment subtype that is being requested + * from the given device. + * @s_host: Host processor ID to which the resources are allocated + * @range_start: Start index of the resource range + * @range_num: Number of resources in the range + * + * Return: 0 if all went fine, else return appropriate error. + */ +static +int ti_sci_cmd_get_resource_range_from_shost(const struct ti_sci_handle *handle, + u32 dev_id, u8 subtype, u8 s_host, + u16 *range_start, u16 *range_num) +{ + return ti_sci_get_resource_range(handle, dev_id, subtype, s_host, + range_start, range_num); +} + /* * ti_sci_setup_ops() - Setup the operations structures * @info: pointer to TISCI pointer @@ -1610,6 +1775,7 @@ static void ti_sci_setup_ops(struct ti_sci_info *info) struct ti_sci_core_ops *core_ops = &ops->core_ops; struct ti_sci_dev_ops *dops = &ops->dev_ops; struct ti_sci_clk_ops *cops = &ops->clk_ops; + struct ti_sci_rm_core_ops *rm_core_ops = &ops->rm_core_ops; core_ops->reboot_device = ti_sci_cmd_core_reboot; @@ -1640,6 +1806,10 @@ static void ti_sci_setup_ops(struct ti_sci_info *info) cops->get_best_match_freq = ti_sci_cmd_clk_get_match_freq; cops->set_freq = ti_sci_cmd_clk_set_freq; cops->get_freq = ti_sci_cmd_clk_get_freq; + + rm_core_ops->get_range = ti_sci_cmd_get_resource_range; + rm_core_ops->get_range_from_shost = + ti_sci_cmd_get_resource_range_from_shost; } /** diff --git a/drivers/firmware/ti_sci.h b/drivers/firmware/ti_sci.h index 12bf316b68df..a043c4762791 100644 --- a/drivers/firmware/ti_sci.h +++ b/drivers/firmware/ti_sci.h @@ -35,6 +35,9 @@ #define TI_SCI_MSG_QUERY_CLOCK_FREQ 0x010d #define TI_SCI_MSG_GET_CLOCK_FREQ 0x010e +/* Resource Management Requests */ +#define TI_SCI_MSG_GET_RESOURCE_RANGE 0x1500 + /** * struct ti_sci_msg_hdr - Generic Message Header for All messages and responses * @type: Type of messages: One of TI_SCI_MSG* values @@ -461,4 +464,43 @@ struct ti_sci_msg_resp_get_clock_freq { u64 freq_hz; } __packed; +#define TI_SCI_IRQ_SECONDARY_HOST_INVALID 0xff + +/** + * struct ti_sci_msg_req_get_resource_range - Request to get a host's assigned + * range of resources. + * @hdr: Generic Header + * @type: Unique resource assignment type + * @subtype: Resource assignment subtype within the resource type. + * @secondary_host: Host processing entity to which the resources are + * allocated. This is required only when the destination + * host id id different from ti sci interface host id, + * else TI_SCI_IRQ_SECONDARY_HOST_INVALID can be passed. + * + * Request type is TI_SCI_MSG_GET_RESOURCE_RANGE. Responded with requested + * resource range which is of type TI_SCI_MSG_GET_RESOURCE_RANGE. + */ +struct ti_sci_msg_req_get_resource_range { + struct ti_sci_msg_hdr hdr; +#define MSG_RM_RESOURCE_TYPE_MASK GENMASK(9, 0) +#define MSG_RM_RESOURCE_SUBTYPE_MASK GENMASK(5, 0) + u16 type; + u8 subtype; + u8 secondary_host; +} __packed; + +/** + * struct ti_sci_msg_resp_get_resource_range - Response to resource get range. + * @hdr: Generic Header + * @range_start: Start index of the resource range. + * @range_num: Number of resources in the range. + * + * Response to request TI_SCI_MSG_GET_RESOURCE_RANGE. + */ +struct ti_sci_msg_resp_get_resource_range { + struct ti_sci_msg_hdr hdr; + u16 range_start; + u16 range_num; +} __packed; + #endif /* __TI_SCI_H */ -- cgit v1.2.3 From 997b001f6bb2ede3043bdf80b62a5a4dd466d7eb Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Tue, 30 Apr 2019 15:42:19 +0530 Subject: firmware: ti_sci: Add support for IRQ management TISCI abstracts the handling of IRQ routes where interrupt sources are not directly connected to host interrupt controller. Add support for the set of TISCI commands for requesting and releasing IRQs. Signed-off-by: Lokesh Vutla Acked-by: Nishanth Menon Signed-off-by: Marc Zyngier --- drivers/firmware/ti_sci.c | 245 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/firmware/ti_sci.h | 60 ++++++++++++ 2 files changed, 305 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 503195223c09..4ccdf9a53648 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -1765,6 +1765,245 @@ int ti_sci_cmd_get_resource_range_from_shost(const struct ti_sci_handle *handle, range_start, range_num); } +/** + * ti_sci_manage_irq() - Helper api to configure/release the irq route between + * the requested source and destination + * @handle: Pointer to TISCI handle. + * @valid_params: Bit fields defining the validity of certain params + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @dst_id: Device ID of the IRQ destination + * @dst_host_irq: IRQ number of the destination device + * @ia_id: Device ID of the IA, if the IRQ flows through this IA + * @vint: Virtual interrupt to be used within the IA + * @global_event: Global event number to be used for the requesting event + * @vint_status_bit: Virtual interrupt status bit to be used for the event + * @s_host: Secondary host ID to which the irq/event is being + * requested for. + * @type: Request type irq set or release. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_manage_irq(const struct ti_sci_handle *handle, + u32 valid_params, u16 src_id, u16 src_index, + u16 dst_id, u16 dst_host_irq, u16 ia_id, u16 vint, + u16 global_event, u8 vint_status_bit, u8 s_host, + u16 type) +{ + struct ti_sci_msg_req_manage_irq *req; + struct ti_sci_msg_hdr *resp; + struct ti_sci_xfer *xfer; + struct ti_sci_info *info; + struct device *dev; + int ret = 0; + + if (IS_ERR(handle)) + return PTR_ERR(handle); + if (!handle) + return -EINVAL; + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, type, TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "Message alloc failed(%d)\n", ret); + return ret; + } + req = (struct ti_sci_msg_req_manage_irq *)xfer->xfer_buf; + req->valid_params = valid_params; + req->src_id = src_id; + req->src_index = src_index; + req->dst_id = dst_id; + req->dst_host_irq = dst_host_irq; + req->ia_id = ia_id; + req->vint = vint; + req->global_event = global_event; + req->vint_status_bit = vint_status_bit; + req->secondary_host = s_host; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "Mbox send fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_hdr *)xfer->xfer_buf; + + ret = ti_sci_is_response_ack(resp) ? 0 : -ENODEV; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + + return ret; +} + +/** + * ti_sci_set_irq() - Helper api to configure the irq route between the + * requested source and destination + * @handle: Pointer to TISCI handle. + * @valid_params: Bit fields defining the validity of certain params + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @dst_id: Device ID of the IRQ destination + * @dst_host_irq: IRQ number of the destination device + * @ia_id: Device ID of the IA, if the IRQ flows through this IA + * @vint: Virtual interrupt to be used within the IA + * @global_event: Global event number to be used for the requesting event + * @vint_status_bit: Virtual interrupt status bit to be used for the event + * @s_host: Secondary host ID to which the irq/event is being + * requested for. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_set_irq(const struct ti_sci_handle *handle, u32 valid_params, + u16 src_id, u16 src_index, u16 dst_id, + u16 dst_host_irq, u16 ia_id, u16 vint, + u16 global_event, u8 vint_status_bit, u8 s_host) +{ + pr_debug("%s: IRQ set with valid_params = 0x%x from src = %d, index = %d, to dst = %d, irq = %d,via ia_id = %d, vint = %d, global event = %d,status_bit = %d\n", + __func__, valid_params, src_id, src_index, + dst_id, dst_host_irq, ia_id, vint, global_event, + vint_status_bit); + + return ti_sci_manage_irq(handle, valid_params, src_id, src_index, + dst_id, dst_host_irq, ia_id, vint, + global_event, vint_status_bit, s_host, + TI_SCI_MSG_SET_IRQ); +} + +/** + * ti_sci_free_irq() - Helper api to free the irq route between the + * requested source and destination + * @handle: Pointer to TISCI handle. + * @valid_params: Bit fields defining the validity of certain params + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @dst_id: Device ID of the IRQ destination + * @dst_host_irq: IRQ number of the destination device + * @ia_id: Device ID of the IA, if the IRQ flows through this IA + * @vint: Virtual interrupt to be used within the IA + * @global_event: Global event number to be used for the requesting event + * @vint_status_bit: Virtual interrupt status bit to be used for the event + * @s_host: Secondary host ID to which the irq/event is being + * requested for. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_free_irq(const struct ti_sci_handle *handle, u32 valid_params, + u16 src_id, u16 src_index, u16 dst_id, + u16 dst_host_irq, u16 ia_id, u16 vint, + u16 global_event, u8 vint_status_bit, u8 s_host) +{ + pr_debug("%s: IRQ release with valid_params = 0x%x from src = %d, index = %d, to dst = %d, irq = %d,via ia_id = %d, vint = %d, global event = %d,status_bit = %d\n", + __func__, valid_params, src_id, src_index, + dst_id, dst_host_irq, ia_id, vint, global_event, + vint_status_bit); + + return ti_sci_manage_irq(handle, valid_params, src_id, src_index, + dst_id, dst_host_irq, ia_id, vint, + global_event, vint_status_bit, s_host, + TI_SCI_MSG_FREE_IRQ); +} + +/** + * ti_sci_cmd_set_irq() - Configure a host irq route between the requested + * source and destination. + * @handle: Pointer to TISCI handle. + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @dst_id: Device ID of the IRQ destination + * @dst_host_irq: IRQ number of the destination device + * @vint_irq: Boolean specifying if this interrupt belongs to + * Interrupt Aggregator. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_set_irq(const struct ti_sci_handle *handle, u16 src_id, + u16 src_index, u16 dst_id, u16 dst_host_irq) +{ + u32 valid_params = MSG_FLAG_DST_ID_VALID | MSG_FLAG_DST_HOST_IRQ_VALID; + + return ti_sci_set_irq(handle, valid_params, src_id, src_index, dst_id, + dst_host_irq, 0, 0, 0, 0, 0); +} + +/** + * ti_sci_cmd_set_event_map() - Configure an event based irq route between the + * requested source and Interrupt Aggregator. + * @handle: Pointer to TISCI handle. + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @ia_id: Device ID of the IA, if the IRQ flows through this IA + * @vint: Virtual interrupt to be used within the IA + * @global_event: Global event number to be used for the requesting event + * @vint_status_bit: Virtual interrupt status bit to be used for the event + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_set_event_map(const struct ti_sci_handle *handle, + u16 src_id, u16 src_index, u16 ia_id, + u16 vint, u16 global_event, + u8 vint_status_bit) +{ + u32 valid_params = MSG_FLAG_IA_ID_VALID | MSG_FLAG_VINT_VALID | + MSG_FLAG_GLB_EVNT_VALID | + MSG_FLAG_VINT_STS_BIT_VALID; + + return ti_sci_set_irq(handle, valid_params, src_id, src_index, 0, 0, + ia_id, vint, global_event, vint_status_bit, 0); +} + +/** + * ti_sci_cmd_free_irq() - Free a host irq route between the between the + * requested source and destination. + * @handle: Pointer to TISCI handle. + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @dst_id: Device ID of the IRQ destination + * @dst_host_irq: IRQ number of the destination device + * @vint_irq: Boolean specifying if this interrupt belongs to + * Interrupt Aggregator. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_free_irq(const struct ti_sci_handle *handle, u16 src_id, + u16 src_index, u16 dst_id, u16 dst_host_irq) +{ + u32 valid_params = MSG_FLAG_DST_ID_VALID | MSG_FLAG_DST_HOST_IRQ_VALID; + + return ti_sci_free_irq(handle, valid_params, src_id, src_index, dst_id, + dst_host_irq, 0, 0, 0, 0, 0); +} + +/** + * ti_sci_cmd_free_event_map() - Free an event map between the requested source + * and Interrupt Aggregator. + * @handle: Pointer to TISCI handle. + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @ia_id: Device ID of the IA, if the IRQ flows through this IA + * @vint: Virtual interrupt to be used within the IA + * @global_event: Global event number to be used for the requesting event + * @vint_status_bit: Virtual interrupt status bit to be used for the event + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_free_event_map(const struct ti_sci_handle *handle, + u16 src_id, u16 src_index, u16 ia_id, + u16 vint, u16 global_event, + u8 vint_status_bit) +{ + u32 valid_params = MSG_FLAG_IA_ID_VALID | + MSG_FLAG_VINT_VALID | MSG_FLAG_GLB_EVNT_VALID | + MSG_FLAG_VINT_STS_BIT_VALID; + + return ti_sci_free_irq(handle, valid_params, src_id, src_index, 0, 0, + ia_id, vint, global_event, vint_status_bit, 0); +} + /* * ti_sci_setup_ops() - Setup the operations structures * @info: pointer to TISCI pointer @@ -1776,6 +2015,7 @@ static void ti_sci_setup_ops(struct ti_sci_info *info) struct ti_sci_dev_ops *dops = &ops->dev_ops; struct ti_sci_clk_ops *cops = &ops->clk_ops; struct ti_sci_rm_core_ops *rm_core_ops = &ops->rm_core_ops; + struct ti_sci_rm_irq_ops *iops = &ops->rm_irq_ops; core_ops->reboot_device = ti_sci_cmd_core_reboot; @@ -1810,6 +2050,11 @@ static void ti_sci_setup_ops(struct ti_sci_info *info) rm_core_ops->get_range = ti_sci_cmd_get_resource_range; rm_core_ops->get_range_from_shost = ti_sci_cmd_get_resource_range_from_shost; + + iops->set_irq = ti_sci_cmd_set_irq; + iops->set_event_map = ti_sci_cmd_set_event_map; + iops->free_irq = ti_sci_cmd_free_irq; + iops->free_event_map = ti_sci_cmd_free_event_map; } /** diff --git a/drivers/firmware/ti_sci.h b/drivers/firmware/ti_sci.h index a043c4762791..4983827151bf 100644 --- a/drivers/firmware/ti_sci.h +++ b/drivers/firmware/ti_sci.h @@ -38,6 +38,10 @@ /* Resource Management Requests */ #define TI_SCI_MSG_GET_RESOURCE_RANGE 0x1500 +/* IRQ requests */ +#define TI_SCI_MSG_SET_IRQ 0x1000 +#define TI_SCI_MSG_FREE_IRQ 0x1001 + /** * struct ti_sci_msg_hdr - Generic Message Header for All messages and responses * @type: Type of messages: One of TI_SCI_MSG* values @@ -503,4 +507,60 @@ struct ti_sci_msg_resp_get_resource_range { u16 range_num; } __packed; +/** + * struct ti_sci_msg_req_manage_irq - Request to configure/release the route + * between the dev and the host. + * @hdr: Generic Header + * @valid_params: Bit fields defining the validity of interrupt source + * parameters. If a bit is not set, then corresponding + * field is not valid and will not be used for route set. + * Bit field definitions: + * 0 - Valid bit for @dst_id + * 1 - Valid bit for @dst_host_irq + * 2 - Valid bit for @ia_id + * 3 - Valid bit for @vint + * 4 - Valid bit for @global_event + * 5 - Valid bit for @vint_status_bit_index + * 31 - Valid bit for @secondary_host + * @src_id: IRQ source peripheral ID. + * @src_index: IRQ source index within the peripheral + * @dst_id: IRQ Destination ID. Based on the architecture it can be + * IRQ controller or host processor ID. + * @dst_host_irq: IRQ number of the destination host IRQ controller + * @ia_id: Device ID of the interrupt aggregator in which the + * vint resides. + * @vint: Virtual interrupt number if the interrupt route + * is through an interrupt aggregator. + * @global_event: Global event that is to be mapped to interrupt + * aggregator virtual interrupt status bit. + * @vint_status_bit: Virtual interrupt status bit if the interrupt route + * utilizes an interrupt aggregator status bit. + * @secondary_host: Host ID of the IRQ destination computing entity. This is + * required only when destination host id is different + * from ti sci interface host id. + * + * Request type is TI_SCI_MSG_SET/RELEASE_IRQ. + * Response is generic ACK / NACK message. + */ +struct ti_sci_msg_req_manage_irq { + struct ti_sci_msg_hdr hdr; +#define MSG_FLAG_DST_ID_VALID TI_SCI_MSG_FLAG(0) +#define MSG_FLAG_DST_HOST_IRQ_VALID TI_SCI_MSG_FLAG(1) +#define MSG_FLAG_IA_ID_VALID TI_SCI_MSG_FLAG(2) +#define MSG_FLAG_VINT_VALID TI_SCI_MSG_FLAG(3) +#define MSG_FLAG_GLB_EVNT_VALID TI_SCI_MSG_FLAG(4) +#define MSG_FLAG_VINT_STS_BIT_VALID TI_SCI_MSG_FLAG(5) +#define MSG_FLAG_SHOST_VALID TI_SCI_MSG_FLAG(31) + u32 valid_params; + u16 src_id; + u16 src_index; + u16 dst_id; + u16 dst_host_irq; + u16 ia_id; + u16 vint; + u16 global_event; + u8 vint_status_bit; + u8 secondary_host; +} __packed; + #endif /* __TI_SCI_H */ -- cgit v1.2.3 From 754c9477ae78727d31692c76190b5567c567f435 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 30 Apr 2019 15:42:20 +0530 Subject: firmware: ti_sci: Add RM mapping table for am654 Add the resource mapping table for AM654 SoC as defined in http://downloads.ti.com/tisci/esd/latest/5_soc_doc/am6x/resasg_types.html Introduce a new compatible for AM654 "ti,am654-sci" for using this resource map table. Reviewed-by: Rob Herring Acked-by: Tony Lindgren Signed-off-by: Peter Ujfalusi Signed-off-by: Lokesh Vutla Acked-by: Nishanth Menon Signed-off-by: Marc Zyngier --- drivers/firmware/ti_sci.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 4ccdf9a53648..551895535767 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -2282,10 +2282,33 @@ static const struct ti_sci_desc ti_sci_pmmc_k2g_desc = { /* Limited by MBOX_TX_QUEUE_LEN. K2G can handle upto 128 messages! */ .max_msgs = 20, .max_msg_size = 64, + .rm_type_map = NULL, +}; + +static struct ti_sci_rm_type_map ti_sci_am654_rm_type_map[] = { + {.dev_id = 56, .type = 0x00b}, /* GIC_IRQ */ + {.dev_id = 179, .type = 0x000}, /* MAIN_NAV_UDMASS_IA0 */ + {.dev_id = 187, .type = 0x009}, /* MAIN_NAV_RA */ + {.dev_id = 188, .type = 0x006}, /* MAIN_NAV_UDMAP */ + {.dev_id = 194, .type = 0x007}, /* MCU_NAV_UDMAP */ + {.dev_id = 195, .type = 0x00a}, /* MCU_NAV_RA */ + {.dev_id = 0, .type = 0x000}, /* end of table */ +}; + +/* Description for AM654 */ +static const struct ti_sci_desc ti_sci_pmmc_am654_desc = { + .default_host_id = 12, + /* Conservative duration */ + .max_rx_timeout_ms = 10000, + /* Limited by MBOX_TX_QUEUE_LEN. K2G can handle upto 128 messages! */ + .max_msgs = 20, + .max_msg_size = 60, + .rm_type_map = ti_sci_am654_rm_type_map, }; static const struct of_device_id ti_sci_of_match[] = { {.compatible = "ti,k2g-sci", .data = &ti_sci_pmmc_k2g_desc}, + {.compatible = "ti,am654-sci", .data = &ti_sci_pmmc_am654_desc}, { /* Sentinel */ }, }; MODULE_DEVICE_TABLE(of, ti_sci_of_match); -- cgit v1.2.3 From 032a1ec549a79711078d26b6bcbba9d435822059 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Tue, 30 Apr 2019 15:42:21 +0530 Subject: firmware: ti_sci: Add helper apis to manage resources Each resource with in the device can be uniquely identified as defined by TISCI. Since this is generic across the devices, resource allocation also can be made generic instead of each client driver handling the resource. So add helper apis to manage the resource. Signed-off-by: Lokesh Vutla Acked-by: Nishanth Menon Signed-off-by: Marc Zyngier --- drivers/firmware/ti_sci.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 551895535767..ef93406ace1b 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -2262,6 +2262,136 @@ const struct ti_sci_handle *devm_ti_sci_get_by_phandle(struct device *dev, } EXPORT_SYMBOL_GPL(devm_ti_sci_get_by_phandle); +/** + * ti_sci_get_free_resource() - Get a free resource from TISCI resource. + * @res: Pointer to the TISCI resource + * + * Return: resource num if all went ok else TI_SCI_RESOURCE_NULL. + */ +u16 ti_sci_get_free_resource(struct ti_sci_resource *res) +{ + unsigned long flags; + u16 set, free_bit; + + raw_spin_lock_irqsave(&res->lock, flags); + for (set = 0; set < res->sets; set++) { + free_bit = find_first_zero_bit(res->desc[set].res_map, + res->desc[set].num); + if (free_bit != res->desc[set].num) { + set_bit(free_bit, res->desc[set].res_map); + raw_spin_unlock_irqrestore(&res->lock, flags); + return res->desc[set].start + free_bit; + } + } + raw_spin_unlock_irqrestore(&res->lock, flags); + + return TI_SCI_RESOURCE_NULL; +} +EXPORT_SYMBOL_GPL(ti_sci_get_free_resource); + +/** + * ti_sci_release_resource() - Release a resource from TISCI resource. + * @res: Pointer to the TISCI resource + * @id: Resource id to be released. + */ +void ti_sci_release_resource(struct ti_sci_resource *res, u16 id) +{ + unsigned long flags; + u16 set; + + raw_spin_lock_irqsave(&res->lock, flags); + for (set = 0; set < res->sets; set++) { + if (res->desc[set].start <= id && + (res->desc[set].num + res->desc[set].start) > id) + clear_bit(id - res->desc[set].start, + res->desc[set].res_map); + } + raw_spin_unlock_irqrestore(&res->lock, flags); +} +EXPORT_SYMBOL_GPL(ti_sci_release_resource); + +/** + * ti_sci_get_num_resources() - Get the number of resources in TISCI resource + *