* [PATCH 1/3] mfd: iManager2: Add support for IT8516/18/28
@ 2014-05-29 5:57 Wei-Chun Pan
2014-05-29 5:57 ` [PATCH 2/3] hwmon: (iManager2) " Wei-Chun Pan
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Wei-Chun Pan @ 2014-05-29 5:57 UTC (permalink / raw)
To: Samuel Ortiz, Lee Jones, Jean Delvare, Guenter Roeck,
Wolfram Sang
Cc: Louis.Lu, Neo.Lo, Hank.Peng, Kevin.Ong, linux-kernel,
Wei-Chun Pan
Advantech's new module comes equipped with "iManager" - an embedded controller (EC), providing embedded features for system integrators to increase reliability and simplify integration.
This patch add the MFD driver for enabling Advantech iManager V2.0 chipset. Available functions support to recognize ITE-IT85XX and basic control APIs. These functions are tested on Advantech SOM-5892 board. All the embedded functions are configured by a utility. Advantech has done all the hard work for user with the release of a suite of Software APIs.
These provide not only the underlying drivers required but also a rich set of user-friendly, intelligent and integrated interfaces, which speeds development, enhances security and offers add-on value for Advantech platforms.
Signed-off-by: Wei-Chun Pan Developer <weichun.pan@advantech.com.tw>
---
drivers/mfd/Kconfig | 6 +
drivers/mfd/Makefile | 2 +
drivers/mfd/imanager2_core.c | 316 ++++++++
drivers/mfd/imanager2_ec.c | 1093 ++++++++++++++++++++++++++++
include/linux/mfd/advantech/imanager2.h | 61 ++
include/linux/mfd/advantech/imanager2_ec.h | 389 ++++++++++
6 files changed, 1867 insertions(+)
mode change 100644 => 100755 drivers/mfd/Kconfig
mode change 100644 => 100755 drivers/mfd/Makefile
create mode 100755 drivers/mfd/imanager2_core.c
create mode 100755 drivers/mfd/imanager2_ec.c
create mode 100755 include/linux/mfd/advantech/imanager2.h
create mode 100755 include/linux/mfd/advantech/imanager2_ec.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
old mode 100644
new mode 100755
index 3383412..48b063f
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -10,6 +10,12 @@ config MFD_CORE
select IRQ_DOMAIN
default n
+config MFD_IMANAGER2
+ tristate "Support for Advantech iManager2 EC ICs"
+ select MFD_CORE
+ help
+ Support for Advantech iManager2 EC ICs
+
config MFD_CS5535
tristate "AMD CS5535 and CS5536 southbridge core functions"
select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
old mode 100644
new mode 100755
index 2851275..10c64ae
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -166,3 +166,5 @@ obj-$(CONFIG_MFD_RETU) += retu-mfd.o
obj-$(CONFIG_MFD_AS3711) += as3711.o
obj-$(CONFIG_MFD_AS3722) += as3722.o
obj-$(CONFIG_MFD_STW481X) += stw481x.o
+imanager2-objs := imanager2_core.o imanager2_ec.o
+obj-$(CONFIG_MFD_IMANAGER2) += imanager2.o
diff --git a/drivers/mfd/imanager2_core.c b/drivers/mfd/imanager2_core.c
new file mode 100755
index 0000000..786853d
--- /dev/null
+++ b/drivers/mfd/imanager2_core.c
@@ -0,0 +1,316 @@
+/* imanager2_core.c - MFD core driver of Advantech EC IT8516/18/28
+ * Copyright (C) 2014 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/advantech/imanager2.h>
+
+#define DRV_NAME CHIP_NAME
+#define DRV_VERSION "0.2.2"
+
+static struct platform_device *pdev;
+
+static struct mfd_cell it85xx_devs[] = {
+ { .name = DRV_NAME "_hwm", },
+ { .name = DRV_NAME "_i2c", },
+};
+
+static int ec_authentication(struct it85xx *ec)
+{
+ u8 tmp;
+ int ret = 0;
+
+ spin_lock(&ec->lock);
+
+ if (inb(EC_IO_PORT_CMD) == 0xFF && inb(EC_IO_PORT_DATA) == 0xFF) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if ((inb(EC_IO_PORT_CMD) & OBF_MASK) != 0)
+ inb(EC_IO_PORT_DATA); /* initial OBF */
+
+ if (outb_after_ibc(EC_IO_PORT_CMD, EC_CMD_AUTHENTICATION) != 0) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (inb_after_obf(&tmp) != 0)
+ ret = -EFAULT;
+
+out:
+ spin_unlock(&ec->lock);
+
+ if (ret != 0)
+ return ret;
+
+ if (tmp != 0x95)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int ec_get_chip_type(struct it85xx *ec)
+{
+ spin_lock(&ec->lock);
+
+ outb(0x20, EC_SIO_CMD);
+ ec->type = (inb(EC_SIO_DATA) << 8);
+ outb(0x21, EC_SIO_CMD);
+ ec->type |= inb(EC_SIO_DATA);
+
+ pr_info("chip type = %04X\n", ec->type);
+
+ spin_unlock(&ec->lock);
+
+ switch (ec->type) {
+ case it8516:
+ case it8518:
+ break;
+ case it8519:
+ return -EOPNOTSUPP;
+ case it8528:
+ ec->flag |= EC_F_IO_MAILBOX;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int ec_get_info(struct it85xx *ec)
+{
+ int ret;
+ /* first kernel version that supports ITE mailbox */
+ const u16 supmbox_1st_kver = 0x1105;
+ u8 *tmp = (u8 *)&ec->info.version.kernel_ver;
+
+ spin_lock(&ec->lock);
+
+ ret = ec_io_read(EC_CMD_ACPIRAM_READ,
+ EC_ACPIRAM_ADDR_KERNEL_MAJOR_VERSION, &tmp[0], 2);
+
+ if (ec->info.version.kernel_ver >= supmbox_1st_kver) {
+ ec->flag |= EC_F_MAILBOX;
+ ret = ec_get_firmware_version_and_project_name(
+ ec, ec->info.prj_name,
+ &ec->info.version.kernel_ver,
+ &ec->info.version.chip_code,
+ &ec->info.version.proj_id,
+ &ec->info.version.proj_ver);
+ } else {
+ ec->flag &= ~EC_F_MAILBOX;
+ ret = ec_io_read(EC_CMD_ACPIRAM_READ,
+ EC_ACPIRAM_ADDR_KERNEL_MAJOR_VERSION,
+ &tmp[0], sizeof(struct ec_version));
+ }
+
+ spin_unlock(&ec->lock);
+
+ if (ret != 0)
+ return -EFAULT;
+
+ if ((ec->flag & EC_F_MAILBOX) != 0)
+ pr_info("name = %s\n", ec->info.prj_name);
+
+ pr_info("kernel_ver = %04X\n", ec->info.version.kernel_ver);
+ pr_info("chip_code = %04X\n", ec->info.version.chip_code);
+ pr_info("proj_id = %04X\n", ec->info.version.proj_id);
+ pr_info("proj_ver = %04X\n", ec->info.version.proj_ver);
+
+ return 0;
+}
+
+static int ec_device_initial_by_mailbox(struct it85xx *ec)
+{
+ int i, ret;
+
+ spin_lock(&ec->lock);
+ ret = ec_get_dynamic_table(ec, ec->table.devid,
+ ec->table.pinnum, NULL);
+ spin_unlock(&ec->lock);
+
+ if (ret != 0)
+ return ret;
+
+ for (i = 0; i < EC_MAX_ITEM_NUM; i++) {
+ if (ec->table.devid[i] == EC_TABLE_DID_NODEV)
+ break;
+
+ ec->table.devid2itemnum[ec->table.devid[i]] = i;
+ }
+
+ return ret;
+}
+
+static int ec_device_initial_by_io(struct it85xx *ec)
+{
+ int i, ret;
+ u8 tmp;
+
+ spin_lock(&ec->lock);
+
+ for (i = 0; i < EC_MAX_ITEM_NUM; i++) {
+ ret = ec_io_read(EC_CMD_HWCTRLTABLE_INDEX, i, &tmp, 1);
+ if (ret != 0)
+ break;
+ if (tmp == EC_TABLE_NOITEM)
+ break;
+
+ ec_io_read_byte_without_offset(EC_CMD_HWCTRLTABLE_GET_PIN_NUM,
+ &ec->table.pinnum[i]);
+ ec_io_read_byte_without_offset(EC_CMD_HWCTRLTABLE_GET_DEVICE_ID,
+ &ec->table.devid[i]);
+
+ if (ec->table.devid[i] == EC_TABLE_DID_NODEV)
+ continue;
+
+ ec->table.devid2itemnum[ec->table.devid[i]] = i;
+ }
+
+ spin_unlock(&ec->lock);
+
+ if (i < EC_MAX_ITEM_NUM) {
+ memset(&ec->table.devid[i], EC_TABLE_DID_NODEV,
+ EC_MAX_ITEM_NUM - i);
+ memset(&ec->table.pinnum[i], EC_TABLE_HWP_NODEV,
+ EC_MAX_ITEM_NUM - i);
+ }
+
+ return ret;
+}
+
+static int ec_build_device_table(struct it85xx *ec)
+{
+ memset(&ec->table.devid2itemnum[0], EC_TABLE_ITEM_UNUSED,
+ ARRAY_SIZE(ec->table.devid2itemnum));
+
+ if (ec->flag & EC_F_MAILBOX)
+ return ec_device_initial_by_mailbox(ec);
+ else
+ return ec_device_initial_by_io(ec);
+}
+
+static int __init it85xx_probe(struct it85xx *ec)
+{
+ int ret;
+
+ ret = ec_authentication(ec);
+ if (ret != 0)
+ return ret;
+
+ ret = ec_get_chip_type(ec);
+ if (ret != 0)
+ return ret;
+
+ ret = ec_get_info(ec);
+ if (ret != 0)
+ return ret;
+
+ ret = ec_build_device_table(ec);
+ if (ret != 0)
+ return ret;
+
+ if (request_region(EC_IO_PORT_DATA, 2, DRV_NAME) == NULL) {
+ release_region(EC_IO_PORT_DATA, 2);
+ return -EIO;
+ }
+
+ if (request_region(EC_ITE_PORT_OFS, 2, DRV_NAME) == NULL) {
+ release_region(EC_ITE_PORT_OFS, 2);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int __init it85xx_device_add(const struct it85xx *ec)
+{
+ int ret;
+
+ pdev = platform_device_alloc(DRV_NAME, 0);
+ if (pdev == NULL) {
+ ret = -ENOMEM;
+ pr_err("Device allocation failed\n");
+ goto exit;
+ }
+
+ ret = platform_device_add_data(pdev, ec,
+ sizeof(struct it85xx));
+ if (ret != 0) {
+ pr_err("Platform data allocation failed\n");
+ goto exit_device_put;
+ }
+
+ ret = platform_device_add(pdev);
+ if (ret != 0) {
+ pr_err("Device addition failed (%d)\n", ret);
+ goto exit_device_put;
+ }
+
+ ret = mfd_add_devices(&pdev->dev, pdev->id, it85xx_devs,
+ ARRAY_SIZE(it85xx_devs), NULL, -1, NULL);
+
+ if (ret != 0) {
+ pr_err("Cannot add sub device (error=%d)\n", ret);
+ goto exit_device_unregister;
+ } else {
+ pr_info("MFD core driver v%s loaded\n", DRV_VERSION);
+ }
+
+ return 0;
+
+exit_device_unregister:
+ platform_device_unregister(pdev);
+exit_device_put:
+ platform_device_put(pdev);
+exit:
+ return ret;
+}
+
+
+static int __init it85xx_init(void)
+{
+ struct it85xx ec;
+
+ memset(&ec, 0, sizeof(struct it85xx));
+ spin_lock_init(&ec.lock);
+ if (it85xx_probe(&ec) != 0)
+ return -ENODEV;
+
+ return it85xx_device_add(&ec);
+}
+
+static void __exit it85xx_exit(void)
+{
+ release_region(EC_ITE_PORT_OFS, 2);
+ release_region(EC_IO_PORT_DATA, 2);
+ mfd_remove_devices(&pdev->dev);
+ platform_device_unregister(pdev);
+ pr_info("MFD core driver removed\n");
+}
+
+MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>");
+MODULE_DESCRIPTION("ITE it85xx platform device definitions v" DRV_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+module_init(it85xx_init);
+module_exit(it85xx_exit);
diff --git a/drivers/mfd/imanager2_ec.c b/drivers/mfd/imanager2_ec.c
new file mode 100755
index 0000000..475c40c
--- /dev/null
+++ b/drivers/mfd/imanager2_ec.c
@@ -0,0 +1,1093 @@
+/* imanager2_ec.c - MFD accessing driver of Advantech EC IT8516/18/28
+ * Copyright (C) 2014 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/mfd/advantech/imanager2.h>
+
+#define EC_UDELAY_TIME 100
+#define EC_MAX_TIMEOUT_COUNT 10000
+
+/*===========================================================
+ * Name : wait_obf
+ * Purpose: wait output buffer full flag set
+ * Input : none
+ * Output : 0: success; else: fail
+ *===========================================================*/
+static int wait_obf(void)
+{
+ int i;
+ for (i = 0; i < EC_MAX_TIMEOUT_COUNT; i++) {
+ if ((inb(EC_IO_PORT_CMD) & OBF_MASK) != 0)
+ return 0;
+
+ udelay(EC_UDELAY_TIME);
+ }
+
+ return -ETIMEDOUT;
+}
+
+int inb_after_obf(u8 *data)
+{
+ int ret = wait_obf();
+ if (ret != 0)
+ return ret;
+ *data = inb(EC_IO_PORT_DATA);
+ return 0;
+}
+EXPORT_SYMBOL(inb_after_obf);
+
+/*===========================================================
+ * Name : wait_ibc
+ * Purpose: wait input buffer full flag clear
+ * Input : none
+ * Output : 0: success; else: fail
+ *===========================================================*/
+static int wait_ibc(void)
+{
+ int i;
+ for (i = 0; i < EC_MAX_TIMEOUT_COUNT; i++) {
+ if ((inb(EC_IO_PORT_CMD) & IBF_MASK) == 0)
+ return 0;
+
+ udelay(EC_UDELAY_TIME);
+ }
+
+ return -ETIMEDOUT;
+}
+
+int outb_after_ibc(u16 port, u8 data)
+{
+ int ret = wait_ibc();
+ if (ret != 0)
+ return ret;
+ outb(data, port);
+ return 0;
+}
+EXPORT_SYMBOL(outb_after_ibc);
+
+/*===========================================================
+ * Name : ec_read_mailbox
+ * Purpose: read data from real mailbox through 29E/29F
+ * Input : offset: mailbox cammand byte;
+ * data: return data byte
+ * Output : 0: success; else: fail
+ *===========================================================*/
+static int ec_read_mailbox(struct it85xx *ec, u8 offset, u8 *data)
+{
+ if ((ec->flag & EC_F_IO_MAILBOX) != 0) {
+ int ret = wait_ibc();
+ if (ret != 0)
+ return ret;
+ inb(EC_IO_PORT_DATA);
+ outb(offset + EC_IO_CMD_READ_OFFSET, EC_IO_PORT_CMD);
+
+ return inb_after_obf(data);
+ } else {
+ outb(offset, EC_ITE_PORT_OFS);
+ *data = inb(EC_ITE_PORT_DATA);
+ }
+
+ return 0;
+}
+
+/*===========================================================
+ * Name : ec_write_mailbox
+ * Purpose: write data to real mailbox through 29E/29F
+ * Input : offset: mailbox command byte
+ * data: data byte
+ * Output : 0: success; else: fail
+ *===========================================================*/
+static int ec_write_mailbox(struct it85xx *ec, u8 offset, u8 data)
+{
+ if (ec->flag & EC_F_IO_MAILBOX) {
+ int ret = outb_after_ibc(EC_IO_PORT_CMD,
+ offset + EC_IO_CMD_WRITE_OFFSET);
+ if (ret != 0)
+ return ret;
+
+ ret = outb_after_ibc(EC_IO_PORT_DATA, data);
+ if (ret != 0)
+ return ret;
+ } else {
+ outb(offset, EC_ITE_PORT_OFS);
+ outb(data, EC_ITE_PORT_DATA);
+ }
+
+ return 0;
+}
+
+/*===========================================================
+ * Name : ec_wait_cmd_clear
+ * Purpose: wait for mailbox "cmd" clear to zero
+ * Input : none
+ * Output : 0: success; else: fail
+ *===========================================================*/
+static int ec_wait_cmd_clear(struct it85xx *ec)
+{
+ u8 cmd;
+ int i, ret;
+
+ for (i = 0; i < EC_MAX_TIMEOUT_COUNT; i++) {
+ ret = ec_read_mailbox(ec, EC_MAILBOX_OFFSET_CMD, &cmd);
+ if (ret != 0)
+ return ret;
+ if (cmd == 0x00)
+ return 0;
+
+ udelay(EC_UDELAY_TIME);
+ }
+
+ return -ETIMEDOUT;
+}
+
+/*===========================================================
+ * Name : ec_clear_mailbox
+ * Purpose: clear all data in mailbox
+ * Input : none
+ * Output : 0: success; else: fail
+ *===========================================================*/
+int ec_clear_mailbox(struct it85xx *ec)
+{
+ int ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD, EC_CMD_MAILBOX_CLEAR_ALL);
+
+ return 0;
+}
+EXPORT_SYMBOL(ec_clear_mailbox);
+
+/*===========================================================
+ * Name : mailbox_read_buffer
+ * Purpose: read [1..n] bytes through ITE Mailbox method
+ * Input : cmd: EC Command
+ * param: EC command parameter/index
+ * p_data: use EC_MB_DECL_DATA_PROTO() macro
+ * Output : 0: success; else: fail
+ *===========================================================*/
+int ec_mailbox_read_buffer(struct it85xx *ec, u8 cmd, u8 para,
+ u8 *data, int len)
+{
+ int ret, i;
+ u8 status;
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA, para);
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD, cmd);
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ret = ec_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS, &status);
+ if (ret != 0)
+ return ret;
+ if (status != EC_MAILBOX_STATUS_SUCCESS)
+ return -EFAULT;
+
+ for (i = 0; i < len; i++)
+ ec_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(i), &data[i]);
+
+ return 0;
+}
+EXPORT_SYMBOL(ec_mailbox_read_buffer);
+
+/*===========================================================
+ * Name : mailbox_write_buffer
+ * Purpose: read [1..n] bytes through ITE Mailbox method
+ * Input : cmd: EC Command
+ * param: EC command parameter/index
+ * p_data: use EC_MB_DECL_DATA_PROTO() macro
+ * Output : 0: success; else: fail
+ *===========================================================*/
+int ec_mailbox_write_buffer(struct it85xx *ec, u8 cmd, u8 para,
+ u8 *data, int len)
+{
+ int ret, i;
+ u8 status;
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA, para);
+ for (i = 0; i < len; i++)
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_DAT(i), data[i]);
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD, cmd);
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ret = ec_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS, &status);
+ if (ret != 0)
+ return ret;
+ if (status != EC_MAILBOX_STATUS_SUCCESS)
+ return -EFAULT;
+
+ return 0;
+}
+EXPORT_SYMBOL(ec_mailbox_write_buffer);
+
+/*===========================================================
+ * Name : ec_clear_buffer_ram
+ * Purpose: clear buffer ram
+ * Input : none
+ * Output : 0: success; else: fail
+ *===========================================================*/
+int ec_clear_buffer_ram(struct it85xx *ec)
+{
+ int ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD,
+ EC_CMD_MALLBOX_CLEAR_256_BYTES_BUFFER);
+
+ return 0;
+}
+EXPORT_SYMBOL(ec_clear_buffer_ram);
+
+/*===========================================================
+ * Name : ec_read_ram
+ * Purpose: read ec ram
+ * Input : bank: memory bank; offset: memory offset
+ * len: data length
+ * buf: buffer pointer, return data
+ * Output : 0: success; else: fail
+ *===========================================================*/
+int ec_read_ram(struct it85xx *ec, u8 bank, u8 offset, u8 *buf, u8 len)
+{
+ int i, ret;
+ u8 status;
+
+ if (len != 0 && buf == NULL)
+ return -EINVAL;
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA, bank);
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x00), offset);
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x2C), len);
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD, EC_CMD_MAILBOX_READ_EC_RAM);
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ret = ec_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS, &status);
+ if (ret != 0)
+ return ret;
+ if (status != EC_MAILBOX_STATUS_SUCCESS)
+ return -EFAULT;
+
+ for (i = 0; i < len; i++)
+ /* range: DATA01~DATA2B */
+ ec_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(1 + i), &buf[i]);
+
+ return 0;
+}
+EXPORT_SYMBOL(ec_read_ram);
+
+/*===========================================================
+ * Name : ec_write_ram
+ * Purpose: write ec ram
+ * Input : bank: memory bank; offset: memory offset
+ * len: data length
+ * buf: buffer pointer, return data
+ * Output : 0: success; else: fail
+ *===========================================================*/
+int ec_write_ram(struct it85xx *ec, u8 bank, u8 offset, u8 *buf, u8 len)
+{
+ int i, ret;
+ u8 status;
+
+ if (len != 0 && buf == NULL)
+ return -EINVAL;
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA, bank);
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x00), offset);
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x2C), len);
+
+ for (i = 0; i < len; i++)
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_DAT(1 + i), buf[i]);
+
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD,
+ EC_CMD_MAILBOX_WRITE_EC_RAM);
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ret = ec_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS, &status);
+ if (ret != 0)
+ return ret;
+ if (status != EC_MAILBOX_STATUS_SUCCESS)
+ return -EFAULT;
+
+ return 0;
+}
+EXPORT_SYMBOL(ec_write_ram);
+
+/*===========================================================
+ * Name : ec_read_buffer_ram
+ * Purpose: read data from buffer ram
+ * Input : data: read data pointer, 256 bytes array
+ * len: data length
+ * Output : 0: success; else: fail
+ *===========================================================*/
+int ec_read_buffer_ram(struct it85xx *ec, u8 *data, int len)
+{
+ int i, j, ret;
+ int banknum, addition;
+ u8 status;
+
+ if (len > EC_RAM_BUFFER_SIZE)
+ len = EC_RAM_BUFFER_SIZE;
+ else if (len == 0)
+ return -EINVAL;
+
+ banknum = len / EC_RAM_BANK_SIZE;
+ addition = len % EC_RAM_BANK_SIZE;
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ for (i = 0; i < banknum || (i == banknum && addition > 0); i++) {
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA, i);
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD,
+ EC_CMD_MALLBOX_READ_256_BYTES_BUFFER);
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ret = ec_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS, &status);
+ if (ret != 0)
+ return ret;
+ if (status != EC_MAILBOX_STATUS_SUCCESS)
+ return -EFAULT;
+
+ for (j = 0; j < addition; j++)
+ ec_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(j),
+ &data[i * EC_RAM_BANK_SIZE + j]);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ec_read_buffer_ram);
+
+static int ec_mailbox_smbus_i2c_set(struct it85xx *ec, u8 protocol, u8 addr,
+ u8 cmd, u8 *wdata, u8 wlen, u8 *rdata,
+ u8 *rlen)
+{
+ u32 i, ret;
+
+ ret = ec_write_mailbox(ec, EC_MAILBOX_OFFSET_SMBI2C_ADDR, addr);
+ if (ret != 0)
+ return ret;
+
+ switch (protocol & 0x7F) {
+ /* I2C */
+ case EC_CMD_MALLBOX_I2C_WRITEREAD_WITH_READ_BUFFER:
+ if (rlen != NULL)
+ return -EINVAL;
+
+ ret = ec_clear_buffer_ram(ec);
+ if (ret != 0)
+ return ret;
+
+ if (wlen > EC_MAILBOX_SMBI2C_DATA_LENGTH)
+ return -EINVAL;
+ ret = ec_write_mailbox(ec, EC_MAILBOX_OFFSET_SMBI2C_WLEN,
+ wlen);
+ if (ret != 0)
+ return ret;
+
+ /* (u8) *rlen is always less than EC_RAM_BUFFER_SIZE */
+ ret = ec_write_mailbox(ec, EC_MAILBOX_OFFSET_SMBI2C_RLEN,
+ *rlen);
+ if (ret != 0)
+ return ret;
+ break;
+ case EC_CMD_MALLBOX_I2C_READ_WRITE:
+ case EC_CMD_MALLBOX_I2C_WRITE_READ:
+ if (rlen != NULL)
+ return -EINVAL;
+
+ if (wlen > EC_MAILBOX_SMBI2C_DATA_LENGTH)
+ return -EINVAL;
+
+ ret = ec_write_mailbox(ec, EC_MAILBOX_OFFSET_SMBI2C_WLEN,
+ wlen);
+ if (ret != 0)
+ return ret;
+
+ if (*rlen > EC_MAILBOX_SMBI2C_DATA_LENGTH)
+ ret = ec_write_mailbox(ec,
+ EC_MAILBOX_OFFSET_SMBI2C_RLEN,
+ EC_MAILBOX_SMBI2C_DATA_LENGTH);
+ else
+ ret = ec_write_mailbox(ec,
+ EC_MAILBOX_OFFSET_SMBI2C_RLEN,
+ *rlen);
+ if (ret != 0)
+ return ret;
+ break;
+ /* SMBus Write */
+ case EC_CMD_MALLBOX_SMBUS_WRITE_BLOCK:
+ if (wlen > EC_MAILBOX_SMBI2C_DATA_LENGTH)
+ return -EINVAL;
+
+ ret = ec_write_mailbox(ec, EC_MAILBOX_OFFSET_SMBI2C_WLEN,
+ wlen);
+ if (ret != 0)
+ return ret;
+ case EC_CMD_MALLBOX_SMBUS_WRITE_BYTE:
+ case EC_CMD_MALLBOX_SMBUS_WRITE_WORD:
+ ret = ec_write_mailbox(ec, EC_MAILBOX_OFFSET_SMBI2C_CMD, cmd);
+ if (ret != 0)
+ return ret;
+ case EC_CMD_MALLBOX_SMBUS_SEND_BYTE:
+ break;
+ /* SMBus Read */
+ case EC_CMD_MALLBOX_SMBUS_READ_BLOCK:
+ if (rlen != NULL)
+ return -EINVAL;
+
+ if (*rlen > EC_MAILBOX_SMBI2C_DATA_LENGTH)
+ ret = ec_write_mailbox(ec,
+ EC_MAILBOX_OFFSET_SMBI2C_RLEN,
+ EC_MAILBOX_SMBI2C_DATA_LENGTH);
+ else
+ ret = ec_write_mailbox(ec,
+ EC_MAILBOX_OFFSET_SMBI2C_RLEN,
+ *rlen);
+ if (ret != 0)
+ return ret;
+ case EC_CMD_MALLBOX_SMBUS_READ_BYTE:
+ case EC_CMD_MALLBOX_SMBUS_READ_WORD:
+ ret = ec_write_mailbox(ec, EC_MAILBOX_OFFSET_SMBI2C_CMD, cmd);
+ if (ret != 0)
+ return ret;
+ case EC_CMD_MALLBOX_SMBUS_WRITE_QUICK:
+ case EC_CMD_MALLBOX_SMBUS_READ_QUICK:
+ case EC_CMD_MALLBOX_SMBUS_RECEIVE_BYTE:
+ default:
+ return 0;
+ }
+
+ if (wlen > 0 && wdata == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < wlen; i++) {
+ ret = ec_write_mailbox(ec, EC_MAILBOX_OFFSET_SMBI2C_DAT(i),
+ wdata[i]);
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ec_mailbox_smbus_i2c_get(struct it85xx *ec, u8 cmd, u8 *rdata,
+ u8 *rlen)
+{
+ int tmp_rlen = 0, ret = 0, ret2, i;
+
+ switch (cmd & 0x7F) {
+ case EC_CMD_MALLBOX_I2C_WRITEREAD_WITH_READ_BUFFER:
+ if (rlen != NULL)
+ return -EINVAL;
+
+ return ec_read_buffer_ram(ec, rdata, (int)((u32) *rlen));
+ case EC_CMD_MALLBOX_SMBUS_READ_BLOCK:
+ if (rlen == NULL)
+ return -EINVAL;
+
+ tmp_rlen = *rlen;
+ ret = ec_read_mailbox(ec, EC_MAILBOX_OFFSET_SMBI2C_RLEN, rlen);
+ if (ret != 0)
+ return ret;
+
+ if (tmp_rlen > *rlen)
+ ret = -ENOMEM;
+ break;
+ case EC_CMD_MALLBOX_SMBUS_RECEIVE_BYTE:
+ case EC_CMD_MALLBOX_SMBUS_READ_WORD:
+ case EC_CMD_MALLBOX_SMBUS_SEND_BYTE:
+ case EC_CMD_MALLBOX_SMBUS_READ_BYTE:
+ break;
+ case EC_CMD_MALLBOX_I2C_READ_WRITE:
+ case EC_CMD_MALLBOX_I2C_WRITE_READ:
+ if (rlen == NULL)
+ return -EINVAL;
+
+ if (*rlen > EC_MAILBOX_SMBI2C_DATA_LENGTH)
+ *rlen = EC_MAILBOX_SMBI2C_DATA_LENGTH;
+ break;
+ case EC_CMD_MALLBOX_SMBUS_READ_QUICK:
+ case EC_CMD_MALLBOX_SMBUS_WRITE_QUICK:
+ case EC_CMD_MALLBOX_SMBUS_WRITE_BYTE:
+ case EC_CMD_MALLBOX_SMBUS_WRITE_WORD:
+ case EC_CMD_MALLBOX_SMBUS_WRITE_BLOCK:
+ default:
+ return 0;
+ }
+
+ if (*rlen > 0 && rdata == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < *rlen; i++) {
+ ret2 = ec_read_mailbox(ec,
+ EC_MAILBOX_OFFSET_SMBI2C_DAT(i),
+ &rdata[i]);
+ if (ret2 != 0)
+ return ret2;
+ }
+
+ return ret;
+}
+
+int ec_mailbox_i2c_smbus(struct it85xx *ec, u8 did, u8 protocol, u8 addr,
+ u8 cmd, u8 *wdata, u8 wlen, u8 *rdata, u8 *rlen)
+{
+ int ret = 0;
+ u8 status;
+
+ if (cmd == 0xFF)
+ return -EINVAL;
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA, did);
+
+ ec_mailbox_smbus_i2c_set(ec, protocol, addr, cmd, wdata, wlen, rdata,
+ rlen);
+
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD, protocol);
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ret = ec_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS, &status);
+ if (ret != 0)
+ return ret;
+ if (status != 0x80)
+ return -EFAULT;
+
+ ret = ec_mailbox_smbus_i2c_get(ec, protocol, rdata, rlen);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL(ec_mailbox_i2c_smbus);
+
+/*===========================================================
+ * Name : ec_get_dynamic_table
+ * Purpose: get dynamic table
+ * Input : did: 32 bytes array pointer, device id
+ * hwpin: 32 bytes array pointer, HW pin
+ * pol: 32 bytes array pointer, polarity
+ * Output : 0: success; else: fail
+ *===========================================================*/
+int ec_get_dynamic_table(struct it85xx *ec, u8 *did, u8 *hwpin, u8 *pol)
+{
+ int i, ret;
+ u8 status;
+
+ if (did == NULL && hwpin == NULL && pol == NULL)
+ return -EINVAL;
+
+ if (did != NULL) {
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA,
+ EC_DYNAMIC_DEVICE_ID);
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD,
+ EC_CMD_MAILBOX_READ_NYNAMIC_TABLE);
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ret = ec_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS, &status);
+ if (ret != 0)
+ return ret;
+ if (status != EC_MAILBOX_STATUS_SUCCESS)
+ return -EFAULT;
+
+ for (i = 0; i < EC_MAX_ITEM_NUM; i++)
+ ec_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(i), &did[i]);
+ }
+
+ if (hwpin != NULL) {
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA, EC_DYNAMIC_HW_PIN);
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD,
+ EC_CMD_MAILBOX_READ_NYNAMIC_TABLE);
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ret = ec_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS, &status);
+ if (ret != 0)
+ return ret;
+ if (status != EC_MAILBOX_STATUS_SUCCESS)
+ return -EFAULT;
+
+ for (i = 0; i < EC_MAX_ITEM_NUM; i++)
+ ec_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(i),
+ &hwpin[i]);
+ }
+
+ if (pol != NULL) {
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA,
+ EC_DYNAMIC_POLARITY);
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD,
+ EC_CMD_MAILBOX_READ_NYNAMIC_TABLE);
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ret = ec_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS, &status);
+ if (ret != 0)
+ return ret;
+ if (status != EC_MAILBOX_STATUS_SUCCESS)
+ return -EFAULT;
+
+ for (i = 0; i < EC_MAX_ITEM_NUM; i++)
+ ec_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(i), &pol[i]);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ec_get_dynamic_table);
+
+/*===========================================================
+ * Name : ec_read_thermalzone
+ * Purpose: read thermal zone data
+ * Input : zone: zone number
+ * smbid: return smbus device id
+ * fanid: return related fan device id
+ * buf: return thermal zone structure,
+ * must bigger than structure length.
+ * more than 6 bytes
+ * len: return structure length
+ * Output : 0: success; else: fail
+ *===========================================================*/
+int ec_read_thermalzone(struct it85xx *ec, u8 zone, u8 *smbid, u8 *fanid,
+ u8 *buf, int *len)
+{
+ int ret, i;
+ u8 status, getlength;
+
+ if (smbid == NULL && fanid == NULL && len == NULL)
+ return -EINVAL;
+
+ if (*len != 0 && buf == NULL)
+ return -EINVAL;
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA, zone);
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD,
+ EC_CMD_MAILBOX_READ_THERMAL_SOURCE);
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ret = ec_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS, &status);
+ if (ret != 0)
+ return ret;
+ if (status != EC_MAILBOX_STATUS_SUCCESS)
+ return -EFAULT;
+
+
+ if (smbid != NULL)
+ ec_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x00), smbid);
+
+ if (fanid != NULL)
+ ec_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x01), fanid);
+
+ if (len == NULL)
+ return 0;
+
+ ec_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x2C), &getlength);
+
+ if (*len > getlength)
+ *len = getlength;
+
+ for (i = 0; i < *len; i++)
+ ec_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x02 + i), &buf[i]);
+
+ if (*len < getlength) {
+ *len = getlength;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ec_read_thermalzone);
+
+/*===========================================================
+ * Name : ec_get_project_name
+ * Purpose: get project name
+ * Input : prj_name: 9 bytes (8 string length) character array.
+ * EC return project name in this array
+ * fw_version: 8 bytes array.
+ * EC return firmware version in this array
+ * Output : 0: success; else: fail
+ *===========================================================*/
+int ec_get_firmware_version_and_project_name(struct it85xx *ec, u8 *prj_name,
+ u16 *kernel_ver, u16 *chip_code,
+ u16 *proj_id, u16 *proj_ver)
+{
+ int ret, i;
+
+ if (prj_name == NULL && kernel_ver == NULL && chip_code == NULL &&
+ proj_id == NULL && proj_ver == NULL
+ )
+ return -EINVAL;
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ ec_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD,
+ EC_CMD_MAILBOX_GET_FIRMWARE_VERSION_AND_PROJECT_NAME);
+
+ ret = ec_wait_cmd_clear(ec);
+ if (ret != 0)
+ return ret;
+
+ if (prj_name != NULL) {
+ for (i = 0; i < EC_MAX_LEN_PROJECT_NAME; i++)
+ ec_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(i),
+ &prj_name[i]);
+ prj_name[EC_MAX_LEN_PROJECT_NAME] = '\0';
+ }
+
+ if (kernel_ver != NULL) {
+ u8 *tmp = (u8 *)kernel_ver;
+ for (i = 0; i < 2; i++)
+ ec_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x09 + i),
+ &tmp[i]);
+ }
+
+ if (chip_code != NULL) {
+ u8 *tmp = (u8 *)chip_code;
+ for (i = 0; i < 2; i++)
+ ec_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x0B + i),
+ &tmp[i]);
+ }
+
+ if (proj_id != NULL) {
+ u8 *tmp = (u8 *)proj_id;
+ for (i = 0; i < 2; i++)
+ ec_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x0D + i),
+ &tmp[i]);
+ }
+
+ if (proj_ver != NULL) {
+ u8 *tmp = (u8 *)proj_ver;
+ for (i = 0; i < 2; i++)
+ ec_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x0F + i),
+ &tmp[i]);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ec_get_firmware_version_and_project_name);
+
+/* IO */
+int ec_io_read(u8 command, u8 offset, u8 *buf, u8 len)
+{
+ int ret, i;
+
+ if (len == 0)
+ return 0;
+
+ if (buf == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < len; i++) {
+ ret = outb_after_ibc(EC_IO_PORT_CMD, command);
+ if (ret != 0)
+ return ret;
+
+ ret = outb_after_ibc(EC_IO_PORT_DATA, offset + i);
+ if (ret != 0)
+ return ret;
+
+ ret = inb_after_obf(&buf[i]);
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ec_io_read);
+
+int ec_io_write(u8 command, u8 offset, u8 *buf, u8 len)
+{
+ int ret, i;
+
+ if (len == 0)
+ return 0;
+
+ if (buf == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < len; i++) {
+ ret = outb_after_ibc(EC_IO_PORT_CMD, command);
+ if (ret != 0)
+ return ret;
+
+ ret = outb_after_ibc(EC_IO_PORT_DATA, offset + i);
+ if (ret != 0)
+ return ret;
+
+ ret = outb_after_ibc(EC_IO_PORT_DATA, buf[i]);
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ec_io_write);
+
+int ec_io_read_byte_without_offset(u8 command, u8 *value)
+{
+ int ret;
+
+ if (value == NULL)
+ return -EINVAL;
+
+ ret = outb_after_ibc(EC_IO_PORT_CMD, command);
+ if (ret != 0)
+ return ret;
+
+ ret = inb_after_obf(value);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL(ec_io_read_byte_without_offset);
+
+static int ec_io_i2c_wait_protocol_clear(void)
+{
+ int i, ret;
+ u8 tmp;
+
+ for (i = 0; i < EC_MAX_TIMEOUT_COUNT; i++) {
+ ret = ec_io_read(EC_CMD_HWRAM_READ, EC_HWRAM_ADDR_SMB_PROTOCOL,
+ &tmp, 1);
+ if (ret != 0)
+ return ret;
+ if (tmp == 0x00)
+ return 0;
+
+ udelay(EC_UDELAY_TIME);
+ }
+
+ return -ETIMEDOUT;
+}
+
+int ec_io_i2c_smbus(u8 pin, u8 protocol, u8 addr, u8 cmd, u8 *wdata, u8 wlen,
+ u8 *rdata, u8 *rlen, int is_i2c)
+{
+ int ret;
+ u8 tmp;
+
+ if (is_i2c != 0)
+ return -EOPNOTSUPP;
+
+ if (wlen > 0 && wdata == NULL)
+ return -EINVAL;
+
+ switch (protocol) {
+ case EC_CMD_MALLBOX_SMBUS_WRITE_BLOCK:
+ ret = ec_io_write(EC_CMD_HWRAM_WRITE,
+ EC_HWRAM_ADDR_SMB_BLOCKCNT, (u8 *)&wlen, 1);
+ if (ret != 0)
+ return ret;
+ case EC_CMD_MALLBOX_SMBUS_WRITE_WORD:
+ case EC_CMD_MALLBOX_SMBUS_WRITE_BYTE:
+ ret = ec_io_write(EC_CMD_HWRAM_WRITE, EC_HWRAM_ADDR_SMB_CMD,
+ &cmd, 1);
+ if (ret != 0)
+ return ret;
+ case EC_CMD_MALLBOX_SMBUS_SEND_BYTE:
+ ret = ec_io_write(EC_CMD_HWRAM_WRITE,
+ EC_HWRAM_ADDR_SMB_DATA(0), &wdata[0], wlen);
+ if (ret != 0)
+ return ret;
+ case EC_CMD_MALLBOX_SMBUS_READ_BLOCK:
+ case EC_CMD_MALLBOX_SMBUS_READ_WORD:
+ case EC_CMD_MALLBOX_SMBUS_READ_BYTE:
+ ret = ec_io_write(EC_CMD_HWRAM_WRITE, EC_HWRAM_ADDR_SMB_CMD,
+ &cmd, 1);
+ if (ret != 0)
+ return ret;
+ case EC_CMD_MALLBOX_SMBUS_RECEIVE_BYTE:
+ case EC_CMD_MALLBOX_SMBUS_READ_QUICK:
+ case EC_CMD_MALLBOX_SMBUS_WRITE_QUICK:
+ break;
+ case EC_CMD_MALLBOX_I2C_WRITEREAD_WITH_READ_BUFFER:
+ case EC_CMD_MALLBOX_I2C_READ_WRITE:
+ case EC_CMD_MALLBOX_I2C_WRITE_READ:
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ ret = ec_io_read(EC_CMD_SMB_INDEX, pin, &tmp, 1);
+ if (ret != 0)
+ return ret;
+ if (tmp != pin)
+ return -EFAULT;
+
+ ret = ec_io_write(EC_CMD_HWRAM_WRITE, EC_HWRAM_ADDR_SMB_ADDRESS,
+ &addr, 1);
+ if (ret != 0)
+ return ret;
+
+ ret = ec_io_write(EC_CMD_HWRAM_WRITE, EC_HWRAM_ADDR_SMB_PROTOCOL,
+ &protocol, 1);
+ if (ret != 0)
+ return ret;
+
+ ret = ec_io_i2c_wait_protocol_clear();
+ if (ret != 0)
+ return ret;
+
+ ret = ec_io_read(EC_CMD_HWRAM_READ, EC_HWRAM_ADDR_SMB_STATUS,
+ &tmp, 1);
+ if (ret != 0)
+ return ret;
+ if (tmp != 0x80)
+ return -EFAULT;
+
+ if (rlen != NULL && *rlen > 0) {
+ if (rdata == NULL)
+ return -EINVAL;
+
+ switch (protocol) {
+ case EC_CMD_MALLBOX_SMBUS_READ_BLOCK:
+ ret = ec_io_read(EC_CMD_HWRAM_READ,
+ EC_HWRAM_ADDR_SMB_BLOCKCNT, rlen, 1);
+ if (ret != 0)
+ return ret;
+ case EC_CMD_MALLBOX_SMBUS_RECEIVE_BYTE:
+ case EC_CMD_MALLBOX_SMBUS_READ_BYTE:
+ case EC_CMD_MALLBOX_SMBUS_READ_WORD:
+ ret = ec_io_read(EC_CMD_HWRAM_READ,
+ EC_HWRAM_ADDR_SMB_DATA(0), &rdata[0],
+ *rlen);
+ if (ret != 0)
+ return ret;
+ case EC_CMD_MALLBOX_SMBUS_READ_QUICK:
+ break;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(ec_io_i2c_smbus);
+
+/* Mailbox & IO */
+int ec_acpiram_read_byte(struct it85xx *ec, u8 addr, u8 *value)
+{
+ if ((ec->flag & EC_F_MAILBOX) != 0)
+ return ec_read_ram(ec, EC_RAM_BANK_ACPI, addr, value, 1);
+ else
+ return ec_io_read(EC_CMD_ACPIRAM_READ, addr, value, 1);
+}
+EXPORT_SYMBOL(ec_acpiram_read_byte);
+
+int ec_acpiram_write_byte(struct it85xx *ec, u8 addr, u8 value)
+{
+ if ((ec->flag & EC_F_MAILBOX) != 0)
+ return ec_write_ram(ec, EC_RAM_BANK_ACPI, addr, &value, 1);
+ else
+ return ec_io_write(EC_CMD_ACPIRAM_WRITE, addr, &value, 1);
+}
+EXPORT_SYMBOL(ec_acpiram_write_byte);
+
+int ec_hwram_read_byte(struct it85xx *ec, u8 addr, u8 *value)
+{
+ if ((ec->flag & EC_F_MAILBOX) != 0)
+ return ec_read_ram(ec, EC_RAM_BANK_HW, addr, value, 1);
+ else
+ return ec_io_read(EC_CMD_HWRAM_READ, addr, value, 1);
+}
+EXPORT_SYMBOL(ec_hwram_read_byte);
+
+int ec_hwram_write_byte(struct it85xx *ec, u8 addr, u8 value)
+{
+ if ((ec->flag & EC_F_MAILBOX) != 0)
+ return ec_write_ram(ec, EC_RAM_BANK_HW, addr, &value, 1);
+ else
+ return ec_io_write(EC_CMD_HWRAM_WRITE, addr, &value, 1);
+}
+EXPORT_SYMBOL(ec_hwram_write_byte);
+
+int ec_smbus_transmit_routine(struct it85xx *ec, u8 did, u8 protocol, u8 addr,
+ u8 cmd, u8 *wdata, u8 wlen, u8 *rdata, u8 *rlen,
+ int is_i2c)
+{
+ if ((ec->flag & EC_F_MAILBOX) != 0) {
+ return ec_mailbox_i2c_smbus(ec, did, protocol, addr, cmd,
+ wdata, wlen, rdata, rlen);
+ } else {
+ u8 pin = ec->table.pinnum[ec->table.devid2itemnum[did]];
+ return ec_io_i2c_smbus(pin, protocol, addr, cmd, wdata,
+ wlen, rdata, rlen, is_i2c);
+ }
+}
+EXPORT_SYMBOL(ec_smbus_transmit_routine);
diff --git a/include/linux/mfd/advantech/imanager2.h b/include/linux/mfd/advantech/imanager2.h
new file mode 100755
index 0000000..1893a81
--- /dev/null
+++ b/include/linux/mfd/advantech/imanager2.h
@@ -0,0 +1,61 @@
+/* imanager2.h - MFD chip information defines of Advantech EC IT8516/18/28
+ * Copyright (C) 2014 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __IMANAGER2_H__
+#define __IMANAGER2_H__
+
+#include <linux/spinlock.h>
+#include "imanager2_ec.h"
+
+#define EC_F_IO 0
+#define EC_F_IO_MAILBOX (1 << 0)
+#define EC_F_MAILBOX (1 << 1)
+
+#define CHIP_NAME "it85xx"
+
+enum chips {
+ it8516 = 0x8516,
+ it8518 = 0x8518,
+ it8519,
+ it8528 = 0x8528
+};
+
+struct ec_table {
+ u8 devid2itemnum[EC_MAX_DEVICE_ID_NUM];
+ u8 pinnum[EC_MAX_ITEM_NUM];
+ u8 devid[EC_MAX_ITEM_NUM];
+ u8 active_polarity[EC_MAX_ITEM_NUM];
+};
+
+struct ec_version {
+ u16 kernel_ver, chip_code, proj_id, proj_ver;
+};
+
+struct ec_version_info {
+ char prj_name[EC_MAX_LEN_PROJECT_NAME + 1]; /* strlen + '\0' */
+ struct ec_version version;
+};
+
+struct it85xx {
+ u16 type;
+ u32 flag;
+ spinlock_t lock; /* protects io */
+ struct ec_version_info info;
+ struct ec_table table;
+};
+
+#endif /* __IMANAGER2_H__ */
diff --git a/include/linux/mfd/advantech/imanager2_ec.h b/include/linux/mfd/advantech/imanager2_ec.h
new file mode 100755
index 0000000..379bc87
--- /dev/null
+++ b/include/linux/mfd/advantech/imanager2_ec.h
@@ -0,0 +1,389 @@
+/* imanager2_ec.h - MFD driver defines of Advantech EC IT8516/18/28
+ * Copyright (C) 2014 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __IMANAGER2_EC_H__
+#define __IMANAGER2_EC_H__
+
+/* --------------------------------------------------------------------------
+ * Definition
+ * -------------------------------------------------------------------------- */
+#define EC_SIO_CMD 0x29C
+#define EC_SIO_DATA 0x29D
+
+/* Access Mailbox */
+#define EC_IO_PORT_CMD 0x29A
+#define EC_IO_PORT_DATA 0x299
+
+#define EC_IO_CMD_READ_OFFSET 0xA0
+#define EC_IO_CMD_WRITE_OFFSET 0x50
+
+#define EC_ITE_PORT_OFS 0x29E
+#define EC_ITE_PORT_DATA 0x29F
+
+#define EC_TABLE_ITEM_UNUSED 0xFF
+#define EC_TABLE_DID_NODEV 0x00
+#define EC_TABLE_HWP_NODEV 0xFF
+#define EC_TABLE_NOITEM 0xFF
+
+#define EC_ERROR 0xFF
+
+#define EC_RAM_BANK_SIZE 32 /* 32 bytes size for each bank. */
+#define EC_RAM_BUFFER_SIZE 256 /* 32 bytes * 8 banks = 256 bytes */
+
+#define EC_CMD_AUTHENTICATION 0x30
+#define EC_MAX_ITEM_NUM 32
+#define EC_MAX_DEVICE_ID_NUM 0xFF
+#define EC_MAX_LEN_PROJECT_NAME 8
+#define EC_MAX_LEN_FW_VERSION 9
+
+
+/* --------------------------------------------------------------------------
+ * CMD - IO
+ * -------------------------------------------------------------------------- */
+/* ADC */
+#define EC_CMD_ADC_INDEX 0x15
+#define EC_CMD_ADC_READ_LSB 0x16
+#define EC_CMD_ADC_READ_MSB 0x1F
+/* HW Control Table */
+#define EC_CMD_HWCTRLTABLE_INDEX 0x20
+#define EC_CMD_HWCTRLTABLE_GET_PIN_NUM 0x21
+#define EC_CMD_HWCTRLTABLE_GET_DEVICE_ID 0x22
+#define EC_CMD_HWCTRLTABLE_GET_PIN_ACTIVE_POLARITY 0x23
+/* ACPI RAM */
+#define EC_CMD_ACPIRAM_READ 0x80
+#define EC_CMD_ACPIRAM_WRITE 0x81
+/* Extend RAM */
+#define EC_CMD_EXTRAM_READ 0x86
+#define EC_CMD_EXTRAM_WRITE 0x87
+/* HW RAM */
+#define EC_CMD_HWRAM_READ 0x88
+#define EC_CMD_HWRAM_WRITE 0x89
+/* SMB I2C */
+#define EC_CMD_SMB_INDEX 0x8A
+#define EC_CMD_SMB_DEVICE_ADDR 0x8B
+#define EC_CMD_ENABLE_CHANNEL_I2C 0x8C
+#define EC_CMD_DISABLE_CHANNEL_I2C 0x8D
+#define EC_CMD_SETUP_SMBUS_FREQUENCY 0x8E
+
+/* --------------------------------------------------------------------------
+ * ACPI RAM Address Table
+ * -------------------------------------------------------------------------- */
+/* n = 1 ~ 2 */
+#define EC_ACPIRAM_ADDR_TEMPERATURE_BASE(n) (0x60 + 3 * (n - 1))
+#define EC_ACPIRAM_ADDR_LOCAL_TEMPERATURE(n) \
+ EC_ACPIRAM_ADDR_TEMPERATURE_BASE(n)
+#define EC_ACPIRAM_ADDR_REMOTE_TEMPERATURE(n) \
+ (EC_ACPIRAM_ADDR_TEMPERATURE_BASE(n) + 1)
+#define EC_ACPIRAM_ADDR_WARNING_TEMPERATURE(n)\
+ (EC_ACPIRAM_ADDR_TEMPERATURE_BASE(n) + 2)
+
+/* N = 0 ~ 2 */
+#define EC_ACPIRAM_ADDR_FAN_SPEED_BASE(N) (0x70 + 2 * (N))
+
+#define EC_ACPIRAM_ADDR_KERNEL_MAJOR_VERSION 0xF8
+#define EC_ACPIRAM_ADDR_CHIP_VENDOR_CODE 0xFA
+#define EC_ACPIRAM_ADDR_PROJECT_NAME_CODE 0xFC
+#define EC_ACPIRAM_ADDR_FIRMWARE_MAJOR_VERSION 0xFE
+
+/* --------------------------------------------------------------------------
+ * HW RAM Address Table
+ * -------------------------------------------------------------------------- */
+/* SMBus Control RAM */
+#define EC_HWRAM_ADDR_SMB_PROTOCOL 0x00
+#define EC_HWRAM_ADDR_SMB_STATUS 0x01
+#define EC_HWRAM_ADDR_SMB_ADDRESS 0x02
+#define EC_HWRAM_ADDR_SMB_CMD 0x03
+#define EC_HWRAM_ADDR_SMB_DATA(N) (0x04 + (N)) /* N: 0 ~ 31 */
+#define EC_HWRAM_ADDR_SMB_BLOCKCNT 0x24
+#define EC_HWRAM_ADDR_SMB_SELECTOR 0x2B
+#define EC_HWRAM_ADDR_SMB_I2CCTL 0x2C
+/* Thermal Source Control RAM 0xB0-0xC7 (N: 0 ~ 3) */
+#define EC_HWRAM_ADDR_THERMAL_SOURCE_BASE_ADDR(N) (0xB0 + 6 * (N))
+#define EC_HWRAM_ADDR_THERMAL_SOURCE_SMB_CHANNEL(N) \
+ EC_HWRAM_ADDR_THERMAL_SOURCE_BASE_ADDR(N)
+#define EC_HWRAM_ADDR_THERMAL_SOURCE_SMB_ADDR(N) \
+ (EC_HWRAM_ADDR_THERMAL_SOURCE_BASE_ADDR(N) + 1)
+#define EC_HWRAM_ADDR_THERMAL_SOURCE_SMB_CMD(N) \
+ (EC_HWRAM_ADDR_THERMAL_SOURCE_BASE_ADDR(N) + 2)
+#define EC_HWRAM_ADDR_THERMAL_SOURCE_SMB_STATUS(N) \
+ (EC_HWRAM_ADDR_THERMAL_SOURCE_BASE_ADDR(N) + 3)
+#define EC_HWRAM_ADDR_THERMAL_SOURCE_SMB_FAN_CODE(N) \
+ (EC_HWRAM_ADDR_THERMAL_SOURCE_BASE_ADDR(N) + 4)
+#define EC_HWRAM_ADDR_THERMAL_SOURCE_SMB_TEMPERATURE(N) \
+ (EC_HWRAM_ADDR_THERMAL_SOURCE_BASE_ADDR(N) + 5)
+/* Fan Control 0xD0-0xEF (N: 0 ~ 3) */
+#define EC_HWRAM_ADDR_FAN_BASE_ADDR(N) (0xD0 + 0x10 * (N))
+#define EC_HWRAM_ADDR_FAN_CODE(N) EC_HWRAM_ADDR_FAN_BASE_ADDR(N)
+#define EC_HWRAM_ADDR_FAN_STATUS(N) (EC_HWRAM_ADDR_FAN_BASE_ADDR(N) + 1)
+#define EC_HWRAM_ADDR_FAN_CONTROL(N) (EC_HWRAM_ADDR_FAN_BASE_ADDR(N) + 2)
+#define EC_HWRAM_ADDR_FAN_TEMP_HI(N) (EC_HWRAM_ADDR_FAN_BASE_ADDR(N) + 3)
+#define EC_HWRAM_ADDR_FAN_TEMP_LO(N) (EC_HWRAM_ADDR_FAN_BASE_ADDR(N) + 4)
+#define EC_HWRAM_ADDR_FAN_TEMP_LOSTOP(N) \
+ (EC_HWRAM_ADDR_FAN_BASE_ADDR(N) + 5)
+#define EC_HWRAM_ADDR_FAN_PWM_HI(N) (EC_HWRAM_ADDR_FAN_BASE_ADDR(N) + 6)
+#define EC_HWRAM_ADDR_FAN_PWM_LO(N) (EC_HWRAM_ADDR_FAN_BASE_ADDR(N) + 7)
+
+/* --------------------------------------------------------------------------
+ * OFS - Mailbox
+ * -------------------------------------------------------------------------- */
+/* Mailbox Structure */
+#define EC_MAILBOX_OFFSET_CMD 0x00
+#define EC_MAILBOX_OFFSET_STATUS 0x01
+#define EC_MAILBOX_OFFSET_PARA 0x02
+#define EC_MAILBOX_OFFSET_DAT(N) (0x03 + (N)) /* N = 0x00 ~ 0x2C */
+/* SMBus & I2C */
+#define EC_MAILBOX_OFFSET_SMBI2C_ADDR EC_MAILBOX_OFFSET_DAT(0x00)
+#define EC_MAILBOX_OFFSET_SMBI2C_CMD EC_MAILBOX_OFFSET_DAT(0x01)
+#define EC_MAILBOX_OFFSET_SMBI2C_RLEN EC_MAILBOX_OFFSET_DAT(0x02)
+#define EC_MAILBOX_OFFSET_SMBI2C_WLEN EC_MAILBOX_OFFSET_DAT(0x03)
+#define EC_MAILBOX_OFFSET_SMBI2C_DAT(N) EC_MAILBOX_OFFSET_DAT(0x04 + N)
+#define EC_MAILBOX_SMBI2C_DATA_LENGTH (0x2C - 0x04 + 1)
+
+/* --------------------------------------------------------------------------
+ * CMD - Mailbox
+ * -------------------------------------------------------------------------- */
+/* SMBus/I2C */
+#define EC_CMD_MALLBOX_I2C_WRITEREAD_WITH_READ_BUFFER 0x01
+#define EC_CMD_MALLBOX_SMBUS_WRITE_QUICK 0x02
+#define EC_CMD_MALLBOX_SMBUS_READ_QUICK 0x03
+#define EC_CMD_MALLBOX_SMBUS_SEND_BYTE 0x04
+#define EC_CMD_MALLBOX_SMBUS_RECEIVE_BYTE 0x05
+#define EC_CMD_MALLBOX_SMBUS_WRITE_BYTE 0x06
+#define EC_CMD_MALLBOX_SMBUS_READ_BYTE 0x07
+#define EC_CMD_MALLBOX_SMBUS_WRITE_WORD 0x08
+#define EC_CMD_MALLBOX_SMBUS_READ_WORD 0x09
+#define EC_CMD_MALLBOX_SMBUS_WRITE_BLOCK 0x0A
+#define EC_CMD_MALLBOX_SMBUS_READ_BLOCK 0x0B
+#define EC_CMD_MALLBOX_I2C_READ_WRITE 0x0E
+#define EC_CMD_MALLBOX_I2C_WRITE_READ 0x0F
+/* GPIO */
+#define EC_CMD_MAILBOX_READ_HW_PIN 0x11
+#define EC_CMD_MAILBOX_WRITE_HW_PIN 0x12
+/* Storage */
+#define EC_CMD_MAILBOX_ENABLE_ALL_EC_ACCESS 0x1D
+#define EC_CMD_MAILBOX_READ_EC_RAM 0x1E
+#define EC_CMD_MAILBOX_WRITE_EC_RAM 0x1F
+/* OTHERS */
+#define EC_CMD_MAILBOX_READ_NYNAMIC_TABLE 0x20
+/* SMBus */
+#define EC_CMD_MAILBOX_GET_SMBUS_FREQUENCY 0x34
+#define EC_CMD_MAILBOX_SET_SMBUS_FREQUENCY 0x35
+/* FAN */
+#define EC_CMD_MAILBOX_READ_FAN_CONTROL 0x40
+#define EC_CMD_MAILBOX_WRITE_FAN_CONTROL 0x41
+/* Thermal Protect */
+#define EC_CMD_MAILBOX_READ_THERMAL_SOURCE 0x42
+#define EC_CMD_MAILBOX_WRITE_THERMAL_SOURCE 0x43
+/* Storage */
+#define EC_CMD_MALLBOX_CLEAR_256_BYTES_BUFFER 0xC0
+#define EC_CMD_MALLBOX_READ_256_BYTES_BUFFER 0xC1
+#define EC_CMD_MALLBOX_WRITE_256_BYTES_BUFFER 0xC2
+#define EC_CMD_MALLBOX_READ_EEPROM_DATA_FROM_256_BYTES_BUFFER 0xC3
+#define EC_CMD_MALLBOX_WRITE_256_BYTES_BUFFER_INTO_EEPROM_DATA 0xC4
+/* General Mailbox Command */
+#define EC_CMD_MAILBOX_GET_FIRMWARE_VERSION_AND_PROJECT_NAME 0xF0
+#define EC_CMD_MAILBOX_CLEAR_ALL 0xFF
+
+/* --------------------------------------------------------------------------
+ * Status - Mailbox
+ * -------------------------------------------------------------------------- */
+#define EC_MAILBOX_STATUS_FAIL 0x00
+#define EC_MAILBOX_STATUS_SUCCESS 0x01
+
+/* --------------------------------------------------------------------------
+ * PARA - Mailbox
+ * -------------------------------------------------------------------------- */
+/* RAM Type */
+#define EC_RAM_BANK_ACPI 0x01
+#define EC_RAM_BANK_HW 0x02
+#define EC_RAM_BANK_EXT 0x03
+#define EC_RAM_BANK_BUFFER 0x06
+/* Dynamic Type */
+#define EC_DYNAMIC_DEVICE_ID 0x00
+#define EC_DYNAMIC_HW_PIN 0x01
+#define EC_DYNAMIC_POLARITY 0x02
+
+/* --------------------------------------------------------------------------
+ * Functions - Mailbox
+ * -------------------------------------------------------------------------- */
+struct it85xx;
+
+/* command = 0x20 */
+int ec_get_dynamic_table(struct it85xx *ec, u8 *did, u8 *hwpin, u8 *pol);
+/* command = 0x42 */
+int ec_read_thermalzone(struct it85xx *ec, u8 zone, u8 *smbid, u8 *fanid,
+ u8 *buf, int *len);
+/* command = 0xC0 */
+int ec_clear_buffer_ram(struct it85xx *ec);
+/* command = 0xC1 */
+int ec_read_buffer_ram(struct it85xx *ec, u8 *data, int len);
+/* command = 0x1E */
+int ec_read_ram(struct it85xx *ec, u8 bank, u8 offset, u8 *buf, u8 len);
+/* command = 0x1F */
+int ec_write_ram(struct it85xx *ec, u8 bank, u8 offset, u8 *buf, u8 len);
+/* command = 0xF0 */
+int ec_get_firmware_version_and_project_name(struct it85xx *ec, u8 *prj_name,
+ u16 *kernel_ver, u16 *chip_code,
+ u16 *proj_id, u16 *proj_ver);
+/* command = 0xFF */
+int ec_clear_mailbox(struct it85xx *ec);
+
+/* --------------------------------------------------------------------------
+ * Functions - basic
+ * -------------------------------------------------------------------------- */
+/* mailbox available */
+#define OBF_MASK (1 << 0)
+#define IBF_MASK (1 << 1)
+int inb_after_obf(u8 *data);
+int outb_after_ibc(u16 port, u8 data);
+int ec_mailbox_read_buffer(struct it85xx *ec, u8 cmd, u8 para, u8 *data,
+ int len);
+int ec_mailbox_write_buffer(struct it85xx *ec, u8 cmd, u8 para, u8 *data,
+ int len);
+int ec_mailbox_i2c_smbus(struct it85xx *ec, u8 did, u8 protocol, u8 addr,
+ u8 cmd, u8 *wdata, u8 wlen, u8 *rdata, u8 *rlen);
+/* only IO available */
+int ec_io_read(u8 command, u8 offset, u8 *buf, u8 len);
+int ec_io_write(u8 command, u8 offset, u8 *buf, u8 len);
+int ec_io_read_byte_without_offset(u8 command, u8 *value);
+int ec_io_i2c_smbus(u8 pin, u8 protocol, u8 addr, u8 cmd, u8 *wdata, u8 wlen,
+ u8 *rdata, u8 *rlen, int is_i2c);
+/* Mailbox & IO */
+int ec_acpiram_read_byte(struct it85xx *ec, u8 addr, u8 *value);
+int ec_acpiram_write_byte(struct it85xx *ec, u8 addr, u8 value);
+int ec_hwram_read_byte(struct it85xx *ec, u8 addr, u8 *value);
+int ec_hwram_write_byte(struct it85xx *ec, u8 addr, u8 value);
+int ec_smbus_transmit_routine(struct it85xx *ec, u8 did, u8 protocol, u8 addr,
+ u8 cmd, u8 *wdata, u8 wlen, u8 *rdata, u8 *rlen,
+ int is_i2c);
+
+/* --------------------------------------------------------------------------
+ * Device ID
+ * -------------------------------------------------------------------------- */
+enum ec_device_id {
+ /* GPIO */
+ altgpio0 = 0x10, /* 0x10 */
+ altgpio1,
+ altgpio2,
+ altgpio3,
+ altgpio4,
+ altgpio5,
+ altgpio6,
+ altgpio7,
+ /* GPIO - Button */
+ btn0,
+ btn1,
+ btn2,
+ btn3,
+ btn4,
+ btn5,
+ btn6,
+ btn7,
+ /* PWM - Fan */
+ cpufan_2p, /* 0x20 */
+ cpufan_4p,
+ sysfan1_2p,
+ sysfan1_4p,
+ sysfan2_2p,
+ sysfan2_4p,
+ /* PWM - Brightness Control */
+ pwmbrightness,
+ /* PWM - System Speaker */
+ pwmbeep,
+ /* SMBus */
+ smboem0,
+ smboem1,
+ smboem2,
+ smbeeprom,
+ smbthermal0,
+ smbthermal1,
+ smbsecurityeep,
+ i2coem,
+ /* DAC - Speaker */
+ dacspeaker, /* 0x30 */
+ /* SMBus */
+ smbeep2k = 0x38,
+ oemeep,
+ oemeep2k,
+ peci,
+ smboem3,
+ smblink,
+ smbslv,
+ /* GPIO - LED */
+ powerled = 0x40, /* 0x40 */
+ batledg,
+ oemled0,
+ oemled1,
+ oemled2,
+ batledr,
+ /* SMBus - Smart Battery */
+ smartbat1 = 0x48,
+ smartbat2,
+ /* ADC */
+ adcmosbat = 0x50, /* 0x50 */
+ adcmosbatx2,
+ adcmosbatx10,
+ adcbat,
+ adcbatx2,
+ adcbatx10,
+ adc5vs0,
+ adc5vs0x2,
+ adc5vs0x10,
+ adv5vs5,
+ adv5vs5x2,
+ adv5vs5x10,
+ adc33vs0,
+ adc33vs0x2,
+ adc33vs0x10,
+ adc33vs5,
+ adc33vs5x2, /* 0x60 */
+ adc33vs5x10,
+ adv12vs0,
+ adv12vs0x2,
+ adv12vs0x10,
+ adcvcorea,
+ adcvcoreax2,
+ adcvcoreax10,
+ adcvcoreb,
+ adcvcorebx2,
+ adcvcorebx10,
+ adcdc,
+ adcdcx2,
+ adcdcx10,
+ adcdcstby,
+ adcdcstbyx2,
+ adcdcstbyx10, /* 0x70 */
+ adcdcother,
+ adcdcotherx2,
+ adcdcotherx10,
+ adccurrent,
+ /* IRQ - Watchdog */
+ wdirq = 0x78,
+ /* GPIO - Watchdog */
+ wdnmi,
+ /* Tacho - Fan */
+ tacho0 = 0x80, /* 0x80 */
+ tacho1,
+ tacho2,
+ /* PWM - Brightness Control */
+ pwmbrightness2 = 0x88,
+ /* GPIO - Backlight Control */
+ brionoff1,
+ brionoff2,
+};
+
+#endif /* __IMANAGER2_EC_H__ */
--
1.9.1
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 2/3] hwmon: (iManager2) Add support for IT8516/18/28
2014-05-29 5:57 [PATCH 1/3] mfd: iManager2: Add support for IT8516/18/28 Wei-Chun Pan
@ 2014-05-29 5:57 ` Wei-Chun Pan
2014-05-30 3:08 ` Guenter Roeck
2014-05-29 5:57 ` [PATCH 3/3] i2c: iManager2: add " Wei-Chun Pan
2014-05-30 7:43 ` [PATCH 1/3] mfd: iManager2: Add " Lee Jones
2 siblings, 1 reply; 5+ messages in thread
From: Wei-Chun Pan @ 2014-05-29 5:57 UTC (permalink / raw)
To: Samuel Ortiz, Lee Jones, Jean Delvare, Guenter Roeck,
Wolfram Sang
Cc: Louis.Lu, Neo.Lo, Hank.Peng, Kevin.Ong, linux-kernel,
Wei-Chun Pan
Advantech's new module comes equipped with "iManager" - an embedded controller (EC), providing embedded features for system integrators to increase reliability and simplify integration.
This patch add the MFD driver for enabling Advantech iManager V2.0 chipset. Available functions support HW Monitor base on ITE-IT85XX chip. These functions are tested on Advantech SOM-5892 board. All the embedded functions are configured by a utility. Advantech has done all the hard work for user with the release of a suite of Software APIs.
These provide not only the underlying drivers required but also a rich set of user-friendly, intelligent and integrated interfaces, which speeds development, enhances security and offers add-on value for Advantech platforms.
Signed-off-by: Wei-Chun Pan Developer <weichun.pan@advantech.com.tw>
---
drivers/hwmon/Kconfig | 7 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/imanager2_hwm.c | 635 ++++++++++++++++++++++++++++++++++++++++++
drivers/hwmon/imanager2_hwm.h | 118 ++++++++
4 files changed, 761 insertions(+)
mode change 100644 => 100755 drivers/hwmon/Kconfig
mode change 100644 => 100755 drivers/hwmon/Makefile
create mode 100755 drivers/hwmon/imanager2_hwm.c
create mode 100755 drivers/hwmon/imanager2_hwm.h
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
old mode 100644
new mode 100755
index bc196f4..d4aeab6
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -39,6 +39,13 @@ config HWMON_DEBUG_CHIP
comment "Native drivers"
+config SENSORS_IMANAGER2
+ tristate "Support for Advantech iManager2 EC H.W. Monitor"
+ select MFD_CORE
+ select MFD_IMANAGER2
+ help
+ Support for the Advantech iManager2 EC H.W. Monitor
+
config SENSORS_AB8500
tristate "AB8500 thermal monitoring"
depends on AB8500_GPADC && AB8500_BM
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
old mode 100644
new mode 100755
index c48f987..a2c8f07
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -146,6 +146,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
+obj-$(CONFIG_SENSORS_IMANAGER2) += imanager2_hwm.o
obj-$(CONFIG_PMBUS) += pmbus/
diff --git a/drivers/hwmon/imanager2_hwm.c b/drivers/hwmon/imanager2_hwm.c
new file mode 100755
index 0000000..48fe3dd
--- /dev/null
+++ b/drivers/hwmon/imanager2_hwm.c
@@ -0,0 +1,635 @@
+/* imanager2_hwm.c - HW Monitoring interface for Advantech EC IT8516/18/28
+ * Copyright (C) 2014 Richard Vidal-Dorsch <richard.dorsch@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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mfd/advantech/imanager2.h>
+#include "imanager2_hwm.h"
+
+#define DRV_NAME CHIP_NAME "_hwm"
+#define DRV_VERSION "0.4.6"
+
+/* Voltage */
+static int ec_get_voltage_adc(struct it85xx *ec, int index,
+ u32 *volt_millivolt)
+{
+ int ret;
+ u8 portnum, tmp[2];
+
+ *volt_millivolt = 0;
+
+ spin_lock(&ec->lock);
+
+ if ((ec->flag & EC_F_MAILBOX) != 0) {
+ ret = ec_mailbox_read_buffer(ec, EC_CMD_MAILBOX_READ_HW_PIN,
+ ec_volt_table[index].did, &tmp[0],
+ 2);
+ } else {
+ u8 pin = ec->table.pinnum[ec->table.devid2itemnum[
+ ec_volt_table[index].did]];
+
+ ret = ec_io_read(EC_CMD_ADC_INDEX, pin, &portnum, 1);
+ if (ret != 0)
+ goto unlock;
+ if (portnum == 0xFF) {
+ ret = -EFAULT;
+ goto unlock;
+ }
+
+ ret = ec_io_read_byte_without_offset(EC_CMD_ADC_READ_LSB,
+ &tmp[1]);
+ if (ret != 0)
+ goto unlock;
+
+ ret = ec_io_read_byte_without_offset(EC_CMD_ADC_READ_MSB,
+ &tmp[0]);
+ }
+unlock:
+ spin_unlock(&ec->lock);
+
+ if (ret != 0)
+ return ret;
+
+ *volt_millivolt = ((tmp[0] << 8 | tmp[1]) & EC_ADC_RESOLUTION_MAX) *
+ ec_volt_table[index].factor *
+ EC_ADC_VOLTAGE_VALUE_MAX / EC_ADC_RESOLUTION_MAX;
+
+ return 0;
+}
+
+static void ec_volt_init_once(struct it85xx *ec, int index)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ec_volt_table); i++) {
+ if (ec->table.devid2itemnum[ec_volt_table[i].did] !=
+ EC_TABLE_ITEM_UNUSED) {
+ ec_volt_table[i].factor = 1;
+ ec_volt_table[i].visible = 1;
+ } else if (ec->table.devid2itemnum[ec_volt_table[i].did + 1] !=
+ EC_TABLE_ITEM_UNUSED) {
+ ec_volt_table[i].did += 1;
+ ec_volt_table[i].factor = 2;
+ ec_volt_table[i].visible = 1;
+ } else if (ec->table.devid2itemnum[ec_volt_table[i].did + 2] !=
+ EC_TABLE_ITEM_UNUSED) {
+ ec_volt_table[i].did += 2;
+ ec_volt_table[i].factor = 10;
+ ec_volt_table[i].visible = 1;
+ } else {
+ ec_volt_table[i].visible = 0;
+ }
+ }
+}
+
+static ssize_t show_in(struct device *dev, struct device_attribute *dev_attr,
+ char *buf)
+{
+ struct it85xx *ec = dev_get_drvdata(dev);
+ u32 val;
+ int i = to_sensor_dev_attr(dev_attr)->index;
+ int ret = ec_get_voltage_adc(ec, i, &val);
+
+ if (ret != 0)
+ return (ssize_t)ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t show_in_label(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
+{
+ int i = to_sensor_dev_attr(dev_attr)->index;
+ return sprintf(buf, "%s\n", ec_volt_table[i].name);
+}
+
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3);
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in, NULL, 4);
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_in, NULL, 5);
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_in, NULL, 6);
+static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_in, NULL, 7);
+static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_in, NULL, 8);
+static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_in, NULL, 9);
+static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_in, NULL, 10);
+static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, show_in, NULL, 11);
+
+static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_in_label, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_label, S_IRUGO, show_in_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_label, S_IRUGO, show_in_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_in_label, NULL, 3);
+static SENSOR_DEVICE_ATTR(in4_label, S_IRUGO, show_in_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(in5_label, S_IRUGO, show_in_label, NULL, 5);
+static SENSOR_DEVICE_ATTR(in6_label, S_IRUGO, show_in_label, NULL, 6);
+static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_in_label, NULL, 7);
+static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_in_label, NULL, 8);
+static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_in_label, NULL, 9);
+static SENSOR_DEVICE_ATTR(in10_label, S_IRUGO, show_in_label, NULL, 10);
+static SENSOR_DEVICE_ATTR(in11_label, S_IRUGO, show_in_label, NULL, 11);
+
+static struct attribute *it85xx_volt_attrs[] = {
+ &sensor_dev_attr_in0_label.dev_attr.attr,
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+
+ &sensor_dev_attr_in1_label.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+
+ &sensor_dev_attr_in2_label.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+
+ &sensor_dev_attr_in3_label.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+
+ &sensor_dev_attr_in4_label.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+
+ &sensor_dev_attr_in5_label.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+
+ &sensor_dev_attr_in6_label.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+
+ &sensor_dev_attr_in7_label.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+
+ &sensor_dev_attr_in8_label.dev_attr.attr,
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+
+ &sensor_dev_attr_in9_label.dev_attr.attr,
+ &sensor_dev_attr_in9_input.dev_attr.attr,
+
+ &sensor_dev_attr_in10_label.dev_attr.attr,
+ &sensor_dev_attr_in10_input.dev_attr.attr,
+
+ &sensor_dev_attr_in11_label.dev_attr.attr,
+ &sensor_dev_attr_in11_input.dev_attr.attr,
+
+ NULL
+};
+
+static umode_t it85xx_volt_mode(struct kobject *kobj, struct attribute *attr,
+ int index)
+{
+ struct sensor_device_attribute *sensor = container_of(
+ attr, struct sensor_device_attribute, dev_attr.attr);
+
+ if (ec_volt_table[sensor->index].visible == 0)
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group it85xx_volt_group = {
+ .attrs = it85xx_volt_attrs,
+ .is_visible = it85xx_volt_mode,
+};
+
+/* Current */
+static int ec_get_current_adc(struct it85xx *ec, int index,
+ u32 *curr_milliampere)
+{
+ int ret;
+ u8 i, tmp[5];
+ u16 value, factor;
+ u32 baseunit;
+
+ *curr_milliampere = 0;
+
+ spin_lock(&ec->lock);
+
+ if ((ec->flag & EC_F_MAILBOX) != 0)
+ ret = ec_mailbox_read_buffer(ec, EC_CMD_MAILBOX_READ_HW_PIN,
+ ec_curr_table[index].did,
+ tmp, ARRAY_SIZE(tmp));
+ else
+ ret = -EOPNOTSUPP;
+
+ spin_unlock(&ec->lock);
+
+ if (ret != 0)
+ return ret;
+
+ value = (tmp[0] << 8 | tmp[1]) & EC_ADC_RESOLUTION_MAX;
+ factor = tmp[2] << 8 | tmp[3];
+ baseunit = 1;
+ for (i = 1; i < tmp[4]; i++)
+ baseunit *= 10;
+
+ *curr_milliampere = value * factor * baseunit *
+ EC_ADC_CURRENT_VALUE_MAX / EC_ADC_RESOLUTION_MAX;
+
+ return 0;
+}
+
+static void ec_current_init_once(struct it85xx *ec)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ec_curr_table); i++)
+ if (ec->table.devid2itemnum[ec_curr_table[i].did] !=
+ EC_TABLE_ITEM_UNUSED)
+ ec_curr_table[i].visible = 1;
+ else
+ ec_curr_table[i].visible = 0;
+}
+
+static ssize_t show_curr(struct device *dev, struct device_attribute *dev_attr,
+ char *buf)
+{
+ struct it85xx *ec = dev_get_drvdata(dev);
+ u32 val;
+ int i = to_sensor_dev_attr(dev_attr)->index;
+ int ret = ec_get_current_adc(ec, i, &val);
+
+ if (ret != 0)
+ return (ssize_t)ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t show_curr_label(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
+{
+ int i = to_sensor_dev_attr(dev_attr)->index;
+ return sprintf(buf, "%s\n", ec_curr_table[i].name);
+}
+
+static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, show_curr, NULL, 0);
+static SENSOR_DEVICE_ATTR(curr1_label, S_IRUGO, show_curr_label, NULL, 0);
+
+static struct attribute *it85xx_curr_attrs[] = {
+ &sensor_dev_attr_curr1_label.dev_attr.attr,
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+
+ NULL
+};
+
+static umode_t it85xx_curr_mode(struct kobject *kobj, struct attribute *attr,
+ int index)
+{
+ struct sensor_device_attribute *sensor = container_of(
+ attr, struct sensor_device_attribute, dev_attr.attr);
+
+ if (ec_curr_table[sensor->index].visible == 0)
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group it85xx_curr_group = {
+ .attrs = it85xx_curr_attrs,
+ .is_visible = it85xx_curr_mode,
+};
+
+/* Temperature */
+static int ec_get_thermal_temperature(struct it85xx *ec, int index,
+ int *temp_millicelsius)
+{
+ int ret;
+ u8 tmp;
+
+ *temp_millicelsius = 0;
+
+ spin_lock(&ec->lock);
+ ret = ec_acpiram_read_byte(ec, ec_temp_table[index].zonetype_acpireg,
+ &tmp);
+ spin_unlock(&ec->lock);
+
+ if (ret != 0)
+ return ret;
+
+ *temp_millicelsius = ((signed)tmp) * 1000;
+
+ return 0;
+}
+
+static void ec_temp_init_once(struct it85xx *ec)
+{
+ int i, j, ret;
+ u8 tmltype;
+ struct ec_thermalzone zone;
+
+ for (i = 0; i < ARRAY_SIZE(ec_temp_table); i++)
+ ec_temp_table[i].visible = 0;
+
+ for (i = 0; i < EC_MAX_THERMAL_ZONE; i++) {
+ spin_lock(&ec->lock);
+
+ if ((ec->flag & EC_F_MAILBOX) != 0) {
+ int len = sizeof(struct ec_thermalzone);
+ ret = ec_read_thermalzone(ec, i, NULL, NULL,
+ (u8 *)&zone, &len);
+ } else {
+ ret = ec_io_read(
+ EC_CMD_HWRAM_READ,
+ EC_HWRAM_ADDR_THERMAL_SOURCE_SMB_STATUS(i),
+ &zone.status, 1);
+ }
+
+ spin_unlock(&ec->lock);
+
+ if (ret != 0)
+ continue;
+
+ tmltype = (zone.status >> 5);
+
+ for (j = 0; j < ARRAY_SIZE(ec_temp_table); j++) {
+ if (tmltype == ec_temp_table[j].zonetype_value) {
+ ec_temp_table[j].visible = 1;
+ break;
+ }
+ }
+ }
+}
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *dev_attr,
+ char *buf)
+{
+ struct it85xx *ec = dev_get_drvdata(dev);
+ int i = to_sensor_dev_attr(dev_attr)->index;
+ int val, ret;
+ ret = ec_get_thermal_temperature(ec, i, &val);
+
+ if (ret != 0)
+ return (ssize_t)ret;
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t show_temp_label(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
+{
+ int i = to_sensor_dev_attr(dev_attr)->index;
+ return sprintf(buf, "%s\n", ec_temp_table[i].name);
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
+
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_temp_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_temp_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, show_temp_label, NULL, 3);
+
+static struct attribute *it85xx_temp_attrs[] = {
+ &sensor_dev_attr_temp1_label.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+
+ &sensor_dev_attr_temp2_label.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+
+ &sensor_dev_attr_temp3_label.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+
+ &sensor_dev_attr_temp4_label.dev_attr.attr,
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+
+ NULL
+};
+
+static umode_t it85xx_temp_mode(struct kobject *kobj, struct attribute *attr,
+ int index)
+{
+ struct sensor_device_attribute *sensor = container_of(
+ attr, struct sensor_device_attribute, dev_attr.attr);
+
+ if (ec_temp_table[sensor->index].visible == 0)
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group it85xx_temp_group = {
+ .attrs = it85xx_temp_attrs,
+ .is_visible = it85xx_temp_mode,
+};
+
+/* Fan Speed */
+static int ec_get_fan_speed(struct it85xx *ec, int index, u32 *speed_rpm)
+{
+ int ret;
+ u8 tmp[2];
+
+ *speed_rpm = 0;
+
+ spin_lock(&ec->lock);
+
+ if ((ec->flag & EC_F_MAILBOX) != 0)
+ ret = ec_mailbox_read_buffer(ec, EC_CMD_MAILBOX_READ_HW_PIN,
+ ec_fan_table[index].did,
+ &tmp[0], 2);
+ else
+ ret = ec_io_read(EC_CMD_ACPIRAM_READ,
+ ec_fan_table[index].fspeed_acpireg,
+ &tmp[0], 2);
+
+ spin_unlock(&ec->lock);
+
+ if (ret != 0)
+ return ret;
+
+ if (tmp[0] == 0xFF && tmp[1] == 0xFF)
+ return -ENODEV;
+
+ *speed_rpm = (tmp[0] << 8) | tmp[1];
+
+ return 0;
+}
+
+static void ec_fan_init_once(struct it85xx *ec)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ec_fan_table); i++)
+ ec_fan_table[i].visible = 0;
+
+ if ((ec->flag & EC_F_MAILBOX) != 0) {
+ for (i = 0; i < ARRAY_SIZE(ec_fan_table); i++)
+ if (ec->table.devid2itemnum[ec_fan_table[i].did] !=
+ EC_TABLE_ITEM_UNUSED)
+ ec_fan_table[i].visible = 1;
+ } else {
+ int fnum, ret;
+ u8 tmp, fscr;
+
+ for (fnum = 0; fnum < EC_MAX_IO_FAN; fnum++) {
+ spin_lock(&ec->lock);
+ ret = ec_io_read(EC_CMD_HWRAM_READ,
+ EC_HWRAM_ADDR_FAN_CONTROL(fnum),
+ &tmp, 1);
+ spin_unlock(&ec->lock);
+
+ if (ret != 0)
+ continue;
+
+ fscr = (tmp >> 4) & 0x03;
+
+ switch (fscr) {
+ case 1: /* tacho0 */
+ case 2: /* tacho1 */
+ case 3: /* tacho2 */
+ i = fscr - 1;
+ break;
+ default:
+ continue;
+ }
+
+ if (ec_fan_table[i].visible == 1)
+ continue;
+
+ ec_fan_table[i].fspeed_acpireg =
+ EC_ACPIRAM_ADDR_FAN_SPEED_BASE(i);
+
+ ec_fan_table[i].visible = 1;
+ }
+ }
+}
+
+static ssize_t show_fan(struct device *dev, struct device_attribute *dev_attr,
+ char *buf)
+{
+ struct it85xx *ec = dev_get_drvdata(dev);
+ int i = to_sensor_dev_attr(dev_attr)->index;
+ u32 val;
+ int ret = ec_get_fan_speed(ec, i, &val);
+
+ if (ret != 0)
+ return (ssize_t)ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t show_fan_label(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
+{
+ int i = to_sensor_dev_attr(dev_attr)->index;
+ return sprintf(buf, "%s\n", ec_fan_table[i].name);
+}
+
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2);
+
+static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, show_fan_label, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, show_fan_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan3_label, S_IRUGO, show_fan_label, NULL, 2);
+
+static struct attribute *it85xx_fan_attrs[] = {
+ &sensor_dev_attr_fan1_label.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+
+ &sensor_dev_attr_fan2_label.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+
+ &sensor_dev_attr_fan3_label.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+
+ NULL
+};
+
+static umode_t it85xx_fan_mode(struct kobject *kobj, struct attribute *attr,
+ int index)
+{
+ struct sensor_device_attribute *sensor = container_of(
+ attr, struct sensor_device_attribute, dev_attr.attr);
+
+ if (ec_fan_table[sensor->index].visible == 0)
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group it85xx_fan_group = {
+ .attrs = it85xx_fan_attrs,
+ .is_visible = it85xx_fan_mode,
+};
+
+/* HWM groups */
+static const struct attribute_group *it85xx_hwmon_groups[] = {
+ &it85xx_volt_group,
+ &it85xx_curr_group,
+ &it85xx_temp_group,
+ &it85xx_fan_group,
+
+ NULL
+};
+
+/* Module */
+static int it85xx_hwmon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct it85xx *hwmon_ec;
+ struct device *hwmon_dev;
+
+ hwmon_ec = devm_kzalloc(dev, sizeof(struct it85xx), GFP_KERNEL);
+
+ if (hwmon_ec == NULL)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, hwmon_ec);
+
+ hwmon_ec = dev->parent->platform_data;
+
+ ec_volt_init_once(hwmon_ec, 0);
+ ec_temp_init_once(hwmon_ec);
+ ec_current_init_once(hwmon_ec);
+ ec_fan_init_once(hwmon_ec);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, CHIP_NAME,
+ hwmon_ec,
+ it85xx_hwmon_groups);
+
+ if (IS_ERR(hwmon_dev) != 0) {
+ pr_err("Could not register it85xx hwmon device\n");
+ return PTR_ERR(hwmon_dev);
+ }
+
+ pr_info("HWM driver v%s loaded\n", DRV_VERSION);
+
+ return 0;
+}
+
+static int it85xx_hwmon_remove(struct platform_device *pdev)
+{
+ pr_info("HWM driver removed\n");
+
+ return 0;
+}
+
+static struct platform_driver it85xx_hwmon_driver = {
+ .probe = it85xx_hwmon_probe,
+ .remove = it85xx_hwmon_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ },
+};
+
+module_platform_driver(it85xx_hwmon_driver);
+
+MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>");
+MODULE_DESCRIPTION("HW Monitoring interface for Advantech EC IT8516/18/28");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/hwmon/imanager2_hwm.h b/drivers/hwmon/imanager2_hwm.h
new file mode 100755
index 0000000..ccf4a4c
--- /dev/null
+++ b/drivers/hwmon/imanager2_hwm.h
@@ -0,0 +1,118 @@
+/* imanager2_hwm.h - HW Monitoring interface for Advantech EC IT8516/18/28
+ * Copyright (C) 2014 Richard Vidal-Dorsch <richard.dorsch@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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __IMANAGER2_HWM_H__
+#define __IMANAGER2_HWM_H__
+
+/* ADC */
+#define EC_ADC_RESOLUTION_MAX 0x03FF /* 10-bit */
+#define EC_ADC_VOLTAGE_VALUE_MAX 3000 /* max: 3.0 V */
+#define EC_ADC_CURRENT_VALUE_MAX 3000 /* max: 3.0 A */
+
+struct volt_item {
+ u8 did;
+ const char *name;
+ int factor;
+ int visible;
+};
+
+struct curr_item {
+ const u8 did;
+ const char *name;
+ int visible;
+};
+
+static struct volt_item ec_volt_table[] = {
+ {.did = adcmosbat, .name = "BAT CMOS", .factor = 0, .visible = 0},
+ {.did = adcbat, .name = "BAT", .factor = 0, .visible = 0},
+ {.did = adc5vs0, .name = "5V S0", .factor = 0, .visible = 0},
+ {.did = adv5vs5, .name = "5V S5", .factor = 0, .visible = 0},
+ {.did = adc33vs0, .name = "3V3 S0", .factor = 0, .visible = 0},
+ {.did = adc33vs5, .name = "3V3 S5", .factor = 0, .visible = 0},
+ {.did = adv12vs0, .name = "12V S0", .factor = 0, .visible = 0},
+ {.did = adcvcorea, .name = "Vcore A", .factor = 0, .visible = 0},
+ {.did = adcvcoreb, .name = "Vcore B", .factor = 0, .visible = 0},
+ {.did = adcdc, .name = "DC", .factor = 0, .visible = 0},
+ {.did = adcdcstby, .name = "DC Standby", .factor = 0, .visible = 0},
+ {.did = adcdcother, .name = "DC Other", .factor = 0, .visible = 0}
+};
+
+static struct curr_item ec_curr_table[] = {
+ {.did = adccurrent, .name = "IMON", .visible = 0}
+};
+
+/* Thermal */
+#define EC_MAX_THERMAL_ZONE 4
+
+#define EC_THERMAL_TYPE_NONE 0
+#define EC_THERMAL_TYPE_SYS1 1
+#define EC_THERMAL_TYPE_CPU 2
+#define EC_THERMAL_TYPE_SYS3 3
+#define EC_THERMAL_TYPE_SYS2 4
+
+struct temp_item {
+ const char *name;
+ const u8 zonetype_value;
+ u8 zonetype_acpireg;
+ int visible;
+};
+
+static struct temp_item ec_temp_table[] = {
+ {.name = "Temp SYS",
+ .zonetype_value = EC_THERMAL_TYPE_SYS1,
+ .zonetype_acpireg = EC_ACPIRAM_ADDR_LOCAL_TEMPERATURE(1),
+ .visible = 0},
+ {.name = "Temp CPU",
+ .zonetype_value = EC_THERMAL_TYPE_CPU,
+ .zonetype_acpireg = EC_ACPIRAM_ADDR_REMOTE_TEMPERATURE(1),
+ .visible = 0},
+ {.name = "Temp SYS3",
+ .zonetype_value = EC_THERMAL_TYPE_SYS3,
+ .zonetype_acpireg = EC_ACPIRAM_ADDR_LOCAL_TEMPERATURE(2),
+ .visible = 0},
+ {.name = "Temp SYS2",
+ .zonetype_value = EC_THERMAL_TYPE_SYS2,
+ .zonetype_acpireg = EC_ACPIRAM_ADDR_REMOTE_TEMPERATURE(2),
+ .visible = 0},
+};
+
+struct ec_thermalzone {
+ u8 channel,
+ addr,
+ cmd,
+ status,
+ fancode,
+ temp;
+};
+
+/* Tacho */
+#define EC_MAX_IO_FAN 3
+
+struct fan_item {
+ const u8 did;
+ const char *name;
+ u8 fspeed_acpireg;
+ int visible;
+};
+
+static struct fan_item ec_fan_table[] = {
+ {.did = tacho0, .name = "Fan CPU", .fspeed_acpireg = 0, .visible = 0},
+ {.did = tacho1, .name = "Fan SYS", .fspeed_acpireg = 0, .visible = 0},
+ {.did = tacho2, .name = "Fan SYS2", .fspeed_acpireg = 0, .visible = 0},
+};
+
+#endif /* __IMANAGER2_HWM_H__ */
--
1.9.1
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH 2/3] hwmon: (iManager2) Add support for IT8516/18/28
2014-05-29 5:57 ` [PATCH 2/3] hwmon: (iManager2) " Wei-Chun Pan
@ 2014-05-30 3:08 ` Guenter Roeck
0 siblings, 0 replies; 5+ messages in thread
From: Guenter Roeck @ 2014-05-30 3:08 UTC (permalink / raw)
To: Wei-Chun Pan, Samuel Ortiz, Lee Jones, Jean Delvare, Wolfram Sang
Cc: Louis.Lu, Neo.Lo, Hank.Peng, Kevin.Ong, linux-kernel
On 05/28/2014 10:57 PM, Wei-Chun Pan wrote:
> Advantech's new module comes equipped with "iManager" - an embedded controller (EC), providing embedded features for system integrators to increase reliability and simplify integration.
> This patch add the MFD driver for enabling Advantech iManager V2.0 chipset. Available functions support HW Monitor base on ITE-IT85XX chip. These functions are tested on Advantech SOM-5892 board. All the embedded functions are configured by a utility. Advantech has done all the hard work for user with the release of a suite of Software APIs.
> These provide not only the underlying drivers required but also a rich set of user-friendly, intelligent and integrated interfaces, which speeds development, enhances security and offers add-on value for Advantech platforms.
>
Doesn't really follow SubmittingPatches guidelines; lines should be shorter.
> Signed-off-by: Wei-Chun Pan Developer <weichun.pan@advantech.com.tw>
I assume 'Developer' is not part of your name ?
> ---
> drivers/hwmon/Kconfig | 7 +
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/imanager2_hwm.c | 635 ++++++++++++++++++++++++++++++++++++++++++
> drivers/hwmon/imanager2_hwm.h | 118 ++++++++
> 4 files changed, 761 insertions(+)
> mode change 100644 => 100755 drivers/hwmon/Kconfig
> mode change 100644 => 100755 drivers/hwmon/Makefile
> create mode 100755 drivers/hwmon/imanager2_hwm.c
> create mode 100755 drivers/hwmon/imanager2_hwm.h
Executable source files ? Note that checkpatch complains about this.
>
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> old mode 100644
> new mode 100755
> index bc196f4..d4aeab6
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -39,6 +39,13 @@ config HWMON_DEBUG_CHIP
>
> comment "Native drivers"
>
> +config SENSORS_IMANAGER2
> + tristate "Support for Advantech iManager2 EC H.W. Monitor"
> + select MFD_CORE
> + select MFD_IMANAGER2
It is customary to express this as dependency. Should be
depends on MFD_IMANAGER2
> + help
> + Support for the Advantech iManager2 EC H.W. Monitor
> +
> config SENSORS_AB8500
> tristate "AB8500 thermal monitoring"
> depends on AB8500_GPADC && AB8500_BM
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> old mode 100644
> new mode 100755
> index c48f987..a2c8f07
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -146,6 +146,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
> obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
> obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
> obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
> +obj-$(CONFIG_SENSORS_IMANAGER2) += imanager2_hwm.o
>
> obj-$(CONFIG_PMBUS) += pmbus/
>
> diff --git a/drivers/hwmon/imanager2_hwm.c b/drivers/hwmon/imanager2_hwm.c
> new file mode 100755
> index 0000000..48fe3dd
> --- /dev/null
> +++ b/drivers/hwmon/imanager2_hwm.c
> @@ -0,0 +1,635 @@
> +/* imanager2_hwm.c - HW Monitoring interface for Advantech EC IT8516/18/28
> + * Copyright (C) 2014 Richard Vidal-Dorsch <richard.dorsch@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 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
Not needed; see below.
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/mfd/advantech/imanager2.h>
> +#include "imanager2_hwm.h"
> +
> +#define DRV_NAME CHIP_NAME "_hwm"
Note that "it85xx" as chip name is a bit too generic. You don't support all chips
starting with IT85, do you ? I would suggest to pick one of the supported chips as
driver name.
> +#define DRV_VERSION "0.4.6"
> +
> +/* Voltage */
> +static int ec_get_voltage_adc(struct it85xx *ec, int index,
Same applies to this structure, really.
> + u32 *volt_millivolt)
> +{
> + int ret;
> + u8 portnum, tmp[2];
> +
> + *volt_millivolt = 0;
> +
> + spin_lock(&ec->lock);
> +
> + if ((ec->flag & EC_F_MAILBOX) != 0) {
The "!= 0" in those comparisons is really unnecessary.
This is implied.
> + ret = ec_mailbox_read_buffer(ec, EC_CMD_MAILBOX_READ_HW_PIN,
> + ec_volt_table[index].did, &tmp[0],
> + 2);
> + } else {
> + u8 pin = ec->table.pinnum[ec->table.devid2itemnum[
> + ec_volt_table[index].did]];
> +
> + ret = ec_io_read(EC_CMD_ADC_INDEX, pin, &portnum, 1);
> + if (ret != 0)
> + goto unlock;
> + if (portnum == 0xFF) {
> + ret = -EFAULT;
> + goto unlock;
> + }
> +
> + ret = ec_io_read_byte_without_offset(EC_CMD_ADC_READ_LSB,
> + &tmp[1]);
> + if (ret != 0)
> + goto unlock;
> +
> + ret = ec_io_read_byte_without_offset(EC_CMD_ADC_READ_MSB,
> + &tmp[0]);
> + }
> +unlock:
> + spin_unlock(&ec->lock);
> +
> + if (ret != 0)
> + return ret;
> +
> + *volt_millivolt = ((tmp[0] << 8 | tmp[1]) & EC_ADC_RESOLUTION_MAX) *
> + ec_volt_table[index].factor *
> + EC_ADC_VOLTAGE_VALUE_MAX / EC_ADC_RESOLUTION_MAX;
> +
> + return 0;
> +}
> +
> +static void ec_volt_init_once(struct it85xx *ec, int index)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(ec_volt_table); i++) {
> + if (ec->table.devid2itemnum[ec_volt_table[i].did] !=
> + EC_TABLE_ITEM_UNUSED) {
> + ec_volt_table[i].factor = 1;
> + ec_volt_table[i].visible = 1;
> + } else if (ec->table.devid2itemnum[ec_volt_table[i].did + 1] !=
> + EC_TABLE_ITEM_UNUSED) {
> + ec_volt_table[i].did += 1;
> + ec_volt_table[i].factor = 2;
> + ec_volt_table[i].visible = 1;
> + } else if (ec->table.devid2itemnum[ec_volt_table[i].did + 2] !=
> + EC_TABLE_ITEM_UNUSED) {
> + ec_volt_table[i].did += 2;
> + ec_volt_table[i].factor = 10;
> + ec_volt_table[i].visible = 1;
> + } else {
> + ec_volt_table[i].visible = 0;
> + }
> + }
> +}
> +
> +static ssize_t show_in(struct device *dev, struct device_attribute *dev_attr,
> + char *buf)
> +{
> + struct it85xx *ec = dev_get_drvdata(dev);
> + u32 val;
> + int i = to_sensor_dev_attr(dev_attr)->index;
> + int ret = ec_get_voltage_adc(ec, i, &val);
> +
> + if (ret != 0)
> + return (ssize_t)ret;
Unnecessary typecast.
> +
> + return sprintf(buf, "%u\n", val);
> +}
> +
> +static ssize_t show_in_label(struct device *dev,
> + struct device_attribute *dev_attr, char *buf)
> +{
> + int i = to_sensor_dev_attr(dev_attr)->index;
> + return sprintf(buf, "%s\n", ec_volt_table[i].name);
> +}
> +
> +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0);
> +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
> +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2);
> +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3);
> +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in, NULL, 4);
> +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_in, NULL, 5);
> +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_in, NULL, 6);
> +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_in, NULL, 7);
> +static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_in, NULL, 8);
> +static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_in, NULL, 9);
> +static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_in, NULL, 10);
> +static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, show_in, NULL, 11);
> +
> +static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_in_label, NULL, 0);
> +static SENSOR_DEVICE_ATTR(in1_label, S_IRUGO, show_in_label, NULL, 1);
> +static SENSOR_DEVICE_ATTR(in2_label, S_IRUGO, show_in_label, NULL, 2);
> +static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_in_label, NULL, 3);
> +static SENSOR_DEVICE_ATTR(in4_label, S_IRUGO, show_in_label, NULL, 4);
> +static SENSOR_DEVICE_ATTR(in5_label, S_IRUGO, show_in_label, NULL, 5);
> +static SENSOR_DEVICE_ATTR(in6_label, S_IRUGO, show_in_label, NULL, 6);
> +static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_in_label, NULL, 7);
> +static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_in_label, NULL, 8);
> +static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_in_label, NULL, 9);
> +static SENSOR_DEVICE_ATTR(in10_label, S_IRUGO, show_in_label, NULL, 10);
> +static SENSOR_DEVICE_ATTR(in11_label, S_IRUGO, show_in_label, NULL, 11);
> +
> +static struct attribute *it85xx_volt_attrs[] = {
> + &sensor_dev_attr_in0_label.dev_attr.attr,
> + &sensor_dev_attr_in0_input.dev_attr.attr,
> +
> + &sensor_dev_attr_in1_label.dev_attr.attr,
> + &sensor_dev_attr_in1_input.dev_attr.attr,
> +
> + &sensor_dev_attr_in2_label.dev_attr.attr,
> + &sensor_dev_attr_in2_input.dev_attr.attr,
> +
> + &sensor_dev_attr_in3_label.dev_attr.attr,
> + &sensor_dev_attr_in3_input.dev_attr.attr,
> +
> + &sensor_dev_attr_in4_label.dev_attr.attr,
> + &sensor_dev_attr_in4_input.dev_attr.attr,
> +
> + &sensor_dev_attr_in5_label.dev_attr.attr,
> + &sensor_dev_attr_in5_input.dev_attr.attr,
> +
> + &sensor_dev_attr_in6_label.dev_attr.attr,
> + &sensor_dev_attr_in6_input.dev_attr.attr,
> +
> + &sensor_dev_attr_in7_label.dev_attr.attr,
> + &sensor_dev_attr_in7_input.dev_attr.attr,
> +
> + &sensor_dev_attr_in8_label.dev_attr.attr,
> + &sensor_dev_attr_in8_input.dev_attr.attr,
> +
> + &sensor_dev_attr_in9_label.dev_attr.attr,
> + &sensor_dev_attr_in9_input.dev_attr.attr,
> +
> + &sensor_dev_attr_in10_label.dev_attr.attr,
> + &sensor_dev_attr_in10_input.dev_attr.attr,
> +
> + &sensor_dev_attr_in11_label.dev_attr.attr,
> + &sensor_dev_attr_in11_input.dev_attr.attr,
> +
> + NULL
> +};
> +
> +static umode_t it85xx_volt_mode(struct kobject *kobj, struct attribute *attr,
> + int index)
> +{
> + struct sensor_device_attribute *sensor = container_of(
> + attr, struct sensor_device_attribute, dev_attr.attr);
> +
> + if (ec_volt_table[sensor->index].visible == 0)
> + return 0;
> +
> + return attr->mode;
> +}
> +
> +static const struct attribute_group it85xx_volt_group = {
> + .attrs = it85xx_volt_attrs,
> + .is_visible = it85xx_volt_mode,
> +};
> +
> +/* Current */
> +static int ec_get_current_adc(struct it85xx *ec, int index,
> + u32 *curr_milliampere)
> +{
> + int ret;
> + u8 i, tmp[5];
> + u16 value, factor;
> + u32 baseunit;
> +
> + *curr_milliampere = 0;
> +
> + spin_lock(&ec->lock);
> +
> + if ((ec->flag & EC_F_MAILBOX) != 0)
> + ret = ec_mailbox_read_buffer(ec, EC_CMD_MAILBOX_READ_HW_PIN,
> + ec_curr_table[index].did,
> + tmp, ARRAY_SIZE(tmp));
> + else
> + ret = -EOPNOTSUPP;
> +
If the operation is not supported in this case, why is the attribute
visible in the first place ?
> + spin_unlock(&ec->lock);
> +
> + if (ret != 0)
> + return ret;
> +
> + value = (tmp[0] << 8 | tmp[1]) & EC_ADC_RESOLUTION_MAX;
> + factor = tmp[2] << 8 | tmp[3];
I would suggest to use ( ) around the shift operations. Technically
not necessary but makes it more obvious what is intended, and you do
it elsewhere.
> + baseunit = 1;
> + for (i = 1; i < tmp[4]; i++)
> + baseunit *= 10;
> +
> + *curr_milliampere = value * factor * baseunit *
> + EC_ADC_CURRENT_VALUE_MAX / EC_ADC_RESOLUTION_MAX;
> +
Would it make sense to use DIV_ROUND_CLOSEST() for those operations
to improve accuracy ?
> + return 0;
> +}
> +
> +static void ec_current_init_once(struct it85xx *ec)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(ec_curr_table); i++)
> + if (ec->table.devid2itemnum[ec_curr_table[i].did] !=
> + EC_TABLE_ITEM_UNUSED)
> + ec_curr_table[i].visible = 1;
> + else
> + ec_curr_table[i].visible = 0;
ec_curr_table[i].visible = ec->table.devid2itemnum[ec_curr_table[i].did] !=
EC_TABLE_ITEM_UNUSED;
would be a bit simpler. Either case, there should be no need to ever set visible
to 0.
> +}
> +
> +static ssize_t show_curr(struct device *dev, struct device_attribute *dev_attr,
> + char *buf)
> +{
> + struct it85xx *ec = dev_get_drvdata(dev);
> + u32 val;
> + int i = to_sensor_dev_attr(dev_attr)->index;
> + int ret = ec_get_current_adc(ec, i, &val);
> +
> + if (ret != 0)
> + return (ssize_t)ret;
> +
> + return sprintf(buf, "%u\n", val);
> +}
> +
> +static ssize_t show_curr_label(struct device *dev,
> + struct device_attribute *dev_attr, char *buf)
> +{
> + int i = to_sensor_dev_attr(dev_attr)->index;
> + return sprintf(buf, "%s\n", ec_curr_table[i].name);
> +}
> +
> +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, show_curr, NULL, 0);
> +static SENSOR_DEVICE_ATTR(curr1_label, S_IRUGO, show_curr_label, NULL, 0);
> +
> +static struct attribute *it85xx_curr_attrs[] = {
> + &sensor_dev_attr_curr1_label.dev_attr.attr,
> + &sensor_dev_attr_curr1_input.dev_attr.attr,
> +
> + NULL
> +};
> +
> +static umode_t it85xx_curr_mode(struct kobject *kobj, struct attribute *attr,
> + int index)
> +{
> + struct sensor_device_attribute *sensor = container_of(
> + attr, struct sensor_device_attribute, dev_attr.attr);
> +
> + if (ec_curr_table[sensor->index].visible == 0)
> + return 0;
> +
> + return attr->mode;
> +}
> +
> +static const struct attribute_group it85xx_curr_group = {
> + .attrs = it85xx_curr_attrs,
> + .is_visible = it85xx_curr_mode,
> +};
> +
> +/* Temperature */
> +static int ec_get_thermal_temperature(struct it85xx *ec, int index,
> + int *temp_millicelsius)
thermal_temperature seems to be a bit redundant. Is there another
non-thermal temperature ? Just use 'temperature'.
> +{
> + int ret;
> + u8 tmp;
> +
> + *temp_millicelsius = 0;
> +
> + spin_lock(&ec->lock);
> + ret = ec_acpiram_read_byte(ec, ec_temp_table[index].zonetype_acpireg,
> + &tmp);
> + spin_unlock(&ec->lock);
> +
> + if (ret != 0)
> + return ret;
> +
> + *temp_millicelsius = ((signed)tmp) * 1000;
Does this mean that tmp is really s8 and can be negative.
If so, I don't think the 'signed' typecase will help;
you might need (s8) instead. Please test.
> +
> + return 0;
> +}
> +
> +static void ec_temp_init_once(struct it85xx *ec)
> +{
> + int i, j, ret;
> + u8 tmltype;
> + struct ec_thermalzone zone;
> +
> + for (i = 0; i < ARRAY_SIZE(ec_temp_table); i++)
> + ec_temp_table[i].visible = 0;
> +
> + for (i = 0; i < EC_MAX_THERMAL_ZONE; i++) {
> + spin_lock(&ec->lock);
> +
> + if ((ec->flag & EC_F_MAILBOX) != 0) {
> + int len = sizeof(struct ec_thermalzone);
> + ret = ec_read_thermalzone(ec, i, NULL, NULL,
> + (u8 *)&zone, &len);
> + } else {
> + ret = ec_io_read(
> + EC_CMD_HWRAM_READ,
> + EC_HWRAM_ADDR_THERMAL_SOURCE_SMB_STATUS(i),
> + &zone.status, 1);
> + }
> +
> + spin_unlock(&ec->lock);
> +
> + if (ret != 0)
> + continue;
> +
> + tmltype = (zone.status >> 5);
> +
> + for (j = 0; j < ARRAY_SIZE(ec_temp_table); j++) {
> + if (tmltype == ec_temp_table[j].zonetype_value) {
> + ec_temp_table[j].visible = 1;
> + break;
> + }
> + }
> + }
> +}
> +
> +static ssize_t show_temp(struct device *dev, struct device_attribute *dev_attr,
> + char *buf)
> +{
> + struct it85xx *ec = dev_get_drvdata(dev);
> + int i = to_sensor_dev_attr(dev_attr)->index;
> + int val, ret;
> + ret = ec_get_thermal_temperature(ec, i, &val);
> +
> + if (ret != 0)
> + return (ssize_t)ret;
> +
> + return sprintf(buf, "%d\n", val);
> +}
> +
> +static ssize_t show_temp_label(struct device *dev,
> + struct device_attribute *dev_attr, char *buf)
> +{
> + int i = to_sensor_dev_attr(dev_attr)->index;
> + return sprintf(buf, "%s\n", ec_temp_table[i].name);
> +}
> +
> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
> +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
> +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
> +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
> +
> +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL, 0);
> +static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_temp_label, NULL, 1);
> +static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_temp_label, NULL, 2);
> +static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, show_temp_label, NULL, 3);
> +
> +static struct attribute *it85xx_temp_attrs[] = {
> + &sensor_dev_attr_temp1_label.dev_attr.attr,
> + &sensor_dev_attr_temp1_input.dev_attr.attr,
> +
> + &sensor_dev_attr_temp2_label.dev_attr.attr,
> + &sensor_dev_attr_temp2_input.dev_attr.attr,
> +
> + &sensor_dev_attr_temp3_label.dev_attr.attr,
> + &sensor_dev_attr_temp3_input.dev_attr.attr,
> +
> + &sensor_dev_attr_temp4_label.dev_attr.attr,
> + &sensor_dev_attr_temp4_input.dev_attr.attr,
> +
> + NULL
> +};
> +
> +static umode_t it85xx_temp_mode(struct kobject *kobj, struct attribute *attr,
> + int index)
> +{
> + struct sensor_device_attribute *sensor = container_of(
> + attr, struct sensor_device_attribute, dev_attr.attr);
> +
> + if (ec_temp_table[sensor->index].visible == 0)
> + return 0;
> +
> + return attr->mode;
> +}
> +
> +static const struct attribute_group it85xx_temp_group = {
> + .attrs = it85xx_temp_attrs,
> + .is_visible = it85xx_temp_mode,
> +};
> +
> +/* Fan Speed */
> +static int ec_get_fan_speed(struct it85xx *ec, int index, u32 *speed_rpm)
> +{
> + int ret;
> + u8 tmp[2];
> +
> + *speed_rpm = 0;
> +
> + spin_lock(&ec->lock);
> +
> + if ((ec->flag & EC_F_MAILBOX) != 0)
> + ret = ec_mailbox_read_buffer(ec, EC_CMD_MAILBOX_READ_HW_PIN,
> + ec_fan_table[index].did,
> + &tmp[0], 2);
> + else
> + ret = ec_io_read(EC_CMD_ACPIRAM_READ,
> + ec_fan_table[index].fspeed_acpireg,
> + &tmp[0], 2);
> +
For exported functions those function names are quite generic.
Might make sense to pick something a bit less generic.
> + spin_unlock(&ec->lock);
> +
> + if (ret != 0)
> + return ret;
> +
> + if (tmp[0] == 0xFF && tmp[1] == 0xFF)
> + return -ENODEV;
> +
> + *speed_rpm = (tmp[0] << 8) | tmp[1];
> +
> + return 0;
> +}
> +
> +static void ec_fan_init_once(struct it85xx *ec)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(ec_fan_table); i++)
> + ec_fan_table[i].visible = 0;
> +
> + if ((ec->flag & EC_F_MAILBOX) != 0) {
> + for (i = 0; i < ARRAY_SIZE(ec_fan_table); i++)
> + if (ec->table.devid2itemnum[ec_fan_table[i].did] !=
> + EC_TABLE_ITEM_UNUSED)
> + ec_fan_table[i].visible = 1;
> + } else {
> + int fnum, ret;
> + u8 tmp, fscr;
> +
> + for (fnum = 0; fnum < EC_MAX_IO_FAN; fnum++) {
> + spin_lock(&ec->lock);
> + ret = ec_io_read(EC_CMD_HWRAM_READ,
> + EC_HWRAM_ADDR_FAN_CONTROL(fnum),
> + &tmp, 1);
> + spin_unlock(&ec->lock);
> +
> + if (ret != 0)
> + continue;
> +
> + fscr = (tmp >> 4) & 0x03;
> +
> + switch (fscr) {
> + case 1: /* tacho0 */
> + case 2: /* tacho1 */
> + case 3: /* tacho2 */
> + i = fscr - 1;
> + break;
> + default:
> + continue;
> + }
> +
if (!fscr)
continue;
i = fscr - 1;
would simpler and do the same. Actually you don't even need 'i';
you could just use fscr as index, after
fscr--;
> + if (ec_fan_table[i].visible == 1)
> + continue;
> +
Can you explain this logic a bit ?
> + ec_fan_table[i].fspeed_acpireg =
> + EC_ACPIRAM_ADDR_FAN_SPEED_BASE(i);
> +
> + ec_fan_table[i].visible = 1;
> + }
> + }
> +}
> +
> +static ssize_t show_fan(struct device *dev, struct device_attribute *dev_attr,
> + char *buf)
> +{
> + struct it85xx *ec = dev_get_drvdata(dev);
> + int i = to_sensor_dev_attr(dev_attr)->index;
> + u32 val;
> + int ret = ec_get_fan_speed(ec, i, &val);
> +
> + if (ret != 0)
> + return (ssize_t)ret;
> +
> + return sprintf(buf, "%u\n", val);
> +}
> +
> +static ssize_t show_fan_label(struct device *dev,
> + struct device_attribute *dev_attr, char *buf)
The second-line alignment differs from function to function.
Please make it consistent, preferably aligned with ( as suggested
in coding style.
> +{
> + int i = to_sensor_dev_attr(dev_attr)->index;
> + return sprintf(buf, "%s\n", ec_fan_table[i].name);
> +}
> +
> +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
> +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
> +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2);
> +
> +static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, show_fan_label, NULL, 0);
> +static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, show_fan_label, NULL, 1);
> +static SENSOR_DEVICE_ATTR(fan3_label, S_IRUGO, show_fan_label, NULL, 2);
> +
> +static struct attribute *it85xx_fan_attrs[] = {
> + &sensor_dev_attr_fan1_label.dev_attr.attr,
> + &sensor_dev_attr_fan1_input.dev_attr.attr,
> +
> + &sensor_dev_attr_fan2_label.dev_attr.attr,
> + &sensor_dev_attr_fan2_input.dev_attr.attr,
> +
> + &sensor_dev_attr_fan3_label.dev_attr.attr,
> + &sensor_dev_attr_fan3_input.dev_attr.attr,
> +
> + NULL
> +};
> +
> +static umode_t it85xx_fan_mode(struct kobject *kobj, struct attribute *attr,
> + int index)
> +{
> + struct sensor_device_attribute *sensor = container_of(
> + attr, struct sensor_device_attribute, dev_attr.attr);
> +
> + if (ec_fan_table[sensor->index].visible == 0)
the '== 0' is really unnecessary here.
if (!ec_fan_table[sensor->index].visible)
would be a better fit (and visible could be a boolean).
> + return 0;
> +
> + return attr->mode;
> +}
> +
> +static const struct attribute_group it85xx_fan_group = {
> + .attrs = it85xx_fan_attrs,
> + .is_visible = it85xx_fan_mode,
> +};
> +
> +/* HWM groups */
> +static const struct attribute_group *it85xx_hwmon_groups[] = {
> + &it85xx_volt_group,
> + &it85xx_curr_group,
> + &it85xx_temp_group,
> + &it85xx_fan_group,
> +
> + NULL
> +};
> +
> +/* Module */
> +static int it85xx_hwmon_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct it85xx *hwmon_ec;
> + struct device *hwmon_dev;
> +
> + hwmon_ec = devm_kzalloc(dev, sizeof(struct it85xx), GFP_KERNEL);
> +
> + if (hwmon_ec == NULL)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, hwmon_ec);
> +
> + hwmon_ec = dev->parent->platform_data;
> +
You lost me here. What is this for ? Why do you allocate this structure
locally just to drop it and replace it with the one you got from the parent
platform data ?
> + ec_volt_init_once(hwmon_ec, 0);
> + ec_temp_init_once(hwmon_ec);
> + ec_current_init_once(hwmon_ec);
> + ec_fan_init_once(hwmon_ec);
> +
> + hwmon_dev = devm_hwmon_device_register_with_groups(dev, CHIP_NAME,
> + hwmon_ec,
> + it85xx_hwmon_groups);
> +
> + if (IS_ERR(hwmon_dev) != 0) {
> + pr_err("Could not register it85xx hwmon device\n");
> + return PTR_ERR(hwmon_dev);
> + }
> +
> + pr_info("HWM driver v%s loaded\n", DRV_VERSION);
> +
Can you drop all this noise ? Besides, you can use dev_info and dev_err
in all cases and don't need to use pr_ functions.
> + return 0;
> +}
> +
> +static int it85xx_hwmon_remove(struct platform_device *pdev)
> +{
> + pr_info("HWM driver removed\n");
> +
> + return 0;
> +}
Please drop the remove function.
> +
> +static struct platform_driver it85xx_hwmon_driver = {
> + .probe = it85xx_hwmon_probe,
> + .remove = it85xx_hwmon_remove,
> + .driver = {
> + .owner = THIS_MODULE,
> + .name = DRV_NAME,
> + },
> +};
> +
> +module_platform_driver(it85xx_hwmon_driver);
> +
> +MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>");
> +MODULE_DESCRIPTION("HW Monitoring interface for Advantech EC IT8516/18/28");
> +MODULE_LICENSE("GPL");
> +MODULE_VERSION(DRV_VERSION);
> diff --git a/drivers/hwmon/imanager2_hwm.h b/drivers/hwmon/imanager2_hwm.h
> new file mode 100755
> index 0000000..ccf4a4c
> --- /dev/null
> +++ b/drivers/hwmon/imanager2_hwm.h
> @@ -0,0 +1,118 @@
> +/* imanager2_hwm.h - HW Monitoring interface for Advantech EC IT8516/18/28
> + * Copyright (C) 2014 Richard Vidal-Dorsch <richard.dorsch@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 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef __IMANAGER2_HWM_H__
> +#define __IMANAGER2_HWM_H__
> +
> +/* ADC */
> +#define EC_ADC_RESOLUTION_MAX 0x03FF /* 10-bit */
> +#define EC_ADC_VOLTAGE_VALUE_MAX 3000 /* max: 3.0 V */
> +#define EC_ADC_CURRENT_VALUE_MAX 3000 /* max: 3.0 A */
> +
> +struct volt_item {
> + u8 did;
> + const char *name;
> + int factor;
> + int visible;
Any reason for not using bool for the 'visible' variables ?
> +};
> +
> +struct curr_item {
> + const u8 did;
> + const char *name;
> + int visible;
> +};
> +
> +static struct volt_item ec_volt_table[] = {
> + {.did = adcmosbat, .name = "BAT CMOS", .factor = 0, .visible = 0},
> + {.did = adcbat, .name = "BAT", .factor = 0, .visible = 0},
> + {.did = adc5vs0, .name = "5V S0", .factor = 0, .visible = 0},
> + {.did = adv5vs5, .name = "5V S5", .factor = 0, .visible = 0},
> + {.did = adc33vs0, .name = "3V3 S0", .factor = 0, .visible = 0},
> + {.did = adc33vs5, .name = "3V3 S5", .factor = 0, .visible = 0},
> + {.did = adv12vs0, .name = "12V S0", .factor = 0, .visible = 0},
> + {.did = adcvcorea, .name = "Vcore A", .factor = 0, .visible = 0},
> + {.did = adcvcoreb, .name = "Vcore B", .factor = 0, .visible = 0},
> + {.did = adcdc, .name = "DC", .factor = 0, .visible = 0},
> + {.did = adcdcstby, .name = "DC Standby", .factor = 0, .visible = 0},
> + {.did = adcdcother, .name = "DC Other", .factor = 0, .visible = 0}
> +};
> +
> +static struct curr_item ec_curr_table[] = {
> + {.did = adccurrent, .name = "IMON", .visible = 0}
> +};
> +
> +/* Thermal */
> +#define EC_MAX_THERMAL_ZONE 4
> +
> +#define EC_THERMAL_TYPE_NONE 0
> +#define EC_THERMAL_TYPE_SYS1 1
> +#define EC_THERMAL_TYPE_CPU 2
> +#define EC_THERMAL_TYPE_SYS3 3
> +#define EC_THERMAL_TYPE_SYS2 4
> +
> +struct temp_item {
> + const char *name;
> + const u8 zonetype_value;
> + u8 zonetype_acpireg;
> + int visible;
> +};
> +
> +static struct temp_item ec_temp_table[] = {
> + {.name = "Temp SYS",
> + .zonetype_value = EC_THERMAL_TYPE_SYS1,
> + .zonetype_acpireg = EC_ACPIRAM_ADDR_LOCAL_TEMPERATURE(1),
> + .visible = 0},
> + {.name = "Temp CPU",
> + .zonetype_value = EC_THERMAL_TYPE_CPU,
> + .zonetype_acpireg = EC_ACPIRAM_ADDR_REMOTE_TEMPERATURE(1),
> + .visible = 0},
> + {.name = "Temp SYS3",
> + .zonetype_value = EC_THERMAL_TYPE_SYS3,
> + .zonetype_acpireg = EC_ACPIRAM_ADDR_LOCAL_TEMPERATURE(2),
> + .visible = 0},
> + {.name = "Temp SYS2",
> + .zonetype_value = EC_THERMAL_TYPE_SYS2,
> + .zonetype_acpireg = EC_ACPIRAM_ADDR_REMOTE_TEMPERATURE(2),
> + .visible = 0},
> +};
> +
> +struct ec_thermalzone {
> + u8 channel,
> + addr,
> + cmd,
> + status,
> + fancode,
> + temp;
> +};
> +
> +/* Tacho */
> +#define EC_MAX_IO_FAN 3
> +
> +struct fan_item {
> + const u8 did;
> + const char *name;
> + u8 fspeed_acpireg;
> + int visible;
> +};
> +
> +static struct fan_item ec_fan_table[] = {
> + {.did = tacho0, .name = "Fan CPU", .fspeed_acpireg = 0, .visible = 0},
> + {.did = tacho1, .name = "Fan SYS", .fspeed_acpireg = 0, .visible = 0},
> + {.did = tacho2, .name = "Fan SYS2", .fspeed_acpireg = 0, .visible = 0},
No need to initialize static variables with 0.
> +};
> +
> +#endif /* __IMANAGER2_HWM_H__ */
>
Please merge the include file into the source. There is no benefit of having it extra,
and variable declarations in include files is a no-go.
Thanks,
Guenter
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 3/3] i2c: iManager2: add support for IT8516/18/28
2014-05-29 5:57 [PATCH 1/3] mfd: iManager2: Add support for IT8516/18/28 Wei-Chun Pan
2014-05-29 5:57 ` [PATCH 2/3] hwmon: (iManager2) " Wei-Chun Pan
@ 2014-05-29 5:57 ` Wei-Chun Pan
2014-05-30 7:43 ` [PATCH 1/3] mfd: iManager2: Add " Lee Jones
2 siblings, 0 replies; 5+ messages in thread
From: Wei-Chun Pan @ 2014-05-29 5:57 UTC (permalink / raw)
To: Samuel Ortiz, Lee Jones, Jean Delvare, Guenter Roeck,
Wolfram Sang
Cc: Louis.Lu, Neo.Lo, Hank.Peng, Kevin.Ong, linux-kernel,
Wei-Chun Pan
Advantech's new module comes equipped with "iManager" - an embedded controller (EC), providing embedded features for system integrators to increase reliability and simplify integration.
This patch add the MFD driver for enabling Advantech iManager V2.0 chipset. Available functions support I2C base on ITE-IT85XX chip. These functions are tested on Advantech SOM-5892 board. All the embedded functions are configured by a utility. Advantech has done all the hard work for user with the release of a suite of Software APIs.
These provide not only the underlying drivers required but also a rich set of user-friendly, intelligent and integrated interfaces, which speeds development, enhances security and offers add-on value for Advantech platforms.
Signed-off-by: Wei-Chun Pan Developer <weichun.pan@advantech.com.tw>
---
drivers/i2c/busses/Kconfig | 8 ++
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/imanager2_i2c.c | 257 +++++++++++++++++++++++++++++++++++++
drivers/i2c/busses/imanager2_i2c.h | 38 ++++++
4 files changed, 304 insertions(+)
mode change 100644 => 100755 drivers/i2c/busses/Kconfig
mode change 100644 => 100755 drivers/i2c/busses/Makefile
create mode 100755 drivers/i2c/busses/imanager2_i2c.c
create mode 100755 drivers/i2c/busses/imanager2_i2c.h
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
old mode 100644
new mode 100755
index c94db1c..8aad058
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -8,6 +8,14 @@ menu "I2C Hardware Bus support"
comment "PC SMBus host controller drivers"
depends on PCI
+config I2C_IMANAGER2
+ tristate "Support for Advantech iManager2 EC I2C"
+ select MFD_CORE
+ select MFD_IMANAGER2
+ depends on I2C=y
+ help
+ Support for the Advantech iManager2 EC I2C.
+
config I2C_ALI1535
tristate "ALI 1535"
depends on PCI
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
old mode 100644
new mode 100755
index 18d18ff..8a2a26b
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -82,6 +82,7 @@ obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
+obj-$(CONFIG_I2C_IMANAGER2) += imanager2_i2c.o
# External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
diff --git a/drivers/i2c/busses/imanager2_i2c.c b/drivers/i2c/busses/imanager2_i2c.c
new file mode 100755
index 0000000..6510dca
--- /dev/null
+++ b/drivers/i2c/busses/imanager2_i2c.c
@@ -0,0 +1,257 @@
+/* imanager2_i2c.c - I2C interface for Advantech EC IT8516/18/28 driver
+ * Copyright (C) 2014 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/mfd/advantech/imanager2.h>
+#include "imanager2_i2c.h"
+
+#define DRV_NAME CHIP_NAME "_i2c"
+#define DRV_VERSION "0.2.4"
+
+struct it85xx_i2c {
+ struct i2c_adapter adapter;
+ struct it85xx *ec;
+ enum ec_device_id did;
+};
+
+struct it85xx_i2c_drv {
+ struct it85xx_i2c *devs[EC_I2C_SMB_DEV_MAX];
+ int devcount;
+};
+
+static int it85xx_smb_access(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write, u8 command,
+ int size, union i2c_smbus_data *data)
+{
+ struct it85xx_i2c *i2cdev = i2c_get_adapdata(adap);
+ int ret = 0;
+ u8 rlen = 0;
+
+ addr <<= 1;
+
+ spin_lock(&i2cdev->ec->lock);
+
+ switch (size) {
+ case I2C_SMBUS_QUICK:
+ pr_info("I2C_SMBUS_QUICK\n");
+ if (read_write == I2C_SMBUS_WRITE)
+ ret = ec_smbus_transmit_routine(
+ i2cdev->ec, i2cdev->did,
+ EC_CMD_MALLBOX_SMBUS_WRITE_QUICK,
+ (u8)addr, 0, NULL, 0, NULL, NULL, 0);
+ else
+ ret = ec_smbus_transmit_routine(
+ i2cdev->ec, i2cdev->did,
+ EC_CMD_MALLBOX_SMBUS_READ_QUICK,
+ (u8)addr, 0, NULL, 0, NULL, NULL, 0);
+ pr_info("ret = %X, addr = %X\n", ret , addr >> 1);
+ break;
+ case I2C_SMBUS_BYTE:
+ pr_info("I2C_SMBUS_BYTE\n");
+ if (read_write == I2C_SMBUS_WRITE) {
+ ret = ec_smbus_transmit_routine(
+ i2cdev->ec, i2cdev->did,
+ EC_CMD_MALLBOX_SMBUS_SEND_BYTE,
+ (u8)addr, 0, &data->byte, 1, NULL, NULL,
+ 0);
+ } else {
+ rlen = 1;
+ ret = ec_smbus_transmit_routine(
+ i2cdev->ec, i2cdev->did,
+ EC_CMD_MALLBOX_SMBUS_RECEIVE_BYTE,
+ (u8)addr, 0, NULL, 0, &data->byte,
+ &rlen, 0);
+ }
+ pr_info("ret = %X, addr = %X\n", ret , addr >> 1);
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ pr_info("I2C_SMBUS_BYTE_DATA\n");
+ if (read_write == I2C_SMBUS_WRITE) {
+ ret = ec_smbus_transmit_routine(
+ i2cdev->ec, i2cdev->did,
+ EC_CMD_MALLBOX_SMBUS_WRITE_BYTE,
+ (u8)addr, command, &data->byte, 1, NULL,
+ NULL, 0);
+ } else {
+ rlen = 1;
+ ret = ec_smbus_transmit_routine(
+ i2cdev->ec, i2cdev->did,
+ EC_CMD_MALLBOX_SMBUS_READ_BYTE,
+ (u8)addr, command, NULL, 0, &data->byte,
+ &rlen, 0);
+ }
+ pr_info("ret = %X, addr = %X\n", ret , addr >> 1);
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ pr_info("I2C_SMBUS_WORD_DATA\n");
+ if (read_write == I2C_SMBUS_WRITE) {
+ ret = ec_smbus_transmit_routine(
+ i2cdev->ec, i2cdev->did,
+ EC_CMD_MALLBOX_SMBUS_WRITE_WORD,
+ (u8)addr, command, (u8 *)&data->word, 2,
+ NULL, NULL, 0);
+ } else {
+ rlen = 2;
+ ret = ec_smbus_transmit_routine(
+ i2cdev->ec, i2cdev->did,
+ EC_CMD_MALLBOX_SMBUS_READ_WORD,
+ (u8)addr, command, NULL, 0,
+ (u8 *)&data->word, &rlen, 0);
+ }
+ pr_info("ret = %X, addr = %X\n", ret , addr >> 1);
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ if (read_write == I2C_SMBUS_WRITE)
+ ret = ec_smbus_transmit_routine(
+ i2cdev->ec, i2cdev->did,
+ EC_CMD_MALLBOX_SMBUS_WRITE_BLOCK,
+ (u8)addr, command, &data->block[1],
+ data->block[0], NULL, NULL, 0);
+ else
+ ret = ec_smbus_transmit_routine(
+ i2cdev->ec, i2cdev->did,
+ EC_CMD_MALLBOX_SMBUS_READ_BLOCK,
+ (u8)addr, command, NULL, 0,
+ &data->block[1], &data->block[0], 0);
+ break;
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ if (read_write == I2C_SMBUS_READ)
+ ret = ec_smbus_transmit_routine(
+ i2cdev->ec, i2cdev->did,
+ EC_CMD_MALLBOX_SMBUS_WRITE_BLOCK,
+ (u8)addr, command, &data->block[0],
+ data->block[0], NULL, NULL, 1);
+ else
+ ret = ec_smbus_transmit_routine(
+ i2cdev->ec, i2cdev->did,
+ EC_CMD_MALLBOX_SMBUS_READ_BLOCK,
+ (u8)addr, command, NULL, 0,
+ &data->block[0], &data->block[0], 1);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ }
+
+ spin_unlock(&i2cdev->ec->lock);
+
+ return ret;
+}
+
+static u32 it85xx_smb_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_I2C_BLOCK;
+}
+
+static const struct i2c_algorithm it85xx_algorithm = {
+ .smbus_xfer = it85xx_smb_access,
+ .functionality = it85xx_smb_i2c_func
+};
+
+static struct it85xx_i2c *add_i2c_adapter(struct device *dev, int index)
+{
+ int ret;
+ struct it85xx_i2c *i2cdev;
+
+ i2cdev = devm_kzalloc(dev, sizeof(struct it85xx_i2c), GFP_KERNEL);
+ if (i2cdev == NULL)
+ return NULL;
+
+ i2cdev->did = ec_i2c_table[index].did;
+ i2cdev->ec = dev->parent->platform_data;
+ i2cdev->adapter.owner = THIS_MODULE;
+ i2cdev->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ i2cdev->adapter.algo = &it85xx_algorithm;
+ i2cdev->adapter.dev.parent = dev;
+ i2cdev->adapter.retries = 3;
+ i2c_set_adapdata(&i2cdev->adapter, i2cdev);
+ snprintf(i2cdev->adapter.name, sizeof(i2cdev->adapter.name),
+ ec_i2c_table[index].name);
+
+ ret = i2c_add_adapter(&i2cdev->adapter);
+ if (ret != 0) {
+ dev_err(dev, "Failed to add I2C adapter\n");
+ return NULL;
+ }
+
+ return i2cdev;
+}
+
+static int it85xx_i2c_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct it85xx *ec = dev->parent->platform_data;
+ struct it85xx_i2c_drv *drvdata;
+ int i;
+
+ drvdata = devm_kzalloc(dev, sizeof(struct it85xx_i2c_drv), GFP_KERNEL);
+
+ if (drvdata == NULL)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, drvdata);
+
+ drvdata->devcount = 0;
+ for (i = 0; i < ARRAY_SIZE(ec_i2c_table); i++)
+ if (ec->table.devid2itemnum[ec_i2c_table[i].did] !=
+ EC_TABLE_ITEM_UNUSED) {
+ struct it85xx_i2c *i2cdev = add_i2c_adapter(dev, i);
+ if (i2cdev != NULL) {
+ drvdata->devs[drvdata->devcount] = i2cdev;
+ drvdata->devcount++;
+ }
+ }
+
+ pr_info("I2C driver v%s loaded\n", DRV_VERSION);
+
+ return 0;
+}
+
+static int it85xx_i2c_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct it85xx_i2c_drv *drvdata = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < drvdata->devcount; i++)
+ i2c_del_adapter(&drvdata->devs[i]->adapter);
+
+ pr_info("I2C driver removed\n");
+
+ return 0;
+}
+
+static struct platform_driver it85xx_i2c_driver = {
+ .probe = it85xx_i2c_probe,
+ .remove = it85xx_i2c_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ },
+};
+
+module_platform_driver(it85xx_i2c_driver);
+
+MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>");
+MODULE_DESCRIPTION("I2C interface for Advantech EC IT8516/18/28 driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/i2c/busses/imanager2_i2c.h b/drivers/i2c/busses/imanager2_i2c.h
new file mode 100755
index 0000000..b9b836b
--- /dev/null
+++ b/drivers/i2c/busses/imanager2_i2c.h
@@ -0,0 +1,38 @@
+/* imanager2_i2c.h - I2C interface for Advantech EC IT8516/18/28 driver
+ * Copyright (C) 2014 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __IMANAGER2_I2C_H__
+#define __IMANAGER2_I2C_H__
+
+#define EC_I2C_SMB_DEV_MAX 8
+#define EC_I2C_BLOCK_MAX 32
+
+struct i2c_item {
+ const u8 did;
+ const char *name;
+};
+
+static struct i2c_item ec_i2c_table[] = {
+ {smboem0, "IT85xx SMBus0"},
+ {smboem1, "IT85xx SMBus1"},
+ {smboem2, "IT85xx SMBus2"},
+ {smbthermal0, "IT85xx Thermal0"},
+ {smbthermal1, "IT85xx Thermal1"},
+ {i2coem, "IT85xx I2COEM"}
+};
+
+#endif /* __IMANAGER2_I2C_H__ */
--
1.9.1
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH 1/3] mfd: iManager2: Add support for IT8516/18/28
2014-05-29 5:57 [PATCH 1/3] mfd: iManager2: Add support for IT8516/18/28 Wei-Chun Pan
2014-05-29 5:57 ` [PATCH 2/3] hwmon: (iManager2) " Wei-Chun Pan
2014-05-29 5:57 ` [PATCH 3/3] i2c: iManager2: add " Wei-Chun Pan
@ 2014-05-30 7:43 ` Lee Jones
2 siblings, 0 replies; 5+ messages in thread
From: Lee Jones @ 2014-05-30 7:43 UTC (permalink / raw)
To: Wei-Chun Pan
Cc: Samuel Ortiz, Jean Delvare, Guenter Roeck, Wolfram Sang, Louis.Lu,
Neo.Lo, Hank.Peng, Kevin.Ong, linux-kernel
Couple of things on top of Guenter's comments.
> +config MFD_IMANAGER2
> + tristate "Support for Advantech iManager2 EC ICs"
> + select MFD_CORE
So you're not using any other frameworks? I see quite a lot of Mailbox
stuff. Why aren't you using the Mailbox API, drivers/mailbox? I'm
gussing you'll need I2C somewhere down the line too. And Regmap?
> +/* imanager2_core.c - MFD core driver of Advantech EC IT8516/18/28
I can't find any documentation on this device. Is there a datasheet
or similar?
> + * Copyright (C) 2014 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 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
Documentation/CodingStyle - Comments.
Run scripts/checkpatch.pl on your patches _before_ resending.
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
Don't like this.
> +#include <linux/module.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/advantech/imanager2.h>
> +
> +#define DRV_NAME CHIP_NAME
Or this.
> +#define DRV_VERSION "0.2.2"
> +
> +static struct platform_device *pdev;
Why don't you register from Device Tree or platform data? I.e. from
arch/<arch>/. While we're on the subject, which architecture(s) is
this likely to run on, or is this device completely agnostic?
> +static struct mfd_cell it85xx_devs[] = {
> + { .name = DRV_NAME "_hwm", },
> + { .name = DRV_NAME "_i2c", },
> +};
Don't like this, just use "it85xx" instead of DRV_NAME.
> +static int ec_authentication(struct it85xx *ec)
> +{
> + u8 tmp;
> + int ret = 0;
> +
> + spin_lock(&ec->lock);
> +
> + if (inb(EC_IO_PORT_CMD) == 0xFF && inb(EC_IO_PORT_DATA) == 0xFF) {
One would guess that the Mailbox driver should be taking care of all
these.
[...]
> +static int ec_get_chip_type(struct it85xx *ec)
> +{
> + spin_lock(&ec->lock);
Can you explain to me why you've chosen spin_lock over, say a mutex?
> + outb(0x20, EC_SIO_CMD);
Don't use magic numbers - all addresses/masks should be #defined
[...]
> +static int ec_get_info(struct it85xx *ec)
> +{
> + int ret;
> + /* first kernel version that supports ITE mailbox */
What?
> + const u16 supmbox_1st_kver = 0x1105;
No way, please remove.
> + u8 *tmp = (u8 *)&ec->info.version.kernel_ver;
> +
> + spin_lock(&ec->lock);
> +
> + ret = ec_io_read(EC_CMD_ACPIRAM_READ,
> + EC_ACPIRAM_ADDR_KERNEL_MAJOR_VERSION, &tmp[0], 2);
> +
> + if (ec->info.version.kernel_ver >= supmbox_1st_kver) {
Yuk!
If you explain why you're doing this, maybe we can suggest another way
of achieving the same aim.
> +static int __init it85xx_probe(struct it85xx *ec)
> +{
> + int ret;
> +
> + ret = ec_authentication(ec);
> + if (ret != 0)
> + return ret;
> +
> + ret = ec_get_chip_type(ec);
> + if (ret != 0)
> + return ret;
> +
> + ret = ec_get_info(ec);
> + if (ret != 0)
> + return ret;
> +
> + ret = ec_build_device_table(ec);
> + if (ret != 0)
> + return ret;
> +
> + if (request_region(EC_IO_PORT_DATA, 2, DRV_NAME) == NULL) {
devm_*
> + release_region(EC_IO_PORT_DATA, 2);
Delete
> + return -EIO;
> + }
> +
> + if (request_region(EC_ITE_PORT_OFS, 2, DRV_NAME) == NULL) {
Etc ...
> + release_region(EC_ITE_PORT_OFS, 2);
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static int __init it85xx_device_add(const struct it85xx *ec)
> +{
> + int ret;
> +
> + pdev = platform_device_alloc(DRV_NAME, 0);
> + if (pdev == NULL) {
> + ret = -ENOMEM;
> + pr_err("Device allocation failed\n");
> + goto exit;
Remove the 'exit' lable and just 'return ret' here.
> + }
> +
> + ret = platform_device_add_data(pdev, ec,
> + sizeof(struct it85xx));
ec is not platform data, it's device data. Platform data should be
passed in via the platform_data structure or Device Tree.
> + if (ret != 0) {
> + pr_err("Platform data allocation failed\n");
> + goto exit_device_put;
> + }
> +
> + ret = platform_device_add(pdev);
> + if (ret != 0) {
> + pr_err("Device addition failed (%d)\n", ret);
> + goto exit_device_put;
> + }
> +
> + ret = mfd_add_devices(&pdev->dev, pdev->id, it85xx_devs,
> + ARRAY_SIZE(it85xx_devs), NULL, -1, NULL);
Second argument here should probably be -1 for 'auto'.
> + if (ret != 0) {
> + pr_err("Cannot add sub device (error=%d)\n", ret);
Once you've converted this whole driver to a platform device, all the
pr_*'s need moving over to dev_*'s.
> + goto exit_device_unregister;
> + } else {
No need for the else.
> + pr_info("MFD core driver v%s loaded\n", DRV_VERSION);
Remove this.
> + }
> +
> + return 0;
> +
> +exit_device_unregister:
> + platform_device_unregister(pdev);
> +exit_device_put:
> + platform_device_put(pdev);
> +exit:
> + return ret;
> +}
> +
> +
Too many '\n'.
> +static int __init it85xx_init(void)
> +{
> + struct it85xx ec;
> +
> + memset(&ec, 0, sizeof(struct it85xx));
> + spin_lock_init(&ec.lock);
> + if (it85xx_probe(&ec) != 0)
Don't role your own Device Driver framework, instead [un]register with
platform_driver_[un]register().
> + return -ENODEV;
> +
> + return it85xx_device_add(&ec);
> +}
> +
> +static void __exit it85xx_exit(void)
> +{
> + release_region(EC_ITE_PORT_OFS, 2);
> + release_region(EC_IO_PORT_DATA, 2);
> + mfd_remove_devices(&pdev->dev);
This should all be done in .remove, not exit.
> + platform_device_unregister(pdev);
> + pr_info("MFD core driver removed\n");
Useless print, please remove.
> +++ b/drivers/mfd/imanager2_ec.c
> @@ -0,0 +1,1093 @@
[...]
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <linux/mfd/advantech/imanager2.h>
> +
> +#define EC_UDELAY_TIME 100
> +#define EC_MAX_TIMEOUT_COUNT 10000
Are these arbitrary?
> +/*===========================================================
> + * Name : wait_obf
> + * Purpose: wait output buffer full flag set
> + * Input : none
> + * Output : 0: success; else: fail
> + *===========================================================*/
These headers are ugly, please use kerneldoc format.
> +static int wait_obf(void)
> +{
> + int i;
> + for (i = 0; i < EC_MAX_TIMEOUT_COUNT; i++) {
> + if ((inb(EC_IO_PORT_CMD) & OBF_MASK) != 0)
> + return 0;
> +
> + udelay(EC_UDELAY_TIME);
> + }
> +
> + return -ETIMEDOUT;
> +}
[...]
> +int ec_mailbox_read_buffer(struct it85xx *ec, u8 cmd, u8 para,
> + u8 *data, int len)
> +{
> + int ret, i;
> + u8 status;
> +
> + ret = ec_wait_cmd_clear(ec);
> + if (ret != 0)
> + return ret;
> +
> + ec_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA, para);
> + ec_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD, cmd);
> +
> + ret = ec_wait_cmd_clear(ec);
> + if (ret != 0)
> + return ret;
> +
> + ret = ec_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS, &status);
> + if (ret != 0)
> + return ret;
> + if (status != EC_MAILBOX_STATUS_SUCCESS)
> + return -EFAULT;
> +
> + for (i = 0; i < len; i++)
> + ec_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(i), &data[i]);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(ec_mailbox_read_buffer);
I suggest that you'll need to move a lot of this code to
drivers/mailbox.
[...]
I need to stop here - this patch is massive.
Please break it up into more manageable chunks for your next submission.
A 1500 line patch, is pretty unacceptable.
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2014-05-30 7:43 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-05-29 5:57 [PATCH 1/3] mfd: iManager2: Add support for IT8516/18/28 Wei-Chun Pan
2014-05-29 5:57 ` [PATCH 2/3] hwmon: (iManager2) " Wei-Chun Pan
2014-05-30 3:08 ` Guenter Roeck
2014-05-29 5:57 ` [PATCH 3/3] i2c: iManager2: add " Wei-Chun Pan
2014-05-30 7:43 ` [PATCH 1/3] mfd: iManager2: Add " Lee Jones
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox