Merge "esoc: mdm-4x: Add support for mdm9x45 and apq8096"
This commit is contained in:
commit
a2c574ad5b
9 changed files with 332 additions and 29 deletions
|
@ -6,8 +6,8 @@ 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,mdm2-modem", "qcom,ext-mdm9x25", "qcom,ext-mdm9x35", "qcom,ext-mdm9x45",
|
||||
"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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue