Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 29898305 authored by Elliot Berman's avatar Elliot Berman
Browse files

firmware: qcom_scm: Merge legacy and SMCCC conventions



Copy/paste legacy SCM driver into qcom_scm-64 with the following notes:
- Renamed qcom_scm_call in qcom_scm-32 to qcom_scm_call_legacy in the
  copy.
- Renamed qcom_scm_call_atomic in qcom_scm 32 to
  qcom_scm_call_atomic_legacy in the copy.
- Rename __qcom_scm_call_smccc to qcom_scm_call_smccc.
- Filled in implementations set_cold_boot_addr, set_warm_boot_addr, and
  cpu_power_down from qcom_scm-32.c.
- set_dload_mode, io_writel, io_readl now use atomic variants as in
  qcom_scm-32.

Change-Id: Ifa72ded7d6150bef08c6a0451d422b0782e8320a
Signed-off-by: default avatarElliot Berman <eberman@codeaurora.org>
parent 3919d02c
Loading
Loading
Loading
Loading
+315 −8
Original line number Original line Diff line number Diff line
@@ -71,6 +71,83 @@ static DEFINE_MUTEX(qcom_scm_lock);
#define SMCCC_FIRST_REG_IDX	2
#define SMCCC_FIRST_REG_IDX	2
#define SMCCC_LAST_REG_IDX	(SMCCC_FIRST_REG_IDX + SMCCC_N_REG_ARGS - 1)
#define SMCCC_LAST_REG_IDX	(SMCCC_FIRST_REG_IDX + SMCCC_N_REG_ARGS - 1)


#define LEGACY_FUNCNUM(s, c)  (((s) << 10) | ((c) & 0x3ff))

/**
 * struct legacy_command - one SCM command buffer
 * @len: total available memory for command and response
 * @buf_offset: start of command buffer
 * @resp_hdr_offset: start of response buffer
 * @id: command to be executed
 * @buf: buffer returned from legacy_get_command_buffer()
 *
 * An SCM command is laid out in memory as follows:
 *
 *	------------------- <--- struct legacy_command
 *	| command header  |
 *	------------------- <--- legacy_get_command_buffer()
 *	| command buffer  |
 *	------------------- <--- struct legacy_response and
 *	| response header |      legacy_command_to_response()
 *	------------------- <--- legacy_get_response_buffer()
 *	| response buffer |
 *	-------------------
 *
 * There can be arbitrary padding between the headers and buffers so
 * you should always use the appropriate qcom_scm_get_*_buffer() routines
 * to access the buffers in a safe manner.
 */
struct legacy_command {
	__le32 len;
	__le32 buf_offset;
	__le32 resp_hdr_offset;
	__le32 id;
	__le32 buf[0];
};

/**
 * struct legacy_response - one SCM response buffer
 * @len: total available memory for response
 * @buf_offset: start of response data relative to start of legacy_response
 * @is_complete: indicates if the command has finished processing
 */
struct legacy_response {
	__le32 len;
	__le32 buf_offset;
	__le32 is_complete;
};

#define LEGACY_ATOMIC_N_REG_ARGS	5
#define LEGACY_ATOMIC_FIRST_REG_IDX	2
#define LEGACY_CLASS_REGISTER	(0x2 << 8)
#define LEGACY_MASK_IRQS		BIT(5)
#define LEGACY_ATOMIC(svc, cmd, n) ((LEGACY_FUNCNUM(svc, cmd) << 12) | \
				    LEGACY_CLASS_REGISTER | \
				    LEGACY_MASK_IRQS | \
				    (n & 0xf))

#define QCOM_SCM_FLAG_COLDBOOT_CPU0	0x00
#define QCOM_SCM_FLAG_COLDBOOT_CPU1	0x01
#define QCOM_SCM_FLAG_COLDBOOT_CPU2	0x08
#define QCOM_SCM_FLAG_COLDBOOT_CPU3	0x20

#define QCOM_SCM_FLAG_WARMBOOT_CPU0	0x04
#define QCOM_SCM_FLAG_WARMBOOT_CPU1	0x02
#define QCOM_SCM_FLAG_WARMBOOT_CPU2	0x10
#define QCOM_SCM_FLAG_WARMBOOT_CPU3	0x40

struct qcom_scm_entry {
	int flag;
	void *entry;
};

static struct qcom_scm_entry qcom_scm_wb[] = {
	{ .flag = QCOM_SCM_FLAG_WARMBOOT_CPU0 },
	{ .flag = QCOM_SCM_FLAG_WARMBOOT_CPU1 },
	{ .flag = QCOM_SCM_FLAG_WARMBOOT_CPU2 },
	{ .flag = QCOM_SCM_FLAG_WARMBOOT_CPU3 },
};

static void __qcom_scm_call_do_quirk(const struct arm_smccc_args *smc,
static void __qcom_scm_call_do_quirk(const struct arm_smccc_args *smc,
				     struct arm_smccc_res *res)
				     struct arm_smccc_res *res)
{
{
@@ -90,7 +167,7 @@ static void __qcom_scm_call_do_quirk(const struct arm_smccc_args *smc,
	} while (res->a0 == QCOM_SCM_INTERRUPTED);
	} while (res->a0 == QCOM_SCM_INTERRUPTED);
}
}


static int ___qcom_scm_call_smccc(struct device *dev,
static int qcom_scm_call_smccc(struct device *dev,
				  struct qcom_scm_desc *desc, bool atomic)
				  struct qcom_scm_desc *desc, bool atomic)
{
{
	int arglen = desc->arginfo & 0xf;
	int arglen = desc->arginfo & 0xf;
@@ -176,6 +253,169 @@ static int ___qcom_scm_call_smccc(struct device *dev,
	return res.a0 ? qcom_scm_remap_error(res.a0) : 0;
	return res.a0 ? qcom_scm_remap_error(res.a0) : 0;
}
}


/**
 * legacy_command_to_response() - Get a pointer to a legacy_response
 * @cmd: command
 *
 * Returns a pointer to a response for a command.
 */
static inline struct legacy_response *legacy_command_to_response(
		const struct legacy_command *cmd)
{
	return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset);
}

/**
 * legacy_get_command_buffer() - Get a pointer to a command buffer
 * @cmd: command
 *
 * Returns a pointer to the command buffer of a command.
 */
static inline void *legacy_get_command_buffer(const struct legacy_command *cmd)
{
	return (void *)cmd->buf;
}

/**
 * legacy_get_response_buffer() - Get a pointer to a response buffer
 * @rsp: response
 *
 * Returns a pointer to a response buffer of a response.
 */
static inline void *legacy_get_response_buffer(
		const struct legacy_response *rsp)
{
	return (void *)rsp + le32_to_cpu(rsp->buf_offset);
}

static void __qcom_scm_call_do(const struct arm_smccc_args *smc,
			      struct arm_smccc_res *res)
{
	do {
		arm_smccc_smc(smc->a[0], smc->a[1], smc->a[2], smc->a[3],
			      smc->a[4], smc->a[5], smc->a[6], smc->a[7], res);
	} while (res->a0 == QCOM_SCM_INTERRUPTED);
}

/**
 * qcom_scm_call_legacy() - Send an SCM command
 * @dev: struct device
 * @svc_id: service identifier
 * @cmd_id: command identifier
 * @cmd_buf: command buffer
 * @cmd_len: length of the command buffer
 * @resp_buf: response buffer
 * @resp_len: length of the response buffer
 *
 * Sends a command to the SCM and waits for the command to finish processing.
 *
 * A note on cache maintenance:
 * Note that any buffers that are expected to be accessed by the secure world
 * must be flushed before invoking qcom_scm_call and invalidated in the cache
 * immediately after qcom_scm_call returns. Cache maintenance on the command
 * and response buffers is taken care of by qcom_scm_call; however, callers are
 * responsible for any other cached buffers passed over to the secure world.
 */
static int qcom_scm_call_legacy(struct device *dev, struct qcom_scm_desc *desc)
{
	int arglen = desc->arginfo & 0xf;
	int ret = 0, context_id;
	size_t i;
	struct legacy_command *cmd;
	struct legacy_response *rsp;
	struct arm_smccc_args smc = {{0}};
	struct arm_smccc_res res;
	const size_t cmd_len = arglen * sizeof(__le32);
	const size_t resp_len = MAX_QCOM_SCM_RETS * sizeof(__le32);
	size_t alloc_len = sizeof(*cmd) + cmd_len + sizeof(*rsp) + resp_len;
	dma_addr_t cmd_phys;
	__le32 *arg_buf;
	__le32 *res_buf;

	cmd = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL);
	if (!cmd)
		return -ENOMEM;

	cmd->len = cpu_to_le32(alloc_len);
	cmd->buf_offset = cpu_to_le32(sizeof(*cmd));
	cmd->resp_hdr_offset = cpu_to_le32(sizeof(*cmd) + cmd_len);
	cmd->id = cpu_to_le32(LEGACY_FUNCNUM(desc->svc, desc->cmd));

	arg_buf = legacy_get_command_buffer(cmd);
	for (i = 0; i < arglen; i++)
		arg_buf[i] = cpu_to_le32(desc->args[i]);

	rsp = legacy_command_to_response(cmd);

	cmd_phys = dma_map_single(dev, cmd, alloc_len, DMA_TO_DEVICE);
	if (dma_mapping_error(dev, cmd_phys)) {
		kfree(cmd);
		return -ENOMEM;
	}

	smc.a[0] = 1;
	smc.a[1] = (unsigned long)&context_id;
	smc.a[2] = cmd_phys;

	mutex_lock(&qcom_scm_lock);
	__qcom_scm_call_do(&smc, &res);
	if (res.a0 < 0)
		ret = qcom_scm_remap_error(res.a0);
	mutex_unlock(&qcom_scm_lock);
	if (ret)
		goto out;

	do {
		dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len,
					sizeof(*rsp), DMA_FROM_DEVICE);
	} while (!rsp->is_complete);

	dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len +
				le32_to_cpu(rsp->buf_offset),
				resp_len, DMA_FROM_DEVICE);

	res_buf = legacy_get_response_buffer(rsp);
	for (i = 0; i < MAX_QCOM_SCM_RETS; i++)
		desc->res[i] = le32_to_cpu(res_buf[i]);
out:
	dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE);
	kfree(cmd);
	return ret;
}

/**
 * qcom_scm_call_atomic_legacy() - Send an atomic SCM command with up to
 * 5 arguments and 3 return values
 *
 * This shall only be used with commands that are guaranteed to be
 * uninterruptable, atomic and SMP safe.
 */
static int qcom_scm_call_atomic_legacy(struct device *dev,
				       struct qcom_scm_desc *desc)
{
	int context_id;
	struct arm_smccc_args smc = {{0}};
	struct arm_smccc_res res;
	size_t i, arglen = desc->arginfo & 0xf;

	BUG_ON(arglen > LEGACY_ATOMIC_N_REG_ARGS);

	smc.a[0] = LEGACY_ATOMIC(desc->svc, desc->cmd, arglen);
	smc.a[1] = (unsigned long)&context_id;

	for (i = 0; i < arglen; i++)
		smc.a[i + LEGACY_ATOMIC_FIRST_REG_IDX] = desc->args[i];

	arm_smccc_smc(smc.a[0], smc.a[1], smc.a[2], smc.a[3],
		      smc.a[4], smc.a[5], smc.a[6], smc.a[7], &res);

	desc->res[0] = res.a1;
	desc->res[1] = res.a2;
	desc->res[2] = res.a3;

	return res.a0;
}

/**
/**
 * qcom_scm_call() - Invoke a syscall in the secure world
 * qcom_scm_call() - Invoke a syscall in the secure world
 * @dev:	device
 * @dev:	device
@@ -189,7 +429,7 @@ static int ___qcom_scm_call_smccc(struct device *dev,
static int qcom_scm_call(struct device *dev, struct qcom_scm_desc *desc)
static int qcom_scm_call(struct device *dev, struct qcom_scm_desc *desc)
{
{
	might_sleep();
	might_sleep();
	return ___qcom_scm_call_smccc(dev, desc, false);
	return qcom_scm_call_smccc(dev, desc, false);
}
}


/**
/**
@@ -205,7 +445,7 @@ static int qcom_scm_call(struct device *dev, struct qcom_scm_desc *desc)
 */
 */
static int qcom_scm_call_atomic(struct device *dev, struct qcom_scm_desc *desc)
static int qcom_scm_call_atomic(struct device *dev, struct qcom_scm_desc *desc)
{
{
	return ___qcom_scm_call_smccc(dev, desc, true);
	return qcom_scm_call_smccc(dev, desc, true);
}
}


/**
/**
@@ -219,7 +459,35 @@ static int qcom_scm_call_atomic(struct device *dev, struct qcom_scm_desc *desc)
int __qcom_scm_set_cold_boot_addr(struct device *dev, void *entry,
int __qcom_scm_set_cold_boot_addr(struct device *dev, void *entry,
				  const cpumask_t *cpus)
				  const cpumask_t *cpus)
{
{
	return -ENOTSUPP;
	int flags = 0;
	int cpu;
	int scm_cb_flags[] = {
		QCOM_SCM_FLAG_COLDBOOT_CPU0,
		QCOM_SCM_FLAG_COLDBOOT_CPU1,
		QCOM_SCM_FLAG_COLDBOOT_CPU2,
		QCOM_SCM_FLAG_COLDBOOT_CPU3,
	};
	struct qcom_scm_desc desc = {
		.svc = QCOM_SCM_SVC_BOOT,
		.cmd = QCOM_SCM_BOOT_SET_ADDR,
		.owner = ARM_SMCCC_OWNER_SIP,
	};

	if (!cpus || (cpus && cpumask_empty(cpus)))
		return -EINVAL;

	for_each_cpu(cpu, cpus) {
		if (cpu < ARRAY_SIZE(scm_cb_flags))
			flags |= scm_cb_flags[cpu];
		else
			set_cpu_present(cpu, false);
	}

	desc.args[0] = flags;
	desc.args[1] = virt_to_phys(entry);
	desc.arginfo = QCOM_SCM_ARGS(2);

	return qcom_scm_call_atomic(dev, &desc);
}
}


/**
/**
@@ -234,7 +502,37 @@ int __qcom_scm_set_cold_boot_addr(struct device *dev, void *entry,
int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry,
int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry,
				  const cpumask_t *cpus)
				  const cpumask_t *cpus)
{
{
	return -ENOTSUPP;
	int ret;
	int flags = 0;
	int cpu;
	struct qcom_scm_desc desc = {
		.svc = QCOM_SCM_SVC_BOOT,
		.cmd = QCOM_SCM_BOOT_SET_ADDR,
	};

	/*
	 * Reassign only if we are switching from hotplug entry point
	 * to cpuidle entry point or vice versa.
	 */
	for_each_cpu(cpu, cpus) {
		if (entry == qcom_scm_wb[cpu].entry)
			continue;
		flags |= qcom_scm_wb[cpu].flag;
	}

	/* No change in entry function */
	if (!flags)
		return 0;

	desc.args[0] = virt_to_phys(entry);
	desc.args[1] = flags;
	ret = qcom_scm_call(dev, &desc);
	if (!ret) {
		for_each_cpu(cpu, cpus)
			qcom_scm_wb[cpu].entry = entry;
	}

	return ret;
}
}


/**
/**
@@ -247,6 +545,15 @@ int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry,
 */
 */
void __qcom_scm_cpu_power_down(struct device *dev, u32 flags)
void __qcom_scm_cpu_power_down(struct device *dev, u32 flags)
{
{
	struct qcom_scm_desc desc = {
		.svc = QCOM_SCM_SVC_BOOT,
		.cmd = QCOM_SCM_BOOT_TERMINATE_PC,
		.args[0] = flags & QCOM_SCM_FLUSH_FLAG_MASK,
		.arginfo = QCOM_SCM_ARGS(1),
		.owner = ARM_SMCCC_OWNER_SIP,
	};

	qcom_scm_call_atomic(dev, &desc);
}
}


int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id)
int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id)
@@ -279,7 +586,7 @@ int __qcom_scm_set_dload_mode(struct device *dev, bool enable)
	desc.args[1] = enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0;
	desc.args[1] = enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0;
	desc.arginfo = QCOM_SCM_ARGS(2);
	desc.arginfo = QCOM_SCM_ARGS(2);


	return qcom_scm_call(dev, &desc);
	return qcom_scm_call_atomic(dev, &desc);
}
}


bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral)
bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral)
@@ -403,7 +710,7 @@ int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr,
	desc.args[0] = addr;
	desc.args[0] = addr;
	desc.arginfo = QCOM_SCM_ARGS(1);
	desc.arginfo = QCOM_SCM_ARGS(1);


	ret = qcom_scm_call(dev, &desc);
	ret = qcom_scm_call_atomic(dev, &desc);
	if (ret >= 0)
	if (ret >= 0)
		*val = desc.res[0];
		*val = desc.res[0];


@@ -422,7 +729,7 @@ int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val)
	desc.args[1] = val;
	desc.args[1] = val;
	desc.arginfo = QCOM_SCM_ARGS(2);
	desc.arginfo = QCOM_SCM_ARGS(2);


	return qcom_scm_call(dev, &desc);
	return qcom_scm_call_atomic(dev, &desc);
}
}


int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id)
int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id)