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-70-g09d2 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-70-g09d2 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-70-g09d2 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-70-g09d2 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-70-g09d2 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-70-g09d2 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-70-g09d2 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-70-g09d2 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-70-g09d2 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-70-g09d2 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-70-g09d2 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 ++++++++++++++++++++++++++++++++++ include/linux/soc/ti/ti_sci_protocol.h | 17 +++++++ 2 files changed, 100 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) { diff --git a/include/linux/soc/ti/ti_sci_protocol.h b/include/linux/soc/ti/ti_sci_protocol.h index 18435e5c6364..515587e9d373 100644 --- a/include/linux/soc/ti/ti_sci_protocol.h +++ b/include/linux/soc/ti/ti_sci_protocol.h @@ -217,6 +217,10 @@ struct ti_sci_handle { const struct ti_sci_handle *ti_sci_get_handle(struct device *dev); int ti_sci_put_handle(const struct ti_sci_handle *handle); const struct ti_sci_handle *devm_ti_sci_get_handle(struct device *dev); +const struct ti_sci_handle *ti_sci_get_by_phandle(struct device_node *np, + const char *property); +const struct ti_sci_handle *devm_ti_sci_get_by_phandle(struct device *dev, + const char *property); #else /* CONFIG_TI_SCI_PROTOCOL */ @@ -236,6 +240,19 @@ const struct ti_sci_handle *devm_ti_sci_get_handle(struct device *dev) return ERR_PTR(-EINVAL); } +static inline +const struct ti_sci_handle *ti_sci_get_by_phandle(struct device_node *np, + const char *property) +{ + return ERR_PTR(-EINVAL); +} + +static inline +const struct ti_sci_handle *devm_ti_sci_get_by_phandle(struct device *dev, + const char *property) +{ + return ERR_PTR(-EINVAL); +} #endif /* CONFIG_TI_SCI_PROTOCOL */ #endif /* __TISCI_PROTOCOL_H */ -- cgit v1.2.3-70-g09d2 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 ++++++++ include/linux/soc/ti/ti_sci_protocol.h | 27 ++++++ 3 files changed, 239 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 */ diff --git a/include/linux/soc/ti/ti_sci_protocol.h b/include/linux/soc/ti/ti_sci_protocol.h index 515587e9d373..0c92a922db6a 100644 --- a/include/linux/soc/ti/ti_sci_protocol.h +++ b/include/linux/soc/ti/ti_sci_protocol.h @@ -192,15 +192,42 @@ struct ti_sci_clk_ops { u64 *current_freq); }; +/** + * struct ti_sci_rm_core_ops - Resource management core operations + * @get_range: Get a range of resources belonging to ti sci host. + * @get_rage_from_shost: Get a range of resources belonging to + * specified host id. + * - s_host: Host processing entity to which the + * resources are allocated + * + * NOTE: for these functions, all the parameters are consolidated and defined + * as below: + * - handle: Pointer to TISCI handle as retrieved by *ti_sci_get_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_end: Number of resources in the range + */ +struct ti_sci_rm_core_ops { + int (*get_range)(const struct ti_sci_handle *handle, u32 dev_id, + u8 subtype, u16 *range_start, u16 *range_num); + int (*get_range_from_shost)(const struct ti_sci_handle *handle, + u32 dev_id, u8 subtype, u8 s_host, + u16 *range_start, u16 *range_num); +}; + /** * struct ti_sci_ops - Function support for TI SCI * @dev_ops: Device specific operations * @clk_ops: Clock specific operations + * @rm_core_ops: Resource management core operations. */ struct ti_sci_ops { struct ti_sci_core_ops core_ops; struct ti_sci_dev_ops dev_ops; struct ti_sci_clk_ops clk_ops; + struct ti_sci_rm_core_ops rm_core_ops; }; /** -- cgit v1.2.3-70-g09d2 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 ++++++++ include/linux/soc/ti/ti_sci_protocol.h | 26 ++++ 3 files changed, 331 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 */ diff --git a/include/linux/soc/ti/ti_sci_protocol.h b/include/linux/soc/ti/ti_sci_protocol.h index 0c92a922db6a..d0bc4123232e 100644 --- a/include/linux/soc/ti/ti_sci_protocol.h +++ b/include/linux/soc/ti/ti_sci_protocol.h @@ -217,17 +217,43 @@ struct ti_sci_rm_core_ops { u16 *range_start, u16 *range_num); }; +/** + * struct ti_sci_rm_irq_ops: IRQ management operations + * @set_irq: Set an IRQ route between the requested source + * and destination + * @set_event_map: Set an Event based peripheral irq to Interrupt + * Aggregator. + * @free_irq: Free an an IRQ route between the requested source + * destination. + * @free_event_map: Free an event based peripheral irq to Interrupt + * Aggregator. + */ +struct ti_sci_rm_irq_ops { + int (*set_irq)(const struct ti_sci_handle *handle, u16 src_id, + u16 src_index, u16 dst_id, u16 dst_host_irq); + int (*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); + int (*free_irq)(const struct ti_sci_handle *handle, u16 src_id, + u16 src_index, u16 dst_id, u16 dst_host_irq); + int (*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); +}; + /** * struct ti_sci_ops - Function support for TI SCI * @dev_ops: Device specific operations * @clk_ops: Clock specific operations * @rm_core_ops: Resource management core operations. + * @rm_irq_ops: IRQ management specific operations */ struct ti_sci_ops { struct ti_sci_core_ops core_ops; struct ti_sci_dev_ops dev_ops; struct ti_sci_clk_ops clk_ops; struct ti_sci_rm_core_ops rm_core_ops; + struct ti_sci_rm_irq_ops rm_irq_ops; }; /** -- cgit v1.2.3-70-g09d2 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 --- .../devicetree/bindings/arm/keystone/ti,sci.txt | 3 ++- drivers/firmware/ti_sci.c | 23 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/arm/keystone/ti,sci.txt b/Documentation/devicetree/bindings/arm/keystone/ti,sci.txt index b56a02c10ae6..6f0cd31c1520 100644 --- a/Documentation/devicetree/bindings/arm/keystone/ti,sci.txt +++ b/Documentation/devicetree/bindings/arm/keystone/ti,sci.txt @@ -24,7 +24,8 @@ relationship between the TI-SCI parent node to the child node. Required properties: ------------------- -- compatible: should be "ti,k2g-sci" +- compatible: should be "ti,k2g-sci" for TI 66AK2G SoC + should be "ti,am654-sci" for for TI AM654 SoC - mbox-names: "rx" - Mailbox corresponding to receive path "tx" - Mailbox corresponding to transmit path 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-70-g09d2 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 +++++++++++++++++++++++++++++++++ include/linux/soc/ti/ti_sci_protocol.h | 54 ++++++++++++++ 2 files changed, 184 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 + * @res: Pointer to the TISCI resource + * + * Return: Total number of available resources. + */ +u32 ti_sci_get_num_resources(struct ti_sci_resource *res) +{ + u32 set, count = 0; + + for (set = 0; set < res->sets; set++) + count += res->desc[set].num; + + return count; +} +EXPORT_SYMBOL_GPL(ti_sci_get_num_resources); + +/** + * devm_ti_sci_get_of_resource() - Get a TISCI resource assigned to a device + * @handle: TISCI handle + * @dev: Device pointer to which the resource is assigned + * @dev_id: TISCI device id to which the resource is assigned + * @of_prop: property name by which the resource are represented + * + * Return: Pointer to ti_sci_resource if all went well else appropriate + * error pointer. + */ +struct ti_sci_resource * +devm_ti_sci_get_of_resource(const struct ti_sci_handle *handle, + struct device *dev, u32 dev_id, char *of_prop) +{ + struct ti_sci_resource *res; + u32 resource_subtype; + int i, ret; + + res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL); + if (!res) + return ERR_PTR(-ENOMEM); + + res->sets = of_property_count_elems_of_size(dev_of_node(dev), of_prop, + sizeof(u32)); + if (res->sets < 0) { + dev_err(dev, "%s resource type ids not available\n", of_prop); + return ERR_PTR(res->sets); + } + + res->desc = devm_kcalloc(dev, res->sets, sizeof(*res->desc), + GFP_KERNEL); + if (!res->desc) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < res->sets; i++) { + ret = of_property_read_u32_index(dev_of_node(dev), of_prop, i, + &resource_subtype); + if (ret) + return ERR_PTR(-EINVAL); + + ret = handle->ops.rm_core_ops.get_range(handle, dev_id, + resource_subtype, + &res->desc[i].start, + &res->desc[i].num); + if (ret) { + dev_err(dev, "dev = %d subtype %d not allocated for this host\n", + dev_id, resource_subtype); + return ERR_PTR(ret); + } + + dev_dbg(dev, "dev = %d, subtype = %d, start = %d, num = %d\n", + dev_id, resource_subtype, res->desc[i].start, + res->desc[i].num); + + res->desc[i].res_map = + devm_kzalloc(dev, BITS_TO_LONGS(res->desc[i].num) * + sizeof(*res->desc[i].res_map), GFP_KERNEL); + if (!res->desc[i].res_map) + return ERR_PTR(-ENOMEM); + } + raw_spin_lock_init(&res->lock); + + return res; +} + static int tisci_reboot_handler(struct notifier_block *nb, unsigned long mode, void *cmd) { diff --git a/include/linux/soc/ti/ti_sci_protocol.h b/include/linux/soc/ti/ti_sci_protocol.h index d0bc4123232e..568722a041bf 100644 --- a/include/linux/soc/ti/ti_sci_protocol.h +++ b/include/linux/soc/ti/ti_sci_protocol.h @@ -266,6 +266,33 @@ struct ti_sci_handle { struct ti_sci_ops ops; }; +#define TI_SCI_RESOURCE_NULL 0xffff + +/** + * struct ti_sci_resource_desc - Description of TI SCI resource instance range. + * @start: Start index of the resource. + * @num: Number of resources. + * @res_map: Bitmap to manage the allocation of these resources. + */ +struct ti_sci_resource_desc { + u16 start; + u16 num; + unsigned long *res_map; +}; + +/** + * struct ti_sci_resource - Structure representing a resource assigned + * to a device. + * @sets: Number of sets available from this resource type + * @lock: Lock to guard the res map in each set. + * @desc: Array of resource descriptors. + */ +struct ti_sci_resource { + u16 sets; + raw_spinlock_t lock; + struct ti_sci_resource_desc *desc; +}; + #if IS_ENABLED(CONFIG_TI_SCI_PROTOCOL) const struct ti_sci_handle *ti_sci_get_handle(struct device *dev); int ti_sci_put_handle(const struct ti_sci_handle *handle); @@ -274,6 +301,12 @@ const struct ti_sci_handle *ti_sci_get_by_phandle(struct device_node *np, const char *property); const struct ti_sci_handle *devm_ti_sci_get_by_phandle(struct device *dev, const char *property); +u16 ti_sci_get_free_resource(struct ti_sci_resource *res); +void ti_sci_release_resource(struct ti_sci_resource *res, u16 id); +u32 ti_sci_get_num_resources(struct ti_sci_resource *res); +struct ti_sci_resource * +devm_ti_sci_get_of_resource(const struct ti_sci_handle *handle, + struct device *dev, u32 dev_id, char *of_prop); #else /* CONFIG_TI_SCI_PROTOCOL */ @@ -306,6 +339,27 @@ const struct ti_sci_handle *devm_ti_sci_get_by_phandle(struct device *dev, { return ERR_PTR(-EINVAL); } + +static inline u16 ti_sci_get_free_resource(struct ti_sci_resource *res) +{ + return TI_SCI_RESOURCE_NULL; +} + +static inline void ti_sci_release_resource(struct ti_sci_resource *res, u16 id) +{ +} + +static inline u32 ti_sci_get_num_resources(struct ti_sci_resource *res) +{ + return 0; +} + +static inline struct ti_sci_resource * +devm_ti_sci_get_of_resource(const struct ti_sci_handle *handle, + struct device *dev, u32 dev_id, char *of_prop) +{ + return ERR_PTR(-EINVAL); +} #endif /* CONFIG_TI_SCI_PROTOCOL */ #endif /* __TISCI_PROTOCOL_H */ -- cgit v1.2.3-70-g09d2 From 0d04d0c146786da42c6e68c7d2f09c956c5b5bd3 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Tue, 30 Apr 2019 15:42:23 +0530 Subject: gpio: thunderx: Use the default parent apis for {request,release}_resources thunderx_gpio_irq_{request,release}_resources apis are trying to {request,release} resources on parent interrupt. There are default apis doing the same. Use the default parent apis instead of writing the same code snippet. Cc: linux-gpio@vger.kernel.org Cc: Linus Walleij Acked-by: Linus Walleij Signed-off-by: Lokesh Vutla Signed-off-by: Marc Zyngier --- drivers/gpio/gpio-thunderx.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c index 1306722faa5a..715371b5102a 100644 --- a/drivers/gpio/gpio-thunderx.c +++ b/drivers/gpio/gpio-thunderx.c @@ -363,22 +363,16 @@ static int thunderx_gpio_irq_request_resources(struct irq_data *data) { struct thunderx_line *txline = irq_data_get_irq_chip_data(data); struct thunderx_gpio *txgpio = txline->txgpio; - struct irq_data *parent_data = data->parent_data; int r; r = gpiochip_lock_as_irq(&txgpio->chip, txline->line); if (r) return r; - if (parent_data && parent_data->chip->irq_request_resources) { - r = parent_data->chip->irq_request_resources(parent_data); - if (r) - goto error; - } + r = irq_chip_request_resources_parent(data); + if (r) + gpiochip_unlock_as_irq(&txgpio->chip, txline->line); - return 0; -error: - gpiochip_unlock_as_irq(&txgpio->chip, txline->line); return r; } @@ -386,10 +380,8 @@ static void thunderx_gpio_irq_release_resources(struct irq_data *data) { struct thunderx_line *txline = irq_data_get_irq_chip_data(data); struct thunderx_gpio *txgpio = txline->txgpio; - struct irq_data *parent_data = data->parent_data; - if (parent_data && parent_data->chip->irq_release_resources) - parent_data->chip->irq_release_resources(parent_data); + irq_chip_release_resources_parent(data); gpiochip_unlock_as_irq(&txgpio->chip, txline->line); } -- cgit v1.2.3-70-g09d2 From cd844b0715ceda3287d1fa8e5d8e1b25a85c9b0f Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Tue, 30 Apr 2019 15:42:25 +0530 Subject: irqchip/ti-sci-intr: Add support for Interrupt Router driver Texas Instruments' K3 generation SoCs has an IP Interrupt Router that does allows for redirection of input interrupts to host interrupt controller. Interrupt Router inputs are either from a peripheral or from an Interrupt Aggregator which is another interrupt controller. Configuration of the interrupt router registers can only be done by a system co-processor and the driver needs to send a message to this co processor over TISCI protocol. Add support for Interrupt Router driver over TISCI protocol. Signed-off-by: Lokesh Vutla Signed-off-by: Marc Zyngier --- MAINTAINERS | 1 + drivers/irqchip/Kconfig | 10 ++ drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-ti-sci-intr.c | 275 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 287 insertions(+) create mode 100644 drivers/irqchip/irq-ti-sci-intr.c (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index b40762656a6d..63851f106b5c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15350,6 +15350,7 @@ F: Documentation/devicetree/bindings/clock/ti,sci-clk.txt F: drivers/clk/keystone/sci-clk.c F: drivers/reset/reset-ti-sci.c F: Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt +F: drivers/irqchip/irq-ti-sci-intr.c Texas Instruments ASoC drivers M: Peter Ujfalusi diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index c4ed19cb2be7..88bce736d1de 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -419,6 +419,16 @@ config LS1X_IRQ help Support for the Loongson-1 platform Interrupt Controller. +config TI_SCI_INTR_IRQCHIP + bool + depends on TI_SCI_PROTOCOL + select IRQ_DOMAIN_HIERARCHY + help + This enables the irqchip driver support for K3 Interrupt router + over TI System Control Interface available on some new TI's SoCs. + If you wish to use interrupt router irq resources managed by the + TI System Controller, say Y here. Otherwise, say N. + endmenu config SIFIVE_PLIC diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 85972ae1bd7f..fa5c865788b5 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -97,3 +97,4 @@ obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o obj-$(CONFIG_MADERA_IRQ) += irq-madera.o obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o +obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o diff --git a/drivers/irqchip/irq-ti-sci-intr.c b/drivers/irqchip/irq-ti-sci-intr.c new file mode 100644 index 000000000000..59d51a20bbd8 --- /dev/null +++ b/drivers/irqchip/irq-ti-sci-intr.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments' K3 Interrupt Router irqchip driver + * + * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ + * Lokesh Vutla + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TI_SCI_DEV_ID_MASK 0xffff +#define TI_SCI_DEV_ID_SHIFT 16 +#define TI_SCI_IRQ_ID_MASK 0xffff +#define TI_SCI_IRQ_ID_SHIFT 0 +#define HWIRQ_TO_DEVID(hwirq) (((hwirq) >> (TI_SCI_DEV_ID_SHIFT)) & \ + (TI_SCI_DEV_ID_MASK)) +#define HWIRQ_TO_IRQID(hwirq) ((hwirq) & (TI_SCI_IRQ_ID_MASK)) +#define TO_HWIRQ(dev, index) ((((dev) & TI_SCI_DEV_ID_MASK) << \ + TI_SCI_DEV_ID_SHIFT) | \ + ((index) & TI_SCI_IRQ_ID_MASK)) + +/** + * struct ti_sci_intr_irq_domain - Structure representing a TISCI based + * Interrupt Router IRQ domain. + * @sci: Pointer to TISCI handle + * @dst_irq: TISCI resource pointer representing GIC irq controller. + * @dst_id: TISCI device ID of the GIC irq controller. + * @type: Specifies the trigger type supported by this Interrupt Router + */ +struct ti_sci_intr_irq_domain { + const struct ti_sci_handle *sci; + struct ti_sci_resource *dst_irq; + u32 dst_id; + u32 type; +}; + +static struct irq_chip ti_sci_intr_irq_chip = { + .name = "INTR", + .irq_eoi = irq_chip_eoi_parent, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_affinity = irq_chip_set_affinity_parent, +}; + +/** + * ti_sci_intr_irq_domain_translate() - Retrieve hwirq and type from + * IRQ firmware specific handler. + * @domain: Pointer to IRQ domain + * @fwspec: Pointer to IRQ specific firmware structure + * @hwirq: IRQ number identified by hardware + * @type: IRQ type + * + * Return 0 if all went ok else appropriate error. + */ +static int ti_sci_intr_irq_domain_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + struct ti_sci_intr_irq_domain *intr = domain->host_data; + + if (fwspec->param_count != 2) + return -EINVAL; + + *hwirq = TO_HWIRQ(fwspec->param[0], fwspec->param[1]); + *type = intr->type; + + return 0; +} + +/** + * ti_sci_intr_irq_domain_free() - Free the specified IRQs from the domain. + * @domain: Domain to which the irqs belong + * @virq: Linux virtual IRQ to be freed. + * @nr_irqs: Number of continuous irqs to be freed + */ +static void ti_sci_intr_irq_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct ti_sci_intr_irq_domain *intr = domain->host_data; + struct irq_data *data, *parent_data; + u16 dev_id, irq_index; + + parent_data = irq_domain_get_irq_data(domain->parent, virq); + data = irq_domain_get_irq_data(domain, virq); + irq_index = HWIRQ_TO_IRQID(data->hwirq); + dev_id = HWIRQ_TO_DEVID(data->hwirq); + + intr->sci->ops.rm_irq_ops.free_irq(intr->sci, dev_id, irq_index, + intr->dst_id, parent_data->hwirq); + ti_sci_release_resource(intr->dst_irq, parent_data->hwirq); + irq_domain_free_irqs_parent(domain, virq, 1); + irq_domain_reset_irq_data(data); +} + +/** + * ti_sci_intr_alloc_gic_irq() - Allocate GIC specific IRQ + * @domain: Pointer to the interrupt router IRQ domain + * @virq: Corresponding Linux virtual IRQ number + * @hwirq: Corresponding hwirq for the IRQ within this IRQ domain + * + * Returns 0 if all went well else appropriate error pointer. + */ +static int ti_sci_intr_alloc_gic_irq(struct irq_domain *domain, + unsigned int virq, u32 hwirq) +{ + struct ti_sci_intr_irq_domain *intr = domain->host_data; + struct irq_fwspec fwspec; + u16 dev_id, irq_index; + u16 dst_irq; + int err; + + dev_id = HWIRQ_TO_DEVID(hwirq); + irq_index = HWIRQ_TO_IRQID(hwirq); + + dst_irq = ti_sci_get_free_resource(intr->dst_irq); + if (dst_irq == TI_SCI_RESOURCE_NULL) + return -EINVAL; + + fwspec.fwnode = domain->parent->fwnode; + fwspec.param_count = 3; + fwspec.param[0] = 0; /* SPI */ + fwspec.param[1] = dst_irq - 32; /* SPI offset */ + fwspec.param[2] = intr->type; + + err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec); + if (err) + goto err_irqs; + + err = intr->sci->ops.rm_irq_ops.set_irq(intr->sci, dev_id, irq_index, + intr->dst_id, dst_irq); + if (err) + goto err_msg; + + return 0; + +err_msg: + irq_domain_free_irqs_parent(domain, virq, 1); +err_irqs: + ti_sci_release_resource(intr->dst_irq, dst_irq); + return err; +} + +/** + * ti_sci_intr_irq_domain_alloc() - Allocate Interrupt router IRQs + * @domain: Point to the interrupt router IRQ domain + * @virq: Corresponding Linux virtual IRQ number + * @nr_irqs: Continuous irqs to be allocated + * @data: Pointer to firmware specifier + * + * Return 0 if all went well else appropriate error value. + */ +static int ti_sci_intr_irq_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *data) +{ + struct irq_fwspec *fwspec = data; + unsigned long hwirq; + unsigned int flags; + int err; + + err = ti_sci_intr_irq_domain_translate(domain, fwspec, &hwirq, &flags); + if (err) + return err; + + err = ti_sci_intr_alloc_gic_irq(domain, virq, hwirq); + if (err) + return err; + + irq_domain_set_hwirq_and_chip(domain, virq, hwirq, + &ti_sci_intr_irq_chip, NULL); + + return 0; +} + +static const struct irq_domain_ops ti_sci_intr_irq_domain_ops = { + .free = ti_sci_intr_irq_domain_free, + .alloc = ti_sci_intr_irq_domain_alloc, + .translate = ti_sci_intr_irq_domain_translate, +}; + +static int ti_sci_intr_irq_domain_probe(struct platform_device *pdev) +{ + struct irq_domain *parent_domain, *domain; + struct ti_sci_intr_irq_domain *intr; + struct device_node *parent_node; + struct device *dev = &pdev->dev; + int ret; + + parent_node = of_irq_find_parent(dev_of_node(dev)); + if (!parent_node) { + dev_err(dev, "Failed to get IRQ parent node\n"); + return -ENODEV; + } + + parent_domain = irq_find_host(parent_node); + if (!parent_domain) { + dev_err(dev, "Failed to find IRQ parent domain\n"); + return -ENODEV; + } + + intr = devm_kzalloc(dev, sizeof(*intr), GFP_KERNEL); + if (!intr) + return -ENOMEM; + + ret = of_property_read_u32(dev_of_node(dev), "ti,intr-trigger-type", + &intr->type); + if (ret) { + dev_err(dev, "missing ti,intr-trigger-type property\n"); + return -EINVAL; + } + + intr->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci"); + if (IS_ERR(intr->sci)) { + ret = PTR_ERR(intr->sci); + if (ret != -EPROBE_DEFER) + dev_err(dev, "ti,sci read fail %d\n", ret); + intr->sci = NULL; + return ret; + } + + ret = of_property_read_u32(dev_of_node(dev), "ti,sci-dst-id", + &intr->dst_id); + if (ret) { + dev_err(dev, "missing 'ti,sci-dst-id' property\n"); + return -EINVAL; + } + + intr->dst_irq = devm_ti_sci_get_of_resource(intr->sci, dev, + intr->dst_id, + "ti,sci-rm-range-girq"); + if (IS_ERR(intr->dst_irq)) { + dev_err(dev, "Destination irq resource allocation failed\n"); + return PTR_ERR(intr->dst_irq); + } + + domain = irq_domain_add_hierarchy(parent_domain, 0, 0, dev_of_node(dev), + &ti_sci_intr_irq_domain_ops, intr); + if (!domain) { + dev_err(dev, "Failed to allocate IRQ domain\n"); + return -ENOMEM; + } + + return 0; +} + +static const struct of_device_id ti_sci_intr_irq_domain_of_match[] = { + { .compatible = "ti,sci-intr", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ti_sci_intr_irq_domain_of_match); + +static struct platform_driver ti_sci_intr_irq_domain_driver = { + .probe = ti_sci_intr_irq_domain_probe, + .driver = { + .name = "ti-sci-intr", + .of_match_table = ti_sci_intr_irq_domain_of_match, + }, +}; +module_platform_driver(ti_sci_intr_irq_domain_driver); + +MODULE_AUTHOR("Lokesh Vutla "); +MODULE_DESCRIPTION("K3 Interrupt Router driver over TI SCI protocol"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From 9f1463b86c13277d0bd88d5ee359577ef40f4da7 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Tue, 30 Apr 2019 15:42:27 +0530 Subject: irqchip/ti-sci-inta: Add support for Interrupt Aggregator driver Texas Instruments' K3 generation SoCs has an IP Interrupt Aggregator which is an interrupt controller that does the following: - Converts events to interrupts that can be understood by an interrupt router. - Allows for multiplexing of events to interrupts. Configuration of the interrupt aggregator registers can only be done by a system co-processor and the driver needs to send a message to this co processor over TISCI protocol. Add the required infrastructure to allow the allocation and routing of these events. Signed-off-by: Lokesh Vutla Signed-off-by: Marc Zyngier --- MAINTAINERS | 1 + drivers/irqchip/Kconfig | 10 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-ti-sci-inta.c | 577 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 589 insertions(+) create mode 100644 drivers/irqchip/irq-ti-sci-inta.c (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 461db0a8233f..055467fb2019 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15352,6 +15352,7 @@ F: drivers/reset/reset-ti-sci.c F: Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt F: Documentation/devicetree/bindings/interrupt-controller/ti,sci-inta.txt F: drivers/irqchip/irq-ti-sci-intr.c +F: drivers/irqchip/irq-ti-sci-inta.c Texas Instruments ASoC drivers M: Peter Ujfalusi diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 88bce736d1de..2c8d5daad57c 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -429,6 +429,16 @@ config TI_SCI_INTR_IRQCHIP If you wish to use interrupt router irq resources managed by the TI System Controller, say Y here. Otherwise, say N. +config TI_SCI_INTA_IRQCHIP + bool + depends on TI_SCI_PROTOCOL + select IRQ_DOMAIN_HIERARCHY + help + This enables the irqchip driver support for K3 Interrupt aggregator + over TI System Control Interface available on some new TI's SoCs. + If you wish to use interrupt aggregator irq resources managed by the + TI System Controller, say Y here. Otherwise, say N. + endmenu config SIFIVE_PLIC diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index fa5c865788b5..8a33013da953 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -98,3 +98,4 @@ obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o obj-$(CONFIG_MADERA_IRQ) += irq-madera.o obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o +obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o diff --git a/drivers/irqchip/irq-ti-sci-inta.c b/drivers/irqchip/irq-ti-sci-inta.c new file mode 100644 index 000000000000..87e0abe0041f --- /dev/null +++ b/drivers/irqchip/irq-ti-sci-inta.c @@ -0,0 +1,577 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments' K3 Interrupt Aggregator irqchip driver + * + * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ + * Lokesh Vutla + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TI_SCI_DEV_ID_MASK 0xffff +#define TI_SCI_DEV_ID_SHIFT 16 +#define TI_SCI_IRQ_ID_MASK 0xffff +#define TI_SCI_IRQ_ID_SHIFT 0 +#define HWIRQ_TO_DEVID(hwirq) (((hwirq) >> (TI_SCI_DEV_ID_SHIFT)) & \ + (TI_SCI_DEV_ID_MASK)) +#define HWIRQ_TO_IRQID(hwirq) ((hwirq) & (TI_SCI_IRQ_ID_MASK)) + +#define MAX_EVENTS_PER_VINT 64 +#define VINT_ENABLE_SET_OFFSET 0x0 +#define VINT_ENABLE_CLR_OFFSET 0x8 +#define VINT_STATUS_OFFSET 0x18 + +/** + * struct ti_sci_inta_event_desc - Description of an event coming to + * Interrupt Aggregator. This serves + * as a mapping table for global event, + * hwirq and vint bit. + * @global_event: Global event number corresponding to this event + * @hwirq: Hwirq of the incoming interrupt + * @vint_bit: Corresponding vint bit to which this event is attached. + */ +struct ti_sci_inta_event_desc { + u16 global_event; + u32 hwirq; + u8 vint_bit; +}; + +/** + * struct ti_sci_inta_vint_desc - Description of a virtual interrupt coming out + * of Interrupt Aggregator. + * @domain: Pointer to IRQ domain to which this vint belongs. + * @list: List entry for the vint list + * @event_map: Bitmap to manage the allocation of events to vint. + * @events: Array of event descriptors assigned to this vint. + * @parent_virq: Linux IRQ number that gets attached to parent + * @vint_id: TISCI vint ID + */ +struct ti_sci_inta_vint_desc { + struct irq_domain *domain; + struct list_head list; + DECLARE_BITMAP(event_map, MAX_EVENTS_PER_VINT); + struct ti_sci_inta_event_desc events[MAX_EVENTS_PER_VINT]; + unsigned int parent_virq; + u16 vint_id; +}; + +/** + * struct ti_sci_inta_irq_domain - Structure representing a TISCI based + * Interrupt Aggregator IRQ domain. + * @sci: Pointer to TISCI handle + * @vint: TISCI resource pointer representing IA inerrupts. + * @global_event: TISCI resource pointer representing global events. + * @vint_list: List of the vints active in the system + * @vint_mutex: Mutex to protect vint_list + * @base: Base address of the memory mapped IO registers + * @pdev: Pointer to platform device. + */ +struct ti_sci_inta_irq_domain { + const struct ti_sci_handle *sci; + struct ti_sci_resource *vint; + struct ti_sci_resource *global_event; + struct list_head vint_list; + /* Mutex to protect vint list */ + struct mutex vint_mutex; + void __iomem *base; + struct platform_device *pdev; +}; + +#define to_vint_desc(e, i) container_of(e, struct ti_sci_inta_vint_desc, \ + events[i]) + +/** + * ti_sci_inta_irq_handler() - Chained IRQ handler for the vint irqs + * @desc: Pointer to irq_desc corresponding to the irq + */ +static void ti_sci_inta_irq_handler(struct irq_desc *desc) +{ + struct ti_sci_inta_vint_desc *vint_desc; + struct ti_sci_inta_irq_domain *inta; + struct irq_domain *domain; + unsigned int virq, bit; + unsigned long val; + + vint_desc = irq_desc_get_handler_data(desc); + domain = vint_desc->domain; + inta = domain->host_data; + + chained_irq_enter(irq_desc_get_chip(desc), desc); + + val = readq_relaxed(inta->base + vint_desc->vint_id * 0x1000 + + VINT_STATUS_OFFSET); + + for_each_set_bit(bit, &val, MAX_EVENTS_PER_VINT) { + virq = irq_find_mapping(domain, vint_desc->events[bit].hwirq); + if (virq) + generic_handle_irq(virq); + } + + chained_irq_exit(irq_desc_get_chip(desc), desc); +} + +/** + * ti_sci_inta_alloc_parent_irq() - Allocate parent irq to Interrupt aggregator + * @domain: IRQ domain corresponding to Interrupt Aggregator + * + * Return 0 if all went well else corresponding error value. + */ +static struct ti_sci_inta_vint_desc *ti_sci_inta_alloc_parent_irq(struct irq_domain *domain) +{ + struct ti_sci_inta_irq_domain *inta = domain->host_data; + struct ti_sci_inta_vint_desc *vint_desc; + struct irq_fwspec parent_fwspec; + unsigned int parent_virq; + u16 vint_id; + + vint_id = ti_sci_get_free_resource(inta->vint); + if (vint_id == TI_SCI_RESOURCE_NULL) + return ERR_PTR(-EINVAL); + + vint_desc = kzalloc(sizeof(*vint_desc), GFP_KERNEL); + if (!vint_desc) + return ERR_PTR(-ENOMEM); + + vint_desc->domain = domain; + vint_desc->vint_id = vint_id; + INIT_LIST_HEAD(&vint_desc->list); + + parent_fwspec.fwnode = of_node_to_fwnode(of_irq_find_parent(dev_of_node(&inta->pdev->dev))); + parent_fwspec.param_count = 2; + parent_fwspec.param[0] = inta->pdev->id; + parent_fwspec.param[1] = vint_desc->vint_id; + + parent_virq = irq_create_fwspec_mapping(&parent_fwspec); + if (parent_virq <= 0) { + kfree(vint_desc); + return ERR_PTR(parent_virq); + } + vint_desc->parent_virq = parent_virq; + + list_add_tail(&vint_desc->list, &inta->vint_list); + irq_set_chained_handler_and_data(vint_desc->parent_virq, + ti_sci_inta_irq_handler, vint_desc); + + return vint_desc; +} + +/** + * ti_sci_inta_alloc_event() - Attach an event to a IA vint. + * @vint_desc: Pointer to vint_desc to which the event gets attached + * @free_bit: Bit inside vint to which event gets attached + * @hwirq: hwirq of the input event + * + * Return event_desc pointer if all went ok else appropriate error value. + */ +static struct ti_sci_inta_event_desc *ti_sci_inta_alloc_event(struct ti_sci_inta_vint_desc *vint_desc, + u16 free_bit, + u32 hwirq) +{ + struct ti_sci_inta_irq_domain *inta = vint_desc->domain->host_data; + struct ti_sci_inta_event_desc *event_desc; + u16 dev_id, dev_index; + int err; + + dev_id = HWIRQ_TO_DEVID(hwirq); + dev_index = HWIRQ_TO_IRQID(hwirq); + + event_desc = &vint_desc->events[free_bit]; + event_desc->hwirq = hwirq; + event_desc->vint_bit = free_bit; + event_desc->global_event = ti_sci_get_free_resource(inta->global_event); + if (event_desc->global_event == TI_SCI_RESOURCE_NULL) + return ERR_PTR(-EINVAL); + + err = inta->sci->ops.rm_irq_ops.set_event_map(inta->sci, + dev_id, dev_index, + inta->pdev->id, + vint_desc->vint_id, + event_desc->global_event, + free_bit); + if (err) + goto free_global_event; + + return event_desc; +free_global_event: + ti_sci_release_resource(inta->global_event, event_desc->global_event); + return ERR_PTR(err); +} + +/** + * ti_sci_inta_alloc_irq() - Allocate an irq within INTA domain + * @domain: irq_domain pointer corresponding to INTA + * @hwirq: hwirq of the input event + * + * Note: Allocation happens in the following manner: + * - Find a free bit available in any of the vints available in the list. + * - If not found, allocate a vint from the vint pool + * - Attach the free bit to input hwirq. + * Return event_desc if all went ok else appropriate error value. + */ +static struct ti_sci_inta_event_desc *ti_sci_inta_alloc_irq(struct irq_domain *domain, + u32 hwirq) +{ + struct ti_sci_inta_irq_domain *inta = domain->host_data; + struct ti_sci_inta_vint_desc *vint_desc = NULL; + struct ti_sci_inta_event_desc *event_desc; + u16 free_bit; + + mutex_lock(&inta->vint_mutex); + list_for_each_entry(vint_desc, &inta->vint_list, list) { + free_bit = find_first_zero_bit(vint_desc->event_map, + MAX_EVENTS_PER_VINT); + if (free_bit != MAX_EVENTS_PER_VINT) { + set_bit(free_bit, vint_desc->event_map); + goto alloc_event; + } + } + + /* No free bits available. Allocate a new vint */ + vint_desc = ti_sci_inta_alloc_parent_irq(domain); + if (IS_ERR(vint_desc)) { + mutex_unlock(&inta->vint_mutex); + return ERR_PTR(PTR_ERR(vint_desc)); + } + + free_bit = find_first_zero_bit(vint_desc->event_map, + MAX_EVENTS_PER_VINT); + set_bit(free_bit, vint_desc->event_map); + +alloc_event: + event_desc = ti_sci_inta_alloc_event(vint_desc, free_bit, hwirq); + if (IS_ERR(event_desc)) + clear_bit(free_bit, vint_desc->event_map); + + mutex_unlock(&inta->vint_mutex); + return event_desc; +} + +/** + * ti_sci_inta_free_parent_irq() - Free a parent irq to INTA + * @inta: Pointer to inta domain. + * @vint_desc: Pointer to vint_desc that needs to be freed. + */ +static void ti_sci_inta_free_parent_irq(struct ti_sci_inta_irq_domain *inta, + struct ti_sci_inta_vint_desc *vint_desc) +{ + if (find_first_bit(vint_desc->event_map, MAX_EVENTS_PER_VINT) == MAX_EVENTS_PER_VINT) { + list_del(&vint_desc->list); + ti_sci_release_resource(inta->vint, vint_desc->vint_id); + irq_dispose_mapping(vint_desc->parent_virq); + kfree(vint_desc); + } +} + +/** + * ti_sci_inta_free_irq() - Free an IRQ within INTA domain + * @event_desc: Pointer to event_desc that needs to be freed. + * @hwirq: Hwirq number within INTA domain that needs to be freed + */ +static void ti_sci_inta_free_irq(struct ti_sci_inta_event_desc *event_desc, + u32 hwirq) +{ + struct ti_sci_inta_vint_desc *vint_desc; + struct ti_sci_inta_irq_domain *inta; + + vint_desc = to_vint_desc(event_desc, event_desc->vint_bit); + inta = vint_desc->domain->host_data; + /* free event irq */ + mutex_lock(&inta->vint_mutex); + inta->sci->ops.rm_irq_ops.free_event_map(inta->sci, + HWIRQ_TO_DEVID(hwirq), + HWIRQ_TO_IRQID(hwirq), + inta->pdev->id, + vint_desc->vint_id, + event_desc->global_event, + event_desc->vint_bit); + + clear_bit(event_desc->vint_bit, vint_desc->event_map); + ti_sci_release_resource(inta->global_event, event_desc->global_event); + event_desc->global_event = TI_SCI_RESOURCE_NULL; + event_desc->hwirq = 0; + + ti_sci_inta_free_parent_irq(inta, vint_desc); + mutex_unlock(&inta->vint_mutex); +} + +/** + * ti_sci_inta_request_resources() - Allocate resources for input irq + * @data: Pointer to corresponding irq_data + * + * Note: This is the core api where the actual allocation happens for input + * hwirq. This allocation involves creating a parent irq for vint. + * If this is done in irq_domain_ops.alloc() then a deadlock is reached + * for allocation. So this allocation is being done in request_resources() + * + * Return: 0 if all went well else corresponding error. + */ +static int ti_sci_inta_request_resources(struct irq_data *data) +{ + struct ti_sci_inta_event_desc *event_desc; + + event_desc = ti_sci_inta_alloc_irq(data->domain, data->hwirq); + if (IS_ERR(event_desc)) + return PTR_ERR(event_desc); + + data->chip_data = event_desc; + + return 0; +} + +/** + * ti_sci_inta_release_resources - Release resources for input irq + * @data: Pointer to corresponding irq_data + * + * Note: Corresponding to request_resources(), all the unmapping and deletion + * of parent vint irqs happens in this api. + */ +static void ti_sci_inta_release_resources(struct irq_data *data) +{ + struct ti_sci_inta_event_desc *event_desc; + + event_desc = irq_data_get_irq_chip_data(data); + ti_sci_inta_free_irq(event_desc, data->hwirq); +} + +/** + * ti_sci_inta_manage_event() - Control the event based on the offset + * @data: Pointer to corresponding irq_data + * @offset: register offset using which event is controlled. + */ +static void ti_sci_inta_manage_event(struct irq_data *data, u32 offset) +{ + struct ti_sci_inta_event_desc *event_desc; + struct ti_sci_inta_vint_desc *vint_desc; + struct ti_sci_inta_irq_domain *inta; + + event_desc = irq_data_get_irq_chip_data(data); + vint_desc = to_vint_desc(event_desc, event_desc->vint_bit); + inta = data->domain->host_data; + + writeq_relaxed(BIT(event_desc->vint_bit), + inta->base + vint_desc->vint_id * 0x1000 + offset); +} + +/** + * ti_sci_inta_mask_irq() - Mask an event + * @data: Pointer to corresponding irq_data + */ +static void ti_sci_inta_mask_irq(struct irq_data *data) +{ + ti_sci_inta_manage_event(data, VINT_ENABLE_CLR_OFFSET); +} + +/** + * ti_sci_inta_unmask_irq() - Unmask an event + * @data: Pointer to corresponding irq_data + */ +static void ti_sci_inta_unmask_irq(struct irq_data *data) +{ + ti_sci_inta_manage_event(data, VINT_ENABLE_SET_OFFSET); +} + +/** + * ti_sci_inta_ack_irq() - Ack an event + * @data: Pointer to corresponding irq_data + */ +static void ti_sci_inta_ack_irq(struct irq_data *data) +{ + /* + * Do not clear the event if hardware is capable of sending + * a down event. + */ + if (irqd_get_trigger_type(data) != IRQF_TRIGGER_HIGH) + ti_sci_inta_manage_event(data, VINT_STATUS_OFFSET); +} + +static int ti_sci_inta_set_affinity(struct irq_data *d, + const struct cpumask *mask_val, bool force) +{ + return -EINVAL; +} + +/** + * ti_sci_inta_set_type() - Update the trigger type of the irq. + * @data: Pointer to corresponding irq_data + * @type: Trigger type as specified by user + * + * Note: This updates the handle_irq callback for level msi. + * + * Return 0 if all went well else appropriate error. + */ +static int ti_sci_inta_set_type(struct irq_data *data, unsigned int type) +{ + /* + * .alloc default sets handle_edge_irq. But if the user specifies + * that IRQ is level MSI, then update the handle to handle_level_irq + */ + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQF_TRIGGER_HIGH: + irq_set_handler_locked(data, handle_level_irq); + return 0; + case IRQF_TRIGGER_RISING: + return 0; + default: + return -EINVAL; + } + + return -EINVAL; +} + +static struct irq_chip ti_sci_inta_irq_chip = { + .name = "INTA", + .irq_ack = ti_sci_inta_ack_irq, + .irq_mask = ti_sci_inta_mask_irq, + .irq_set_type = ti_sci_inta_set_type, + .irq_unmask = ti_sci_inta_unmask_irq, + .irq_set_affinity = ti_sci_inta_set_affinity, + .irq_request_resources = ti_sci_inta_request_resources, + .irq_release_resources = ti_sci_inta_release_resources, +}; + +/** + * ti_sci_inta_irq_domain_free() - Free an IRQ from the IRQ domain + * @domain: Domain to which the irqs belong + * @virq: base linux virtual IRQ to be freed. + * @nr_irqs: Number of continuous irqs to be freed + */ +static void ti_sci_inta_irq_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *data = irq_domain_get_irq_data(domain, virq); + + irq_domain_reset_irq_data(data); +} + +/** + * ti_sci_inta_irq_domain_alloc() - Allocate Interrupt aggregator IRQs + * @domain: Point to the interrupt aggregator IRQ domain + * @virq: Corresponding Linux virtual IRQ number + * @nr_irqs: Continuous irqs to be allocated + * @data: Pointer to firmware specifier + * + * No actual allocation happens here. + * + * Return 0 if all went well else appropriate error value. + */ +static int ti_sci_inta_irq_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *data) +{ + msi_alloc_info_t *arg = data; + + irq_domain_set_info(domain, virq, arg->hwirq, &ti_sci_inta_irq_chip, + NULL, handle_edge_irq, NULL, NULL); + + return 0; +} + +static const struct irq_domain_ops ti_sci_inta_irq_domain_ops = { + .free = ti_sci_inta_irq_domain_free, + .alloc = ti_sci_inta_irq_domain_alloc, +}; + +static int ti_sci_inta_irq_domain_probe(struct platform_device *pdev) +{ + struct irq_domain *parent_domain, *domain; + struct device_node *parent_node, *node; + struct ti_sci_inta_irq_domain *inta; + struct device *dev = &pdev->dev; + struct resource *res; + int ret; + + node = dev_of_node(dev); + parent_node = of_irq_find_parent(node); + if (!parent_node) { + dev_err(dev, "Failed to get IRQ parent node\n"); + return -ENODEV; + } + + parent_domain = irq_find_host(parent_node); + if (!parent_domain) + return -EPROBE_DEFER; + + inta = devm_kzalloc(dev, sizeof(*inta), GFP_KERNEL); + if (!inta) + return -ENOMEM; + + inta->pdev = pdev; + inta->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci"); + if (IS_ERR(inta->sci)) { + ret = PTR_ERR(inta->sci); + if (ret != -EPROBE_DEFER) + dev_err(dev, "ti,sci read fail %d\n", ret); + inta->sci = NULL; + return ret; + } + + ret = of_property_read_u32(dev->of_node, "ti,sci-dev-id", &pdev->id); + if (ret) { + dev_err(dev, "missing 'ti,sci-dev-id' property\n"); + return -EINVAL; + } + + inta->vint = devm_ti_sci_get_of_resource(inta->sci, dev, pdev->id, + "ti,sci-rm-range-vint"); + if (IS_ERR(inta->vint)) { + dev_err(dev, "VINT resource allocation failed\n"); + return PTR_ERR(inta->vint); + } + + inta->global_event = devm_ti_sci_get_of_resource(inta->sci, dev, pdev->id, + "ti,sci-rm-range-global-event"); + if (IS_ERR(inta->global_event)) { + dev_err(dev, "Global event resource allocation failed\n"); + return PTR_ERR(inta->global_event); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + inta->base = devm_ioremap_resource(dev, res); + if (IS_ERR(inta->base)) + return -ENODEV; + + domain = irq_domain_add_linear(dev_of_node(dev), + ti_sci_get_num_resources(inta->vint), + &ti_sci_inta_irq_domain_ops, inta); + if (!domain) { + dev_err(dev, "Failed to allocate IRQ domain\n"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&inta->vint_list); + mutex_init(&inta->vint_mutex); + + return 0; +} + +static const struct of_device_id ti_sci_inta_irq_domain_of_match[] = { + { .compatible = "ti,sci-inta", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ti_sci_inta_irq_domain_of_match); + +static struct platform_driver ti_sci_inta_irq_domain_driver = { + .probe = ti_sci_inta_irq_domain_probe, + .driver = { + .name = "ti-sci-inta", + .of_match_table = ti_sci_inta_irq_domain_of_match, + }, +}; +module_platform_driver(ti_sci_inta_irq_domain_driver); + +MODULE_AUTHOR("Lokesh Vutla "); +MODULE_DESCRIPTION("K3 Interrupt Aggregator driver over TI SCI protocol"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From 49b323157bf1e70bfb3114a463d340399906c43a Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Tue, 30 Apr 2019 15:42:28 +0530 Subject: soc: ti: Add MSI domain bus support for Interrupt Aggregator With the system coprocessor managing the range allocation of the inputs to Interrupt Aggregator, it is difficult to represent the device IRQs from DT. The suggestion is to use MSI in such cases where devices wants to allocate and group interrupts dynamically. Create a MSI domain bus layer that allocates and frees MSIs for a device. APIs that are implemented: - ti_sci_inta_msi_create_irq_domain() that creates a MSI domain - ti_sci_inta_msi_domain_alloc_irqs() that creates MSIs for the specified device and resource. - ti_sci_inta_msi_domain_free_irqs() frees the irqs attached to the device. - ti_sci_inta_msi_get_virq() for getting the virq attached to a specific event. Signed-off-by: Lokesh Vutla Signed-off-by: Marc Zyngier --- MAINTAINERS | 2 + drivers/soc/ti/Kconfig | 6 ++ drivers/soc/ti/Makefile | 1 + drivers/soc/ti/ti_sci_inta_msi.c | 146 +++++++++++++++++++++++++++++++++ include/linux/irqdomain.h | 1 + include/linux/msi.h | 10 +++ include/linux/soc/ti/ti_sci_inta_msi.h | 23 ++++++ 7 files changed, 189 insertions(+) create mode 100644 drivers/soc/ti/ti_sci_inta_msi.c create mode 100644 include/linux/soc/ti/ti_sci_inta_msi.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 055467fb2019..96159e502e6c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15353,6 +15353,8 @@ F: Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt F: Documentation/devicetree/bindings/interrupt-controller/ti,sci-inta.txt F: drivers/irqchip/irq-ti-sci-intr.c F: drivers/irqchip/irq-ti-sci-inta.c +F: include/linux/soc/ti/ti_sci_inta_msi.h +F: drivers/soc/ti/ti_sci_inta_msi.c Texas Instruments ASoC drivers M: Peter Ujfalusi diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig index be4570baad96..82f110fe4953 100644 --- a/drivers/soc/ti/Kconfig +++ b/drivers/soc/ti/Kconfig @@ -73,4 +73,10 @@ config TI_SCI_PM_DOMAINS called ti_sci_pm_domains. Note this is needed early in boot before rootfs may be available. +config TI_SCI_INTA_MSI_DOMAIN + bool + select GENERIC_MSI_IRQ_DOMAIN + help + Driver to enable Interrupt Aggregator specific MSI Domain. + endif # SOC_TI diff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile index a22edc0b258a..b3868d392d4f 100644 --- a/drivers/soc/ti/Makefile +++ b/drivers/soc/ti/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o obj-$(CONFIG_AMX3_PM) += pm33xx.o obj-$(CONFIG_WKUP_M3_IPC) += wkup_m3_ipc.o obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o +obj-$(CONFIG_TI_SCI_INTA_MSI_DOMAIN) += ti_sci_inta_msi.o diff --git a/drivers/soc/ti/ti_sci_inta_msi.c b/drivers/soc/ti/ti_sci_inta_msi.c new file mode 100644 index 000000000000..0eb9462f609e --- /dev/null +++ b/drivers/soc/ti/ti_sci_inta_msi.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments' K3 Interrupt Aggregator MSI bus + * + * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ + * Lokesh Vutla + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static void ti_sci_inta_msi_write_msg(struct irq_data *data, + struct msi_msg *msg) +{ + /* Nothing to do */ +} + +static void ti_sci_inta_msi_compose_msi_msg(struct irq_data *data, + struct msi_msg *msg) +{ + /* Nothing to do */ +} + +static void ti_sci_inta_msi_update_chip_ops(struct msi_domain_info *info) +{ + struct irq_chip *chip = info->chip; + + if (WARN_ON(!chip)) + return; + + chip->irq_request_resources = irq_chip_request_resources_parent; + chip->irq_release_resources = irq_chip_release_resources_parent; + chip->irq_compose_msi_msg = ti_sci_inta_msi_compose_msi_msg; + chip->irq_write_msi_msg = ti_sci_inta_msi_write_msg; + chip->irq_set_type = irq_chip_set_type_parent; + chip->irq_unmask = irq_chip_unmask_parent; + chip->irq_mask = irq_chip_mask_parent; + chip->irq_ack = irq_chip_ack_parent; +} + +struct irq_domain *ti_sci_inta_msi_create_irq_domain(struct fwnode_handle *fwnode, + struct msi_domain_info *info, + struct irq_domain *parent) +{ + struct irq_domain *domain; + + ti_sci_inta_msi_update_chip_ops(info); + + domain = msi_create_irq_domain(fwnode, info, parent); + if (domain) + irq_domain_update_bus_token(domain, DOMAIN_BUS_TI_SCI_INTA_MSI); + + return domain; +} +EXPORT_SYMBOL_GPL(ti_sci_inta_msi_create_irq_domain); + +static void ti_sci_inta_msi_free_descs(struct device *dev) +{ + struct msi_desc *desc, *tmp; + + list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) { + list_del(&desc->list); + free_msi_entry(desc); + } +} + +static int ti_sci_inta_msi_alloc_descs(struct device *dev, + struct ti_sci_resource *res) +{ + struct msi_desc *msi_desc; + int set, i, count = 0; + + for (set = 0; set < res->sets; set++) { + for (i = 0; i < res->desc[set].num; i++) { + msi_desc = alloc_msi_entry(dev, 1, NULL); + if (!msi_desc) { + ti_sci_inta_msi_free_descs(dev); + return -ENOMEM; + } + + msi_desc->inta.dev_index = res->desc[set].start + i; + INIT_LIST_HEAD(&msi_desc->list); + list_add_tail(&msi_desc->list, dev_to_msi_list(dev)); + count++; + } + } + + return count; +} + +int ti_sci_inta_msi_domain_alloc_irqs(struct device *dev, + struct ti_sci_resource *res) +{ + struct platform_device *pdev = to_platform_device(dev); + struct irq_domain *msi_domain; + int ret, nvec; + + msi_domain = dev_get_msi_domain(dev); + if (!msi_domain) + return -EINVAL; + + if (pdev->id < 0) + return -ENODEV; + + nvec = ti_sci_inta_msi_alloc_descs(dev, res); + if (nvec <= 0) + return nvec; + + ret = msi_domain_alloc_irqs(msi_domain, dev, nvec); + if (ret) { + dev_err(dev, "Failed to allocate IRQs %d\n", ret); + goto cleanup; + } + + return 0; + +cleanup: + ti_sci_inta_msi_free_descs(&pdev->dev); + return ret; +} +EXPORT_SYMBOL_GPL(ti_sci_inta_msi_domain_alloc_irqs); + +void ti_sci_inta_msi_domain_free_irqs(struct device *dev) +{ + msi_domain_free_irqs(dev->msi_domain, dev); + ti_sci_inta_msi_free_descs(dev); +} +EXPORT_SYMBOL_GPL(ti_sci_inta_msi_domain_free_irqs); + +unsigned int ti_sci_inta_msi_get_virq(struct device *dev, u32 dev_index) +{ + struct msi_desc *desc; + + for_each_msi_entry(desc, dev) + if (desc->inta.dev_index == dev_index) + return desc->irq; + + return -ENODEV; +} +EXPORT_SYMBOL_GPL(ti_sci_inta_msi_get_virq); diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 61706b430907..07ec8b390161 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -82,6 +82,7 @@ enum irq_domain_bus_token { DOMAIN_BUS_NEXUS, DOMAIN_BUS_IPI, DOMAIN_BUS_FSL_MC_MSI, + DOMAIN_BUS_TI_SCI_INTA_MSI, }; /** diff --git a/include/linux/msi.h b/include/linux/msi.h index 7e9b81c3b50d..7c28762851a3 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -47,6 +47,14 @@ struct fsl_mc_msi_desc { u16 msi_index; }; +/** + * ti_sci_inta_msi_desc - TISCI based INTA specific msi descriptor data + * @dev_index: TISCI device index + */ +struct ti_sci_inta_msi_desc { + u16 dev_index; +}; + /** * struct msi_desc - Descriptor structure for MSI based interrupts * @list: List head for management @@ -68,6 +76,7 @@ struct fsl_mc_msi_desc { * @mask_base: [PCI MSI-X] Mask register base address * @platform: [platform] Platform device specific msi descriptor data * @fsl_mc: [fsl-mc] FSL MC device specific msi descriptor data + * @inta: [INTA] TISCI based INTA specific msi descriptor data */ struct msi_desc { /* Shared device/bus type independent data */ @@ -106,6 +115,7 @@ struct msi_desc { */ struct platform_msi_desc platform; struct fsl_mc_msi_desc fsl_mc; + struct ti_sci_inta_msi_desc inta; }; }; diff --git a/include/linux/soc/ti/ti_sci_inta_msi.h b/include/linux/soc/ti/ti_sci_inta_msi.h new file mode 100644 index 000000000000..11fb5048f5f6 --- /dev/null +++ b/include/linux/soc/ti/ti_sci_inta_msi.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Texas Instruments' K3 TI SCI INTA MSI helper + * + * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ + * Lokesh Vutla + */ + +#ifndef __INCLUDE_LINUX_TI_SCI_INTA_MSI_H +#define __INCLUDE_LINUX_TI_SCI_INTA_MSI_H + +#include +#include + +struct irq_domain +*ti_sci_inta_msi_create_irq_domain(struct fwnode_handle *fwnode, + struct msi_domain_info *info, + struct irq_domain *parent); +int ti_sci_inta_msi_domain_alloc_irqs(struct device *dev, + struct ti_sci_resource *res); +unsigned int ti_sci_inta_msi_get_virq(struct device *dev, u32 index); +void ti_sci_inta_msi_domain_free_irqs(struct device *dev); +#endif /* __INCLUDE_LINUX_IRQCHIP_TI_SCI_INTA_H */ -- cgit v1.2.3-70-g09d2 From f011df6179bdffac63987f009d25dd79b6e2df23 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Tue, 30 Apr 2019 15:42:29 +0530 Subject: irqchip/ti-sci-inta: Add msi domain support Add a msi domain that is child to the INTA domain. Clients uses the INTA MSI bus layer to allocate irqs in this MSI domain. Signed-off-by: Lokesh Vutla Signed-off-by: Marc Zyngier --- drivers/irqchip/Kconfig | 1 + drivers/irqchip/irq-ti-sci-inta.c | 40 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 2c8d5daad57c..fcf9457cc1d9 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -433,6 +433,7 @@ config TI_SCI_INTA_IRQCHIP bool depends on TI_SCI_PROTOCOL select IRQ_DOMAIN_HIERARCHY + select TI_SCI_INTA_MSI_DOMAIN help This enables the irqchip driver support for K3 Interrupt aggregator over TI System Control Interface available on some new TI's SoCs. diff --git a/drivers/irqchip/irq-ti-sci-inta.c b/drivers/irqchip/irq-ti-sci-inta.c index 87e0abe0041f..011b60a49e3f 100644 --- a/drivers/irqchip/irq-ti-sci-inta.c +++ b/drivers/irqchip/irq-ti-sci-inta.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,9 @@ #define HWIRQ_TO_DEVID(hwirq) (((hwirq) >> (TI_SCI_DEV_ID_SHIFT)) & \ (TI_SCI_DEV_ID_MASK)) #define HWIRQ_TO_IRQID(hwirq) ((hwirq) & (TI_SCI_IRQ_ID_MASK)) +#define TO_HWIRQ(dev, index) ((((dev) & TI_SCI_DEV_ID_MASK) << \ + TI_SCI_DEV_ID_SHIFT) | \ + ((index) & TI_SCI_IRQ_ID_MASK)) #define MAX_EVENTS_PER_VINT 64 #define VINT_ENABLE_SET_OFFSET 0x0 @@ -484,9 +488,34 @@ static const struct irq_domain_ops ti_sci_inta_irq_domain_ops = { .alloc = ti_sci_inta_irq_domain_alloc, }; +static struct irq_chip ti_sci_inta_msi_irq_chip = { + .name = "MSI-INTA", + .flags = IRQCHIP_SUPPORTS_LEVEL_MSI, +}; + +static void ti_sci_inta_msi_set_desc(msi_alloc_info_t *arg, + struct msi_desc *desc) +{ + struct platform_device *pdev = to_platform_device(desc->dev); + + arg->desc = desc; + arg->hwirq = TO_HWIRQ(pdev->id, desc->inta.dev_index); +} + +static struct msi_domain_ops ti_sci_inta_msi_ops = { + .set_desc = ti_sci_inta_msi_set_desc, +}; + +static struct msi_domain_info ti_sci_inta_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_LEVEL_CAPABLE), + .ops = &ti_sci_inta_msi_ops, + .chip = &ti_sci_inta_msi_irq_chip, +}; + static int ti_sci_inta_irq_domain_probe(struct platform_device *pdev) { - struct irq_domain *parent_domain, *domain; + struct irq_domain *parent_domain, *domain, *msi_domain; struct device_node *parent_node, *node; struct ti_sci_inta_irq_domain *inta; struct device *dev = &pdev->dev; @@ -551,6 +580,15 @@ static int ti_sci_inta_irq_domain_probe(struct platform_device *pdev) return -ENOMEM; } + msi_domain = ti_sci_inta_msi_create_irq_domain(of_node_to_fwnode(node), + &ti_sci_inta_msi_domain_info, + domain); + if (!msi_domain) { + irq_domain_remove(domain); + dev_err(dev, "Failed to allocate msi domain\n"); + return -ENOMEM; + } + INIT_LIST_HEAD(&inta->vint_list); mutex_init(&inta->vint_mutex); -- cgit v1.2.3-70-g09d2 From ece6e6f0218b7777e650bf93728130ae6f4feb7d Mon Sep 17 00:00:00 2001 From: Julien Grall Date: Wed, 1 May 2019 14:58:19 +0100 Subject: iommu/dma-iommu: Split iommu_dma_map_msi_msg() in two parts On RT, iommu_dma_map_msi_msg() may be called from non-preemptible context. This will lead to a splat with CONFIG_DEBUG_ATOMIC_SLEEP as the function is using spin_lock (they can sleep on RT). iommu_dma_map_msi_msg() is used to map the MSI page in the IOMMU PT and update the MSI message with the IOVA. Only the part to lookup for the MSI page requires to be called in preemptible context. As the MSI page cannot change over the lifecycle of the MSI interrupt, the lookup can be cached and re-used later on. iomma_dma_map_msi_msg() is now split in two functions: - iommu_dma_prepare_msi(): This function will prepare the mapping in the IOMMU and store the cookie in the structure msi_desc. This function should be called in preemptible context. - iommu_dma_compose_msi_msg(): This function will update the MSI message with the IOVA when the device is behind an IOMMU. Signed-off-by: Julien Grall Reviewed-by: Robin Murphy Reviewed-by: Eric Auger Acked-by: Joerg Roedel Signed-off-by: Marc Zyngier --- drivers/iommu/Kconfig | 1 + drivers/iommu/dma-iommu.c | 46 +++++++++++++++++++++++++++++++++++++--------- include/linux/dma-iommu.h | 25 +++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 6f07f3b21816..eb1c8cd243f9 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -94,6 +94,7 @@ config IOMMU_DMA bool select IOMMU_API select IOMMU_IOVA + select IRQ_MSI_IOMMU select NEED_SG_DMA_LENGTH config FSL_PAMU diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 77aabe637a60..f847904098f7 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -888,17 +888,18 @@ out_free_page: return NULL; } -void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) +int iommu_dma_prepare_msi(struct msi_desc *desc, phys_addr_t msi_addr) { - struct device *dev = msi_desc_to_dev(irq_get_msi_desc(irq)); + struct device *dev = msi_desc_to_dev(desc); struct iommu_domain *domain = iommu_get_domain_for_dev(dev); struct iommu_dma_cookie *cookie; struct iommu_dma_msi_page *msi_page; - phys_addr_t msi_addr = (u64)msg->address_hi << 32 | msg->address_lo; unsigned long flags; - if (!domain || !domain->iova_cookie) - return; + if (!domain || !domain->iova_cookie) { + desc->iommu_cookie = NULL; + return 0; + } cookie = domain->iova_cookie; @@ -911,7 +912,36 @@ void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain); spin_unlock_irqrestore(&cookie->msi_lock, flags); - if (WARN_ON(!msi_page)) { + msi_desc_set_iommu_cookie(desc, msi_page); + + if (!msi_page) + return -ENOMEM; + return 0; +} + +void iommu_dma_compose_msi_msg(struct msi_desc *desc, + struct msi_msg *msg) +{ + struct device *dev = msi_desc_to_dev(desc); + const struct iommu_domain *domain = iommu_get_domain_for_dev(dev); + const struct iommu_dma_msi_page *msi_page; + + msi_page = msi_desc_get_iommu_cookie(desc); + + if (!domain || !domain->iova_cookie || WARN_ON(!msi_page)) + return; + + msg->address_hi = upper_32_bits(msi_page->iova); + msg->address_lo &= cookie_msi_granule(domain->iova_cookie) - 1; + msg->address_lo += lower_32_bits(msi_page->iova); +} + +void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) +{ + struct msi_desc *desc = irq_get_msi_desc(irq); + phys_addr_t msi_addr = (u64)msg->address_hi << 32 | msg->address_lo; + + if (WARN_ON(iommu_dma_prepare_msi(desc, msi_addr))) { /* * We're called from a void callback, so the best we can do is * 'fail' by filling the message with obviously bogus values. @@ -922,8 +952,6 @@ void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) msg->address_lo = ~0U; msg->data = ~0U; } else { - msg->address_hi = upper_32_bits(msi_page->iova); - msg->address_lo &= cookie_msi_granule(cookie) - 1; - msg->address_lo += lower_32_bits(msi_page->iova); + iommu_dma_compose_msi_msg(desc, msg); } } diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h index e760dc5d1fa8..0b781a98ee73 100644 --- a/include/linux/dma-iommu.h +++ b/include/linux/dma-iommu.h @@ -71,12 +71,26 @@ void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle, size_t size, enum dma_data_direction dir, unsigned long attrs); /* The DMA API isn't _quite_ the whole story, though... */ +/* + * iommu_dma_prepare_msi() - Map the MSI page in the IOMMU device + * + * The MSI page will be stored in @desc. + * + * Return: 0 on success otherwise an error describing the failure. + */ +int iommu_dma_prepare_msi(struct msi_desc *desc, phys_addr_t msi_addr); + +/* Update the MSI message if required. */ +void iommu_dma_compose_msi_msg(struct msi_desc *desc, + struct msi_msg *msg); + void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg); void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list); #else struct iommu_domain; +struct msi_desc; struct msi_msg; struct device; @@ -99,6 +113,17 @@ static inline void iommu_put_dma_cookie(struct iommu_domain *domain) { } +static inline int iommu_dma_prepare_msi(struct msi_desc *desc, + phys_addr_t msi_addr) +{ + return 0; +} + +static inline void iommu_dma_compose_msi_msg(struct msi_desc *desc, + struct msi_msg *msg) +{ +} + static inline void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) { } -- cgit v1.2.3-70-g09d2 From 737be74710f30e611ee871f7b4f47975d1c6f71a Mon Sep 17 00:00:00 2001 From: Julien Grall Date: Wed, 1 May 2019 14:58:20 +0100 Subject: irqchip/gicv2m: Don't map the MSI page in gicv2m_compose_msi_msg() gicv2m_compose_msi_msg() may be called from non-preemptible context. However, on RT, iommu_dma_map_msi_msg() requires to be called from a preemptible context. A recent change split iommu_dma_map_msi_msg() in two new functions: one that should be called in preemptible context, the other does not have any requirement. The GICv2m driver is reworked to avoid executing preemptible code in non-preemptible context. This can be achieved by preparing the MSI mapping when allocating the MSI interrupt. Signed-off-by: Julien Grall Reviewed-by: Eric Auger Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v2m.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index f5fe0100f9ff..4359f0583377 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c @@ -110,7 +110,7 @@ static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) msg->data -= v2m->spi_offset; - iommu_dma_map_msi_msg(data->irq, msg); + iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg); } static struct irq_chip gicv2m_irq_chip = { @@ -167,6 +167,7 @@ static void gicv2m_unalloc_msi(struct v2m_data *v2m, unsigned int hwirq, static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *args) { + msi_alloc_info_t *info = args; struct v2m_data *v2m = NULL, *tmp; int hwirq, offset, i, err = 0; @@ -186,6 +187,11 @@ static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, hwirq = v2m->spi_start + offset; + err = iommu_dma_prepare_msi(info->desc, + v2m->res.start + V2M_MSI_SETSPI_NS); + if (err) + return err; + for (i = 0; i < nr_irqs; i++) { err = gicv2m_irq_gic_domain_alloc(domain, virq + i, hwirq + i); if (err) -- cgit v1.2.3-70-g09d2 From 35ae7df21be098848722f96f0f33bf33467436a8 Mon Sep 17 00:00:00 2001 From: Julien Grall Date: Wed, 1 May 2019 14:58:21 +0100 Subject: irqchip/gic-v3-its: Don't map the MSI page in its_irq_compose_msi_msg() its_irq_compose_msi_msg() may be called from non-preemptible context. However, on RT, iommu_dma_map_msi_msg requires to be called from a preemptible context. A recent change split iommu_dma_map_msi_msg() in two new functions: one that should be called in preemptible context, the other does not have any requirement. The GICv3 ITS driver is reworked to avoid executing preemptible code in non-preemptible context. This can be achieved by preparing the MSI mapping when allocating the MSI interrupt. Signed-off-by: Julien Grall Reviewed-by: Eric Auger Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 75d396b9b666..1e364d3ad9c5 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1178,7 +1178,7 @@ static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) msg->address_hi = upper_32_bits(addr); msg->data = its_get_event_id(d); - iommu_dma_map_msi_msg(d->irq, msg); + iommu_dma_compose_msi_msg(irq_data_get_msi_desc(d), msg); } static int its_irq_set_irqchip_state(struct irq_data *d, @@ -2563,6 +2563,7 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, { msi_alloc_info_t *info = args; struct its_device *its_dev = info->scratchpad[0].ptr; + struct its_node *its = its_dev->its; irq_hw_number_t hwirq; int err; int i; @@ -2571,6 +2572,10 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, if (err) return err; + err = iommu_dma_prepare_msi(info->desc, its->get_msi_base(its_dev)); + if (err) + return err; + for (i = 0; i < nr_irqs; i++) { err = its_irq_gic_domain_alloc(domain, virq + i, hwirq + i); if (err) -- cgit v1.2.3-70-g09d2 From 2cb3b16545495ee31dc9438f88232c2cfe44a41f Mon Sep 17 00:00:00 2001 From: Julien Grall Date: Wed, 1 May 2019 14:58:22 +0100 Subject: irqchip/ls-scfg-msi: Don't map the MSI page in ls_scfg_msi_compose_msg() ls_scfg_msi_compose_msg() may be called from non-preemptible context. However, on RT, iommu_dma_map_msi_msg() requires to be called from a preemptible context. A recent patch split iommu_dma_map_msi_msg() in two new functions: one that should be called in preemptible context, the other does not have any requirement. The FreeScale SCFG MSI driver is reworked to avoid executing preemptible code in non-preemptible context. This can be achieved by preparing the MSI maping when allocating the MSI interrupt. Signed-off-by: Julien Grall Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-ls-scfg-msi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-ls-scfg-msi.c b/drivers/irqchip/irq-ls-scfg-msi.c index c671b3212010..669d29105772 100644 --- a/drivers/irqchip/irq-ls-scfg-msi.c +++ b/drivers/irqchip/irq-ls-scfg-msi.c @@ -100,7 +100,7 @@ static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) msg->data |= cpumask_first(mask); } - iommu_dma_map_msi_msg(data->irq, msg); + iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg); } static int ls_scfg_msi_set_affinity(struct irq_data *irq_data, @@ -141,6 +141,7 @@ static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain, unsigned int nr_irqs, void *args) { + msi_alloc_info_t *info = args; struct ls_scfg_msi *msi_data = domain->host_data; int pos, err = 0; @@ -154,6 +155,10 @@ static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain, err = -ENOSPC; spin_unlock(&msi_data->lock); + if (err) + return err; + + err = iommu_dma_prepare_msi(info->desc, msi_data->msiir_addr); if (err) return err; -- cgit v1.2.3-70-g09d2 From 73103975425786ebdb6c4d2868ecf26f391fb77e Mon Sep 17 00:00:00 2001 From: Julien Grall Date: Wed, 1 May 2019 14:58:23 +0100 Subject: irqchip/gic-v3-mbi: Don't map the MSI page in mbi_compose_m{b, s}i_msg() The functions mbi_compose_m{b, s}i_msg may be called from non-preemptible context. However, on RT, iommu_dma_map_msi_msg() requires to be called from a preemptible context. A recent patch split iommu_dma_map_msi_msg in two new functions: one that should be called in preemptible context, the other does not have any requirement. The GICv3 MSI driver is reworked to avoid executing preemptible code in non-preemptible context. This can be achieved by preparing the MSI mapping when allocating the MSI interrupt. Signed-off-by: Julien Grall [maz: only call iommu_dma_prepare_msi once, fix commit log accordingly] Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-mbi.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v3-mbi.c b/drivers/irqchip/irq-gic-v3-mbi.c index fbfa7ff6deb1..563a9b366294 100644 --- a/drivers/irqchip/irq-gic-v3-mbi.c +++ b/drivers/irqchip/irq-gic-v3-mbi.c @@ -84,6 +84,7 @@ static void mbi_free_msi(struct mbi_range *mbi, unsigned int hwirq, static int mbi_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *args) { + msi_alloc_info_t *info = args; struct mbi_range *mbi = NULL; int hwirq, offset, i, err = 0; @@ -104,6 +105,11 @@ static int mbi_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, hwirq = mbi->spi_start + offset; + err = iommu_dma_prepare_msi(info->desc, + mbi_phys_base + GICD_SETSPI_NSR); + if (err) + return err; + for (i = 0; i < nr_irqs; i++) { err = mbi_irq_gic_domain_alloc(domain, virq + i, hwirq + i); if (err) @@ -142,7 +148,7 @@ static void mbi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) msg[0].address_lo = lower_32_bits(mbi_phys_base + GICD_SETSPI_NSR); msg[0].data = data->parent_data->hwirq; - iommu_dma_map_msi_msg(data->irq, msg); + iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg); } #ifdef CONFIG_PCI_MSI @@ -202,7 +208,7 @@ static void mbi_compose_mbi_msg(struct irq_data *data, struct msi_msg *msg) msg[1].address_lo = lower_32_bits(mbi_phys_base + GICD_CLRSPI_NSR); msg[1].data = data->parent_data->hwirq; - iommu_dma_map_msi_msg(data->irq, &msg[1]); + iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), &msg[1]); } /* Platform-MSI specific irqchip */ -- cgit v1.2.3-70-g09d2 From 16e32c3cde7763ab875b9030b443ecbc8e352d8a Mon Sep 17 00:00:00 2001 From: Julien Grall Date: Wed, 1 May 2019 14:58:24 +0100 Subject: iommu/dma-iommu: Remove iommu_dma_map_msi_msg() A recent change split iommu_dma_map_msi_msg() in two new functions. The function was still implemented to avoid modifying all the callers at once. Now that all the callers have been reworked, iommu_dma_map_msi_msg() can be removed. Signed-off-by: Julien Grall Reviewed-by: Robin Murphy Reviewed-by: Eric Auger Acked-by: Joerg Roedel Signed-off-by: Marc Zyngier --- drivers/iommu/dma-iommu.c | 20 -------------------- include/linux/dma-iommu.h | 5 ----- 2 files changed, 25 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index f847904098f7..13916fefeb27 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -935,23 +935,3 @@ void iommu_dma_compose_msi_msg(struct msi_desc *desc, msg->address_lo &= cookie_msi_granule(domain->iova_cookie) - 1; msg->address_lo += lower_32_bits(msi_page->iova); } - -void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) -{ - struct msi_desc *desc = irq_get_msi_desc(irq); - phys_addr_t msi_addr = (u64)msg->address_hi << 32 | msg->address_lo; - - if (WARN_ON(iommu_dma_prepare_msi(desc, msi_addr))) { - /* - * We're called from a void callback, so the best we can do is - * 'fail' by filling the message with obviously bogus values. - * Since we got this far due to an IOMMU being present, it's - * not like the existing address would have worked anyway... - */ - msg->address_hi = ~0U; - msg->address_lo = ~0U; - msg->data = ~0U; - } else { - iommu_dma_compose_msi_msg(desc, msg); - } -} diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h index 0b781a98ee73..476e0c54de2d 100644 --- a/include/linux/dma-iommu.h +++ b/include/linux/dma-iommu.h @@ -84,7 +84,6 @@ int iommu_dma_prepare_msi(struct msi_desc *desc, phys_addr_t msi_addr); void iommu_dma_compose_msi_msg(struct msi_desc *desc, struct msi_msg *msg); -void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg); void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list); #else @@ -124,10 +123,6 @@ static inline void iommu_dma_compose_msi_msg(struct msi_desc *desc, { } -static inline void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) -{ -} - static inline void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list) { } -- cgit v1.2.3-70-g09d2