blob: 31cd8c49a878126a0bedd22de0a997168baf3730 [file] [log] [blame]
Satya Durga Srinivasu Prabhala377d32f2017-03-15 11:09:28 -07001/* Copyright (c) 2013-2017, 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>
16#include "esoc.h"
Trilok Soni8ca296e2016-08-21 23:38:25 -070017#include "mdm-dbg.h"
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -080018
19enum {
20 PWR_OFF = 0x1,
21 PWR_ON,
22 BOOT,
23 RUN,
24 CRASH,
25 IN_DEBUG,
26 SHUTDOWN,
27 RESET,
28 PEER_CRASH,
29};
30
31struct mdm_drv {
32 unsigned int mode;
33 struct esoc_eng cmd_eng;
34 struct completion boot_done;
35 struct completion req_eng_wait;
36 struct esoc_clink *esoc_clink;
37 bool boot_fail;
38 struct workqueue_struct *mdm_queue;
39 struct work_struct ssr_work;
40 struct notifier_block esoc_restart;
41};
42#define to_mdm_drv(d) container_of(d, struct mdm_drv, cmd_eng)
43
44static int esoc_msm_restart_handler(struct notifier_block *nb,
45 unsigned long action, void *data)
46{
47 struct mdm_drv *mdm_drv = container_of(nb, struct mdm_drv,
48 esoc_restart);
49 struct esoc_clink *esoc_clink = mdm_drv->esoc_clink;
50 const struct esoc_clink_ops * const clink_ops = esoc_clink->clink_ops;
51
52 if (action == SYS_RESTART) {
Trilok Soni8ca296e2016-08-21 23:38:25 -070053 if (mdm_dbg_stall_notify(ESOC_PRIMARY_REBOOT))
54 return NOTIFY_OK;
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -080055 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}
60static 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
65 switch (evt) {
66 case ESOC_INVALID_STATE:
67 mdm_drv->boot_fail = true;
68 complete(&mdm_drv->boot_done);
69 break;
70 case ESOC_RUN_STATE:
71 mdm_drv->boot_fail = false;
72 mdm_drv->mode = RUN,
73 complete(&mdm_drv->boot_done);
74 break;
75 case ESOC_UNEXPECTED_RESET:
76 case ESOC_ERR_FATAL:
77 if (mdm_drv->mode == CRASH)
78 return;
79 mdm_drv->mode = CRASH;
80 queue_work(mdm_drv->mdm_queue, &mdm_drv->ssr_work);
81 break;
82 case ESOC_REQ_ENG_ON:
83 complete(&mdm_drv->req_eng_wait);
84 break;
85 default:
86 break;
87 }
88}
89
90static void mdm_ssr_fn(struct work_struct *work)
91{
92 struct mdm_drv *mdm_drv = container_of(work, struct mdm_drv, ssr_work);
93
94 /*
95 * If restarting esoc fails, the SSR framework triggers a kernel panic
96 */
97 esoc_clink_request_ssr(mdm_drv->esoc_clink);
98}
99
100static void mdm_crash_shutdown(const struct subsys_desc *mdm_subsys)
101{
102 struct esoc_clink *esoc_clink =
103 container_of(mdm_subsys,
104 struct esoc_clink,
105 subsys);
106 const struct esoc_clink_ops * const clink_ops = esoc_clink->clink_ops;
107
Trilok Soni8ca296e2016-08-21 23:38:25 -0700108 if (mdm_dbg_stall_notify(ESOC_PRIMARY_CRASH))
109 return;
110
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800111 clink_ops->notify(ESOC_PRIMARY_CRASH, esoc_clink);
112}
113
114static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys,
115 bool force_stop)
116{
117 int ret;
118 struct esoc_clink *esoc_clink =
119 container_of(crashed_subsys, struct esoc_clink, subsys);
120 struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);
121 const struct esoc_clink_ops * const clink_ops = esoc_clink->clink_ops;
122
123 if (mdm_drv->mode == CRASH || mdm_drv->mode == PEER_CRASH) {
Trilok Soni8ca296e2016-08-21 23:38:25 -0700124 if (mdm_dbg_stall_cmd(ESOC_PREPARE_DEBUG))
125 /* We want to mask debug command.
126 * In this case return success
127 * to move to next stage
128 */
129 return 0;
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800130 ret = clink_ops->cmd_exe(ESOC_PREPARE_DEBUG,
131 esoc_clink);
132 if (ret) {
133 dev_err(&esoc_clink->dev, "failed to enter debug\n");
134 return ret;
135 }
136 mdm_drv->mode = IN_DEBUG;
137 } else if (!force_stop) {
138 if (esoc_clink->subsys.sysmon_shutdown_ret)
139 ret = clink_ops->cmd_exe(ESOC_FORCE_PWR_OFF,
140 esoc_clink);
Trilok Soni8ca296e2016-08-21 23:38:25 -0700141 else {
142 if (mdm_dbg_stall_cmd(ESOC_PWR_OFF))
143 /* Since power off command is masked
144 * we return success, and leave the state
145 * of the command engine as is.
146 */
147 return 0;
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800148 ret = clink_ops->cmd_exe(ESOC_PWR_OFF, esoc_clink);
Trilok Soni8ca296e2016-08-21 23:38:25 -0700149 }
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800150 if (ret) {
151 dev_err(&esoc_clink->dev, "failed to exe power off\n");
152 return ret;
153 }
154 mdm_drv->mode = PWR_OFF;
155 }
156 return 0;
157}
158
159static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
160{
161 int ret;
162 struct esoc_clink *esoc_clink =
163 container_of(crashed_subsys, struct esoc_clink,
164 subsys);
165 struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);
166 const struct esoc_clink_ops * const clink_ops = esoc_clink->clink_ops;
167
168 if (!esoc_req_eng_enabled(esoc_clink)) {
169 dev_dbg(&esoc_clink->dev, "Wait for req eng registration\n");
170 wait_for_completion(&mdm_drv->req_eng_wait);
171 }
172 if (mdm_drv->mode == PWR_OFF) {
Trilok Soni8ca296e2016-08-21 23:38:25 -0700173 if (mdm_dbg_stall_cmd(ESOC_PWR_ON))
174 return -EBUSY;
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800175 ret = clink_ops->cmd_exe(ESOC_PWR_ON, esoc_clink);
176 if (ret) {
177 dev_err(&esoc_clink->dev, "pwr on fail\n");
178 return ret;
179 }
180 } else if (mdm_drv->mode == IN_DEBUG) {
181 ret = clink_ops->cmd_exe(ESOC_EXIT_DEBUG, esoc_clink);
182 if (ret) {
183 dev_err(&esoc_clink->dev, "cannot exit debug mode\n");
184 return ret;
185 }
186 mdm_drv->mode = PWR_OFF;
187 ret = clink_ops->cmd_exe(ESOC_PWR_ON, esoc_clink);
188 if (ret) {
189 dev_err(&esoc_clink->dev, "pwr on fail\n");
190 return ret;
191 }
192 }
193 wait_for_completion(&mdm_drv->boot_done);
194 if (mdm_drv->boot_fail) {
195 dev_err(&esoc_clink->dev, "booting failed\n");
196 return -EIO;
197 }
198 return 0;
199}
200
201static int mdm_subsys_ramdumps(int want_dumps,
202 const struct subsys_desc *crashed_subsys)
203{
204 int ret;
205 struct esoc_clink *esoc_clink =
206 container_of(crashed_subsys, struct esoc_clink,
207 subsys);
208 const struct esoc_clink_ops * const clink_ops = esoc_clink->clink_ops;
209
210 if (want_dumps) {
211 ret = clink_ops->cmd_exe(ESOC_EXE_DEBUG, esoc_clink);
212 if (ret) {
213 dev_err(&esoc_clink->dev, "debugging failed\n");
214 return ret;
215 }
216 }
217 return 0;
218}
219
220static int mdm_register_ssr(struct esoc_clink *esoc_clink)
221{
222 esoc_clink->subsys.shutdown = mdm_subsys_shutdown;
223 esoc_clink->subsys.ramdump = mdm_subsys_ramdumps;
224 esoc_clink->subsys.powerup = mdm_subsys_powerup;
225 esoc_clink->subsys.crash_shutdown = mdm_crash_shutdown;
226 return esoc_clink_register_ssr(esoc_clink);
227}
228
229int esoc_ssr_probe(struct esoc_clink *esoc_clink, struct esoc_drv *drv)
230{
231 int ret;
232 struct mdm_drv *mdm_drv;
233 struct esoc_eng *esoc_eng;
234
235 mdm_drv = devm_kzalloc(&esoc_clink->dev, sizeof(*mdm_drv), GFP_KERNEL);
Satya Durga Srinivasu Prabhala377d32f2017-03-15 11:09:28 -0700236 if (IS_ERR_OR_NULL(mdm_drv))
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800237 return PTR_ERR(mdm_drv);
238 esoc_eng = &mdm_drv->cmd_eng;
239 esoc_eng->handle_clink_evt = mdm_handle_clink_evt;
240 ret = esoc_clink_register_cmd_eng(esoc_clink, esoc_eng);
241 if (ret) {
242 dev_err(&esoc_clink->dev, "failed to register cmd engine\n");
243 return ret;
244 }
245 ret = mdm_register_ssr(esoc_clink);
246 if (ret)
247 goto ssr_err;
248 mdm_drv->mdm_queue = alloc_workqueue("mdm_drv_queue", 0, 0);
249 if (!mdm_drv->mdm_queue) {
250 dev_err(&esoc_clink->dev, "could not create mdm_queue\n");
251 goto queue_err;
252 }
253 esoc_set_drv_data(esoc_clink, mdm_drv);
254 init_completion(&mdm_drv->boot_done);
255 init_completion(&mdm_drv->req_eng_wait);
256 INIT_WORK(&mdm_drv->ssr_work, mdm_ssr_fn);
257 mdm_drv->esoc_clink = esoc_clink;
258 mdm_drv->mode = PWR_OFF;
259 mdm_drv->boot_fail = false;
260 mdm_drv->esoc_restart.notifier_call = esoc_msm_restart_handler;
261 ret = register_reboot_notifier(&mdm_drv->esoc_restart);
262 if (ret)
263 dev_err(&esoc_clink->dev, "register for reboot failed\n");
Hanumant Singhb35bbc62015-08-10 21:23:16 -0700264 ret = mdm_dbg_eng_init(drv, esoc_clink);
Trilok Soni8ca296e2016-08-21 23:38:25 -0700265 if (ret) {
266 debug_init_done = false;
267 dev_err(&esoc_clink->dev, "dbg engine failure\n");
268 } else {
269 dev_dbg(&esoc_clink->dev, "dbg engine initialized\n");
270 debug_init_done = true;
271 }
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800272 return 0;
273queue_err:
274 esoc_clink_unregister_ssr(esoc_clink);
275ssr_err:
276 esoc_clink_unregister_cmd_eng(esoc_clink, esoc_eng);
277 return ret;
278}
279
280static struct esoc_compat compat_table[] = {
281 { .name = "MDM9x25",
282 .data = NULL,
283 },
284 {
285 .name = "MDM9x35",
286 .data = NULL,
287 },
288 {
289 .name = "MDM9x55",
290 .data = NULL,
291 },
292};
293
294static struct esoc_drv esoc_ssr_drv = {
295 .owner = THIS_MODULE,
296 .probe = esoc_ssr_probe,
297 .compat_table = compat_table,
298 .compat_entries = ARRAY_SIZE(compat_table),
299 .driver = {
300 .name = "mdm-4x",
301 },
302};
303
304int __init esoc_ssr_init(void)
305{
306 return esoc_drv_register(&esoc_ssr_drv);
307}
308module_init(esoc_ssr_init);
309MODULE_LICENSE("GPL v2");