From 688498ee2785aefeb9fb57a1bc17c1c28b261b8c Mon Sep 17 00:00:00 2001 From: Prasad Sodagudi Date: Fri, 17 Jan 2020 19:25:01 -0800 Subject: [PATCH] soc: qcom: Add support for early brought out subsystems Add support into subsytem restart and secure pil drivers for the subsystems which is brought out of reset during the bootloader stage. During the hlos stage when any client requested for this type of subsystem, check subsystem status before loading the firmware. If subsystem is active and not crashed just enable subsystem IRQs and continue. If subsystem is already crashed, shutdown the subsystem and inform the client driver about error state. Client driver can request for the subsystem second time to load the firmware freshly into memory and to bring out of reset. Change-Id: Ie653a44a67ae33760891334b9449984d56142128 Signed-off-by: Prasad Sodagudi Signed-off-by: Prakruthi Deepak Heragu --- drivers/soc/qcom/subsys-pil-tz.c | 81 ++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c index caa0c3be0d02..a59bea9666d2 100644 --- a/drivers/soc/qcom/subsys-pil-tz.c +++ b/drivers/soc/qcom/subsys-pil-tz.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved. */ #include @@ -82,6 +82,7 @@ struct reg_info { * @subsys: subsystem device pointer * @subsys_desc: subsystem descriptor * @u32 bits_arr[2]: array of bit positions in SCSR registers + * @boot_enabled: true if subsystem is brough of reset during the bootloader. */ struct pil_tz_data { struct device *dev; @@ -98,6 +99,7 @@ struct pil_tz_data { void *minidump_dev; u32 pas_id; struct icc_path *bus_client; + bool boot_enabled; bool enable_bus_scaling; bool keep_proxy_regs_on; struct completion err_ready; @@ -144,6 +146,7 @@ static DEFINE_MUTEX(scm_pas_bw_mutex); static void subsys_disable_all_irqs(struct pil_tz_data *d); static void subsys_enable_all_irqs(struct pil_tz_data *d); +static bool generic_read_status(struct pil_tz_data *d); static int enable_debug; module_param(enable_debug, int, 0644); @@ -800,6 +803,34 @@ static int subsys_powerup(const struct subsys_desc *subsys) return ret; } +static int subsys_powerup_boot_enabled(const struct subsys_desc *subsys) +{ + struct pil_tz_data *d = subsys_to_data(subsys); + int ret = 0; + + if (generic_read_status(d)) { + pr_info("%s: subsystem %s is alive at during device bootup\n", + __func__, d->subsys_desc.name); + subsys_enable_all_irqs(d); + ret = wait_for_err_ready(d); + if (ret) { + pr_err("%s failed to get error ready for %s\n", + __func__, d->subsys_desc.name); + pil_shutdown(&d->desc); + subsys_disable_all_irqs(d); + } + } else { + pil_shutdown(&d->desc); + ret = -EAGAIN; + pr_err("%s: subsystem %s is crashed while device booting\n", + __func__, d->subsys_desc.name); + } + + /* Update the .powerup call back to regular subsys powerup function.*/ + d->subsys_desc.powerup = subsys_powerup; + return ret; +} + static int subsys_ramdump(int enable, const struct subsys_desc *subsys) { struct pil_tz_data *d = subsys_to_data(subsys); @@ -983,6 +1014,24 @@ static void clear_wdog(struct pil_tz_data *d) } } +static bool generic_read_status(struct pil_tz_data *d) +{ + uint32_t status_val, err_value; + + err_value = __raw_readl(d->err_status_spare); + status_val = __raw_readl(d->irq_status); + + if (status_val & BIT(d->bits_arr[ERR_READY])) { + if (err_value == 0x44554d50) { + pr_err("wdog bite is pending\n"); + __raw_writel(BIT(d->bits_arr[ERR_READY]), d->irq_clear); + return false; + } + } + + return true; +} + static irqreturn_t subsys_generic_handler(int irq, void *drv_data) { struct pil_tz_data *d = drv_data; @@ -1009,7 +1058,17 @@ static irqreturn_t subsys_generic_handler(int irq, void *drv_data) static void mask_scsr_irqs(struct pil_tz_data *d) { uint32_t mask_val; - /* Masking all interrupts not handled by HLOS */ + + /* Masking all interrupts from subsystem */ + mask_val = ~0; + __raw_writel(mask_val, d->irq_mask); +} + +static void unmask_scsr_irqs(struct pil_tz_data *d) +{ + uint32_t mask_val; + + /* Un masking interrupts from subsystem to be handled by HLOS */ mask_val = ~0; __raw_writel(mask_val & ~BIT(d->bits_arr[ERR_READY]) & ~BIT(d->bits_arr[PBL_DONE]), d->irq_mask); @@ -1032,6 +1091,7 @@ static void subsys_enable_all_irqs(struct pil_tz_data *d) if (d->ramdump_disable_irq) enable_irq(d->ramdump_disable_irq); if (d->generic_irq) { + unmask_scsr_irqs(d); enable_irq(d->generic_irq); irq_set_irq_wake(d->generic_irq, 1); } @@ -1052,8 +1112,9 @@ static void subsys_disable_all_irqs(struct pil_tz_data *d) if (d->shutdown_ack_irq) disable_irq(d->shutdown_ack_irq); if (d->generic_irq) { - disable_irq(d->generic_irq); + mask_scsr_irqs(d); irq_set_irq_wake(d->generic_irq, 0); + disable_irq(d->generic_irq); } } @@ -1258,11 +1319,15 @@ static int pil_tz_generic_probe(struct platform_device *pdev) d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL); if (!d) return -ENOMEM; + platform_set_drvdata(pdev, d); if (of_property_read_bool(pdev->dev.of_node, "qcom,pil-no-auth")) d->subsys_desc.no_auth = true; + if (of_property_read_bool(pdev->dev.of_node, "qcom,boot-enabled")) + d->boot_enabled = true; + d->keep_proxy_regs_on = of_property_read_bool(pdev->dev.of_node, "qcom,keep-proxy-regs-on"); @@ -1327,11 +1392,21 @@ static int pil_tz_generic_probe(struct platform_device *pdev) d->subsys_desc.dev = &pdev->dev; d->subsys_desc.shutdown = subsys_shutdown; d->subsys_desc.powerup = subsys_powerup; + + /* + * If subsystem is already bought out reset during the bootloader stage, + * instead of doing regular power need to check subsystem status. + * So override power up function to check subsystem crash status. + */ + if (d->boot_enabled) + d->subsys_desc.powerup = subsys_powerup_boot_enabled; + d->subsys_desc.ramdump = subsys_ramdump; d->subsys_desc.free_memory = subsys_free_memory; d->subsys_desc.crash_shutdown = subsys_crash_shutdown; if (of_property_read_bool(pdev->dev.of_node, "qcom,pil-generic-irq-handler")) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sp2soc_irq_status"); d->irq_status = devm_ioremap_resource(&pdev->dev, res);