#include <linux/qcom_scm.h>
-#define MPSS_FIRMWARE_NAME "modem.mdt"
-
#define MPSS_CRASH_REASON_SMEM 421
/* RMB Status Register Values */
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;
}
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,
};
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);
}
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);