lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20161102083751.6335-2-richard.dorsch@gmail.com>
Date:   Wed,  2 Nov 2016 01:37:46 -0700
From:   Richard Vidal-Dorsch <richard.dorsch@...il.com>
To:     linus.walleij@...aro.org, gnurou@...il.com, jdelvare@...e.com,
        linux@...ck-us.net, wsa@...-dreams.de, lee.jones@...aro.org,
        jingoohan1@...il.com, tomi.valkeinen@...com, wim@...ana.be,
        linux-kernel@...r.kernel.org, linux-gpio@...r.kernel.org,
        linux-hwmon@...r.kernel.org, linux-i2c@...r.kernel.org,
        linux-fbdev@...r.kernel.org, linux-watchdog@...r.kernel.org,
        k.kozlowski@...sung.com
Cc:     Richard Vidal-Dorsch <richard.dorsch@...il.com>,
        jo.sunga@...antech.com, weilun.huang@...antech.com,
        andrew.chou@...antech.com
Subject: [PATCH v4 1/6] Add Advantech iManager MFD core driver

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.dorsch@...il.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.dorsch@...antech.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.dorsch@...antech.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.dorsch@...antech.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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ