* The driver uses the Linux GPIO and interrupt framework to expose a virtual
* GPIO for each outbound entry and a virtual interrupt controller for each
* inbound entry.
+ *
+ * V2 of SMP2P allows remote processors to write to outbound smp2p items before
+ * the full smp2p connection is negotiated. This is important for processors
+ * started before linux runs.
*/
#define SMP2P_MAX_ENTRY 16
#define SMP2P_MAGIC 0x504d5324
#define SMP2P_ALL_FEATURES SMP2P_FEATURE_SSR_ACK
+#define MAX_VERSION 2
/**
* struct smp2p_smem_item - in memory communication structure
* @magic: magic number
- * @version: version - must be 1
+ * @version: version
* @features: features flag - currently unused
* @local_pid: processor id of sending end
* @remote_pid: processor id of receiving end
static bool qcom_smp2p_check_ssr(struct qcom_smp2p *smp2p)
{
struct smp2p_smem_item *in = smp2p->in;
+ struct smp2p_entry *entry;
+ bool restart_done;
bool restart;
if (!smp2p->ssr_ack_enabled)
return false;
- restart = in->flags & BIT(SMP2P_FLAGS_RESTART_DONE_BIT);
+ restart_done = in->flags & BIT(SMP2P_FLAGS_RESTART_DONE_BIT);
+ restart = restart_done != smp2p->ssr_ack;
+ list_for_each_entry(entry, &smp2p->inbound, node) {
+ if (!entry->value)
+ continue;
+ entry->last_value = 0;
+ }
- return restart != smp2p->ssr_ack;
+ return restart;
}
static void qcom_smp2p_do_ssr_ack(struct qcom_smp2p *smp2p)
smp2p->negotiation_done = true;
trace_smp2p_negotiate(smp2p->dev, out->features);
+ } else if (in->version && in->version < out->version) {
+ out->version = in->version;
+ qcom_smp2p_kick(smp2p);
}
}
+static int qcom_smp2p_in_version(struct qcom_smp2p *smp2p)
+{
+ unsigned int smem_id = smp2p->smem_items[SMP2P_INBOUND];
+ unsigned int pid = smp2p->remote_pid;
+ struct smp2p_smem_item *in;
+ size_t size;
+
+ in = qcom_smem_get(pid, smem_id, &size);
+ if (IS_ERR(in))
+ return 0;
+
+ return in->version;
+}
+
static void qcom_smp2p_start_in(struct qcom_smp2p *smp2p)
{
unsigned int smem_id = smp2p->smem_items[SMP2P_INBOUND];
struct smp2p_smem_item *out;
unsigned smem_id = smp2p->smem_items[SMP2P_OUTBOUND];
unsigned pid = smp2p->remote_pid;
+ u8 in_version;
int ret;
ret = qcom_smem_alloc(pid, smem_id, sizeof(*out));
out->valid_entries = 0;
out->features = SMP2P_ALL_FEATURES;
+ in_version = qcom_smp2p_in_version(smp2p);
+ if (in_version > MAX_VERSION) {
+ dev_err(smp2p->dev, "Unsupported smp2p version %d\n", in_version);
+ return -EINVAL;
+ }
+
/*
* Make sure the rest of the header is written before we validate the
* item by writing a valid version number.
*/
wmb();
- out->version = 1;
+ if (in_version && in_version <= 2)
+ out->version = in_version;
+ else
+ out->version = 2;
qcom_smp2p_kick(smp2p);