|
@@ -37,8 +37,6 @@
|
|
|
|
|
|
#include <linux/qcom_scm.h>
|
|
|
|
|
|
-#define MPSS_FIRMWARE_NAME "modem.mdt"
|
|
|
-
|
|
|
#define MPSS_CRASH_REASON_SMEM 421
|
|
|
|
|
|
/* RMB Status Register Values */
|
|
@@ -277,6 +275,16 @@ static void q6v5_clk_disable(struct device *dev,
|
|
|
clk_disable_unprepare(clks[i]);
|
|
|
}
|
|
|
|
|
|
+static struct resource_table *q6v5_find_rsc_table(struct rproc *rproc,
|
|
|
+ const struct firmware *fw,
|
|
|
+ int *tablesz)
|
|
|
+{
|
|
|
+ static struct resource_table table = { .ver = 1, };
|
|
|
+
|
|
|
+ *tablesz = sizeof(table);
|
|
|
+ return &table;
|
|
|
+}
|
|
|
+
|
|
|
static int q6v5_load(struct rproc *rproc, const struct firmware *fw)
|
|
|
{
|
|
|
struct q6v5 *qproc = rproc->priv;
|
|
@@ -287,7 +295,7 @@ static int q6v5_load(struct rproc *rproc, const struct firmware *fw)
|
|
|
}
|
|
|
|
|
|
static const struct rproc_fw_ops q6v5_fw_ops = {
|
|
|
- .find_rsc_table = qcom_mdt_find_rsc_table,
|
|
|
+ .find_rsc_table = q6v5_find_rsc_table,
|
|
|
.load = q6v5_load,
|
|
|
};
|
|
|
|
|
@@ -464,45 +472,109 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw)
|
|
|
return ret < 0 ? ret : 0;
|
|
|
}
|
|
|
|
|
|
-static int q6v5_mpss_validate(struct q6v5 *qproc, const struct firmware *fw)
|
|
|
+static bool q6v5_phdr_valid(const struct elf32_phdr *phdr)
|
|
|
+{
|
|
|
+ if (phdr->p_type != PT_LOAD)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if (!phdr->p_memsz)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static int q6v5_mpss_load(struct q6v5 *qproc)
|
|
|
{
|
|
|
const struct elf32_phdr *phdrs;
|
|
|
const struct elf32_phdr *phdr;
|
|
|
+ const struct firmware *seg_fw;
|
|
|
+ const struct firmware *fw;
|
|
|
struct elf32_hdr *ehdr;
|
|
|
+ phys_addr_t mpss_reloc;
|
|
|
phys_addr_t boot_addr;
|
|
|
- phys_addr_t fw_addr;
|
|
|
- bool relocate;
|
|
|
+ phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX;
|
|
|
+ phys_addr_t max_addr = 0;
|
|
|
+ bool relocate = false;
|
|
|
+ char seg_name[10];
|
|
|
+ size_t offset;
|
|
|
size_t size;
|
|
|
+ void *ptr;
|
|
|
int ret;
|
|
|
int i;
|
|
|
|
|
|
- ret = qcom_mdt_parse(fw, &fw_addr, NULL, &relocate);
|
|
|
- if (ret) {
|
|
|
- dev_err(qproc->dev, "failed to parse mdt header\n");
|
|
|
+ ret = request_firmware(&fw, "modem.mdt", qproc->dev);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(qproc->dev, "unable to load modem.mdt\n");
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- if (relocate)
|
|
|
- boot_addr = qproc->mpss_phys;
|
|
|
- else
|
|
|
- boot_addr = fw_addr;
|
|
|
+ /* Initialize the RMB validator */
|
|
|
+ writel(0, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG);
|
|
|
+
|
|
|
+ ret = q6v5_mpss_init_image(qproc, fw);
|
|
|
+ if (ret)
|
|
|
+ goto release_firmware;
|
|
|
|
|
|
ehdr = (struct elf32_hdr *)fw->data;
|
|
|
phdrs = (struct elf32_phdr *)(ehdr + 1);
|
|
|
- for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
|
|
|
+
|
|
|
+ for (i = 0; i < ehdr->e_phnum; i++) {
|
|
|
phdr = &phdrs[i];
|
|
|
|
|
|
- if (phdr->p_type != PT_LOAD)
|
|
|
+ if (!q6v5_phdr_valid(phdr))
|
|
|
continue;
|
|
|
|
|
|
- if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
|
|
|
- continue;
|
|
|
+ if (phdr->p_flags & QCOM_MDT_RELOCATABLE)
|
|
|
+ relocate = true;
|
|
|
+
|
|
|
+ if (phdr->p_paddr < min_addr)
|
|
|
+ min_addr = phdr->p_paddr;
|
|
|
|
|
|
- if (!phdr->p_memsz)
|
|
|
+ if (phdr->p_paddr + phdr->p_memsz > max_addr)
|
|
|
+ max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K);
|
|
|
+ }
|
|
|
+
|
|
|
+ mpss_reloc = relocate ? min_addr : qproc->mpss_phys;
|
|
|
+
|
|
|
+ for (i = 0; i < ehdr->e_phnum; i++) {
|
|
|
+ phdr = &phdrs[i];
|
|
|
+
|
|
|
+ if (!q6v5_phdr_valid(phdr))
|
|
|
continue;
|
|
|
|
|
|
+ offset = phdr->p_paddr - mpss_reloc;
|
|
|
+ if (offset < 0 || offset + phdr->p_memsz > qproc->mpss_size) {
|
|
|
+ dev_err(qproc->dev, "segment outside memory range\n");
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto release_firmware;
|
|
|
+ }
|
|
|
+
|
|
|
+ ptr = qproc->mpss_region + offset;
|
|
|
+
|
|
|
+ if (phdr->p_filesz) {
|
|
|
+ snprintf(seg_name, sizeof(seg_name), "modem.b%02d", i);
|
|
|
+ ret = request_firmware(&seg_fw, seg_name, qproc->dev);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(qproc->dev, "failed to load %s\n", seg_name);
|
|
|
+ goto release_firmware;
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(ptr, seg_fw->data, seg_fw->size);
|
|
|
+
|
|
|
+ release_firmware(seg_fw);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (phdr->p_memsz > phdr->p_filesz) {
|
|
|
+ memset(ptr + phdr->p_filesz, 0,
|
|
|
+ phdr->p_memsz - phdr->p_filesz);
|
|
|
+ }
|
|
|
+
|
|
|
size = readl(qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG);
|
|
|
if (!size) {
|
|
|
+ boot_addr = relocate ? qproc->mpss_phys : min_addr;
|
|
|
writel(boot_addr, qproc->rmb_base + RMB_PMI_CODE_START_REG);
|
|
|
writel(RMB_CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG);
|
|
|
}
|
|
@@ -517,44 +589,6 @@ static int q6v5_mpss_validate(struct q6v5 *qproc, const struct firmware *fw)
|
|
|
else if (ret < 0)
|
|
|
dev_err(qproc->dev, "MPSS authentication failed: %d\n", ret);
|
|
|
|
|
|
- return ret < 0 ? ret : 0;
|
|
|
-}
|
|
|
-
|
|
|
-static int q6v5_mpss_load(struct q6v5 *qproc)
|
|
|
-{
|
|
|
- const struct firmware *fw;
|
|
|
- phys_addr_t fw_addr;
|
|
|
- bool relocate;
|
|
|
- int ret;
|
|
|
-
|
|
|
- ret = request_firmware(&fw, MPSS_FIRMWARE_NAME, qproc->dev);
|
|
|
- if (ret < 0) {
|
|
|
- dev_err(qproc->dev, "unable to load " MPSS_FIRMWARE_NAME "\n");
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
- ret = qcom_mdt_parse(fw, &fw_addr, NULL, &relocate);
|
|
|
- if (ret) {
|
|
|
- dev_err(qproc->dev, "failed to parse mdt header\n");
|
|
|
- goto release_firmware;
|
|
|
- }
|
|
|
-
|
|
|
- if (relocate)
|
|
|
- qproc->mpss_reloc = fw_addr;
|
|
|
-
|
|
|
- /* Initialize the RMB validator */
|
|
|
- writel(0, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG);
|
|
|
-
|
|
|
- ret = q6v5_mpss_init_image(qproc, fw);
|
|
|
- if (ret)
|
|
|
- goto release_firmware;
|
|
|
-
|
|
|
- ret = qcom_mdt_load(qproc->rproc, fw, MPSS_FIRMWARE_NAME);
|
|
|
- if (ret)
|
|
|
- goto release_firmware;
|
|
|
-
|
|
|
- ret = q6v5_mpss_validate(qproc, fw);
|
|
|
-
|
|
|
release_firmware:
|
|
|
release_firmware(fw);
|
|
|
|