Merge tag 'rproc-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/remoteproc...
[linux-block.git] / drivers / remoteproc / qcom_q6v5_adsp.c
index 4c9a1b99cd511abd40d3830ff3f4abc3e2cad9fc..08d8dad22ca7e2fe3ecf2f37ac0fade8797d08b7 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/firmware.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/iommu.h>
 #include <linux/iopoll.h>
 #include <linux/kernel.h>
 #include <linux/mfd/syscon.h>
 #define LPASS_PWR_ON_REG               0x10
 #define LPASS_HALTREQ_REG              0x0
 
+#define SID_MASK_DEFAULT        0xF
+
 #define QDSP6SS_XO_CBCR                0x38
 #define QDSP6SS_CORE_CBCR      0x20
 #define QDSP6SS_SLEEP_CBCR     0x3c
 
 #define QCOM_Q6V5_RPROC_PROXY_PD_MAX   3
 
+#define LPASS_BOOT_CORE_START  BIT(0)
+#define LPASS_BOOT_CMD_START   BIT(0)
+#define LPASS_EFUSE_Q6SS_EVB_SEL 0x0
+
 struct adsp_pil_data {
        int crash_reason_smem;
        const char *firmware_name;
@@ -62,6 +69,7 @@ struct adsp_pil_data {
        const char *sysmon_name;
        int ssctl_id;
        bool is_wpss;
+       bool has_iommu;
        bool auto_boot;
 
        const char **clk_ids;
@@ -82,6 +90,7 @@ struct qcom_adsp {
        struct clk_bulk_data *clks;
 
        void __iomem *qdsp6ss_base;
+       void __iomem *lpass_efuse;
 
        struct reset_control *pdc_sync_reset;
        struct reset_control *restart;
@@ -99,6 +108,7 @@ struct qcom_adsp {
        phys_addr_t mem_reloc;
        void *mem_region;
        size_t mem_size;
+       bool has_iommu;
 
        struct device *proxy_pds[QCOM_Q6V5_RPROC_PROXY_PD_MAX];
        size_t proxy_pd_count;
@@ -325,6 +335,48 @@ static int adsp_load(struct rproc *rproc, const struct firmware *fw)
        return 0;
 }
 
+static void adsp_unmap_carveout(struct rproc *rproc)
+{
+       struct qcom_adsp *adsp = rproc->priv;
+
+       if (adsp->has_iommu)
+               iommu_unmap(rproc->domain, adsp->mem_phys, adsp->mem_size);
+}
+
+static int adsp_map_carveout(struct rproc *rproc)
+{
+       struct qcom_adsp *adsp = rproc->priv;
+       struct of_phandle_args args;
+       long long sid;
+       unsigned long iova;
+       int ret;
+
+       if (!adsp->has_iommu)
+               return 0;
+
+       if (!rproc->domain)
+               return -EINVAL;
+
+       ret = of_parse_phandle_with_args(adsp->dev->of_node, "iommus", "#iommu-cells", 0, &args);
+       if (ret < 0)
+               return ret;
+
+       sid = args.args[0] & SID_MASK_DEFAULT;
+
+       /* Add SID configuration for ADSP Firmware to SMMU */
+       iova =  adsp->mem_phys | (sid << 32);
+
+       ret = iommu_map(rproc->domain, iova, adsp->mem_phys,
+                       adsp->mem_size, IOMMU_READ | IOMMU_WRITE,
+                       GFP_KERNEL);
+       if (ret) {
+               dev_err(adsp->dev, "Unable to map ADSP Physical Memory\n");
+               return ret;
+       }
+
+       return 0;
+}
+
 static int adsp_start(struct rproc *rproc)
 {
        struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
@@ -335,9 +387,15 @@ static int adsp_start(struct rproc *rproc)
        if (ret)
                return ret;
 
+       ret = adsp_map_carveout(rproc);
+       if (ret) {
+               dev_err(adsp->dev, "ADSP smmu mapping failed\n");
+               goto disable_irqs;
+       }
+
        ret = clk_prepare_enable(adsp->xo);
        if (ret)
-               goto disable_irqs;
+               goto adsp_smmu_unmap;
 
        ret = qcom_rproc_pds_enable(adsp, adsp->proxy_pds,
                                    adsp->proxy_pd_count);
@@ -362,11 +420,14 @@ static int adsp_start(struct rproc *rproc)
        /* Program boot address */
        writel(adsp->mem_phys >> 4, adsp->qdsp6ss_base + RST_EVB_REG);
 
+       if (adsp->lpass_efuse)
+               writel(LPASS_EFUSE_Q6SS_EVB_SEL, adsp->lpass_efuse);
+
        /* De-assert QDSP6 stop core. QDSP6 will execute after out of reset */
-       writel(0x1, adsp->qdsp6ss_base + CORE_START_REG);
+       writel(LPASS_BOOT_CORE_START, adsp->qdsp6ss_base + CORE_START_REG);
 
        /* Trigger boot FSM to start QDSP6 */
-       writel(0x1, adsp->qdsp6ss_base + BOOT_CMD_REG);
+       writel(LPASS_BOOT_CMD_START, adsp->qdsp6ss_base + BOOT_CMD_REG);
 
        /* Wait for core to come out of reset */
        ret = readl_poll_timeout(adsp->qdsp6ss_base + BOOT_STATUS_REG,
@@ -390,6 +451,8 @@ disable_power_domain:
        qcom_rproc_pds_disable(adsp, adsp->proxy_pds, adsp->proxy_pd_count);
 disable_xo_clk:
        clk_disable_unprepare(adsp->xo);
+adsp_smmu_unmap:
+       adsp_unmap_carveout(rproc);
 disable_irqs:
        qcom_q6v5_unprepare(&adsp->q6v5);
 
@@ -418,6 +481,8 @@ static int adsp_stop(struct rproc *rproc)
        if (ret)
                dev_err(adsp->dev, "failed to shutdown: %d\n", ret);
 
+       adsp_unmap_carveout(rproc);
+
        handover = qcom_q6v5_unprepare(&adsp->q6v5);
        if (handover)
                qcom_adsp_pil_handover(&adsp->q6v5);
@@ -437,6 +502,27 @@ static void *adsp_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iom
        return adsp->mem_region + offset;
 }
 
+static int adsp_parse_firmware(struct rproc *rproc, const struct firmware *fw)
+{
+       struct qcom_adsp *adsp = rproc->priv;
+       int ret;
+
+       ret = qcom_register_dump_segments(rproc, fw);
+       if (ret) {
+               dev_err(&rproc->dev, "Error in registering dump segments\n");
+               return ret;
+       }
+
+       if (adsp->has_iommu) {
+               ret = rproc_elf_load_rsc_table(rproc, fw);
+               if (ret) {
+                       dev_err(&rproc->dev, "Error in loading resource table\n");
+                       return ret;
+               }
+       }
+       return 0;
+}
+
 static unsigned long adsp_panic(struct rproc *rproc)
 {
        struct qcom_adsp *adsp = rproc->priv;
@@ -448,7 +534,7 @@ static const struct rproc_ops adsp_ops = {
        .start = adsp_start,
        .stop = adsp_stop,
        .da_to_va = adsp_da_to_va,
-       .parse_fw = qcom_register_dump_segments,
+       .parse_fw = adsp_parse_firmware,
        .load = adsp_load,
        .panic = adsp_panic,
 };
@@ -507,6 +593,7 @@ static int adsp_init_reset(struct qcom_adsp *adsp)
 static int adsp_init_mmio(struct qcom_adsp *adsp,
                                struct platform_device *pdev)
 {
+       struct resource *efuse_region;
        struct device_node *syscon;
        int ret;
 
@@ -516,6 +603,17 @@ static int adsp_init_mmio(struct qcom_adsp *adsp,
                return PTR_ERR(adsp->qdsp6ss_base);
        }
 
+       efuse_region = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (!efuse_region) {
+               adsp->lpass_efuse = NULL;
+               dev_dbg(adsp->dev, "failed to get efuse memory region\n");
+       } else {
+               adsp->lpass_efuse = devm_ioremap_resource(&pdev->dev, efuse_region);
+               if (IS_ERR(adsp->lpass_efuse)) {
+                       dev_err(adsp->dev, "failed to map efuse registers\n");
+                       return PTR_ERR(adsp->lpass_efuse);
+               }
+       }
        syscon = of_parse_phandle(pdev->dev.of_node, "qcom,halt-regs", 0);
        if (!syscon) {
                dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n");
@@ -595,12 +693,15 @@ static int adsp_probe(struct platform_device *pdev)
        }
 
        rproc->auto_boot = desc->auto_boot;
+       rproc->has_iommu = desc->has_iommu;
        rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
 
        adsp = (struct qcom_adsp *)rproc->priv;
        adsp->dev = &pdev->dev;
        adsp->rproc = rproc;
        adsp->info_name = desc->sysmon_name;
+       adsp->has_iommu = desc->has_iommu;
+
        platform_set_drvdata(pdev, adsp);
 
        if (desc->is_wpss)
@@ -696,6 +797,21 @@ static const struct adsp_pil_data adsp_resource_init = {
        },
 };
 
+static const struct adsp_pil_data adsp_sc7280_resource_init = {
+       .crash_reason_smem = 423,
+       .firmware_name = "adsp.pbn",
+       .load_state = "adsp",
+       .ssr_name = "lpass",
+       .sysmon_name = "adsp",
+       .ssctl_id = 0x14,
+       .has_iommu = true,
+       .auto_boot = true,
+       .clk_ids = (const char*[]) {
+               "gcc_cfg_noc_lpass", NULL
+       },
+       .num_clks = 1,
+};
+
 static const struct adsp_pil_data cdsp_resource_init = {
        .crash_reason_smem = 601,
        .firmware_name = "cdsp.mdt",
@@ -734,6 +850,7 @@ static const struct adsp_pil_data wpss_resource_init = {
 
 static const struct of_device_id adsp_of_match[] = {
        { .compatible = "qcom,qcs404-cdsp-pil", .data = &cdsp_resource_init },
+       { .compatible = "qcom,sc7280-adsp-pil", .data = &adsp_sc7280_resource_init },
        { .compatible = "qcom,sc7280-wpss-pil", .data = &wpss_resource_init },
        { .compatible = "qcom,sdm845-adsp-pil", .data = &adsp_resource_init },
        { },