blob: 4291bbc770f45f447df01c665fd757ad776421d5 [file] [log] [blame]
Raghavendra Rao Anantacaa60aa2017-10-20 11:27:12 -07001/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -08002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/delay.h>
14#include <linux/workqueue.h>
15#include <linux/reboot.h>
Arun KS0cb73fd2017-01-16 17:47:03 +053016#include <linux/of.h>
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -080017#include "esoc.h"
Trilok Soni8ca296e2016-08-21 23:38:25 -070018#include "mdm-dbg.h"
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -080019
20enum {
21 PWR_OFF = 0x1,
22 PWR_ON,
23 BOOT,
24 RUN,
25 CRASH,
26 IN_DEBUG,
27 SHUTDOWN,
28 RESET,
29 PEER_CRASH,
30};
31
32struct mdm_drv {
33 unsigned int mode;
34 struct esoc_eng cmd_eng;
35 struct completion boot_done;
36 struct completion req_eng_wait;
37 struct esoc_clink *esoc_clink;
38 bool boot_fail;
39 struct workqueue_struct *mdm_queue;
40 struct work_struct ssr_work;
41 struct notifier_block esoc_restart;
42};
43#define to_mdm_drv(d) container_of(d, struct mdm_drv, cmd_eng)
44
45static int esoc_msm_restart_handler(struct notifier_block *nb,
46 unsigned long action, void *data)
47{
48 struct mdm_drv *mdm_drv = container_of(nb, struct mdm_drv,
49 esoc_restart);
50 struct esoc_clink *esoc_clink = mdm_drv->esoc_clink;
51 const struct esoc_clink_ops * const clink_ops = esoc_clink->clink_ops;
52
53 if (action == SYS_RESTART) {
Trilok Soni8ca296e2016-08-21 23:38:25 -070054 if (mdm_dbg_stall_notify(ESOC_PRIMARY_REBOOT))
55 return NOTIFY_OK;
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -080056 dev_dbg(&esoc_clink->dev, "Notifying esoc of cold reboot\n");
57 clink_ops->notify(ESOC_PRIMARY_REBOOT, esoc_clink);
58 }
59 return NOTIFY_OK;
60}
61static void mdm_handle_clink_evt(enum esoc_evt evt,
62 struct esoc_eng *eng)
63{
64 struct mdm_drv *mdm_drv = to_mdm_drv(eng);
65
66 switch (evt) {
67 case ESOC_INVALID_STATE:
68 mdm_drv->boot_fail = true;
69 complete(&mdm_drv->boot_done);
70 break;
71 case ESOC_RUN_STATE:
72 mdm_drv->boot_fail = false;
73 mdm_drv->mode = RUN,
74 complete(&mdm_drv->boot_done);
75 break;
76 case ESOC_UNEXPECTED_RESET:
77 case ESOC_ERR_FATAL:
Arun KS0cb73fd2017-01-16 17:47:03 +053078 /*
79 * Modem can crash while we are waiting for boot_done during
80 * a subsystem_get(). Setting mode to CRASH will prevent a
81 * subsequent subsystem_get() from entering poweron ops. Avoid
82 * this by seting mode to CRASH only if device was up and
83 * running.
84 */
85 if (mdm_drv->mode == CRASH || mdm_drv->mode != RUN)
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -080086 return;
87 mdm_drv->mode = CRASH;
88 queue_work(mdm_drv->mdm_queue, &mdm_drv->ssr_work);
89 break;
90 case ESOC_REQ_ENG_ON:
91 complete(&mdm_drv->req_eng_wait);
92 break;
93 default:
94 break;
95 }
96}
97
98static void mdm_ssr_fn(struct work_struct *work)
99{
100 struct mdm_drv *mdm_drv = container_of(work, struct mdm_drv, ssr_work);
101
102 /*
103 * If restarting esoc fails, the SSR framework triggers a kernel panic
104 */
105 esoc_clink_request_ssr(mdm_drv->esoc_clink);
106}
107
108static void mdm_crash_shutdown(const struct subsys_desc *mdm_subsys)
109{
110 struct esoc_clink *esoc_clink =
111 container_of(mdm_subsys,
112 struct esoc_clink,
113 subsys);
114 const struct esoc_clink_ops * const clink_ops = esoc_clink->clink_ops;
115
Trilok Soni8ca296e2016-08-21 23:38:25 -0700116 if (mdm_dbg_stall_notify(ESOC_PRIMARY_CRASH))
117 return;
118
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800119 clink_ops->notify(ESOC_PRIMARY_CRASH, esoc_clink);
120}
121
122static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys,
123 bool force_stop)
124{
125 int ret;
126 struct esoc_clink *esoc_clink =
127 container_of(crashed_subsys, struct esoc_clink, subsys);
128 struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);
129 const struct esoc_clink_ops * const clink_ops = esoc_clink->clink_ops;
130
131 if (mdm_drv->mode == CRASH || mdm_drv->mode == PEER_CRASH) {
Trilok Soni8ca296e2016-08-21 23:38:25 -0700132 if (mdm_dbg_stall_cmd(ESOC_PREPARE_DEBUG))
133 /* We want to mask debug command.
134 * In this case return success
135 * to move to next stage
136 */
137 return 0;
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800138 ret = clink_ops->cmd_exe(ESOC_PREPARE_DEBUG,
139 esoc_clink);
140 if (ret) {
141 dev_err(&esoc_clink->dev, "failed to enter debug\n");
142 return ret;
143 }
144 mdm_drv->mode = IN_DEBUG;
145 } else if (!force_stop) {
146 if (esoc_clink->subsys.sysmon_shutdown_ret)
147 ret = clink_ops->cmd_exe(ESOC_FORCE_PWR_OFF,
148 esoc_clink);
Trilok Soni8ca296e2016-08-21 23:38:25 -0700149 else {
150 if (mdm_dbg_stall_cmd(ESOC_PWR_OFF))
151 /* Since power off command is masked
152 * we return success, and leave the state
153 * of the command engine as is.
154 */
155 return 0;
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800156 ret = clink_ops->cmd_exe(ESOC_PWR_OFF, esoc_clink);
Trilok Soni8ca296e2016-08-21 23:38:25 -0700157 }
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800158 if (ret) {
159 dev_err(&esoc_clink->dev, "failed to exe power off\n");
160 return ret;
161 }
162 mdm_drv->mode = PWR_OFF;
163 }
164 return 0;
165}
166
167static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
168{
169 int ret;
170 struct esoc_clink *esoc_clink =
171 container_of(crashed_subsys, struct esoc_clink,
172 subsys);
173 struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);
174 const struct esoc_clink_ops * const clink_ops = esoc_clink->clink_ops;
Arun KS0cb73fd2017-01-16 17:47:03 +0530175 int timeout = INT_MAX;
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800176
Arun KS0cb73fd2017-01-16 17:47:03 +0530177 if (!esoc_clink->auto_boot && !esoc_req_eng_enabled(esoc_clink)) {
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800178 dev_dbg(&esoc_clink->dev, "Wait for req eng registration\n");
179 wait_for_completion(&mdm_drv->req_eng_wait);
180 }
181 if (mdm_drv->mode == PWR_OFF) {
Trilok Soni8ca296e2016-08-21 23:38:25 -0700182 if (mdm_dbg_stall_cmd(ESOC_PWR_ON))
183 return -EBUSY;
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800184 ret = clink_ops->cmd_exe(ESOC_PWR_ON, esoc_clink);
185 if (ret) {
186 dev_err(&esoc_clink->dev, "pwr on fail\n");
187 return ret;
188 }
189 } else if (mdm_drv->mode == IN_DEBUG) {
190 ret = clink_ops->cmd_exe(ESOC_EXIT_DEBUG, esoc_clink);
191 if (ret) {
192 dev_err(&esoc_clink->dev, "cannot exit debug mode\n");
193 return ret;
194 }
195 mdm_drv->mode = PWR_OFF;
196 ret = clink_ops->cmd_exe(ESOC_PWR_ON, esoc_clink);
197 if (ret) {
198 dev_err(&esoc_clink->dev, "pwr on fail\n");
199 return ret;
200 }
201 }
Arun KS0cb73fd2017-01-16 17:47:03 +0530202
203 /*
204 * In autoboot case, it is possible that we can forever wait for
205 * boot completion, when esoc fails to boot. This is because there
206 * is no helper application which can alert esoc driver about boot
207 * failure. Prevent going to wait forever in such case.
208 */
209 if (esoc_clink->auto_boot)
210 timeout = 10 * HZ;
211 ret = wait_for_completion_timeout(&mdm_drv->boot_done, timeout);
212 if (mdm_drv->boot_fail || ret <= 0) {
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800213 dev_err(&esoc_clink->dev, "booting failed\n");
214 return -EIO;
215 }
216 return 0;
217}
218
219static int mdm_subsys_ramdumps(int want_dumps,
220 const struct subsys_desc *crashed_subsys)
221{
222 int ret;
223 struct esoc_clink *esoc_clink =
224 container_of(crashed_subsys, struct esoc_clink,
225 subsys);
226 const struct esoc_clink_ops * const clink_ops = esoc_clink->clink_ops;
227
228 if (want_dumps) {
229 ret = clink_ops->cmd_exe(ESOC_EXE_DEBUG, esoc_clink);
230 if (ret) {
231 dev_err(&esoc_clink->dev, "debugging failed\n");
232 return ret;
233 }
234 }
235 return 0;
236}
237
238static int mdm_register_ssr(struct esoc_clink *esoc_clink)
239{
Arun KS0cb73fd2017-01-16 17:47:03 +0530240 struct subsys_desc *subsys = &esoc_clink->subsys;
241
242 subsys->shutdown = mdm_subsys_shutdown;
243 subsys->ramdump = mdm_subsys_ramdumps;
244 subsys->powerup = mdm_subsys_powerup;
245 subsys->crash_shutdown = mdm_crash_shutdown;
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800246 return esoc_clink_register_ssr(esoc_clink);
247}
248
249int esoc_ssr_probe(struct esoc_clink *esoc_clink, struct esoc_drv *drv)
250{
251 int ret;
252 struct mdm_drv *mdm_drv;
253 struct esoc_eng *esoc_eng;
254
255 mdm_drv = devm_kzalloc(&esoc_clink->dev, sizeof(*mdm_drv), GFP_KERNEL);
Satya Durga Srinivasu Prabhala377d32f2017-03-15 11:09:28 -0700256 if (IS_ERR_OR_NULL(mdm_drv))
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800257 return PTR_ERR(mdm_drv);
258 esoc_eng = &mdm_drv->cmd_eng;
259 esoc_eng->handle_clink_evt = mdm_handle_clink_evt;
260 ret = esoc_clink_register_cmd_eng(esoc_clink, esoc_eng);
261 if (ret) {
262 dev_err(&esoc_clink->dev, "failed to register cmd engine\n");
263 return ret;
264 }
265 ret = mdm_register_ssr(esoc_clink);
266 if (ret)
267 goto ssr_err;
268 mdm_drv->mdm_queue = alloc_workqueue("mdm_drv_queue", 0, 0);
269 if (!mdm_drv->mdm_queue) {
270 dev_err(&esoc_clink->dev, "could not create mdm_queue\n");
271 goto queue_err;
272 }
273 esoc_set_drv_data(esoc_clink, mdm_drv);
274 init_completion(&mdm_drv->boot_done);
275 init_completion(&mdm_drv->req_eng_wait);
276 INIT_WORK(&mdm_drv->ssr_work, mdm_ssr_fn);
277 mdm_drv->esoc_clink = esoc_clink;
278 mdm_drv->mode = PWR_OFF;
279 mdm_drv->boot_fail = false;
280 mdm_drv->esoc_restart.notifier_call = esoc_msm_restart_handler;
281 ret = register_reboot_notifier(&mdm_drv->esoc_restart);
282 if (ret)
283 dev_err(&esoc_clink->dev, "register for reboot failed\n");
Hanumant Singhb35bbc62015-08-10 21:23:16 -0700284 ret = mdm_dbg_eng_init(drv, esoc_clink);
Trilok Soni8ca296e2016-08-21 23:38:25 -0700285 if (ret) {
286 debug_init_done = false;
287 dev_err(&esoc_clink->dev, "dbg engine failure\n");
288 } else {
289 dev_dbg(&esoc_clink->dev, "dbg engine initialized\n");
290 debug_init_done = true;
291 }
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800292 return 0;
293queue_err:
294 esoc_clink_unregister_ssr(esoc_clink);
295ssr_err:
296 esoc_clink_unregister_cmd_eng(esoc_clink, esoc_eng);
297 return ret;
298}
299
300static struct esoc_compat compat_table[] = {
301 { .name = "MDM9x25",
302 .data = NULL,
303 },
304 {
305 .name = "MDM9x35",
306 .data = NULL,
307 },
308 {
309 .name = "MDM9x55",
310 .data = NULL,
311 },
Raghavendra Rao Anantacaa60aa2017-10-20 11:27:12 -0700312 {
313 .name = "SDXPOORWILLS",
314 .data = NULL,
315 },
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800316};
317
318static struct esoc_drv esoc_ssr_drv = {
319 .owner = THIS_MODULE,
320 .probe = esoc_ssr_probe,
321 .compat_table = compat_table,
322 .compat_entries = ARRAY_SIZE(compat_table),
323 .driver = {
324 .name = "mdm-4x",
325 },
326};
327
328int __init esoc_ssr_init(void)
329{
330 return esoc_drv_register(&esoc_ssr_drv);
331}
332module_init(esoc_ssr_init);
333MODULE_LICENSE("GPL v2");