Advantech's new module comes equipped with "iManager" - an embedded controller (EC), providing embedded features for system integrators to increase reliability and simplify integration.Doesn't really follow SubmittingPatches guidelines; lines should be shorter.
This patch add the MFD driver for enabling Advantech iManager V2.0 chipset. Available functions support HW Monitor base on ITE-IT85XX chip. These functions are tested on Advantech SOM-5892 board. All the embedded functions are configured by a utility. Advantech has done all the hard work for user with the release of a suite of Software APIs.
These provide not only the underlying drivers required but also a rich set of user-friendly, intelligent and integrated interfaces, which speeds development, enhances security and offers add-on value for Advantech platforms.
Signed-off-by: Wei-Chun Pan Developer <weichun.pan@xxxxxxxxxxxxxxxx>
---
drivers/hwmon/Kconfig | 7 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/imanager2_hwm.c | 635 ++++++++++++++++++++++++++++++++++++++++++
drivers/hwmon/imanager2_hwm.h | 118 ++++++++
4 files changed, 761 insertions(+)
mode change 100644 => 100755 drivers/hwmon/Kconfig
mode change 100644 => 100755 drivers/hwmon/Makefile
create mode 100755 drivers/hwmon/imanager2_hwm.c
create mode 100755 drivers/hwmon/imanager2_hwm.h
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
old mode 100644
new mode 100755
index bc196f4..d4aeab6
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -39,6 +39,13 @@ config HWMON_DEBUG_CHIP
comment "Native drivers"
+config SENSORS_IMANAGER2
+ tristate "Support for Advantech iManager2 EC H.W. Monitor"
+ select MFD_CORE
+ select MFD_IMANAGER2
+ helpNot needed; see below.
+ Support for the Advantech iManager2 EC H.W. Monitor
+
config SENSORS_AB8500
tristate "AB8500 thermal monitoring"
depends on AB8500_GPADC && AB8500_BM
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
old mode 100644
new mode 100755
index c48f987..a2c8f07
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -146,6 +146,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
+obj-$(CONFIG_SENSORS_IMANAGER2) += imanager2_hwm.o
obj-$(CONFIG_PMBUS) += pmbus/
diff --git a/drivers/hwmon/imanager2_hwm.c b/drivers/hwmon/imanager2_hwm.c
new file mode 100755
index 0000000..48fe3dd
--- /dev/null
+++ b/drivers/hwmon/imanager2_hwm.c
@@ -0,0 +1,635 @@
+/* imanager2_hwm.c - HW Monitoring interface for Advantech EC IT8516/18/28
+ * Copyright (C) 2014 Richard Vidal-Dorsch <richard.dorsch@xxxxxxxxxxxxx>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mfd/advantech/imanager2.h>
+#include "imanager2_hwm.h"
+
+#define DRV_NAME CHIP_NAME "_hwm"
+#define DRV_VERSION "0.4.6"
+
+/* Voltage */
+static int ec_get_voltage_adc(struct it85xx *ec, int index,
+ u32 *volt_millivolt)
+{
+ int ret;
+ u8 portnum, tmp[2];
+
+ *volt_millivolt = 0;
+
+ spin_lock(&ec->lock);
+
+ if ((ec->flag & EC_F_MAILBOX) != 0) {
+ ret = ec_mailbox_read_buffer(ec, EC_CMD_MAILBOX_READ_HW_PIN,
+ ec_volt_table[index].did, &tmp[0],
+ 2);
+ } else {
+ u8 pin = ec->table.pinnum[ec->table.devid2itemnum[
+ ec_volt_table[index].did]];
+
+ ret = ec_io_read(EC_CMD_ADC_INDEX, pin, &portnum, 1);
+ if (ret != 0)
+ goto unlock;
+ if (portnum == 0xFF) {
+ ret = -EFAULT;
+ goto unlock;
+ }
+
+ ret = ec_io_read_byte_without_offset(EC_CMD_ADC_READ_LSB,
+ &tmp[1]);
+ if (ret != 0)
+ goto unlock;
+
+ ret = ec_io_read_byte_without_offset(EC_CMD_ADC_READ_MSB,
+ &tmp[0]);
+ }
+unlock:
+ spin_unlock(&ec->lock);
+
+ if (ret != 0)
+ return ret;
+
+ *volt_millivolt = ((tmp[0] << 8 | tmp[1]) & EC_ADC_RESOLUTION_MAX) *
+ ec_volt_table[index].factor *
+ EC_ADC_VOLTAGE_VALUE_MAX / EC_ADC_RESOLUTION_MAX;
+
+ return 0;
+}
+
+static void ec_volt_init_once(struct it85xx *ec, int index)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ec_volt_table); i++) {
+ if (ec->table.devid2itemnum[ec_volt_table[i].did] !=
+ EC_TABLE_ITEM_UNUSED) {
+ ec_volt_table[i].factor = 1;
+ ec_volt_table[i].visible = 1;
+ } else if (ec->table.devid2itemnum[ec_volt_table[i].did + 1] !=
+ EC_TABLE_ITEM_UNUSED) {
+ ec_volt_table[i].did += 1;
+ ec_volt_table[i].factor = 2;
+ ec_volt_table[i].visible = 1;
+ } else if (ec->table.devid2itemnum[ec_volt_table[i].did + 2] !=
+ EC_TABLE_ITEM_UNUSED) {
+ ec_volt_table[i].did += 2;
+ ec_volt_table[i].factor = 10;
+ ec_volt_table[i].visible = 1;
+ } else {
+ ec_volt_table[i].visible = 0;
+ }
+ }
+}
+
+static ssize_t show_in(struct device *dev, struct device_attribute *dev_attr,
+ char *buf)
+{
+ struct it85xx *ec = dev_get_drvdata(dev);
+ u32 val;
+ int i = to_sensor_dev_attr(dev_attr)->index;
+ int ret = ec_get_voltage_adc(ec, i, &val);
+
+ if (ret != 0)
+ return (ssize_t)ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t show_in_label(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
+{
+ int i = to_sensor_dev_attr(dev_attr)->index;
+ return sprintf(buf, "%s\n", ec_volt_table[i].name);
+}
+
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3);
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in, NULL, 4);
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_in, NULL, 5);
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_in, NULL, 6);
+static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_in, NULL, 7);
+static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_in, NULL, 8);
+static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_in, NULL, 9);
+static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_in, NULL, 10);
+static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, show_in, NULL, 11);
+
+static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_in_label, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_label, S_IRUGO, show_in_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_label, S_IRUGO, show_in_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_in_label, NULL, 3);
+static SENSOR_DEVICE_ATTR(in4_label, S_IRUGO, show_in_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(in5_label, S_IRUGO, show_in_label, NULL, 5);
+static SENSOR_DEVICE_ATTR(in6_label, S_IRUGO, show_in_label, NULL, 6);
+static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_in_label, NULL, 7);
+static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_in_label, NULL, 8);
+static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_in_label, NULL, 9);
+static SENSOR_DEVICE_ATTR(in10_label, S_IRUGO, show_in_label, NULL, 10);
+static SENSOR_DEVICE_ATTR(in11_label, S_IRUGO, show_in_label, NULL, 11);
+
+static struct attribute *it85xx_volt_attrs[] = {
+ &sensor_dev_attr_in0_label.dev_attr.attr,
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+
+ &sensor_dev_attr_in1_label.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+
+ &sensor_dev_attr_in2_label.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+
+ &sensor_dev_attr_in3_label.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+
+ &sensor_dev_attr_in4_label.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+
+ &sensor_dev_attr_in5_label.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+
+ &sensor_dev_attr_in6_label.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+
+ &sensor_dev_attr_in7_label.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+
+ &sensor_dev_attr_in8_label.dev_attr.attr,
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+
+ &sensor_dev_attr_in9_label.dev_attr.attr,
+ &sensor_dev_attr_in9_input.dev_attr.attr,
+
+ &sensor_dev_attr_in10_label.dev_attr.attr,
+ &sensor_dev_attr_in10_input.dev_attr.attr,
+
+ &sensor_dev_attr_in11_label.dev_attr.attr,
+ &sensor_dev_attr_in11_input.dev_attr.attr,
+
+ NULL
+};
+
+static umode_t it85xx_volt_mode(struct kobject *kobj, struct attribute *attr,
+ int index)
+{
+ struct sensor_device_attribute *sensor = container_of(
+ attr, struct sensor_device_attribute, dev_attr.attr);
+
+ if (ec_volt_table[sensor->index].visible == 0)
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group it85xx_volt_group = {
+ .attrs = it85xx_volt_attrs,
+ .is_visible = it85xx_volt_mode,
+};
+
+/* Current */
+static int ec_get_current_adc(struct it85xx *ec, int index,
+ u32 *curr_milliampere)
+{
+ int ret;
+ u8 i, tmp[5];
+ u16 value, factor;
+ u32 baseunit;
+
+ *curr_milliampere = 0;
+
+ spin_lock(&ec->lock);
+
+ if ((ec->flag & EC_F_MAILBOX) != 0)
+ ret = ec_mailbox_read_buffer(ec, EC_CMD_MAILBOX_READ_HW_PIN,
+ ec_curr_table[index].did,
+ tmp, ARRAY_SIZE(tmp));
+ else
+ ret = -EOPNOTSUPP;
+
+ spin_unlock(&ec->lock);
+
+ if (ret != 0)
+ return ret;
+
+ value = (tmp[0] << 8 | tmp[1]) & EC_ADC_RESOLUTION_MAX;
+ factor = tmp[2] << 8 | tmp[3];
+ baseunit = 1;Would it make sense to use DIV_ROUND_CLOSEST() for those operations
+ for (i = 1; i < tmp[4]; i++)
+ baseunit *= 10;
+
+ *curr_milliampere = value * factor * baseunit *
+ EC_ADC_CURRENT_VALUE_MAX / EC_ADC_RESOLUTION_MAX;
+
+ return 0;
+}
+
+static void ec_current_init_once(struct it85xx *ec)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ec_curr_table); i++)
+ if (ec->table.devid2itemnum[ec_curr_table[i].did] !=
+ EC_TABLE_ITEM_UNUSED)
+ ec_curr_table[i].visible = 1;
+ else
+ ec_curr_table[i].visible = 0;
+}
+
+static ssize_t show_curr(struct device *dev, struct device_attribute *dev_attr,
+ char *buf)
+{
+ struct it85xx *ec = dev_get_drvdata(dev);
+ u32 val;
+ int i = to_sensor_dev_attr(dev_attr)->index;
+ int ret = ec_get_current_adc(ec, i, &val);
+
+ if (ret != 0)
+ return (ssize_t)ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t show_curr_label(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
+{
+ int i = to_sensor_dev_attr(dev_attr)->index;
+ return sprintf(buf, "%s\n", ec_curr_table[i].name);
+}
+
+static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, show_curr, NULL, 0);
+static SENSOR_DEVICE_ATTR(curr1_label, S_IRUGO, show_curr_label, NULL, 0);
+
+static struct attribute *it85xx_curr_attrs[] = {
+ &sensor_dev_attr_curr1_label.dev_attr.attr,
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+
+ NULL
+};
+
+static umode_t it85xx_curr_mode(struct kobject *kobj, struct attribute *attr,
+ int index)
+{
+ struct sensor_device_attribute *sensor = container_of(
+ attr, struct sensor_device_attribute, dev_attr.attr);
+
+ if (ec_curr_table[sensor->index].visible == 0)
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group it85xx_curr_group = {
+ .attrs = it85xx_curr_attrs,
+ .is_visible = it85xx_curr_mode,
+};
+
+/* Temperature */
+static int ec_get_thermal_temperature(struct it85xx *ec, int index,
+ int *temp_millicelsius)
+{
+ int ret;
+ u8 tmp;
+
+ *temp_millicelsius = 0;
+
+ spin_lock(&ec->lock);
+ ret = ec_acpiram_read_byte(ec, ec_temp_table[index].zonetype_acpireg,
+ &tmp);
+ spin_unlock(&ec->lock);
+
+ if (ret != 0)
+ return ret;
+
+ *temp_millicelsius = ((signed)tmp) * 1000;
+
+ return 0;
+}
+
+static void ec_temp_init_once(struct it85xx *ec)
+{
+ int i, j, ret;
+ u8 tmltype;
+ struct ec_thermalzone zone;
+
+ for (i = 0; i < ARRAY_SIZE(ec_temp_table); i++)
+ ec_temp_table[i].visible = 0;
+
+ for (i = 0; i < EC_MAX_THERMAL_ZONE; i++) {
+ spin_lock(&ec->lock);
+
+ if ((ec->flag & EC_F_MAILBOX) != 0) {
+ int len = sizeof(struct ec_thermalzone);
+ ret = ec_read_thermalzone(ec, i, NULL, NULL,
+ (u8 *)&zone, &len);
+ } else {
+ ret = ec_io_read(
+ EC_CMD_HWRAM_READ,
+ EC_HWRAM_ADDR_THERMAL_SOURCE_SMB_STATUS(i),
+ &zone.status, 1);
+ }
+
+ spin_unlock(&ec->lock);
+
+ if (ret != 0)
+ continue;
+
+ tmltype = (zone.status >> 5);
+
+ for (j = 0; j < ARRAY_SIZE(ec_temp_table); j++) {
+ if (tmltype == ec_temp_table[j].zonetype_value) {
+ ec_temp_table[j].visible = 1;
+ break;
+ }
+ }
+ }
+}
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *dev_attr,
+ char *buf)
+{
+ struct it85xx *ec = dev_get_drvdata(dev);
+ int i = to_sensor_dev_attr(dev_attr)->index;
+ int val, ret;
+ ret = ec_get_thermal_temperature(ec, i, &val);
+
+ if (ret != 0)
+ return (ssize_t)ret;
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t show_temp_label(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
+{
+ int i = to_sensor_dev_attr(dev_attr)->index;
+ return sprintf(buf, "%s\n", ec_temp_table[i].name);
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
+
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_temp_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_temp_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, show_temp_label, NULL, 3);
+
+static struct attribute *it85xx_temp_attrs[] = {
+ &sensor_dev_attr_temp1_label.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+
+ &sensor_dev_attr_temp2_label.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+
+ &sensor_dev_attr_temp3_label.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+
+ &sensor_dev_attr_temp4_label.dev_attr.attr,
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+
+ NULL
+};
+
+static umode_t it85xx_temp_mode(struct kobject *kobj, struct attribute *attr,
+ int index)
+{
+ struct sensor_device_attribute *sensor = container_of(
+ attr, struct sensor_device_attribute, dev_attr.attr);
+
+ if (ec_temp_table[sensor->index].visible == 0)
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group it85xx_temp_group = {
+ .attrs = it85xx_temp_attrs,
+ .is_visible = it85xx_temp_mode,
+};
+
+/* Fan Speed */
+static int ec_get_fan_speed(struct it85xx *ec, int index, u32 *speed_rpm)
+{
+ int ret;
+ u8 tmp[2];
+
+ *speed_rpm = 0;
+
+ spin_lock(&ec->lock);
+
+ if ((ec->flag & EC_F_MAILBOX) != 0)
+ ret = ec_mailbox_read_buffer(ec, EC_CMD_MAILBOX_READ_HW_PIN,
+ ec_fan_table[index].did,
+ &tmp[0], 2);
+ else
+ ret = ec_io_read(EC_CMD_ACPIRAM_READ,
+ ec_fan_table[index].fspeed_acpireg,
+ &tmp[0], 2);
+
+ spin_unlock(&ec->lock);if (!fscr)
+
+ if (ret != 0)
+ return ret;
+
+ if (tmp[0] == 0xFF && tmp[1] == 0xFF)
+ return -ENODEV;
+
+ *speed_rpm = (tmp[0] << 8) | tmp[1];
+
+ return 0;
+}
+
+static void ec_fan_init_once(struct it85xx *ec)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ec_fan_table); i++)
+ ec_fan_table[i].visible = 0;
+
+ if ((ec->flag & EC_F_MAILBOX) != 0) {
+ for (i = 0; i < ARRAY_SIZE(ec_fan_table); i++)
+ if (ec->table.devid2itemnum[ec_fan_table[i].did] !=
+ EC_TABLE_ITEM_UNUSED)
+ ec_fan_table[i].visible = 1;
+ } else {
+ int fnum, ret;
+ u8 tmp, fscr;
+
+ for (fnum = 0; fnum < EC_MAX_IO_FAN; fnum++) {
+ spin_lock(&ec->lock);
+ ret = ec_io_read(EC_CMD_HWRAM_READ,
+ EC_HWRAM_ADDR_FAN_CONTROL(fnum),
+ &tmp, 1);
+ spin_unlock(&ec->lock);
+
+ if (ret != 0)
+ continue;
+
+ fscr = (tmp >> 4) & 0x03;
+
+ switch (fscr) {
+ case 1: /* tacho0 */
+ case 2: /* tacho1 */
+ case 3: /* tacho2 */
+ i = fscr - 1;
+ break;
+ default:
+ continue;
+ }
+
+ if (ec_fan_table[i].visible == 1)
+ continue;
+
+ ec_fan_table[i].fspeed_acpireg =
+ EC_ACPIRAM_ADDR_FAN_SPEED_BASE(i);
+
+ ec_fan_table[i].visible = 1;
+ }
+ }
+}
+
+static ssize_t show_fan(struct device *dev, struct device_attribute *dev_attr,
+ char *buf)
+{
+ struct it85xx *ec = dev_get_drvdata(dev);
+ int i = to_sensor_dev_attr(dev_attr)->index;
+ u32 val;
+ int ret = ec_get_fan_speed(ec, i, &val);
+
+ if (ret != 0)
+ return (ssize_t)ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t show_fan_label(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
+{
+ int i = to_sensor_dev_attr(dev_attr)->index;
+ return sprintf(buf, "%s\n", ec_fan_table[i].name);
+}
+
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2);
+
+static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, show_fan_label, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, show_fan_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan3_label, S_IRUGO, show_fan_label, NULL, 2);
+
+static struct attribute *it85xx_fan_attrs[] = {
+ &sensor_dev_attr_fan1_label.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+
+ &sensor_dev_attr_fan2_label.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+
+ &sensor_dev_attr_fan3_label.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+
+ NULL
+};
+
+static umode_t it85xx_fan_mode(struct kobject *kobj, struct attribute *attr,
+ int index)
+{
+ struct sensor_device_attribute *sensor = container_of(
+ attr, struct sensor_device_attribute, dev_attr.attr);
+
+ if (ec_fan_table[sensor->index].visible == 0)
+ return 0;You lost me here. What is this for ? Why do you allocate this structure
+
+ return attr->mode;
+}
+
+static const struct attribute_group it85xx_fan_group = {
+ .attrs = it85xx_fan_attrs,
+ .is_visible = it85xx_fan_mode,
+};
+
+/* HWM groups */
+static const struct attribute_group *it85xx_hwmon_groups[] = {
+ &it85xx_volt_group,
+ &it85xx_curr_group,
+ &it85xx_temp_group,
+ &it85xx_fan_group,
+
+ NULL
+};
+
+/* Module */
+static int it85xx_hwmon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct it85xx *hwmon_ec;
+ struct device *hwmon_dev;
+
+ hwmon_ec = devm_kzalloc(dev, sizeof(struct it85xx), GFP_KERNEL);
+
+ if (hwmon_ec == NULL)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, hwmon_ec);
+
+ hwmon_ec = dev->parent->platform_data;
+
+ ec_volt_init_once(hwmon_ec, 0);
+ ec_temp_init_once(hwmon_ec);
+ ec_current_init_once(hwmon_ec);
+ ec_fan_init_once(hwmon_ec);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, CHIP_NAME,
+ hwmon_ec,
+ it85xx_hwmon_groups);
+
+ if (IS_ERR(hwmon_dev) != 0) {
+ pr_err("Could not register it85xx hwmon device\n");
+ return PTR_ERR(hwmon_dev);
+ }
+
+ pr_info("HWM driver v%s loaded\n", DRV_VERSION);
+
+ return 0;
+}
+
+static int it85xx_hwmon_remove(struct platform_device *pdev)
+{
+ pr_info("HWM driver removed\n");
+
+ return 0;
+}
+
+static struct platform_driver it85xx_hwmon_driver = {
+ .probe = it85xx_hwmon_probe,
+ .remove = it85xx_hwmon_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ },
+};
+
+module_platform_driver(it85xx_hwmon_driver);
+
+MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>");
+MODULE_DESCRIPTION("HW Monitoring interface for Advantech EC IT8516/18/28");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/hwmon/imanager2_hwm.h b/drivers/hwmon/imanager2_hwm.h
new file mode 100755
index 0000000..ccf4a4c
--- /dev/null
+++ b/drivers/hwmon/imanager2_hwm.h
@@ -0,0 +1,118 @@
+/* imanager2_hwm.h - HW Monitoring interface for Advantech EC IT8516/18/28
+ * Copyright (C) 2014 Richard Vidal-Dorsch <richard.dorsch@xxxxxxxxxxxxx>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __IMANAGER2_HWM_H__
+#define __IMANAGER2_HWM_H__
+
+/* ADC */
+#define EC_ADC_RESOLUTION_MAX 0x03FF /* 10-bit */
+#define EC_ADC_VOLTAGE_VALUE_MAX 3000 /* max: 3.0 V */
+#define EC_ADC_CURRENT_VALUE_MAX 3000 /* max: 3.0 A */
+
+struct volt_item {
+ u8 did;
+ const char *name;
+ int factor;
+ int visible;
+};
+
+struct curr_item {
+ const u8 did;
+ const char *name;
+ int visible;
+};
+
+static struct volt_item ec_volt_table[] = {
+ {.did = adcmosbat, .name = "BAT CMOS", .factor = 0, .visible = 0},
+ {.did = adcbat, .name = "BAT", .factor = 0, .visible = 0},
+ {.did = adc5vs0, .name = "5V S0", .factor = 0, .visible = 0},
+ {.did = adv5vs5, .name = "5V S5", .factor = 0, .visible = 0},
+ {.did = adc33vs0, .name = "3V3 S0", .factor = 0, .visible = 0},
+ {.did = adc33vs5, .name = "3V3 S5", .factor = 0, .visible = 0},
+ {.did = adv12vs0, .name = "12V S0", .factor = 0, .visible = 0},
+ {.did = adcvcorea, .name = "Vcore A", .factor = 0, .visible = 0},
+ {.did = adcvcoreb, .name = "Vcore B", .factor = 0, .visible = 0},
+ {.did = adcdc, .name = "DC", .factor = 0, .visible = 0},
+ {.did = adcdcstby, .name = "DC Standby", .factor = 0, .visible = 0},
+ {.did = adcdcother, .name = "DC Other", .factor = 0, .visible = 0}
+};
+
+static struct curr_item ec_curr_table[] = {
+ {.did = adccurrent, .name = "IMON", .visible = 0}
+};
+
+/* Thermal */
+#define EC_MAX_THERMAL_ZONE 4
+
+#define EC_THERMAL_TYPE_NONE 0
+#define EC_THERMAL_TYPE_SYS1 1
+#define EC_THERMAL_TYPE_CPU 2
+#define EC_THERMAL_TYPE_SYS3 3
+#define EC_THERMAL_TYPE_SYS2 4
+
+struct temp_item {
+ const char *name;
+ const u8 zonetype_value;
+ u8 zonetype_acpireg;
+ int visible;
+};
+
+static struct temp_item ec_temp_table[] = {
+ {.name = "Temp SYS",
+ .zonetype_value = EC_THERMAL_TYPE_SYS1,
+ .zonetype_acpireg = EC_ACPIRAM_ADDR_LOCAL_TEMPERATURE(1),
+ .visible = 0},
+ {.name = "Temp CPU",
+ .zonetype_value = EC_THERMAL_TYPE_CPU,
+ .zonetype_acpireg = EC_ACPIRAM_ADDR_REMOTE_TEMPERATURE(1),
+ .visible = 0},
+ {.name = "Temp SYS3",
+ .zonetype_value = EC_THERMAL_TYPE_SYS3,
+ .zonetype_acpireg = EC_ACPIRAM_ADDR_LOCAL_TEMPERATURE(2),
+ .visible = 0},
+ {.name = "Temp SYS2",
+ .zonetype_value = EC_THERMAL_TYPE_SYS2,
+ .zonetype_acpireg = EC_ACPIRAM_ADDR_REMOTE_TEMPERATURE(2),
+ .visible = 0},
+};
+
+struct ec_thermalzone {
+ u8 channel,
+ addr,
+ cmd,
+ status,
+ fancode,
+ temp;
+};
+
+/* Tacho */
+#define EC_MAX_IO_FAN 3
+
+struct fan_item {
+ const u8 did;
+ const char *name;
+ u8 fspeed_acpireg;
+ int visible;
+};
+
+static struct fan_item ec_fan_table[] = {
+ {.did = tacho0, .name = "Fan CPU", .fspeed_acpireg = 0, .visible = 0},
+ {.did = tacho1, .name = "Fan SYS", .fspeed_acpireg = 0, .visible = 0},
+ {.did = tacho2, .name = "Fan SYS2", .fspeed_acpireg = 0, .visible = 0},
+};Please merge the include file into the source. There is no benefit of having it extra,
+
+#endif /* __IMANAGER2_HWM_H__ */