The change is to bring bg pil driver to 4.14 which is taken from snapshot of the same driver on 4.9 commit "43ff8774753d32a76f62160f0ad1cecf69fb56e4". ("soc: qcom: Add BG PIL and feature drivers for msm8909w"). Change-Id: I430101cb1c0a4448d77e4315f3bbcf101dadb774 Signed-off-by: Archit Saxena <archsaxe@codeaurora.org>tirimbino
parent
ec8d33c43e
commit
31c8fd0a1a
@ -0,0 +1,33 @@ |
||||
Qualcomm Technologies Inc Blackghost(BG) PIL driver: |
||||
|
||||
Blackghost(BG) PIL driver provide interface to load and boot Blackghost(BG) |
||||
SOC. Blackghost(BG) SOC is an external SOC which communicate to MSM via |
||||
SPI. The PIL driver loads firmware into memory and invoke secure app via |
||||
qseecom to transfer and handshake the boot process. Once Blackghost(BG) SOC |
||||
is booted it raises ready interrupt which is handled by BG PIL driver, and |
||||
this event is informed to other MSM drivers, other remote subsystem via |
||||
notifier framework. The PIL driver also handles interrupt for the BG SOC |
||||
crash upon arrival of which it reset and initiates ramdump collection for BG |
||||
SOC. |
||||
|
||||
Required properties: |
||||
- compatible: Must be "qcom,pil-blackghost" |
||||
- qcom,firmware-name: Base name of the firmware image. |
||||
- qcom,bg2ap-status-gpio: GPIO used by the blackghost to indicate status to the apps. |
||||
- qcom,bg2ap-errfatal-gpio: GPIO used by the blackghost to indicate software error to the apps. |
||||
- qcom,ap2bg-status-gpio: GPIO used by the apps to indicate its status to blackghost. |
||||
- qcom,ap2bg-errfatal-gpio: GPIO used by the apps to indicate blackghost about apps reset. |
||||
|
||||
Example: |
||||
|
||||
qcom,blackghost { |
||||
compatible = "qcom,pil-blackghost"; |
||||
qcom,firmware-name = "bgelf"; |
||||
|
||||
/* GPIO inputs from blackghost */ |
||||
qcom,bg2ap-status-gpio = <&msm_gpio 97 0>; |
||||
qcom,bg2ap-errfatal-gpio = <&msm_gpio 95 0>; |
||||
/* GPIO output to blackghost */ |
||||
qcom,ap2bg-status-gpio = <&msm_gpio 17 0>; |
||||
qcom,ap2bg-errfatal-gpio = <&msm_gpio 23 0>; |
||||
}; |
@ -0,0 +1,45 @@ |
||||
/* Copyright (c) 2017-2018, 2020, The Linux Foundation. All rights reserved.
|
||||
* |
||||
* This program is free software; you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License version 2 and |
||||
* only version 2 as published by the Free Software Foundation. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#ifndef __BG_INTF_H_ |
||||
#define __BG_INTF_H_ |
||||
|
||||
#define MAX_APP_NAME_SIZE 100 |
||||
#define RESULT_SUCCESS 0 |
||||
#define RESULT_FAILURE -1 |
||||
|
||||
/* tzapp command list.*/ |
||||
enum bg_tz_commands { |
||||
BGPIL_RAMDUMP, |
||||
BGPIL_IMAGE_LOAD, |
||||
BGPIL_AUTH_MDT, |
||||
BGPIL_DLOAD_CONT, |
||||
BGPIL_GET_BG_VERSION, |
||||
}; |
||||
|
||||
/* tzapp bg request.*/ |
||||
struct tzapp_bg_req { |
||||
uint8_t tzapp_bg_cmd; |
||||
uint8_t padding[3]; |
||||
phys_addr_t address_fw; |
||||
size_t size_fw; |
||||
} __attribute__ ((__packed__)); |
||||
|
||||
/* tzapp bg response.*/ |
||||
struct tzapp_bg_rsp { |
||||
uint32_t tzapp_bg_cmd; |
||||
uint32_t bg_info_len; |
||||
int32_t status; |
||||
uint32_t bg_info[100]; |
||||
} __attribute__ ((__packed__)); |
||||
|
||||
#endif |
@ -0,0 +1,840 @@ |
||||
/* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
* |
||||
* This program is free software; you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License version 2 and |
||||
* only version 2 as published by the Free Software Foundation. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#include <linux/kernel.h> |
||||
#include <linux/err.h> |
||||
#include <linux/io.h> |
||||
#include <linux/module.h> |
||||
#include <linux/platform_device.h> |
||||
#include <linux/of.h> |
||||
#include <linux/interrupt.h> |
||||
#include <linux/of_gpio.h> |
||||
#include <linux/delay.h> |
||||
#include <linux/slab.h> |
||||
#include <linux/dma-mapping.h> |
||||
#include <linux/reboot.h> |
||||
#include <soc/qcom/subsystem_restart.h> |
||||
#include <soc/qcom/ramdump.h> |
||||
#include <soc/qcom/subsystem_notif.h> |
||||
#include <linux/highmem.h> |
||||
|
||||
#include "peripheral-loader.h" |
||||
#include "../../misc/qseecom_kernel.h" |
||||
#include "pil_bg_intf.h" |
||||
#include "bgcom_interface.h" |
||||
|
||||
#define INVALID_GPIO -1 |
||||
#define NUM_GPIOS 4 |
||||
#define SECURE_APP "bgapp" |
||||
#define desc_to_data(d) container_of(d, struct pil_bg_data, desc) |
||||
#define subsys_to_data(d) container_of(d, struct pil_bg_data, subsys_desc) |
||||
#define BG_RAMDUMP_SZ 0x00102000 |
||||
#define BG_VERSION_SZ 32 |
||||
#define BG_CRASH_IN_TWM -2 |
||||
/**
|
||||
* struct pil_bg_data |
||||
* @qseecom_handle: handle of TZ app |
||||
* @bg_queue: private queue to schedule worker threads for bottom half |
||||
* @restart_work: work struct for executing ssr |
||||
* @reboot_blk: notification block for reboot event |
||||
* @subsys_desc: subsystem descriptor |
||||
* @subsys: subsystem device pointer |
||||
* @gpios: array to hold all gpio handle |
||||
* @desc: PIL descriptor |
||||
* @address_fw: address where firmware binaries loaded |
||||
* @ramdump_dev: ramdump device pointer |
||||
* @size_fw: size of bg firmware binaries |
||||
* @errfatal_irq: irq number to indicate bg crash or shutdown |
||||
* @status_irq: irq to indicate bg status |
||||
* @app_status: status of tz app loading |
||||
* @is_ready: Is BG chip up |
||||
* @err_ready: The error ready signal |
||||
*/ |
||||
|
||||
struct pil_bg_data { |
||||
struct qseecom_handle *qseecom_handle; |
||||
struct workqueue_struct *bg_queue; |
||||
struct work_struct restart_work; |
||||
struct notifier_block reboot_blk; |
||||
struct subsys_desc subsys_desc; |
||||
struct subsys_device *subsys; |
||||
unsigned int gpios[NUM_GPIOS]; |
||||
int errfatal_irq; |
||||
int status_irq; |
||||
struct pil_desc desc; |
||||
phys_addr_t address_fw; |
||||
void *ramdump_dev; |
||||
u32 cmd_status; |
||||
size_t size_fw; |
||||
int app_status; |
||||
bool is_ready; |
||||
struct completion err_ready; |
||||
}; |
||||
|
||||
static irqreturn_t bg_status_change(int irq, void *dev_id); |
||||
|
||||
/**
|
||||
* bg_app_shutdown_notify() - Toggle AP2BG err fatal gpio when |
||||
* called by SSR framework. |
||||
* @subsys: struct containing private BG data. |
||||
* |
||||
* Return: none. |
||||
*/ |
||||
static void bg_app_shutdown_notify(const struct subsys_desc *subsys) |
||||
{ |
||||
struct pil_bg_data *bg_data = subsys_to_data(subsys); |
||||
|
||||
/* Disable irq if already BG is up */ |
||||
if (bg_data->is_ready) { |
||||
disable_irq(bg_data->status_irq); |
||||
disable_irq(bg_data->errfatal_irq); |
||||
bg_data->is_ready = false; |
||||
} |
||||
/* Toggle AP2BG err fatal gpio here to inform apps err fatal event */ |
||||
if (gpio_is_valid(bg_data->gpios[2])) { |
||||
pr_debug("Sending Apps shutdown signal\n"); |
||||
gpio_set_value(bg_data->gpios[2], 1); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* bg_app_reboot_notify() - Toggle AP2BG err fatal gpio. |
||||
* @nb: struct containing private BG data. |
||||
* |
||||
* Return: NOTIFY_DONE indicating success. |
||||
*/ |
||||
static int bg_app_reboot_notify(struct notifier_block *nb, |
||||
unsigned long code, void *unused) |
||||
{ |
||||
struct pil_bg_data *bg_data = container_of(nb, |
||||
struct pil_bg_data, reboot_blk); |
||||
|
||||
/* Disable irq if already BG is up */ |
||||
if (bg_data->is_ready) { |
||||
disable_irq(bg_data->status_irq); |
||||
disable_irq(bg_data->errfatal_irq); |
||||
bg_data->is_ready = false; |
||||
} |
||||
/* Toggle AP2BG err fatal gpio here to inform apps err fatal event */ |
||||
if (gpio_is_valid(bg_data->gpios[2])) { |
||||
pr_debug("Sending reboot signal\n"); |
||||
gpio_set_value(bg_data->gpios[2], 1); |
||||
} |
||||
return NOTIFY_DONE; |
||||
} |
||||
|
||||
/**
|
||||
* 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; |
||||
} |
||||
|
||||
/**
|
||||
* pil_load_bg_tzapp() - Called to load TZ app. |
||||
* @pbd: struct containing private BG data. |
||||
* |
||||
* Return: 0 on success. Error code on failure. |
||||
*/ |
||||
static int pil_load_bg_tzapp(struct pil_bg_data *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->desc.dev, "BG TZ app load failure\n"); |
||||
pbd->app_status = RESULT_FAILURE; |
||||
return -EIO; |
||||
} |
||||
pbd->app_status = RESULT_SUCCESS; |
||||
return 0; |
||||
} |
||||
|
||||
/**
|
||||
* bgpil_tzapp_comm() - Function called to communicate with TZ APP. |
||||
* @req: struct containing command and parameters. |
||||
* |
||||
* Return: 0 on success. Error code on failure. |
||||
*/ |
||||
static long bgpil_tzapp_comm(struct pil_bg_data *pbd, |
||||
struct tzapp_bg_req *req) |
||||
{ |
||||
struct tzapp_bg_req *bg_tz_req; |
||||
struct tzapp_bg_rsp *bg_tz_rsp; |
||||
int rc, req_len, rsp_len; |
||||
unsigned char *ascii; |
||||
char fiwmare_version[100] = {'\0'}; |
||||
char ascii_string[5]; |
||||
|
||||
/* Fill command structure */ |
||||
req_len = sizeof(struct tzapp_bg_req); |
||||
rsp_len = sizeof(struct tzapp_bg_rsp); |
||||
rc = get_cmd_rsp_buffers(pbd->qseecom_handle, |
||||
(void **)&bg_tz_req, &req_len, |
||||
(void **)&bg_tz_rsp, &rsp_len); |
||||
if (rc) |
||||
goto end; |
||||
|
||||
bg_tz_req->tzapp_bg_cmd = req->tzapp_bg_cmd; |
||||
bg_tz_req->address_fw = req->address_fw; |
||||
bg_tz_req->size_fw = req->size_fw; |
||||
rc = qseecom_send_command(pbd->qseecom_handle, |
||||
(void *)bg_tz_req, req_len, (void *)bg_tz_rsp, rsp_len); |
||||
pr_debug("BG PIL qseecom returned with value 0x%x and status 0x%x\n", |
||||
rc, bg_tz_rsp->status); |
||||
if (rc || bg_tz_rsp->status) |
||||
pbd->cmd_status = bg_tz_rsp->status; |
||||
else |
||||
pbd->cmd_status = 0; |
||||
/* if last command sent was BG_VERSION print the version*/ |
||||
if (req->tzapp_bg_cmd == BGPIL_GET_BG_VERSION) { |
||||
int i; |
||||
|
||||
pr_info("BG FW version "); |
||||
for (i = 0; i < bg_tz_rsp->bg_info_len; i++) { |
||||
pr_info("0x%08x ", bg_tz_rsp->bg_info[i]); |
||||
ascii = (unsigned char *)&bg_tz_rsp->bg_info[i]; |
||||
snprintf(ascii_string, PAGE_SIZE, "%c%c%c%c", ascii[0], |
||||
ascii[1], ascii[2], ascii[3]); |
||||
strlcat(fiwmare_version, ascii_string, |
||||
PAGE_SIZE); |
||||
} |
||||
pr_info("%s\n", fiwmare_version); |
||||
} |
||||
end: |
||||
return rc; |
||||
} |
||||
|
||||
/**
|
||||
* wait_for_err_ready() - Called in power_up to wait for error ready. |
||||
* Signal waiting function. |
||||
* @bg_data: BG PIL private structure. |
||||
* |
||||
* Return: 0 on success. Error code on failure. |
||||
*/ |
||||
static int wait_for_err_ready(struct pil_bg_data *bg_data) |
||||
{ |
||||
int ret; |
||||
|
||||
if ((!bg_data->status_irq)) |
||||
return 0; |
||||
|
||||
ret = wait_for_completion_timeout(&bg_data->err_ready, |
||||
msecs_to_jiffies(10000)); |
||||
if (!ret) { |
||||
pr_err("[%s]: Error ready timed out\n", bg_data->desc.name); |
||||
return -ETIMEDOUT; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
/**
|
||||
* bg_powerup() - Called by SSR framework on userspace invocation. |
||||
* does load tz app and call peripheral loader. |
||||
* @subsys: struct containing private BG data. |
||||
* |
||||
* Return: 0 on success. Error code on failure. |
||||
*/ |
||||
static int bg_powerup(const struct subsys_desc *subsys) |
||||
{ |
||||
struct pil_bg_data *bg_data = subsys_to_data(subsys); |
||||
int ret; |
||||
|
||||
if (is_bg_running()) { |
||||
pr_debug("bg is already up\n"); |
||||
return 0; |
||||
} |
||||
|
||||
init_completion(&bg_data->err_ready); |
||||
if (!bg_data->qseecom_handle) { |
||||
ret = pil_load_bg_tzapp(bg_data); |
||||
if (ret) { |
||||
dev_err(bg_data->desc.dev, |
||||
"%s: BG TZ app load failure\n", |
||||
__func__); |
||||
return ret; |
||||
} |
||||
} |
||||
pr_debug("bgapp loaded\n"); |
||||
bg_data->desc.fw_name = subsys->fw_name; |
||||
|
||||
ret = devm_request_irq(bg_data->desc.dev, bg_data->status_irq, |
||||
bg_status_change, |
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
||||
"bg2ap_status", bg_data); |
||||
if (ret < 0) { |
||||
dev_err(bg_data->desc.dev, |
||||
"%s: BG2AP_STATUS IRQ#%d re registration failed, err=%d", |
||||
__func__, bg_data->status_irq, ret); |
||||
return ret; |
||||
} |
||||
|
||||
/* Enable status and err fatal irqs */ |
||||
ret = pil_boot(&bg_data->desc); |
||||
if (ret) { |
||||
dev_err(bg_data->desc.dev, |
||||
"%s: BG PIL Boot failed\n", __func__); |
||||
return ret; |
||||
} |
||||
ret = wait_for_err_ready(bg_data); |
||||
if (ret) { |
||||
dev_err(bg_data->desc.dev, |
||||
"[%s:%d]: Timed out waiting for error ready: %s!\n", |
||||
current->comm, current->pid, bg_data->desc.name); |
||||
return ret; |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
/**
|
||||
* bg_shutdown() - Called by SSR framework on userspace invocation. |
||||
* disable status interrupt to avoid spurious signal during PRM exit. |
||||
* @subsys: struct containing private BG data. |
||||
* @force_stop: unused |
||||
* |
||||
* Return: always success |
||||
*/ |
||||
static int bg_shutdown(const struct subsys_desc *subsys, bool force_stop) |
||||
{ |
||||
struct pil_bg_data *bg_data = subsys_to_data(subsys); |
||||
|
||||
if (bg_data->is_ready) { |
||||
disable_irq(bg_data->status_irq); |
||||
devm_free_irq(bg_data->desc.dev, bg_data->status_irq, bg_data); |
||||
disable_irq(bg_data->errfatal_irq); |
||||
bg_data->is_ready = false; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
/**
|
||||
* bg_auth_metadata() - Called by Peripheral loader framework |
||||
* send command to tz app for authentication of metadata. |
||||
* @pil: pil descriptor. |
||||
* @metadata: metadata load address |
||||
* @size: size of metadata |
||||
* |
||||
* Return: 0 on success. Error code on failure. |
||||
*/ |
||||
static int bg_auth_metadata(struct pil_desc *pil, |
||||
const u8 *metadata, size_t size) |
||||
{ |
||||
struct pil_bg_data *bg_data = desc_to_data(pil); |
||||
struct tzapp_bg_req bg_tz_req; |
||||
void *mdata_buf; |
||||
dma_addr_t mdata_phys; |
||||
unsigned long attrs = 0; |
||||
struct device dev = {0}; |
||||
int ret; |
||||
|
||||
arch_setup_dma_ops(&dev, 0, 0, NULL, 0); |
||||
|
||||
dev.coherent_dma_mask = DMA_BIT_MASK(sizeof(dma_addr_t) * 8); |
||||
attrs |= DMA_ATTR_STRONGLY_ORDERED; |
||||
mdata_buf = dma_alloc_attrs(&dev, size, |
||||
&mdata_phys, GFP_KERNEL, attrs); |
||||
|
||||
if (!mdata_buf) { |
||||
pr_err("BG_PIL: Allocation for metadata failed.\n"); |
||||
return -ENOMEM; |
||||
} |
||||
|
||||
/* Make sure there are no mappings in PKMAP and fixmap */ |
||||
kmap_flush_unused(); |
||||
kmap_atomic_flush_unused(); |
||||
|
||||
memcpy(mdata_buf, metadata, size); |
||||
|
||||
bg_tz_req.tzapp_bg_cmd = BGPIL_AUTH_MDT; |
||||
bg_tz_req.address_fw = (phys_addr_t)mdata_phys; |
||||
bg_tz_req.size_fw = size; |
||||
|
||||
ret = bgpil_tzapp_comm(bg_data, &bg_tz_req); |
||||
if (ret || bg_data->cmd_status) { |
||||
dev_err(pil->dev, |
||||
"%s: BGPIL_AUTH_MDT qseecom call failed\n", |
||||
__func__); |
||||
return bg_data->cmd_status; |
||||
} |
||||
dma_free_attrs(&dev, size, mdata_buf, mdata_phys, attrs); |
||||
pr_debug("BG MDT Authenticated\n"); |
||||
return 0; |
||||
} |
||||
|
||||
/**
|
||||
* bg_get_firmware_addr() - Called by Peripheral loader framework |
||||
* to get address and size of bg firmware binaries. |
||||
* @pil: pil descriptor. |
||||
* @addr: fw load address |
||||
* @size: size of fw |
||||
* |
||||
* Return: 0 on success. |
||||
*/ |
||||
static int bg_get_firmware_addr(struct pil_desc *pil, |
||||
phys_addr_t addr, size_t size) |
||||
{ |
||||
struct pil_bg_data *bg_data = desc_to_data(pil); |
||||
|
||||
bg_data->address_fw = addr; |
||||
bg_data->size_fw = size; |
||||
pr_debug("BG PIL loads firmware blobs at 0x%x with size 0x%x\n", |
||||
addr, size); |
||||
return 0; |
||||
} |
||||
|
||||
static int bg_get_version(const struct subsys_desc *subsys) |
||||
{ |
||||
struct pil_bg_data *bg_data = subsys_to_data(subsys); |
||||
struct pil_desc desc = bg_data->desc; |
||||
struct tzapp_bg_req bg_tz_req; |
||||
int ret; |
||||
struct device dev = {NULL}; |
||||
|
||||
arch_setup_dma_ops(&dev, 0, 0, NULL, 0); |
||||
|
||||
desc.attrs = 0; |
||||
desc.attrs |= DMA_ATTR_SKIP_ZEROING; |
||||
desc.attrs |= DMA_ATTR_STRONGLY_ORDERED; |
||||
|
||||
|
||||
bg_tz_req.tzapp_bg_cmd = BGPIL_GET_BG_VERSION; |
||||
|
||||
ret = bgpil_tzapp_comm(bg_data, &bg_tz_req); |
||||
if (ret || bg_data->cmd_status) { |
||||
dev_dbg(desc.dev, "%s: BG PIL get BG version failed error %d\n", |
||||
__func__, bg_data->cmd_status); |
||||
return bg_data->cmd_status; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/**
|
||||
* bg_auth_and_xfer() - Called by Peripheral loader framework |
||||
* to signal tz app to authenticate and boot bg chip. |
||||
* @pil: pil descriptor. |
||||
* |
||||
* Return: 0 on success. Error code on failure. |
||||
*/ |
||||
static int bg_auth_and_xfer(struct pil_desc *pil) |
||||
{ |
||||
struct pil_bg_data *bg_data = desc_to_data(pil); |
||||
struct tzapp_bg_req bg_tz_req; |
||||
int ret; |
||||
|
||||
bg_tz_req.tzapp_bg_cmd = BGPIL_IMAGE_LOAD; |
||||
bg_tz_req.address_fw = bg_data->address_fw; |
||||
bg_tz_req.size_fw = bg_data->size_fw; |
||||
|
||||
ret = bgpil_tzapp_comm(bg_data, &bg_tz_req); |
||||
if (bg_data->cmd_status == BG_CRASH_IN_TWM) { |
||||
/* Do ramdump and resend boot cmd */ |
||||
if (is_twm_exit()) |
||||
bg_data->subsys_desc.ramdump(true, |
||||
&bg_data->subsys_desc); |
||||
|
||||
bg_tz_req.tzapp_bg_cmd = BGPIL_DLOAD_CONT; |
||||
ret = bgpil_tzapp_comm(bg_data, &bg_tz_req); |
||||
} |
||||
if (ret || bg_data->cmd_status) { |
||||
dev_err(pil->dev, |
||||
"%s: BGPIL_IMAGE_LOAD qseecom call failed\n", |
||||
__func__); |
||||
pil_free_memory(&bg_data->desc); |
||||
return bg_data->cmd_status; |
||||
} |
||||
ret = bg_get_version(&bg_data->subsys_desc); |
||||
/* BG Transfer of image is complete, free up the memory */ |
||||
pr_debug("BG Firmware authentication and transfer done\n"); |
||||
pil_free_memory(&bg_data->desc); |
||||
return 0; |
||||
} |
||||
|
||||
/**
|
||||
* bg_ramdump() - Called by SSR framework to save dump of BG internal |
||||
* memory, BG PIL does allocate region from dynamic memory and pass this |
||||
* region to tz to dump memory content of BG. |
||||
* @subsys: subsystem descriptor. |
||||
* |
||||
* Return: 0 on success. Error code on failure. |
||||
*/ |
||||
static int bg_ramdump(int enable, const struct subsys_desc *subsys) |
||||
{ |
||||
struct pil_bg_data *bg_data = subsys_to_data(subsys); |
||||
struct pil_desc desc = bg_data->desc; |
||||
struct ramdump_segment *ramdump_segments; |
||||
struct tzapp_bg_req bg_tz_req; |
||||
phys_addr_t start_addr; |
||||
void *region; |
||||
int ret; |
||||
struct device dev = {0}; |
||||
|
||||
arch_setup_dma_ops(&dev, 0, 0, NULL, 0); |
||||
|
||||
desc.attrs = 0; |
||||
desc.attrs |= DMA_ATTR_SKIP_ZEROING; |
||||
desc.attrs |= DMA_ATTR_STRONGLY_ORDERED; |
||||
|
||||
region = dma_alloc_attrs(desc.dev, BG_RAMDUMP_SZ, |
||||
&start_addr, GFP_KERNEL, desc.attrs); |
||||
|
||||
if (region == NULL) { |
||||
dev_dbg(desc.dev, |
||||
"BG PIL failure to allocate ramdump region of size %zx\n", |
||||
BG_RAMDUMP_SZ); |
||||
return -ENOMEM; |
||||
} |
||||
|
||||
ramdump_segments = kcalloc(1, sizeof(*ramdump_segments), GFP_KERNEL); |
||||
if (!ramdump_segments) |
||||
return -ENOMEM; |
||||
|
||||
bg_tz_req.tzapp_bg_cmd = BGPIL_RAMDUMP; |
||||
bg_tz_req.address_fw = start_addr; |
||||
bg_tz_req.size_fw = BG_RAMDUMP_SZ; |
||||
|
||||
ret = bgpil_tzapp_comm(bg_data, &bg_tz_req); |
||||
if (ret || bg_data->cmd_status) { |
||||
dev_dbg(desc.dev, "%s: BG PIL ramdump collection failed\n", |
||||
__func__); |
||||
return bg_data->cmd_status; |
||||
} |
||||
|
||||
ramdump_segments->address = start_addr; |
||||
ramdump_segments->size = BG_RAMDUMP_SZ; |
||||
|
||||
do_ramdump(bg_data->ramdump_dev, ramdump_segments, 1); |
||||
kfree(ramdump_segments); |
||||
dma_free_attrs(desc.dev, BG_RAMDUMP_SZ, region, |
||||
start_addr, desc.attrs); |
||||
return 0; |
||||
} |
||||
|
||||
static struct pil_reset_ops pil_ops_trusted = { |
||||
.init_image = bg_auth_metadata, |
||||
.mem_setup = bg_get_firmware_addr, |
||||
.auth_and_reset = bg_auth_and_xfer, |
||||
.shutdown = NULL, |
||||
.proxy_vote = NULL, |
||||
.proxy_unvote = NULL, |
||||
}; |
||||
|
||||
/**
|
||||
* bg_restart_work() - scheduled by interrupt handler to carry |
||||
* out ssr sequence |
||||
* @work: work struct. |
||||
* |
||||
* Return: none. |
||||
*/ |
||||
static void bg_restart_work(struct work_struct *work) |
||||
{ |
||||
struct pil_bg_data *drvdata = |
||||
container_of(work, struct pil_bg_data, restart_work); |
||||
subsystem_restart_dev(drvdata->subsys); |
||||
} |
||||
|
||||
static irqreturn_t bg_errfatal(int irq, void *dev_id) |
||||
{ |
||||
struct pil_bg_data *drvdata = (struct pil_bg_data *)dev_id; |
||||
|
||||
if (!drvdata) |
||||
return IRQ_HANDLED; |
||||
|
||||
dev_dbg(drvdata->desc.dev, "BG s/w err fatal\n"); |
||||
|
||||
queue_work(drvdata->bg_queue, &drvdata->restart_work); |
||||
|
||||
return IRQ_HANDLED; |
||||
} |
||||
|
||||
static irqreturn_t bg_status_change(int irq, void *dev_id) |
||||
{ |
||||
bool value; |
||||
struct pil_bg_data *drvdata = (struct pil_bg_data *)dev_id; |
||||
|
||||
if (!drvdata) |
||||
return IRQ_HANDLED; |
||||
|
||||
value = gpio_get_value(drvdata->gpios[0]); |
||||
if (value == true && !drvdata->is_ready) { |
||||
dev_info(drvdata->desc.dev, |
||||
"BG services are up and running: irq state changed 0->1\n"); |
||||
drvdata->is_ready = true; |
||||
complete(&drvdata->err_ready); |
||||
} else if (value == false && drvdata->is_ready) { |
||||
dev_err(drvdata->desc.dev, |
||||
"BG got unexpected reset: irq state changed 1->0\n"); |
||||
queue_work(drvdata->bg_queue, &drvdata->restart_work); |
||||
} else { |
||||
dev_err(drvdata->desc.dev, |
||||
"BG status irq: unknown status\n"); |
||||
} |
||||
|
||||
return IRQ_HANDLED; |
||||
} |
||||
|
||||
/**
|
||||
* setup_bg_gpio_irq() - called in probe to configure input/ |
||||
* output gpio. |
||||
* @drvdata: private data struct for BG. |
||||
* |
||||
* Return: 0 on success. Error code on failure. |
||||
*/ |
||||
static int setup_bg_gpio_irq(struct platform_device *pdev, |
||||
struct pil_bg_data *drvdata) |
||||
{ |
||||
int ret = -1; |
||||
int irq, i; |
||||
|
||||
if (gpio_request(drvdata->gpios[0], "BG2AP_STATUS")) { |
||||
dev_err(&pdev->dev, |
||||
"%s Failed to configure BG2AP_STATUS gpio\n", |
||||
__func__); |
||||
goto err; |
||||
} |
||||
if (gpio_request(drvdata->gpios[1], "BG2AP_ERRFATAL")) { |
||||
dev_err(&pdev->dev, |
||||
"%s Failed to configure BG2AP_ERRFATAL gpio\n", |
||||
__func__); |
||||
goto err; |
||||
} |
||||
gpio_direction_input(drvdata->gpios[0]); |
||||
gpio_direction_input(drvdata->gpios[1]); |
||||
/* BG2AP STATUS IRQ */ |
||||
irq = gpio_to_irq(drvdata->gpios[0]); |
||||
if (irq < 0) { |
||||
dev_err(&pdev->dev, |
||||
"%s: bad BG2AP_STATUS IRQ resource, err = %d\n", |
||||
__func__, irq); |
||||
goto err; |
||||
} |
||||
|
||||
drvdata->status_irq = irq; |
||||
/* BG2AP ERR_FATAL irq. */ |
||||
irq = gpio_to_irq(drvdata->gpios[1]); |
||||
if (irq < 0) { |
||||
dev_err(&pdev->dev, "bad BG2AP_ERRFATAL IRQ resource\n"); |
||||
goto err; |
||||
} |
||||
ret = request_irq(irq, bg_errfatal, |
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT, "bg2ap_errfatal", drvdata); |
||||
if (ret < 0) { |
||||
dev_err(&pdev->dev, |
||||
"%s: BG2AP_ERRFATAL IRQ#%d request failed,\n", |
||||
__func__, irq); |
||||
goto err; |
||||
} |
||||
drvdata->errfatal_irq = irq; |
||||
/* Configure outgoing GPIO's */ |
||||
if (gpio_request(drvdata->gpios[2], "AP2BG_ERRFATAL")) { |
||||
dev_err(&pdev->dev, |
||||
"%s Failed to configure AP2BG_ERRFATAL gpio\n", |
||||
__func__); |
||||
goto err; |
||||
} |
||||
if (gpio_request(drvdata->gpios[3], "AP2BG_STATUS")) { |
||||
dev_err(&pdev->dev, |
||||
"%s Failed to configure AP2BG_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[2], 0); |
||||
gpio_direction_output(drvdata->gpios[3], 1); |
||||
/* Inform BG that AP is up */ |
||||
gpio_set_value(drvdata->gpios[3], 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; |
||||
} |
||||
|
||||
/**
|
||||
* bg_dt_parse_gpio() - called in probe to parse gpio's |
||||
* @drvdata: private data struct for BG. |
||||
* |
||||
* Return: 0 on success. Error code on failure. |
||||
*/ |
||||
static int bg_dt_parse_gpio(struct platform_device *pdev, |
||||
struct pil_bg_data *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,bg2ap-status-gpio", 0); |
||||
if (val >= 0) |
||||
drvdata->gpios[0] = val; |
||||
else { |
||||
pr_err("BG status gpio not found, error=%d\n", val); |
||||
return -EINVAL; |
||||
} |
||||
val = of_get_named_gpio(pdev->dev.of_node, |
||||
"qcom,bg2ap-errfatal-gpio", 0); |
||||
if (val >= 0) |
||||
drvdata->gpios[1] = val; |
||||
else { |
||||
pr_err("BG err-fatal gpio not found, error=%d\n", val); |
||||
return -EINVAL; |
||||
} |
||||
val = of_get_named_gpio(pdev->dev.of_node, |
||||
"qcom,ap2bg-errfatal-gpio", 0); |
||||
if (val >= 0) |
||||
drvdata->gpios[2] = val; |
||||
else { |
||||
pr_err("ap2bg err-fatal gpio not found, error=%d\n", val); |
||||
return -EINVAL; |
||||
} |
||||
val = of_get_named_gpio(pdev->dev.of_node, |
||||
"qcom,ap2bg-status-gpio", 0); |
||||
if (val >= 0) |
||||
drvdata->gpios[3] = val; |
||||
else { |
||||
pr_err("ap2bg status gpio not found, error=%d\n", val); |
||||
return -EINVAL; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
static int pil_bg_driver_probe(struct platform_device *pdev) |
||||
{ |
||||
struct pil_bg_data *bg_data; |
||||
int rc; |
||||
|
||||
bg_data = devm_kzalloc(&pdev->dev, sizeof(*bg_data), GFP_KERNEL); |
||||
if (!bg_data) |
||||
return -ENOMEM; |
||||
platform_set_drvdata(pdev, bg_data); |
||||
rc = of_property_read_string(pdev->dev.of_node, |
||||
"qcom,firmware-name", &bg_data->desc.name); |
||||
if (rc) |
||||
return rc; |
||||
bg_data->desc.dev = &pdev->dev; |
||||
bg_data->desc.owner = THIS_MODULE; |
||||
bg_data->desc.ops = &pil_ops_trusted; |
||||
rc = pil_desc_init(&bg_data->desc); |
||||
if (rc) |
||||
return rc; |
||||
/* Read gpio configuration */ |
||||
rc = bg_dt_parse_gpio(pdev, bg_data); |
||||
if (rc) |
||||
return rc; |
||||
rc = setup_bg_gpio_irq(pdev, bg_data); |
||||
if (rc < 0) |
||||
return rc; |
||||
bg_data->subsys_desc.name = bg_data->desc.name; |
||||
bg_data->subsys_desc.owner = THIS_MODULE; |
||||
bg_data->subsys_desc.dev = &pdev->dev; |
||||
bg_data->subsys_desc.shutdown = bg_shutdown; |
||||
bg_data->subsys_desc.powerup = bg_powerup; |
||||
bg_data->subsys_desc.ramdump = bg_ramdump; |
||||
bg_data->subsys_desc.free_memory = NULL; |
||||
bg_data->subsys_desc.crash_shutdown = bg_app_shutdown_notify; |
||||
bg_data->ramdump_dev = |
||||
create_ramdump_device(bg_data->subsys_desc.name, &pdev->dev); |
||||
if (!bg_data->ramdump_dev) { |
||||
rc = -ENOMEM; |
||||
goto err_ramdump; |
||||
} |
||||
bg_data->subsys = subsys_register(&bg_data->subsys_desc); |
||||
if (IS_ERR(bg_data->subsys)) { |
||||
rc = PTR_ERR(bg_data->subsys); |
||||
goto err_subsys; |
||||
} |
||||
|
||||
bg_data->reboot_blk.notifier_call = bg_app_reboot_notify; |
||||
register_reboot_notifier(&bg_data->reboot_blk); |
||||
|
||||
bg_data->bg_queue = alloc_workqueue("bg_queue", 0, 0); |
||||
if (!bg_data->bg_queue) { |
||||
dev_err(&pdev->dev, "could not create bg_queue\n"); |
||||
subsys_unregister(bg_data->subsys); |
||||
goto err_subsys; |
||||
} |
||||
INIT_WORK(&bg_data->restart_work, bg_restart_work); |
||||
return 0; |
||||
err_subsys: |
||||
destroy_ramdump_device(bg_data->ramdump_dev); |
||||
err_ramdump: |
||||
pil_desc_release(&bg_data->desc); |
||||
return rc; |
||||
} |
||||
|
||||
static int pil_bg_driver_exit(struct platform_device *pdev) |
||||
{ |
||||
struct pil_bg_data *bg_data = platform_get_drvdata(pdev); |
||||
|
||||
subsys_unregister(bg_data->subsys); |
||||
destroy_ramdump_device(bg_data->ramdump_dev); |
||||
pil_desc_release(&bg_data->desc); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
const struct of_device_id pil_bg_match_table[] = { |
||||
{.compatible = "qcom,pil-blackghost"}, |
||||
{} |
||||
}; |
||||
|
||||
static struct platform_driver pil_bg_driver = { |
||||
.probe = pil_bg_driver_probe, |
||||
.remove = pil_bg_driver_exit, |
||||
.driver = { |
||||
.name = "subsys-pil-bg", |
||||
.of_match_table = pil_bg_match_table, |
||||
.owner = THIS_MODULE, |
||||
}, |
||||
}; |
||||
|
||||
static int __init pil_bg_init(void) |
||||
{ |
||||
return platform_driver_register(&pil_bg_driver); |
||||
} |
||||
module_init(pil_bg_init); |
||||
|
||||
static void __exit pil_bg_exit(void) |
||||
{ |
||||
platform_driver_unregister(&pil_bg_driver); |
||||
} |
||||
module_exit(pil_bg_exit); |
||||
|
||||
MODULE_DESCRIPTION("Support for booting QTI Blackghost SoC"); |
||||
MODULE_LICENSE("GPL v2"); |
Loading…
Reference in new issue