From 98bc7be821d179e206f57478547e63c14aa6580c Mon Sep 17 00:00:00 2001 From: Arun KS Date: Mon, 16 Jan 2017 14:39:52 +0530 Subject: [PATCH 1/4] esoc: Fix integration with SSR driver SSR driver expects subsystem descriptor device type to be struct platform_device, whereas esoc driver is passing a struct device instead. This results in a NULL pointer crash during platform_get_irq in subsystem_restart.c. Fix esoc driver to pass platform_device. Change-Id: I93d02623cb7ac14ea3171c3792a4c52c73a74dea Signed-off-by: Arun KS Signed-off-by: Srivatsa Vaddagiri --- drivers/esoc/esoc-mdm-4x.c | 3 +++ drivers/esoc/esoc.h | 2 ++ drivers/esoc/esoc_bus.c | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/esoc/esoc-mdm-4x.c b/drivers/esoc/esoc-mdm-4x.c index 1e5f35d8422d..9ddd91c23c0b 100644 --- a/drivers/esoc/esoc-mdm-4x.c +++ b/drivers/esoc/esoc-mdm-4x.c @@ -748,6 +748,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 +819,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"); @@ -906,6 +908,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"); diff --git a/drivers/esoc/esoc.h b/drivers/esoc/esoc.h index 755fb24bd60a..11e36f70d4bf 100644 --- a/drivers/esoc/esoc.h +++ b/drivers/esoc/esoc.h @@ -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 @@ -66,6 +67,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; diff --git a/drivers/esoc/esoc_bus.c b/drivers/esoc/esoc_bus.c index f925607511ba..94f52764c8c3 100644 --- a/drivers/esoc/esoc_bus.c +++ b/drivers/esoc/esoc_bus.c @@ -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"); From 5c846e4f7c3c31ab503d9a6b1a5bd485edf68275 Mon Sep 17 00:00:00 2001 From: Arun KS Date: Mon, 16 Jan 2017 15:27:48 +0530 Subject: [PATCH 2/4] esoc: Add err_fatal signal status to clink_ops Auto_boot esoc devices can boot and crash before esoc driver comes up. But there is no way for the user space code to know that it has crashed by looking at status line alone. Hence, create a new ioctl entry to export status of err_fatal line to user space. Change-Id: Ie7d6115c749d4c63f06aefca29ba457d38eccc7f Signed-off-by: Arun KS --- drivers/esoc/esoc-mdm-4x.c | 14 ++++++++++++-- drivers/esoc/esoc.h | 4 +++- drivers/esoc/esoc_dev.c | 8 +++++--- include/uapi/linux/esoc_ctrl.h | 1 + 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/esoc/esoc-mdm-4x.c b/drivers/esoc/esoc-mdm-4x.c index 9ddd91c23c0b..543bad093090 100644 --- a/drivers/esoc/esoc-mdm-4x.c +++ b/drivers/esoc/esoc-mdm-4x.c @@ -481,7 +481,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 +489,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) @@ -969,6 +978,7 @@ static int mdm9x55_setup_hw(struct mdm_ctrl *mdm, 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, }; diff --git a/drivers/esoc/esoc.h b/drivers/esoc/esoc.h index 11e36f70d4bf..86aa1a918dfb 100644 --- a/drivers/esoc/esoc.h +++ b/drivers/esoc/esoc.h @@ -85,11 +85,13 @@ struct esoc_clink { * 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); }; diff --git a/drivers/esoc/esoc_dev.c b/drivers/esoc/esoc_dev.c index bbe1d24fb1f6..a1e7a52a8c26 100644 --- a/drivers/esoc/esoc_dev.c +++ b/drivers/esoc/esoc_dev.c @@ -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: diff --git a/include/uapi/linux/esoc_ctrl.h b/include/uapi/linux/esoc_ctrl.h index 57266ed29fb3..d0743790e09c 100644 --- a/include/uapi/linux/esoc_ctrl.h +++ b/include/uapi/linux/esoc_ctrl.h @@ -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) From fe1cc57aafd720bfbe5bc270804ac11667dbffa2 Mon Sep 17 00:00:00 2001 From: Arun KS Date: Mon, 16 Jan 2017 17:47:03 +0530 Subject: [PATCH 3/4] esoc: Add support for autoboot Some of the external SoC are flash based and can boot independently. Extend esoc driver to support such auto boot esocs. This patch also adds support for primary esoc. Primary esoc are esoc that control secondary esoc such as modems. Primary esoc have control over reset/poweroff of secondary esoc. Secondary esoc don't have control over reset/poweroff of primary esoc. In general modems are considered as secondary esoc while apps processor is considered as primary esoc. Change-Id: Id02417fcd122ac108cf75d3381ee7955f0f8f783 Signed-off-by: Arun KS Signed-off-by: Srivatsa Vaddagiri --- drivers/esoc/esoc-mdm-4x.c | 66 +++++++++++++++++++++++++++++++------ drivers/esoc/esoc-mdm-drv.c | 36 +++++++++++++++----- drivers/esoc/esoc-mdm-pon.c | 8 ++++- drivers/esoc/esoc.h | 5 +++ 4 files changed, 96 insertions(+), 19 deletions(-) diff --git a/drivers/esoc/esoc-mdm-4x.c b/drivers/esoc/esoc-mdm-4x.c index 543bad093090..ac8d8002bcd3 100644 --- a/drivers/esoc/esoc-mdm-4x.c +++ b/drivers/esoc/esoc-mdm-4x.c @@ -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); - gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1); - dev_dbg(mdm->dev, "set ap2mdm errfatal to force reset\n"); - msleep(mdm->ramdump_delay_ms); + 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"); + 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; @@ -420,7 +445,7 @@ static irqreturn_t mdm_errfatal(int irq, void *dev_id) goto mdm_pwroff_irq; esoc = mdm->esoc; dev_err(dev, "%s: mdm sent errfatal interrupt\n", - __func__); + __func__); /* disable irq ?*/ esoc_clink_evt_notify(ESOC_ERR_FATAL, esoc); return IRQ_HANDLED; @@ -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; } @@ -582,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__); @@ -621,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); diff --git a/drivers/esoc/esoc-mdm-drv.c b/drivers/esoc/esoc-mdm-drv.c index 8697428eceb2..266eaccf8b69 100644 --- a/drivers/esoc/esoc-mdm-drv.c +++ b/drivers/esoc/esoc-mdm-drv.c @@ -13,6 +13,7 @@ #include #include #include +#include #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); } diff --git a/drivers/esoc/esoc-mdm-pon.c b/drivers/esoc/esoc-mdm-pon.c index acda06485364..a54c3e2715dd 100644 --- a/drivers/esoc/esoc-mdm-pon.c +++ b/drivers/esoc/esoc-mdm-pon.c @@ -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); diff --git a/drivers/esoc/esoc.h b/drivers/esoc/esoc.h index 86aa1a918dfb..6d9d0aa3272f 100644 --- a/drivers/esoc/esoc.h +++ b/drivers/esoc/esoc.h @@ -60,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; @@ -79,6 +82,8 @@ struct esoc_clink { struct subsys_desc subsys; struct subsys_device *subsys_dev; struct device_node *np; + bool auto_boot; + bool primary; }; /** From 6a8855841643dd07e95f493d4b8b8fde6fd8f4c9 Mon Sep 17 00:00:00 2001 From: Arun KS Date: Mon, 16 Jan 2017 18:00:37 +0530 Subject: [PATCH 4/4] esoc: mdm-4x: Add support for mdm9x45 and apq8096 Add mdm_ops for mdm9x45 and apq8064. Change-Id: Iea167175b9bd35a515d15a72897947a889093c03 Signed-off-by: Arun KS Signed-off-by: Srivatsa Vaddagiri --- .../devicetree/bindings/arm/msm/mdm-modem.txt | 4 +- drivers/esoc/esoc-mdm-4x.c | 166 ++++++++++++++++++ drivers/esoc/esoc-mdm-drv.c | 8 + drivers/esoc/esoc-mdm-pon.c | 29 +++ drivers/esoc/esoc-mdm.h | 5 +- 5 files changed, 209 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt b/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt index a6537ebd2512..86d2268c8b53 100644 --- a/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt +++ b/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt @@ -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 diff --git a/drivers/esoc/esoc-mdm-4x.c b/drivers/esoc/esoc-mdm-4x.c index ac8d8002bcd3..618ceb957c65 100644 --- a/drivers/esoc/esoc-mdm-4x.c +++ b/drivers/esoc/esoc-mdm-4x.c @@ -945,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) @@ -1021,6 +1095,82 @@ 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, @@ -1040,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, @@ -1051,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); diff --git a/drivers/esoc/esoc-mdm-drv.c b/drivers/esoc/esoc-mdm-drv.c index 266eaccf8b69..9c2c68dfef65 100644 --- a/drivers/esoc/esoc-mdm-drv.c +++ b/drivers/esoc/esoc-mdm-drv.c @@ -306,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 = { diff --git a/drivers/esoc/esoc-mdm-pon.c b/drivers/esoc/esoc-mdm-pon.c index a54c3e2715dd..d9b16b23839b 100644 --- a/drivers/esoc/esoc-mdm-pon.c +++ b/drivers/esoc/esoc-mdm-pon.c @@ -158,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; @@ -189,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, @@ -224,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, +}; diff --git a/drivers/esoc/esoc-mdm.h b/drivers/esoc/esoc-mdm.h index ac811720b035..9343e49559f2 100644 --- a/drivers/esoc/esoc-mdm.h +++ b/drivers/esoc/esoc-mdm.h @@ -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