1/*
2 * Intel MIC Platform Software Stack (MPSS)
3 *
4 * Copyright(c) 2013 Intel Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License, version 2, as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * The full GNU General Public License is included in this distribution in
16 * the file called "COPYING".
17 *
18 * Intel MIC Host driver.
19 *
20 */
21#include <linux/pci.h>
22
23#include <linux/mic_common.h>
24#include "../common/mic_dev.h"
25#include "mic_device.h"
26
27/*
28 * A state-to-string lookup table, for exposing a human readable state
29 * via sysfs. Always keep in sync with enum mic_states
30 */
31static const char * const mic_state_string[] = {
32	[MIC_OFFLINE] = "offline",
33	[MIC_ONLINE] = "online",
34	[MIC_SHUTTING_DOWN] = "shutting_down",
35	[MIC_RESET_FAILED] = "reset_failed",
36	[MIC_SUSPENDING] = "suspending",
37	[MIC_SUSPENDED] = "suspended",
38};
39
40/*
41 * A shutdown-status-to-string lookup table, for exposing a human
42 * readable state via sysfs. Always keep in sync with enum mic_shutdown_status
43 */
44static const char * const mic_shutdown_status_string[] = {
45	[MIC_NOP] = "nop",
46	[MIC_CRASHED] = "crashed",
47	[MIC_HALTED] = "halted",
48	[MIC_POWER_OFF] = "poweroff",
49	[MIC_RESTART] = "restart",
50};
51
52void mic_set_shutdown_status(struct mic_device *mdev, u8 shutdown_status)
53{
54	dev_dbg(mdev->sdev->parent, "Shutdown Status %s -> %s\n",
55		mic_shutdown_status_string[mdev->shutdown_status],
56		mic_shutdown_status_string[shutdown_status]);
57	mdev->shutdown_status = shutdown_status;
58}
59
60void mic_set_state(struct mic_device *mdev, u8 state)
61{
62	dev_dbg(mdev->sdev->parent, "State %s -> %s\n",
63		mic_state_string[mdev->state],
64		mic_state_string[state]);
65	mdev->state = state;
66	sysfs_notify_dirent(mdev->state_sysfs);
67}
68
69static ssize_t
70family_show(struct device *dev, struct device_attribute *attr, char *buf)
71{
72	static const char x100[] = "x100";
73	static const char unknown[] = "Unknown";
74	const char *card = NULL;
75	struct mic_device *mdev = dev_get_drvdata(dev->parent);
76
77	if (!mdev)
78		return -EINVAL;
79
80	switch (mdev->family) {
81	case MIC_FAMILY_X100:
82		card = x100;
83		break;
84	default:
85		card = unknown;
86		break;
87	}
88	return scnprintf(buf, PAGE_SIZE, "%s\n", card);
89}
90static DEVICE_ATTR_RO(family);
91
92static ssize_t
93stepping_show(struct device *dev, struct device_attribute *attr, char *buf)
94{
95	struct mic_device *mdev = dev_get_drvdata(dev->parent);
96	char *string = "??";
97
98	if (!mdev)
99		return -EINVAL;
100
101	switch (mdev->stepping) {
102	case MIC_A0_STEP:
103		string = "A0";
104		break;
105	case MIC_B0_STEP:
106		string = "B0";
107		break;
108	case MIC_B1_STEP:
109		string = "B1";
110		break;
111	case MIC_C0_STEP:
112		string = "C0";
113		break;
114	default:
115		break;
116	}
117	return scnprintf(buf, PAGE_SIZE, "%s\n", string);
118}
119static DEVICE_ATTR_RO(stepping);
120
121static ssize_t
122state_show(struct device *dev, struct device_attribute *attr, char *buf)
123{
124	struct mic_device *mdev = dev_get_drvdata(dev->parent);
125
126	if (!mdev || mdev->state >= MIC_LAST)
127		return -EINVAL;
128
129	return scnprintf(buf, PAGE_SIZE, "%s\n",
130		mic_state_string[mdev->state]);
131}
132
133static ssize_t
134state_store(struct device *dev, struct device_attribute *attr,
135	    const char *buf, size_t count)
136{
137	int rc = 0;
138	struct mic_device *mdev = dev_get_drvdata(dev->parent);
139	if (!mdev)
140		return -EINVAL;
141	if (sysfs_streq(buf, "boot")) {
142		rc = mic_start(mdev, buf);
143		if (rc) {
144			dev_err(mdev->sdev->parent,
145				"mic_boot failed rc %d\n", rc);
146			count = rc;
147		}
148		goto done;
149	}
150
151	if (sysfs_streq(buf, "reset")) {
152		schedule_work(&mdev->reset_trigger_work);
153		goto done;
154	}
155
156	if (sysfs_streq(buf, "shutdown")) {
157		mic_shutdown(mdev);
158		goto done;
159	}
160
161	if (sysfs_streq(buf, "suspend")) {
162		mic_suspend(mdev);
163		goto done;
164	}
165
166	count = -EINVAL;
167done:
168	return count;
169}
170static DEVICE_ATTR_RW(state);
171
172static ssize_t shutdown_status_show(struct device *dev,
173				    struct device_attribute *attr, char *buf)
174{
175	struct mic_device *mdev = dev_get_drvdata(dev->parent);
176
177	if (!mdev || mdev->shutdown_status >= MIC_STATUS_LAST)
178		return -EINVAL;
179
180	return scnprintf(buf, PAGE_SIZE, "%s\n",
181		mic_shutdown_status_string[mdev->shutdown_status]);
182}
183static DEVICE_ATTR_RO(shutdown_status);
184
185static ssize_t
186cmdline_show(struct device *dev, struct device_attribute *attr, char *buf)
187{
188	struct mic_device *mdev = dev_get_drvdata(dev->parent);
189	char *cmdline;
190
191	if (!mdev)
192		return -EINVAL;
193
194	cmdline = mdev->cmdline;
195
196	if (cmdline)
197		return scnprintf(buf, PAGE_SIZE, "%s\n", cmdline);
198	return 0;
199}
200
201static ssize_t
202cmdline_store(struct device *dev, struct device_attribute *attr,
203	      const char *buf, size_t count)
204{
205	struct mic_device *mdev = dev_get_drvdata(dev->parent);
206
207	if (!mdev)
208		return -EINVAL;
209
210	mutex_lock(&mdev->mic_mutex);
211	kfree(mdev->cmdline);
212
213	mdev->cmdline = kmalloc(count + 1, GFP_KERNEL);
214	if (!mdev->cmdline) {
215		count = -ENOMEM;
216		goto unlock;
217	}
218
219	strncpy(mdev->cmdline, buf, count);
220
221	if (mdev->cmdline[count - 1] == '\n')
222		mdev->cmdline[count - 1] = '\0';
223	else
224		mdev->cmdline[count] = '\0';
225unlock:
226	mutex_unlock(&mdev->mic_mutex);
227	return count;
228}
229static DEVICE_ATTR_RW(cmdline);
230
231static ssize_t
232firmware_show(struct device *dev, struct device_attribute *attr, char *buf)
233{
234	struct mic_device *mdev = dev_get_drvdata(dev->parent);
235	char *firmware;
236
237	if (!mdev)
238		return -EINVAL;
239
240	firmware = mdev->firmware;
241
242	if (firmware)
243		return scnprintf(buf, PAGE_SIZE, "%s\n", firmware);
244	return 0;
245}
246
247static ssize_t
248firmware_store(struct device *dev, struct device_attribute *attr,
249	       const char *buf, size_t count)
250{
251	struct mic_device *mdev = dev_get_drvdata(dev->parent);
252
253	if (!mdev)
254		return -EINVAL;
255
256	mutex_lock(&mdev->mic_mutex);
257	kfree(mdev->firmware);
258
259	mdev->firmware = kmalloc(count + 1, GFP_KERNEL);
260	if (!mdev->firmware) {
261		count = -ENOMEM;
262		goto unlock;
263	}
264	strncpy(mdev->firmware, buf, count);
265
266	if (mdev->firmware[count - 1] == '\n')
267		mdev->firmware[count - 1] = '\0';
268	else
269		mdev->firmware[count] = '\0';
270unlock:
271	mutex_unlock(&mdev->mic_mutex);
272	return count;
273}
274static DEVICE_ATTR_RW(firmware);
275
276static ssize_t
277ramdisk_show(struct device *dev, struct device_attribute *attr, char *buf)
278{
279	struct mic_device *mdev = dev_get_drvdata(dev->parent);
280	char *ramdisk;
281
282	if (!mdev)
283		return -EINVAL;
284
285	ramdisk = mdev->ramdisk;
286
287	if (ramdisk)
288		return scnprintf(buf, PAGE_SIZE, "%s\n", ramdisk);
289	return 0;
290}
291
292static ssize_t
293ramdisk_store(struct device *dev, struct device_attribute *attr,
294	      const char *buf, size_t count)
295{
296	struct mic_device *mdev = dev_get_drvdata(dev->parent);
297
298	if (!mdev)
299		return -EINVAL;
300
301	mutex_lock(&mdev->mic_mutex);
302	kfree(mdev->ramdisk);
303
304	mdev->ramdisk = kmalloc(count + 1, GFP_KERNEL);
305	if (!mdev->ramdisk) {
306		count = -ENOMEM;
307		goto unlock;
308	}
309
310	strncpy(mdev->ramdisk, buf, count);
311
312	if (mdev->ramdisk[count - 1] == '\n')
313		mdev->ramdisk[count - 1] = '\0';
314	else
315		mdev->ramdisk[count] = '\0';
316unlock:
317	mutex_unlock(&mdev->mic_mutex);
318	return count;
319}
320static DEVICE_ATTR_RW(ramdisk);
321
322static ssize_t
323bootmode_show(struct device *dev, struct device_attribute *attr, char *buf)
324{
325	struct mic_device *mdev = dev_get_drvdata(dev->parent);
326	char *bootmode;
327
328	if (!mdev)
329		return -EINVAL;
330
331	bootmode = mdev->bootmode;
332
333	if (bootmode)
334		return scnprintf(buf, PAGE_SIZE, "%s\n", bootmode);
335	return 0;
336}
337
338static ssize_t
339bootmode_store(struct device *dev, struct device_attribute *attr,
340	       const char *buf, size_t count)
341{
342	struct mic_device *mdev = dev_get_drvdata(dev->parent);
343
344	if (!mdev)
345		return -EINVAL;
346
347	if (!sysfs_streq(buf, "linux") && !sysfs_streq(buf, "elf"))
348		return -EINVAL;
349
350	mutex_lock(&mdev->mic_mutex);
351	kfree(mdev->bootmode);
352
353	mdev->bootmode = kmalloc(count + 1, GFP_KERNEL);
354	if (!mdev->bootmode) {
355		count = -ENOMEM;
356		goto unlock;
357	}
358
359	strncpy(mdev->bootmode, buf, count);
360
361	if (mdev->bootmode[count - 1] == '\n')
362		mdev->bootmode[count - 1] = '\0';
363	else
364		mdev->bootmode[count] = '\0';
365unlock:
366	mutex_unlock(&mdev->mic_mutex);
367	return count;
368}
369static DEVICE_ATTR_RW(bootmode);
370
371static ssize_t
372log_buf_addr_show(struct device *dev, struct device_attribute *attr,
373		  char *buf)
374{
375	struct mic_device *mdev = dev_get_drvdata(dev->parent);
376
377	if (!mdev)
378		return -EINVAL;
379
380	return scnprintf(buf, PAGE_SIZE, "%p\n", mdev->log_buf_addr);
381}
382
383static ssize_t
384log_buf_addr_store(struct device *dev, struct device_attribute *attr,
385		   const char *buf, size_t count)
386{
387	struct mic_device *mdev = dev_get_drvdata(dev->parent);
388	int ret;
389	unsigned long addr;
390
391	if (!mdev)
392		return -EINVAL;
393
394	ret = kstrtoul(buf, 16, &addr);
395	if (ret)
396		goto exit;
397
398	mdev->log_buf_addr = (void *)addr;
399	ret = count;
400exit:
401	return ret;
402}
403static DEVICE_ATTR_RW(log_buf_addr);
404
405static ssize_t
406log_buf_len_show(struct device *dev, struct device_attribute *attr,
407		 char *buf)
408{
409	struct mic_device *mdev = dev_get_drvdata(dev->parent);
410
411	if (!mdev)
412		return -EINVAL;
413
414	return scnprintf(buf, PAGE_SIZE, "%p\n", mdev->log_buf_len);
415}
416
417static ssize_t
418log_buf_len_store(struct device *dev, struct device_attribute *attr,
419		  const char *buf, size_t count)
420{
421	struct mic_device *mdev = dev_get_drvdata(dev->parent);
422	int ret;
423	unsigned long addr;
424
425	if (!mdev)
426		return -EINVAL;
427
428	ret = kstrtoul(buf, 16, &addr);
429	if (ret)
430		goto exit;
431
432	mdev->log_buf_len = (int *)addr;
433	ret = count;
434exit:
435	return ret;
436}
437static DEVICE_ATTR_RW(log_buf_len);
438
439static struct attribute *mic_default_attrs[] = {
440	&dev_attr_family.attr,
441	&dev_attr_stepping.attr,
442	&dev_attr_state.attr,
443	&dev_attr_shutdown_status.attr,
444	&dev_attr_cmdline.attr,
445	&dev_attr_firmware.attr,
446	&dev_attr_ramdisk.attr,
447	&dev_attr_bootmode.attr,
448	&dev_attr_log_buf_addr.attr,
449	&dev_attr_log_buf_len.attr,
450
451	NULL
452};
453
454ATTRIBUTE_GROUPS(mic_default);
455
456void mic_sysfs_init(struct mic_device *mdev)
457{
458	mdev->attr_group = mic_default_groups;
459}
460