1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Intel MIC Platform Software Stack (MPSS)
4  *
5  * Copyright(c) 2015 Intel Corporation.
6  *
7  * Intel MIC Coprocessor State Management (COSM) Driver
8  */
9 #include <linux/slab.h>
10 #include "cosm_main.h"
11 
12 /*
13  * A state-to-string lookup table, for exposing a human readable state
14  * via sysfs. Always keep in sync with enum cosm_states
15  */
16 const char * const cosm_state_string[] = {
17 	[MIC_READY] = "ready",
18 	[MIC_BOOTING] = "booting",
19 	[MIC_ONLINE] = "online",
20 	[MIC_SHUTTING_DOWN] = "shutting_down",
21 	[MIC_RESETTING] = "resetting",
22 	[MIC_RESET_FAILED] = "reset_failed",
23 };
24 
25 /*
26  * A shutdown-status-to-string lookup table, for exposing a human
27  * readable state via sysfs. Always keep in sync with enum cosm_shutdown_status
28  */
29 const char * const cosm_shutdown_status_string[] = {
30 	[MIC_NOP] = "nop",
31 	[MIC_CRASHED] = "crashed",
32 	[MIC_HALTED] = "halted",
33 	[MIC_POWER_OFF] = "poweroff",
34 	[MIC_RESTART] = "restart",
35 };
36 
cosm_set_shutdown_status(struct cosm_device * cdev,u8 shutdown_status)37 void cosm_set_shutdown_status(struct cosm_device *cdev, u8 shutdown_status)
38 {
39 	dev_dbg(&cdev->dev, "Shutdown Status %s -> %s\n",
40 		cosm_shutdown_status_string[cdev->shutdown_status],
41 		cosm_shutdown_status_string[shutdown_status]);
42 	cdev->shutdown_status = shutdown_status;
43 }
44 
cosm_set_state(struct cosm_device * cdev,u8 state)45 void cosm_set_state(struct cosm_device *cdev, u8 state)
46 {
47 	dev_dbg(&cdev->dev, "State %s -> %s\n",
48 		cosm_state_string[cdev->state],
49 		cosm_state_string[state]);
50 	cdev->state = state;
51 	sysfs_notify_dirent(cdev->state_sysfs);
52 }
53 
54 static ssize_t
family_show(struct device * dev,struct device_attribute * attr,char * buf)55 family_show(struct device *dev, struct device_attribute *attr, char *buf)
56 {
57 	struct cosm_device *cdev = dev_get_drvdata(dev);
58 
59 	if (!cdev)
60 		return -EINVAL;
61 
62 	return cdev->hw_ops->family(cdev, buf);
63 }
64 static DEVICE_ATTR_RO(family);
65 
66 static ssize_t
stepping_show(struct device * dev,struct device_attribute * attr,char * buf)67 stepping_show(struct device *dev, struct device_attribute *attr, char *buf)
68 {
69 	struct cosm_device *cdev = dev_get_drvdata(dev);
70 
71 	if (!cdev)
72 		return -EINVAL;
73 
74 	return cdev->hw_ops->stepping(cdev, buf);
75 }
76 static DEVICE_ATTR_RO(stepping);
77 
78 static ssize_t
state_show(struct device * dev,struct device_attribute * attr,char * buf)79 state_show(struct device *dev, struct device_attribute *attr, char *buf)
80 {
81 	struct cosm_device *cdev = dev_get_drvdata(dev);
82 
83 	if (!cdev || cdev->state >= MIC_LAST)
84 		return -EINVAL;
85 
86 	return scnprintf(buf, PAGE_SIZE, "%s\n",
87 		cosm_state_string[cdev->state]);
88 }
89 
90 static ssize_t
state_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)91 state_store(struct device *dev, struct device_attribute *attr,
92 	    const char *buf, size_t count)
93 {
94 	struct cosm_device *cdev = dev_get_drvdata(dev);
95 	int rc;
96 
97 	if (!cdev)
98 		return -EINVAL;
99 
100 	if (sysfs_streq(buf, "boot")) {
101 		rc = cosm_start(cdev);
102 		goto done;
103 	}
104 	if (sysfs_streq(buf, "reset")) {
105 		rc = cosm_reset(cdev);
106 		goto done;
107 	}
108 
109 	if (sysfs_streq(buf, "shutdown")) {
110 		rc = cosm_shutdown(cdev);
111 		goto done;
112 	}
113 	rc = -EINVAL;
114 done:
115 	if (rc)
116 		count = rc;
117 	return count;
118 }
119 static DEVICE_ATTR_RW(state);
120 
shutdown_status_show(struct device * dev,struct device_attribute * attr,char * buf)121 static ssize_t shutdown_status_show(struct device *dev,
122 				    struct device_attribute *attr, char *buf)
123 {
124 	struct cosm_device *cdev = dev_get_drvdata(dev);
125 
126 	if (!cdev || cdev->shutdown_status >= MIC_STATUS_LAST)
127 		return -EINVAL;
128 
129 	return scnprintf(buf, PAGE_SIZE, "%s\n",
130 		cosm_shutdown_status_string[cdev->shutdown_status]);
131 }
132 static DEVICE_ATTR_RO(shutdown_status);
133 
134 static ssize_t
heartbeat_enable_show(struct device * dev,struct device_attribute * attr,char * buf)135 heartbeat_enable_show(struct device *dev,
136 		      struct device_attribute *attr, char *buf)
137 {
138 	struct cosm_device *cdev = dev_get_drvdata(dev);
139 
140 	if (!cdev)
141 		return -EINVAL;
142 
143 	return scnprintf(buf, PAGE_SIZE, "%d\n", cdev->sysfs_heartbeat_enable);
144 }
145 
146 static ssize_t
heartbeat_enable_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)147 heartbeat_enable_store(struct device *dev,
148 		       struct device_attribute *attr,
149 		       const char *buf, size_t count)
150 {
151 	struct cosm_device *cdev = dev_get_drvdata(dev);
152 	int enable;
153 	int ret;
154 
155 	if (!cdev)
156 		return -EINVAL;
157 
158 	mutex_lock(&cdev->cosm_mutex);
159 	ret = kstrtoint(buf, 10, &enable);
160 	if (ret)
161 		goto unlock;
162 
163 	cdev->sysfs_heartbeat_enable = enable;
164 	/* if state is not online, cdev->heartbeat_watchdog_enable is 0 */
165 	if (cdev->state == MIC_ONLINE)
166 		cdev->heartbeat_watchdog_enable = enable;
167 	ret = count;
168 unlock:
169 	mutex_unlock(&cdev->cosm_mutex);
170 	return ret;
171 }
172 static DEVICE_ATTR_RW(heartbeat_enable);
173 
174 static ssize_t
cmdline_show(struct device * dev,struct device_attribute * attr,char * buf)175 cmdline_show(struct device *dev, struct device_attribute *attr, char *buf)
176 {
177 	struct cosm_device *cdev = dev_get_drvdata(dev);
178 	char *cmdline;
179 
180 	if (!cdev)
181 		return -EINVAL;
182 
183 	cmdline = cdev->cmdline;
184 
185 	if (cmdline)
186 		return scnprintf(buf, PAGE_SIZE, "%s\n", cmdline);
187 	return 0;
188 }
189 
190 static ssize_t
cmdline_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)191 cmdline_store(struct device *dev, struct device_attribute *attr,
192 	      const char *buf, size_t count)
193 {
194 	struct cosm_device *cdev = dev_get_drvdata(dev);
195 
196 	if (!cdev)
197 		return -EINVAL;
198 
199 	mutex_lock(&cdev->cosm_mutex);
200 	kfree(cdev->cmdline);
201 
202 	cdev->cmdline = kmalloc(count + 1, GFP_KERNEL);
203 	if (!cdev->cmdline) {
204 		count = -ENOMEM;
205 		goto unlock;
206 	}
207 
208 	strncpy(cdev->cmdline, buf, count);
209 
210 	if (cdev->cmdline[count - 1] == '\n')
211 		cdev->cmdline[count - 1] = '\0';
212 	else
213 		cdev->cmdline[count] = '\0';
214 unlock:
215 	mutex_unlock(&cdev->cosm_mutex);
216 	return count;
217 }
218 static DEVICE_ATTR_RW(cmdline);
219 
220 static ssize_t
firmware_show(struct device * dev,struct device_attribute * attr,char * buf)221 firmware_show(struct device *dev, struct device_attribute *attr, char *buf)
222 {
223 	struct cosm_device *cdev = dev_get_drvdata(dev);
224 	char *firmware;
225 
226 	if (!cdev)
227 		return -EINVAL;
228 
229 	firmware = cdev->firmware;
230 
231 	if (firmware)
232 		return scnprintf(buf, PAGE_SIZE, "%s\n", firmware);
233 	return 0;
234 }
235 
236 static ssize_t
firmware_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)237 firmware_store(struct device *dev, struct device_attribute *attr,
238 	       const char *buf, size_t count)
239 {
240 	struct cosm_device *cdev = dev_get_drvdata(dev);
241 
242 	if (!cdev)
243 		return -EINVAL;
244 
245 	mutex_lock(&cdev->cosm_mutex);
246 	kfree(cdev->firmware);
247 
248 	cdev->firmware = kmalloc(count + 1, GFP_KERNEL);
249 	if (!cdev->firmware) {
250 		count = -ENOMEM;
251 		goto unlock;
252 	}
253 	strncpy(cdev->firmware, buf, count);
254 
255 	if (cdev->firmware[count - 1] == '\n')
256 		cdev->firmware[count - 1] = '\0';
257 	else
258 		cdev->firmware[count] = '\0';
259 unlock:
260 	mutex_unlock(&cdev->cosm_mutex);
261 	return count;
262 }
263 static DEVICE_ATTR_RW(firmware);
264 
265 static ssize_t
ramdisk_show(struct device * dev,struct device_attribute * attr,char * buf)266 ramdisk_show(struct device *dev, struct device_attribute *attr, char *buf)
267 {
268 	struct cosm_device *cdev = dev_get_drvdata(dev);
269 	char *ramdisk;
270 
271 	if (!cdev)
272 		return -EINVAL;
273 
274 	ramdisk = cdev->ramdisk;
275 
276 	if (ramdisk)
277 		return scnprintf(buf, PAGE_SIZE, "%s\n", ramdisk);
278 	return 0;
279 }
280 
281 static ssize_t
ramdisk_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)282 ramdisk_store(struct device *dev, struct device_attribute *attr,
283 	      const char *buf, size_t count)
284 {
285 	struct cosm_device *cdev = dev_get_drvdata(dev);
286 
287 	if (!cdev)
288 		return -EINVAL;
289 
290 	mutex_lock(&cdev->cosm_mutex);
291 	kfree(cdev->ramdisk);
292 
293 	cdev->ramdisk = kmalloc(count + 1, GFP_KERNEL);
294 	if (!cdev->ramdisk) {
295 		count = -ENOMEM;
296 		goto unlock;
297 	}
298 
299 	strncpy(cdev->ramdisk, buf, count);
300 
301 	if (cdev->ramdisk[count - 1] == '\n')
302 		cdev->ramdisk[count - 1] = '\0';
303 	else
304 		cdev->ramdisk[count] = '\0';
305 unlock:
306 	mutex_unlock(&cdev->cosm_mutex);
307 	return count;
308 }
309 static DEVICE_ATTR_RW(ramdisk);
310 
311 static ssize_t
bootmode_show(struct device * dev,struct device_attribute * attr,char * buf)312 bootmode_show(struct device *dev, struct device_attribute *attr, char *buf)
313 {
314 	struct cosm_device *cdev = dev_get_drvdata(dev);
315 	char *bootmode;
316 
317 	if (!cdev)
318 		return -EINVAL;
319 
320 	bootmode = cdev->bootmode;
321 
322 	if (bootmode)
323 		return scnprintf(buf, PAGE_SIZE, "%s\n", bootmode);
324 	return 0;
325 }
326 
327 static ssize_t
bootmode_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)328 bootmode_store(struct device *dev, struct device_attribute *attr,
329 	       const char *buf, size_t count)
330 {
331 	struct cosm_device *cdev = dev_get_drvdata(dev);
332 
333 	if (!cdev)
334 		return -EINVAL;
335 
336 	if (!sysfs_streq(buf, "linux") && !sysfs_streq(buf, "flash"))
337 		return -EINVAL;
338 
339 	mutex_lock(&cdev->cosm_mutex);
340 	kfree(cdev->bootmode);
341 
342 	cdev->bootmode = kmalloc(count + 1, GFP_KERNEL);
343 	if (!cdev->bootmode) {
344 		count = -ENOMEM;
345 		goto unlock;
346 	}
347 
348 	strncpy(cdev->bootmode, buf, count);
349 
350 	if (cdev->bootmode[count - 1] == '\n')
351 		cdev->bootmode[count - 1] = '\0';
352 	else
353 		cdev->bootmode[count] = '\0';
354 unlock:
355 	mutex_unlock(&cdev->cosm_mutex);
356 	return count;
357 }
358 static DEVICE_ATTR_RW(bootmode);
359 
360 static ssize_t
log_buf_addr_show(struct device * dev,struct device_attribute * attr,char * buf)361 log_buf_addr_show(struct device *dev, struct device_attribute *attr,
362 		  char *buf)
363 {
364 	struct cosm_device *cdev = dev_get_drvdata(dev);
365 
366 	if (!cdev)
367 		return -EINVAL;
368 
369 	return scnprintf(buf, PAGE_SIZE, "%p\n", cdev->log_buf_addr);
370 }
371 
372 static ssize_t
log_buf_addr_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)373 log_buf_addr_store(struct device *dev, struct device_attribute *attr,
374 		   const char *buf, size_t count)
375 {
376 	struct cosm_device *cdev = dev_get_drvdata(dev);
377 	int ret;
378 	unsigned long addr;
379 
380 	if (!cdev)
381 		return -EINVAL;
382 
383 	ret = kstrtoul(buf, 16, &addr);
384 	if (ret)
385 		goto exit;
386 
387 	cdev->log_buf_addr = (void *)addr;
388 	ret = count;
389 exit:
390 	return ret;
391 }
392 static DEVICE_ATTR_RW(log_buf_addr);
393 
394 static ssize_t
log_buf_len_show(struct device * dev,struct device_attribute * attr,char * buf)395 log_buf_len_show(struct device *dev, struct device_attribute *attr,
396 		 char *buf)
397 {
398 	struct cosm_device *cdev = dev_get_drvdata(dev);
399 
400 	if (!cdev)
401 		return -EINVAL;
402 
403 	return scnprintf(buf, PAGE_SIZE, "%p\n", cdev->log_buf_len);
404 }
405 
406 static ssize_t
log_buf_len_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)407 log_buf_len_store(struct device *dev, struct device_attribute *attr,
408 		  const char *buf, size_t count)
409 {
410 	struct cosm_device *cdev = dev_get_drvdata(dev);
411 	int ret;
412 	unsigned long addr;
413 
414 	if (!cdev)
415 		return -EINVAL;
416 
417 	ret = kstrtoul(buf, 16, &addr);
418 	if (ret)
419 		goto exit;
420 
421 	cdev->log_buf_len = (int *)addr;
422 	ret = count;
423 exit:
424 	return ret;
425 }
426 static DEVICE_ATTR_RW(log_buf_len);
427 
428 static struct attribute *cosm_default_attrs[] = {
429 	&dev_attr_family.attr,
430 	&dev_attr_stepping.attr,
431 	&dev_attr_state.attr,
432 	&dev_attr_shutdown_status.attr,
433 	&dev_attr_heartbeat_enable.attr,
434 	&dev_attr_cmdline.attr,
435 	&dev_attr_firmware.attr,
436 	&dev_attr_ramdisk.attr,
437 	&dev_attr_bootmode.attr,
438 	&dev_attr_log_buf_addr.attr,
439 	&dev_attr_log_buf_len.attr,
440 
441 	NULL
442 };
443 
444 ATTRIBUTE_GROUPS(cosm_default);
445 
cosm_sysfs_init(struct cosm_device * cdev)446 void cosm_sysfs_init(struct cosm_device *cdev)
447 {
448 	cdev->attr_group = cosm_default_groups;
449 }
450