/**
* qcom_scm_pas_supported() - Check if the peripheral authentication
service is
* available for the given peripherial
diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h
index e6e512bd57d1..99e3ab2f1986 100644
--- a/drivers/firmware/qcom_scm.h
+++ b/drivers/firmware/qcom_scm.h
@@ -132,6 +132,12 @@ extern int scm_legacy_call(struct device *dev,
const struct qcom_scm_desc *desc,
#define QCOM_SCM_SMMU_CONFIG_ERRATA1 0x03
#define QCOM_SCM_SMMU_CONFIG_ERRATA1_CLIENT_ALL 0x02
+#define QCOM_SCM_PD_LOAD_SVC_ID 0x2
+#define QCOM_SCM_PD_LOAD_CMD_ID 0x16
+#define QCOM_SCM_PD_LOAD_V2_CMD_ID 0x19
+#define QCOM_SCM_INT_RAD_PWR_UP_CMD_ID 0x17
+#define QCOM_SCM_INT_RAD_PWR_DN_CMD_ID 0x18
+
#define QCOM_SCM_SVC_WAITQ 0x24
#define QCOM_SCM_WAITQ_RESUME 0x02
#define QCOM_SCM_WAITQ_GET_WQ_CTX 0x03
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index a850e9f486dd..44af5c36f67e 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -234,6 +234,26 @@ config QCOM_Q6V5_PAS
CDSP (Compute DSP), MPSS (Modem Peripheral SubSystem), and
SLPI (Sensor Low Power Island).
+config QCOM_Q6V5_MPD
+ tristate "Qualcomm Hexagon based MPD model Peripheral Image Loader"
+ depends on OF && ARCH_QCOM
+ depends on QCOM_SMEM
+ depends on RPMSG_QCOM_SMD || RPMSG_QCOM_SMD=n
+ depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n
+ depends on QCOM_SYSMON || QCOM_SYSMON=n
+ depends on RPMSG_QCOM_GLINK || RPMSG_QCOM_GLINK=n
+ depends on QCOM_AOSS_QMP || QCOM_AOSS_QMP=n
+ select MFD_SYSCON
+ select QCOM_MDT_LOADER
+ select QCOM_PIL_INFO
+ select QCOM_Q6V5_COMMON
+ select QCOM_RPROC_COMMON
+ select QCOM_SCM
+ help
+ Say y here to support the Qualcomm Secure Peripheral Image Loader
+ for the Hexagon based MultiPD model remote processors on e.g.
IPQ5018.
+ This is trustZone wireless subsystem.
+
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 91314a9b43ce..b64051080ec1 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_QCOM_PIL_INFO) += qcom_pil_info.o
obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o
obj-$(CONFIG_QCOM_Q6V5_COMMON) += qcom_q6v5.o
obj-$(CONFIG_QCOM_Q6V5_ADSP) += qcom_q6v5_adsp.o
+obj-$(CONFIG_QCOM_Q6V5_MPD) += qcom_q6v5_mpd.o
obj-$(CONFIG_QCOM_Q6V5_MSS) += qcom_q6v5_mss.o
obj-$(CONFIG_QCOM_Q6V5_PAS) += qcom_q6v5_pas.o
obj-$(CONFIG_QCOM_Q6V5_WCSS) += qcom_q6v5_wcss.o
diff --git a/drivers/remoteproc/qcom_common.c
b/drivers/remoteproc/qcom_common.c
index a0d4238492e9..b72fbda02242 100644
--- a/drivers/remoteproc/qcom_common.c
+++ b/drivers/remoteproc/qcom_common.c
@@ -510,5 +510,28 @@ void qcom_remove_ssr_subdev(struct rproc *rproc,
struct qcom_rproc_ssr *ssr)
}
EXPORT_SYMBOL_GPL(qcom_remove_ssr_subdev);
+/**
+ * qcom_get_pd_asid() - get the pd asid number from DT node
+ * @node: device tree node
+ *
+ * Returns asid if node name has 'pd' string
+ */
+s8 qcom_get_pd_asid(struct device_node *node)
+{
+ char *str;
+ u8 pd_asid;
+
+ if (!node)
+ return -EINVAL;
+
+ str = strstr(node->name, "pd");
+ if (!str)
+ return 0;
+
+ str += strlen("pd");
+ return kstrtos8(str, 10, &pd_asid) ? -EINVAL : pd_asid;
+}
+EXPORT_SYMBOL(qcom_get_pd_asid);
+
MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/remoteproc/qcom_common.h
b/drivers/remoteproc/qcom_common.h
index 9ef4449052a9..9f3fb11224aa 100644
--- a/drivers/remoteproc/qcom_common.h
+++ b/drivers/remoteproc/qcom_common.h
@@ -75,5 +75,6 @@ static inline bool qcom_sysmon_shutdown_acked(struct
qcom_sysmon *sysmon)
return false;
}
#endif
+s8 qcom_get_pd_asid(struct device_node *node);
#endif
diff --git a/drivers/remoteproc/qcom_q6v5.c
b/drivers/remoteproc/qcom_q6v5.c
index 192c7aa0e39e..b88bf3a8e53b 100644
--- a/drivers/remoteproc/qcom_q6v5.c
+++ b/drivers/remoteproc/qcom_q6v5.c
@@ -118,7 +118,7 @@ static irqreturn_t q6v5_wdog_interrupt(int irq,
void *data)
return IRQ_HANDLED;
}
-static irqreturn_t q6v5_fatal_interrupt(int irq, void *data)
+irqreturn_t q6v5_fatal_interrupt(int irq, void *data)
{
struct qcom_q6v5 *q6v5 = data;
size_t len;
@@ -139,7 +139,7 @@ static irqreturn_t q6v5_fatal_interrupt(int irq,
void *data)
return IRQ_HANDLED;
}
-static irqreturn_t q6v5_ready_interrupt(int irq, void *data)
+irqreturn_t q6v5_ready_interrupt(int irq, void *data)
{
struct qcom_q6v5 *q6v5 = data;
@@ -183,7 +183,16 @@ static irqreturn_t q6v5_handover_interrupt(int
irq, void *data)
return IRQ_HANDLED;
}
-static irqreturn_t q6v5_stop_interrupt(int irq, void *data)
+irqreturn_t q6v5_spawn_interrupt(int irq, void *data)
+{
+ struct qcom_q6v5 *q6v5 = data;
+
+ complete(&q6v5->spawn_done);
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t q6v5_stop_interrupt(int irq, void *data)
{
struct qcom_q6v5 *q6v5 = data;
@@ -220,6 +229,28 @@ int qcom_q6v5_request_stop(struct qcom_q6v5
*q6v5, struct qcom_sysmon *sysmon)
}
EXPORT_SYMBOL_GPL(qcom_q6v5_request_stop);
+/**
+ * qcom_q6v5_request_spawn() - request the remote processor to spawn
+ * @q6v5: reference to qcom_q6v5 context
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+int qcom_q6v5_request_spawn(struct qcom_q6v5 *q6v5)
+{
+ int ret;
+
+ ret = qcom_smem_state_update_bits(q6v5->spawn_state,
+ BIT(q6v5->spawn_bit), BIT(q6v5->spawn_bit));
+
+ ret = wait_for_completion_timeout(&q6v5->spawn_done, 5 * HZ);
+
+ qcom_smem_state_update_bits(q6v5->spawn_state,
+ BIT(q6v5->spawn_bit), 0);
+
+ return ret == 0 ? -ETIMEDOUT : 0;
+}
+EXPORT_SYMBOL_GPL(qcom_q6v5_request_spawn);
+
/**
* qcom_q6v5_panic() - panic handler to invoke a stop on the remote
* @q6v5: reference to qcom_q6v5 context
@@ -250,7 +281,8 @@ EXPORT_SYMBOL_GPL(qcom_q6v5_panic);
* Return: 0 on success, negative errno on failure
*/
int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device
*pdev,
- struct rproc *rproc, int crash_reason, const char
*load_state,
+ struct rproc *rproc, int remote_id, int crash_reason,
+ const char *load_state,
void (*handover)(struct qcom_q6v5 *q6v5))
{
int ret;
@@ -258,6 +290,7 @@ int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct
platform_device *pdev,
q6v5->rproc = rproc;
q6v5->dev = &pdev->dev;
q6v5->crash_reason = crash_reason;
+ q6v5->remote_id = remote_id;
q6v5->handover = handover;
init_completion(&q6v5->start_done);
diff --git a/drivers/remoteproc/qcom_q6v5.h
b/drivers/remoteproc/qcom_q6v5.h
index 5a859c41896e..d00568339d46 100644
--- a/drivers/remoteproc/qcom_q6v5.h
+++ b/drivers/remoteproc/qcom_q6v5.h
@@ -18,22 +18,29 @@ struct qcom_q6v5 {
struct qcom_smem_state *state;
struct qmp *qmp;
+ struct qcom_smem_state *shutdown_state;
+ struct qcom_smem_state *spawn_state;
struct icc_path *path;
unsigned stop_bit;
+ unsigned shutdown_bit;
+ unsigned spawn_bit;
int wdog_irq;
int fatal_irq;
int ready_irq;
int handover_irq;
int stop_irq;
+ int spawn_irq;
bool handover_issued;
struct completion start_done;
struct completion stop_done;
+ struct completion spawn_done;
+ int remote_id;
int crash_reason;
bool running;
@@ -43,14 +50,20 @@ struct qcom_q6v5 {
};
int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device
*pdev,
- struct rproc *rproc, int crash_reason, const char
*load_state,
+ struct rproc *rproc, int remote_id, int crash_reason,
+ const char *load_state,
void (*handover)(struct qcom_q6v5 *q6v5));
void qcom_q6v5_deinit(struct qcom_q6v5 *q6v5);
int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5);
int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5);
int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5, struct
qcom_sysmon *sysmon);
+int qcom_q6v5_request_spawn(struct qcom_q6v5 *q6v5);
int qcom_q6v5_wait_for_start(struct qcom_q6v5 *q6v5, int timeout);
unsigned long qcom_q6v5_panic(struct qcom_q6v5 *q6v5);
+irqreturn_t q6v5_fatal_interrupt(int irq, void *data);
+irqreturn_t q6v5_ready_interrupt(int irq, void *data);
+irqreturn_t q6v5_spawn_interrupt(int irq, void *data);
+irqreturn_t q6v5_stop_interrupt(int irq, void *data);