Merge "esoc: mdm-4x: Add support for mdm9x45 and apq8096"

This commit is contained in:
Linux Build Service Account 2017-05-01 23:56:58 -07:00 committed by Gerrit - the friendly Code Review server
commit a2c574ad5b
9 changed files with 332 additions and 29 deletions

View file

@ -7,7 +7,7 @@ to be reset.
Required Properties:
- compatible: The bus devices need to be compatible with
"qcom,mdm2-modem", "qcom,ext-mdm9x25", "qcom,ext-mdm9x35", "qcom,ext-mdm9x45",
"qcom,ext-mdm9x55".
"qcom,ext-mdm9x55", "qcom,ext-apq8096".
Required named gpio properties:
- qcom,mdm2ap-errfatal-gpio: gpio for the external modem to indicate to the apps processor

View file

@ -179,19 +179,37 @@ static int mdm_cmd_exe(enum esoc_cmd cmd, struct esoc_clink *esoc)
struct device *dev = mdm->dev;
int ret;
bool graceful_shutdown = false;
u32 status, err_fatal;
switch (cmd) {
case ESOC_PWR_ON:
if (esoc->auto_boot) {
/*
* If esoc has already booted, we would have missed
* status change interrupt. Read status and err_fatal
* signals to arrive at the state of esoc.
*/
esoc->clink_ops->get_status(&status, esoc);
esoc->clink_ops->get_err_fatal(&err_fatal, esoc);
if (err_fatal)
return -EIO;
if (status && !mdm->ready) {
mdm->ready = true;
esoc->clink_ops->notify(ESOC_BOOT_DONE, esoc);
}
}
gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
mdm_enable_irqs(mdm);
mdm->init = 1;
mdm_do_first_power_on(mdm);
mdm_enable_irqs(mdm);
break;
case ESOC_PWR_OFF:
mdm_disable_irqs(mdm);
mdm->debug = 0;
mdm->ready = false;
mdm->trig_cnt = 0;
if (esoc->primary)
break;
graceful_shutdown = true;
ret = sysmon_send_shutdown(&esoc->subsys);
if (ret) {
@ -228,6 +246,8 @@ force_poff:
esoc->subsys.sysmon_shutdown_ret);
}
if (esoc->primary)
break;
/*
* Force a shutdown of the mdm. This is required in order
* to prevent the mdm from immediately powering back on
@ -249,9 +269,12 @@ force_poff:
*/
mdm->ready = false;
cancel_delayed_work(&mdm->mdm2ap_status_check_work);
if (!mdm->esoc->auto_boot) {
gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1);
dev_dbg(mdm->dev, "set ap2mdm errfatal to force reset\n");
dev_dbg(mdm->dev,
"set ap2mdm errfatal to force reset\n");
msleep(mdm->ramdump_delay_ms);
}
break;
case ESOC_EXE_DEBUG:
mdm->debug = 1;
@ -378,6 +401,8 @@ static void mdm_notify(enum esoc_notify notify, struct esoc_clink *esoc)
status_down = false;
dev_dbg(dev, "signal apq err fatal for graceful restart\n");
gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1);
if (esoc->primary)
break;
timeout = local_clock();
do_div(timeout, NSEC_PER_MSEC);
timeout += MDM_MODEM_TIMEOUT;
@ -441,11 +466,25 @@ static irqreturn_t mdm_status_change(int irq, void *dev_id)
return IRQ_HANDLED;
dev = mdm->dev;
esoc = mdm->esoc;
/*
* On auto boot devices, there is a possibility of receiving
* status change interrupt before esoc_clink structure is
* initialized. Ignore them.
*/
if (!esoc)
return IRQ_HANDLED;
value = gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS));
if (value == 0 && mdm->ready) {
dev_err(dev, "unexpected reset external modem\n");
esoc_clink_evt_notify(ESOC_UNEXPECTED_RESET, esoc);
} else if (value == 1) {
/*
* In auto_boot cases, bailout early if mdm
* is up already.
*/
if (esoc->auto_boot && mdm->ready)
return IRQ_HANDLED;
cancel_delayed_work(&mdm->mdm2ap_status_check_work);
dev_dbg(dev, "status = 1: mdm is now ready\n");
mdm->ready = true;
@ -453,6 +492,8 @@ static irqreturn_t mdm_status_change(int irq, void *dev_id)
queue_work(mdm->mdm_queue, &mdm->mdm_status_work);
if (mdm->get_restart_reason)
queue_work(mdm->mdm_queue, &mdm->restart_reason_work);
if (esoc->auto_boot)
esoc->clink_ops->notify(ESOC_BOOT_DONE, esoc);
}
return IRQ_HANDLED;
}
@ -481,7 +522,7 @@ static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id)
return IRQ_HANDLED;
}
static int mdm_get_status(u32 *status, struct esoc_clink *esoc)
static void mdm_get_status(u32 *status, struct esoc_clink *esoc)
{
struct mdm_ctrl *mdm = get_esoc_clink_data(esoc);
@ -489,7 +530,16 @@ static int mdm_get_status(u32 *status, struct esoc_clink *esoc)
*status = 0;
else
*status = 1;
return 0;
}
static void mdm_get_err_fatal(u32 *status, struct esoc_clink *esoc)
{
struct mdm_ctrl *mdm = get_esoc_clink_data(esoc);
if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_ERRFATAL)) == 0)
*status = 0;
else
*status = 1;
}
static void mdm_configure_debug(struct mdm_ctrl *mdm)
@ -573,13 +623,21 @@ static int mdm_configure_ipc(struct mdm_ctrl *mdm, struct platform_device *pdev)
&mdm->ramdump_delay_ms);
if (ret)
mdm->ramdump_delay_ms = DEF_RAMDUMP_DELAY;
/* Multilple gpio_request calls are allowed */
/*
* In certain scenarios, multiple esoc devices are monitoring
* same AP2MDM_STATUS line. But only one of them will have a
* successful gpio_request call. Initialize gpio only if request
* succeeds.
*/
if (gpio_request(MDM_GPIO(mdm, AP2MDM_STATUS), "AP2MDM_STATUS"))
dev_err(dev, "Failed to configure AP2MDM_STATUS gpio\n");
/* Multilple gpio_request calls are allowed */
else
gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
if (gpio_request(MDM_GPIO(mdm, AP2MDM_ERRFATAL), "AP2MDM_ERRFATAL"))
dev_err(dev, "%s Failed to configure AP2MDM_ERRFATAL gpio\n",
__func__);
else
gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
if (gpio_request(MDM_GPIO(mdm, MDM2AP_STATUS), "MDM2AP_STATUS")) {
dev_err(dev, "%s Failed to configure MDM2AP_STATUS gpio\n",
__func__);
@ -612,9 +670,6 @@ static int mdm_configure_ipc(struct mdm_ctrl *mdm, struct platform_device *pdev)
}
}
gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_CHNLRDY)))
gpio_direction_output(MDM_GPIO(mdm, AP2MDM_CHNLRDY), 0);
@ -748,6 +803,7 @@ static int mdm9x25_setup_hw(struct mdm_ctrl *mdm,
dev_err(mdm->dev, "cannot allocate esoc device\n");
return PTR_ERR(esoc);
}
esoc->pdev = pdev;
mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
if (!mdm->mdm_queue) {
dev_err(mdm->dev, "could not create mdm_queue\n");
@ -818,6 +874,7 @@ static int mdm9x35_setup_hw(struct mdm_ctrl *mdm,
dev_err(mdm->dev, "cannot allocate esoc device\n");
return PTR_ERR(esoc);
}
esoc->pdev = pdev;
mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
if (!mdm->mdm_queue) {
dev_err(mdm->dev, "could not create mdm_queue\n");
@ -888,6 +945,80 @@ static int mdm9x35_setup_hw(struct mdm_ctrl *mdm,
return 0;
}
static int mdm9x45_setup_hw(struct mdm_ctrl *mdm,
const struct mdm_ops *ops,
struct platform_device *pdev)
{
int ret;
struct esoc_clink *esoc;
const struct esoc_clink_ops *const clink_ops = ops->clink_ops;
const struct mdm_pon_ops *pon_ops = ops->pon_ops;
mdm->dev = &pdev->dev;
mdm->pon_ops = pon_ops;
esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL);
if (IS_ERR_OR_NULL(esoc)) {
dev_err(mdm->dev, "cannot allocate esoc device\n");
return PTR_ERR(esoc);
}
esoc->pdev = pdev;
mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
if (!mdm->mdm_queue) {
dev_err(mdm->dev, "could not create mdm_queue\n");
return -ENOMEM;
}
mdm->irq_mask = 0;
mdm->ready = false;
ret = mdm_dt_parse_gpios(mdm);
if (ret)
return ret;
dev_err(mdm->dev, "parsing gpio done\n");
ret = mdm_pon_dt_init(mdm);
if (ret)
return ret;
dev_dbg(mdm->dev, "pon dt init done\n");
ret = mdm_pinctrl_init(mdm);
if (ret)
return ret;
dev_err(mdm->dev, "pinctrl init done\n");
ret = mdm_pon_setup(mdm);
if (ret)
return ret;
dev_dbg(mdm->dev, "pon setup done\n");
ret = mdm_configure_ipc(mdm, pdev);
if (ret)
return ret;
mdm_configure_debug(mdm);
dev_err(mdm->dev, "ipc configure done\n");
esoc->name = MDM9x45_LABEL;
esoc->link_name = MDM9x45_PCIE;
esoc->clink_ops = clink_ops;
esoc->parent = mdm->dev;
esoc->owner = THIS_MODULE;
esoc->np = pdev->dev.of_node;
esoc->auto_boot = of_property_read_bool(esoc->np,
"qcom,mdm-auto-boot");
set_esoc_clink_data(esoc, mdm);
ret = esoc_clink_register(esoc);
if (ret) {
dev_err(mdm->dev, "esoc registration failed\n");
return ret;
}
dev_dbg(mdm->dev, "esoc registration done\n");
init_completion(&mdm->debug_done);
INIT_WORK(&mdm->mdm_status_work, mdm_status_fn);
INIT_WORK(&mdm->restart_reason_work, mdm_get_restart_reason);
INIT_DELAYED_WORK(&mdm->mdm2ap_status_check_work, mdm2ap_status_check);
mdm->get_restart_reason = false;
mdm->debug_fail = false;
mdm->esoc = esoc;
mdm->init = 0;
if (esoc->auto_boot)
gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 1);
return 0;
}
static int mdm9x55_setup_hw(struct mdm_ctrl *mdm,
const struct mdm_ops *ops,
struct platform_device *pdev)
@ -906,6 +1037,7 @@ static int mdm9x55_setup_hw(struct mdm_ctrl *mdm,
dev_err(mdm->dev, "cannot allocate esoc device\n");
return PTR_ERR(esoc);
}
esoc->pdev = pdev;
mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
if (!mdm->mdm_queue) {
dev_err(mdm->dev, "could not create mdm_queue\n");
@ -963,9 +1095,86 @@ static int mdm9x55_setup_hw(struct mdm_ctrl *mdm,
return 0;
}
static int apq8096_setup_hw(struct mdm_ctrl *mdm,
const struct mdm_ops *ops,
struct platform_device *pdev)
{
int ret;
struct device_node *node;
struct esoc_clink *esoc;
const struct esoc_clink_ops *const clink_ops = ops->clink_ops;
const struct mdm_pon_ops *pon_ops = ops->pon_ops;
mdm->dev = &pdev->dev;
mdm->pon_ops = pon_ops;
node = pdev->dev.of_node;
esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL);
if (IS_ERR_OR_NULL(esoc)) {
dev_err(mdm->dev, "cannot allocate esoc device\n");
return PTR_ERR(esoc);
}
esoc->pdev = pdev;
mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
if (!mdm->mdm_queue) {
dev_err(mdm->dev, "could not create mdm_queue\n");
return -ENOMEM;
}
mdm->irq_mask = 0;
mdm->ready = false;
ret = mdm_dt_parse_gpios(mdm);
if (ret)
return ret;
dev_dbg(mdm->dev, "parsing gpio done\n");
ret = mdm_pon_dt_init(mdm);
if (ret)
return ret;
dev_dbg(mdm->dev, "pon dt init done\n");
ret = mdm_pinctrl_init(mdm);
if (ret)
return ret;
dev_dbg(mdm->dev, "pinctrl init done\n");
ret = mdm_pon_setup(mdm);
if (ret)
return ret;
dev_dbg(mdm->dev, "pon setup done\n");
ret = mdm_configure_ipc(mdm, pdev);
if (ret)
return ret;
dev_dbg(mdm->dev, "ipc configure done\n");
esoc->name = APQ8096_LABEL;
esoc->link_name = APQ8096_PCIE;
esoc->clink_ops = clink_ops;
esoc->parent = mdm->dev;
esoc->owner = THIS_MODULE;
esoc->np = pdev->dev.of_node;
esoc->auto_boot = of_property_read_bool(esoc->np,
"qcom,mdm-auto-boot");
esoc->primary = of_property_read_bool(esoc->np,
"qcom,mdm-primary");
set_esoc_clink_data(esoc, mdm);
ret = esoc_clink_register(esoc);
if (ret) {
dev_err(mdm->dev, "esoc registration failed\n");
return ret;
}
dev_dbg(mdm->dev, "esoc registration done\n");
init_completion(&mdm->debug_done);
INIT_WORK(&mdm->mdm_status_work, mdm_status_fn);
INIT_WORK(&mdm->restart_reason_work, mdm_get_restart_reason);
INIT_DELAYED_WORK(&mdm->mdm2ap_status_check_work, mdm2ap_status_check);
mdm->get_restart_reason = false;
mdm->debug_fail = false;
mdm->esoc = esoc;
mdm->init = 0;
gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 1);
gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
return 0;
}
static struct esoc_clink_ops mdm_cops = {
.cmd_exe = mdm_cmd_exe,
.get_status = mdm_get_status,
.get_err_fatal = mdm_get_err_fatal,
.notify = mdm_notify,
};
@ -981,6 +1190,18 @@ static struct mdm_ops mdm9x35_ops = {
.pon_ops = &mdm9x35_pon_ops,
};
static struct mdm_ops mdm9x45_ops = {
.clink_ops = &mdm_cops,
.config_hw = mdm9x45_setup_hw,
.pon_ops = &mdm9x45_pon_ops,
};
static struct mdm_ops apq8096_ops = {
.clink_ops = &mdm_cops,
.config_hw = apq8096_setup_hw,
.pon_ops = &apq8096_pon_ops,
};
static struct mdm_ops mdm9x55_ops = {
.clink_ops = &mdm_cops,
.config_hw = mdm9x55_setup_hw,
@ -992,8 +1213,12 @@ static const struct of_device_id mdm_dt_match[] = {
.data = &mdm9x25_ops, },
{ .compatible = "qcom,ext-mdm9x35",
.data = &mdm9x35_ops, },
{ .compatible = "qcom,ext-mdm9x45",
.data = &mdm9x45_ops, },
{ .compatible = "qcom,ext-mdm9x55",
.data = &mdm9x55_ops, },
{ .compatible = "qcom,ext-apq8096",
.data = &apq8096_ops, },
{},
};
MODULE_DEVICE_TABLE(of, mdm_dt_match);

View file

@ -13,6 +13,7 @@
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/reboot.h>
#include <linux/of.h>
#include "esoc.h"
#include "mdm-dbg.h"
@ -72,7 +73,14 @@ static void mdm_handle_clink_evt(enum esoc_evt evt,
break;
case ESOC_UNEXPECTED_RESET:
case ESOC_ERR_FATAL:
if (mdm_drv->mode == CRASH)
/*
* Modem can crash while we are waiting for boot_done during
* a subsystem_get(). Setting mode to CRASH will prevent a
* subsequent subsystem_get() from entering poweron ops. Avoid
* this by seting mode to CRASH only if device was up and
* running.
*/
if (mdm_drv->mode == CRASH || mdm_drv->mode != RUN)
return;
mdm_drv->mode = CRASH;
queue_work(mdm_drv->mdm_queue, &mdm_drv->ssr_work);
@ -161,8 +169,9 @@ static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
subsys);
struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);
const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops;
int timeout = INT_MAX;
if (!esoc_req_eng_enabled(esoc_clink)) {
if (!esoc_clink->auto_boot && !esoc_req_eng_enabled(esoc_clink)) {
dev_dbg(&esoc_clink->dev, "Wait for req eng registration\n");
wait_for_completion(&mdm_drv->req_eng_wait);
}
@ -187,8 +196,17 @@ static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
return ret;
}
}
wait_for_completion(&mdm_drv->boot_done);
if (mdm_drv->boot_fail) {
/*
* In autoboot case, it is possible that we can forever wait for
* boot completion, when esoc fails to boot. This is because there
* is no helper application which can alert esoc driver about boot
* failure. Prevent going to wait forever in such case.
*/
if (esoc_clink->auto_boot)
timeout = 10 * HZ;
ret = wait_for_completion_timeout(&mdm_drv->boot_done, timeout);
if (mdm_drv->boot_fail || ret <= 0) {
dev_err(&esoc_clink->dev, "booting failed\n");
return -EIO;
}
@ -216,10 +234,12 @@ static int mdm_subsys_ramdumps(int want_dumps,
static int mdm_register_ssr(struct esoc_clink *esoc_clink)
{
esoc_clink->subsys.shutdown = mdm_subsys_shutdown;
esoc_clink->subsys.ramdump = mdm_subsys_ramdumps;
esoc_clink->subsys.powerup = mdm_subsys_powerup;
esoc_clink->subsys.crash_shutdown = mdm_crash_shutdown;
struct subsys_desc *subsys = &esoc_clink->subsys;
subsys->shutdown = mdm_subsys_shutdown;
subsys->ramdump = mdm_subsys_ramdumps;
subsys->powerup = mdm_subsys_powerup;
subsys->crash_shutdown = mdm_crash_shutdown;
return esoc_clink_register_ssr(esoc_clink);
}
@ -286,6 +306,14 @@ static struct esoc_compat compat_table[] = {
.name = "MDM9x55",
.data = NULL,
},
{
.name = "MDM9x45",
.data = NULL,
},
{
.name = "APQ8096",
.data = NULL,
},
};
static struct esoc_drv esoc_ssr_drv = {

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2014-2015, 2017, 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
@ -68,6 +68,9 @@ static int mdm4x_do_first_power_on(struct mdm_ctrl *mdm)
struct device *dev = mdm->dev;
dev_dbg(dev, "Powering on modem for the first time\n");
if (mdm->esoc->auto_boot)
return 0;
mdm_toggle_soft_reset(mdm, false);
/* Add a delay to allow PON sequence to complete*/
msleep(50);
@ -134,6 +137,9 @@ static int mdm9x55_power_down(struct mdm_ctrl *mdm)
static void mdm4x_cold_reset(struct mdm_ctrl *mdm)
{
if (!gpio_is_valid(MDM_GPIO(mdm, AP2MDM_SOFT_RESET)))
return;
dev_dbg(mdm->dev, "Triggering mdm cold reset");
gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
!!mdm->soft_reset_inverted);
@ -152,6 +158,11 @@ static void mdm9x55_cold_reset(struct mdm_ctrl *mdm)
!mdm->soft_reset_inverted);
}
static int apq8096_pon_dt_init(struct mdm_ctrl *mdm)
{
return 0;
}
static int mdm4x_pon_dt_init(struct mdm_ctrl *mdm)
{
int val;
@ -183,6 +194,21 @@ static int mdm4x_pon_setup(struct mdm_ctrl *mdm)
return 0;
}
/* This function can be called from atomic context. */
static int apq8096_toggle_soft_reset(struct mdm_ctrl *mdm, bool atomic)
{
return 0;
}
static int apq8096_power_down(struct mdm_ctrl *mdm)
{
return 0;
}
static void apq8096_cold_reset(struct mdm_ctrl *mdm)
{
}
struct mdm_pon_ops mdm9x25_pon_ops = {
.pon = mdm4x_do_first_power_on,
.soft_reset = mdm4x_toggle_soft_reset,
@ -218,3 +244,12 @@ struct mdm_pon_ops mdm9x55_pon_ops = {
.dt_init = mdm4x_pon_dt_init,
.setup = mdm4x_pon_setup,
};
struct mdm_pon_ops apq8096_pon_ops = {
.pon = mdm4x_do_first_power_on,
.soft_reset = apq8096_toggle_soft_reset,
.poff_force = apq8096_power_down,
.cold_reset = apq8096_cold_reset,
.dt_init = apq8096_pon_dt_init,
.setup = mdm4x_pon_setup,
};

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2014-2015, 2017, 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
@ -37,6 +37,8 @@
#define MDM9x45_PCIE "PCIe"
#define MDM9x55_LABEL "MDM9x55"
#define MDM9x55_PCIE "PCIe"
#define APQ8096_LABEL "APQ8096"
#define APQ8096_PCIE "PCIe"
#define MDM2AP_STATUS_TIMEOUT_MS 120000L
#define MDM_MODEM_TIMEOUT 3000
#define DEF_RAMDUMP_TIMEOUT 120000
@ -153,4 +155,5 @@ extern struct mdm_pon_ops mdm9x25_pon_ops;
extern struct mdm_pon_ops mdm9x35_pon_ops;
extern struct mdm_pon_ops mdm9x45_pon_ops;
extern struct mdm_pon_ops mdm9x55_pon_ops;
extern struct mdm_pon_ops apq8096_pon_ops;
#endif

View file

@ -49,6 +49,7 @@ struct esoc_eng {
* @link_info: additional info about the physical link.
* @parent: parent device.
* @dev: device for userspace interface.
* @pdev: platform device to interface with SSR driver.
* @id: id of the external device.
* @owner: owner of the device.
* @clink_ops: control operations for the control link
@ -59,6 +60,9 @@ struct esoc_eng {
* @subsys_desc: descriptor for subsystem restart
* @subsys_dev: ssr device handle.
* @np: device tree node for esoc_clink.
* @auto_boot: boots independently.
* @primary: primary esoc controls(reset/poweroff) all secondary
* esocs, but not otherway around.
*/
struct esoc_clink {
const char *name;
@ -66,6 +70,7 @@ struct esoc_clink {
const char *link_info;
struct device *parent;
struct device dev;
struct platform_device *pdev;
unsigned int id;
struct module *owner;
const struct esoc_clink_ops const *clink_ops;
@ -77,17 +82,21 @@ struct esoc_clink {
struct subsys_desc subsys;
struct subsys_device *subsys_dev;
struct device_node *np;
bool auto_boot;
bool primary;
};
/**
* struct esoc_clink_ops: Operations to control external soc
* @cmd_exe: Execute control command
* @get_status: Get current status, or response to previous command
* @get_err_fatal: Get status of err fatal signal
* @notify_esoc: notify external soc of events
*/
struct esoc_clink_ops {
int (*cmd_exe)(enum esoc_cmd cmd, struct esoc_clink *dev);
int (*get_status)(u32 *status, struct esoc_clink *dev);
void (*get_status)(u32 *status, struct esoc_clink *dev);
void (*get_err_fatal)(u32 *status, struct esoc_clink *dev);
void (*notify)(enum esoc_notify notify, struct esoc_clink *dev);
};

View file

@ -189,7 +189,7 @@ int esoc_clink_register_ssr(struct esoc_clink *esoc_clink)
snprintf(subsys_name, len, "esoc%d", esoc_clink->id);
esoc_clink->subsys.name = subsys_name;
esoc_clink->dev.of_node = esoc_clink->np;
esoc_clink->subsys.dev = &esoc_clink->dev;
esoc_clink->subsys.dev = &esoc_clink->pdev->dev;
esoc_clink->subsys_dev = subsys_register(&esoc_clink->subsys);
if (IS_ERR_OR_NULL(esoc_clink->subsys_dev)) {
dev_err(&esoc_clink->dev, "failed to register ssr node\n");

View file

@ -224,9 +224,11 @@ static long esoc_dev_ioctl(struct file *file, unsigned int cmd,
clink_ops->notify(esoc_cmd, esoc_clink);
break;
case ESOC_GET_STATUS:
err = clink_ops->get_status(&status, esoc_clink);
if (err)
return err;
clink_ops->get_status(&status, esoc_clink);
put_user(status, (unsigned int __user *)uarg);
break;
case ESOC_GET_ERR_FATAL:
clink_ops->get_err_fatal(&status, esoc_clink);
put_user(status, (unsigned int __user *)uarg);
break;
case ESOC_WAIT_FOR_CRASH:

View file

@ -7,6 +7,7 @@
#define ESOC_WAIT_FOR_REQ _IOR(ESOC_CODE, 2, unsigned int)
#define ESOC_NOTIFY _IOW(ESOC_CODE, 3, unsigned int)
#define ESOC_GET_STATUS _IOR(ESOC_CODE, 4, unsigned int)
#define ESOC_GET_ERR_FATAL _IOR(ESOC_CODE, 5, unsigned int)
#define ESOC_WAIT_FOR_CRASH _IOR(ESOC_CODE, 6, unsigned int)
#define ESOC_REG_REQ_ENG _IO(ESOC_CODE, 7)
#define ESOC_REG_CMD_ENG _IO(ESOC_CODE, 8)