staging: gasket: Add thermal sysfs nodes.

Add node to read current temperature and nodes
to set and enable/disable thermal warnings.

Change-Id: I8a12f6e33679573107686fe32be2e4a8bb0f9cc7
Signed-off-by: Leonid Lobachev <leonidl@google.com>
diff --git a/drivers/staging/gasket/apex_driver.c b/drivers/staging/gasket/apex_driver.c
index 1eff202..d1b823e 100644
--- a/drivers/staging/gasket/apex_driver.c
+++ b/drivers/staging/gasket/apex_driver.c
@@ -68,6 +68,10 @@
 	ATTR_KERNEL_HIB_SIMPLE_PAGE_TABLE_SIZE,
 	ATTR_KERNEL_HIB_NUM_ACTIVE_PAGES,
 	ATTR_TEMP,
+	ATTR_TEMP_WARN1,
+	ATTR_TEMP_WARN1_EN,
+	ATTR_TEMP_WARN2,
+	ATTR_TEMP_WARN2_EN,
 };
 
 /*
@@ -100,6 +104,7 @@
 	APEX_BAR2_REG_IDLEGENERATOR_IDLEGEN_IDLEREGISTER = 0x4A000,
 	APEX_BAR2_REG_KERNEL_HIB_PAGE_TABLE = 0x50000,
 	APEX_BAR2_REG_OMC0_D0 = 0x01a0d0,
+	APEX_BAR2_REG_OMC0_D4 = 0x01a0d4,
 	APEX_BAR2_REG_OMC0_D8 = 0x01a0d8,
 	APEX_BAR2_REG_OMC0_DC = 0x01a0dc,
 
@@ -566,11 +571,23 @@
 	}
 }
 
+/* Linear fit optimized for 25C-100C */
+static int adc_to_millic(int adc)
+{
+	return (662 - adc) * 250 + 550;
+}
+
+static int millic_to_adc(int millic)
+{
+	return (550 - millic) / 250 + 662;
+}
+
 /* Display driver sysfs entries. */
 static ssize_t sysfs_show(struct device *device, struct device_attribute *attr,
 			  char *buf)
 {
 	int ret;
+	unsigned value;
 	struct gasket_dev *gasket_dev;
 	struct gasket_sysfs_attribute *gasket_attr;
 	enum sysfs_attribute_type type;
@@ -606,13 +623,95 @@
 					gasket_dev->page_table[0]));
 		break;
 	case ATTR_TEMP:
-		// Read temperature
-		ret = gasket_dev_read_32(gasket_dev, APEX_BAR_INDEX,
-						APEX_BAR2_REG_OMC0_DC);
-		ret = (ret >> 16) & ((1 << 10) - 1);
-		// Remap to millicelsius (rough calculation)
-		ret = (((662 - ret) * 1000 / 4 + 500) * 10 + 500) / 10;
-		ret = snprintf(buf, PAGE_SIZE, "%i\n", ret);
+		value = gasket_dev_read_32(gasket_dev, APEX_BAR_INDEX,
+					   APEX_BAR2_REG_OMC0_DC);
+		value = (value >> 16) & ((1 << 10) - 1);
+		ret = scnprintf(buf, PAGE_SIZE, "%i\n", adc_to_millic(value));
+		break;
+	case ATTR_TEMP_WARN1:
+		value = gasket_dev_read_32(gasket_dev, APEX_BAR_INDEX,
+					   APEX_BAR2_REG_OMC0_D4);
+		value = (value >> 16) & ((1 << 10) - 1);
+		ret = scnprintf(buf, PAGE_SIZE, "%i\n", adc_to_millic(value));
+		break;
+	case ATTR_TEMP_WARN2:
+		value = gasket_dev_read_32(gasket_dev, APEX_BAR_INDEX,
+					   APEX_BAR2_REG_OMC0_D8);
+		value = (value >> 16) & ((1 << 10) - 1);
+		ret = scnprintf(buf, PAGE_SIZE, "%i\n", adc_to_millic(value));
+		break;
+	case ATTR_TEMP_WARN1_EN:
+		value = gasket_dev_read_32(gasket_dev, APEX_BAR_INDEX,
+					   APEX_BAR2_REG_OMC0_D4);
+		ret = scnprintf(buf, PAGE_SIZE, "%i\n", value >> 31);
+		break;
+	case ATTR_TEMP_WARN2_EN:
+		value = gasket_dev_read_32(gasket_dev, APEX_BAR_INDEX,
+					   APEX_BAR2_REG_OMC0_D8);
+		ret = scnprintf(buf, PAGE_SIZE, "%i\n", value >> 31);
+		break;
+	default:
+		dev_dbg(gasket_dev->dev, "Unknown attribute: %s\n",
+			attr->attr.name);
+		ret = 0;
+		break;
+	}
+
+	gasket_sysfs_put_attr(device, gasket_attr);
+	gasket_sysfs_put_device_data(device, gasket_dev);
+	return ret;
+}
+
+/* Set driver sysfs entries. */
+static ssize_t sysfs_store(struct device *device, struct device_attribute *attr,
+			   const char *buf, size_t count)
+{
+	int ret = count, value;
+	struct gasket_dev *gasket_dev;
+	struct gasket_sysfs_attribute *gasket_attr;
+	enum sysfs_attribute_type type;
+
+	if (kstrtoint(buf, 10, &value))
+		return -EINVAL;
+
+	gasket_dev = gasket_sysfs_get_device_data(device);
+	if (!gasket_dev) {
+		dev_err(device, "No Apex device sysfs mapping found\n");
+		return -ENODEV;
+	}
+
+	gasket_attr = gasket_sysfs_get_attr(device, attr);
+	if (!gasket_attr) {
+		dev_err(device, "No Apex device sysfs attr data found\n");
+		gasket_sysfs_put_device_data(device, gasket_dev);
+		return -ENODEV;
+	}
+
+	type = (enum sysfs_attribute_type)gasket_attr->data.attr_type;
+	switch (type) {
+	case ATTR_TEMP_WARN1:
+		value = millic_to_adc(value);
+		gasket_read_modify_write_32(gasket_dev, APEX_BAR_INDEX,
+					    APEX_BAR2_REG_OMC0_D4, value, 10,
+					    16);
+		break;
+	case ATTR_TEMP_WARN2:
+		value = millic_to_adc(value);
+		gasket_read_modify_write_32(gasket_dev, APEX_BAR_INDEX,
+					    APEX_BAR2_REG_OMC0_D8, value, 10,
+					    16);
+		break;
+	case ATTR_TEMP_WARN1_EN:
+		value = value > 0 ? 1 : 0;
+		gasket_read_modify_write_32(gasket_dev, APEX_BAR_INDEX,
+					    APEX_BAR2_REG_OMC0_D4, value, 1,
+					    31);
+		break;
+	case ATTR_TEMP_WARN2_EN:
+		value = value > 0 ? 1 : 0;
+		gasket_read_modify_write_32(gasket_dev, APEX_BAR_INDEX,
+					    APEX_BAR2_REG_OMC0_D8, value, 1,
+					    31);
 		break;
 	default:
 		dev_dbg(gasket_dev->dev, "Unknown attribute: %s\n",
@@ -634,6 +733,12 @@
 	GASKET_SYSFS_RO(node_0_num_mapped_pages, sysfs_show,
 			ATTR_KERNEL_HIB_NUM_ACTIVE_PAGES),
 	GASKET_SYSFS_RO(temp, sysfs_show, ATTR_TEMP),
+	GASKET_SYSFS_RW(temp_warn1, sysfs_show, sysfs_store, ATTR_TEMP_WARN1),
+	GASKET_SYSFS_RW(temp_warn1_en, sysfs_show, sysfs_store,
+			ATTR_TEMP_WARN1_EN),
+	GASKET_SYSFS_RW(temp_warn2, sysfs_show, sysfs_store, ATTR_TEMP_WARN2),
+	GASKET_SYSFS_RW(temp_warn2_en, sysfs_show, sysfs_store,
+			ATTR_TEMP_WARN2_EN),
 	GASKET_END_OF_ATTR_ARRAY
 };
 
@@ -711,17 +816,17 @@
 
 	// Enable thermal sensor clocks
 	gasket_read_modify_write_32(gasket_dev, APEX_BAR_INDEX,
-					APEX_BAR2_REG_OMC0_D0, 0x1, 1, 7);
+				    APEX_BAR2_REG_OMC0_D0, 0x1, 1, 7);
 
 	// Enable thermal sensor (ENAD ENVR ENBG)
 	gasket_read_modify_write_32(gasket_dev, APEX_BAR_INDEX,
-					APEX_BAR2_REG_OMC0_D8, 0x7, 3, 0);
+				    APEX_BAR2_REG_OMC0_D8, 0x7, 3, 0);
 
 	// Enable OMC thermal sensor controller
 	// This bit should be asserted 100 us after ENAD ENVR ENBG
 	schedule_timeout(usecs_to_jiffies(100));
 	gasket_read_modify_write_32(gasket_dev, APEX_BAR_INDEX,
-					APEX_BAR2_REG_OMC0_DC, 0x1, 1, 0);
+				    APEX_BAR2_REG_OMC0_DC, 0x1, 1, 0);
 
 	ret = gasket_sysfs_create_entries(gasket_dev->dev_info.device,
 					  apex_sysfs_attrs);
diff --git a/drivers/staging/gasket/gasket_sysfs.h b/drivers/staging/gasket/gasket_sysfs.h
index 151e8ed..e8f29a3 100644
--- a/drivers/staging/gasket/gasket_sysfs.h
+++ b/drivers/staging/gasket/gasket_sysfs.h
@@ -79,6 +79,13 @@
 		.data.attr_type = _attr_type                                   \
 	}
 
+#define GASKET_SYSFS_RW(_name, _show_function, _store_function, _attr_type)    \
+	{                                                                      \
+		.attr = __ATTR(_name, S_IWUSR | S_IWGRP | S_IRUGO,             \
+				_show_function, _store_function),              \
+		.data.attr_type = _attr_type                                   \
+	}
+
 /* Initializes the Gasket sysfs subsystem.
  *
  * Description: Performs one-time initialization. Must be called before usage