OSDN Git Service

266eaccf8b69874f70c05e58d62c10859de1ad7f
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / drivers / esoc / esoc-mdm-drv.c
1 /* Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved.
2  *
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>
16 #include <linux/of.h>
17 #include "esoc.h"
18 #include "mdm-dbg.h"
19
20 enum {
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
32 struct mdm_drv {
33         unsigned 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
45 static 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         if (action == SYS_RESTART) {
53                 if (mdm_dbg_stall_notify(ESOC_PRIMARY_REBOOT))
54                         return NOTIFY_OK;
55                 dev_dbg(&esoc_clink->dev, "Notifying esoc of cold reboot\n");
56                 clink_ops->notify(ESOC_PRIMARY_REBOOT, esoc_clink);
57         }
58         return NOTIFY_OK;
59 }
60 static void mdm_handle_clink_evt(enum esoc_evt evt,
61                                         struct esoc_eng *eng)
62 {
63         struct mdm_drv *mdm_drv = to_mdm_drv(eng);
64         switch (evt) {
65         case ESOC_INVALID_STATE:
66                 mdm_drv->boot_fail = true;
67                 complete(&mdm_drv->boot_done);
68                 break;
69         case ESOC_RUN_STATE:
70                 mdm_drv->boot_fail = false;
71                 mdm_drv->mode = RUN,
72                 complete(&mdm_drv->boot_done);
73                 break;
74         case ESOC_UNEXPECTED_RESET:
75         case ESOC_ERR_FATAL:
76                 /*
77                  * Modem can crash while we are waiting for boot_done during
78                  * a subsystem_get(). Setting mode to CRASH will prevent a
79                  * subsequent subsystem_get() from entering poweron ops. Avoid
80                  * this by seting mode to CRASH only if device was up and
81                  * running.
82                  */
83                 if (mdm_drv->mode == CRASH || mdm_drv->mode != RUN)
84                         return;
85                 mdm_drv->mode = CRASH;
86                 queue_work(mdm_drv->mdm_queue, &mdm_drv->ssr_work);
87                 break;
88         case ESOC_REQ_ENG_ON:
89                 complete(&mdm_drv->req_eng_wait);
90                 break;
91         default:
92                 break;
93         }
94 }
95
96 static void mdm_ssr_fn(struct work_struct *work)
97 {
98         struct mdm_drv *mdm_drv = container_of(work, struct mdm_drv, ssr_work);
99
100         /*
101          * If restarting esoc fails, the SSR framework triggers a kernel panic
102          */
103         esoc_clink_request_ssr(mdm_drv->esoc_clink);
104         return;
105 }
106
107 static void mdm_crash_shutdown(const struct subsys_desc *mdm_subsys)
108 {
109         struct esoc_clink *esoc_clink =
110                                         container_of(mdm_subsys,
111                                                         struct esoc_clink,
112                                                                 subsys);
113         const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops;
114         if (mdm_dbg_stall_notify(ESOC_PRIMARY_CRASH))
115                 return;
116         clink_ops->notify(ESOC_PRIMARY_CRASH, esoc_clink);
117 }
118
119 static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys,
120                                                         bool force_stop)
121 {
122         int ret;
123         struct esoc_clink *esoc_clink =
124          container_of(crashed_subsys, struct esoc_clink, subsys);
125         struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);
126         const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops;
127
128         if (mdm_drv->mode == CRASH || mdm_drv->mode == PEER_CRASH) {
129                 if (mdm_dbg_stall_cmd(ESOC_PREPARE_DEBUG))
130                         /* We want to mask debug command.
131                          * In this case return success
132                          * to move to next stage
133                          */
134                         return 0;
135                 ret = clink_ops->cmd_exe(ESOC_PREPARE_DEBUG,
136                                                         esoc_clink);
137                 if (ret) {
138                         dev_err(&esoc_clink->dev, "failed to enter debug\n");
139                         return ret;
140                 }
141                 mdm_drv->mode = IN_DEBUG;
142         } else if (!force_stop) {
143                 if (esoc_clink->subsys.sysmon_shutdown_ret)
144                         ret = clink_ops->cmd_exe(ESOC_FORCE_PWR_OFF,
145                                                         esoc_clink);
146                 else {
147                         if (mdm_dbg_stall_cmd(ESOC_PWR_OFF))
148                                 /* Since power off command is masked
149                                  * we return success, and leave the state
150                                  * of the command engine as is.
151                                  */
152                                 return 0;
153                         ret = clink_ops->cmd_exe(ESOC_PWR_OFF, esoc_clink);
154                 }
155                 if (ret) {
156                         dev_err(&esoc_clink->dev, "failed to exe power off\n");
157                         return ret;
158                 }
159                 mdm_drv->mode = PWR_OFF;
160         }
161         return 0;
162 }
163
164 static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
165 {
166         int ret;
167         struct esoc_clink *esoc_clink =
168                                 container_of(crashed_subsys, struct esoc_clink,
169                                                                 subsys);
170         struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);
171         const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops;
172         int timeout = INT_MAX;
173
174         if (!esoc_clink->auto_boot && !esoc_req_eng_enabled(esoc_clink)) {
175                 dev_dbg(&esoc_clink->dev, "Wait for req eng registration\n");
176                 wait_for_completion(&mdm_drv->req_eng_wait);
177         }
178         if (mdm_drv->mode == PWR_OFF) {
179                 if (mdm_dbg_stall_cmd(ESOC_PWR_ON))
180                         return -EBUSY;
181                 ret = clink_ops->cmd_exe(ESOC_PWR_ON, esoc_clink);
182                 if (ret) {
183                         dev_err(&esoc_clink->dev, "pwr on fail\n");
184                         return ret;
185                 }
186         } else if (mdm_drv->mode == IN_DEBUG) {
187                 ret = clink_ops->cmd_exe(ESOC_EXIT_DEBUG, esoc_clink);
188                 if (ret) {
189                         dev_err(&esoc_clink->dev, "cannot exit debug mode\n");
190                         return ret;
191                 }
192                 mdm_drv->mode = PWR_OFF;
193                 ret = clink_ops->cmd_exe(ESOC_PWR_ON, esoc_clink);
194                 if (ret) {
195                         dev_err(&esoc_clink->dev, "pwr on fail\n");
196                         return ret;
197                 }
198         }
199
200         /*
201          * In autoboot case, it is possible that we can forever wait for
202          * boot completion, when esoc fails to boot. This is because there
203          * is no helper application which can alert esoc driver about boot
204          * failure. Prevent going to wait forever in such case.
205          */
206         if (esoc_clink->auto_boot)
207                 timeout = 10 * HZ;
208         ret = wait_for_completion_timeout(&mdm_drv->boot_done, timeout);
209         if (mdm_drv->boot_fail || ret <= 0) {
210                 dev_err(&esoc_clink->dev, "booting failed\n");
211                 return -EIO;
212         }
213         return 0;
214 }
215
216 static int mdm_subsys_ramdumps(int want_dumps,
217                                 const struct subsys_desc *crashed_subsys)
218 {
219         int ret;
220         struct esoc_clink *esoc_clink =
221                                 container_of(crashed_subsys, struct esoc_clink,
222                                                                 subsys);
223         const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops;
224
225         if (want_dumps) {
226                 ret = clink_ops->cmd_exe(ESOC_EXE_DEBUG, esoc_clink);
227                 if (ret) {
228                         dev_err(&esoc_clink->dev, "debugging failed\n");
229                         return ret;
230                 }
231         }
232         return 0;
233 }
234
235 static int mdm_register_ssr(struct esoc_clink *esoc_clink)
236 {
237         struct subsys_desc *subsys = &esoc_clink->subsys;
238
239         subsys->shutdown = mdm_subsys_shutdown;
240         subsys->ramdump = mdm_subsys_ramdumps;
241         subsys->powerup = mdm_subsys_powerup;
242         subsys->crash_shutdown = mdm_crash_shutdown;
243         return esoc_clink_register_ssr(esoc_clink);
244 }
245
246 int esoc_ssr_probe(struct esoc_clink *esoc_clink, struct esoc_drv *drv)
247 {
248         int ret;
249         struct mdm_drv *mdm_drv;
250         struct esoc_eng *esoc_eng;
251
252         mdm_drv = devm_kzalloc(&esoc_clink->dev, sizeof(*mdm_drv), GFP_KERNEL);
253         if (IS_ERR_OR_NULL(mdm_drv))
254                 return PTR_ERR(mdm_drv);
255         esoc_eng = &mdm_drv->cmd_eng;
256         esoc_eng->handle_clink_evt = mdm_handle_clink_evt;
257         ret = esoc_clink_register_cmd_eng(esoc_clink, esoc_eng);
258         if (ret) {
259                 dev_err(&esoc_clink->dev, "failed to register cmd engine\n");
260                 return ret;
261         }
262         ret = mdm_register_ssr(esoc_clink);
263         if (ret)
264                 goto ssr_err;
265         mdm_drv->mdm_queue = alloc_workqueue("mdm_drv_queue", 0, 0);
266         if (!mdm_drv->mdm_queue) {
267                 dev_err(&esoc_clink->dev, "could not create mdm_queue\n");
268                 goto queue_err;
269         }
270         esoc_set_drv_data(esoc_clink, mdm_drv);
271         init_completion(&mdm_drv->boot_done);
272         init_completion(&mdm_drv->req_eng_wait);
273         INIT_WORK(&mdm_drv->ssr_work, mdm_ssr_fn);
274         mdm_drv->esoc_clink = esoc_clink;
275         mdm_drv->mode = PWR_OFF;
276         mdm_drv->boot_fail = false;
277         mdm_drv->esoc_restart.notifier_call = esoc_msm_restart_handler;
278         ret = register_reboot_notifier(&mdm_drv->esoc_restart);
279         if (ret)
280                 dev_err(&esoc_clink->dev, "register for reboot failed\n");
281         ret = mdm_dbg_eng_init(drv, esoc_clink);
282         if (ret) {
283                 debug_init_done = false;
284                 dev_err(&esoc_clink->dev, "dbg engine failure\n");
285         } else {
286                 dev_dbg(&esoc_clink->dev, "dbg engine initialized\n");
287                 debug_init_done = true;
288         }
289         return 0;
290 queue_err:
291         esoc_clink_unregister_ssr(esoc_clink);
292 ssr_err:
293         esoc_clink_unregister_cmd_eng(esoc_clink, esoc_eng);
294         return ret;
295 }
296
297 static struct esoc_compat compat_table[] = {
298         {       .name = "MDM9x25",
299                 .data = NULL,
300         },
301         {
302                 .name = "MDM9x35",
303                 .data = NULL,
304         },
305         {
306                 .name = "MDM9x55",
307                 .data = NULL,
308         },
309 };
310
311 static struct esoc_drv esoc_ssr_drv = {
312         .owner = THIS_MODULE,
313         .probe = esoc_ssr_probe,
314         .compat_table = compat_table,
315         .compat_entries = ARRAY_SIZE(compat_table),
316         .driver = {
317                 .name = "mdm-4x",
318         },
319 };
320
321 int __init esoc_ssr_init(void)
322 {
323         return esoc_drv_register(&esoc_ssr_drv);
324 }
325 module_init(esoc_ssr_init);
326 MODULE_LICENSE("GPL v2");