@@ -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
@@ -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
new file mode 100644
@@ -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@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");
new file mode 100644
@@ -0,0 +1,228 @@
+/*
+ * Advantech iManager - firmware interface
+ *
+ * Copyright (C) 2016 Advantech Co., Ltd.
+ * Author: Richard Vidal-Dorsch <richard.dorsch@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
new file mode 100644
@@ -0,0 +1,221 @@
+/*
+ * Advantech iManager MFD
+ *
+ * Copyright (C) 2016 Advantech Co., Ltd.
+ * Author: Richard Vidal-Dorsch <richard.dorsch@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
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@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