drivers: soc: qcom: Add bg pil driver to 4.14

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
Archit Saxena 5 years ago
parent ec8d33c43e
commit 31c8fd0a1a
  1. 33
      Documentation/devicetree/bindings/pil/pil-blackghost.txt
  2. 8
      drivers/soc/qcom/Kconfig
  3. 1
      drivers/soc/qcom/Makefile
  4. 45
      drivers/soc/qcom/pil_bg_intf.h
  5. 840
      drivers/soc/qcom/subsys-pil-bg.c

@ -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>;
};

@ -1022,6 +1022,14 @@ config MSM_BAM_DMUX
communication between G-Link/bg_com_dev and BG processor over SPI.
This handle the interrupts raised by BG and notify the G-link with
interrupt event and event data.
config MSM_PIL_SSR_BG
tristate "MSM Subsystem Blackghost(BG) Support"
depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
help
Support for booting and shutting down Blackghost(BG) SOC which is
an external SOC. This driver communicates with Blackghost(BG) SOC
via pair of IPC GPIOs for inward and outward signals between MSM
and Blackghost(BG) SOC.
config QCOM_SOC_INFO
bool "Chip information for QTI SoCs"

@ -69,6 +69,7 @@ obj-$(CONFIG_MSM_JTAGV8) += jtagv8.o jtagv8-etm.o
obj-$(CONFIG_MSM_CDSP_LOADER) += qdsp6v2/
obj-$(CONFIG_QCOM_SMCINVOKE) += smcinvoke.o
obj-$(CONFIG_SDX_EXT_IPC) += sdx_ext_ipc.o
obj-$(CONFIG_MSM_PIL_SSR_BG) += subsys-pil-bg.o
ifdef CONFIG_MSM_SUBSYSTEM_RESTART
obj-y += subsystem_notif.o

@ -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…
Cancel
Save