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