|
@@ -23,22 +23,21 @@
|
|
|
#include <linux/mfd/syscon.h>
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/of_address.h>
|
|
|
+#include <linux/of_device.h>
|
|
|
#include <linux/platform_device.h>
|
|
|
#include <linux/regmap.h>
|
|
|
#include <linux/regulator/consumer.h>
|
|
|
#include <linux/remoteproc.h>
|
|
|
#include <linux/reset.h>
|
|
|
+#include <linux/soc/qcom/mdt_loader.h>
|
|
|
#include <linux/soc/qcom/smem.h>
|
|
|
#include <linux/soc/qcom/smem_state.h>
|
|
|
|
|
|
#include "remoteproc_internal.h"
|
|
|
-#include "qcom_mdt_loader.h"
|
|
|
+#include "qcom_common.h"
|
|
|
|
|
|
#include <linux/qcom_scm.h>
|
|
|
|
|
|
-#define MBA_FIRMWARE_NAME "mba.b00"
|
|
|
-#define MPSS_FIRMWARE_NAME "modem.mdt"
|
|
|
-
|
|
|
#define MPSS_CRASH_REASON_SMEM 421
|
|
|
|
|
|
/* RMB Status Register Values */
|
|
@@ -93,6 +92,26 @@
|
|
|
#define QDSS_BHS_ON BIT(21)
|
|
|
#define QDSS_LDO_BYP BIT(22)
|
|
|
|
|
|
+struct reg_info {
|
|
|
+ struct regulator *reg;
|
|
|
+ int uV;
|
|
|
+ int uA;
|
|
|
+};
|
|
|
+
|
|
|
+struct qcom_mss_reg_res {
|
|
|
+ const char *supply;
|
|
|
+ int uV;
|
|
|
+ int uA;
|
|
|
+};
|
|
|
+
|
|
|
+struct rproc_hexagon_res {
|
|
|
+ const char *hexagon_mba_image;
|
|
|
+ struct qcom_mss_reg_res *proxy_supply;
|
|
|
+ struct qcom_mss_reg_res *active_supply;
|
|
|
+ char **proxy_clk_names;
|
|
|
+ char **active_clk_names;
|
|
|
+};
|
|
|
+
|
|
|
struct q6v5 {
|
|
|
struct device *dev;
|
|
|
struct rproc *rproc;
|
|
@@ -110,11 +129,15 @@ struct q6v5 {
|
|
|
struct qcom_smem_state *state;
|
|
|
unsigned stop_bit;
|
|
|
|
|
|
- struct regulator_bulk_data supply[4];
|
|
|
+ struct clk *active_clks[8];
|
|
|
+ struct clk *proxy_clks[4];
|
|
|
+ int active_clk_count;
|
|
|
+ int proxy_clk_count;
|
|
|
|
|
|
- struct clk *ahb_clk;
|
|
|
- struct clk *axi_clk;
|
|
|
- struct clk *rom_clk;
|
|
|
+ struct reg_info active_regs[1];
|
|
|
+ struct reg_info proxy_regs[3];
|
|
|
+ int active_reg_count;
|
|
|
+ int proxy_reg_count;
|
|
|
|
|
|
struct completion start_done;
|
|
|
struct completion stop_done;
|
|
@@ -128,65 +151,141 @@ struct q6v5 {
|
|
|
phys_addr_t mpss_reloc;
|
|
|
void *mpss_region;
|
|
|
size_t mpss_size;
|
|
|
-};
|
|
|
|
|
|
-enum {
|
|
|
- Q6V5_SUPPLY_CX,
|
|
|
- Q6V5_SUPPLY_MX,
|
|
|
- Q6V5_SUPPLY_MSS,
|
|
|
- Q6V5_SUPPLY_PLL,
|
|
|
+ struct qcom_rproc_subdev smd_subdev;
|
|
|
};
|
|
|
|
|
|
-static int q6v5_regulator_init(struct q6v5 *qproc)
|
|
|
+static int q6v5_regulator_init(struct device *dev, struct reg_info *regs,
|
|
|
+ const struct qcom_mss_reg_res *reg_res)
|
|
|
{
|
|
|
- int ret;
|
|
|
+ int rc;
|
|
|
+ int i;
|
|
|
|
|
|
- qproc->supply[Q6V5_SUPPLY_CX].supply = "cx";
|
|
|
- qproc->supply[Q6V5_SUPPLY_MX].supply = "mx";
|
|
|
- qproc->supply[Q6V5_SUPPLY_MSS].supply = "mss";
|
|
|
- qproc->supply[Q6V5_SUPPLY_PLL].supply = "pll";
|
|
|
+ if (!reg_res)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ for (i = 0; reg_res[i].supply; i++) {
|
|
|
+ regs[i].reg = devm_regulator_get(dev, reg_res[i].supply);
|
|
|
+ if (IS_ERR(regs[i].reg)) {
|
|
|
+ rc = PTR_ERR(regs[i].reg);
|
|
|
+ if (rc != -EPROBE_DEFER)
|
|
|
+ dev_err(dev, "Failed to get %s\n regulator",
|
|
|
+ reg_res[i].supply);
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
|
|
|
- ret = devm_regulator_bulk_get(qproc->dev,
|
|
|
- ARRAY_SIZE(qproc->supply), qproc->supply);
|
|
|
- if (ret < 0) {
|
|
|
- dev_err(qproc->dev, "failed to get supplies\n");
|
|
|
- return ret;
|
|
|
+ regs[i].uV = reg_res[i].uV;
|
|
|
+ regs[i].uA = reg_res[i].uA;
|
|
|
}
|
|
|
|
|
|
- regulator_set_load(qproc->supply[Q6V5_SUPPLY_CX].consumer, 100000);
|
|
|
- regulator_set_load(qproc->supply[Q6V5_SUPPLY_MSS].consumer, 100000);
|
|
|
- regulator_set_load(qproc->supply[Q6V5_SUPPLY_PLL].consumer, 10000);
|
|
|
+ return i;
|
|
|
+}
|
|
|
+
|
|
|
+static int q6v5_regulator_enable(struct q6v5 *qproc,
|
|
|
+ struct reg_info *regs, int count)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < count; i++) {
|
|
|
+ if (regs[i].uV > 0) {
|
|
|
+ ret = regulator_set_voltage(regs[i].reg,
|
|
|
+ regs[i].uV, INT_MAX);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(qproc->dev,
|
|
|
+ "Failed to request voltage for %d.\n",
|
|
|
+ i);
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (regs[i].uA > 0) {
|
|
|
+ ret = regulator_set_load(regs[i].reg,
|
|
|
+ regs[i].uA);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(qproc->dev,
|
|
|
+ "Failed to set regulator mode\n");
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = regulator_enable(regs[i].reg);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(qproc->dev, "Regulator enable failed\n");
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
+err:
|
|
|
+ for (; i >= 0; i--) {
|
|
|
+ if (regs[i].uV > 0)
|
|
|
+ regulator_set_voltage(regs[i].reg, 0, INT_MAX);
|
|
|
+
|
|
|
+ if (regs[i].uA > 0)
|
|
|
+ regulator_set_load(regs[i].reg, 0);
|
|
|
+
|
|
|
+ regulator_disable(regs[i].reg);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
-static int q6v5_regulator_enable(struct q6v5 *qproc)
|
|
|
+static void q6v5_regulator_disable(struct q6v5 *qproc,
|
|
|
+ struct reg_info *regs, int count)
|
|
|
{
|
|
|
- struct regulator *mss = qproc->supply[Q6V5_SUPPLY_MSS].consumer;
|
|
|
- struct regulator *mx = qproc->supply[Q6V5_SUPPLY_MX].consumer;
|
|
|
- int ret;
|
|
|
+ int i;
|
|
|
|
|
|
- /* TODO: Q6V5_SUPPLY_CX is supposed to be set to super-turbo here */
|
|
|
+ for (i = 0; i < count; i++) {
|
|
|
+ if (regs[i].uV > 0)
|
|
|
+ regulator_set_voltage(regs[i].reg, 0, INT_MAX);
|
|
|
|
|
|
- ret = regulator_set_voltage(mx, 1050000, INT_MAX);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
+ if (regs[i].uA > 0)
|
|
|
+ regulator_set_load(regs[i].reg, 0);
|
|
|
+
|
|
|
+ regulator_disable(regs[i].reg);
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- regulator_set_voltage(mss, 1000000, 1150000);
|
|
|
+static int q6v5_clk_enable(struct device *dev,
|
|
|
+ struct clk **clks, int count)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+ int i;
|
|
|
|
|
|
- return regulator_bulk_enable(ARRAY_SIZE(qproc->supply), qproc->supply);
|
|
|
+ for (i = 0; i < count; i++) {
|
|
|
+ rc = clk_prepare_enable(clks[i]);
|
|
|
+ if (rc) {
|
|
|
+ dev_err(dev, "Clock enable failed\n");
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+err:
|
|
|
+ for (i--; i >= 0; i--)
|
|
|
+ clk_disable_unprepare(clks[i]);
|
|
|
+
|
|
|
+ return rc;
|
|
|
}
|
|
|
|
|
|
-static void q6v5_regulator_disable(struct q6v5 *qproc)
|
|
|
+static void q6v5_clk_disable(struct device *dev,
|
|
|
+ struct clk **clks, int count)
|
|
|
{
|
|
|
- struct regulator *mss = qproc->supply[Q6V5_SUPPLY_MSS].consumer;
|
|
|
- struct regulator *mx = qproc->supply[Q6V5_SUPPLY_MX].consumer;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < count; i++)
|
|
|
+ clk_disable_unprepare(clks[i]);
|
|
|
+}
|
|
|
|
|
|
- /* TODO: Q6V5_SUPPLY_CX corner votes should be released */
|
|
|
+static struct resource_table *q6v5_find_rsc_table(struct rproc *rproc,
|
|
|
+ const struct firmware *fw,
|
|
|
+ int *tablesz)
|
|
|
+{
|
|
|
+ static struct resource_table table = { .ver = 1, };
|
|
|
|
|
|
- regulator_bulk_disable(ARRAY_SIZE(qproc->supply), qproc->supply);
|
|
|
- regulator_set_voltage(mx, 0, INT_MAX);
|
|
|
- regulator_set_voltage(mss, 0, 1150000);
|
|
|
+ *tablesz = sizeof(table);
|
|
|
+ return &table;
|
|
|
}
|
|
|
|
|
|
static int q6v5_load(struct rproc *rproc, const struct firmware *fw)
|
|
@@ -199,7 +298,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,
|
|
|
};
|
|
|
|
|
@@ -376,45 +475,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];
|
|
|
+ ssize_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_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;
|
|
|
|
|
|
- if (!phdr->p_memsz)
|
|
|
+ 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);
|
|
|
}
|
|
@@ -429,44 +592,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);
|
|
|
|
|
@@ -478,29 +603,38 @@ static int q6v5_start(struct rproc *rproc)
|
|
|
struct q6v5 *qproc = (struct q6v5 *)rproc->priv;
|
|
|
int ret;
|
|
|
|
|
|
- ret = q6v5_regulator_enable(qproc);
|
|
|
+ ret = q6v5_regulator_enable(qproc, qproc->proxy_regs,
|
|
|
+ qproc->proxy_reg_count);
|
|
|
if (ret) {
|
|
|
- dev_err(qproc->dev, "failed to enable supplies\n");
|
|
|
+ dev_err(qproc->dev, "failed to enable proxy supplies\n");
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+ ret = q6v5_clk_enable(qproc->dev, qproc->proxy_clks,
|
|
|
+ qproc->proxy_clk_count);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(qproc->dev, "failed to enable proxy clocks\n");
|
|
|
+ goto disable_proxy_reg;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = q6v5_regulator_enable(qproc, qproc->active_regs,
|
|
|
+ qproc->active_reg_count);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(qproc->dev, "failed to enable supplies\n");
|
|
|
+ goto disable_proxy_clk;
|
|
|
+ }
|
|
|
ret = reset_control_deassert(qproc->mss_restart);
|
|
|
if (ret) {
|
|
|
dev_err(qproc->dev, "failed to deassert mss restart\n");
|
|
|
goto disable_vdd;
|
|
|
}
|
|
|
|
|
|
- ret = clk_prepare_enable(qproc->ahb_clk);
|
|
|
- if (ret)
|
|
|
+ ret = q6v5_clk_enable(qproc->dev, qproc->active_clks,
|
|
|
+ qproc->active_clk_count);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(qproc->dev, "failed to enable clocks\n");
|
|
|
goto assert_reset;
|
|
|
-
|
|
|
- ret = clk_prepare_enable(qproc->axi_clk);
|
|
|
- if (ret)
|
|
|
- goto disable_ahb_clk;
|
|
|
-
|
|
|
- ret = clk_prepare_enable(qproc->rom_clk);
|
|
|
- if (ret)
|
|
|
- goto disable_axi_clk;
|
|
|
+ }
|
|
|
|
|
|
writel(qproc->mba_phys, qproc->rmb_base + RMB_MBA_IMAGE_REG);
|
|
|
|
|
@@ -535,7 +669,10 @@ static int q6v5_start(struct rproc *rproc)
|
|
|
|
|
|
qproc->running = true;
|
|
|
|
|
|
- /* TODO: All done, release the handover resources */
|
|
|
+ q6v5_clk_disable(qproc->dev, qproc->proxy_clks,
|
|
|
+ qproc->proxy_clk_count);
|
|
|
+ q6v5_regulator_disable(qproc, qproc->proxy_regs,
|
|
|
+ qproc->proxy_reg_count);
|
|
|
|
|
|
return 0;
|
|
|
|
|
@@ -543,16 +680,19 @@ halt_axi_ports:
|
|
|
q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6);
|
|
|
q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem);
|
|
|
q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc);
|
|
|
-
|
|
|
- clk_disable_unprepare(qproc->rom_clk);
|
|
|
-disable_axi_clk:
|
|
|
- clk_disable_unprepare(qproc->axi_clk);
|
|
|
-disable_ahb_clk:
|
|
|
- clk_disable_unprepare(qproc->ahb_clk);
|
|
|
+ q6v5_clk_disable(qproc->dev, qproc->active_clks,
|
|
|
+ qproc->active_clk_count);
|
|
|
assert_reset:
|
|
|
reset_control_assert(qproc->mss_restart);
|
|
|
disable_vdd:
|
|
|
- q6v5_regulator_disable(qproc);
|
|
|
+ q6v5_regulator_disable(qproc, qproc->active_regs,
|
|
|
+ qproc->active_reg_count);
|
|
|
+disable_proxy_clk:
|
|
|
+ q6v5_clk_disable(qproc->dev, qproc->proxy_clks,
|
|
|
+ qproc->proxy_clk_count);
|
|
|
+disable_proxy_reg:
|
|
|
+ q6v5_regulator_disable(qproc, qproc->proxy_regs,
|
|
|
+ qproc->proxy_reg_count);
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
@@ -579,10 +719,10 @@ static int q6v5_stop(struct rproc *rproc)
|
|
|
q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc);
|
|
|
|
|
|
reset_control_assert(qproc->mss_restart);
|
|
|
- clk_disable_unprepare(qproc->rom_clk);
|
|
|
- clk_disable_unprepare(qproc->axi_clk);
|
|
|
- clk_disable_unprepare(qproc->ahb_clk);
|
|
|
- q6v5_regulator_disable(qproc);
|
|
|
+ q6v5_clk_disable(qproc->dev, qproc->active_clks,
|
|
|
+ qproc->active_clk_count);
|
|
|
+ q6v5_regulator_disable(qproc, qproc->active_regs,
|
|
|
+ qproc->active_reg_count);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -702,27 +842,27 @@ static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int q6v5_init_clocks(struct q6v5 *qproc)
|
|
|
+static int q6v5_init_clocks(struct device *dev, struct clk **clks,
|
|
|
+ char **clk_names)
|
|
|
{
|
|
|
- qproc->ahb_clk = devm_clk_get(qproc->dev, "iface");
|
|
|
- if (IS_ERR(qproc->ahb_clk)) {
|
|
|
- dev_err(qproc->dev, "failed to get iface clock\n");
|
|
|
- return PTR_ERR(qproc->ahb_clk);
|
|
|
- }
|
|
|
+ int i;
|
|
|
|
|
|
- qproc->axi_clk = devm_clk_get(qproc->dev, "bus");
|
|
|
- if (IS_ERR(qproc->axi_clk)) {
|
|
|
- dev_err(qproc->dev, "failed to get bus clock\n");
|
|
|
- return PTR_ERR(qproc->axi_clk);
|
|
|
- }
|
|
|
+ if (!clk_names)
|
|
|
+ return 0;
|
|
|
|
|
|
- qproc->rom_clk = devm_clk_get(qproc->dev, "mem");
|
|
|
- if (IS_ERR(qproc->rom_clk)) {
|
|
|
- dev_err(qproc->dev, "failed to get mem clock\n");
|
|
|
- return PTR_ERR(qproc->rom_clk);
|
|
|
+ for (i = 0; clk_names[i]; i++) {
|
|
|
+ clks[i] = devm_clk_get(dev, clk_names[i]);
|
|
|
+ if (IS_ERR(clks[i])) {
|
|
|
+ int rc = PTR_ERR(clks[i]);
|
|
|
+
|
|
|
+ if (rc != -EPROBE_DEFER)
|
|
|
+ dev_err(dev, "Failed to get %s clock\n",
|
|
|
+ clk_names[i]);
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- return 0;
|
|
|
+ return i;
|
|
|
}
|
|
|
|
|
|
static int q6v5_init_reset(struct q6v5 *qproc)
|
|
@@ -805,12 +945,17 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc)
|
|
|
|
|
|
static int q6v5_probe(struct platform_device *pdev)
|
|
|
{
|
|
|
+ const struct rproc_hexagon_res *desc;
|
|
|
struct q6v5 *qproc;
|
|
|
struct rproc *rproc;
|
|
|
int ret;
|
|
|
|
|
|
+ desc = of_device_get_match_data(&pdev->dev);
|
|
|
+ if (!desc)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_ops,
|
|
|
- MBA_FIRMWARE_NAME, sizeof(*qproc));
|
|
|
+ desc->hexagon_mba_image, sizeof(*qproc));
|
|
|
if (!rproc) {
|
|
|
dev_err(&pdev->dev, "failed to allocate rproc\n");
|
|
|
return -ENOMEM;
|
|
@@ -834,13 +979,37 @@ static int q6v5_probe(struct platform_device *pdev)
|
|
|
if (ret)
|
|
|
goto free_rproc;
|
|
|
|
|
|
- ret = q6v5_init_clocks(qproc);
|
|
|
- if (ret)
|
|
|
+ ret = q6v5_init_clocks(&pdev->dev, qproc->proxy_clks,
|
|
|
+ desc->proxy_clk_names);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(&pdev->dev, "Failed to get proxy clocks.\n");
|
|
|
goto free_rproc;
|
|
|
+ }
|
|
|
+ qproc->proxy_clk_count = ret;
|
|
|
|
|
|
- ret = q6v5_regulator_init(qproc);
|
|
|
- if (ret)
|
|
|
+ ret = q6v5_init_clocks(&pdev->dev, qproc->active_clks,
|
|
|
+ desc->active_clk_names);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(&pdev->dev, "Failed to get active clocks.\n");
|
|
|
+ goto free_rproc;
|
|
|
+ }
|
|
|
+ qproc->active_clk_count = ret;
|
|
|
+
|
|
|
+ ret = q6v5_regulator_init(&pdev->dev, qproc->proxy_regs,
|
|
|
+ desc->proxy_supply);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(&pdev->dev, "Failed to get proxy regulators.\n");
|
|
|
+ goto free_rproc;
|
|
|
+ }
|
|
|
+ qproc->proxy_reg_count = ret;
|
|
|
+
|
|
|
+ ret = q6v5_regulator_init(&pdev->dev, qproc->active_regs,
|
|
|
+ desc->active_supply);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(&pdev->dev, "Failed to get active regulators.\n");
|
|
|
goto free_rproc;
|
|
|
+ }
|
|
|
+ qproc->active_reg_count = ret;
|
|
|
|
|
|
ret = q6v5_init_reset(qproc);
|
|
|
if (ret)
|
|
@@ -868,6 +1037,8 @@ static int q6v5_probe(struct platform_device *pdev)
|
|
|
goto free_rproc;
|
|
|
}
|
|
|
|
|
|
+ qcom_add_smd_subdev(rproc, &qproc->smd_subdev);
|
|
|
+
|
|
|
ret = rproc_add(rproc);
|
|
|
if (ret)
|
|
|
goto free_rproc;
|
|
@@ -885,13 +1056,83 @@ static int q6v5_remove(struct platform_device *pdev)
|
|
|
struct q6v5 *qproc = platform_get_drvdata(pdev);
|
|
|
|
|
|
rproc_del(qproc->rproc);
|
|
|
+
|
|
|
+ qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev);
|
|
|
rproc_free(qproc->rproc);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static const struct rproc_hexagon_res msm8916_mss = {
|
|
|
+ .hexagon_mba_image = "mba.mbn",
|
|
|
+ .proxy_supply = (struct qcom_mss_reg_res[]) {
|
|
|
+ {
|
|
|
+ .supply = "mx",
|
|
|
+ .uV = 1050000,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .supply = "cx",
|
|
|
+ .uA = 100000,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .supply = "pll",
|
|
|
+ .uA = 100000,
|
|
|
+ },
|
|
|
+ {}
|
|
|
+ },
|
|
|
+ .proxy_clk_names = (char*[]){
|
|
|
+ "xo",
|
|
|
+ NULL
|
|
|
+ },
|
|
|
+ .active_clk_names = (char*[]){
|
|
|
+ "iface",
|
|
|
+ "bus",
|
|
|
+ "mem",
|
|
|
+ NULL
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+static const struct rproc_hexagon_res msm8974_mss = {
|
|
|
+ .hexagon_mba_image = "mba.b00",
|
|
|
+ .proxy_supply = (struct qcom_mss_reg_res[]) {
|
|
|
+ {
|
|
|
+ .supply = "mx",
|
|
|
+ .uV = 1050000,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .supply = "cx",
|
|
|
+ .uA = 100000,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .supply = "pll",
|
|
|
+ .uA = 100000,
|
|
|
+ },
|
|
|
+ {}
|
|
|
+ },
|
|
|
+ .active_supply = (struct qcom_mss_reg_res[]) {
|
|
|
+ {
|
|
|
+ .supply = "mss",
|
|
|
+ .uV = 1050000,
|
|
|
+ .uA = 100000,
|
|
|
+ },
|
|
|
+ {}
|
|
|
+ },
|
|
|
+ .proxy_clk_names = (char*[]){
|
|
|
+ "xo",
|
|
|
+ NULL
|
|
|
+ },
|
|
|
+ .active_clk_names = (char*[]){
|
|
|
+ "iface",
|
|
|
+ "bus",
|
|
|
+ "mem",
|
|
|
+ NULL
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
static const struct of_device_id q6v5_of_match[] = {
|
|
|
- { .compatible = "qcom,q6v5-pil", },
|
|
|
+ { .compatible = "qcom,q6v5-pil", .data = &msm8916_mss},
|
|
|
+ { .compatible = "qcom,msm8916-mss-pil", .data = &msm8916_mss},
|
|
|
+ { .compatible = "qcom,msm8974-mss-pil", .data = &msm8974_mss},
|
|
|
{ },
|
|
|
};
|
|
|
MODULE_DEVICE_TABLE(of, q6v5_of_match);
|