--- /dev/null
+#
+# Makefile - Intel MIC Coprocessor State Management (COSM) Driver
+# Copyright(c) 2015, Intel Corporation.
+#
+obj-$(CONFIG_MIC_COSM) += mic_cosm.o
+
+mic_cosm-objs := cosm_main.o
+mic_cosm-objs += cosm_debugfs.o
+mic_cosm-objs += cosm_sysfs.o
+mic_cosm-objs += cosm_scif_server.o
 
--- /dev/null
+/*
+ * 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/debugfs.h>
+#include <linux/slab.h>
+#include "cosm_main.h"
+
+/* Debugfs parent dir */
+static struct dentry *cosm_dbg;
+
+/**
+ * cosm_log_buf_show - Display MIC kernel log buffer
+ *
+ * log_buf addr/len is read from System.map by user space
+ * and populated in sysfs entries.
+ */
+static int cosm_log_buf_show(struct seq_file *s, void *unused)
+{
+       void __iomem *log_buf_va;
+       int __iomem *log_buf_len_va;
+       struct cosm_device *cdev = s->private;
+       void *kva;
+       int size;
+       u64 aper_offset;
+
+       if (!cdev || !cdev->log_buf_addr || !cdev->log_buf_len)
+               goto done;
+
+       mutex_lock(&cdev->cosm_mutex);
+       switch (cdev->state) {
+       case MIC_BOOTING:
+       case MIC_ONLINE:
+       case MIC_SHUTTING_DOWN:
+               break;
+       default:
+               goto unlock;
+       }
+
+       /*
+        * Card kernel will never be relocated and any kernel text/data mapping
+        * can be translated to phys address by subtracting __START_KERNEL_map.
+        */
+       aper_offset = (u64)cdev->log_buf_len - __START_KERNEL_map;
+       log_buf_len_va = cdev->hw_ops->aper(cdev)->va + aper_offset;
+       aper_offset = (u64)cdev->log_buf_addr - __START_KERNEL_map;
+       log_buf_va = cdev->hw_ops->aper(cdev)->va + aper_offset;
+
+       size = ioread32(log_buf_len_va);
+       kva = kmalloc(size, GFP_KERNEL);
+       if (!kva)
+               goto unlock;
+
+       memcpy_fromio(kva, log_buf_va, size);
+       seq_write(s, kva, size);
+       kfree(kva);
+unlock:
+       mutex_unlock(&cdev->cosm_mutex);
+done:
+       return 0;
+}
+
+static int cosm_log_buf_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, cosm_log_buf_show, inode->i_private);
+}
+
+static const struct file_operations log_buf_ops = {
+       .owner   = THIS_MODULE,
+       .open    = cosm_log_buf_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = single_release
+};
+
+/**
+ * cosm_force_reset_show - Force MIC reset
+ *
+ * Invokes the force_reset COSM bus op instead of the standard reset
+ * op in case a force reset of the MIC device is required
+ */
+static int cosm_force_reset_show(struct seq_file *s, void *pos)
+{
+       struct cosm_device *cdev = s->private;
+
+       cosm_stop(cdev, true);
+       return 0;
+}
+
+static int cosm_force_reset_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, cosm_force_reset_show, inode->i_private);
+}
+
+static const struct file_operations force_reset_ops = {
+       .owner   = THIS_MODULE,
+       .open    = cosm_force_reset_debug_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = single_release
+};
+
+void cosm_create_debug_dir(struct cosm_device *cdev)
+{
+       char name[16];
+
+       if (!cosm_dbg)
+               return;
+
+       scnprintf(name, sizeof(name), "mic%d", cdev->index);
+       cdev->dbg_dir = debugfs_create_dir(name, cosm_dbg);
+       if (!cdev->dbg_dir)
+               return;
+
+       debugfs_create_file("log_buf", 0444, cdev->dbg_dir, cdev, &log_buf_ops);
+       debugfs_create_file("force_reset", 0444, cdev->dbg_dir, cdev,
+                           &force_reset_ops);
+}
+
+void cosm_delete_debug_dir(struct cosm_device *cdev)
+{
+       if (!cdev->dbg_dir)
+               return;
+
+       debugfs_remove_recursive(cdev->dbg_dir);
+}
+
+void cosm_init_debugfs(void)
+{
+       cosm_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL);
+       if (!cosm_dbg)
+               pr_err("can't create debugfs dir\n");
+}
+
+void cosm_exit_debugfs(void)
+{
+       debugfs_remove(cosm_dbg);
+}
 
--- /dev/null
+/*
+ * 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/module.h>
+#include <linux/delay.h>
+#include <linux/idr.h>
+#include <linux/slab.h>
+#include <linux/cred.h>
+#include "cosm_main.h"
+
+static const char cosm_driver_name[] = "mic";
+
+/* COSM ID allocator */
+static struct ida g_cosm_ida;
+/* Class of MIC devices for sysfs accessibility. */
+static struct class *g_cosm_class;
+/* Number of MIC devices */
+static atomic_t g_num_dev;
+
+/**
+ * cosm_hw_reset - Issue a HW reset for the MIC device
+ * @cdev: pointer to cosm_device instance
+ */
+static void cosm_hw_reset(struct cosm_device *cdev, bool force)
+{
+       int i;
+
+#define MIC_RESET_TO (45)
+       if (force && cdev->hw_ops->force_reset)
+               cdev->hw_ops->force_reset(cdev);
+       else
+               cdev->hw_ops->reset(cdev);
+
+       for (i = 0; i < MIC_RESET_TO; i++) {
+               if (cdev->hw_ops->ready(cdev)) {
+                       cosm_set_state(cdev, MIC_READY);
+                       return;
+               }
+               /*
+                * Resets typically take 10s of seconds to complete.
+                * Since an MMIO read is required to check if the
+                * firmware is ready or not, a 1 second delay works nicely.
+                */
+               msleep(1000);
+       }
+       cosm_set_state(cdev, MIC_RESET_FAILED);
+}
+
+/**
+ * cosm_start - Start the MIC
+ * @cdev: pointer to cosm_device instance
+ *
+ * This function prepares an MIC for boot and initiates boot.
+ * RETURNS: An appropriate -ERRNO error value on error, or 0 for success.
+ */
+int cosm_start(struct cosm_device *cdev)
+{
+       const struct cred *orig_cred;
+       struct cred *override_cred;
+       int rc;
+
+       mutex_lock(&cdev->cosm_mutex);
+       if (!cdev->bootmode) {
+               dev_err(&cdev->dev, "%s %d bootmode not set\n",
+                       __func__, __LINE__);
+               rc = -EINVAL;
+               goto unlock_ret;
+       }
+retry:
+       if (cdev->state != MIC_READY) {
+               dev_err(&cdev->dev, "%s %d MIC state not READY\n",
+                       __func__, __LINE__);
+               rc = -EINVAL;
+               goto unlock_ret;
+       }
+       if (!cdev->hw_ops->ready(cdev)) {
+               cosm_hw_reset(cdev, false);
+               /*
+                * The state will either be MIC_READY if the reset succeeded
+                * or MIC_RESET_FAILED if the firmware reset failed.
+                */
+               goto retry;
+       }
+
+       /*
+        * Set credentials to root to allow non-root user to download initramsfs
+        * with 600 permissions
+        */
+       override_cred = prepare_creds();
+       if (!override_cred) {
+               dev_err(&cdev->dev, "%s %d prepare_creds failed\n",
+                       __func__, __LINE__);
+               rc = -ENOMEM;
+               goto unlock_ret;
+       }
+       override_cred->fsuid = GLOBAL_ROOT_UID;
+       orig_cred = override_creds(override_cred);
+
+       rc = cdev->hw_ops->start(cdev, cdev->index);
+
+       revert_creds(orig_cred);
+       put_cred(override_cred);
+       if (rc)
+               goto unlock_ret;
+
+       /*
+        * If linux is being booted, card is treated 'online' only
+        * when the scif interface in the card is up. If anything else
+        * is booted, we set card to 'online' immediately.
+        */
+       if (!strcmp(cdev->bootmode, "linux"))
+               cosm_set_state(cdev, MIC_BOOTING);
+       else
+               cosm_set_state(cdev, MIC_ONLINE);
+unlock_ret:
+       mutex_unlock(&cdev->cosm_mutex);
+       if (rc)
+               dev_err(&cdev->dev, "cosm_start failed rc %d\n", rc);
+       return rc;
+}
+
+/**
+ * cosm_stop - Prepare the MIC for reset and trigger reset
+ * @cdev: pointer to cosm_device instance
+ * @force: force a MIC to reset even if it is already reset and ready.
+ *
+ * RETURNS: None
+ */
+void cosm_stop(struct cosm_device *cdev, bool force)
+{
+       mutex_lock(&cdev->cosm_mutex);
+       if (cdev->state != MIC_READY || force) {
+               /*
+                * Don't call hw_ops if they have been called previously.
+                * stop(..) calls device_unregister and will crash the system if
+                * called multiple times.
+                */
+               bool call_hw_ops = cdev->state != MIC_RESET_FAILED &&
+                                       cdev->state != MIC_READY;
+
+               if (cdev->state != MIC_RESETTING)
+                       cosm_set_state(cdev, MIC_RESETTING);
+               cdev->heartbeat_watchdog_enable = false;
+               if (call_hw_ops)
+                       cdev->hw_ops->stop(cdev, force);
+               cosm_hw_reset(cdev, force);
+               cosm_set_shutdown_status(cdev, MIC_NOP);
+               if (call_hw_ops && cdev->hw_ops->post_reset)
+                       cdev->hw_ops->post_reset(cdev, cdev->state);
+       }
+       mutex_unlock(&cdev->cosm_mutex);
+       flush_work(&cdev->scif_work);
+}
+
+/**
+ * cosm_reset_trigger_work - Trigger MIC reset
+ * @work: The work structure
+ *
+ * This work is scheduled whenever the host wants to reset the MIC.
+ */
+static void cosm_reset_trigger_work(struct work_struct *work)
+{
+       struct cosm_device *cdev = container_of(work, struct cosm_device,
+                                               reset_trigger_work);
+       cosm_stop(cdev, false);
+}
+
+/**
+ * cosm_reset - Schedule MIC reset
+ * @cdev: pointer to cosm_device instance
+ *
+ * RETURNS: An -EINVAL if the card is already READY or 0 for success.
+ */
+int cosm_reset(struct cosm_device *cdev)
+{
+       int rc = 0;
+
+       mutex_lock(&cdev->cosm_mutex);
+       if (cdev->state != MIC_READY) {
+               cosm_set_state(cdev, MIC_RESETTING);
+               schedule_work(&cdev->reset_trigger_work);
+       } else {
+               dev_err(&cdev->dev, "%s %d MIC is READY\n", __func__, __LINE__);
+               rc = -EINVAL;
+       }
+       mutex_unlock(&cdev->cosm_mutex);
+       return rc;
+}
+
+/**
+ * cosm_shutdown - Initiate MIC shutdown.
+ * @cdev: pointer to cosm_device instance
+ *
+ * RETURNS: None
+ */
+int cosm_shutdown(struct cosm_device *cdev)
+{
+       struct cosm_msg msg = { .id = COSM_MSG_SHUTDOWN };
+       int rc = 0;
+
+       mutex_lock(&cdev->cosm_mutex);
+       if (cdev->state != MIC_ONLINE) {
+               rc = -EINVAL;
+               dev_err(&cdev->dev, "%s %d skipping shutdown in state: %s\n",
+                       __func__, __LINE__, cosm_state_string[cdev->state]);
+               goto err;
+       }
+
+       if (!cdev->epd) {
+               rc = -ENOTCONN;
+               dev_err(&cdev->dev, "%s %d scif endpoint not connected rc %d\n",
+                       __func__, __LINE__, rc);
+               goto err;
+       }
+
+       rc = scif_send(cdev->epd, &msg, sizeof(msg), SCIF_SEND_BLOCK);
+       if (rc < 0) {
+               dev_err(&cdev->dev, "%s %d scif_send failed rc %d\n",
+                       __func__, __LINE__, rc);
+               goto err;
+       }
+       cdev->heartbeat_watchdog_enable = false;
+       cosm_set_state(cdev, MIC_SHUTTING_DOWN);
+       rc = 0;
+err:
+       mutex_unlock(&cdev->cosm_mutex);
+       return rc;
+}
+
+static int cosm_driver_probe(struct cosm_device *cdev)
+{
+       int rc;
+
+       /* Initialize SCIF server at first probe */
+       if (atomic_add_return(1, &g_num_dev) == 1) {
+               rc = cosm_scif_init();
+               if (rc)
+                       goto scif_exit;
+       }
+       mutex_init(&cdev->cosm_mutex);
+       INIT_WORK(&cdev->reset_trigger_work, cosm_reset_trigger_work);
+       INIT_WORK(&cdev->scif_work, cosm_scif_work);
+       cdev->sysfs_heartbeat_enable = true;
+       cosm_sysfs_init(cdev);
+       cdev->sdev = device_create_with_groups(g_cosm_class, cdev->dev.parent,
+                              MKDEV(0, cdev->index), cdev, cdev->attr_group,
+                              "mic%d", cdev->index);
+       if (IS_ERR(cdev->sdev)) {
+               rc = PTR_ERR(cdev->sdev);
+               dev_err(&cdev->dev, "device_create_with_groups failed rc %d\n",
+                       rc);
+               goto scif_exit;
+       }
+
+       cdev->state_sysfs = sysfs_get_dirent(cdev->sdev->kobj.sd,
+               "state");
+       if (!cdev->state_sysfs) {
+               rc = -ENODEV;
+               dev_err(&cdev->dev, "sysfs_get_dirent failed rc %d\n", rc);
+               goto destroy_device;
+       }
+       cosm_create_debug_dir(cdev);
+       return 0;
+destroy_device:
+       device_destroy(g_cosm_class, MKDEV(0, cdev->index));
+scif_exit:
+       if (atomic_dec_and_test(&g_num_dev))
+               cosm_scif_exit();
+       return rc;
+}
+
+static void cosm_driver_remove(struct cosm_device *cdev)
+{
+       cosm_delete_debug_dir(cdev);
+       sysfs_put(cdev->state_sysfs);
+       device_destroy(g_cosm_class, MKDEV(0, cdev->index));
+       flush_work(&cdev->reset_trigger_work);
+       cosm_stop(cdev, false);
+       if (atomic_dec_and_test(&g_num_dev))
+               cosm_scif_exit();
+
+       /* These sysfs entries might have allocated */
+       kfree(cdev->cmdline);
+       kfree(cdev->firmware);
+       kfree(cdev->ramdisk);
+       kfree(cdev->bootmode);
+}
+
+static int cosm_suspend(struct device *dev)
+{
+       struct cosm_device *cdev = dev_to_cosm(dev);
+
+       mutex_lock(&cdev->cosm_mutex);
+       switch (cdev->state) {
+       /**
+        * Suspend/freeze hooks in userspace have already shutdown the card.
+        * Card should be 'ready' in most cases. It is however possible that
+        * some userspace application initiated a boot. In those cases, we
+        * simply reset the card.
+        */
+       case MIC_ONLINE:
+       case MIC_BOOTING:
+       case MIC_SHUTTING_DOWN:
+               mutex_unlock(&cdev->cosm_mutex);
+               cosm_stop(cdev, false);
+               break;
+       default:
+               mutex_unlock(&cdev->cosm_mutex);
+               break;
+       }
+       return 0;
+}
+
+static const struct dev_pm_ops cosm_pm_ops = {
+       .suspend = cosm_suspend,
+       .freeze = cosm_suspend
+};
+
+static struct cosm_driver cosm_driver = {
+       .driver = {
+               .name =  KBUILD_MODNAME,
+               .owner = THIS_MODULE,
+               .pm = &cosm_pm_ops,
+       },
+       .probe = cosm_driver_probe,
+       .remove = cosm_driver_remove
+};
+
+static int __init cosm_init(void)
+{
+       int ret;
+
+       cosm_init_debugfs();
+
+       g_cosm_class = class_create(THIS_MODULE, cosm_driver_name);
+       if (IS_ERR(g_cosm_class)) {
+               ret = PTR_ERR(g_cosm_class);
+               pr_err("class_create failed ret %d\n", ret);
+               goto cleanup_debugfs;
+       }
+
+       ida_init(&g_cosm_ida);
+       ret = cosm_register_driver(&cosm_driver);
+       if (ret) {
+               pr_err("cosm_register_driver failed ret %d\n", ret);
+               goto ida_destroy;
+       }
+       return 0;
+ida_destroy:
+       ida_destroy(&g_cosm_ida);
+       class_destroy(g_cosm_class);
+cleanup_debugfs:
+       cosm_exit_debugfs();
+       return ret;
+}
+
+static void __exit cosm_exit(void)
+{
+       cosm_unregister_driver(&cosm_driver);
+       ida_destroy(&g_cosm_ida);
+       class_destroy(g_cosm_class);
+       cosm_exit_debugfs();
+}
+
+module_init(cosm_init);
+module_exit(cosm_exit);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION("Intel(R) MIC Coprocessor State Management (COSM) Driver");
+MODULE_LICENSE("GPL v2");
 
--- /dev/null
+/*
+ * 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
+ *
+ */
+#ifndef _COSM_COSM_H_
+#define _COSM_COSM_H_
+
+#include <linux/scif.h>
+#include "../bus/cosm_bus.h"
+
+#define COSM_HEARTBEAT_SEND_SEC 30
+#define SCIF_COSM_LISTEN_PORT  201
+
+/**
+ * enum COSM msg id's
+ * @COSM_MSG_SHUTDOWN: host->card trigger shutdown
+ * @COSM_MSG_SYNC_TIME: host->card send host time to card to sync time
+ * @COSM_MSG_HEARTBEAT: card->host heartbeat
+ * @COSM_MSG_SHUTDOWN_STATUS: card->host with shutdown status as payload
+ */
+enum cosm_msg_id {
+       COSM_MSG_SHUTDOWN,
+       COSM_MSG_SYNC_TIME,
+       COSM_MSG_HEARTBEAT,
+       COSM_MSG_SHUTDOWN_STATUS,
+};
+
+struct cosm_msg {
+       u64 id;
+       union {
+               u64 shutdown_status;
+               struct timespec64 timespec;
+       };
+};
+
+extern const char * const cosm_state_string[];
+extern const char * const cosm_shutdown_status_string[];
+
+void cosm_sysfs_init(struct cosm_device *cdev);
+int cosm_start(struct cosm_device *cdev);
+void cosm_stop(struct cosm_device *cdev, bool force);
+int cosm_reset(struct cosm_device *cdev);
+int cosm_shutdown(struct cosm_device *cdev);
+void cosm_set_state(struct cosm_device *cdev, u8 state);
+void cosm_set_shutdown_status(struct cosm_device *cdev, u8 status);
+void cosm_init_debugfs(void);
+void cosm_exit_debugfs(void);
+void cosm_create_debug_dir(struct cosm_device *cdev);
+void cosm_delete_debug_dir(struct cosm_device *cdev);
+int cosm_scif_init(void);
+void cosm_scif_exit(void);
+void cosm_scif_work(struct work_struct *work);
+
+#endif
 
--- /dev/null
+/*
+ * 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;
+}