Merge "msm: subsys-pil-tz: Introduce generic TZ PIL and SSR driver"

This commit is contained in:
Linux Build Service Account 2014-01-09 06:21:51 -08:00 committed by Gerrit - the friendly Code Review server
commit 8b4c489deb
4 changed files with 864 additions and 0 deletions

View File

@ -0,0 +1,108 @@
* Generic Subsystem Peripheral Image Loader
subsys-pil-tz is a generic peripheral image loader (PIL) driver. It is
used for loading the firmware images of the subsystems into memory and
preparing the subsystem's processor to execute code. It's also responsible
for shutting down the processor when it's not needed.
Required properties:
- compatible: Must be "pil-venus" or "pil-pronto" or "pil-bcss" or
"pil-vpu" or "qcom,pil-q6v5-lpass" or "qcom,pil-q6v55-lpass"
- qcom,firmware-name: Base name of the firmware image.
Optional properties:
- reg: Pairs of physical base addresses and region sizes of
memory mapped registers.
- reg-names: Names of the bases for the above registers.
- interrupts: Subsystem to Apps watchdog bite interrupt.
- vdd_'reg'-supply: Reference to the regulator that supplies the corresponding
'reg' domain.
- proxy-reg-names: Names of the regulators that need to be turned on/off
during proxy voting/unvoting.
- active-reg-names: Names of the regulators that need to be turned on for the
subsystem to run. Turned off when the subsystem is shutdown.
- vdd_'reg'-uV-uA: Voltage and current values for the 'reg' regulator.
- proxy-clock-names: Names of the clocks that need to be turned on/off during
proxy voting/unvoting.
- active-clock-names: Names of the clocks that need to be turned on for the
subsystem to run. Turned off when the subsystem is shutdown.
- clock-names: Names of all the clocks that are accessed by the subsystem.
- qcom,msm-bus,name: Name of the bus client for the subsystem.
- qcom,msm-bus,num-cases: Number of use-cases.
- qcom,msm-bus,num-paths: Number of paths.
- qcom,msm-bus,active-only: If not set, uses the dual context by default.
- qcom,msm-bus,vectors-KBps: Vector array of master id, slave id, arbitrated
bandwidth and instantaneous bandwidth.
- qcom,pas-id: pas_id of the subsystem.
- qcom,proxy-timeout-ms: Proxy vote timeout value for the subsystem.
- qcom,smem-id: ID of the SMEM item for the subsystem.
- qcom,is-not-loadable: Boolean- Present if the image does not need to
be loaded.
- qcom,pil-self-auth: Boolean- True if authentication is required.
- qcom,gpio-err-fatal: GPIO used by the modem to indicate error fatal to the apps.
- qcom,gpio-err-ready: GPIO used by the modem to indicate error ready to the apps.
- qcom,gpio-proxy-unvote: GPIO used by the modem to trigger proxy unvoting in
the apps.
- qcom,gpio-force-stop: GPIO used by the apps to force the modem to shutdown.
- qcom,gpio-stop-ack: GPIO used by the modem to ack force stop or a graceful stop
to the apps.
- qcom,restart-group: List of subsystems that will need to restart together.
Example:
qcom,venus@fdce0000 {
compatible = "qcom,pil-venus";
reg = <0xfdce0000 0x4000>,
<0xfdc80000 0x400>;
reg-names = "wrapper_base", "vbif_base";
vdd-supply = <&gdsc_venus>;
proxy-reg-names = "vdd";
clock-names = "core_clk", "iface_clk",
"bus_clk", "mem_clk";
proxy-clock-names = "core_clk", "iface_clk",
"bus_clk", "mem_clk";
qcom,msm-bus,name = "pil-venus";
qcom,msm-bus,num-cases = <2>;
qcom,msm-bus,num-paths = <1>;
qcom,msm-bus,active-only = <0>;
qcom,msm-bus,vectors-KBps =
<63 512 0 0>,
<63 512 0 304000>;
qcom,pas-id = <9>;
qcom,proxy-timeout-ms = <2000>;
qcom,firmware-name = "venus";
};
qcom,lpass@fe200000 {
compatible = "qcom,pil-q6v5-lpass";
reg = <0xfe200000 0x00100>,
<0xfd485100 0x00010>,
<0xfc4016c0 0x00004>;
reg-names = "qdsp6_base", "halt_base", "restart_reg";
vdd_cx-supply = <&pm8841_s2_corner>;
interrupts = <0 162 1>;
vdd_cx-supply = <&pm8841_s2_corner>;
proxy-reg-names = "vdd_cx";
vdd_cx-uV-uA = <7 100000>;
clock-names = "bus_clk", "xo";
active-clock-names = "bus_clk";
proxy-clock-names = "xo";
qcom,smem-id = <423>;
qcom,pas-id = <1>;
qcom,proxy-timeout-ms = <10000>;
qcom,firmware-name = "adsp";
/* GPIO inputs from lpass */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_2_in 0 0>;
qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_2_in 2 0>;
qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_2_in 1 0>;
qcom,gpio-stop-ack = <&smp2pgpio_ssr_smp2p_2_in 3 0>;
/* GPIO output to lpass */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_2_out 0 0>;
};

View File

@ -985,6 +985,16 @@ config MSM_PIL
Say yes to support these devices.
config MSM_PIL_SSR_GENERIC
tristate "MSM Subsystem Boot Support"
depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
help
Support for booting and shutting down MSM Subsystem processors.
This driver also monitors the SMSM status bits and the watchdog
interrupt for the subsystem and restarts it on a watchdog bite
or a fatal error. Subsystems include LPASS, Venus, VPU, WCNSS and
BCSS.
config MSM_PIL_LPASS_QDSP6V5
tristate "LPASS QDSP6v5 (Hexagon) Boot Support"
depends on MSM_PIL && MSM_SUBSYSTEM_RESTART

View File

@ -37,6 +37,7 @@ obj-$(CONFIG_MSM_SMP2P_TEST) += smp2p_loopback.o smp2p_test.o smp2p_gpio_test.o
obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o
obj-$(CONFIG_MSM_XPU_ERR_FATAL) += scm-xpu.o
obj-$(CONFIG_MSM_PIL) += peripheral-loader.o
obj-$(CONFIG_MSM_PIL_SSR_GENERIC) += subsys-pil-tz.o
obj-$(CONFIG_MSM_PIL) += scm-pas.o
obj-$(CONFIG_MSM_PIL_LPASS_QDSP6V5) += pil-q6v5.o pil-q6v5-lpass.o
obj-$(CONFIG_MSM_PIL_MSS_QDSP6V5) += pil-q6v5.o pil-msa.o pil-q6v5-mss.o

View File

@ -0,0 +1,745 @@
/* Copyright (c) 2014, 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/clk.h>
#include <linux/regulator/consumer.h>
#include <linux/interrupt.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <mach/msm_bus_board.h>
#include <mach/msm_bus.h>
#include <mach/subsystem_restart.h>
#include <mach/ramdump.h>
#include <soc/qcom/smem.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
#define XO_FREQ 19200000
#define PROXY_TIMEOUT_MS 10000
#define MAX_SSR_REASON_LEN 81U
#define STOP_ACK_TIMEOUT_MS 1000
#define desc_to_data(d) container_of(d, struct pil_tz_data, desc)
#define subsys_to_data(d) container_of(d, struct pil_tz_data, subsys_desc)
/**
* struct reg_info - regulator info
* @reg: regulator handle
* @uV: voltage in uV
* @uA: current in uA
*/
struct reg_info {
struct regulator *reg;
int uV;
int uA;
};
/**
* struct pil_tz_data
* @regs: regulators that should be always on when the subsystem is
* brought out of reset
* @proxy_regs: regulators that should be on during pil proxy voting
* @clks: clocks that should be always on when the subsystem is
* brought out of reset
* @proxy_clks: clocks that should be on during pil proxy voting
* @reg_count: the number of always on regulators
* @proxy_reg_count: the number of proxy voting regulators
* @clk_count: the number of always on clocks
* @proxy_clk_count: the number of proxy voting clocks
* @smem_id: the smem id used for read the subsystem crash reason
* @ramdump_dev: ramdump device pointer
* @pas_id: the PAS id for tz
* @bus_client: bus client id
* @stop_ack: state of completion of stop ack
* @desc: PIL descriptor
* @subsys: subsystem device pointer
* @subsys_desc: subsystem descriptor
*/
struct pil_tz_data {
struct reg_info *regs;
struct reg_info *proxy_regs;
struct clk **clks;
struct clk **proxy_clks;
int reg_count;
int proxy_reg_count;
int clk_count;
int proxy_clk_count;
int smem_id;
void *ramdump_dev;
u32 pas_id;
u32 bus_client;
struct completion stop_ack;
struct pil_desc desc;
struct subsys_device *subsys;
struct subsys_desc subsys_desc;
};
static int of_read_clocks(struct device *dev, struct clk ***clks_ref,
const char *propname)
{
int clk_count, i, len;
struct clk **clks;
if (!of_find_property(dev->of_node, propname, &len))
return 0;
clk_count = of_property_count_strings(dev->of_node, propname);
if (IS_ERR_VALUE(clk_count)) {
dev_err(dev, "Failed to get clock names\n");
return -EINVAL;
}
clks = devm_kzalloc(dev, sizeof(struct clk *) * clk_count,
GFP_KERNEL);
if (!clks)
return -ENOMEM;
for (i = 0; i < clk_count; i++) {
const char *clock_name;
of_property_read_string_index(dev->of_node,
propname, i,
&clock_name);
clks[i] = devm_clk_get(dev, clock_name);
if (IS_ERR(clks[i])) {
int rc = PTR_ERR(clks[i]);
if (rc != -EPROBE_DEFER)
dev_err(dev, "Failed to get %s clock\n",
clock_name);
return rc;
}
/* Make sure rate-settable clocks' rates are set */
if (clk_get_rate(clks[i]) == 0)
clk_set_rate(clks[i], clk_round_rate(clks[i],
XO_FREQ));
}
*clks_ref = clks;
return clk_count;
}
static int of_read_regs(struct device *dev, struct reg_info **regs_ref,
const char *propname)
{
int reg_count, i, len, rc;
struct reg_info *regs;
if (!of_find_property(dev->of_node, propname, &len))
return 0;
reg_count = of_property_count_strings(dev->of_node, propname);
if (IS_ERR_VALUE(reg_count)) {
dev_err(dev, "Failed to get regulator names\n");
return -EINVAL;
}
regs = devm_kzalloc(dev, sizeof(struct reg_info) * reg_count,
GFP_KERNEL);
if (!regs)
return -ENOMEM;
for (i = 0; i < reg_count; i++) {
const char *reg_name;
char reg_uV_uA_name[50];
u32 vdd_uV_uA[2];
of_property_read_string_index(dev->of_node,
propname, i,
&reg_name);
regs[i].reg = devm_regulator_get(dev, reg_name);
if (IS_ERR(regs[i].reg)) {
int rc = PTR_ERR(regs[i].reg);
if (rc != -EPROBE_DEFER)
dev_err(dev, "Failed to get %s\n regulator",
reg_name);
return rc;
}
/*
* Read the voltage and current values for the corresponding
* regulator. The device tree property name is "regulator_name"
* + "-uV-uA".
*/
rc = snprintf(reg_uV_uA_name, ARRAY_SIZE(reg_uV_uA_name),
"%s-uV-uA", reg_name);
if (rc < strlen(reg_name) + 6) {
dev_err(dev, "Failed to hold reg_uV_uA_name\n");
return -EINVAL;
}
if (!of_find_property(dev->of_node, reg_uV_uA_name, &len))
continue;
len /= sizeof(vdd_uV_uA[0]);
/* There should be two entries: one for uV and one for uA */
if (len != 2) {
dev_err(dev, "Missing uV/uA value\n");
return -EINVAL;
}
rc = of_property_read_u32_array(dev->of_node, reg_uV_uA_name,
vdd_uV_uA, len);
if (rc) {
dev_err(dev, "Failed to read uV/uA values\n");
return rc;
}
regs[i].uV = vdd_uV_uA[0];
regs[i].uA = vdd_uV_uA[1];
}
*regs_ref = regs;
return reg_count;
}
static int of_read_bus_pdata(struct platform_device *pdev,
struct pil_tz_data *d)
{
struct msm_bus_scale_pdata *pdata;
pdata = msm_bus_cl_get_pdata(pdev);
if (!pdata)
return -EINVAL;
d->bus_client = msm_bus_scale_register_client(pdata);
if (!d->bus_client)
return -EINVAL;
return 0;
}
static int piltz_resc_init(struct platform_device *pdev, struct pil_tz_data *d)
{
int len, count, rc;
struct device *dev = &pdev->dev;
count = of_read_clocks(dev, &d->clks, "active-clock-names");
if (count < 0) {
dev_err(dev, "Failed to setup clocks.\n");
return count;
}
d->clk_count = count;
count = of_read_clocks(dev, &d->proxy_clks, "proxy-clock-names");
if (count < 0) {
dev_err(dev, "Failed to setup proxy clocks.\n");
return count;
}
d->proxy_clk_count = count;
count = of_read_regs(dev, &d->regs, "active-reg-names");
if (count < 0) {
dev_err(dev, "Failed to setup regulators.\n");
return count;
}
d->reg_count = count;
count = of_read_regs(dev, &d->proxy_regs, "proxy-reg-names");
if (count < 0) {
dev_err(dev, "Failed to setup proxy regulators.\n");
return count;
}
d->proxy_reg_count = count;
if (of_find_property(dev->of_node, "qcom,msm-bus,name", &len)) {
rc = of_read_bus_pdata(pdev, d);
if (rc) {
dev_err(dev, "Failed to setup bus scaling client.\n");
return rc;
}
}
return 0;
}
static int enable_regulators(struct device *dev, struct reg_info *regs,
int reg_count)
{
int i, rc = 0;
for (i = 0; i < reg_count; i++) {
if (regs[i].uV > 0) {
rc = regulator_set_voltage(regs[i].reg,
regs[i].uV, INT_MAX);
if (rc) {
dev_err(dev, "Failed to request voltage.\n");
goto err_voltage;
}
}
if (regs[i].uA > 0) {
rc = regulator_set_optimum_mode(regs[i].reg,
regs[i].uA);
if (rc < 0) {
dev_err(dev, "Failed to set regulator mode\n");
goto err_mode;
}
}
rc = regulator_enable(regs[i].reg);
if (rc) {
dev_err(dev, "Regulator enable failed\n");
goto err_enable;
}
}
return 0;
err_enable:
if (regs[i].uA > 0) {
regulator_set_voltage(regs[i].reg, 0, INT_MAX);
regulator_set_optimum_mode(regs[i].reg, 0);
}
err_mode:
if (regs[i].uV > 0)
regulator_set_voltage(regs[i].reg, 0, INT_MAX);
err_voltage:
for (i--; i >= 0; i--) {
if (regs[i].uV > 0)
regulator_set_voltage(regs[i].reg, 0, INT_MAX);
if (regs[i].uA > 0)
regulator_set_optimum_mode(regs[i].reg, 0);
regulator_disable(regs[i].reg);
}
return rc;
}
static void disable_regulators(struct reg_info *regs, int reg_count)
{
int i;
for (i = 0; i < reg_count; i++) {
if (regs[i].uV > 0)
regulator_set_voltage(regs[i].reg, 0, INT_MAX);
if (regs[i].uA > 0)
regulator_set_optimum_mode(regs[i].reg, 0);
regulator_disable(regs[i].reg);
}
}
static int prepare_enable_clocks(struct device *dev, struct clk **clks,
int clk_count)
{
int rc = 0;
int i;
for (i = 0; i < clk_count; i++) {
rc = clk_prepare_enable(clks[i]);
if (rc) {
dev_err(dev, "Clock enable failed\n");
goto err;
}
}
return 0;
err:
for (i--; i >= 0; i--)
clk_disable_unprepare(clks[i]);
return rc;
}
static void disable_unprepare_clocks(struct clk **clks, int clk_count)
{
int i;
for (i = 0; i < clk_count; i++)
clk_disable_unprepare(clks[i]);
}
static int pil_make_proxy_vote(struct pil_desc *pil)
{
struct pil_tz_data *d = desc_to_data(pil);
int rc;
rc = enable_regulators(pil->dev, d->proxy_regs, d->proxy_reg_count);
if (rc)
return rc;
rc = prepare_enable_clocks(pil->dev, d->proxy_clks,
d->proxy_clk_count);
if (rc)
goto err_clks;
if (d->bus_client) {
rc = msm_bus_scale_client_update_request(d->bus_client, 1);
if (rc) {
dev_err(pil->dev, "bandwidth request failed\n");
goto err_bw;
}
}
return 0;
err_bw:
disable_unprepare_clocks(d->proxy_clks, d->proxy_clk_count);
err_clks:
disable_regulators(d->proxy_regs, d->proxy_reg_count);
return rc;
}
static void pil_remove_proxy_vote(struct pil_desc *pil)
{
struct pil_tz_data *d = desc_to_data(pil);
if (d->bus_client)
msm_bus_scale_client_update_request(d->bus_client, 0);
disable_unprepare_clocks(d->proxy_clks, d->proxy_clk_count);
disable_regulators(d->proxy_regs, d->proxy_reg_count);
}
static int pil_init_image_trusted(struct pil_desc *pil,
const u8 *metadata, size_t size)
{
struct pil_tz_data *d = desc_to_data(pil);
return pas_init_image(d->pas_id, metadata, size);
}
static int pil_mem_setup_trusted(struct pil_desc *pil, phys_addr_t addr,
size_t size)
{
struct pil_tz_data *d = desc_to_data(pil);
return pas_mem_setup(d->pas_id, addr, size);
}
static int pil_auth_and_reset(struct pil_desc *pil)
{
struct pil_tz_data *d = desc_to_data(pil);
int rc;
rc = enable_regulators(pil->dev, d->regs, d->reg_count);
if (rc)
return rc;
rc = prepare_enable_clocks(pil->dev, d->clks, d->clk_count);
if (rc)
goto err_clks;
rc = pas_auth_and_reset(d->pas_id);
if (rc)
goto err_reset;
return 0;
err_reset:
disable_unprepare_clocks(d->clks, d->clk_count);
err_clks:
disable_regulators(d->regs, d->reg_count);
return rc;
}
static int pil_shutdown_trusted(struct pil_desc *pil)
{
struct pil_tz_data *d = desc_to_data(pil);
int rc;
rc = prepare_enable_clocks(pil->dev, d->proxy_clks,
d->proxy_clk_count);
if (rc)
return rc;
rc = pas_shutdown(d->pas_id);
disable_unprepare_clocks(d->proxy_clks, d->proxy_clk_count);
disable_unprepare_clocks(d->clks, d->clk_count);
disable_regulators(d->regs, d->reg_count);
return rc;
}
static struct pil_reset_ops pil_ops_trusted = {
.init_image = pil_init_image_trusted,
.mem_setup = pil_mem_setup_trusted,
.auth_and_reset = pil_auth_and_reset,
.shutdown = pil_shutdown_trusted,
.proxy_vote = pil_make_proxy_vote,
.proxy_unvote = pil_remove_proxy_vote,
};
static void log_failure_reason(const struct pil_tz_data *d)
{
u32 size;
char *smem_reason, reason[MAX_SSR_REASON_LEN];
const char *name = d->subsys_desc.name;
if (d->smem_id == -1)
return;
smem_reason = smem_get_entry_no_rlock(d->smem_id, &size, 0,
SMEM_ANY_HOST_FLAG);
if (!smem_reason || !size) {
pr_err("%s SFR: (unknown, smem_get_entry_no_rlock failed).\n",
name);
return;
}
if (!smem_reason[0]) {
pr_err("%s SFR: (unknown, empty string found).\n", name);
return;
}
strlcpy(reason, smem_reason, min(size, sizeof(reason)));
pr_err("%s subsystem failure reason: %s.\n", name, reason);
smem_reason[0] = '\0';
wmb();
}
static int subsys_shutdown(const struct subsys_desc *subsys, bool force_stop)
{
struct pil_tz_data *d = subsys_to_data(subsys);
int ret;
if (!subsys_get_crash_status(d->subsys) && force_stop &&
subsys->force_stop_gpio) {
gpio_set_value(subsys->force_stop_gpio, 1);
ret = wait_for_completion_timeout(&d->stop_ack,
msecs_to_jiffies(STOP_ACK_TIMEOUT_MS));
if (!ret)
pr_warn("Timed out on stop ack from %s.\n",
subsys->name);
gpio_set_value(subsys->force_stop_gpio, 0);
}
pil_shutdown(&d->desc);
return 0;
}
static int subsys_powerup(const struct subsys_desc *subsys)
{
struct pil_tz_data *d = subsys_to_data(subsys);
int ret = 0;
if (subsys->stop_ack_irq)
INIT_COMPLETION(d->stop_ack);
ret = pil_boot(&d->desc);
return ret;
}
static int subsys_ramdump(int enable, const struct subsys_desc *subsys)
{
struct pil_tz_data *d = subsys_to_data(subsys);
if (!enable)
return 0;
return pil_do_ramdump(&d->desc, d->ramdump_dev);
}
static void subsys_crash_shutdown(const struct subsys_desc *subsys)
{
struct pil_tz_data *d = subsys_to_data(subsys);
if (subsys->force_stop_gpio > 0 &&
!subsys_get_crash_status(d->subsys)) {
gpio_set_value(subsys->force_stop_gpio, 1);
msleep(STOP_ACK_TIMEOUT_MS);
}
}
static irqreturn_t subsys_err_fatal_intr_handler (int irq, void *dev_id)
{
struct pil_tz_data *d = subsys_to_data(dev_id);
pr_err("Fatal error on %s!\n", d->subsys_desc.name);
if (subsys_get_crash_status(d->subsys)) {
pr_err("%s: Ignoring error fatal, restart in progress\n",
d->subsys_desc.name);
return IRQ_HANDLED;
}
subsys_set_crash_status(d->subsys, true);
log_failure_reason(d);
subsystem_restart_dev(d->subsys);
return IRQ_HANDLED;
}
static irqreturn_t subsys_wdog_bite_irq_handler(int irq, void *dev_id)
{
struct pil_tz_data *d = subsys_to_data(dev_id);
pr_err("Watchdog bite received from %s!\n", d->subsys_desc.name);
if (subsys_get_crash_status(d->subsys)) {
pr_err("%s: Ignoring wdog bite IRQ, restart in progress\n",
d->subsys_desc.name);
return IRQ_HANDLED;
}
subsys_set_crash_status(d->subsys, true);
log_failure_reason(d);
subsystem_restart_dev(d->subsys);
return IRQ_HANDLED;
}
static irqreturn_t subsys_stop_ack_intr_handler(int irq, void *dev_id)
{
struct pil_tz_data *d = subsys_to_data(dev_id);
pr_info("Received stop ack interrupt from %s\n", d->subsys_desc.name);
complete(&d->stop_ack);
return IRQ_HANDLED;
}
static int pil_tz_driver_probe(struct platform_device *pdev)
{
struct pil_tz_data *d;
u32 proxy_timeout;
int len, rc;
d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL);
if (!d)
return -ENOMEM;
platform_set_drvdata(pdev, d);
rc = piltz_resc_init(pdev, d);
if (rc)
return -ENOENT;
rc = of_property_read_u32(pdev->dev.of_node, "qcom,pas-id", &d->pas_id);
if (rc) {
dev_err(&pdev->dev, "Failed to find the pas_id.\n");
return rc;
}
rc = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
&d->desc.name);
if (rc)
return rc;
/* Defaulting smem_id to be not present */
d->smem_id = -1;
if (of_find_property(pdev->dev.of_node, "qcom,smem-id", &len)) {
rc = of_property_read_u32(pdev->dev.of_node, "qcom,smem-id",
&d->smem_id);
if (rc) {
dev_err(&pdev->dev, "Failed to get the smem_id.\n");
return rc;
}
}
d->desc.dev = &pdev->dev;
d->desc.owner = THIS_MODULE;
d->desc.ops = &pil_ops_trusted;
d->desc.proxy_timeout = PROXY_TIMEOUT_MS;
rc = of_property_read_u32(pdev->dev.of_node, "qcom,proxy-timeout-ms",
&proxy_timeout);
if (!rc)
d->desc.proxy_timeout = proxy_timeout;
scm_pas_init(MSM_BUS_MASTER_CRYPTO_CORE0);
rc = pil_desc_init(&d->desc);
if (rc)
return rc;
d->subsys_desc.name = d->desc.name;
d->subsys_desc.owner = THIS_MODULE;
d->subsys_desc.dev = &pdev->dev;
d->subsys_desc.shutdown = subsys_shutdown;
d->subsys_desc.powerup = subsys_powerup;
d->subsys_desc.ramdump = subsys_ramdump;
d->subsys_desc.crash_shutdown = subsys_crash_shutdown;
d->subsys_desc.err_fatal_handler = subsys_err_fatal_intr_handler;
d->subsys_desc.wdog_bite_handler = subsys_wdog_bite_irq_handler;
d->subsys_desc.stop_ack_handler = subsys_stop_ack_intr_handler;
d->ramdump_dev = create_ramdump_device(d->subsys_desc.name,
&pdev->dev);
if (!d->ramdump_dev) {
rc = -ENOMEM;
goto err_ramdump;
}
d->subsys = subsys_register(&d->subsys_desc);
if (IS_ERR(d->subsys)) {
rc = PTR_ERR(d->subsys);
goto err_subsys;
}
if (d->subsys_desc.stop_ack_irq)
init_completion(&d->stop_ack);
return 0;
err_subsys:
destroy_ramdump_device(d->ramdump_dev);
err_ramdump:
pil_desc_release(&d->desc);
return rc;
}
static int pil_tz_driver_exit(struct platform_device *pdev)
{
struct pil_tz_data *d = platform_get_drvdata(pdev);
subsys_unregister(d->subsys);
destroy_ramdump_device(d->ramdump_dev);
pil_desc_release(&d->desc);
return 0;
}
static struct of_device_id pil_tz_match_table[] = {
{}
};
static struct platform_driver pil_tz_driver = {
.probe = pil_tz_driver_probe,
.remove = pil_tz_driver_exit,
.driver = {
.name = "subsys-pil-tz",
.of_match_table = pil_tz_match_table,
.owner = THIS_MODULE,
},
};
static int __init pil_tz_init(void)
{
return platform_driver_register(&pil_tz_driver);
}
module_init(pil_tz_init);
static void __exit pil_tz_exit(void)
{
platform_driver_unregister(&pil_tz_driver);
}
module_exit(pil_tz_exit);
MODULE_DESCRIPTION("Support for booting subsystems");
MODULE_LICENSE("GPL v2");