linux/drivers/misc/mic/cosm/cosm_sysfs.c
Ashutosh Dixit f8487a26b2 misc: mic: Coprocessor State Management (COSM) driver
The COSM driver allows boot, shutdown and reset of Intel MIC devices
via sysfs. This functionality was previously present in the Intel MIC
host driver but has now been taken out into a separate driver so that
it can be shared between multiple generations of Intel MIC products.
The sysfs kernel ABI used by the COSM driver is the same as that
defined originally for the MIC host driver in
Documentation/ABI/testing/sysfs-class-mic.txt.

The COSM driver also contains support for dumping the MIC card log_buf
and doing a "force reset" for the card via debugfs. The OSPM support
present in the MIC host driver has now largely been moved to user
space and only a small required OSPM functionality is now present in
the driver.

Reviewed-by: Nikhil Rao <nikhil.rao@intel.com>
Reviewed-by: Sudeep Dutt <sudeep.dutt@intel.com>
Signed-off-by: Dasaratharaman Chandramouli <dasaratharaman.chandramouli@intel.com>
Signed-off-by: Ashutosh Dixit <ashutosh.dixit@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-10-04 12:46:06 +01:00

462 lines
9.8 KiB
C

/*
* Intel MIC Platform Software Stack (MPSS)
*
* Copyright(c) 2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*
* Intel MIC Coprocessor State Management (COSM) Driver
*
*/
#include <linux/slab.h>
#include "cosm_main.h"
/*
* A state-to-string lookup table, for exposing a human readable state
* via sysfs. Always keep in sync with enum cosm_states
*/
const char * const cosm_state_string[] = {
[MIC_READY] = "ready",
[MIC_BOOTING] = "booting",
[MIC_ONLINE] = "online",
[MIC_SHUTTING_DOWN] = "shutting_down",
[MIC_RESETTING] = "resetting",
[MIC_RESET_FAILED] = "reset_failed",
};
/*
* A shutdown-status-to-string lookup table, for exposing a human
* readable state via sysfs. Always keep in sync with enum cosm_shutdown_status
*/
const char * const cosm_shutdown_status_string[] = {
[MIC_NOP] = "nop",
[MIC_CRASHED] = "crashed",
[MIC_HALTED] = "halted",
[MIC_POWER_OFF] = "poweroff",
[MIC_RESTART] = "restart",
};
void cosm_set_shutdown_status(struct cosm_device *cdev, u8 shutdown_status)
{
dev_dbg(&cdev->dev, "Shutdown Status %s -> %s\n",
cosm_shutdown_status_string[cdev->shutdown_status],
cosm_shutdown_status_string[shutdown_status]);
cdev->shutdown_status = shutdown_status;
}
void cosm_set_state(struct cosm_device *cdev, u8 state)
{
dev_dbg(&cdev->dev, "State %s -> %s\n",
cosm_state_string[cdev->state],
cosm_state_string[state]);
cdev->state = state;
sysfs_notify_dirent(cdev->state_sysfs);
}
static ssize_t
family_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
if (!cdev)
return -EINVAL;
return cdev->hw_ops->family(cdev, buf);
}
static DEVICE_ATTR_RO(family);
static ssize_t
stepping_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
if (!cdev)
return -EINVAL;
return cdev->hw_ops->stepping(cdev, buf);
}
static DEVICE_ATTR_RO(stepping);
static ssize_t
state_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
if (!cdev || cdev->state >= MIC_LAST)
return -EINVAL;
return scnprintf(buf, PAGE_SIZE, "%s\n",
cosm_state_string[cdev->state]);
}
static ssize_t
state_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
int rc;
if (!cdev)
return -EINVAL;
if (sysfs_streq(buf, "boot")) {
rc = cosm_start(cdev);
goto done;
}
if (sysfs_streq(buf, "reset")) {
rc = cosm_reset(cdev);
goto done;
}
if (sysfs_streq(buf, "shutdown")) {
rc = cosm_shutdown(cdev);
goto done;
}
rc = -EINVAL;
done:
if (rc)
count = rc;
return count;
}
static DEVICE_ATTR_RW(state);
static ssize_t shutdown_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
if (!cdev || cdev->shutdown_status >= MIC_STATUS_LAST)
return -EINVAL;
return scnprintf(buf, PAGE_SIZE, "%s\n",
cosm_shutdown_status_string[cdev->shutdown_status]);
}
static DEVICE_ATTR_RO(shutdown_status);
static ssize_t
heartbeat_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
if (!cdev)
return -EINVAL;
return scnprintf(buf, PAGE_SIZE, "%d\n", cdev->sysfs_heartbeat_enable);
}
static ssize_t
heartbeat_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
int enable;
int ret;
if (!cdev)
return -EINVAL;
mutex_lock(&cdev->cosm_mutex);
ret = kstrtoint(buf, 10, &enable);
if (ret)
goto unlock;
cdev->sysfs_heartbeat_enable = enable;
/* if state is not online, cdev->heartbeat_watchdog_enable is 0 */
if (cdev->state == MIC_ONLINE)
cdev->heartbeat_watchdog_enable = enable;
ret = count;
unlock:
mutex_unlock(&cdev->cosm_mutex);
return ret;
}
static DEVICE_ATTR_RW(heartbeat_enable);
static ssize_t
cmdline_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
char *cmdline;
if (!cdev)
return -EINVAL;
cmdline = cdev->cmdline;
if (cmdline)
return scnprintf(buf, PAGE_SIZE, "%s\n", cmdline);
return 0;
}
static ssize_t
cmdline_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
if (!cdev)
return -EINVAL;
mutex_lock(&cdev->cosm_mutex);
kfree(cdev->cmdline);
cdev->cmdline = kmalloc(count + 1, GFP_KERNEL);
if (!cdev->cmdline) {
count = -ENOMEM;
goto unlock;
}
strncpy(cdev->cmdline, buf, count);
if (cdev->cmdline[count - 1] == '\n')
cdev->cmdline[count - 1] = '\0';
else
cdev->cmdline[count] = '\0';
unlock:
mutex_unlock(&cdev->cosm_mutex);
return count;
}
static DEVICE_ATTR_RW(cmdline);
static ssize_t
firmware_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
char *firmware;
if (!cdev)
return -EINVAL;
firmware = cdev->firmware;
if (firmware)
return scnprintf(buf, PAGE_SIZE, "%s\n", firmware);
return 0;
}
static ssize_t
firmware_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
if (!cdev)
return -EINVAL;
mutex_lock(&cdev->cosm_mutex);
kfree(cdev->firmware);
cdev->firmware = kmalloc(count + 1, GFP_KERNEL);
if (!cdev->firmware) {
count = -ENOMEM;
goto unlock;
}
strncpy(cdev->firmware, buf, count);
if (cdev->firmware[count - 1] == '\n')
cdev->firmware[count - 1] = '\0';
else
cdev->firmware[count] = '\0';
unlock:
mutex_unlock(&cdev->cosm_mutex);
return count;
}
static DEVICE_ATTR_RW(firmware);
static ssize_t
ramdisk_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
char *ramdisk;
if (!cdev)
return -EINVAL;
ramdisk = cdev->ramdisk;
if (ramdisk)
return scnprintf(buf, PAGE_SIZE, "%s\n", ramdisk);
return 0;
}
static ssize_t
ramdisk_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
if (!cdev)
return -EINVAL;
mutex_lock(&cdev->cosm_mutex);
kfree(cdev->ramdisk);
cdev->ramdisk = kmalloc(count + 1, GFP_KERNEL);
if (!cdev->ramdisk) {
count = -ENOMEM;
goto unlock;
}
strncpy(cdev->ramdisk, buf, count);
if (cdev->ramdisk[count - 1] == '\n')
cdev->ramdisk[count - 1] = '\0';
else
cdev->ramdisk[count] = '\0';
unlock:
mutex_unlock(&cdev->cosm_mutex);
return count;
}
static DEVICE_ATTR_RW(ramdisk);
static ssize_t
bootmode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
char *bootmode;
if (!cdev)
return -EINVAL;
bootmode = cdev->bootmode;
if (bootmode)
return scnprintf(buf, PAGE_SIZE, "%s\n", bootmode);
return 0;
}
static ssize_t
bootmode_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
if (!cdev)
return -EINVAL;
if (!sysfs_streq(buf, "linux") && !sysfs_streq(buf, "flash"))
return -EINVAL;
mutex_lock(&cdev->cosm_mutex);
kfree(cdev->bootmode);
cdev->bootmode = kmalloc(count + 1, GFP_KERNEL);
if (!cdev->bootmode) {
count = -ENOMEM;
goto unlock;
}
strncpy(cdev->bootmode, buf, count);
if (cdev->bootmode[count - 1] == '\n')
cdev->bootmode[count - 1] = '\0';
else
cdev->bootmode[count] = '\0';
unlock:
mutex_unlock(&cdev->cosm_mutex);
return count;
}
static DEVICE_ATTR_RW(bootmode);
static ssize_t
log_buf_addr_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
if (!cdev)
return -EINVAL;
return scnprintf(buf, PAGE_SIZE, "%p\n", cdev->log_buf_addr);
}
static ssize_t
log_buf_addr_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
int ret;
unsigned long addr;
if (!cdev)
return -EINVAL;
ret = kstrtoul(buf, 16, &addr);
if (ret)
goto exit;
cdev->log_buf_addr = (void *)addr;
ret = count;
exit:
return ret;
}
static DEVICE_ATTR_RW(log_buf_addr);
static ssize_t
log_buf_len_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
if (!cdev)
return -EINVAL;
return scnprintf(buf, PAGE_SIZE, "%p\n", cdev->log_buf_len);
}
static ssize_t
log_buf_len_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct cosm_device *cdev = dev_get_drvdata(dev);
int ret;
unsigned long addr;
if (!cdev)
return -EINVAL;
ret = kstrtoul(buf, 16, &addr);
if (ret)
goto exit;
cdev->log_buf_len = (int *)addr;
ret = count;
exit:
return ret;
}
static DEVICE_ATTR_RW(log_buf_len);
static struct attribute *cosm_default_attrs[] = {
&dev_attr_family.attr,
&dev_attr_stepping.attr,
&dev_attr_state.attr,
&dev_attr_shutdown_status.attr,
&dev_attr_heartbeat_enable.attr,
&dev_attr_cmdline.attr,
&dev_attr_firmware.attr,
&dev_attr_ramdisk.attr,
&dev_attr_bootmode.attr,
&dev_attr_log_buf_addr.attr,
&dev_attr_log_buf_len.attr,
NULL
};
ATTRIBUTE_GROUPS(cosm_default);
void cosm_sysfs_init(struct cosm_device *cdev)
{
cdev->attr_group = cosm_default_groups;
}