This patch adds Advantech iManager Embedded Controller MFD core driver.
This mfd core dirver provides an interface for GPIO, I2C, HWmon,
Watchdog, and Backlight/Brightness control.

Signed-off-by: Richard Vidal-Dorsch <richard.dor...@gmail.com>
---
 drivers/mfd/Kconfig             |  18 +
 drivers/mfd/Makefile            |   1 +
 drivers/mfd/imanager-core.c     | 941 ++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/imanager-ec.h | 228 ++++++++++
 include/linux/mfd/imanager.h    | 221 ++++++++++
 5 files changed, 1409 insertions(+)
 create mode 100644 drivers/mfd/imanager-core.c
 create mode 100644 include/linux/mfd/imanager-ec.h
 create mode 100644 include/linux/mfd/imanager.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index c6df644..294c19d 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -388,6 +388,24 @@ config MFD_INTEL_QUARK_I2C_GPIO
          their respective IO driver.
          The GPIO exports a total amount of 8 interrupt-capable GPIOs.
 
+config MFD_IMANAGER
+       tristate "Advantech iManager EC device"
+       select MFD_CORE
+       help
+         This is the core driver for Advantech iManager Embedded Controller
+         found on some Advantech SOM, MIO, AIMB, and PCM modules/boards. The
+         EC may provide functions like GPIO, I2C bus, HW monitoring, Watchdog,
+         and backlight/brightness control.
+
+         The following Advantech boards are supported:
+               * All SOM modules newer than SOM-5788
+               * MIO-5250/5251/5270/5271/5272/5290 and newer
+               * PCM-9389/9365/9376 and newer
+               * AIMB-273/274/230/231 and newer
+
+         This driver can also be built as a module.  If so, the module
+         will be called imanager-core.
+
 config LPC_ICH
        tristate "Intel ICH LPC"
        depends on PCI
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 9834e66..e4b0a4d 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -156,6 +156,7 @@ obj-$(CONFIG_MFD_DB8500_PRCMU)      += db8500-prcmu.o
 obj-$(CONFIG_AB8500_CORE)      += ab8500-core.o ab8500-sysctrl.o
 obj-$(CONFIG_MFD_TIMBERDALE)    += timberdale.o
 obj-$(CONFIG_PMIC_ADP5520)     += adp5520.o
+obj-$(CONFIG_MFD_IMANAGER)     += imanager-core.o
 obj-$(CONFIG_MFD_KEMPLD)       += kempld-core.o
 obj-$(CONFIG_MFD_INTEL_QUARK_I2C_GPIO) += intel_quark_i2c_gpio.o
 obj-$(CONFIG_LPC_SCH)          += lpc_sch.o
diff --git a/drivers/mfd/imanager-core.c b/drivers/mfd/imanager-core.c
new file mode 100644
index 0000000..2ec1b79
--- /dev/null
+++ b/drivers/mfd/imanager-core.c
@@ -0,0 +1,941 @@
+/*
+ * Advantech iManager MFD driver
+ * Partially derived from kempld-core
+ *
+ * Copyright (C) 2016 Advantech Co., Ltd.
+ * Author: Richard Vidal-Dorsch <richard.dor...@advantech.com>
+ *
+ * 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 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitops.h>
+#include <linux/byteorder/generic.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/imanager.h>
+#include <linux/mfd/imanager-ec.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+
+enum kinds { IT8518, IT8528 };
+
+static struct platform_device *imanager_pdev;
+
+static const char * const chip_names[] = {
+       "it8518",
+       "it8528",
+       NULL
+};
+
+static const struct imanager_ec_device ecdev_table[] = {
+       /* GPIO */
+       { IMANAGER_EC_DEVICE(GPIO0, GPIO, -1) },
+       { IMANAGER_EC_DEVICE(GPIO1, GPIO, -1) },
+       { IMANAGER_EC_DEVICE(GPIO2, GPIO, -1) },
+       { IMANAGER_EC_DEVICE(GPIO3, GPIO, -1) },
+       { IMANAGER_EC_DEVICE(GPIO4, GPIO, -1) },
+       { IMANAGER_EC_DEVICE(GPIO5, GPIO, -1) },
+       { IMANAGER_EC_DEVICE(GPIO6, GPIO, -1) },
+       { IMANAGER_EC_DEVICE(GPIO7, GPIO, -1) },
+       /* FAN */
+       { IMANAGER_EC_DEVICE(CPUFAN_2P,  PWM, 2) },
+       { IMANAGER_EC_DEVICE(CPUFAN_4P,  PWM, 4) },
+       { IMANAGER_EC_DEVICE(SYSFAN1_2P, PWM, 2) },
+       { IMANAGER_EC_DEVICE(SYSFAN1_4P, PWM, 4) },
+       { IMANAGER_EC_DEVICE(SYSFAN2_2P, PWM, 2) },
+       { IMANAGER_EC_DEVICE(SYSFAN2_4P, PWM, 4) },
+       /* ADC */
+       { IMANAGER_EC_DEVICE(ADC12VS0,    ADC, 1) },
+       { IMANAGER_EC_DEVICE(ADC12VS0_2,  ADC, 2) },
+       { IMANAGER_EC_DEVICE(ADC12VS0_10, ADC, 10) },
+       { IMANAGER_EC_DEVICE(ADC5VS0,     ADC, 1) },
+       { IMANAGER_EC_DEVICE(ADC5VS0_2,   ADC, 2) },
+       { IMANAGER_EC_DEVICE(ADC5VS0_10,  ADC, 10) },
+       { IMANAGER_EC_DEVICE(ADC5VS5,     ADC, 1) },
+       { IMANAGER_EC_DEVICE(ADC5VS5_2,   ADC, 2) },
+       { IMANAGER_EC_DEVICE(ADC5VS5_10,  ADC, 10) },
+       { IMANAGER_EC_DEVICE(ADC33VS0,    ADC, 1) },
+       { IMANAGER_EC_DEVICE(ADC33VS0_2,  ADC, 2) },
+       { IMANAGER_EC_DEVICE(ADC33VS0_10, ADC, 10) },
+       { IMANAGER_EC_DEVICE(CMOSBAT,     ADC, 1) },
+       { IMANAGER_EC_DEVICE(CMOSBAT_2,   ADC, 2) },
+       { IMANAGER_EC_DEVICE(CMOSBAT_10,  ADC, 10) },
+       { IMANAGER_EC_DEVICE(VCOREA,      ADC, 1) },
+       { IMANAGER_EC_DEVICE(CURRENT,     ADC, 1) },
+       /* I2C/SMBus */
+       { IMANAGER_EC_DEVICE(SMBEEPROM,   SMB, -1) },
+       { IMANAGER_EC_DEVICE(I2COEM,      SMB, -1) },
+       { IMANAGER_EC_DEVICE(SMBOEM0,     SMB, -1) },
+       { IMANAGER_EC_DEVICE(SMBPECI,     SMB, -1) },
+       /* Backlight/Brightness */
+       { IMANAGER_EC_DEVICE(BRIGHTNESS,  PWM, -1) },
+       { IMANAGER_EC_DEVICE(BRIGHTNESS2, PWM, -1) },
+       /* Watchdog */
+       { IMANAGER_EC_DEVICE(WDIRQ, IRQ,  -1) },
+       { IMANAGER_EC_DEVICE(WDNMI, GPIO, -1) },
+       { 0 }
+};
+
+/**
+ * iManager I/O
+ */
+
+enum imanager_io_buffer_status { IS_CLEARED = 0, IS_SET };
+
+#define CHECK_BIT(reg, bit) ((reg) & (bit))
+
+static inline int check_io28_ready(uint bit, uint state)
+{
+       int ret, i = 0;
+
+       do {
+               ret = inb(IT8528_CMD_PORT);
+               if (CHECK_BIT(ret, bit) == state)
+                       return 0;
+               usleep_range(EC_DELAY_MIN, EC_DELAY_MAX);
+       } while (i++ < EC_MAX_RETRY);
+
+       return -ETIME;
+}
+
+static inline int ec_inb(int addr, int reg)
+{
+       outb(reg, addr);
+       return inb(addr + 1);
+}
+
+static inline void ec_outb(int addr, int reg, int val)
+{
+       outb(reg, addr);
+       outb(val, addr + 1);
+}
+
+static inline int ec_io28_inb(int addr, int reg)
+{
+       int ret;
+
+       ret = check_io28_ready(EC_IO28_INBUF, IS_CLEARED);
+       if (ret)
+               return ret;
+
+       /* prevent firmware lock */
+       inb(addr - 1);
+
+       outb(reg, addr);
+
+       ret = check_io28_ready(EC_IO28_OUTBUF, IS_SET);
+       if (ret)
+               return ret;
+
+       return inb(addr - 1);
+}
+
+static inline int ec_io28_outb(int addr, int reg, int val)
+{
+       int ret;
+
+       ret = check_io28_ready(EC_IO28_INBUF, IS_CLEARED);
+       if (ret)
+               return ret;
+
+       outb(reg, addr);
+
+       ret = check_io28_ready(EC_IO28_INBUF, IS_CLEARED);
+       if (ret)
+               return ret;
+
+       outb(val, addr - 1);
+
+       return 0;
+}
+
+static inline int ec_io18_read(int cmd)
+{
+       return ec_inb(IT8518_CMD_PORT, cmd);
+}
+
+static inline int ec_io18_write(int cmd, int value)
+{
+       ec_outb(IT8518_CMD_PORT, cmd, value);
+
+       return 0;
+}
+
+static inline int ec_io28_read(int cmd)
+{
+       return ec_io28_inb(IT8528_CMD_PORT, cmd + EC_CMD_OFFSET_READ);
+}
+
+static inline int ec_io28_write(int cmd, int value)
+{
+       return ec_io28_outb(IT8528_CMD_PORT, cmd + EC_CMD_OFFSET_WRITE, value);
+}
+
+static int imanager_check_ec_ready(struct imanager_io_ops *io)
+{
+       int i = 0;
+
+       do {
+               if (!io->read(EC_CMD_CHK_RDY))
+                       return 0;
+               usleep_range(EC_DELAY_MIN, EC_DELAY_MAX);
+       } while (i++ < EC_MAX_RETRY);
+
+       return -ETIME;
+}
+
+/**
+ * iManager Device Configuration
+ */
+
+static void imanager_add_attribute(struct imanager_ec_data *ec,
+                                  struct imanager_device_attribute *attr)
+{
+       struct imanager_gpio_device *gpio = &ec->gpio;
+       struct imanager_adc_device *adc = &ec->hwmon.adc;
+       struct imanager_fan_device *fan = &ec->hwmon.fan;
+       struct imanager_i2c_device *i2c = &ec->i2c;
+       struct imanager_backlight_device *bl = &ec->bl;
+       struct imanager_watchdog_device *wdt = &ec->wdt;
+
+       switch (attr->ecdev->type) {
+       case GPIO:
+               switch (attr->did) {
+               case GPIO0:
+               case GPIO1:
+               case GPIO2:
+               case GPIO3:
+               case GPIO4:
+               case GPIO5:
+               case GPIO6:
+               case GPIO7:
+                       gpio->attr[gpio->num++] = attr;
+                       break;
+               case WDNMI:
+                       wdt->attr[1] = attr;
+                       wdt->num++;
+                       break;
+               }
+       case ADC:
+               switch (attr->did) {
+               case ADC12VS0:
+               case ADC12VS0_2:
+               case ADC12VS0_10:
+                       adc->attr[0] = attr;
+                       adc->label[0] = "+12VS0";
+                       adc->num++;
+                       break;
+               case ADC5VS5:
+               case ADC5VS5_2:
+               case ADC5VS5_10:
+                       adc->attr[1] = attr;
+                       adc->label[1] = "+5VS0";
+                       adc->num++;
+                       break;
+               case CMOSBAT:
+               case CMOSBAT_2:
+               case CMOSBAT_10:
+                       adc->attr[2] = attr;
+                       adc->label[2] = "+3.3VS0";
+                       adc->num++;
+                       break;
+               case VCOREA:
+               case ADC5VS0:
+               case ADC5VS0_2:
+               case ADC5VS0_10:
+                       adc->attr[3] = attr;
+                       adc->num++;
+                       break;
+               case CURRENT:
+               case ADC33VS0:
+               case ADC33VS0_2:
+               case ADC33VS0_10:
+                       adc->attr[4] = attr;
+                       adc->num++;
+                       break;
+               }
+       case PWM:
+               switch (attr->did) {
+               case CPUFAN_2P:
+               case CPUFAN_4P:
+                       fan->attr[0] = attr;
+                       fan->label[0] = "FAN CPU";
+                       fan->temp_label[0] = "Temp CPU";
+                       fan->num++;
+                       break;
+               case SYSFAN1_2P:
+               case SYSFAN1_4P:
+                       fan->attr[1] = attr;
+                       fan->label[1] = "FAN SYS1";
+                       fan->temp_label[1] = "Temp SYS1";
+                       fan->num++;
+                       break;
+               case SYSFAN2_2P:
+               case SYSFAN2_4P:
+                       fan->attr[2] = attr;
+                       fan->label[2] = "FAN SYS2";
+                       fan->temp_label[2] = "Temp SYS2";
+                       fan->num++;
+                       break;
+               case BRIGHTNESS:
+                       bl->attr[0] = attr;
+                       bl->brightness[0] = EC_OFFSET_BRIGHTNESS1;
+                       bl->num++;
+                       break;
+               case BRIGHTNESS2:
+                       bl->attr[1] = attr;
+                       bl->brightness[1] = EC_OFFSET_BRIGHTNESS2;
+                       bl->num++;
+                       break;
+               }
+       case SMB:
+               switch (attr->did) {
+               case SMBEEPROM:
+                       i2c->attr[SMB_EEP] = attr;
+                       i2c->num++;
+                       break;
+               case I2COEM:
+                       i2c->attr[I2C_OEM] = attr;
+                       i2c->num++;
+                       break;
+               case SMBOEM0:
+                       i2c->attr[SMB_1] = attr;
+                       i2c->num++;
+                       break;
+               case SMBPECI:
+                       i2c->attr[SMB_PECI] = attr;
+                       i2c->num++;
+                       break;
+               }
+       case IRQ:
+               if (attr->did == WDIRQ) {
+                       wdt->attr[0] = attr;
+                       wdt->num++;
+                       break;
+               }
+       }
+}
+
+enum imanager_device_table_type { DEVID = 0, HWPIN, POLARITY };
+
+static int imanager_read_device_config(struct imanager_ec_data *ec)
+{
+       struct imanager_ec_message msgs[] = {
+               { IMANAGER_MSG_SIMPLE(EC_MAX_DID, 0, DEVID, NULL) },
+               { IMANAGER_MSG_SIMPLE(EC_MAX_DID, 0, HWPIN, NULL) },
+               { IMANAGER_MSG_SIMPLE(EC_MAX_DID, 0, POLARITY, NULL) },
+       };
+       struct imanager_device_attribute *attr;
+       int i, j, ret;
+
+       /* Read iManager device configurations */
+       for (i = 0; i < ARRAY_SIZE(msgs); i++) {
+               ret = imanager_read(ec, EC_CMD_DEV_TBL_RD, &msgs[i]);
+               if (ret)
+                       return ret;
+       }
+
+       /* Generate iManager device atributes */
+       for (i = 0; i < EC_MAX_DID && msgs[DEVID].u.data[i]; i++) {
+               attr = &ec->attr[i];
+               for (j = 0; j < ARRAY_SIZE(ecdev_table); j++) {
+                       if (ecdev_table[j].did == msgs[DEVID].u.data[i]) {
+                               attr->did = msgs[DEVID].u.data[i];
+                               attr->hwp = msgs[HWPIN].u.data[i];
+                               attr->pol = msgs[POLARITY].u.data[i];
+                               attr->ecdev = &ecdev_table[j];
+                               imanager_add_attribute(ec, attr);
+                               break;
+                       }
+               }
+       }
+
+       if (ec->gpio.num)
+               ec->features |= IMANAGER_FEATURE_GPIO;
+       if (ec->hwmon.adc.num)
+               ec->features |= IMANAGER_FEATURE_HWMON_ADC;
+       if (ec->hwmon.fan.num)
+               ec->features |= IMANAGER_FEATURE_HWMON_FAN;
+       if (ec->i2c.num)
+               ec->features |= IMANAGER_FEATURE_SMBUS;
+       if (ec->bl.num)
+               ec->features |= IMANAGER_FEATURE_BACKLIGHT;
+       if (ec->wdt.num)
+               ec->features |= IMANAGER_FEATURE_WDT;
+
+       return 0;
+}
+
+static const char *project_code_to_str(unsigned int code)
+{
+       switch ((char)code) {
+       case 'V':
+               return "release";
+       case 'X':
+               return "debug";
+       case 'A' ... 'U':
+       case 'Y':
+       case 'Z':
+               return "custom";
+       }
+
+       return "unspecified";
+}
+
+static int imanager_read_firmware_version(struct imanager_ec_data *ec)
+{
+       char pcb_name[IMANAGER_PCB_NAME_LEN] = { 0 };
+       struct imanager_info *info = &ec->info;
+       struct imanager_ec_message msg = {
+               .rlen = ARRAY_SIZE(pcb_name) - 1,
+               .wlen = 0,
+               .param = 0,
+               .data = pcb_name,
+       };
+       struct imanager_ec_version ver;
+       unsigned int val;
+       int ret;
+
+       ret = imanager_read_ram(ec, EC_RAM_ACPI, EC_OFFSET_FW_RELEASE,
+                               (u8 *)&ver, sizeof(ver));
+       if (ret < 0)
+               return ret;
+
+       val = cpu_to_be16(ver.kernel);
+       info->kernel_major = EC_KERNEL_MAJOR(val);
+       info->kernel_minor = EC_KERNEL_MINOR(val);
+
+       val = cpu_to_be16(ver.firmware);
+       info->firmware_major = EC_FIRMWARE_MAJOR(val);
+       info->firmware_minor = EC_FIRMWARE_MINOR(val);
+
+       val = cpu_to_be16(ver.project_code);
+       info->type = project_code_to_str(EC_PROJECT_CODE(val));
+
+       /*
+        * The PCB name string, in some FW releases, is not Null-terminated,
+        * so we need to read a fixed amount of chars. Also, the name length
+        * may vary by one char (SOM6867 vs. SOM-6867).
+        */
+       ret = imanager_read(ec, EC_CMD_FW_INFO_RD, &msg);
+       if (ret)
+               return ret;
+
+       if (!strchr(pcb_name, '-'))
+               pcb_name[IMANAGER_PCB_NAME_LEN - 2] = '\0';
+
+       return scnprintf(info->version, sizeof(info->version),
+                        "%s_k%d.%d_f%d.%d_%s", pcb_name, info->kernel_major,
+                        info->kernel_minor, info->firmware_major,
+                        info->firmware_minor, info->type);
+}
+
+static int imanager_ec_init(struct imanager_ec_data *ec)
+{
+       int ret;
+
+       /* Prevent firmware lock */
+       inb(IT8528_DAT_PORT);
+       inb(IT8518_DAT_PORT);
+
+       ret = imanager_read_firmware_version(ec);
+       if (ret < 0)
+               return ret;
+
+       return imanager_read_device_config(ec);
+}
+
+static inline void data_to_ec(struct imanager_io_ops *io, u8 *data, u8 len,
+                             int offset)
+{
+       int i;
+
+       for (i = 0; i < len; i++)
+               io->write(offset++, data[i]);
+}
+
+static inline void data_from_ec(struct imanager_io_ops *io, u8 *data, u8 len,
+                               int offset)
+{
+       int i;
+
+       for (i = 0; i < len; i++)
+               data[i] = io->read(offset++);
+}
+
+static int imanager_msg_xfer(struct imanager_ec_data *ec, u8 cmd,
+                            struct imanager_ec_message *msg, bool payload)
+{
+       int ret;
+       int offset = EC_MSG_OFFSET_DATA;
+
+       ret = imanager_check_ec_ready(&ec->io);
+       if (ret)
+               return ret;
+
+       ec->io.write(EC_MSG_OFFSET_PARAM, msg->param);
+
+       if (msg->wlen) {
+               if (msg->data) {
+                       data_to_ec(&ec->io, msg->data, msg->wlen, offset);
+                       ec->io.write(EC_MSG_OFFSET_LEN, msg->wlen);
+               } else {
+                       data_to_ec(&ec->io, msg->u.data, msg->wlen, offset);
+               }
+       }
+
+       /* Execute command */
+       ec->io.write(EC_MSG_OFFSET_CMD, cmd);
+       ret = imanager_check_ec_ready(&ec->io);
+       if (ret)
+               return ret;
+
+       /* GPIO and I2C have different success return values */
+       ret = ec->io.read(EC_MSG_OFFSET_STATUS);
+       if ((ret != EC_F_SUCCESS) && !(ret & EC_F_CMD_COMPLETE))
+               return -EFAULT;
+       /*
+        * EC I2C may return an error code which we need to handoff
+        * to the caller
+        */
+       else if (ret & 0x007e)
+               return ret;
+
+       if (msg->rlen) {
+               if (msg->rlen == EC_F_HWMON_MSG)
+                       msg->rlen = ec->io.read(EC_MSG_OFFSET_LEN);
+               if (payload) /* i2c, hwmon, wdt */
+                       offset = EC_MSG_OFFSET_PAYLOAD;
+               if (msg->data)
+                       data_from_ec(&ec->io, msg->data, msg->rlen, offset);
+               else
+                       data_from_ec(&ec->io, msg->u.data, msg->rlen, offset);
+       }
+
+       return 0;
+}
+
+/**
+ * imanager_read_ram - read 'size' amount of data @ 'offset' of 'ram_type'
+ * @ec:                imanager_ec_data structure describing the EC
+ * @ram_type:  RAM type such as ACPI, HW, or EXternal
+ * @offset:    offset within the RAM segment
+ * @data:      data pointer
+ * @len:       data length
+ */
+int imanager_read_ram(struct imanager_ec_data *ec, int ram_type, u8 offset,
+                     u8 *data, u8 len)
+{
+       int ret;
+
+       ret = imanager_check_ec_ready(&ec->io);
+       if (ret)
+               return ret;
+
+       ec->io.write(EC_MSG_OFFSET_PARAM, ram_type);
+       ec->io.write(EC_MSG_OFFSET_DATA, offset);
+       ec->io.write(EC_MSG_OFFSET_LEN, len);
+       ec->io.write(EC_MSG_OFFSET_CMD, EC_CMD_RAM_RD);
+
+       ret = imanager_check_ec_ready(&ec->io);
+       if (ret)
+               return ret;
+
+       ret = ec->io.read(EC_MSG_OFFSET_STATUS);
+       if (ret != EC_F_SUCCESS)
+               return -EIO;
+
+       data_from_ec(&ec->io, data, len, EC_MSG_OFFSET_RAM_DATA);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(imanager_read_ram);
+
+/**
+ * imanager_write_ram - write 'len' amount of data @ 'offset' of 'ram_type'
+ * @ec:                imanager_ec_data structure describing the EC
+ * @ram_type:  RAM type such as ACPI, HW, or EXternal
+ * @offset:    offset within the RAM segment
+ * @data:      data pointer
+ * @len:       data length
+ */
+int imanager_write_ram(struct imanager_ec_data *ec, int ram_type, u8 offset,
+                      u8 *data, u8 len)
+{
+       int ret;
+
+       ret = imanager_check_ec_ready(&ec->io);
+       if (ret)
+               return ret;
+
+       ec->io.write(EC_MSG_OFFSET_PARAM, ram_type);
+       ec->io.write(EC_MSG_OFFSET_DATA, offset);
+       ec->io.write(EC_MSG_OFFSET_LEN, len);
+
+       data_to_ec(&ec->io, data, len, EC_MSG_OFFSET_RAM_DATA);
+
+       ec->io.write(EC_MSG_OFFSET_CMD, EC_CMD_RAM_WR);
+
+       ret = imanager_check_ec_ready(&ec->io);
+       if (ret)
+               return ret;
+
+       ret = ec->io.read(EC_MSG_OFFSET_STATUS);
+       if (ret != EC_F_SUCCESS)
+               return -EIO;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(imanager_write_ram);
+
+/**
+ * imanager_read - read data through request/response messaging
+ * @ec:                imanager_ec_data structure describing the EC
+ * @cmd:       imanager EC firmware command
+ * @msg:       imanager_ec_message structure holding the message
+ */
+int imanager_read(struct imanager_ec_data *ec, u8 cmd,
+                 struct imanager_ec_message *msg)
+{
+       return imanager_msg_xfer(ec, cmd, msg, false);
+}
+EXPORT_SYMBOL_GPL(imanager_read);
+
+/**
+ * imanager_write - write data through request/response messaging
+ * @ec:                imanager_ec_data structure describing the EC
+ * @cmd:       imanager EC firmware command
+ * @msg:       imanager_ec_message structure holding the message
+ */
+int imanager_write(struct imanager_ec_data *ec, u8 cmd,
+                  struct imanager_ec_message *msg)
+{
+       return imanager_msg_xfer(ec, cmd, msg, true);
+}
+EXPORT_SYMBOL_GPL(imanager_write);
+
+/**
+ * imanager_read8 - read 8-bit data
+ * @ec:                imanager_ec_data structure describing the EC
+ * @cmd:       imanager EC firmware command
+ * @param:     parameter depening on cmd - device ID, offset or unit number
+ */
+int imanager_read8(struct imanager_ec_data *ec, u8 cmd, u8 param)
+{
+       int ret;
+       struct imanager_ec_message msg = {
+               .rlen = 1,
+               .wlen = 0,
+               .param = param,
+               .data = NULL,
+       };
+
+       ret = imanager_read(ec, cmd, &msg);
+       if (ret)
+               return ret;
+
+       return msg.u.data[0];
+}
+EXPORT_SYMBOL_GPL(imanager_read8);
+
+/**
+ * imanager_read16 - read 16-bit data
+ * @ec:                imanager_ec_data structure describing the EC
+ * @cmd:       imanager EC firmware command
+ * @param:     parameter depening on cmd - device ID, offset or unit number
+ */
+int imanager_read16(struct imanager_ec_data *ec, u8 cmd, u8 param)
+{
+       int ret;
+       struct imanager_ec_message msg = {
+               .rlen = 2,
+               .wlen = 0,
+               .param = param,
+               .data = NULL,
+       };
+
+       ret = imanager_read(ec, cmd, &msg);
+       if (ret)
+               return ret;
+
+       return (msg.u.data[0] << 8 | msg.u.data[1]);
+}
+EXPORT_SYMBOL_GPL(imanager_read16);
+
+/**
+ * imanager_write8 - write 8-bit data
+ * @ec:                imanager_ec_data structure describing the EC
+ * @cmd:       imanager EC firmware command
+ * @param:     parameter depening on cmd - device ID, offset or unit number
+ * @byte:      8-bit data
+ */
+int imanager_write8(struct imanager_ec_data *ec, u8 cmd, u8 param, u8 byte)
+{
+       struct imanager_ec_message msg = {
+               .rlen = 0,
+               .wlen = 1,
+               .param = param,
+               .u = {
+                       .data = { byte, 0 },
+               },
+       };
+
+       return imanager_write(ec, cmd, &msg);
+}
+EXPORT_SYMBOL_GPL(imanager_write8);
+
+/**
+ * imanager_write16 - write 16-bit data
+ * @ec:                imanager_ec_data structure describing the EC
+ * @cmd:       imanager EC firmware command
+ * @param:     parameter depening on cmd - device ID, offset or unit number
+ * @word:      16-bit data
+ */
+int imanager_write16(struct imanager_ec_data *ec, u8 cmd, u8 param, u16 word)
+{
+       struct imanager_ec_message msg = {
+               .rlen = 0,
+               .wlen = 2,
+               .param = param,
+               .u = {
+                       .data = { (word >> 8), (word & 0xff), 0 },
+               },
+       };
+
+       return imanager_write(ec, cmd, &msg);
+}
+EXPORT_SYMBOL_GPL(imanager_write16);
+
+enum imanager_cells {
+       IMANAGER_BACKLIGHT = 0,
+       IMANAGER_GPIO,
+       IMANAGER_HWMON,
+       IMANAGER_SMB,
+       IMANAGER_WDT,
+};
+
+/**
+ * iManager devices which are available via firmware.
+ */
+
+static const struct mfd_cell imanager_devs[] = {
+       [IMANAGER_BACKLIGHT] = {
+               .name = "imanager-backlight",
+       },
+       [IMANAGER_GPIO] = {
+               .name = "imanager-gpio",
+       },
+       [IMANAGER_HWMON] = {
+               .name = "imanager-hwmon",
+       },
+       [IMANAGER_SMB] = {
+               .name = "imanager-smbus",
+       },
+       [IMANAGER_WDT] = {
+               .name = "imanager-wdt",
+       },
+};
+
+static int imanager_register_cells(struct imanager_device_data *imgr)
+{
+       struct imanager_ec_data *ec = &imgr->ec;
+       struct mfd_cell devs[ARRAY_SIZE(imanager_devs)];
+       int i = 0;
+
+       if (ec->features & IMANAGER_FEATURE_BACKLIGHT)
+               devs[i++] = imanager_devs[IMANAGER_BACKLIGHT];
+
+       if (ec->features & IMANAGER_FEATURE_GPIO)
+               devs[i++] = imanager_devs[IMANAGER_GPIO];
+
+       if (ec->features & IMANAGER_FEATURE_HWMON_ADC)
+               devs[i++] = imanager_devs[IMANAGER_HWMON];
+
+       if (ec->features & IMANAGER_FEATURE_SMBUS)
+               devs[i++] = imanager_devs[IMANAGER_SMB];
+
+       if (ec->features & IMANAGER_FEATURE_WDT)
+               devs[i++] = imanager_devs[IMANAGER_WDT];
+
+       return mfd_add_devices(imgr->dev, -1, devs, i, NULL, 0, NULL);
+}
+
+static struct resource imanager_ioresource = {
+       .start  = IT8528_DAT_PORT,
+       .end    = IT8518_DAT_PORT,
+       .flags  = IORESOURCE_IO,
+};
+
+static ssize_t imanager_version_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct imanager_device_data *data = dev_get_drvdata(dev);
+       struct imanager_info *info = &data->ec.info;
+
+       return scnprintf(buf, PAGE_SIZE, "%s\n", info->version);
+}
+
+static ssize_t imanager_chip_show(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       struct imanager_device_data *data = dev_get_drvdata(dev);
+
+       return scnprintf(buf, PAGE_SIZE, "%s\n", data->ec.chip_name);
+}
+
+static DEVICE_ATTR(imanager_version, 0444, imanager_version_show, NULL);
+static DEVICE_ATTR(imanager_chip, 0444, imanager_chip_show, NULL);
+
+static struct attribute *imanager_attributes[] = {
+       &dev_attr_imanager_version.attr,
+       &dev_attr_imanager_chip.attr,
+       NULL
+};
+
+static const struct attribute_group imanager_attr_group = {
+       .attrs = imanager_attributes,
+};
+
+static int imanager_platform_create(void)
+{
+       int ret;
+
+       imanager_pdev = platform_device_alloc("imanager", -1);
+       if (!imanager_pdev)
+               return -ENOMEM;
+
+       /* No platform device data required */
+
+       ret = platform_device_add_resources(imanager_pdev,
+                                           &imanager_ioresource, 1);
+       if (ret)
+               goto err;
+
+       ret = platform_device_add(imanager_pdev);
+       if (ret)
+               goto err;
+
+       return 0;
+err:
+       platform_device_put(imanager_pdev);
+       return ret;
+}
+
+static inline int ec_read_chipid(u16 addr)
+{
+       return (ec_inb(addr, CHIP_DEVID_MSB) << 8 |
+               ec_inb(addr, CHIP_DEVID_LSB));
+}
+
+static int imanager_detect_device(struct imanager_device_data *imgr)
+{
+       struct imanager_ec_data *ec = &imgr->ec;
+       struct device *dev = imgr->dev;
+       struct imanager_info *info = &imgr->ec.info;
+       int chipid = ec_read_chipid(EC_BASE_ADDR);
+       int ret;
+
+       if (chipid == CHIP_ID_IT8518) {
+               ec->io.read     = ec_io18_read;
+               ec->io.write    = ec_io18_write;
+               ec->chip_name   = chip_names[IT8518];
+       } else if (chipid == CHIP_ID_IT8528) {
+               ec->io.read     = ec_io28_read;
+               ec->io.write    = ec_io28_write;
+               ec->chip_name   = chip_names[IT8528];
+       }
+
+       ret = imanager_ec_init(ec);
+       if (ret) {
+               dev_err(dev, "iManager firmware communication error\n");
+               return ret;
+       }
+
+       dev_info(dev, "Found Advantech iManager %s: %s (%s)\n",
+                ec->chip_name, info->version, info->type);
+
+       ret = sysfs_create_group(&dev->kobj, &imanager_attr_group);
+       if (ret)
+               return ret;
+
+       ret = imanager_register_cells(imgr);
+       if (ret)
+               sysfs_remove_group(&dev->kobj, &imanager_attr_group);
+
+       return ret;
+}
+
+static int imanager_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct imanager_device_data *imgr;
+
+       imgr = devm_kzalloc(dev, sizeof(*imgr), GFP_KERNEL);
+       if (!imgr)
+               return -ENOMEM;
+
+       imgr->dev = dev;
+       mutex_init(&imgr->lock);
+
+       platform_set_drvdata(pdev, imgr);
+
+       return imanager_detect_device(imgr);
+}
+
+static int imanager_remove(struct platform_device *pdev)
+{
+       sysfs_remove_group(&pdev->dev.kobj, &imanager_attr_group);
+       mfd_remove_devices(&pdev->dev);
+
+       return 0;
+}
+
+static struct platform_driver imanager_driver = {
+       .driver = {
+               .name  = "imanager",
+       },
+       .probe  = imanager_probe,
+       .remove = imanager_remove,
+};
+
+static int __init imanager_init(void)
+{
+       int chipid = ec_read_chipid(EC_BASE_ADDR);
+       int ret;
+
+       /* Check for the presence of the EC chip */
+       if ((chipid != CHIP_ID_IT8518) && (chipid != CHIP_ID_IT8528))
+               return -ENODEV;
+
+       ret = imanager_platform_create();
+       if (ret)
+               return ret;
+
+       return platform_driver_register(&imanager_driver);
+}
+
+static void __exit imanager_exit(void)
+{
+       if (imanager_pdev)
+               platform_device_unregister(imanager_pdev);
+
+       platform_driver_unregister(&imanager_driver);
+}
+
+module_init(imanager_init);
+module_exit(imanager_exit);
+
+MODULE_DESCRIPTION("Advantech iManager Core Driver");
+MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imanager-core");
diff --git a/include/linux/mfd/imanager-ec.h b/include/linux/mfd/imanager-ec.h
new file mode 100644
index 0000000..6319b7a
--- /dev/null
+++ b/include/linux/mfd/imanager-ec.h
@@ -0,0 +1,228 @@
+/*
+ * Advantech iManager - firmware interface
+ *
+ * Copyright (C) 2016 Advantech Co., Ltd.
+ * Author: Richard Vidal-Dorsch <richard.dor...@advantech.com>
+ *
+ * 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 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _LINUX_MFD_IMANAGER_EC_H_
+#define _LINUX_MFD_IMANAGER_EC_H_
+
+#include <linux/types.h>
+
+/* Delay time for port polling in micro seconds */
+#define EC_DELAY_MIN                   200UL
+#define EC_DELAY_MAX                   250UL
+
+#define EC_MAX_RETRY                   400UL
+
+#define CHIP_ID_IT8518                 0x8518
+#define CHIP_ID_IT8528                 0x8528
+
+#define EC_BASE_ADDR                   0x029C
+
+#define IT8528_CMD_PORT                        0x029A
+#define IT8528_DAT_PORT                        0x0299
+#define IT8518_CMD_PORT                        0x029E
+#define IT8518_DAT_PORT                        0x029F
+
+/* 16-bit device ID registers */
+#define CHIP_DEVID_MSB                 0x20
+#define CHIP_DEVID_LSB                 0x21
+
+#define EC_MAX_GPIO_NUM                        8UL
+#define EC_MAX_ADC_NUM                 5UL
+#define EC_MAX_FAN_NUM                 3UL
+#define EC_MAX_BLC_NUM                 2UL
+#define EC_MAX_SMB_NUM                 4UL
+#define EC_MAX_WDT_NUM                 2UL
+
+#define EC_PAYLOAD_SIZE                        40UL
+#define EC_MSG_SIZE                    sizeof(struct imanager_ec_smb_message)
+#define EC_MSG_HDR_SIZE                        sizeof(struct 
imanager_ec_smb_msg_hdr)
+
+#define EC_MAX_DID                     32UL
+
+/*
+ * iManager commands
+ */
+#define EC_CMD_CHK_RDY                 0UL
+#define EC_CMD_HWP_RD                  0x11UL
+#define EC_CMD_HWP_WR                  0x12UL
+#define EC_CMD_GPIO_DIR_RD             0x30UL
+#define EC_CMD_GPIO_DIR_WR             0x31UL
+#define EC_CMD_PWM_FREQ_RD             0x36UL
+#define EC_CMD_PWM_FREQ_WR             0x32UL
+#define EC_CMD_PWM_POL_RD              0x37UL
+#define EC_CMD_PWM_POL_WR              0x33UL
+#define EC_CMD_SMB_FREQ_RD             0x34UL
+#define EC_CMD_SMB_FREQ_WR             0x35UL
+#define EC_CMD_FAN_CTL_RD              0x40UL
+#define EC_CMD_FAN_CTL_WR              0x41UL
+#define EC_CMD_THZ_RD                  0x42UL
+#define EC_CMD_DEV_TBL_RD              0x20UL
+#define EC_CMD_FW_INFO_RD              0xF0UL
+#define EC_CMD_BUF_CLR                 0xC0UL
+#define EC_CMD_BUF_RD                  0xC1UL
+#define EC_CMD_BUF_WR                  0xC2UL
+#define EC_CMD_RAM_RD                  0x1EUL
+#define EC_CMD_RAM_WR                  0x1FUL
+#define EC_CMD_I2C_RW                  0x0EUL
+#define EC_CMD_I2C_WR                  0x0FUL
+#define EC_CMD_WDT_CTRL                        0x28UL
+
+/*
+ * ACPI RAM offsets
+ */
+#define EC_OFFSET_FAN_ALERT            0x6FUL
+#define EC_OFFSET_FAN_ALERT_LIMIT      0x76UL
+#define EC_OFFSET_BRIGHTNESS1          0x50UL
+#define EC_OFFSET_BRIGHTNESS2          0x52UL
+#define EC_OFFSET_BACKLIGHT_CTRL       0x99UL
+#define EC_OFFSET_FW_RELEASE           0xF8UL
+
+/* iManager flags */
+#define IMANAGER_FEATURE_BACKLIGHT     BIT(0)
+#define IMANAGER_FEATURE_GPIO          BIT(1)
+#define IMANAGER_FEATURE_HWMON_ADC     BIT(2)
+#define IMANAGER_FEATURE_HWMON_FAN     BIT(3)
+#define IMANAGER_FEATURE_SMBUS         BIT(4)
+#define IMANAGER_FEATURE_WDT           BIT(5)
+
+#define EC_IO28_OUTBUF                 BIT(0)
+#define EC_IO28_INBUF                  BIT(1)
+
+#define EC_F_SUCCESS                   BIT(0)
+#define EC_F_CMD_COMPLETE              BIT(7)
+#define EC_F_HWMON_MSG                 BIT(9)
+
+/* iManager offsets */
+#define EC_MSG_OFFSET(N)               (0UL + (N))
+#define EC_MSG_OFFSET_CMD              EC_MSG_OFFSET(0)
+#define EC_MSG_OFFSET_STATUS           EC_MSG_OFFSET(1)
+#define EC_MSG_OFFSET_PARAM            EC_MSG_OFFSET(2)
+#define EC_MSG_OFFSET_DATA             EC_MSG_OFFSET(3)
+#define EC_MSG_OFFSET_RAM_DATA         EC_MSG_OFFSET(4)
+#define EC_MSG_OFFSET_PAYLOAD          EC_MSG_OFFSET(7)
+#define EC_MSG_OFFSET_LEN              EC_MSG_OFFSET(0x2F)
+
+/* IT8528 based firmware require a read/write command offset. */
+#define EC_CMD_OFFSET_READ             0xA0UL
+#define EC_CMD_OFFSET_WRITE            0x50UL
+
+#define EC_KERNEL_MINOR(x)             ((x) & 0xff)
+#define EC_KERNEL_MAJOR(x)             ({ typeof(x) __x = (x >> 8); \
+                                       ((__x >> 4) * 10 + (__x & 0x0f)); })
+#define EC_FIRMWARE_MINOR(x)           EC_KERNEL_MINOR(x)
+#define EC_FIRMWARE_MAJOR(x)           EC_KERNEL_MAJOR(x)
+#define EC_PROJECT_CODE(x)             EC_KERNEL_MINOR(x)
+
+enum imanager_smb_cells { SMB_EEP = 0, I2C_OEM, SMB_1, SMB_PECI };
+
+enum imanager_device_type { ADC = 1, DAC, GPIO, IRQ, PWM, SMB };
+
+enum imanager_device_id {
+       /* GPIO */
+       GPIO0 = 0x10, GPIO1, GPIO2, GPIO3, GPIO4, GPIO5, GPIO6, GPIO7,
+       /* FAN */
+       CPUFAN_2P = 0x20, CPUFAN_4P, SYSFAN1_2P, SYSFAN1_4P, SYSFAN2_2P,
+       SYSFAN2_4P,
+       /* Brightness Control */
+       BRIGHTNESS = 0x26, BRIGHTNESS2 = 0x88,
+       /* SMBus */
+       SMBOEM0  = 0x28, SMBOEM1, SMBOEM2, SMBEEPROM,
+       SMBTHM0  = 0x2C, SMBTHM1, SMBSECEEP, I2COEM,
+       SMBEEP2K = 0x38, OEMEEP, OEMEEP2K, SMBPECI,
+       /* ADC */
+       CMOSBAT  = 0x50, CMOSBAT_2, CMOSBAT_10,
+       ADC5VS0  = 0x56, ADC5VS0_2, ADC5VS0_10,
+       ADC5VS5  = 0x59, ADC5VS5_2, ADC5VS5_10,
+       ADC33VS0 = 0x5C, ADC33VS0_2, ADC33VS0_10,
+       ADC33VS5 = 0x5F, ADC33VS5_2, ADC33VS5_10,
+       ADC12VS0 = 0x62, ADC12VS0_2, ADC12VS0_10,
+       VCOREA   = 0x65, VCOREA_2, VCOREA_10,
+       CURRENT  = 0x74,
+       /* Watchdog */
+       WDIRQ    = 0x78, WDNMI,
+};
+
+/**
+ * struct imanager_ec_device - Describes iManager EC Device
+ * @did:       iManager Device ID
+ * @type:      iManager Device Type
+ * @scale:     Scaling factor
+ */
+struct imanager_ec_device {
+       unsigned int did;
+       unsigned int type;
+       unsigned int scale;
+};
+
+/**
+ * IMANAGER_EC_DEVICE - macro used to describe a specific iManager device
+ * @device_id:         the 8 bit iManager device ID
+ * @device_type:       the iManager device type
+ * @scaling_factor:    the iManager sensor device scaling factor
+ *
+ * This macro is used to create a struct imanager_ec_device that matches a
+ * specific iManager device
+ */
+#define IMANAGER_EC_DEVICE(device_id, device_type, scaling_factor) \
+       .did = (device_id), .type = (device_type), .scale = (scaling_factor)
+
+/**
+ * struct imanager_io_ops - iManager I/O operation structure
+ * @read:      iManager read call-back
+ * @write:     iManager write call-back
+ */
+struct imanager_io_ops {
+       int (*read)(int cmd);
+       int (*write)(int cmd, int value);
+};
+
+/**
+ * struct imanager_ec_smb_msg_hdr - Defines iManager EC SMBus message header
+ * @addr_low:  low-byte of word address (or data)
+ * @addr_high: high-byte of word address (or data)
+ * @rlen:      SMB read length
+ * @wlen:      SMB write length
+ * @cmd:       SMB command
+ */
+struct imanager_ec_smb_msg_hdr {
+       unsigned char addr_low;
+       unsigned char addr_high;
+       unsigned char rlen;
+       unsigned char wlen;
+       unsigned char cmd;
+} __attribute__((__packed__));
+
+/**
+ * struct imanager_ec_smb_message - Defines iManager SMBus message
+ * @hdr:       iManager SMBus message header
+ * @data:      iManager SMBus message data field (payload)
+ */
+struct imanager_ec_smb_message {
+       struct imanager_ec_smb_msg_hdr hdr;
+       unsigned char data[EC_PAYLOAD_SIZE];
+} __attribute__((__packed__));
+
+/**
+ * struct imanager_ec_version - Defines iManager EC firmware version structure
+ * @kernel:            iManager EC FW kernel release
+ * @chipid:            iManager EC chip ID
+ * @project_code:      iManager EC FW status
+ * @firmware:          iManager EC FW release
+ */
+struct imanager_ec_version {
+       unsigned short kernel;
+       unsigned short chipid;
+       unsigned short project_code;
+       unsigned short firmware;
+} __attribute__((__packed__));
+
+#endif
diff --git a/include/linux/mfd/imanager.h b/include/linux/mfd/imanager.h
new file mode 100644
index 0000000..32d7af6
--- /dev/null
+++ b/include/linux/mfd/imanager.h
@@ -0,0 +1,221 @@
+/*
+ * Advantech iManager MFD
+ *
+ * Copyright (C) 2016 Advantech Co., Ltd.
+ * Author: Richard Vidal-Dorsch <richard.dor...@advantech.com>
+ *
+ * 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 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _LINUX_MFD_IMANAGER_H_
+#define _LINUX_MFD_IMANAGER_H_
+
+#include <linux/mutex.h>
+#include <linux/mfd/imanager-ec.h>
+
+#define IMANAGER_PCB_NAME_LEN  9
+#define IMANAGER_VERSION_LEN   40
+
+/**
+ * IMANAGER_MSG_SIMPLE - macro used to describe a simple iManager message
+ * @read_len:  the message read length
+ * @write_len: the message write length
+ * @parameter: the message parameter
+ * @_data:     pointer to data field
+ *
+ * This macro is used to create a struct imanager_ec_message used for basic
+ * EC communication
+ */
+#define IMANAGER_MSG_SIMPLE(read_len, write_len, parameter, _data) \
+       .rlen = (read_len), .wlen = (write_len), \
+       .param = (parameter), .data = (_data)
+
+/**
+ * struct imanager_ec_message - Describes iManager EC message
+ * @rlen:      iManager message read length
+ * @wlen:      iManager message write length
+ * @param:     iManager message parameter (offset, id, or unit number)
+ * @u:         union holding struct imanager_ec_smb_message and data field
+ * @data:      pointer to data field
+ */
+struct imanager_ec_message {
+       unsigned int rlen;
+       unsigned int wlen;
+       unsigned int param;
+       union {
+               struct imanager_ec_smb_message smb;
+               unsigned char data[EC_MSG_SIZE];
+       } u;
+
+       unsigned char *data;
+};
+
+/**
+ * struct imanager_device_attribute - Describes iManager Device attribute
+ * @did:       iManager Device ID
+ * @hwp:       iManager Hardware Pin number
+ * @pol:       iManager Device Polarity
+ * @ecdev:     pointer to iManager device table entry
+ */
+struct imanager_device_attribute {
+       unsigned int did;
+       unsigned int hwp;
+       unsigned int pol;
+       const struct imanager_ec_device *ecdev;
+};
+
+/**
+ * struct imanager_gpio_device - Describes iManager GPIO device
+ * @num:       available GPIO pins
+ * @attr:      pointer to array of iManager GPIO device attribute
+ */
+struct imanager_gpio_device {
+       unsigned int num;
+       struct imanager_device_attribute *attr[EC_MAX_GPIO_NUM];
+};
+
+/**
+ * struct imanager_adc_device - Describes iManager ADC device
+ * @num:       available ADC devices
+ * @attr:      pointer to array of iManager ADC device attribute
+ * @label      pointer to ADC label
+ */
+struct imanager_adc_device {
+       unsigned int num;
+       struct imanager_device_attribute *attr[EC_MAX_ADC_NUM];
+       const char *label[EC_MAX_ADC_NUM];
+};
+
+/**
+ * struct imanager_fan_device - Describes iManager FAN device
+ * @num:       available FAN devices
+ * @attr:      pointer to array of iManager FAN device attribute
+ * @label      pointer to FAN label
+ * @temp_label pointer to FAN temperature label
+ */
+struct imanager_fan_device {
+       unsigned int num;
+       struct imanager_device_attribute *attr[EC_MAX_FAN_NUM];
+       const char *label[EC_MAX_FAN_NUM];
+       const char *temp_label[EC_MAX_FAN_NUM];
+};
+
+/**
+ * struct imanager_hwmon_device - Describes iManager hwmon device
+ * @adc:       iManager ADC device
+ * @fan:       iManager FAN device
+ */
+struct imanager_hwmon_device {
+       struct imanager_adc_device adc;
+       struct imanager_fan_device fan;
+};
+
+/**
+ * struct imanager_i2c_device - Describes iManager I2C device
+ * @num:       available I2C devices
+ * @attr:      pointer to array of iManager GPIO device attribute
+ */
+struct imanager_i2c_device {
+       unsigned int num;
+       struct imanager_device_attribute *attr[EC_MAX_SMB_NUM];
+};
+
+/**
+ * struct imanager_backlight_device - Describes iManager backlight device
+ * @num:       available backlight devices
+ * @attr:      pointer to array of iManager backlight device attribute
+ * @brightnes: array of brightness devices
+ */
+struct imanager_backlight_device {
+       unsigned int num;
+       struct imanager_device_attribute *attr[EC_MAX_BLC_NUM];
+       unsigned char brightness[EC_MAX_BLC_NUM];
+};
+
+/**
+ * struct imanager_watchdog_device - Describes iManager watchdog device
+ * @num:       available WD devices
+ * @attr:      pointer to array of iManager watchdog device attribute
+ */
+struct imanager_watchdog_device {
+       unsigned int num;
+       struct imanager_device_attribute *attr[EC_MAX_BLC_NUM];
+};
+
+/**
+ * struct imanager_info - iManager device information structure
+ * @kernel_major:      iManager EC kernel major revision
+ * @kernel_minor:      iManager EC kernel minor revision
+ * @firmware_major:    iManager EC firmware major revision
+ * @firmware_minor:    iManager EC firmware minor revision
+ * @type:              iManager type - release/debug/custom
+ * @pcb_name:          PC board name
+ * @version:           iManager version string
+ */
+struct imanager_info {
+       unsigned int kernel_major;
+       unsigned int kernel_minor;
+       unsigned int firmware_major;
+       unsigned int firmware_minor;
+       const char *type;
+       char version[IMANAGER_VERSION_LEN];
+};
+
+/**
+ * struct imanager_ec_data - iManager EC data structure
+ * @features:  iManager feature mask
+ * @attr:      array of iManager device attribute structure
+ * @io:                imanager_io_ops structure providing I/O operations
+ * @gpio:      iManager GPIO device structure
+ * @hwmon:     iManager Hardware monitor device structure
+ * @i2c:       iManager I2C/SMBus device structure
+ * @bl:                iManager Backlight/Brightness device structure
+ * @wdt:       iManager Watchdog device structure
+ */
+struct imanager_ec_data {
+       unsigned int features;
+       const char *chip_name;
+       struct imanager_device_attribute        attr[EC_MAX_DID];
+       struct imanager_io_ops                  io;
+       struct imanager_gpio_device             gpio;
+       struct imanager_hwmon_device            hwmon;
+       struct imanager_i2c_device              i2c;
+       struct imanager_backlight_device        bl;
+       struct imanager_watchdog_device         wdt;
+       struct imanager_info                    info;
+};
+
+/**
+ * struct imanager_device_data - Internal representation of the iManager device
+ * @ec:                iManager data structure describing the EC
+ * @dev:       Pointer to kernel device structure
+ * @lock:      iManager mutex
+ */
+struct imanager_device_data {
+       struct imanager_ec_data ec;
+       struct device   *dev;
+       struct mutex    lock; /* generic mutex for imanager core */
+};
+
+enum ec_ram_type { EC_RAM_ACPI = 1, EC_RAM_HW, EC_RAM_EXT };
+
+int imanager_read(struct imanager_ec_data *ec, u8 cmd,
+                 struct imanager_ec_message *msg);
+int imanager_write(struct imanager_ec_data *ec, u8 cmd,
+                  struct imanager_ec_message *msg);
+
+int imanager_read8(struct imanager_ec_data *ec, u8 cmd, u8 param);
+int imanager_write8(struct imanager_ec_data *ec, u8 cmd, u8 param, u8 byte);
+
+int imanager_read16(struct imanager_ec_data *ec, u8 cmd, u8 param);
+int imanager_write16(struct imanager_ec_data *ec, u8 cmd, u8 param, u16 word);
+
+int imanager_read_ram(struct imanager_ec_data *ec, int ram_type, u8 offset,
+                     u8 *buf, u8 len);
+int imanager_write_ram(struct imanager_ec_data *ec, int ram_type, u8 offset,
+                      u8 *data, u8 size);
+
+#endif
-- 
2.10.1

Reply via email to