blob: a61588a714d426458085af8cb0f099a59aa051ca [file] [log] [blame]
Hanumant Singhb35bbc62015-08-10 21:23:16 -07001/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
Trilok Soni8ca296e2016-08-21 23:38:25 -07002 *
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#include <linux/atomic.h>
13#include <linux/device.h>
14#include "esoc.h"
15
16/*
17 * cmd_mask : Specifies if a command/notifier is masked, and
18 * whats the trigger value for mask to take effect.
19 * @mask_trigger: trigger value for mask.
20 * @mask: boolean to determine if command should be masked.
21 */
22struct esoc_mask {
23 atomic_t mask_trigger;
24 bool mask;
25};
26
27/*
28 * manual_to_esoc_cmd: Converts a user provided command
29 * to a corresponding esoc command.
30 * @cmd: ESOC command
31 * @manual_cmd: user specified command string.
32 */
33struct manual_to_esoc_cmd {
34 unsigned int cmd;
35 char manual_cmd[20];
36};
37
38/*
39 * manual_to_esoc_notify: Converts a user provided notification
40 * to corresponding esoc notification for Primary SOC.
41 * @notfication: ESOC notification.
42 * @manual_notifier: user specified notification string.
43 */
44struct manual_to_esoc_notify {
45 unsigned int notify;
46 char manual_notify[20];
47};
48
49static const struct manual_to_esoc_cmd cmd_map[] = {
50 {
51 .cmd = ESOC_PWR_ON,
52 .manual_cmd = "PON",
53 },
54 {
55 .cmd = ESOC_PREPARE_DEBUG,
56 .manual_cmd = "ENTER_DLOAD",
57 },
58 { .cmd = ESOC_PWR_OFF,
59 .manual_cmd = "POFF",
60 },
61 {
62 .cmd = ESOC_FORCE_PWR_OFF,
63 .manual_cmd = "FORCE_POFF",
64 },
65};
66
67static struct esoc_mask cmd_mask[] = {
68 [ESOC_PWR_ON] = {
69 .mask = false,
70 .mask_trigger = ATOMIC_INIT(1),
71 },
72 [ESOC_PREPARE_DEBUG] = {
73 .mask = false,
74 .mask_trigger = ATOMIC_INIT(0),
75 },
76 [ESOC_PWR_OFF] = {
77 .mask = false,
78 .mask_trigger = ATOMIC_INIT(0),
79 },
80 [ESOC_FORCE_PWR_OFF] = {
81 .mask = false,
82 .mask_trigger = ATOMIC_INIT(0),
83 },
84};
85
86static const struct manual_to_esoc_notify notify_map[] = {
87 {
88 .notify = ESOC_PRIMARY_REBOOT,
89 .manual_notify = "REBOOT",
90 },
91 {
92 .notify = ESOC_PRIMARY_CRASH,
93 .manual_notify = "PANIC",
94 },
95};
96
97static struct esoc_mask notify_mask[] = {
98 [ESOC_PRIMARY_REBOOT] = {
99 .mask = false,
100 .mask_trigger = ATOMIC_INIT(0),
101 },
102 [ESOC_PRIMARY_CRASH] = {
103 .mask = false,
104 .mask_trigger = ATOMIC_INIT(0),
105 },
106};
107
108bool dbg_check_cmd_mask(unsigned int cmd)
109{
110 pr_debug("command to mask %d\n", cmd);
111 if (cmd_mask[cmd].mask)
112 return atomic_add_negative(-1, &cmd_mask[cmd].mask_trigger);
113 else
114 return false;
115}
116EXPORT_SYMBOL(dbg_check_cmd_mask);
117
118bool dbg_check_notify_mask(unsigned int notify)
119{
120 pr_debug("notifier to mask %d\n", notify);
121 if (notify_mask[notify].mask)
122 return atomic_add_negative(-1,
123 &notify_mask[notify].mask_trigger);
124 else
125 return false;
126}
127EXPORT_SYMBOL(dbg_check_notify_mask);
128/*
129 * Create driver attributes that let you mask
130 * specific commands.
131 */
132static ssize_t cmd_mask_store(struct device_driver *drv, const char *buf,
133 size_t count)
134{
135 unsigned int cmd, i;
136
137 pr_debug("user input command %s", buf);
138 for (i = 0; i < ARRAY_SIZE(cmd_map); i++) {
139 if (!strcmp(cmd_map[i].manual_cmd, buf)) {
140 /*
141 * Map manual command string to ESOC command
142 * set mask for ESOC command
143 */
144 cmd = cmd_map[i].cmd;
145 cmd_mask[cmd].mask = true;
146 pr_debug("Setting mask for manual command %s\n",
147 buf);
148 break;
149 }
150 }
151 if (i >= ARRAY_SIZE(cmd_map))
152 pr_err("invalid command specified\n");
153 return count;
154}
155static DRIVER_ATTR(command_mask, 00200, NULL, cmd_mask_store);
156
157static ssize_t notifier_mask_store(struct device_driver *drv, const char *buf,
158 size_t count)
159{
160 unsigned int notify, i;
161
162 pr_debug("user input notifier %s", buf);
163 for (i = 0; i < ARRAY_SIZE(notify_map); i++) {
164 if (!strcmp(buf, notify_map[i].manual_notify)) {
165 /*
166 * Map manual notifier string to primary soc
167 * notifier. Also set mask for the notifier.
168 */
169 notify = notify_map[i].notify;
170 notify_mask[notify].mask = true;
171 pr_debug("Setting mask for manual notification %s\n",
172 buf);
173 break;
174 }
175 }
176 if (i >= ARRAY_SIZE(notify_map))
177 pr_err("invalid notifier specified\n");
178 return count;
179}
180static DRIVER_ATTR(notifier_mask, 00200, NULL, notifier_mask_store);
181
Hanumant Singhb35bbc62015-08-10 21:23:16 -0700182#ifdef CONFIG_MDM_DBG_REQ_ENG
183static struct esoc_clink *dbg_clink;
184/* Last recorded request from esoc */
185static enum esoc_req last_req;
186static DEFINE_SPINLOCK(req_lock);
187/*
188 * esoc_to_user: Conversion of esoc ids to user visible strings
189 * id: esoc request, command, notifier, event id
190 * str: string equivalent of the above
191 */
192struct esoc_to_user {
193 unsigned int id;
194 char str[20];
195};
196
197static struct esoc_to_user in_to_resp[] = {
198 {
199 .id = ESOC_IMG_XFER_DONE,
200 .str = "XFER_DONE",
201 },
202 {
203 .id = ESOC_BOOT_DONE,
204 .str = "BOOT_DONE",
205 },
206 {
207 .id = ESOC_BOOT_FAIL,
208 .str = "BOOT_FAIL",
209 },
210 {
211 .id = ESOC_IMG_XFER_RETRY,
212 .str = "XFER_RETRY",
213 },
214 { .id = ESOC_IMG_XFER_FAIL,
215 .str = "XFER_FAIL",
216 },
217 {
218 .id = ESOC_UPGRADE_AVAILABLE,
219 .str = "UPGRADE",
220 },
221 { .id = ESOC_DEBUG_DONE,
222 .str = "DEBUG_DONE",
223 },
224 {
225 .id = ESOC_DEBUG_FAIL,
226 .str = "DEBUG_FAIL",
227 },
228};
229
230static struct esoc_to_user req_to_str[] = {
231 {
232 .id = ESOC_REQ_IMG,
233 .str = "REQ_IMG",
234 },
235 {
236 .id = ESOC_REQ_DEBUG,
237 .str = "REQ_DEBUG",
238 },
239 {
240 .id = ESOC_REQ_SHUTDOWN,
241 .str = "REQ_SHUTDOWN",
242 },
243};
244
245static ssize_t req_eng_resp_store(struct device_driver *drv, const char *buf,
246 size_t count)
247{
248 unsigned int i;
249 const struct esoc_clink_ops *const clink_ops = dbg_clink->clink_ops;
250
251 dev_dbg(&dbg_clink->dev, "user input req eng response %s\n", buf);
252 for (i = 0; i < ARRAY_SIZE(in_to_resp); i++) {
253 size_t len1 = strlen(buf);
254 size_t len2 = strlen(in_to_resp[i].str);
255
256 if (len1 == len2 && !strcmp(buf, in_to_resp[i].str)) {
257 clink_ops->notify(in_to_resp[i].id, dbg_clink);
258 break;
259 }
260 }
261 if (i > ARRAY_SIZE(in_to_resp))
262 dev_err(&dbg_clink->dev, "Invalid resp %s, specified\n", buf);
263 return count;
264}
265
266static DRIVER_ATTR(req_eng_resp, 0200, NULL, req_eng_resp_store);
267
268static ssize_t last_esoc_req_show(struct device_driver *drv, char *buf)
269{
270 unsigned int i;
271 unsigned long flags;
Raghavendra Rao Ananta5d025982017-10-16 11:41:55 -0700272 size_t count = 0;
Hanumant Singhb35bbc62015-08-10 21:23:16 -0700273
274 spin_lock_irqsave(&req_lock, flags);
275 for (i = 0; i < ARRAY_SIZE(req_to_str); i++) {
276 if (last_req == req_to_str[i].id) {
277 count = snprintf(buf, PAGE_SIZE, "%s\n",
278 req_to_str[i].str);
279 break;
280 }
281 }
282 spin_unlock_irqrestore(&req_lock, flags);
283 return count;
284}
285static DRIVER_ATTR(last_esoc_req, 0400, last_esoc_req_show, NULL);
286
287static void esoc_handle_req(enum esoc_req req, struct esoc_eng *eng)
288{
289 unsigned long flags;
290
291 spin_lock_irqsave(&req_lock, flags);
292 last_req = req;
293 spin_unlock_irqrestore(&req_lock, flags);
294}
295
296static void esoc_handle_evt(enum esoc_evt evt, struct esoc_eng *eng)
297{
298}
299
300static struct esoc_eng dbg_req_eng = {
301 .handle_clink_req = esoc_handle_req,
302 .handle_clink_evt = esoc_handle_evt,
303};
304
305int register_dbg_req_eng(struct esoc_clink *clink,
306 struct device_driver *drv)
307{
308 int ret;
309
310 dbg_clink = clink;
311 ret = driver_create_file(drv, &driver_attr_req_eng_resp);
312 if (ret)
313 return ret;
314 ret = driver_create_file(drv, &driver_attr_last_esoc_req);
315 if (ret) {
316 dev_err(&clink->dev, "Unable to create last esoc req\n");
317 goto last_req_err;
318 }
319 ret = esoc_clink_register_req_eng(clink, &dbg_req_eng);
320 if (ret) {
321 pr_err("Unable to register req eng\n");
322 goto req_eng_fail;
323 }
324 spin_lock_init(&req_lock);
325 return 0;
326last_req_err:
327 driver_remove_file(drv, &driver_attr_last_esoc_req);
328req_eng_fail:
329 driver_remove_file(drv, &driver_attr_req_eng_resp);
330 return ret;
331}
332#else
333int register_dbg_req_eng(struct esoc_clink *clink, struct device_driver *d)
334{
335 return 0;
336}
337#endif
338
339int mdm_dbg_eng_init(struct esoc_drv *esoc_drv,
340 struct esoc_clink *clink)
Trilok Soni8ca296e2016-08-21 23:38:25 -0700341{
342 int ret;
343 struct device_driver *drv = &esoc_drv->driver;
344
345 ret = driver_create_file(drv, &driver_attr_command_mask);
346 if (ret) {
347 pr_err("Unable to create command mask file\n");
348 goto cmd_mask_err;
349 }
350 ret = driver_create_file(drv, &driver_attr_notifier_mask);
351 if (ret) {
352 pr_err("Unable to create notify mask file\n");
353 goto notify_mask_err;
354 }
Hanumant Singhb35bbc62015-08-10 21:23:16 -0700355 ret = register_dbg_req_eng(clink, drv);
356 if (ret) {
357 pr_err("Failed to register esoc dbg req eng\n");
358 goto dbg_req_fail;
359 }
Trilok Soni8ca296e2016-08-21 23:38:25 -0700360 return 0;
Hanumant Singhb35bbc62015-08-10 21:23:16 -0700361dbg_req_fail:
362 driver_remove_file(drv, &driver_attr_notifier_mask);
Trilok Soni8ca296e2016-08-21 23:38:25 -0700363notify_mask_err:
364 driver_remove_file(drv, &driver_attr_command_mask);
365cmd_mask_err:
366 return ret;
367}
368EXPORT_SYMBOL(mdm_dbg_eng_init);
369MODULE_LICENSE("GPL v2");