From d18f19fd0963cd7965e21612f3242b8423d22898 Mon Sep 17 00:00:00 2001
From: Jekyll Lai <jekyll_lai@wistron.com>
Date: Thu, 25 Nov 2010 16:53:14 +0800
Subject: [PATCH] Add mm-sysinfo module.
 A module which could showis the version of drivers on sysfs.


Signed-off-by: Jekyll Lai <jekyll_lai@wistron.com>
---
 drivers/platform/x86/Makefile     |    1 +
 drivers/platform/x86/mm-sysinfo.c |  347 +++++++++++++++++++++++++++++++++++++
 2 files changed, 348 insertions(+), 0 deletions(-)
 create mode 100644 meego/src/kernel-mrst-2.6.35.3/linux-2.6.35/drivers/platform/x86/mm-sysinfo.c

diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index ba295be..a4c3d46 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_INTEL_SCU_IPC)	+= intel_scu_ipc.o
 obj-$(CONFIG_INTEL_SCU_IPC_UTIL)+= intel_scu_ipcutil.o
 obj-$(CONFIG_GPIO_INTEL_PMIC)	+= intel_pmic_gpio.o
 obj-$(CONFIG_INTEL_MID_VIB)	+= intel_mid_vibrator.o
+obj-$(CONFIG_BOARD_MM)          += mm-sysinfo.o
diff --git a/drivers/platform/x86/mm-sysinfo.c b/drivers/platform/x86/mm-sysinfo.c
new file mode 100644
index 0000000..11a4139
--- /dev/null
+++ b/drivers/platform/x86/mm-sysinfo.c
@@ -0,0 +1,323 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/sfi.h>
+#include <asm/board-mm.h>
+
+#define mmsysinfo_debug(fmt, arg...) printk("mm_sysinfo: fun=%s "fmt, __FUNCTION__, ##arg);
+
+#ifdef CONFIG_SYSFS
+
+struct driver_info {
+	struct list_head         list;
+	struct class_attribute_string info;
+};
+
+struct sfi_oem_table {
+	struct sfi_table_header header;
+	char BoardID[4];
+	char FabID[4];
+	u8 ia32_major_rev[2];
+	u8 ia32_minor_rev[2];
+	u8 vh_major_rev;
+	u8 vh_minor_rev;
+	u8 ftl_major_rev;
+	u8 ftl_minor_rev;
+	u8 scu_major_rev;
+	u8 scu_minor_rec;
+	u8 pmu_major_rev;
+	u8 pmu_minor_rec;
+	u8 date[4];
+};
+
+static LIST_HEAD(info_list);
+static DEFINE_MUTEX(sysinfo_lock);
+static char remove_str[] = "Removed";
+static struct sfi_oem_table fw_table;
+
+struct driver_info *mm_sysinfo_lookup_name(char *name) {
+	struct driver_info *info = NULL;
+	mutex_lock(&sysinfo_lock);
+	list_for_each_entry(info, &info_list, list) {
+		if (!strcmp(name, info->info.attr.attr.name)) {
+			mutex_unlock(&sysinfo_lock);
+			return info;
+		}
+	}
+	mutex_unlock(&sysinfo_lock);
+	return NULL;
+}
+
+void save_freeinfo(struct driver_info *info) {
+	if(info->info.str!=remove_str && info->info.str!=NULL)
+	kfree(info->info.str);
+}
+
+static ssize_t fw_ia32_show(struct class *class,
+				struct class_attribute *attr,
+				char *buf)
+{
+	struct sfi_oem_table *sb = &fw_table;  
+	return sprintf( buf, "%X.%X.%X.%X\n", sb->ia32_major_rev[1], sb->ia32_major_rev[0], sb->ia32_minor_rev[1], sb->ia32_minor_rev[0]);
+}
+
+static ssize_t fw_vh_show(struct class *class,
+				struct class_attribute *attr,
+				char *buf)
+{
+	struct sfi_oem_table *sb = &fw_table;  
+	return sprintf( buf, "%d.%d\n", sb->vh_major_rev, sb->vh_minor_rev);
+}
+
+static ssize_t fw_ftl_show(struct class *class,
+				struct class_attribute *attr,
+				char *buf)
+{
+	struct sfi_oem_table *sb = &fw_table;
+	return sprintf( buf, "%d.%d\n", sb->ftl_major_rev, sb->ftl_minor_rev);
+}
+
+static ssize_t fw_scu_show(struct class *class,
+				struct class_attribute *attr,
+				char *buf)
+{
+	struct sfi_oem_table *sb = &fw_table;
+	return sprintf( buf, "%X.%X\n", sb->scu_major_rev, sb->scu_minor_rec);
+}
+
+static ssize_t fw_pmu_show(struct class *class,
+				struct class_attribute *attr,
+				char *buf)
+{
+	struct sfi_oem_table *sb = &fw_table;
+	return sprintf( buf, "%X.%X\n", sb->pmu_major_rev, sb->pmu_minor_rec);
+}
+
+static ssize_t fw_date_show(struct class *class,
+                                struct class_attribute *attr,
+                                char *buf)
+{
+        struct sfi_oem_table *sb = &fw_table;
+        return sprintf( buf, "20%d/%d/%d %d:00\n", sb->date[0], sb->date[1], sb->date[2], sb->date[3] );
+}
+
+static ssize_t hw_pcb_show(struct class *class,
+				struct class_attribute *attr,
+				char *buf)
+{
+	int reg;
+	reg = gpio_get_value(MMGPIO_TCA6416_PCB_VER_1) << 1;
+	reg &= gpio_get_value(MMGPIO_TCA6416_PCB_VER_2);
+	return sprintf( buf, "0x%X\n", reg );
+}
+
+static ssize_t hw_memory_show(struct class *class,
+				struct class_attribute *attr,
+				char *buf)
+{
+	int reg;
+	reg = gpio_get_value(MMGPIO_TCA6416_MEM_TYPE_1) << 2;
+	reg &= gpio_get_value(MMGPIO_TCA6416_MEM_TYPE_2) << 1;
+	reg &= gpio_get_value(MMGPIO_TCA6416_MEM_TYPE_3);
+	return sprintf( buf, "0x%X\n", reg );
+}
+
+static ssize_t hw_panel_show(struct class *class,
+				struct class_attribute *attr,
+				char *buf)
+{
+	int reg;
+	reg = gpio_get_value(MMGPIO_TCA6416_LCD_TYPE);
+	return sprintf( buf, "0x%X\n", reg );
+}
+
+static ssize_t hw_battery_show(struct class *class,
+				struct class_attribute *attr,
+				char *buf)
+{
+	int reg;
+	reg = gpio_get_value(MMGPIO_TCA6416_BAT_ID);
+	return sprintf( buf, "0x%X\n", reg );
+}
+
+static ssize_t overview_show(struct class *class,
+				struct class_attribute *attr,
+				char *buf)
+{
+	int lens = 0;
+	struct driver_info *info = NULL;
+	struct class_attribute *attr_ptr = class->class_attrs;
+	
+	mutex_lock(&sysinfo_lock);
+	
+	while(attr_ptr->attr.name!=NULL) {
+	  if(attr_ptr==attr || attr_ptr->show==NULL)
+	    break;
+	  lens += sprintf(buf+lens, "%s: ", attr_ptr->attr.name);
+	  lens += attr_ptr->show(class, attr_ptr, buf+lens);
+	  attr_ptr++;
+	}
+		
+	list_for_each_entry(info, &info_list, list) {
+		lens += sprintf( buf+lens, "%s: %s\n", info->info.attr.attr.name, info->info.str);
+	}
+	mutex_unlock(&sysinfo_lock);
+	
+	return lens;
+}
+
+static ssize_t export_store(struct class *class,
+				struct class_attribute *attr,
+				const char *buf, size_t len)
+{
+	char *name, *ver;
+	mmsysinfo_debug("buf=%s", buf);
+	
+	if(len<=3)
+	    return len;
+	
+	name = (char*)buf;
+	
+	ver = strchr(buf,':');
+	if(ver) {
+	    *ver++ = '\0';
+	    mm_sysinfo_register(name, ver);
+	}
+	return len;
+}
+
+static ssize_t unexport_store(struct class *class,
+				struct class_attribute *attr,
+				const char *buf, size_t len)
+{
+	mmsysinfo_debug("buf=%s", buf);
+	if(len)
+	  mm_sysinfo_unregister((char*)buf);
+	return len;
+}
+
+int sfi_parse_firmware(struct sfi_table_header *table) {
+	struct sfi_oem_table *sb = (struct sfi_oem_table *)table;
+	if(sb->header.len!=0x32) {// OOBS, table seems not match
+	  mmsysinfo_debug("OEM sfi table mismatch: %d\n", sb->header.len);
+	  return 0;
+	}
+	memcpy(&fw_table, sb, sizeof(struct sfi_oem_table));  
+	return 0;
+}
+
+static struct class_attribute mm_sysinfo_class_attrs[] = {
+	__ATTR_RO(fw_ia32),
+	__ATTR_RO(fw_vh),
+	__ATTR_RO(fw_ftl),
+	__ATTR_RO(fw_scu),
+	__ATTR_RO(fw_pmu),
+	__ATTR_RO(fw_date),
+#ifdef CONFIG_BOARD_MM_LAB
+	__ATTR_RO(hw_pcb),
+	__ATTR_RO(hw_memory),
+	__ATTR_RO(hw_panel),
+	__ATTR_RO(hw_battery),
+#endif
+	__ATTR_RO(overview),
+	__ATTR(export, 0222, NULL, export_store),
+	__ATTR(unexport, 0222, NULL, unexport_store),
+	__ATTR_NULL,
+};
+
+static struct class mm_sysinfo_class = {
+	.name =		"sysinfo",
+	.owner =	THIS_MODULE,
+	.class_attrs =	mm_sysinfo_class_attrs,
+};
+
+int mm_sysinfo_init(void)
+{  
+	sfi_table_parse("OEMB", NULL, NULL, sfi_parse_firmware);
+	return class_register(&mm_sysinfo_class);
+}
+EXPORT_SYMBOL(mm_sysinfo_init);
+
+int mm_sysinfo_register(char *drv_name, char *drv_ver) {
+	struct driver_info *info;
+	int status = 0;
+	struct class_attribute *attr_ptr = mm_sysinfo_class.class_attrs;
+	
+	if(drv_name==NULL || drv_ver==NULL) return -1;
+	mmsysinfo_debug("name=%s, ver=%s", drv_name, drv_ver);
+	// lookup the same drv_name...
+	info = mm_sysinfo_lookup_name(drv_name);
+	if(info!=NULL) {
+	  status = 0;
+	  mutex_lock(&sysinfo_lock);
+	  if(strcmp(info->info.str, drv_ver)) {
+	    save_freeinfo(info);
+	    info->info.str = kstrdup(drv_ver, GFP_KERNEL);
+	  }
+	  mutex_unlock(&sysinfo_lock);
+	  return status;
+	}
+	
+	while(attr_ptr->attr.name!=NULL) { // avoid register class attr
+	  if(!strcmp(attr_ptr->attr.name, drv_name))
+	    return 0;
+	  attr_ptr++;
+	}
+	
+	info = kzalloc(sizeof(struct driver_info), GFP_KERNEL);
+	if(info==NULL) {
+	  status = -ENOMEM;
+	  return status;
+	}
+	
+	info->info.attr.attr.name = kstrdup(drv_name, GFP_KERNEL);
+	info->info.attr.attr.mode = 0x0444;
+	info->info.attr.show = show_class_attr_string;
+	info->info.str = kstrdup(drv_ver, GFP_KERNEL);
+	
+	mutex_lock(&sysinfo_lock);
+	status = class_create_file(&mm_sysinfo_class, (struct class_attribute*)&info->info);
+	if(!status) list_add_tail(&info->list, &info_list);
+	mutex_unlock(&sysinfo_lock);
+	
+	return status;
+}
+EXPORT_SYMBOL(mm_sysinfo_register);
+
+int mm_sysinfo_unregister(char *drv_name) {
+	struct driver_info *info;
+	if(drv_name==NULL) return 0;
+	mmsysinfo_debug("remove entry %s", drv_name);
+	// lookup the same drv_name...
+	info = mm_sysinfo_lookup_name(drv_name);
+	if(info!=NULL) {
+	  mutex_lock(&sysinfo_lock);
+	  save_freeinfo(info);
+	  info->info.str = remove_str;
+	  mutex_unlock(&sysinfo_lock);
+	} else {
+	  mmsysinfo_debug("no entry found");
+	}
+	return 0;
+}
+EXPORT_SYMBOL(mm_sysinfo_unregister);
+#else	
+
+int mm_sysinfo_init(void)
+{
+  return 0;
+}
+EXPORT_SYMBOL(mm_sysinfo_init);
+
+int mm_sysinfo_register(char *drv_name, char *drv_ver) {
+  return 0;
+}
+EXPORT_SYMBOL(mm_sysinfo_register);
+
+int mm_sysinfo_unregister(char *drv_name) {
+  return 0;
+}
+EXPORT_SYMBOL(mm_sysinfo_unregister);
+#endif
-- 
1.7.0.4

