diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index e9c43909e219..8ffd425c67df 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -234,6 +234,25 @@ config QCOM_Q6V5_PAS CDSP (Compute DSP), MPSS (Modem Peripheral SubSystem), and SLPI (Sensor Low Power Island). +config QCOM_SPSS + tristate "QTI Secure Subsystem Peripheral loading support" + depends on OF && ARCH_QCOM + depends on QCOM_SMEM + depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n) + depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n + depends on QCOM_SYSMON || QCOM_SYSMON=n + select MFD_SYSCON + select QCOM_MDT_LOADER + select QCOM_Q6V5_COMMON + select QCOM_RPROC_COMMON + select QCOM_SCM + help + Say y here to support the TrustZone based Peripheral Image + Loader for the Qualcomm Technologies, Inc. Secure Subsystem + remote processor. This also supports remote processors that + are booted before kernel comes up. + + config QCOM_Q6V5_WCSS tristate "Qualcomm Hexagon based WCSS Peripheral Image Loader" depends on OF && ARCH_QCOM diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 55232526723f..35482ff7e3ee 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_QCOM_Q6V5_COMMON) += qcom_q6v5.o obj-$(CONFIG_QCOM_Q6V5_ADSP) += qcom_q6v5_adsp.o obj-$(CONFIG_QCOM_Q6V5_MSS) += qcom_q6v5_mss.o obj-$(CONFIG_QCOM_Q6V5_PAS) += qcom_q6v5_pas.o +obj-$(CONFIG_QCOM_SPSS) += qcom_spss.o obj-$(CONFIG_QCOM_Q6V5_WCSS) += qcom_q6v5_wcss.o obj-$(CONFIG_QCOM_SYSMON) += qcom_sysmon.o obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o diff --git a/drivers/remoteproc/qcom_spss.c b/drivers/remoteproc/qcom_spss.c new file mode 100644 index 000000000000..e1a2918a7a5f --- /dev/null +++ b/drivers/remoteproc/qcom_spss.c @@ -0,0 +1,687 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Qualcomm Technologies, Inc. SPSS Peripheral Image Loader + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qcom_common.h" +#include "remoteproc_internal.h" + +#define ERR_READY 0 +#define PBL_DONE 1 +#define SPSS_WDOG_ERR 0x44554d50 +#define SPSS_TIMEOUT 5000 + +/* err_status definitions */ +#define PBL_LOG_VALUE (0xef000000) +#define PBL_LOG_MASK (0xff000000) + +#define to_glink_subdev(d) container_of(d, struct qcom_rproc_glink, subdev) + +#define SP_SCSR_MB0_SP2CL_GP0_ADDR 0x1886020 +#define SP_SCSR_MB1_SP2CL_GP0_ADDR 0x1888020 +#define SP_SCSR_MB3_SP2CL_GP0_ADDR 0x188C020 + +#define NUM_OF_DEBUG_REGISTERS_READ 0x3 +struct spss_data { + const char *firmware_name; + int pas_id; + const char *ssr_name; + bool auto_boot; +}; + +struct qcom_spss { + struct device *dev; + struct rproc *rproc; + + struct clk *xo; + + struct reg_info cx; + + int pas_id; + + struct completion start_done; + + phys_addr_t mem_phys; + phys_addr_t mem_reloc; + void *mem_region; + size_t mem_size; + int generic_irq; + + struct qcom_rproc_glink glink_subdev; + struct qcom_rproc_ssr ssr_subdev; + struct qcom_sysmon *sysmon_subdev; + void __iomem *irq_status; + void __iomem *irq_clr; + void __iomem *irq_mask; + void __iomem *err_status; + void __iomem *err_status_spare; + void __iomem *rmb_gpm; + u32 bits_arr[2]; +}; + +static void read_sp2cl_debug_registers(struct qcom_spss *spss); + +static void read_sp2cl_debug_registers(struct qcom_spss *spss) +{ + uint32_t iter; + void __iomem *addr = NULL; + uint32_t debug_register_addr[NUM_OF_DEBUG_REGISTERS_READ] = {SP_SCSR_MB0_SP2CL_GP0_ADDR, + SP_SCSR_MB1_SP2CL_GP0_ADDR, SP_SCSR_MB3_SP2CL_GP0_ADDR}; + for (iter = 0; iter < NUM_OF_DEBUG_REGISTERS_READ; iter++) { + addr = ioremap(debug_register_addr[iter], sizeof(uint32_t)*2); + if (!addr) { + dev_err(spss->dev, "Iteration: [0x%x], addr: [0x%x]\n", iter, addr); + continue; + } + dev_info(spss->dev, "Iteration: [0x%x], Debug Data1: [0x%x], Debug Data2: [0x%x]\n", + iter, readl_relaxed(addr), readl_relaxed(((char *) addr) + sizeof(uint32_t))); + iounmap(addr); + } +} + +static int glink_spss_subdev_start(struct rproc_subdev *subdev) +{ + struct qcom_rproc_glink *glink = to_glink_subdev(subdev); + + glink->edge = qcom_glink_spss_register(glink->dev, glink->node); + + return PTR_ERR_OR_ZERO(glink->edge); +} + +static void glink_spss_subdev_stop(struct rproc_subdev *subdev, bool crashed) +{ + struct qcom_rproc_glink *glink = to_glink_subdev(subdev); + + qcom_glink_spss_unregister(glink->edge); + glink->edge = NULL; +} + +static void glink_spss_subdev_unprepare(struct rproc_subdev *subdev) +{ + struct qcom_rproc_glink *glink = to_glink_subdev(subdev); + + qcom_glink_ssr_notify(glink->ssr_name); +} + +/** + * qcom_add_glink_spss_subdev() - try to add a GLINK SPSS subdevice to rproc + * @rproc: rproc handle to parent the subdevice + * @glink: reference to a GLINK subdev context + * @ssr_name: identifier of the associated remoteproc for ssr notifications + */ +static void qcom_add_glink_spss_subdev(struct rproc *rproc, + struct qcom_rproc_glink *glink, + const char *ssr_name) +{ + struct device *dev = &rproc->dev; + + glink->node = of_get_child_by_name(dev->parent->of_node, "glink-edge"); + if (!glink->node) + return; + + glink->ssr_name = kstrdup_const(ssr_name, GFP_KERNEL); + if (!glink->ssr_name) + return; + + glink->dev = dev; + glink->subdev.start = glink_spss_subdev_start; + glink->subdev.stop = glink_spss_subdev_stop; + glink->subdev.unprepare = glink_spss_subdev_unprepare; + + rproc_add_subdev(rproc, &glink->subdev); +} + +/** + * qcom_remove_glink_spss_subdev() - remove a GLINK SPSS subdevice from rproc + * @rproc: rproc handle + * @glink: reference to a GLINK subdev context + */ +static void qcom_remove_glink_spss_subdev(struct rproc *rproc, + struct qcom_rproc_glink *glink) +{ + if (!glink->node) + return; + + rproc_remove_subdev(rproc, &glink->subdev); + kfree_const(glink->ssr_name); + of_node_put(glink->node); +} + +static void clear_pbl_done(struct qcom_spss *spss) +{ + uint32_t err_value, rmb_err_spare0, rmb_err_spare1, rmb_err_spare2; + + err_value = __raw_readl(spss->err_status); + rmb_err_spare2 = __raw_readl(spss->err_status_spare); + rmb_err_spare1 = __raw_readl(spss->err_status_spare-4); + rmb_err_spare0 = __raw_readl(spss->err_status_spare-8); + + if (err_value) { + dev_err(spss->dev, "PBL error status register: 0x%08x, spare0 register: 0x%08x, spare1 register: 0x%08x, spare2 register: 0x%08x\n", + err_value, rmb_err_spare0, rmb_err_spare1, rmb_err_spare2); + } else + dev_info(spss->dev, "PBL_DONE - 1st phase loading [%s] completed ok\n", + spss->rproc->name); + + __raw_writel(BIT(spss->bits_arr[PBL_DONE]), spss->irq_clr); +} + +static void clear_err_ready(struct qcom_spss *spss) +{ + dev_info(spss->dev, "SW_INIT_DONE - 2nd phase loading [%s] completed ok\n", + spss->rproc->name); + + __raw_writel(BIT(spss->bits_arr[ERR_READY]), spss->irq_clr); + complete(&spss->start_done); +} + +static void clear_sw_init_done_error(struct qcom_spss *spss, int err) +{ + uint32_t rmb_err_spare0; + uint32_t rmb_err_spare1; + uint32_t rmb_err_spare2; + + dev_info(spss->dev, "SW_INIT_DONE - ERROR [%s] [0x%x].\n", spss->rproc->name, err); + + rmb_err_spare2 = __raw_readl(spss->err_status_spare); + rmb_err_spare1 = __raw_readl(spss->err_status_spare-4); + rmb_err_spare0 = __raw_readl(spss->err_status_spare-8); + + dev_err(spss->dev, "spare0 register: 0x%08x, spare1 register: 0x%08x, spare2 register: 0x%08x\n", + rmb_err_spare0, rmb_err_spare1, rmb_err_spare2); + + /* Clear the interrupt source */ + __raw_writel(BIT(spss->bits_arr[ERR_READY]), spss->irq_clr); +} + + + +static void clear_wdog(struct qcom_spss *spss) +{ + dev_err(spss->dev, "wdog bite received from %s!\n", spss->rproc->name); + dev_err(spss->dev, "rproc recovery state: %s\n", spss->rproc->recovery_disabled ? + "disabled and lead to device crash" : "enabled and kick reovery process"); + if (spss->rproc->recovery_disabled) { + spss->rproc->state = RPROC_CRASHED; + panic("Panicking, remoterpoc %s crashed\n", spss->rproc->name); + } + + __raw_writel(BIT(spss->bits_arr[ERR_READY]), spss->irq_clr); + rproc_report_crash(spss->rproc, RPROC_WATCHDOG); +} + +static irqreturn_t spss_generic_handler(int irq, void *dev_id) +{ + struct qcom_spss *spss = dev_id; + uint32_t status_val, err_value; + + err_value = __raw_readl(spss->err_status_spare); + status_val = __raw_readl(spss->irq_status); + + if (status_val & BIT(spss->bits_arr[ERR_READY])) { + if (!err_value) + clear_err_ready(spss); + else if (err_value == SPSS_WDOG_ERR) + clear_wdog(spss); + else + clear_sw_init_done_error(spss, err_value); + } + + if (status_val & BIT(spss->bits_arr[PBL_DONE])) + clear_pbl_done(spss); + + return IRQ_HANDLED; +} + +static void mask_scsr_irqs(struct qcom_spss *spss) +{ + uint32_t mask_val; + + /* Masking all interrupts */ + mask_val = ~0; + __raw_writel(mask_val, spss->irq_mask); +} + +static void unmask_scsr_irqs(struct qcom_spss *spss) +{ + uint32_t mask_val; + + /* unmasking interrupts handled by HLOS */ + mask_val = ~0; + __raw_writel(mask_val & ~BIT(spss->bits_arr[ERR_READY]) & + ~BIT(spss->bits_arr[PBL_DONE]), spss->irq_mask); +} + + +static bool check_status(struct qcom_spss *spss, int *ret_error) +{ + uint32_t status_val, err_value, rmb_err; + + err_value = __raw_readl(spss->err_status_spare); + status_val = __raw_readl(spss->irq_status); + rmb_err = __raw_readl(spss->err_status); + + if ((rmb_err & PBL_LOG_MASK) == PBL_LOG_VALUE) { + dev_err(spss->dev, "PBL error detected\n"); + *ret_error = rmb_err; + return true; + } + + if ((status_val & BIT(spss->bits_arr[ERR_READY])) && err_value == SPSS_WDOG_ERR) { + dev_err(spss->dev, "wdog bite is pending\n"); + __raw_writel(BIT(spss->bits_arr[ERR_READY]), spss->irq_clr); + return true; + } + return false; +} + +static int spss_load(struct rproc *rproc, const struct firmware *fw) +{ + struct qcom_spss *spss = (struct qcom_spss *)rproc->priv; + + return qcom_mdt_load(spss->dev, fw, rproc->firmware, spss->pas_id, + spss->mem_region, spss->mem_phys, spss->mem_size, + &spss->mem_reloc); +} + +static int spss_stop(struct rproc *rproc) +{ + struct qcom_spss *spss = (struct qcom_spss *)rproc->priv; + int ret; + + ret = qcom_scm_pas_shutdown(spss->pas_id); + if (ret) + panic("Panicking, remoteproc %s failed to shutdown.\n", rproc->name); + + mask_scsr_irqs(spss); + + /* Set state as OFFLINE */ + rproc->state = RPROC_OFFLINE; + reinit_completion(&spss->start_done); + + return ret; +} + +static int spss_attach(struct rproc *rproc) +{ + struct qcom_spss *spss = (struct qcom_spss *)rproc->priv; + int ret = 0; + + /* If rproc already crashed stop it and propagate error */ + if (check_status(spss, &ret)) { + dev_err(spss->dev, "Failed to attach SPSS remote proc and shutdown\n"); + spss_stop(rproc); + return ret; + } + /* If booted successfully then wait for init_done*/ + + unmask_scsr_irqs(spss); + + ret = wait_for_completion_timeout(&spss->start_done, msecs_to_jiffies(SPSS_TIMEOUT)); + read_sp2cl_debug_registers(spss); + if (rproc->recovery_disabled && !ret) { + dev_err(spss->dev, "%d ms timeout poked\n", SPSS_TIMEOUT); + panic("Panicking, %s attach timed out\n", rproc->name); + } else if (!ret) { + dev_err(spss->dev, "recovery disabled (after timeout)\n"); + } + + ret = ret ? 0 : -ETIMEDOUT; + + return ret; +} + +static inline void disable_regulator(struct reg_info *regulator) +{ + regulator_set_voltage(regulator->reg, 0, INT_MAX); + regulator_set_load(regulator->reg, 0); + regulator_disable(regulator->reg); +} + +static inline int enable_regulator(struct reg_info *regulator) +{ + regulator_set_voltage(regulator->reg, regulator->uV, INT_MAX); + regulator_set_load(regulator->reg, regulator->uA); + return regulator_enable(regulator->reg); +} + +static int spss_start(struct rproc *rproc) +{ + struct qcom_spss *spss = (struct qcom_spss *)rproc->priv; + int ret = 0; + + ret = clk_prepare_enable(spss->xo); + if (ret) + return ret; + + ret = enable_regulator(&spss->cx); + if (ret) + goto disable_xo_clk; + + ret = qcom_scm_pas_auth_and_reset(spss->pas_id); + if (ret) + panic("Panicking, auth and reset failed for remoteproc %s\n", rproc->name); + + unmask_scsr_irqs(spss); + dev_err(spss->dev, "trying to read spss registers\n"); + ret = wait_for_completion_timeout(&spss->start_done, msecs_to_jiffies(SPSS_TIMEOUT)); + read_sp2cl_debug_registers(spss); + if (rproc->recovery_disabled && !ret) + panic("Panicking, %s start timed out\n", rproc->name); + else if (!ret) + dev_err(spss->dev, "start timed out\n"); + ret = ret ? 0 : -ETIMEDOUT; + + disable_regulator(&spss->cx); +disable_xo_clk: + clk_disable_unprepare(spss->xo); + return ret; +} + +static void *spss_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem) +{ + struct qcom_spss *spss = (struct qcom_spss *)rproc->priv; + int offset; + + offset = da - spss->mem_reloc; + if (offset < 0 || offset + len > spss->mem_size) { + dev_err(&rproc->dev, "offset: %llx, da: %llx, len: %llx\n", offset, da, len); + return NULL; + } + + return spss->mem_region + offset; +} + +static const struct rproc_ops spss_ops = { + .stop = spss_stop, + .da_to_va = spss_da_to_va, + .load = spss_load, + .attach = spss_attach, + .parse_fw = qcom_register_dump_segments, + .start = spss_start, +}; + +static int spss_init_clock(struct qcom_spss *spss) +{ + int ret; + + spss->xo = devm_clk_get(spss->dev, "xo"); + if (IS_ERR(spss->xo)) { + ret = PTR_ERR(spss->xo); + if (ret != -EPROBE_DEFER) + dev_err(spss->dev, "failed to get xo clock\n"); + return ret; + } + + return 0; +} + +static int init_regulator(struct device *dev, struct reg_info *regulator, const char *reg_name) +{ + int len, rc; + char uv_ua[50]; + u32 uv_ua_vals[2]; + + regulator->reg = devm_regulator_get(dev, reg_name); + if (IS_ERR(regulator->reg)) + return PTR_ERR(regulator->reg); + + snprintf(uv_ua, sizeof(uv_ua), "%s-uV-uA", reg_name); + if (!of_find_property(dev->of_node, uv_ua, &len)) + return -EINVAL; + + rc = of_property_read_u32_array(dev->of_node, uv_ua, + uv_ua_vals, + ARRAY_SIZE(uv_ua_vals)); + if (rc) { + dev_err(dev, "Failed to read uV-uA value(rc:%d)\n", rc); + return rc; + } + if (uv_ua_vals[0] > 0) + regulator->uV = uv_ua_vals[0]; + if (uv_ua_vals[1] > 0) + regulator->uA = uv_ua_vals[1]; + + return 0; +} + +static int spss_alloc_memory_region(struct qcom_spss *spss) +{ + struct device_node *node; + struct resource r; + int ret, extra_size = 0; + + node = of_parse_phandle(spss->dev->of_node, "memory-region", 0); + if (!node) { + dev_err(spss->dev, "no memory-region specified\n"); + return -EINVAL; + } + + ret = of_address_to_resource(node, 0, &r); + if (ret) + return ret; + + ret = of_property_read_u32(spss->dev->of_node, "qcom,extra-size", &extra_size); + + spss->mem_phys = spss->mem_reloc = r.start; + spss->mem_size = resource_size(&r) + extra_size; + spss->mem_region = devm_ioremap_wc(spss->dev, spss->mem_phys, spss->mem_size); + if (!spss->mem_region) { + dev_err(spss->dev, "unable to map memory region: %pa+%zx\n", + &r.start, spss->mem_size); + return -EBUSY; + } + + return 0; +} + +static int qcom_spss_init_mmio(struct platform_device *pdev, struct qcom_spss *spss) +{ + struct resource *res; + int ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb_general_purpose"); + spss->rmb_gpm = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(spss->rmb_gpm)) + return PTR_ERR(spss->rmb_gpm); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sp2soc_irq_status"); + spss->irq_status = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(spss->irq_status)) + return PTR_ERR(spss->irq_status); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sp2soc_irq_clr"); + spss->irq_clr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(spss->irq_clr)) + return PTR_ERR(spss->irq_clr); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sp2soc_irq_mask"); + spss->irq_mask = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(spss->irq_mask)) + return PTR_ERR(spss->irq_mask); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb_err"); + spss->err_status = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(spss->err_status)) + return PTR_ERR(spss->err_status); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb_err_spare2"); + spss->err_status_spare = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(spss->err_status_spare)) + return PTR_ERR(spss->err_status_spare); + + ret = of_property_read_u32_array(pdev->dev.of_node, "qcom,spss-scsr-bits", spss->bits_arr, + ARRAY_SIZE(spss->bits_arr)); + if (ret) + return ret; + + return 0; + +} + +int qcom_spss_set_fw_name(struct rproc *rproc, const char *fw_name) +{ + const char *p; + + p = kstrdup_const(fw_name, GFP_KERNEL); + if (!p) + return -ENOMEM; + + mutex_lock(&rproc->lock); + kfree(rproc->firmware); + rproc->firmware = p; + mutex_unlock(&rproc->lock); + + return 0; +} +EXPORT_SYMBOL(qcom_spss_set_fw_name); + +static int qcom_spss_probe(struct platform_device *pdev) +{ + const struct spss_data *desc; + struct qcom_spss *spss; + struct rproc *rproc; + const char *fw_name; + int ret; + + desc = of_device_get_match_data(&pdev->dev); + if (!desc) + return -EINVAL; + + if (!qcom_scm_is_available()) + return -EPROBE_DEFER; + + fw_name = desc->firmware_name; + rproc = rproc_alloc(&pdev->dev, pdev->name, &spss_ops, fw_name, sizeof(*spss)); + if (!rproc) { + dev_err(&pdev->dev, "unable to allocate remoteproc\n"); + return -ENOMEM; + } + + spss = (struct qcom_spss *)rproc->priv; + spss->dev = &pdev->dev; + spss->rproc = rproc; + spss->pas_id = desc->pas_id; + init_completion(&spss->start_done); + platform_set_drvdata(pdev, spss); + rproc->auto_boot = desc->auto_boot; + rproc->recovery_disabled = true; + rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); + + ret = device_init_wakeup(spss->dev, true); + if (ret) + goto free_rproc; + + ret = qcom_spss_init_mmio(pdev, spss); + if (ret) + goto deinit_wakeup_source; + + if (!(__raw_readl(spss->rmb_gpm) & BIT(0))) + rproc->state = RPROC_DETACHED; + else + rproc->state = RPROC_OFFLINE; + + ret = spss_alloc_memory_region(spss); + if (ret) + goto deinit_wakeup_source; + + ret = spss_init_clock(spss); + if (ret) + goto deinit_wakeup_source; + + ret = init_regulator(spss->dev, &spss->cx, "cx"); + if (ret) + goto deinit_wakeup_source; + + qcom_add_glink_spss_subdev(rproc, &spss->glink_subdev, "spss"); + qcom_add_ssr_subdev(rproc, &spss->ssr_subdev, desc->ssr_name); + spss->sysmon_subdev = qcom_add_sysmon_subdev(rproc, desc->ssr_name, -EINVAL); + if (IS_ERR(spss->sysmon_subdev)) { + dev_err(spss->dev, "failed to add sysmon subdevice\n"); + goto deinit_wakeup_source; + } + + mask_scsr_irqs(spss); + spss->generic_irq = platform_get_irq(pdev, 0); + ret = devm_request_threaded_irq(&pdev->dev, spss->generic_irq, NULL, spss_generic_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, "generic-irq", spss); + if (ret) { + dev_err(&pdev->dev, "failed to acquire generic IRQ\n"); + goto remove_subdev; + } + + ret = rproc_add(rproc); + if (ret) + goto remove_subdev; + + return 0; + +remove_subdev: + qcom_remove_sysmon_subdev(spss->sysmon_subdev); +deinit_wakeup_source: + device_init_wakeup(spss->dev, false); +free_rproc: + rproc_free(rproc); + + return ret; +} + +static int qcom_spss_remove(struct platform_device *pdev) +{ + struct qcom_spss *spss = platform_get_drvdata(pdev); + + rproc_del(spss->rproc); + qcom_remove_glink_spss_subdev(spss->rproc, &spss->glink_subdev); + qcom_remove_ssr_subdev(spss->rproc, &spss->ssr_subdev); + qcom_remove_sysmon_subdev(spss->sysmon_subdev); + device_init_wakeup(spss->dev, false); + rproc_free(spss->rproc); + + return 0; +} + +static const struct spss_data spss_resource_init = { + .firmware_name = "spss.mdt", + .pas_id = 14, + .ssr_name = "spss", + .auto_boot = false, +}; + +static const struct of_device_id spss_of_match[] = { + { .compatible = "qcom,waipio-spss-pas", .data = &spss_resource_init}, + { .compatible = "qcom,kalama-spss-pas", .data = &spss_resource_init}, + { }, +}; +MODULE_DEVICE_TABLE(of, spss_of_match); + +static struct platform_driver spss_driver = { + .probe = qcom_spss_probe, + .remove = qcom_spss_remove, + .driver = { + .name = "qcom_spss", + .of_match_table = spss_of_match, + }, +}; + +module_platform_driver(spss_driver); +MODULE_DESCRIPTION("QTI Peripheral Image Loader for Secure Subsystem"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/remoteproc/qcom_spss.h b/include/linux/remoteproc/qcom_spss.h new file mode 100644 index 000000000000..ffab59b9c592 --- /dev/null +++ b/include/linux/remoteproc/qcom_spss.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _QCOM_RPROC_SPSS_H_ +#define _QCOM_RPROC_SPSS_H_ + +/* + * Include this file only after including linux/remoteproc.h + */ + +#if IS_ENABLED(CONFIG_QCOM_SPSS) + +extern int qcom_spss_set_fw_name(struct rproc *rproc, const char *fw_name); + +#else + +static inline int qcom_spss_set_fw_name(struct rproc *rproc, const char *fw_name) +{ + return -ENODEV; +} + +#endif + +#endif