All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/5] mfd: imanager2: Add defines support for IT8516/18/28
@ 2014-06-23  8:39 Wei-Chun Pan
  2014-06-23  8:39 ` [PATCH 2/5] mfd: imanager2: Add Advantech EC APIs " Wei-Chun Pan
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Wei-Chun Pan @ 2014-06-23  8:39 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

Signed-off-by: Wei-Chun Pan <weichun.pan@advantech.com.tw>
---
 include/linux/mfd/advantech/imanager2.h    |  59 +++++
 include/linux/mfd/advantech/imanager2_ec.h | 391 +++++++++++++++++++++++++++++
 2 files changed, 450 insertions(+)
 create mode 100644 include/linux/mfd/advantech/imanager2.h
 create mode 100644 include/linux/mfd/advantech/imanager2_ec.h

diff --git a/include/linux/mfd/advantech/imanager2.h b/include/linux/mfd/advantech/imanager2.h
new file mode 100644
index 0000000..80808d1
--- /dev/null
+++ b/include/linux/mfd/advantech/imanager2.h
@@ -0,0 +1,59 @@
+/*
+ * 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/mutex.h>
+#include "imanager2_ec.h"
+
+#define EC_FLAG_IO		0
+#define EC_FLAG_IO_MAILBOX	(1 << 0)
+#define EC_FLAG_MAILBOX		(1 << 1)
+
+enum chips {
+	it8516 = 0x8516,
+	it8518 = 0x8518,
+	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 imanager2 {
+	u16 id;
+	u32 flag;
+	struct mutex 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 100644
index 0000000..fd03de5
--- /dev/null
+++ b/include/linux/mfd/advantech/imanager2_ec.h
@@ -0,0 +1,391 @@
+/*
+ * 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	41	/* 0x04 ~ 0x2C */
+
+/*
+ * 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 imanager2;
+
+/* command = 0x20 */
+int imanager2_get_dynamic_table(struct imanager2 *ec, u8 *did, u8 *hwpin,
+				u8 *pol);
+/* command = 0x42 */
+int imanager2_read_thermalzone(struct imanager2 *ec, u8 zone, u8 *smbid,
+			       u8 *fanid, u8 *buf, int *len);
+/* command = 0xC0 */
+int imanager2_clear_buffer_ram(struct imanager2 *ec);
+/* command = 0xC1 */
+int imanager2_read_buffer_ram(struct imanager2 *ec, u8 *data, int len);
+/* command = 0x1E */
+int imanager2_read_ram(struct imanager2 *ec, u8 bank, u8 offset, u8 *buf,
+		       u8 len);
+/* command = 0x1F */
+int imanager2_write_ram(struct imanager2 *ec, u8 bank, u8 offset, u8 *buf,
+			u8 len);
+/* command = 0xF0 */
+int imanager2_get_firmware_version_and_project_name(struct imanager2 *ec,
+						    u8 *prj_name,
+						    u16 *kernel_ver,
+						    u16 *chip_code,
+						    u16 *proj_id,
+						    u16 *proj_ver);
+/* command = 0xFF */
+int imanager2_clear_mailbox(struct imanager2 *ec);
+
+/*
+ * Functions - basic
+ */
+/* ITE 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 imanager2_read_mailbox_buffer(struct imanager2 *ec, u8 cmd, u8 para,
+				  u8 *data, int len);
+int imanager2_write_mailbox_buffer(struct imanager2 *ec, u8 cmd, u8 para,
+				   u8 *data, int len);
+/* only IO chennel available */
+int imanager2_io_read(u8 command, u8 offset, u8 *buf, u8 len);
+int imanager2_io_write(u8 command, u8 offset, u8 *buf, u8 len);
+int imanager2_io_read_byte_without_offset(u8 command, u8 *value);
+/* ITE Mailbox & IO chennel */
+int imanager2_acpiram_read_byte(struct imanager2 *ec, u8 addr, u8 *value);
+int imanager2_acpiram_write_byte(struct imanager2 *ec, u8 addr, u8 value);
+int imanager2_hwram_read_byte(struct imanager2 *ec, u8 addr, u8 *value);
+int imanager2_hwram_write_byte(struct imanager2 *ec, u8 addr, u8 value);
+int imanager2_smbus_transmit_routine(struct imanager2 *ec, u8 did, u8 protocol,
+				     u8 addr, u8 cmd, u8 *wdata, u8 wlen,
+				     u8 *rdata, u8 *rlen);
+
+/*
+ * 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/5] mfd: imanager2: Add Advantech EC APIs support for IT8516/18/28
  2014-06-23  8:39 [PATCH 1/5] mfd: imanager2: Add defines support for IT8516/18/28 Wei-Chun Pan
@ 2014-06-23  8:39 ` Wei-Chun Pan
  2014-06-23  8:39 ` [PATCH 3/5] mfd: imanager2: Add Core supports " Wei-Chun Pan
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Wei-Chun Pan @ 2014-06-23  8:39 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

Signed-off-by: Wei-Chun Pan <weichun.pan@advantech.com.tw>
---
 drivers/mfd/imanager2_ec.c | 1219 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 1219 insertions(+)
 create mode 100644 drivers/mfd/imanager2_ec.c

diff --git a/drivers/mfd/imanager2_ec.c b/drivers/mfd/imanager2_ec.c
new file mode 100644
index 0000000..ec0bcbf
--- /dev/null
+++ b/drivers/mfd/imanager2_ec.c
@@ -0,0 +1,1219 @@
+/*
+ * 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		50
+#define EC_MAX_TIMEOUT_COUNT	1000
+
+static int wait_obf(void)
+{
+	int i;
+	for (i = 0; i < EC_MAX_TIMEOUT_COUNT; i++) {
+		/* wait output buffer full flag set */
+		if (inb(EC_IO_PORT_CMD) & OBF_MASK)
+			return 0;
+
+		udelay(EC_UDELAY_TIME);
+	}
+
+	return -EBUSY;
+}
+
+int inb_after_obf(u8 *data)
+{
+	int ret = wait_obf();
+	if (ret)
+		return ret;
+	*data = inb(EC_IO_PORT_DATA);
+	return 0;
+}
+EXPORT_SYMBOL(inb_after_obf);
+
+static int wait_ibc(void)
+{
+	int i;
+	for (i = 0; i < EC_MAX_TIMEOUT_COUNT; i++) {
+		/* wait input buffer full flag clear */
+		if (!(inb(EC_IO_PORT_CMD) & IBF_MASK))
+			return 0;
+
+		udelay(EC_UDELAY_TIME);
+	}
+
+	return -EBUSY;
+}
+
+int outb_after_ibc(u16 port, u8 data)
+{
+	int ret = wait_ibc();
+	if (ret)
+		return ret;
+	outb(data, port);
+	return 0;
+}
+EXPORT_SYMBOL(outb_after_ibc);
+
+static int imanager2_read_mailbox(struct imanager2 *ec, u8 offset, u8 *data)
+{
+	if (ec->flag & EC_FLAG_IO_MAILBOX) {
+		int ret = wait_ibc();
+		if (ret)
+			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;
+}
+
+static int imanager2_write_mailbox(struct imanager2 *ec, u8 offset, u8 data)
+{
+	if (ec->flag & EC_FLAG_IO_MAILBOX) {
+		int ret = outb_after_ibc(EC_IO_PORT_CMD,
+					 offset + EC_IO_CMD_WRITE_OFFSET);
+		if (ret)
+			return ret;
+
+		return outb_after_ibc(EC_IO_PORT_DATA, data);
+	} else {
+		outb(offset, EC_ITE_PORT_OFS);
+		outb(data, EC_ITE_PORT_DATA);
+	}
+
+	return 0;
+}
+
+static int imanager2_wait_cmd_clear(struct imanager2 *ec)
+{
+	u8 cmd;
+	int i, ret;
+
+	for (i = 0; i < EC_MAX_TIMEOUT_COUNT; i++) {
+		ret = imanager2_read_mailbox(ec, EC_MAILBOX_OFFSET_CMD, &cmd);
+		if (ret)
+			return ret;
+		if (cmd == 0x00)
+			return 0;
+
+		udelay(EC_UDELAY_TIME);
+	}
+
+	return -EBUSY;
+}
+
+int imanager2_clear_mailbox(struct imanager2 *ec)
+{
+	int ret = imanager2_wait_cmd_clear(ec);
+	if (ret)
+		return ret;
+
+	return imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD,
+				       EC_CMD_MAILBOX_CLEAR_ALL);
+}
+EXPORT_SYMBOL(imanager2_clear_mailbox);
+
+int imanager2_read_mailbox_buffer(struct imanager2 *ec, u8 cmd, u8 para,
+				  u8 *data, int len)
+{
+	int ret, i;
+	u8 status;
+
+	ret = imanager2_wait_cmd_clear(ec);
+	if (ret)
+		return ret;
+
+	ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA, para);
+	if (ret)
+		return ret;
+
+	ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD, cmd);
+	if (ret)
+		return ret;
+
+	ret = imanager2_wait_cmd_clear(ec);
+	if (ret)
+		return ret;
+
+	ret = imanager2_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS, &status);
+	if (ret)
+		return ret;
+	if (status != EC_MAILBOX_STATUS_SUCCESS)
+		return -ENXIO;
+
+	for (i = 0; i < len; i++) {
+		ret = imanager2_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(i),
+					     &data[i]);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(imanager2_read_mailbox_buffer);
+
+int imanager2_write_mailbox_buffer(struct imanager2 *ec, u8 cmd, u8 para,
+				   u8 *data, int len)
+{
+	int ret, i;
+	u8 status;
+
+	ret = imanager2_wait_cmd_clear(ec);
+	if (ret)
+		return ret;
+
+	ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA, para);
+	for (i = 0; i < len; i++) {
+		ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_DAT(i),
+					      data[i]);
+		if (ret)
+			return ret;
+	}
+
+	ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD, cmd);
+	if (ret)
+		return ret;
+
+	ret = imanager2_wait_cmd_clear(ec);
+	if (ret)
+		return ret;
+
+	ret = imanager2_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS, &status);
+	if (ret)
+		return ret;
+	if (status != EC_MAILBOX_STATUS_SUCCESS)
+		return -ENXIO;
+
+	return 0;
+}
+EXPORT_SYMBOL(imanager2_write_mailbox_buffer);
+
+/**
+ * imanager2_clear_buffer_ram() - clear buffer ram
+ * @ec:		EC device sturcture
+ *
+ * The bank size is 256 bytes EC memory. This function can clear 256 bytes
+ * buffer ram to 0xFF.
+ *
+ * Advantech EC command is 0xC0.
+ */
+int imanager2_clear_buffer_ram(struct imanager2 *ec)
+{
+	int ret = imanager2_wait_cmd_clear(ec);
+	if (ret)
+		return ret;
+
+	return imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD,
+				       EC_CMD_MALLBOX_CLEAR_256_BYTES_BUFFER);
+}
+EXPORT_SYMBOL(imanager2_clear_buffer_ram);
+
+/**
+ * imanager2_read_ram() - read ec ram
+ * @ec:		EC device sturcture
+ * @bank:	memory bank
+ * @offset:	memory offset
+ * @buf:	data buffer pointer
+ * @len:	data length
+ *
+ * System can read EC memory to control EC function or get information data.
+ * There are 4 banks are always public to system access. These four banks are
+ * ACPI ram, HW ram, EXT ram, and buffer ram. Besides these four banks, other
+ * memory access is designed for OEM project*
+ *
+ * Advantech EC command is 0x1E.
+ */
+int imanager2_read_ram(struct imanager2 *ec, u8 bank, u8 offset, u8 *buf,
+		       u8 len)
+{
+	int i, ret;
+	u8 status;
+
+	if (len && !buf)
+		return -EINVAL;
+
+	ret = imanager2_wait_cmd_clear(ec);
+	if (ret)
+		return ret;
+
+	ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA, bank);
+	if (ret)
+		return ret;
+
+	ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x00), offset);
+	if (ret)
+		return ret;
+
+	ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x2C), len);
+	if (ret)
+		return ret;
+
+	ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD,
+				      EC_CMD_MAILBOX_READ_EC_RAM);
+	if (ret)
+		return ret;
+
+	ret = imanager2_wait_cmd_clear(ec);
+	if (ret)
+		return ret;
+
+	ret = imanager2_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS, &status);
+	if (ret)
+		return ret;
+	if (status != EC_MAILBOX_STATUS_SUCCESS)
+		return -ENXIO;
+
+	for (i = 0; i < len; i++) {
+		/* range: DATA01~DATA2B */
+		ret = imanager2_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(1 + i),
+					     &buf[i]);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(imanager2_read_ram);
+
+/**
+ * imanager2_write_ram() - write ec ram
+ * @ec:		EC device sturcture
+ * @bank:	memory bank
+ * @offset:	memory offset
+ * @buf:	data buffer pointer
+ * @len:	data length
+ *
+ * System can write EC memory to control EC function or get information data.
+ * There are 4 banks are always public to system access. These four banks are
+ * ACPI ram, HW ram, EXT ram, and buffer ram. Besides these four banks, other
+ * memory access is designed for OEM project*
+ *
+ * Advantech EC command is 0x1F.
+ */
+int imanager2_write_ram(struct imanager2 *ec, u8 bank, u8 offset, u8 *buf,
+			u8 len)
+{
+	int i, ret;
+	u8 status;
+
+	if (len && !buf)
+		return -EINVAL;
+
+	ret = imanager2_wait_cmd_clear(ec);
+	if (ret)
+		return ret;
+
+	ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA, bank);
+	if (ret)
+		return ret;
+
+	ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x00), offset);
+	if (ret)
+		return ret;
+
+	ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x2C), len);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < len; i++) {
+		ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_DAT(1 + i),
+					      buf[i]);
+		if (ret)
+			return ret;
+	}
+
+	ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD,
+				      EC_CMD_MAILBOX_WRITE_EC_RAM);
+	if (ret)
+		return ret;
+
+	ret = imanager2_wait_cmd_clear(ec);
+	if (ret)
+		return ret;
+
+	ret = imanager2_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS, &status);
+	if (ret)
+		return ret;
+	if (status != EC_MAILBOX_STATUS_SUCCESS)
+		return -ENXIO;
+
+	return 0;
+}
+EXPORT_SYMBOL(imanager2_write_ram);
+
+/**
+ * imanager2_read_buffer_ram() - read data from buffer ram
+ * @ec:		EC device sturcture
+ *
+ * The bank size is 256 bytes EC memory. This function can read buffer ram and
+ * cross banks.
+ *
+ * Advantech EC command is 0xC1.
+ */
+int imanager2_read_buffer_ram(struct imanager2 *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)
+		return -EINVAL;
+
+	banknum = len / EC_RAM_BANK_SIZE;
+	addition = len % EC_RAM_BANK_SIZE;
+
+	ret = imanager2_wait_cmd_clear(ec);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < banknum || (i == banknum && addition > 0); i++) {
+		ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA, i);
+		if (ret)
+			return ret;
+
+		ret = imanager2_write_mailbox(
+					ec, EC_MAILBOX_OFFSET_CMD,
+					EC_CMD_MALLBOX_READ_256_BYTES_BUFFER);
+		if (ret)
+			return ret;
+
+		ret = imanager2_wait_cmd_clear(ec);
+		if (ret)
+			return ret;
+
+		ret = imanager2_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS,
+					     &status);
+		if (ret)
+			return ret;
+		if (status != EC_MAILBOX_STATUS_SUCCESS)
+			return -ENXIO;
+
+		for (j = 0; j < addition; j++) {
+			ret = imanager2_read_mailbox(
+					ec, EC_MAILBOX_OFFSET_DAT(j),
+					&data[i * EC_RAM_BANK_SIZE + j]);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(imanager2_read_buffer_ram);
+
+static int imanager2_smbus_i2c_set(struct imanager2 *ec, u8 protocol, u8 addr,
+				   u8 cmd, u8 *wdata, u8 wlen, u8 *rdata,
+				   u8 *rlen)
+{
+	u32 i, ret;
+
+	ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_SMBI2C_ADDR, addr);
+	if (ret)
+		return ret;
+
+	switch (protocol & 0x7F) {
+	/* I2C */
+	case EC_CMD_MALLBOX_I2C_WRITEREAD_WITH_READ_BUFFER:
+		if (!rlen)
+			return -EINVAL;
+
+		ret = imanager2_clear_buffer_ram(ec);
+		if (ret)
+			return ret;
+
+		if (wlen > EC_MAILBOX_SMBI2C_DATA_LENGTH)
+			return -EINVAL;
+		ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_SMBI2C_WLEN,
+					      wlen);
+		if (ret)
+			return ret;
+
+		/* (u8) *rlen is always less than EC_RAM_BUFFER_SIZE */
+		ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_SMBI2C_RLEN,
+					      *rlen);
+		if (ret)
+			return ret;
+		break;
+	case EC_CMD_MALLBOX_I2C_READ_WRITE:
+	case EC_CMD_MALLBOX_I2C_WRITE_READ:
+		if (!rlen)
+			return -EINVAL;
+
+		if (wlen > EC_MAILBOX_SMBI2C_DATA_LENGTH)
+			return -EINVAL;
+
+		ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_SMBI2C_WLEN,
+					      wlen);
+		if (ret)
+			return ret;
+
+		if (*rlen > EC_MAILBOX_SMBI2C_DATA_LENGTH)
+			ret = imanager2_write_mailbox(
+				ec, EC_MAILBOX_OFFSET_SMBI2C_RLEN,
+				EC_MAILBOX_SMBI2C_DATA_LENGTH);
+		else
+			ret = imanager2_write_mailbox(
+				ec, EC_MAILBOX_OFFSET_SMBI2C_RLEN, *rlen);
+		if (ret)
+			return ret;
+		break;
+	/* SMBus Write */
+	case EC_CMD_MALLBOX_SMBUS_WRITE_BLOCK:
+		if (wlen > EC_MAILBOX_SMBI2C_DATA_LENGTH)
+			return -EINVAL;
+
+		ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_SMBI2C_WLEN,
+					      wlen);
+		if (ret)
+			return ret;
+	case EC_CMD_MALLBOX_SMBUS_WRITE_BYTE:
+	case EC_CMD_MALLBOX_SMBUS_WRITE_WORD:
+		ret =
+		    imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_SMBI2C_CMD,
+					    cmd);
+		if (ret)
+			return ret;
+	case EC_CMD_MALLBOX_SMBUS_SEND_BYTE:
+		break;
+	/* SMBus Read */
+	case EC_CMD_MALLBOX_SMBUS_READ_BLOCK:
+		if (!rlen)
+			return -EINVAL;
+
+		if (*rlen > EC_MAILBOX_SMBI2C_DATA_LENGTH)
+			ret = imanager2_write_mailbox(
+				ec, EC_MAILBOX_OFFSET_SMBI2C_RLEN,
+				EC_MAILBOX_SMBI2C_DATA_LENGTH);
+		else
+			ret = imanager2_write_mailbox(
+				ec, EC_MAILBOX_OFFSET_SMBI2C_RLEN, *rlen);
+		if (ret)
+			return ret;
+	case EC_CMD_MALLBOX_SMBUS_READ_BYTE:
+	case EC_CMD_MALLBOX_SMBUS_READ_WORD:
+		ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_SMBI2C_CMD,
+					      cmd);
+		if (ret)
+			return ret;
+	case EC_CMD_MALLBOX_SMBUS_RECEIVE_BYTE:
+	default:
+		return 0;
+	}
+
+	if (wlen && !wdata)
+		return -EINVAL;
+
+	for (i = 0; i < wlen; i++) {
+		ret = imanager2_write_mailbox(ec,
+					      EC_MAILBOX_OFFSET_SMBI2C_DAT(i),
+					      wdata[i]);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int imanager2_smbus_i2c_get(struct imanager2 *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)
+			return -EINVAL;
+
+		return imanager2_read_buffer_ram(ec, rdata, (int)((u32)*rlen));
+	case EC_CMD_MALLBOX_I2C_READ_WRITE:
+	case EC_CMD_MALLBOX_I2C_WRITE_READ:
+		if (!rlen)
+			return -EINVAL;
+
+		if (*rlen > EC_MAILBOX_SMBI2C_DATA_LENGTH)
+			*rlen = EC_MAILBOX_SMBI2C_DATA_LENGTH;
+		break;
+	/* SMBus Read */
+	case EC_CMD_MALLBOX_SMBUS_READ_BLOCK:
+		if (!rlen)
+			return -EINVAL;
+
+		tmp_rlen = *rlen;
+		ret =
+		    imanager2_read_mailbox(ec, EC_MAILBOX_OFFSET_SMBI2C_RLEN,
+					   rlen);
+		if (ret)
+			return ret;
+
+		if (tmp_rlen > *rlen)
+			ret = -EINVAL;
+		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;
+	default:
+		return 0;
+	}
+
+	if (*rlen && !rdata)
+		return -EINVAL;
+
+	for (i = 0; i < *rlen; i++) {
+		ret2 = imanager2_read_mailbox(ec,
+					      EC_MAILBOX_OFFSET_SMBI2C_DAT(i),
+					      &rdata[i]);
+		if (ret2)
+			return ret2;
+	}
+
+	return ret;
+}
+
+static int imanager2_i2c_smbus(struct imanager2 *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 = imanager2_wait_cmd_clear(ec);
+	if (ret)
+		return ret;
+
+	ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA, did);
+	if (ret)
+		return ret;
+
+	ret = imanager2_smbus_i2c_set(ec, protocol, addr, cmd, wdata, wlen,
+				      rdata, rlen);
+	if (ret)
+		return ret;
+
+	ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD, protocol);
+	if (ret)
+		return ret;
+
+	ret = imanager2_wait_cmd_clear(ec);
+	if (ret)
+		return ret;
+
+	ret = imanager2_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS, &status);
+	if (ret)
+		return ret;
+	if (status != 0x80)
+		return -ENXIO;
+
+	return imanager2_smbus_i2c_get(ec, protocol, rdata, rlen);
+}
+
+/**
+ * imanager2_get_dynamic_table() - get dynamic table
+ * @ec:		EC device sturcture
+ * @did:	32 bytes array pointer, device id
+ * @hwpin:	32 bytes array pointer, HW pin
+ * @pol:	32 bytes array pointer, polarity
+ *
+ * In different project, EC has different hardware pin routing and control
+ * devices. To unite the control method in different project, we need a
+ * handshake method to translate mailbox command to hardware control. This
+ * handshake method is Dynamic Table method.
+ *
+ * Dynamic table is ec function list table in the project. This table can tell
+ * user how many functions on this EC and how to control. EC also uses this
+ * table to recognize the control function exists or not. Dynamic table contains
+ * 32 items. Each item has 3 byte data, device id, HW pin code, and polarity.
+ *
+ * Advantech EC command is 0x20.
+ */
+int imanager2_get_dynamic_table(struct imanager2 *ec, u8 *did, u8 *hwpin,
+				u8 *pol)
+{
+	int i, ret;
+	u8 status;
+
+	if (!did && !hwpin && !pol)
+		return -EINVAL;
+
+	if (did) {
+		ret = imanager2_wait_cmd_clear(ec);
+		if (ret)
+			return ret;
+
+		ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA,
+					      EC_DYNAMIC_DEVICE_ID);
+		if (ret)
+			return ret;
+		ret =
+		    imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD,
+					    EC_CMD_MAILBOX_READ_NYNAMIC_TABLE);
+		if (ret)
+			return ret;
+
+		ret = imanager2_wait_cmd_clear(ec);
+		if (ret)
+			return ret;
+
+		ret = imanager2_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS,
+					     &status);
+		if (ret)
+			return ret;
+		if (status != EC_MAILBOX_STATUS_SUCCESS)
+			return -ENXIO;
+
+		for (i = 0; i < EC_MAX_ITEM_NUM; i++) {
+			ret = imanager2_read_mailbox(ec,
+						     EC_MAILBOX_OFFSET_DAT(i),
+						     &did[i]);
+			if (ret)
+				return ret;
+		}
+	}
+
+	if (hwpin) {
+		ret = imanager2_wait_cmd_clear(ec);
+		if (ret != 0)
+			return ret;
+
+		ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA,
+					      EC_DYNAMIC_HW_PIN);
+		if (ret)
+			return ret;
+
+		ret =
+		    imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD,
+					    EC_CMD_MAILBOX_READ_NYNAMIC_TABLE);
+		if (ret)
+			return ret;
+
+		ret = imanager2_wait_cmd_clear(ec);
+		if (ret)
+			return ret;
+
+		ret = imanager2_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS,
+					     &status);
+		if (ret)
+			return ret;
+		if (status != EC_MAILBOX_STATUS_SUCCESS)
+			return -ENXIO;
+
+		for (i = 0; i < EC_MAX_ITEM_NUM; i++) {
+			ret = imanager2_read_mailbox(ec,
+						     EC_MAILBOX_OFFSET_DAT(i),
+						     &hwpin[i]);
+			if (ret)
+				return ret;
+		}
+	}
+
+	if (pol) {
+		ret = imanager2_wait_cmd_clear(ec);
+		if (ret)
+			return ret;
+
+		ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA,
+					      EC_DYNAMIC_POLARITY);
+		if (ret)
+			return ret;
+
+		ret = imanager2_write_mailbox(
+					ec, EC_MAILBOX_OFFSET_CMD,
+					EC_CMD_MAILBOX_READ_NYNAMIC_TABLE);
+		if (ret)
+			return ret;
+
+		ret = imanager2_wait_cmd_clear(ec);
+		if (ret)
+			return ret;
+
+		ret = imanager2_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS,
+					     &status);
+		if (ret)
+			return ret;
+		if (status != EC_MAILBOX_STATUS_SUCCESS)
+			return -ENXIO;
+
+		for (i = 0; i < EC_MAX_ITEM_NUM; i++) {
+			ret = imanager2_read_mailbox(ec,
+						     EC_MAILBOX_OFFSET_DAT(i),
+						     &pol[i]);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(imanager2_get_dynamic_table);
+
+/**
+ * imanager2_read_thermalzone() - read thermal zone data
+ * @ec:		EC device sturcture
+ * @zone:	zone number
+ * @smbid:	smbus device id
+ * @fanid:	related fan device id
+ * @buf:	thermal zone structure,
+ *		must bigger than structure length (more than 6 bytes).
+ * @len:	structure length
+ *
+ * EC has 4 thermal zones. User can define protocol, zone type, and related fan
+ * in each zone. There are 3 types protocols supported, I2C/SMBUS, SMLINK, and
+ * PECI 3.0. User can search dynamic table to get smbus device id to setup oem
+ * thermal zone protocol channel.
+ *
+ * User also can setup thermal zone related fan. The related fan will get the
+ * temperature from the zone to control speed. The thermal zone can be setup its
+ * zone type, like CPU temperature, system temperature, etc... This can help
+ * system to identify the temperature source and put the temperature into
+ * corresponding ACPI ram.
+ *
+ * Advantech EC command is 0x42.
+ */
+int imanager2_read_thermalzone(struct imanager2 *ec, u8 zone, u8 *smbid,
+			       u8 *fanid, u8 *buf, int *len)
+{
+	int ret, i;
+	u8 status, getlength;
+
+	if (!smbid && !fanid && !len)
+		return -EINVAL;
+
+	if (*len && !buf)
+		return -EINVAL;
+
+	ret = imanager2_wait_cmd_clear(ec);
+	if (ret)
+		return ret;
+
+	ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_PARA, zone);
+	if (ret)
+		return ret;
+	ret = imanager2_write_mailbox(ec, EC_MAILBOX_OFFSET_CMD,
+				      EC_CMD_MAILBOX_READ_THERMAL_SOURCE);
+	if (ret)
+		return ret;
+
+	ret = imanager2_wait_cmd_clear(ec);
+	if (ret)
+		return ret;
+
+	ret = imanager2_read_mailbox(ec, EC_MAILBOX_OFFSET_STATUS, &status);
+	if (ret)
+		return ret;
+	if (status != EC_MAILBOX_STATUS_SUCCESS)
+		return -ENXIO;
+
+	if (smbid) {
+		ret = imanager2_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x00),
+					     smbid);
+		if (ret)
+			return ret;
+	}
+
+	if (fanid) {
+		ret = imanager2_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x01),
+					     fanid);
+		if (ret)
+			return ret;
+	}
+
+	if (!len)
+		return 0;
+
+	ret = imanager2_read_mailbox(ec, EC_MAILBOX_OFFSET_DAT(0x2C),
+				     &getlength);
+	if (ret)
+		return ret;
+
+	if (*len > getlength)
+		*len = getlength;
+
+	for (i = 0; i < *len; i++) {
+		ret = imanager2_read_mailbox(ec,
+					     EC_MAILBOX_OFFSET_DAT(0x02 + i),
+					     &buf[i]);
+		if (ret)
+			return ret;
+	}
+
+	if (*len < getlength) {
+		*len = getlength;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(imanager2_read_thermalzone);
+
+/**
+ * imanager2_get_firmware_version_and_project_name() - get project name
+ * @ec:		EC device sturcture
+ * @prj_name:	EC project name array, size is 9 bytes (8 string length)
+ * @kernel_ver:	EC kernel version
+ * @chip_code:	EC chip Vendor code and chip code
+ * @proj_id:	EC project ID and type
+ * @proj_ver:	EC project version
+ *
+ * Get all EC firmware version and project name.
+ *
+ * Advantech EC command is 0xF0.
+ */
+int imanager2_get_firmware_version_and_project_name(struct imanager2 *ec,
+						    u8 *prj_name,
+						    u16 *kernel_ver,
+						    u16 *chip_code,
+						    u16 *proj_id,
+						    u16 *proj_ver)
+{
+	int ret, i;
+
+	if (!prj_name && !kernel_ver && !chip_code && !proj_id && !proj_ver)
+		return -EINVAL;
+
+	ret = imanager2_wait_cmd_clear(ec);
+	if (ret)
+		return ret;
+
+	ret = imanager2_write_mailbox(
+			ec, EC_MAILBOX_OFFSET_CMD,
+			EC_CMD_MAILBOX_GET_FIRMWARE_VERSION_AND_PROJECT_NAME);
+
+	ret = imanager2_wait_cmd_clear(ec);
+	if (ret)
+		return ret;
+
+	if (prj_name) {
+		for (i = 0; i < EC_MAX_LEN_PROJECT_NAME; i++) {
+			ret = imanager2_read_mailbox(ec,
+						     EC_MAILBOX_OFFSET_DAT(i),
+						     &prj_name[i]);
+			if (ret)
+				return ret;
+		}
+		prj_name[EC_MAX_LEN_PROJECT_NAME] = '\0';
+	}
+
+	if (kernel_ver) {
+		u8 *tmp = (u8 *)kernel_ver;
+		for (i = 0; i < 2; i++) {
+			ret = imanager2_read_mailbox(
+				ec,
+				EC_MAILBOX_OFFSET_DAT(0x09 + i),
+				&tmp[i]);
+			if (ret)
+				return ret;
+		}
+	}
+
+	if (chip_code) {
+		u8 *tmp = (u8 *)chip_code;
+		for (i = 0; i < 2; i++) {
+			ret = imanager2_read_mailbox(
+				ec,
+				EC_MAILBOX_OFFSET_DAT(0x0B + i),
+				&tmp[i]);
+			if (ret)
+				return ret;
+		}
+	}
+
+	if (proj_id) {
+		u8 *tmp = (u8 *)proj_id;
+		for (i = 0; i < 2; i++) {
+			ret = imanager2_read_mailbox(
+				ec,
+				EC_MAILBOX_OFFSET_DAT(0x0D + i),
+				&tmp[i]);
+			if (ret)
+				return ret;
+		}
+	}
+
+	if (proj_ver) {
+		u8 *tmp = (u8 *)proj_ver;
+		for (i = 0; i < 2; i++) {
+			ret = imanager2_read_mailbox(
+				ec,
+				EC_MAILBOX_OFFSET_DAT(0x0F + i),
+				&tmp[i]);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(imanager2_get_firmware_version_and_project_name);
+
+/* IO chennel access */
+int imanager2_io_read(u8 command, u8 offset, u8 *buf, u8 len)
+{
+	int ret, i;
+
+	if (!len)
+		return 0;
+
+	if (!buf)
+		return -EINVAL;
+
+	for (i = 0; i < len; i++) {
+		ret = outb_after_ibc(EC_IO_PORT_CMD, command);
+		if (ret)
+			return ret;
+
+		ret = outb_after_ibc(EC_IO_PORT_DATA, offset + i);
+		if (ret)
+			return ret;
+
+		ret = inb_after_obf(&buf[i]);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(imanager2_io_read);
+
+int imanager2_io_write(u8 command, u8 offset, u8 *buf, u8 len)
+{
+	int ret, i;
+
+	if (!buf && len)
+		return -EINVAL;
+
+	for (i = 0; i < len; i++) {
+		ret = outb_after_ibc(EC_IO_PORT_CMD, command);
+		if (ret)
+			return ret;
+
+		ret = outb_after_ibc(EC_IO_PORT_DATA, offset + i);
+		if (ret)
+			return ret;
+
+		ret = outb_after_ibc(EC_IO_PORT_DATA, buf[i]);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(imanager2_io_write);
+
+int imanager2_io_read_byte_without_offset(u8 command, u8 *value)
+{
+	int ret;
+
+	if (!value)
+		return -EINVAL;
+
+	ret = outb_after_ibc(EC_IO_PORT_CMD, command);
+	if (ret)
+		return ret;
+
+	return inb_after_obf(value);
+}
+EXPORT_SYMBOL(imanager2_io_read_byte_without_offset);
+
+static int imanager2_io_i2c_wait_protocol_clear(void)
+{
+	int i, ret;
+	u8 tmp;
+
+	for (i = 0; i < EC_MAX_TIMEOUT_COUNT; i++) {
+		ret =
+		    imanager2_io_read(EC_CMD_HWRAM_READ,
+				      EC_HWRAM_ADDR_SMB_PROTOCOL, &tmp, 1);
+		if (ret)
+			return ret;
+		if (tmp == 0x00)
+			return 0;
+
+		udelay(EC_UDELAY_TIME);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int imanager2_io_i2c_smbus(u8 pin, u8 protocol, u8 addr, u8 cmd,
+				  u8 *wdata, u8 wlen, u8 *rdata, u8 *rlen)
+{
+	int ret;
+	u8 tmp;
+
+	if (wlen && !wdata)
+		return -EINVAL;
+
+	switch (protocol) {
+	case EC_CMD_MALLBOX_SMBUS_WRITE_BLOCK:
+		ret = imanager2_io_write(EC_CMD_HWRAM_WRITE,
+					 EC_HWRAM_ADDR_SMB_BLOCKCNT,
+					 (u8 *)&wlen, 1);
+		if (ret)
+			return ret;
+	case EC_CMD_MALLBOX_SMBUS_WRITE_WORD:
+	case EC_CMD_MALLBOX_SMBUS_WRITE_BYTE:
+		ret =
+		    imanager2_io_write(EC_CMD_HWRAM_WRITE,
+				       EC_HWRAM_ADDR_SMB_CMD, &cmd, 1);
+		if (ret)
+			return ret;
+	case EC_CMD_MALLBOX_SMBUS_SEND_BYTE:
+		ret = imanager2_io_write(EC_CMD_HWRAM_WRITE,
+					 EC_HWRAM_ADDR_SMB_DATA(0), &wdata[0],
+					 wlen);
+		if (ret)
+			return ret;
+	case EC_CMD_MALLBOX_SMBUS_READ_BLOCK:
+	case EC_CMD_MALLBOX_SMBUS_READ_WORD:
+	case EC_CMD_MALLBOX_SMBUS_READ_BYTE:
+		ret =
+		    imanager2_io_write(EC_CMD_HWRAM_WRITE,
+				       EC_HWRAM_ADDR_SMB_CMD, &cmd, 1);
+		if (ret)
+			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 = imanager2_io_read(EC_CMD_SMB_INDEX, pin, &tmp, 1);
+	if (ret)
+		return ret;
+	if (tmp != pin)
+		return -ENXIO;
+
+	ret = imanager2_io_write(EC_CMD_HWRAM_WRITE, EC_HWRAM_ADDR_SMB_ADDRESS,
+				 &addr, 1);
+	if (ret)
+		return ret;
+
+	ret = imanager2_io_write(EC_CMD_HWRAM_WRITE, EC_HWRAM_ADDR_SMB_PROTOCOL,
+				 &protocol, 1);
+	if (ret)
+		return ret;
+
+	ret = imanager2_io_i2c_wait_protocol_clear();
+	if (ret)
+		return ret;
+
+	ret = imanager2_io_read(EC_CMD_HWRAM_READ, EC_HWRAM_ADDR_SMB_STATUS,
+				&tmp, 1);
+	if (ret)
+		return ret;
+	if (tmp != 0x80)
+		return -ENXIO;
+
+	if (rlen && *rlen) {
+		if (!rdata)
+			return -EINVAL;
+
+		switch (protocol) {
+		case EC_CMD_MALLBOX_SMBUS_READ_BLOCK:
+			ret = imanager2_io_read(EC_CMD_HWRAM_READ,
+						EC_HWRAM_ADDR_SMB_BLOCKCNT,
+						rlen, 1);
+			if (ret)
+				return ret;
+		case EC_CMD_MALLBOX_SMBUS_RECEIVE_BYTE:
+		case EC_CMD_MALLBOX_SMBUS_READ_BYTE:
+		case EC_CMD_MALLBOX_SMBUS_READ_WORD:
+			ret = imanager2_io_read(EC_CMD_HWRAM_READ,
+						EC_HWRAM_ADDR_SMB_DATA(0),
+						&rdata[0], *rlen);
+			if (ret)
+				return ret;
+		case EC_CMD_MALLBOX_SMBUS_READ_QUICK:
+			break;
+		}
+	}
+
+	return ret;
+}
+
+/* ITE Mailbox & IO chennel access*/
+int imanager2_acpiram_read_byte(struct imanager2 *ec, u8 addr, u8 *value)
+{
+	if (ec->flag & EC_FLAG_MAILBOX)
+		return imanager2_read_ram(ec, EC_RAM_BANK_ACPI, addr, value, 1);
+	else
+		return imanager2_io_read(EC_CMD_ACPIRAM_READ, addr, value, 1);
+}
+EXPORT_SYMBOL(imanager2_acpiram_read_byte);
+
+int imanager2_acpiram_write_byte(struct imanager2 *ec, u8 addr, u8 value)
+{
+	if (ec->flag & EC_FLAG_MAILBOX)
+		return imanager2_write_ram(ec, EC_RAM_BANK_ACPI, addr, &value,
+					   1);
+	else
+		return imanager2_io_write(EC_CMD_ACPIRAM_WRITE, addr, &value,
+					  1);
+}
+EXPORT_SYMBOL(imanager2_acpiram_write_byte);
+
+int imanager2_hwram_read_byte(struct imanager2 *ec, u8 addr, u8 *value)
+{
+	if (ec->flag & EC_FLAG_MAILBOX)
+		return imanager2_read_ram(ec, EC_RAM_BANK_HW, addr, value, 1);
+	else
+		return imanager2_io_read(EC_CMD_HWRAM_READ, addr, value, 1);
+}
+EXPORT_SYMBOL(imanager2_hwram_read_byte);
+
+int imanager2_hwram_write_byte(struct imanager2 *ec, u8 addr, u8 value)
+{
+	if (ec->flag & EC_FLAG_MAILBOX)
+		return imanager2_write_ram(ec, EC_RAM_BANK_HW, addr, &value, 1);
+	else
+		return imanager2_io_write(EC_CMD_HWRAM_WRITE, addr, &value, 1);
+}
+EXPORT_SYMBOL(imanager2_hwram_write_byte);
+
+int imanager2_smbus_transmit_routine(struct imanager2 *ec, u8 did, u8 protocol,
+				     u8 addr, u8 cmd, u8 *wdata, u8 wlen,
+				     u8 *rdata, u8 *rlen)
+{
+	if (ec->flag & EC_FLAG_MAILBOX) {
+		return imanager2_i2c_smbus(ec, did, protocol, addr, cmd,
+					   wdata, wlen, rdata, rlen);
+	} else {
+		u8 pin = ec->table.pinnum[ec->table.devid2itemnum[did]];
+		return imanager2_io_i2c_smbus(pin, protocol, addr, cmd, wdata,
+					      wlen, rdata, rlen);
+	}
+}
+EXPORT_SYMBOL(imanager2_smbus_transmit_routine);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 3/5] mfd: imanager2: Add Core supports for IT8516/18/28
  2014-06-23  8:39 [PATCH 1/5] mfd: imanager2: Add defines support for IT8516/18/28 Wei-Chun Pan
  2014-06-23  8:39 ` [PATCH 2/5] mfd: imanager2: Add Advantech EC APIs " Wei-Chun Pan
@ 2014-06-23  8:39 ` Wei-Chun Pan
  2014-06-23  8:39 ` [PATCH 4/5] hwmon: (imanager2) Add support " Wei-Chun Pan
  2014-06-23  8:39 ` [PATCH 5/5] i2c: imanager2: add " Wei-Chun Pan
  3 siblings, 0 replies; 5+ messages in thread
From: Wei-Chun Pan @ 2014-06-23  8:39 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

Signed-off-by: Wei-Chun Pan <weichun.pan@advantech.com.tw>
---
 drivers/mfd/Kconfig          |   6 +
 drivers/mfd/Makefile         |   2 +
 drivers/mfd/imanager2_core.c | 276 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 284 insertions(+)
 create mode 100644 drivers/mfd/imanager2_core.c

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 3383412..48b063f 100644
--- 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
index 2851275..10c64ae 100644
--- 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 100644
index 0000000..1a45f09
--- /dev/null
+++ b/drivers/mfd/imanager2_core.c
@@ -0,0 +1,276 @@
+/*
+ * 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/>.
+ */
+
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/advantech/imanager2.h>
+
+#define DRV_NAME	"imanager2"
+#define DRV_VERSION	"4.0.1"
+
+static struct mfd_cell imanager2_cells[] = {
+	{.name = "imanager2_hwm",},
+	{.name = "imanager2_i2c",},
+};
+
+static int imanager2_authentication(struct imanager2 *ec)
+{
+	u8 tmp;
+	int ret;
+
+	mutex_lock(&ec->lock);
+
+	if (inb(EC_IO_PORT_CMD) == 0xFF && inb(EC_IO_PORT_DATA) == 0xFF) {
+		ret = -ENODEV;
+		goto unlock;
+	}
+
+	if (inb(EC_IO_PORT_CMD) & OBF_MASK)
+		inb(EC_IO_PORT_DATA);	/* initial OBF */
+
+	if (outb_after_ibc(EC_IO_PORT_CMD, EC_CMD_AUTHENTICATION)) {
+		ret = -ENODEV;
+		goto unlock;
+	}
+
+	ret = inb_after_obf(&tmp);
+
+unlock:
+	mutex_unlock(&ec->lock);
+
+	if (ret)
+		return ret;
+
+	if (tmp != 0x95)
+		return -ENODEV;
+
+	return 0;
+}
+
+#define EC_ITE_CHIPID_H8	0x20
+#define EC_ITE_CHIPID_L8	0x21
+
+static int imanager2_get_chip_type(struct imanager2 *ec)
+{
+	mutex_lock(&ec->lock);
+
+	outb(EC_ITE_CHIPID_H8, EC_SIO_CMD);
+	ec->id = inb(EC_SIO_DATA) << 8;
+	outb(EC_ITE_CHIPID_L8, EC_SIO_CMD);
+	ec->id |= inb(EC_SIO_DATA);
+
+	mutex_unlock(&ec->lock);
+
+	switch (ec->id) {
+	case it8516:
+	case it8518:
+		ec->flag = EC_FLAG_IO;
+		break;
+	case it8528:
+		ec->flag |= EC_FLAG_IO_MAILBOX;
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/*
+ * EC provides IO channel and ITE mailbox ways to access mailbox. IO channel is
+ * a common way to access mailbox, but IET mailbox way is much faster than IO
+ * channel. We prefer ITE mailbox if firmware supports. Source kernel code
+ * X11_05 the first firmware version that supports ITE mailbox.
+ */
+#define EC_CHIPFW_SUPP_ITEMAILBOX	0x1105
+
+static int imanager2_get_info(struct imanager2 *ec)
+{
+	int ret;
+	u8 *tmp = (u8 *)&ec->info.version.kernel_ver;
+
+	mutex_lock(&ec->lock);
+
+	ret = imanager2_io_read(EC_CMD_ACPIRAM_READ,
+				EC_ACPIRAM_ADDR_KERNEL_MAJOR_VERSION, &tmp[0],
+				2);
+
+	if (ret)
+		goto unlock;
+
+	if (ec->info.version.kernel_ver >= EC_CHIPFW_SUPP_ITEMAILBOX) {
+		ec->flag |= EC_FLAG_MAILBOX;
+		ret = imanager2_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_FLAG_MAILBOX;
+		ret = imanager2_io_read(EC_CMD_ACPIRAM_READ,
+					EC_ACPIRAM_ADDR_KERNEL_MAJOR_VERSION,
+					&tmp[0], sizeof(struct ec_version));
+	}
+unlock:
+	mutex_unlock(&ec->lock);
+
+	return ret;
+}
+
+static int imanager2_device_initial_by_mailbox(struct imanager2 *ec)
+{
+	int i, ret;
+
+	mutex_lock(&ec->lock);
+	ret = imanager2_get_dynamic_table(ec, ec->table.devid,
+					  ec->table.pinnum, NULL);
+	mutex_unlock(&ec->lock);
+
+	if (ret)
+		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 0;
+}
+
+static int imanager2_device_initial_by_io(struct imanager2 *ec)
+{
+	int i, ret;
+	u8 tmp;
+
+	mutex_lock(&ec->lock);
+
+	for (i = 0; i < EC_MAX_ITEM_NUM; i++) {
+		ret = imanager2_io_read(EC_CMD_HWCTRLTABLE_INDEX, i, &tmp, 1);
+		if (ret)
+			break;
+		if (tmp == EC_TABLE_NOITEM)
+			break;
+
+		imanager2_io_read_byte_without_offset
+		    (EC_CMD_HWCTRLTABLE_GET_PIN_NUM, &ec->table.pinnum[i]);
+		imanager2_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;
+	}
+
+	mutex_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 imanager2_build_device_table(struct imanager2 *ec)
+{
+	memset(&ec->table.devid2itemnum[0], EC_TABLE_ITEM_UNUSED,
+	       ARRAY_SIZE(ec->table.devid2itemnum));
+
+	if (ec->flag & EC_FLAG_MAILBOX)
+		return imanager2_device_initial_by_mailbox(ec);
+	else
+		return imanager2_device_initial_by_io(ec);
+}
+
+static struct platform_device *ec_pdev;
+
+static int __init imanager2_mfd_device_init(void)
+{
+	struct imanager2 ec = { 0 };
+	int ret;
+
+	mutex_init(&ec.lock);
+
+	ret = imanager2_authentication(&ec);
+	if (ret)
+		return ret;
+
+	ret = imanager2_get_chip_type(&ec);
+	if (ret)
+		return ret;
+
+	ret = imanager2_get_info(&ec);
+	if (ret)
+		return ret;
+
+	ret = imanager2_build_device_table(&ec);
+	if (ret)
+		return ret;
+
+	ec_pdev = platform_device_alloc(DRV_NAME, PLATFORM_DEVID_AUTO);
+	if (!ec_pdev)
+		return -ENOMEM;
+
+	if (!devm_request_region(&ec_pdev->dev, EC_IO_PORT_DATA, 2, DRV_NAME))
+		return -EIO;
+
+	if (!devm_request_region(&ec_pdev->dev, EC_ITE_PORT_OFS, 2, DRV_NAME))
+		return -EIO;
+
+	ret = platform_device_add_data(ec_pdev, &ec, sizeof(struct imanager2));
+	if (ret)
+		goto exit_device_put;
+
+	ret = platform_device_add(ec_pdev);
+	if (ret)
+		goto exit_device_put;
+
+	ret = mfd_add_devices(&ec_pdev->dev, -1, imanager2_cells,
+			      ARRAY_SIZE(imanager2_cells), NULL, -1, NULL);
+	if (ret)
+		goto exit_device_unregister;
+
+	return 0;
+
+exit_device_unregister:
+	platform_device_unregister(ec_pdev);
+exit_device_put:
+	platform_device_put(ec_pdev);
+
+	return ret;
+}
+
+static void __exit imanager2_mfd_device_exit(void)
+{
+	mfd_remove_devices(&ec_pdev->dev);
+	platform_device_unregister(ec_pdev);
+}
+
+module_init(imanager2_mfd_device_init);
+module_exit(imanager2_mfd_device_exit);
+
+MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>");
+MODULE_DESCRIPTION("iManager2 platform device definitions v" DRV_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 4/5] hwmon: (imanager2) Add support for IT8516/18/28
  2014-06-23  8:39 [PATCH 1/5] mfd: imanager2: Add defines support for IT8516/18/28 Wei-Chun Pan
  2014-06-23  8:39 ` [PATCH 2/5] mfd: imanager2: Add Advantech EC APIs " Wei-Chun Pan
  2014-06-23  8:39 ` [PATCH 3/5] mfd: imanager2: Add Core supports " Wei-Chun Pan
@ 2014-06-23  8:39 ` Wei-Chun Pan
  2014-06-23  8:39 ` [PATCH 5/5] i2c: imanager2: add " Wei-Chun Pan
  3 siblings, 0 replies; 5+ messages in thread
From: Wei-Chun Pan @ 2014-06-23  8:39 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

Signed-off-by: Wei-Chun Pan <weichun.pan@advantech.com.tw>
---
 drivers/hwmon/Kconfig         |   5 +
 drivers/hwmon/Makefile        |   1 +
 drivers/hwmon/imanager2_hwm.c | 773 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 779 insertions(+)
 create mode 100644 drivers/hwmon/imanager2_hwm.c

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index bc196f4..7524fc3 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -39,6 +39,11 @@ config HWMON_DEBUG_CHIP
 
 comment "Native drivers"
 
+config SENSORS_IMANAGER2
+	tristate "Support for Advantech iManager2 EC H.W. Monitor"
+	select MFD_CORE
+	depends on MFD_IMANAGER2
+
 config SENSORS_AB8500
 	tristate "AB8500 thermal monitoring"
 	depends on AB8500_GPADC && AB8500_BM
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index c48f987..a2c8f07 100644
--- 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 100644
index 0000000..ae7508c
--- /dev/null
+++ b/drivers/hwmon/imanager2_hwm.c
@@ -0,0 +1,773 @@
+/*
+ * 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/>.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mfd/advantech/imanager2.h>
+
+#define DRV_NAME	"imanager2_hwm"
+#define DRV_VERSION	"4.0.1"
+
+/* ADC */
+#define EC_ADC_RESOLUTION_MAX	0x03FF	/* 10-bit */
+#define EC_ADC_VALUE_MAX	3000	/* max: 3000 mV or mA */
+
+/* Thermal */
+#define EC_THERMAL_ZONE_MAX	4
+
+enum thermaltype {
+	none,
+	sys1,
+	cpu,
+	sys3,
+	sys2,
+};
+
+struct ec_thermalzone {
+	u8 channel,
+	   addr,
+	   cmd,
+	   status,
+	   fancode,
+	   temp;
+};
+
+/* Tacho */
+#define EC_MAX_IO_FAN	3
+
+/* Voltage */
+struct volt_item {
+	u8 did;
+	const char *name;
+	int factor;
+	bool visible;
+};
+
+static struct volt_item ec_volt_table[] = {
+	{
+		.did = adcmosbat,
+		.name = "BAT CMOS",
+	},
+	{
+		.did = adcbat,
+		.name = "BAT",
+	},
+	{
+		.did = adc5vs0,
+		.name = "5V S0",
+	},
+	{
+		.did = adv5vs5,
+		.name = "5V S5",
+	},
+	{
+		.did = adc33vs0,
+		.name = "3V3 S0",
+	},
+	{
+		.did = adc33vs5,
+		.name = "3V3 S5",
+	},
+	{
+		.did = adv12vs0,
+		.name = "12V S0",
+	},
+	{
+		.did = adcvcorea,
+		.name = "Vcore A",
+	},
+	{
+		.did = adcvcoreb,
+		.name = "Vcore B",
+	},
+	{
+		.did = adcdc,
+		.name = "DC",
+	},
+	{
+		.did = adcdcstby,
+		.name = "DC Standby",
+	},
+	{
+		.did = adcdcother,
+		.name = "DC Other",
+	},
+};
+
+static int imanager2_volt_get_value_by_io(struct imanager2 *ec, int index,
+					  u8 *buf)
+{
+	u8 item = ec->table.devid2itemnum[ec_volt_table[index].did];
+	u8 pin = ec->table.pinnum[item];
+	u8 portnum;
+	int ret;
+
+	ret = imanager2_io_read(EC_CMD_ADC_INDEX, pin, &portnum, 1);
+	if (ret)
+		return ret;
+	if (portnum == EC_ERROR)
+		return -ENXIO;
+
+	ret = imanager2_io_read_byte_without_offset(EC_CMD_ADC_READ_LSB,
+						    &buf[1]);
+	if (ret)
+		return ret;
+
+	ret = imanager2_io_read_byte_without_offset(EC_CMD_ADC_READ_MSB,
+						    &buf[0]);
+
+	return ret;
+}
+
+static int imanager2_volt_get_value(struct imanager2 *ec, int index,
+				    u32 *volt_mvolt)
+{
+	int ret;
+	u8 tmp[2];
+
+	mutex_lock(&ec->lock);
+
+	if (ec->flag & EC_FLAG_MAILBOX)
+		ret = imanager2_read_mailbox_buffer(ec,
+						    EC_CMD_MAILBOX_READ_HW_PIN,
+						    ec_volt_table[index].did,
+						    tmp, 2);
+	else
+		ret = imanager2_volt_get_value_by_io(ec, index, tmp);
+
+	mutex_unlock(&ec->lock);
+
+	if (ret)
+		return ret;
+
+	*volt_mvolt = (((tmp[0] << 8) | tmp[1]) & EC_ADC_RESOLUTION_MAX) *
+		      ec_volt_table[index].factor *
+		      DIV_ROUND_CLOSEST(EC_ADC_VALUE_MAX,
+					EC_ADC_RESOLUTION_MAX);
+
+	return 0;
+}
+
+static void imanager2_volt_init(struct imanager2 *ec)
+{
+	u8 did;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ec_volt_table); i++) {
+		did = ec_volt_table[i].did;
+
+		if (ec->table.devid2itemnum[did] != EC_TABLE_ITEM_UNUSED) {
+			ec_volt_table[i].factor = 1;
+			ec_volt_table[i].visible = true;
+		} else if (ec->table.devid2itemnum[did + 1] !=
+			   EC_TABLE_ITEM_UNUSED) {
+			ec_volt_table[i].did += 1;
+			ec_volt_table[i].factor = 2;
+			ec_volt_table[i].visible = true;
+		} else if (ec->table.devid2itemnum[did + 2] !=
+			   EC_TABLE_ITEM_UNUSED) {
+			ec_volt_table[i].did += 2;
+			ec_volt_table[i].factor = 10;
+			ec_volt_table[i].visible = true;
+		} else {
+			ec_volt_table[i].visible = false;
+		}
+	}
+}
+
+static ssize_t show_in(struct device *dev, struct device_attribute *dev_attr,
+		       char *buf)
+{
+	struct imanager2 *ec = dev_get_drvdata(dev);
+	u32 val;
+	int ret = imanager2_volt_get_value(ec,
+					   to_sensor_dev_attr(dev_attr)->index,
+					   &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t show_in_label(struct device *dev,
+			     struct device_attribute *dev_attr, char *buf)
+{
+	return sprintf(buf, "%s\n",
+		       ec_volt_table[to_sensor_dev_attr(dev_attr)->index].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 *imanager2_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 imanager2_volt_mode(struct kobject *kobj, struct attribute *attr,
+				   int index)
+{
+	struct device_attribute *dev_attr;
+
+	dev_attr = container_of(attr, struct device_attribute, attr);
+	if (!ec_volt_table[to_sensor_dev_attr(dev_attr)->index].visible)
+		return 0;
+
+	return attr->mode;
+}
+
+static const struct attribute_group imanager2_volt_group = {
+	.attrs = imanager2_volt_attrs,
+	.is_visible = imanager2_volt_mode,
+};
+
+/* Current */
+struct curr_item {
+	const u8 did;
+	const char *name;
+	bool visible;
+};
+
+static struct curr_item ec_curr_table[] = {
+	{
+		.did = adccurrent,
+		.name = "IMON"
+	},
+};
+
+static int imanager2_curr_get_value(struct imanager2 *ec, int index,
+				    u32 *curr_mamp)
+{
+	int ret;
+	u8 tmp[5];
+	u16 value, factor;
+	u32 baseunit = 1;
+
+	mutex_lock(&ec->lock);
+	ret = imanager2_read_mailbox_buffer(ec, EC_CMD_MAILBOX_READ_HW_PIN,
+					    ec_curr_table[index].did, tmp,
+					    ARRAY_SIZE(tmp));
+	mutex_unlock(&ec->lock);
+
+	if (ret)
+		return ret;
+
+	value = ((tmp[0] << 8) | tmp[1]) & EC_ADC_RESOLUTION_MAX;
+	factor = (tmp[2] << 8) | tmp[3];
+
+	while (tmp[4]++)
+		baseunit *= 10;
+
+	*curr_mamp = DIV_ROUND_CLOSEST(value * baseunit * EC_ADC_VALUE_MAX,
+				       EC_ADC_RESOLUTION_MAX * factor);
+
+	return 0;
+}
+
+static void imanager2_curr_init(struct imanager2 *ec)
+{
+	u8 did;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ec_curr_table); i++) {
+		did = ec_curr_table[i].did;
+		if (ec->table.devid2itemnum[did] != EC_TABLE_ITEM_UNUSED)
+			ec_curr_table[i].visible = ec->flag & EC_FLAG_MAILBOX;
+		else
+			ec_curr_table[i].visible = false;
+	}
+}
+
+static ssize_t show_curr(struct device *dev, struct device_attribute *dev_attr,
+			 char *buf)
+{
+	struct imanager2 *ec = dev_get_drvdata(dev);
+	u32 val;
+	int ret = imanager2_curr_get_value(ec,
+					   to_sensor_dev_attr(dev_attr)->index,
+					   &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t show_curr_label(struct device *dev,
+			       struct device_attribute *dev_attr, char *buf)
+{
+	return sprintf(buf, "%s\n",
+		       ec_curr_table[to_sensor_dev_attr(dev_attr)->index].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 *imanager2_curr_attrs[] = {
+	&sensor_dev_attr_curr1_label.dev_attr.attr,
+	&sensor_dev_attr_curr1_input.dev_attr.attr,
+
+	NULL
+};
+
+static umode_t imanager2_curr_mode(struct kobject *kobj, struct attribute *attr,
+				   int index)
+{
+	struct device_attribute *dev_attr;
+
+	dev_attr = container_of(attr, struct device_attribute, attr);
+	if (!ec_curr_table[to_sensor_dev_attr(dev_attr)->index].visible)
+		return 0;
+
+	return attr->mode;
+}
+
+static const struct attribute_group imanager2_curr_group = {
+	.attrs = imanager2_curr_attrs,
+	.is_visible = imanager2_curr_mode,
+};
+
+/* Temperature */
+struct temp_item {
+	const char *name;
+	const u8 zonetype_value;
+	u8 zonetype_acpireg;
+	bool visible;
+};
+
+static struct temp_item ec_temp_table[] = {
+	{
+		.name = "Temp SYS",
+		.zonetype_value = sys1,
+		.zonetype_acpireg = EC_ACPIRAM_ADDR_LOCAL_TEMPERATURE(1)
+	},
+	{
+		.name = "Temp CPU",
+		.zonetype_value = cpu,
+		.zonetype_acpireg = EC_ACPIRAM_ADDR_REMOTE_TEMPERATURE(1)
+	},
+	{
+		.name = "Temp SYS3",
+		.zonetype_value = sys3,
+		.zonetype_acpireg = EC_ACPIRAM_ADDR_LOCAL_TEMPERATURE(2)
+	},
+	{
+		.name = "Temp SYS2",
+		.zonetype_value = sys2,
+		.zonetype_acpireg = EC_ACPIRAM_ADDR_REMOTE_TEMPERATURE(2)
+	},
+};
+
+static int imanager2_temp_get_value(struct imanager2 *ec, int index,
+				    s32 *temp_mcelsius)
+{
+	int ret;
+	s8 tmp;
+
+	mutex_lock(&ec->lock);
+	ret = imanager2_acpiram_read_byte(ec,
+					  ec_temp_table[index].zonetype_acpireg,
+					  (u8 *)&tmp);
+	mutex_unlock(&ec->lock);
+
+	if (ret)
+		return ret;
+
+	*temp_mcelsius = tmp * 1000;
+
+	return 0;
+}
+
+static void imanager2_temp_init(struct imanager2 *ec)
+{
+	int i, thm, ret;
+	u8 tmltype, smbid, fanid;
+	struct ec_thermalzone zone;
+
+	for (i = 0; i < ARRAY_SIZE(ec_temp_table); i++)
+		ec_temp_table[i].visible = false;
+
+	for (thm = 0; thm < EC_THERMAL_ZONE_MAX; thm++) {
+		mutex_lock(&ec->lock);
+
+		if (ec->flag & EC_FLAG_MAILBOX) {
+			int len = sizeof(struct ec_thermalzone);
+			ret = imanager2_read_thermalzone(ec, thm, &smbid,
+							 &fanid, (u8 *)&zone,
+							 &len);
+		} else {
+			ret = imanager2_io_read(
+				EC_CMD_HWRAM_READ,
+				EC_HWRAM_ADDR_THERMAL_SOURCE_SMB_STATUS(thm),
+				&zone.status, 1);
+		}
+
+		mutex_unlock(&ec->lock);
+
+		if (ret)
+			continue;
+
+		tmltype = (zone.status >> 5);
+
+		for (i = 0; i < ARRAY_SIZE(ec_temp_table); i++) {
+			if (tmltype == ec_temp_table[i].zonetype_value) {
+				ec_temp_table[i].visible = true;
+				break;
+			}
+		}
+	}
+}
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *dev_attr,
+			 char *buf)
+{
+	struct imanager2 *ec = dev_get_drvdata(dev);
+	s32 val;
+	int ret = imanager2_temp_get_value(ec,
+					   to_sensor_dev_attr(dev_attr)->index,
+					   &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t show_temp_label(struct device *dev,
+			       struct device_attribute *dev_attr, char *buf)
+{
+	return sprintf(buf, "%s\n",
+		       ec_temp_table[to_sensor_dev_attr(dev_attr)->index].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 *imanager2_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 imanager2_temp_mode(struct kobject *kobj, struct attribute *attr,
+				   int index)
+{
+	struct device_attribute *dev_attr;
+
+	dev_attr = container_of(attr, struct device_attribute, attr);
+	if (!ec_temp_table[to_sensor_dev_attr(dev_attr)->index].visible)
+		return 0;
+
+	return attr->mode;
+}
+
+static const struct attribute_group imanager2_temp_group = {
+	.attrs = imanager2_temp_attrs,
+	.is_visible = imanager2_temp_mode,
+};
+
+/* Fan Speed */
+struct fan_item {
+	const u8 did;
+	const char *name;
+	u8 fspeed_acpireg;
+	bool visible;
+};
+
+static struct fan_item ec_fan_table[] = {
+	{
+		.did = tacho0,
+		.name = "Fan CPU",
+		.fspeed_acpireg = 0,
+		.visible = false
+	},
+	{
+		.did = tacho1,
+		.name = "Fan SYS",
+		.fspeed_acpireg = 0,
+		.visible = false
+	},
+	{
+		.did = tacho2,
+		.name = "Fan SYS2",
+		.fspeed_acpireg = 0,
+		.visible = false
+	},
+};
+
+static int imanager2_fan_get_value(struct imanager2 *ec, int index,
+				   u32 *speed_rpm)
+{
+	int ret;
+	u8 tmp[2];
+
+	mutex_lock(&ec->lock);
+
+	if (ec->flag & EC_FLAG_MAILBOX)
+		ret =
+		    imanager2_read_mailbox_buffer(ec,
+						  EC_CMD_MAILBOX_READ_HW_PIN,
+						  ec_fan_table[index].did, tmp,
+						  2);
+	else
+		ret = imanager2_io_read(EC_CMD_ACPIRAM_READ,
+					ec_fan_table[index].fspeed_acpireg, tmp,
+					2);
+
+	mutex_unlock(&ec->lock);
+
+	if (ret)
+		return ret;
+
+	if (tmp[0] == 0xFF && tmp[1] == 0xFF)
+		return -ENODEV;
+
+	*speed_rpm = (tmp[0] << 8) | tmp[1];
+
+	return 0;
+}
+
+#define EC_FANCTROL_SPEEDSRC_MASK	0x30
+
+static int imanager2_fan_item_init_by_io(struct imanager2 *ec, int fnum)
+{
+	int i, ret;
+	u8 tmp;
+
+	mutex_lock(&ec->lock);
+	ret =
+	    imanager2_io_read(EC_CMD_HWRAM_READ,
+			      EC_HWRAM_ADDR_FAN_CONTROL(fnum), &tmp, 1);
+	mutex_unlock(&ec->lock);
+
+	if (ret)
+		return ret;
+
+	i = ((tmp & EC_FANCTROL_SPEEDSRC_MASK) >> 4) - 1;
+	if (i < 0)
+		return -ENODEV;
+
+	/* Unnecessary set again if it has been set. */
+	if (ec_fan_table[i].visible)
+		return 0;
+
+	ec_fan_table[i].fspeed_acpireg = EC_ACPIRAM_ADDR_FAN_SPEED_BASE(i);
+	ec_fan_table[i].visible = true;
+
+	return 0;
+}
+
+static void imanager2_fan_init(struct imanager2 *ec)
+{
+	int i;
+
+	if (ec->flag & EC_FLAG_MAILBOX) {
+		for (i = 0; i < ARRAY_SIZE(ec_fan_table); i++)
+			ec_fan_table[i].visible =
+			    ec->table.devid2itemnum[ec_fan_table[i].did] !=
+			    EC_TABLE_ITEM_UNUSED;
+	} else {
+		int fnum;
+
+		for (i = 0; i < ARRAY_SIZE(ec_fan_table); i++)
+			ec_fan_table[i].visible = false;
+
+		for (fnum = 0; fnum < EC_MAX_IO_FAN; fnum++) {
+			if (imanager2_fan_item_init_by_io(ec, fnum))
+				continue;
+		}
+	}
+}
+
+static ssize_t show_fan(struct device *dev, struct device_attribute *dev_attr,
+			char *buf)
+{
+	struct imanager2 *ec = dev_get_drvdata(dev);
+	u32 val;
+	int ret = imanager2_fan_get_value(ec,
+					  to_sensor_dev_attr(dev_attr)->index,
+					  &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t show_fan_label(struct device *dev,
+			      struct device_attribute *dev_attr, char *buf)
+{
+	return sprintf(buf, "%s\n",
+		       ec_fan_table[to_sensor_dev_attr(dev_attr)->index].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 *imanager2_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 imanager2_fan_mode(struct kobject *kobj, struct attribute *attr,
+				  int index)
+{
+	struct device_attribute *dev_attr;
+
+	dev_attr = container_of(attr, struct device_attribute, attr);
+	if (!ec_fan_table[to_sensor_dev_attr(dev_attr)->index].visible)
+		return 0;
+
+	return attr->mode;
+}
+
+static const struct attribute_group imanager2_fan_group = {
+	.attrs = imanager2_fan_attrs,
+	.is_visible = imanager2_fan_mode,
+};
+
+/* Module */
+static const struct attribute_group *imanager2_hwmon_groups[] = {
+	&imanager2_volt_group,
+	&imanager2_curr_group,
+	&imanager2_temp_group,
+	&imanager2_fan_group,
+	NULL
+};
+
+static int imanager2_hwmon_probe(struct platform_device *pdev)
+{
+	struct device *hwmon_dev;
+	struct imanager2 *hwmon_ec = pdev->dev.parent->platform_data;
+
+	imanager2_volt_init(hwmon_ec);
+	imanager2_curr_init(hwmon_ec);
+	imanager2_temp_init(hwmon_ec);
+	imanager2_fan_init(hwmon_ec);
+
+	hwmon_dev = devm_hwmon_device_register_with_groups(
+					&pdev->dev, "imanager2", hwmon_ec,
+					imanager2_hwmon_groups);
+
+	if (IS_ERR(hwmon_dev))
+		return PTR_ERR(hwmon_dev);
+
+	return 0;
+}
+
+static struct platform_driver imanager2_hwmon_driver = {
+	.probe = imanager2_hwmon_probe,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = DRV_NAME,
+	},
+};
+
+module_platform_driver(imanager2_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);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 5/5] i2c: imanager2: add support for IT8516/18/28
  2014-06-23  8:39 [PATCH 1/5] mfd: imanager2: Add defines support for IT8516/18/28 Wei-Chun Pan
                   ` (2 preceding siblings ...)
  2014-06-23  8:39 ` [PATCH 4/5] hwmon: (imanager2) Add support " Wei-Chun Pan
@ 2014-06-23  8:39 ` Wei-Chun Pan
  3 siblings, 0 replies; 5+ messages in thread
From: Wei-Chun Pan @ 2014-06-23  8:39 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

Signed-off-by: Wei-Chun Pan <weichun.pan@advantech.com.tw>
---
 drivers/i2c/busses/Kconfig         |   8 ++
 drivers/i2c/busses/Makefile        |   1 +
 drivers/i2c/busses/imanager2_i2c.c | 261 +++++++++++++++++++++++++++++++++++++
 3 files changed, 270 insertions(+)
 create mode 100644 drivers/i2c/busses/imanager2_i2c.c

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index c94db1c..8aad058 100644
--- 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
index 18d18ff..8a2a26b 100644
--- 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 100644
index 0000000..bb83155
--- /dev/null
+++ b/drivers/i2c/busses/imanager2_i2c.c
@@ -0,0 +1,261 @@
+/*
+ * 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/>.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/mfd/advantech/imanager2.h>
+
+#define DRV_NAME	"imanager2_i2c"
+#define DRV_VERSION	"4.0.1"
+
+#define EC_I2C_SMB_DEV_MAX	8
+
+struct imanager2_i2c {
+	struct i2c_adapter adapter;
+	struct imanager2 *ec;
+	enum ec_device_id did;
+};
+
+struct imanager2_i2c_drv {
+	struct imanager2_i2c *devs[EC_I2C_SMB_DEV_MAX];
+	int devcount;
+};
+
+static int imanager2_smb_access(struct i2c_adapter *adap, u16 addr,
+				unsigned short flags, char read_write,
+				u8 command, int size,
+				union i2c_smbus_data *data)
+{
+	struct imanager2_i2c *i2cdev = i2c_get_adapdata(adap);
+	int ret;
+	u8 protocol = 0, *wdata = NULL, wlen = 0, *rdata = NULL, *rlen = NULL;
+	u8 buf[EC_MAILBOX_SMBI2C_DATA_LENGTH];
+
+	addr <<= 1;
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		if (read_write == I2C_SMBUS_WRITE)
+			protocol = EC_CMD_MALLBOX_SMBUS_WRITE_QUICK;
+		else
+			protocol = EC_CMD_MALLBOX_SMBUS_READ_QUICK;
+		break;
+	case I2C_SMBUS_BYTE:
+		if (read_write == I2C_SMBUS_WRITE) {
+			protocol = EC_CMD_MALLBOX_SMBUS_SEND_BYTE;
+			wdata = &data->byte;
+			wlen = 1;
+		} else {
+			protocol = EC_CMD_MALLBOX_SMBUS_RECEIVE_BYTE;
+			rdata = &data->byte;
+			*rlen = 1;
+		}
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		if (read_write == I2C_SMBUS_WRITE) {
+			protocol = EC_CMD_MALLBOX_SMBUS_WRITE_BYTE;
+			wdata = &data->byte;
+			wlen = 1;
+		} else {
+			protocol = EC_CMD_MALLBOX_SMBUS_READ_BYTE;
+			rdata = &data->byte;
+			*rlen = 1;
+		}
+		break;
+	case I2C_SMBUS_WORD_DATA:
+		if (read_write == I2C_SMBUS_WRITE) {
+			protocol = EC_CMD_MALLBOX_SMBUS_WRITE_WORD;
+			wdata = (u8 *)&data->word;
+			wlen = 2;
+		} else {
+			protocol = EC_CMD_MALLBOX_SMBUS_READ_WORD;
+			rdata = (u8 *)&data->word;
+			*rlen = 2;
+		}
+		break;
+	case I2C_SMBUS_BLOCK_DATA:
+		if (read_write == I2C_SMBUS_WRITE) {
+			protocol = EC_CMD_MALLBOX_SMBUS_WRITE_BLOCK;
+			wdata = (u8 *)&data->block[1];
+			wlen = data->block[0];
+		} else {
+			protocol = EC_CMD_MALLBOX_SMBUS_READ_BLOCK;
+			rdata = (u8 *)&data->block[1];
+			*rlen = data->block[0];
+		}
+		break;
+	case I2C_SMBUS_I2C_BLOCK_DATA:
+		if (read_write == I2C_SMBUS_WRITE) {
+			int i;
+
+			buf[0] = command;
+			for (i = 0; i < data->block[0]; i++)
+				buf[i + 1] = data->block[i + 1];
+			protocol = EC_CMD_MALLBOX_I2C_WRITE_READ;
+			wdata = buf;
+			wlen = data->block[0] + 1;
+		} else {
+			int i;
+
+			buf[0] = command;
+			for (i = 0; i < data->block[0]; i++)
+				buf[i + 1] = data->block[i + 1];
+
+			protocol = EC_CMD_MALLBOX_I2C_READ_WRITE;
+			rdata = buf;
+			*rlen = data->block[0] + 1;
+		}
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	mutex_lock(&i2cdev->ec->lock);
+	ret = imanager2_smbus_transmit_routine(i2cdev->ec, i2cdev->did,
+					       protocol, (u8) addr, command,
+					       wdata, wlen, rdata, rlen);
+	mutex_unlock(&i2cdev->ec->lock);
+
+	return ret;
+}
+
+static u32 imanager2_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 imanager2_algorithm = {
+	.smbus_xfer = imanager2_smb_access,
+	.functionality = imanager2_smb_i2c_func
+};
+
+static u32 imanager2_smb_io_i2c_func(struct i2c_adapter *adap)
+{
+	/* IO chennel access way has no I2C block protocol */
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	       I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static const struct i2c_algorithm imanager2_io_algorithm = {
+	.smbus_xfer = imanager2_smb_access,
+	.functionality = imanager2_smb_io_i2c_func
+};
+
+struct i2c_item {
+	const u8 did;
+	const char *name;
+};
+
+static struct i2c_item imanager2_i2c_table[] = {
+	{smboem0, "iManager2 SMBus0"},
+	{smboem1, "iManager2 SMBus1"},
+	{smboem2, "iManager2 SMBus2"},
+	{smbthermal0, "iManager2 Thermal0"},
+	{smbthermal1, "iManager2 Thermal1"},
+	{i2coem, "iManager2 I2COEM"}
+};
+
+static struct imanager2_i2c *add_i2c_adapter(struct device *dev, int index)
+{
+	struct imanager2_i2c *i2cdev;
+
+	i2cdev = devm_kzalloc(dev, sizeof(struct imanager2_i2c), GFP_KERNEL);
+	if (!i2cdev)
+		return NULL;
+
+	i2cdev->did = imanager2_i2c_table[index].did;
+	i2cdev->ec = dev->parent->platform_data;
+	i2cdev->adapter.owner = THIS_MODULE;
+	i2cdev->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+	if (i2cdev->ec->flag & EC_FLAG_MAILBOX)
+		i2cdev->adapter.algo = &imanager2_algorithm;
+	else
+		i2cdev->adapter.algo = &imanager2_io_algorithm;
+	i2cdev->adapter.dev.parent = dev;
+	i2cdev->adapter.retries = 3;
+	i2c_set_adapdata(&i2cdev->adapter, i2cdev);
+
+	snprintf(i2cdev->adapter.name, sizeof(i2cdev->adapter.name),
+		 imanager2_i2c_table[index].name);
+
+	if (i2c_add_adapter(&i2cdev->adapter))
+		return NULL;
+
+	return i2cdev;
+}
+
+static int imanager2_i2c_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct imanager2 *ec = dev->parent->platform_data;
+	struct imanager2_i2c_drv *drvdata;
+	int i;
+
+	drvdata =
+	    devm_kzalloc(dev, sizeof(struct imanager2_i2c_drv), GFP_KERNEL);
+
+	if (!drvdata)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, drvdata);
+
+	drvdata->devcount = 0;
+	for (i = 0; i < ARRAY_SIZE(imanager2_i2c_table); i++)
+		if (ec->table.devid2itemnum[imanager2_i2c_table[i].did] !=
+		    EC_TABLE_ITEM_UNUSED) {
+			struct imanager2_i2c *i2cdev = add_i2c_adapter(dev, i);
+			if (i2cdev) {
+				drvdata->devs[drvdata->devcount] = i2cdev;
+				drvdata->devcount++;
+			}
+		}
+
+	return 0;
+}
+
+static int imanager2_i2c_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct imanager2_i2c_drv *drvdata = dev_get_drvdata(dev);
+	int i;
+
+	for (i = 0; i < drvdata->devcount; i++)
+		i2c_del_adapter(&drvdata->devs[i]->adapter);
+
+	return 0;
+}
+
+static struct platform_driver imanager2_i2c_driver = {
+	.probe = imanager2_i2c_probe,
+	.remove = imanager2_i2c_remove,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = DRV_NAME,
+	},
+};
+
+module_platform_driver(imanager2_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);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2014-06-23  9:13 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-06-23  8:39 [PATCH 1/5] mfd: imanager2: Add defines support for IT8516/18/28 Wei-Chun Pan
2014-06-23  8:39 ` [PATCH 2/5] mfd: imanager2: Add Advantech EC APIs " Wei-Chun Pan
2014-06-23  8:39 ` [PATCH 3/5] mfd: imanager2: Add Core supports " Wei-Chun Pan
2014-06-23  8:39 ` [PATCH 4/5] hwmon: (imanager2) Add support " Wei-Chun Pan
2014-06-23  8:39 ` [PATCH 5/5] i2c: imanager2: add " Wei-Chun Pan

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.