Files
android_kernel_samsung_sm8750/drivers/remoteproc/qcom_rproc_slate.c
2025-08-12 22:16:57 +02:00

1333 lines
34 KiB
C
Executable File

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/delay.h>
#include <linux/dma-buf.h>
#include <linux/dma-heap.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/highmem.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/qseecom.h>
#include <linux/qtee_shmbridge.h>
#include <linux/reboot.h>
#include <linux/slab.h>
#include <linux/soc/qcom/mdt_loader.h>
#include <linux/qseecom_kernel.h>
#include <linux/soc/qcom/slatecom_interface.h>
#include <linux/suspend.h>
#include "qcom_common.h"
#include "remoteproc_internal.h"
#define SECURE_APP "slateapp"
#define INVALID_GPIO -1
#define NUM_GPIOS 3
#define RESULT_SUCCESS 0
#define RESULT_FAILURE -1
/* Slate Ramdump Size 8MB + 3MB (ITCM) */
#define SLATE_RAMDUMP_SZ 0XB00000
#define SLATE_MINIDUMP_SZ (0x400*40)
#define SLATE_RAMDUMP 3
#define SLATE_MINIDUMP 4
#define SLATE_CRASH_IN_TWM -2
#define segment_is_hash(flag) (((flag) & (0x7 << 24)) == (0x2 << 24))
static struct workqueue_struct *slate_reset_wq;
/* tzapp command list.*/
enum slate_tz_commands {
SLATE_RPROC_RAMDUMP,
SLATE_RPROC_IMAGE_LOAD,
SLATE_RPROC_AUTH_MDT,
SLATE_RPROC_DLOAD_CONT,
SLATE_RPROC_GET_SLATE_VERSION,
SLATE_RPROC_SHUTDOWN,
SLATE_RPROC_DUMPINFO,
SLATE_RPROC_UP_INFO,
SLATE_RPROC_RESET,
};
/* tzapp bg request.*/
struct tzapp_slate_req {
uint64_t address_fw;
uint32_t size_fw;
uint8_t tzapp_slate_cmd;
} __packed;
/* tzapp bg response.*/
struct tzapp_slate_rsp {
uint32_t tzapp_slate_cmd;
uint32_t slate_info_len;
int32_t status;
uint32_t slate_info[100];
} __packed;
/**
* struct pil_mdt - Representation of <name>.mdt file in memory
* @hdr: ELF32 header
* @phdr: ELF32 program headers
*/
struct pil_mdt {
struct elf32_hdr hdr;
struct elf32_phdr phdr[];
};
/**
* struct qcom_slate
* @dev: Device pointer
* @rproc: Remoteproc handle for slate
* @firmware_name: FW image file name
* @ssr_subdev: SSR subdevice to be registered with remoteproc
* @ssr_name: SSR subdevice name used as reference in remoteproc
* @glink_subdev: GLINK subdevice to be registered with remoteproc
* @sysmon: sysmon subdevice to be registered with remoteproc
* @sysmon_name: sysmon subdevice name used as reference in remoteproc
* @ssctl_id: instance id of the ssctl QMI service
* @reboot_nb: notifier block to handle reboot scenarios
* @address_fw: address where firmware binaries loaded in DMA
* @size_fw: size of slate firmware binaries in DMA
* @qseecom_handle: handle of TZ app
* @cmd_status: qseecom command status
* @app_status: status of tz app loading
* @is_ready: Is slate chip up
* @err_ready: The error ready signal
* @shutdown_done: The shutdown done signal
* @region_start: DMA handle for loading FW
* @region_end: DMA address indicating end of DMA buffer
* @region: CPU address for DMA buffer
* @is_region_allocated: Is DMA buffer allocated
* @region_size: DMA buffer size for FW
* @gpios: arry to hold all gpio handler
* @status_irq: irq to indicate slate status
* @slate_queue: private queue to schedule worker thread for bottom half
* @restart_work: work struct for executing ssr
* @mem_phys: pa of DMA buffer for ramdump
* @mem_region: va of DMA buffer for ramdump
* @mem_size: DMA buffer size for ramdump
*/
struct qcom_slate {
struct device *dev;
struct rproc *rproc;
const char *firmware_name;
struct qcom_rproc_ssr ssr_subdev;
const char *ssr_name;
struct qcom_rproc_glink glink_subdev;
struct qcom_sysmon *sysmon;
const char *sysmon_name;
int ssctl_id;
struct notifier_block reboot_nb;
phys_addr_t address_fw;
size_t size_fw;
struct qseecom_handle *qseecom_handle;
u32 cmd_status;
int app_status;
bool is_ready;
struct completion err_ready;
struct completion shutdown_done;
phys_addr_t region_start;
phys_addr_t region_end;
void *region;
bool is_region_allocated;
size_t region_size;
unsigned int gpios[NUM_GPIOS];
int status_irq;
struct workqueue_struct *slate_queue;
struct work_struct restart_work;
phys_addr_t mem_phys;
void *mem_region;
size_t mem_size;
struct wakeup_source rproc_slate_ws;
};
static irqreturn_t slate_status_change(int irq, void *dev_id);
struct mutex cmdsync_lock;
static ssize_t txn_id_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = container_of(dev,
struct platform_device, dev);
struct qcom_slate *slate_data =
(struct qcom_slate *)platform_get_drvdata(pdev);
return sysfs_emit(buf, "%u\n",
qcom_sysmon_get_txn_id(slate_data->sysmon));
}
static DEVICE_ATTR_RO(txn_id);
/**
* get_cmd_rsp_buffers() - Function sets cmd & rsp buffer pointers and
* aligns buffer lengths
* @hdl: index of qseecom_handle
* @cmd: req buffer - set to qseecom_handle.sbuf
* @cmd_len: ptr to req buffer len
* @rsp: rsp buffer - set to qseecom_handle.sbuf + offset
* @rsp_len: ptr to rsp buffer len
*
* Return: Success always .
*/
static int get_cmd_rsp_buffers(struct qseecom_handle *handle, void **cmd,
uint32_t *cmd_len, void **rsp, uint32_t *rsp_len)
{
*cmd = handle->sbuf;
if (*cmd_len & QSEECOM_ALIGN_MASK)
*cmd_len = QSEECOM_ALIGN(*cmd_len);
*rsp = handle->sbuf + *cmd_len;
if (*rsp_len & QSEECOM_ALIGN_MASK)
*rsp_len = QSEECOM_ALIGN(*rsp_len);
return 0;
}
/**
* load_slate_tzapp() - Called to load TZ app.
* @pbd: struct containing private SLATE data.
*
* Return: 0 on success. Error code on failure.
*/
static int load_slate_tzapp(struct qcom_slate *pbd)
{
int rc;
/* return success if already loaded */
if (pbd->qseecom_handle && !pbd->app_status)
return 0;
/* Load the APP */
rc = qseecom_start_app(&pbd->qseecom_handle, SECURE_APP, SZ_4K);
if (rc < 0) {
dev_err(pbd->dev, "SLATE TZ app load failure\n");
pbd->app_status = RESULT_FAILURE;
return -EIO;
}
pbd->app_status = RESULT_SUCCESS;
return 0;
}
/**
* slate_tzapp_comm() - Function called to communicate with TZ APP.
* @pbd: struct containing private SLATE data.
* @req: struct containing command and parameters.
*
* Return: 0 on success. Error code on failure.
*/
static long slate_tzapp_comm(struct qcom_slate *pbd,
struct tzapp_slate_req *req)
{
struct tzapp_slate_req *slate_tz_req;
struct tzapp_slate_rsp *slate_tz_rsp;
int rc, req_len, rsp_len, i;
unsigned char *ascii;
/* Fill command structure */
req_len = sizeof(struct tzapp_slate_req);
rsp_len = sizeof(struct tzapp_slate_rsp);
mutex_lock(&cmdsync_lock);
rc = get_cmd_rsp_buffers(pbd->qseecom_handle,
(void **)&slate_tz_req, &req_len,
(void **)&slate_tz_rsp, &rsp_len);
if (rc)
goto end;
slate_tz_req->tzapp_slate_cmd = req->tzapp_slate_cmd;
slate_tz_req->address_fw = req->address_fw;
slate_tz_req->size_fw = req->size_fw;
rc = qseecom_send_command(pbd->qseecom_handle,
(void *)slate_tz_req, req_len, (void *)slate_tz_rsp, rsp_len);
mutex_unlock(&cmdsync_lock);
pr_debug("SLATE PIL qseecom returned with value 0x%x and status 0x%x\n",
rc, slate_tz_rsp->status);
if (rc || slate_tz_rsp->status)
pbd->cmd_status = slate_tz_rsp->status;
else
pbd->cmd_status = 0;
/* if last command sent was SLATE_VERSION print the version */
if (req->tzapp_slate_cmd == SLATE_RPROC_GET_SLATE_VERSION) {
pr_info("SLATE FW version\n");
for (i = 0; i < slate_tz_rsp->slate_info_len; i++) {
pr_info("Version 0x%x\n", slate_tz_rsp->slate_info[i]);
ascii = (unsigned char *)&slate_tz_rsp->slate_info[i];
}
}
return rc;
end:
mutex_unlock(&cmdsync_lock);
return rc;
}
/**
* slate_restart_work() - schedule by interrupt handler to carry
* out ssr sequence
* @work: work struct.
*
* Return: none.
*/
static void slate_restart_work(struct work_struct *work)
{
struct qcom_slate *slate_data =
container_of(work, struct qcom_slate, restart_work);
struct rproc *slate_rproc = slate_data->rproc;
/* Trigger apps crash if recovery is disabled */
BUG_ON(slate_rproc->recovery_disabled);
/* If recovery is enabled, go for recovery path */
pr_debug("Slate is crashed! Starting recovery...\n");
rproc_report_crash(slate_rproc, RPROC_FATAL_ERROR);
}
static irqreturn_t slate_status_change(int irq, void *dev_id)
{
bool value;
struct qcom_slate *drvdata = (struct qcom_slate *)dev_id;
struct tzapp_slate_req slate_tz_req;
int ret = 0;
if (!drvdata)
return IRQ_HANDLED;
value = gpio_get_value(drvdata->gpios[0]);
update_s2a_status(value);
if (value && !drvdata->is_ready) {
dev_info(drvdata->dev,
"SLATE services are up and running: irq state changed 0->1\n");
__pm_relax(&drvdata->rproc_slate_ws);
drvdata->is_ready = true;
complete(&drvdata->err_ready);
slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_UP_INFO;
ret = slate_tzapp_comm(drvdata, &slate_tz_req);
if (ret || drvdata->cmd_status) {
dev_err(drvdata->dev, "%s: SLATE RPROC get slate version failed error %d\n",
__func__, drvdata->cmd_status);
return IRQ_NONE;
}
} else if (!value && drvdata->is_ready) {
dev_err(drvdata->dev,
"SLATE got unexpected reset: irq state changed 1->0\n");
/* skip dump collection and return with shutdown completion signal */
if (is_slate_unload_only()) {
dev_info(drvdata->dev,
"Received shutdown_only request\n");
complete(&drvdata->shutdown_done);
return IRQ_HANDLED;
}
__pm_stay_awake(&drvdata->rproc_slate_ws);
queue_work(drvdata->slate_queue, &drvdata->restart_work);
} else {
dev_err(drvdata->dev, "SLATE status irq: unknown status\n");
}
return IRQ_HANDLED;
}
/**
* setup_slate_gpio_irq() - called in probe to configure input/
* output gpio.
* @drvdata: private data struct for SLATE.
*
* Return: 0 on success. Error code on failure.
*/
static int setup_slate_gpio_irq(struct platform_device *pdev,
struct qcom_slate *drvdata)
{
int ret = -1;
int irq, i;
if (gpio_request(drvdata->gpios[0], "SLATE2AP_STATUS")) {
dev_err(&pdev->dev,
"%s Failed to configure SLATE2AP_STATUS gpio\n",
__func__);
goto err;
}
gpio_direction_input(drvdata->gpios[0]);
/* SLATE2AP STATUS IRQ */
irq = gpio_to_irq(drvdata->gpios[0]);
if (irq < 0) {
dev_err(&pdev->dev,
"%s: bad SLATE2AP_STATUS IRQ resource, err = %d\n",
__func__, irq);
goto err;
}
drvdata->status_irq = irq;
ret = devm_request_threaded_irq(drvdata->dev, drvdata->status_irq,
NULL, slate_status_change,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"slate2ap_status", drvdata);
if (ret < 0) {
dev_err(drvdata->dev,
"%s: SLATE2AP_STATUS IRQ#%d re registration failed, err=%d\n",
__func__, drvdata->status_irq, ret);
goto err;
}
ret = irq_set_irq_wake(drvdata->status_irq, true);
if (ret < 0) {
dev_err(drvdata->dev,
"%s: SLATE2AP_STATUS IRQ#%d set wakeup capable failed, err=%d\n",
__func__, drvdata->status_irq, ret);
goto err;
}
/* AP2SLATE STATUS IRQ */
if (gpio_request(drvdata->gpios[1], "AP2SLATE_STATUS")) {
dev_err(&pdev->dev,
"%s Failed to configure AP2SLATE_STATUS gpio\n",
__func__);
goto err;
}
/*
* Put status gpio in default high state which will
* make transition to low on any sudden reset case of msm
*/
gpio_direction_output(drvdata->gpios[1], 1);
/* Inform SLATE that AP is up */
gpio_set_value(drvdata->gpios[1], 1);
return 0;
err:
for (i = 0; i < NUM_GPIOS; ++i) {
if (gpio_is_valid(drvdata->gpios[i]))
gpio_free(drvdata->gpios[i]);
}
return ret;
}
/**
* slate_dt_parse_gpio() - called in probe to parse gpio's
* @drvdata: private data struct for SLATE.
*
* Return: 0 on success. Error code on failure.
*/
static int slate_dt_parse_gpio(struct platform_device *pdev,
struct qcom_slate *drvdata)
{
int i, val;
for (i = 0; i < NUM_GPIOS; i++)
drvdata->gpios[i] = INVALID_GPIO;
val = of_get_named_gpio(pdev->dev.of_node,
"qcom,slate2ap-status-gpio", 0);
if (val >= 0) {
drvdata->gpios[0] = val;
update_s2a_status(gpio_get_value(drvdata->gpios[0]));
} else {
pr_err("SLATE status gpio not found, error=%d\n", val);
return -EINVAL;
}
val = of_get_named_gpio(pdev->dev.of_node,
"qcom,ap2slate-status-gpio", 0);
if (val >= 0)
drvdata->gpios[1] = val;
else {
pr_err("ap2slate status gpio not found, error=%d\n", val);
return -EINVAL;
}
return 0;
}
/**
* slate_auth_and_xfer() - Called by start operation of remoteproc framework
* to signal tz app to authenticate and boot slate chip.
* @slate_data: struct containing private <slate> data.
*
* Return: 0 on success. Error code on failure.
*/
static int slate_auth_and_xfer(struct qcom_slate *slate_data)
{
struct tzapp_slate_req slate_tz_req;
uint32_t ns_vmids[] = {VMID_HLOS};
uint32_t ns_vm_perms[] = {PERM_READ | PERM_WRITE};
u64 shm_bridge_handle;
int ret;
ret = qtee_shmbridge_register(slate_data->address_fw, slate_data->size_fw,
ns_vmids, ns_vm_perms, 1, PERM_READ | PERM_WRITE,
&shm_bridge_handle);
if (ret) {
dev_err(slate_data->dev,
"%s: Failed to create shm bridge [%d]\n",
__func__, ret);
return ret;
}
slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_IMAGE_LOAD;
slate_tz_req.address_fw = slate_data->address_fw;
slate_tz_req.size_fw = slate_data->size_fw;
ret = slate_tzapp_comm(slate_data, &slate_tz_req);
if (slate_data->cmd_status == SLATE_CRASH_IN_TWM) {
slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_DLOAD_CONT;
ret = slate_tzapp_comm(slate_data, &slate_tz_req);
}
if (ret || slate_data->cmd_status) {
dev_err(slate_data->dev,
"%s: Firmware image authentication failed\n",
__func__);
ret = slate_data->cmd_status;
goto tzapp_comm_failed;
}
/* slate Transfer of image is complete, free up the memory */
pr_debug("Firmware authentication and transfer done\n");
tzapp_comm_failed:
qtee_shmbridge_deregister(shm_bridge_handle);
return ret;
}
/**
* wait_for_err_ready() - Called in power_up to wait for error ready.
* Signal waiting function.
* @slate_data: SLATE RPROC private structure.
*
* Return: 0 on success. Error code on failure.
*/
static int wait_for_err_ready(struct qcom_slate *slate_data)
{
int ret;
if ((!slate_data->status_irq))
return 0;
ret = wait_for_completion_timeout(&slate_data->err_ready,
msecs_to_jiffies(10000));
if (!ret) {
pr_err("[%s]: Error ready timed out\n",
slate_data->firmware_name);
return -ETIMEDOUT;
}
return 0;
}
/**
* wait_for_shutdown_done() - Called in power_down to wait for shutdown done.
* Signal waiting function.
* @slate_data: SLATE RPROC private structure.
*
* Return: 0 on success. Error code on failure.
*/
static int wait_for_shutdown_done(struct qcom_slate *slate_data)
{
int ret;
if ((!slate_data->status_irq))
return 0;
ret = wait_for_completion_timeout(&slate_data->shutdown_done,
msecs_to_jiffies(10000));
if (!ret) {
pr_err("[%s]: shutdown done timed out\n",
slate_data->firmware_name);
return -ETIMEDOUT;
}
return 0;
}
void send_reset_signal(struct qcom_slate *slate_data)
{
struct tzapp_slate_req slate_tz_req;
int ret;
slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_RESET;
slate_tz_req.address_fw = 0;
slate_tz_req.size_fw = 0;
ret = slate_tzapp_comm(slate_data, &slate_tz_req);
if (ret || slate_data->cmd_status)
dev_err(slate_data->dev,
"%s: Failed to send reset signal to tzapp\n",
__func__);
}
int slate_flash_mode(struct qcom_slate *slate_data)
{
int ret;
int retry_attempt = 2;
do {
ret = wait_for_err_ready(slate_data);
if (!ret)
return RESULT_SUCCESS;
dev_err(slate_data->dev,
"[%s:%d]: Timed out waiting for error ready: %s!\n",
current->comm, current->pid, slate_data->firmware_name);
pr_debug("Retry booting slate, Mode: Flash, attempt: %d\n",
retry_attempt);
send_reset_signal(slate_data);
retry_attempt -= 1;
} while (retry_attempt);
return ret;
}
static int slate_start(struct rproc *rproc)
{
struct qcom_slate *slate_data = (struct qcom_slate *)rproc->priv;
struct tzapp_slate_req slate_tz_req;
int ret = 0;
if (!slate_data) {
dev_err(slate_data->dev, "%s Invalid slate pointer !!\n",
__func__);
return -EINVAL;
}
/* Slate is booted from flash */
if (get_slate_boot_mode()) {
if (gpio_get_value(slate_data->gpios[0])) {
pr_info("Slate is booted up!! Mode: FLASH\n");
slate_data->is_ready = true;
/* Update dump configuration when it boots from FLASH Mode. */
slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_UP_INFO;
ret = slate_tzapp_comm(slate_data, &slate_tz_req);
if (ret || slate_data->cmd_status)
dev_err(slate_data->dev,
"%s: failed to update dump info %d\n",
__func__, slate_data->cmd_status);
return RESULT_SUCCESS;
} else
return RESULT_FAILURE;
}
slate_data->address_fw = slate_data->region_start;
slate_data->size_fw = slate_data->region_end - slate_data->region_start;
pr_debug("SLATE PIL loads firmware blobs at 0x%llx with size 0x%zx\n",
slate_data->address_fw, slate_data->size_fw);
ret = slate_auth_and_xfer(slate_data);
if (ret) {
dev_err(slate_data->dev, "%s slate TZ app load failure\n",
__func__);
return ret;
}
ret = wait_for_err_ready(slate_data);
if (ret) {
dev_err(slate_data->dev,
"[%s:%d]: Timed out waiting for error ready: %s!\n",
current->comm, current->pid, slate_data->firmware_name);
return ret;
}
pr_err("Slate is booted up!! Mode: HOST\n");
dma_free_coherent(slate_data->dev, slate_data->region_size,
slate_data->region, slate_data->region_start);
slate_data->is_region_allocated = false;
slate_data->region = NULL;
slate_data->region_start = 0;
slate_data->region_end = 0;
slate_data->region_size = 0;
return 0;
}
/**
* slate_coredump() - Called by SSR framework to save dump of SLATE internal
* memory, SLATE PIL does allocate region from dynamic memory and pass this
* region to tz to dump memory content of SLATE.
* @rproc: remoteproc handle for slate.
*
* Return: 0 on success. Error code on failure.
*/
static void slate_coredump(struct rproc *rproc)
{
struct qcom_slate *slate_data = (struct qcom_slate *)rproc->priv;
struct tzapp_slate_req slate_tz_req;
uint32_t ns_vmids[] = {VMID_HLOS};
uint32_t ns_vm_perms[] = {PERM_READ | PERM_WRITE};
u64 shm_bridge_handle;
void *region;
phys_addr_t start_addr;
uint32_t dump_info;
unsigned long size = SLATE_RAMDUMP_SZ;
unsigned long attr = 0;
int ret = 0;
pr_err("Setup for Coredump.\n");
if (!slate_data->qseecom_handle) {
ret = load_slate_tzapp(slate_data);
if (ret) {
dev_err(slate_data->dev,
"%s: SLATE TZ app load failure\n",
__func__);
return;
}
}
/* This check is added here to make slate dump collection
* decision in RTOS/TWM mode exit. Only way for kernel to know slate state
* info(crashed/running)in RTOS/TWM exit is by reading S2A irq line.
* When S2A is pulled LOW, it is interpreted as slate crashed state and
* slate dump needs to be collected. Once dumps are collected TZ will
* automatically send SLATE_RESET CMD.
* When S2A is pulled HIGH, it is interpreted as slate running state and
* dump should not be collected. At this point it is necessary
* to send SLATE_RESET CMD to bring slate out of RTOS slate.
* This check does not disturb SSR/system dump collection.
*/
if (is_twm_exit()) {
/* reset_cmd signals shutdown on slate, lets ack s2a irq for same
* otherwise it will haunt after slate boot up and crash system.
*/
if (!gpio_get_value(slate_data->gpios[0])) {
pr_info("TWM Exit: Collect Dump, slate is CRASHED..!!\n");
/* We are assuming that Slate TZapp has started
* subsystem_ramdump service.
* Introducing delay here for subsystem_ramdump
* service to start.
*/
msleep(5000);
} else {
enable_irq(slate_data->status_irq);
slate_data->is_ready = true;
pr_info("TWM Exit: Skip dump collection, slate is RUNNING ..!!\n");
/* Send RESET CMD to bring slate out of RTOS state */
slate_data->cmd_status = 0;
slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_RESET;
ret = slate_tzapp_comm(slate_data, &slate_tz_req);
if (ret || slate_data->cmd_status) {
dev_err(slate_data->dev,
"%s: Failed to send reset signal to tzapp\n",
__func__);
goto rtos_out;
}
/* reset_cmd signals shutdown on slate, lets ack s2a irq for same
* otherwise it will haunt after slate boot up and crash system.
*/
/* By this time if S2A is not pulled then wait for it to go LOW */
if (gpio_get_value(slate_data->gpios[0])) {
ret = wait_for_shutdown_done(slate_data);
if (ret) {
dev_err(slate_data->dev,
"[%s:%d]: Timed out waiting for error ready: %s!\n",
current->comm, current->pid, slate_data->firmware_name);
}
}
goto rtos_out;
}
}
rproc_coredump_cleanup(rproc);
slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_DUMPINFO;
ret = slate_tzapp_comm(slate_data, &slate_tz_req);
dump_info = slate_data->cmd_status;
if (dump_info == SLATE_RAMDUMP)
size = SLATE_RAMDUMP_SZ;
else if (dump_info == SLATE_MINIDUMP)
size = SLATE_MINIDUMP_SZ;
else {
dev_err(slate_data->dev,
"%s: SLATE RPROC ramdump collection failed\n",
__func__);
return;
}
region = dma_alloc_attrs(slate_data->dev, size,
&start_addr, GFP_KERNEL, attr);
if (region == NULL) {
dev_dbg(slate_data->dev,
"fail to allocate ramdump region of size %zx\n",
size);
return;
}
slate_data->mem_phys = start_addr;
slate_data->mem_size = size;
slate_data->mem_region = region;
ret = qtee_shmbridge_register(start_addr, size, ns_vmids,
ns_vm_perms, 1, PERM_READ | PERM_WRITE, &shm_bridge_handle);
if (ret) {
pr_err("%s Failed to create shm bridge. ret=[%d]\n",
__func__, ret);
goto dma_free;
}
slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_RAMDUMP;
slate_tz_req.address_fw = start_addr;
slate_tz_req.size_fw = size;
ret = slate_tzapp_comm(slate_data, &slate_tz_req);
if (ret != 0 || slate_data->cmd_status) {
dev_dbg(slate_data->dev,
"%s: SLATE RPROC ramdmp collection failed\n",
__func__);
goto shm_free;
}
dma_sync_single_for_cpu(slate_data->dev, slate_data->mem_phys, size, DMA_FROM_DEVICE);
pr_debug("Add coredump segment!\n");
ret = rproc_coredump_add_custom_segment(rproc, start_addr, size,
NULL, NULL);
if (ret) {
dev_err(slate_data->dev, "failed to add rproc_segment: %d\n",
ret);
rproc_coredump_cleanup(slate_data->rproc);
goto shm_free;
}
rproc_coredump(rproc);
shm_free:
qtee_shmbridge_deregister(shm_bridge_handle);
dma_free:
dma_free_attrs(slate_data->dev, size, region,
start_addr, attr);
return;
rtos_out:
disable_irq(slate_data->status_irq);
slate_data->is_ready = false;
}
/**
* slate_prepare() - Called by rproc_boot. This loads tz app.
* @rproc: struct caontaining private slate data.
*
* Return: 0 on success. Error code on failure.
*/
static int slate_prepare(struct rproc *rproc)
{
struct qcom_slate *slate_data = (struct qcom_slate *)rproc->priv;
int ret = 0;
if (!slate_data) {
dev_err(slate_data->dev, "%s Invalid slate pointer !!\n",
__func__);
return -EINVAL;
}
if (slate_data->app_status != RESULT_SUCCESS) {
ret = load_slate_tzapp(slate_data);
if (ret) {
dev_err(slate_data->dev,
"%s: slate TZ app load failure\n", __func__);
return ret;
}
}
pr_debug("slateapp loaded\n");
return ret;
}
static int slate_alloc_mem(struct qcom_slate *slate_data, size_t aligned_size)
{
slate_data->region = dma_alloc_coherent(slate_data->dev, aligned_size,
&slate_data->region_start, GFP_KERNEL);
if (!slate_data->region)
return -ENOMEM;
return 0;
}
static int slate_alloc_region(struct qcom_slate *slate_data, phys_addr_t min_addr,
phys_addr_t max_addr, size_t align)
{
size_t size = max_addr - min_addr;
size_t aligned_size;
int ret;
/* Don't reallocate due to fragmentation concerns, just sanity check */
if (slate_data->is_region_allocated) {
if (WARN(slate_data->region_end - slate_data->region_start < size,
"Can't reuse PIL memory, too small\n"))
return -ENOMEM;
return 0;
}
if (align >= SZ_4M)
aligned_size = ALIGN(size, SZ_4M);
else if (align >= SZ_1M)
aligned_size = ALIGN(size, SZ_1M);
else
aligned_size = ALIGN(size, SZ_4K);
ret = slate_alloc_mem(slate_data, aligned_size);
if (ret) {
dev_err(slate_data->dev,
"%s: Failed to allocate relocatable region\n",
__func__);
slate_data->region_start = 0;
slate_data->region_end = 0;
return ret;
}
slate_data->is_region_allocated = true;
slate_data->region_end = slate_data->region_start + size;
slate_data->region_size = aligned_size;
return 0;
}
static bool segment_is_relocatable(const struct elf32_phdr *p)
{
return !!(p->p_flags & BIT(27));
}
static int segment_is_loadable(const struct elf32_phdr *p)
{
return (p->p_type == PT_LOAD) && !segment_is_hash(p->p_flags) &&
p->p_memsz;
}
static int slate_setup_region(struct qcom_slate *slate_data, const struct pil_mdt *mdt)
{
const struct elf32_phdr *phdr;
phys_addr_t min_addr_r, min_addr_n, max_addr_r, max_addr_n, start, end;
size_t align = 0;
int i, ret = 0;
bool relocatable = false;
min_addr_n = min_addr_r = (phys_addr_t)ULLONG_MAX;
max_addr_n = max_addr_r = 0;
/* Find the image limits */
for (i = 0; i < mdt->hdr.e_phnum; i++) {
phdr = &mdt->phdr[i];
if (!segment_is_loadable(phdr))
continue;
start = phdr->p_paddr;
end = start + phdr->p_memsz;
if (segment_is_relocatable(phdr)) {
min_addr_r = min(min_addr_r, start);
max_addr_r = max(max_addr_r, end);
/*
* Lowest relocatable segment dictates alignment of
* relocatable region
*/
if (min_addr_r == start)
align = phdr->p_align;
relocatable = true;
} else {
min_addr_n = min(min_addr_n, start);
max_addr_n = max(max_addr_n, end);
}
}
/* Align the max address to the next 4K boundary to satisfy iommus and
* XPUs that operate on 4K chunks.
*/
max_addr_n = ALIGN(max_addr_n, SZ_4K);
max_addr_r = ALIGN(max_addr_r, SZ_4K);
if (relocatable) {
ret = slate_alloc_region(slate_data, min_addr_r, max_addr_r, align);
} else {
slate_data->region_start = min_addr_n;
slate_data->region_end = max_addr_n;
}
return ret;
}
static int slate_auth_metadata(struct qcom_slate *slate_data, const u8 *metadata, size_t size)
{
struct tzapp_slate_req slate_tz_req;
struct qtee_shm shm;
int ret = 0;
ret = qtee_shmbridge_allocate_shm(size, &shm);
if (ret) {
pr_err("Shmbridge memory allocation failed\n");
return ret;
}
/* Make sure there are no mapping in PKMAP and fixmap */
kmap_flush_unused();
memcpy(shm.vaddr, metadata, size);
slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_AUTH_MDT;
slate_tz_req.address_fw = shm.paddr;
slate_tz_req.size_fw = size;
ret = slate_tzapp_comm(slate_data, &slate_tz_req);
if (ret || slate_data->cmd_status) {
dev_err(slate_data->dev,
"%s: Metadata loading is failed\n",
__func__);
ret = slate_data->cmd_status;
goto tzapp_com_failed;
}
pr_debug("Metadata loaded successfully\n");
tzapp_com_failed:
qtee_shmbridge_free_shm(&shm);
return ret;
}
static int slate_load(struct rproc *rproc, const struct firmware *fw)
{
struct qcom_slate *slate_data = (struct qcom_slate *)rproc->priv;
int ret = 0;
const struct pil_mdt *mdt;
if (!slate_data) {
dev_err(slate_data->dev, "%s Invalid slate pointer !!\n",
__func__);
return -EINVAL;
}
/* Enable status and err fetal irqs */
enable_irq(slate_data->status_irq);
/* boot slate from flash */
if (get_slate_boot_mode()) {
if (gpio_get_value(slate_data->gpios[0]))
return RESULT_SUCCESS;
ret = slate_flash_mode(slate_data);
if (!ret)
return RESULT_SUCCESS;
dev_err(slate_data->dev,
"%s: Failed to boot slate from flash\n",
__func__);
return ret;
}
mdt = (const struct pil_mdt *)fw->data;
ret = slate_setup_region(slate_data, mdt);
if (ret) {
dev_err(slate_data->dev, "%s: slate memory_setup failure\n",
__func__);
return ret;
}
pr_debug("Loading from %pa tp %pa\n", &slate_data->region_start,
&slate_data->region_end);
ret = qcom_mdt_load_no_init(slate_data->dev, fw, rproc->firmware, 0,
slate_data->region, slate_data->region_start,
slate_data->region_size, NULL);
if (ret) {
dev_err(slate_data->dev, "%s: slate memory setup failure\n",
__func__);
return ret;
}
/* send the metadata */
ret = slate_auth_metadata(slate_data, fw->data, fw->size);
if (ret) {
dev_err(slate_data->dev, "%s: slate TZ app load failure\n",
__func__);
return ret;
}
return ret;
}
static int slate_stop(struct rproc *rproc)
{
struct qcom_slate *slate_data = rproc->priv;
struct tzapp_slate_req slate_tz_req;
int ret = RESULT_FAILURE;
if (!slate_data) {
dev_err(slate_data->dev, "%s Invalid slate pointer !!\n",
__func__);
return -EINVAL;
}
if (!slate_data->is_ready) {
dev_err(slate_data->dev, "%s: Slate is not up!\n", __func__);
return ret;
}
/* Dont send shutdown cmd if slate is crashed or offline state*/
if (!gpio_get_value(slate_data->gpios[0]))
goto out;
ret = load_slate_tzapp(slate_data);
if (ret) {
dev_err(slate_data->dev,
"%s: SLATE TZ app load failure\n",
__func__);
return ret;
}
slate_data->cmd_status = 0;
slate_tz_req.tzapp_slate_cmd = SLATE_RPROC_SHUTDOWN;
slate_tz_req.address_fw = 0;
slate_tz_req.size_fw = 0;
ret = slate_tzapp_comm(slate_data, &slate_tz_req);
if (ret || slate_data->cmd_status) {
pr_debug("Slate pil shutdown failed\n");
return ret;
}
/* wait for slate shutdown completion in exclusive slate shutdown request */
if (is_slate_unload_only()) {
ret = wait_for_shutdown_done(slate_data);
if (ret) {
dev_err(slate_data->dev,
"[%s:%d]: Timed out waiting for error ready: %s!\n",
current->comm, current->pid, slate_data->firmware_name);
return ret;
}
}
out:
disable_irq(slate_data->status_irq);
slate_data->is_ready = false;
return 0;
}
static void *slate_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
{
struct qcom_slate *slate_data = (struct qcom_slate *)rproc->priv;
int offset;
offset = da - slate_data->mem_phys;
if (offset < 0 || offset + len > slate_data->mem_size)
return NULL;
return slate_data->mem_region + offset;
}
static const struct rproc_ops slate_ops = {
.prepare = slate_prepare,
.get_boot_addr = rproc_elf_get_boot_addr,
.load = slate_load,
.start = slate_start,
.stop = slate_stop,
.coredump = slate_coredump,
.da_to_va = slate_da_to_va,
};
static int slate_app_reboot_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct qcom_slate *slate_data = container_of(nb,
struct qcom_slate, reboot_nb);
/* Disable irq if already SLATE is up */
if (slate_data->is_ready) {
disable_irq(slate_data->status_irq);
slate_data->is_ready = false;
}
/* Toggle AP2SLATE err fetal gpio here to inform apps err fetal event */
if (gpio_is_valid(slate_data->gpios[1])) {
pr_debug("Sending reboot signal\n");
gpio_set_value(slate_data->gpios[1], 1);
}
return NOTIFY_DONE;
}
#ifdef CONFIG_DEEPSLEEP
static int rproc_slate_suspend(struct device *dev)
{
if (pm_suspend_via_firmware()) {
struct qcom_slate *slate_data = dev_get_drvdata(dev);
qseecom_shutdown_app(&slate_data->qseecom_handle);
slate_data->qseecom_handle = NULL;
}
return 0;
}
static int rproc_slate_resume(struct device *dev)
{
return 0;
}
#endif
static int rproc_slate_driver_probe(struct platform_device *pdev)
{
struct qcom_slate *slate;
struct rproc *rproc;
const char *fw_name;
int ret;
char md_dev_name[32];
ret = of_property_read_string(pdev->dev.of_node,
"qcom,firmware-name", &fw_name);
if (ret)
return ret;
mutex_init(&cmdsync_lock);
rproc = rproc_alloc(&pdev->dev, pdev->name, &slate_ops,
fw_name, sizeof(*slate));
if (!rproc) {
dev_err(&pdev->dev, "unable to allocate remoteproc\n");
mutex_destroy(&cmdsync_lock);
return -ENOMEM;
}
rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
slate = (struct qcom_slate *)rproc->priv;
slate->dev = &pdev->dev;
/* Add wake lock */
wakeup_source_add(&slate->rproc_slate_ws);
/* Read GPIO configuration */
ret = slate_dt_parse_gpio(pdev, slate);
if (ret)
goto free_rproc;
ret = setup_slate_gpio_irq(pdev, slate);
if (ret < 0)
goto free_rproc;
/* Disable S2A irq bydefault */
disable_irq(slate->status_irq);
slate->firmware_name = fw_name;
slate->app_status = RESULT_FAILURE;
rproc->dump_conf = RPROC_COREDUMP_ENABLED;
rproc->recovery_disabled = true;
rproc->auto_boot = false;
slate->rproc = rproc;
slate->sysmon_name = "slatefw";
slate->ssr_name = "slatefw";
slate->ssctl_id = -EINVAL;
platform_set_drvdata(pdev, slate);
/* Register SSR subdev to rproc*/
qcom_add_ssr_subdev(rproc, &slate->ssr_subdev, slate->ssr_name);
qcom_add_glink_subdev(rproc, &slate->glink_subdev, slate->ssr_name);
slate->sysmon = qcom_add_sysmon_subdev(rproc, slate->sysmon_name,
slate->ssctl_id);
if (IS_ERR(slate->sysmon)) {
ret = PTR_ERR(slate->sysmon);
dev_err(slate->dev, "%s: Error while adding sysmon subdevice:[%d]\n",
__func__, ret);
goto free_rproc;
}
ret = device_create_file(slate->dev, &dev_attr_txn_id);
if (ret)
goto remove_subdev;
/* Register callback for handling reboot */
slate->reboot_nb.notifier_call = slate_app_reboot_notify;
register_reboot_notifier(&slate->reboot_nb);
slate->slate_queue = alloc_workqueue("slate_queue", 0, 0);
if (!slate->slate_queue) {
dev_err(slate->dev, "%s: creation of slate_queue failed\n",
__func__);
goto unregister_notify;
}
/* Initialize work queue for reset handler */
INIT_WORK(&slate->restart_work, slate_restart_work);
snprintf(md_dev_name, ARRAY_SIZE(md_dev_name),
"%s-md", pdev->dev.of_node->name);
/* Register with rproc */
ret = rproc_add(rproc);
if (ret)
goto destroy_wq;
init_completion(&slate->err_ready);
init_completion(&slate->shutdown_done);
pr_debug("Slate probe is completed\n");
return 0;
destroy_wq:
destroy_workqueue(slate_reset_wq);
unregister_notify:
unregister_reboot_notifier(&slate->reboot_nb);
remove_subdev:
qcom_remove_sysmon_subdev(slate->sysmon);
free_rproc:
rproc_free(rproc);
mutex_destroy(&cmdsync_lock);
return ret;
}
static int rproc_slate_driver_remove(struct platform_device *pdev)
{
struct qcom_slate *slate = platform_get_drvdata(pdev);
if (slate_reset_wq)
destroy_workqueue(slate_reset_wq);
device_remove_file(slate->dev, &dev_attr_txn_id);
qcom_remove_glink_subdev(slate->rproc, &slate->glink_subdev);
qcom_remove_sysmon_subdev(slate->sysmon);
qcom_remove_ssr_subdev(slate->rproc, &slate->ssr_subdev);
unregister_reboot_notifier(&slate->reboot_nb);
rproc_del(slate->rproc);
rproc_free(slate->rproc);
return 0;
}
static const struct dev_pm_ops rproc_slate_pm_ops = {
#ifdef CONFIG_DEEPSLEEP
.suspend = rproc_slate_suspend,
.resume = rproc_slate_resume,
#endif
};
static const struct of_device_id rproc_slate_match_table[] = {
{.compatible = "qcom,rproc-slate"},
{}
};
MODULE_DEVICE_TABLE(of, rproc_slate_match_table);
static struct platform_driver rproc_slate_driver = {
.probe = rproc_slate_driver_probe,
.remove = rproc_slate_driver_remove,
.driver = {
.name = "qcom-rproc-slate",
.of_match_table = rproc_slate_match_table,
.pm = &rproc_slate_pm_ops,
},
};
module_platform_driver(rproc_slate_driver);
MODULE_DESCRIPTION("Support for booting QTI slate SoC");
MODULE_LICENSE("GPL");