All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH] Topcliff PHUB: Generate PacketHub driver
@ 2010-06-22  5:33 Masayuki Ohtak
  2010-06-22 10:33 ` Masayuki Ohtak
                   ` (10 more replies)
  0 siblings, 11 replies; 46+ messages in thread
From: Masayuki Ohtak @ 2010-06-22  5:33 UTC (permalink / raw)
  To: Arnd Bergmann, Wang, Yong Y, Wang Qi"
  Cc: Intel OTC", Andrew", Alan Cox, LKML

Hi, Arnd and Yong Y

I have released Phub patch file.
Please confirm below.

Thanks, Ohtake.

Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
---
 drivers/char/Kconfig             |   12 +
 drivers/char/Makefile            |    2 +
 drivers/char/pch_phub/Makefile   |    7 +
 drivers/char/pch_phub/pch_phub.c |  937 +++++++++++++++++++++++
 drivers/char/pch_phub/pch_phub.h |   58 ++
 5 files changed, 1016 insertions(+), 0 deletions(-)
 create mode 100644 drivers/char/pch_phub/Makefile
 create mode 100755 drivers/char/pch_phub/pch_phub.c
 create mode 100755 drivers/char/pch_phub/pch_phub.h

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index e023682..7ff728a 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -4,6 +4,18 @@
 
 menu "Character devices"
 
+config PCH_PHUB
+	tristate "PCH PHUB"
+	depends on PCI
+	help
+	  If you say yes to this option, support will be included for the
+	  PCH Packet Hub Host controller.
+	  This driver is for PCH Packet hub driver for Topcliff.
+	  This driver is integrated as built-in.
+	  This driver can access GbE MAC address.
+	  This driver can access HW register.
+	  You can also be integrated as module.
+
 config VT
 	bool "Virtual terminal" if EMBEDDED
 	depends on !S390
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index f957edf..1e3eb6c 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -111,6 +111,8 @@ obj-$(CONFIG_PS3_FLASH)		+= ps3flash.o
 obj-$(CONFIG_JS_RTC)		+= js-rtc.o
 js-rtc-y = rtc.o
 
+obj-$(CONFIG_PCH_PHUB)	+= pch_phub/
+
 # Files generated that shall be removed upon make clean
 clean-files := consolemap_deftbl.c defkeymap.c
 
diff --git a/drivers/char/pch_phub/Makefile b/drivers/char/pch_phub/Makefile
new file mode 100644
index 0000000..51ce785
--- /dev/null
+++ b/drivers/char/pch_phub/Makefile
@@ -0,0 +1,7 @@
+ifeq ($(CONFIG_PHUB_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+
+obj-$(CONFIG_PCH_PHUB) += pch_phub_drv.o
+
+pch_phub_drv-objs := pch_phub.o
diff --git a/drivers/char/pch_phub/pch_phub.c b/drivers/char/pch_phub/pch_phub.c
new file mode 100755
index 0000000..6993182
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.c
@@ -0,0 +1,937 @@
+/*!
+ * @file pch_phub.c
+ * @brief Provides all the implementation of the interfaces pertaining to
+ *		the Packet Hub module.
+ * @version 1.0.0.0
+ * @section
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+/*
+ * History:
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * created:
+ *	OKI SEMICONDUCTOR 04/14/2010
+ * modified:
+ *
+ */
+
+/* includes */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+
+#include "pch_phub.h"
+
+/*--------------------------------------------
+	macros
+--------------------------------------------*/
+/* Status Register offset */
+#define PHUB_STATUS 0x00
+/* Control Register offset */
+#define PHUB_CONTROL 0x04
+/* Time out value for Status Register */
+#define PHUB_TIMEOUT 0x05
+/* Enabling for writing ROM */
+#define PCH_PHUB_ROM_WRITE_ENABLE 0x01
+/* Disabling for writing ROM */
+#define PCH_PHUB_ROM_WRITE_DISABLE 0x00
+/* ROM data area start address offset */
+#define PCH_PHUB_ROM_START_ADDR 0x14
+/* MAX number of INT_REDUCE_CONTROL registers */
+#define MAX_NUM_INT_REDUCE_CONTROL_REG 128
+
+#define PCI_DEVICE_ID_PCH1_PHUB 0x8801
+
+#define PCH_MINOR_NOS 1
+#define CLKCFG_CAN_50MHZ 0x12000000
+#define CLKCFG_CANCLK_MASK 0xFF000000
+
+#define MODULE_NAME "pch_phub"
+
+/*--------------------------------------------
+	global variables
+--------------------------------------------*/
+/**
+ * struct pch_phub_reg - PHUB register structure
+ * @phub_id_reg:		 PHUB_ID register val
+ * @q_pri_val_reg:		 QUEUE_PRI_VAL register val
+ * @rc_q_maxsize_reg:	 RC_QUEUE_MAXSIZE register val
+ * @bri_q_maxsize_reg:	 BRI_QUEUE_MAXSIZE register val
+ * @comp_resp_timeout_reg:	 COMP_RESP_TIMEOUT register val
+ * @bus_slave_control_reg:	 BUS_SLAVE_CONTROL_REG register val
+ * @deadlock_avoid_type_reg:  DEADLOCK_AVOID_TYPE register val
+ * @intpin_reg_wpermit_reg0:  INTPIN_REG_WPERMIT register 0 val
+ * @intpin_reg_wpermit_reg1:  INTPIN_REG_WPERMIT register 1 val
+ * @intpin_reg_wpermit_reg2:  INTPIN_REG_WPERMIT register 2 val
+ * @intpin_reg_wpermit_reg3:  INTPIN_REG_WPERMIT register 3 val
+ * @int_reduce_control_reg:	 INT_REDUCE_CONTROL registers val
+ * @clkcfg_reg:		 CLK CFG register val
+ * @pch_phub_base_address:  register base address
+ * @pch_phub_extrom_base_address:  external rom base address
+ * @pch_phub_suspended: PHUB status val
+ */
+struct pch_phub_reg {
+	unsigned int phub_id_reg;
+	unsigned int q_pri_val_reg;
+	unsigned int rc_q_maxsize_reg;
+	unsigned int bri_q_maxsize_reg;
+	unsigned int comp_resp_timeout_reg;
+	unsigned int bus_slave_control_reg;
+	unsigned int deadlock_avoid_type_reg;
+	unsigned int intpin_reg_wpermit_reg0;
+	unsigned int intpin_reg_wpermit_reg1;
+	unsigned int intpin_reg_wpermit_reg2;
+	unsigned int intpin_reg_wpermit_reg3;
+	unsigned int int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG];
+	unsigned int clkcfg_reg;
+	void __iomem *pch_phub_base_address;
+	void __iomem *pch_phub_extrom_base_address;
+	int pch_phub_suspended;
+} pch_phub_reg;
+
+static DEFINE_MUTEX(pch_phub_ioctl_mutex);
+
+/* ToDo: major number allocation via module parameter */
+static dev_t pch_phub_dev_no;
+static int pch_phub_major_no;
+static struct cdev pch_phub_dev;
+
+/*--------------------------------------------
+	functions implementations
+--------------------------------------------*/
+/** pch_phub_read_modify_write_reg - Implements the functionality of reading,
+ * 						modifying and writing register.
+ *  @reg_addr_offset:  Contains the register offset address value
+ *  @data:             Contains the writing value
+ *  @mask:             Contains the mask value
+ */
+static void pch_phub_read_modify_write_reg(unsigned int reg_addr_offset,
+				       unsigned int data, unsigned int mask)
+{
+	void __iomem *reg_addr = pch_phub_reg.pch_phub_base_address +\
+							 reg_addr_offset;
+	iowrite32(((ioread32(reg_addr) & ~mask)) | data, reg_addr);
+	return;
+}
+
+
+/** pch_phub_save_reg_conf - saves register configuration
+ */
+static void pch_phub_save_reg_conf(struct pci_dev *pdev)
+{
+	unsigned int i = 0;
+	void __iomem *p = pch_phub_reg.pch_phub_base_address;
+
+	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf ENTRY\n");
+	/* to store contents of PHUB_ID register */
+	pch_phub_reg.phub_id_reg = ioread32(p + PCH_PHUB_PHUB_ID_REG);
+	/* to store contents of QUEUE_PRI_VAL register */
+	pch_phub_reg.q_pri_val_reg = ioread32(p + PCH_PHUB_QUEUE_PRI_VAL_REG);
+	/* to store contents of RC_QUEUE_MAXSIZE register */
+	pch_phub_reg.rc_q_maxsize_reg =
+	    ioread32(p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+	/* to store contents of BRI_QUEUE_MAXSIZE register */
+	pch_phub_reg.bri_q_maxsize_reg =
+	    ioread32(p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+	/* to store contents of COMP_RESP_TIMEOUT register */
+	pch_phub_reg.comp_resp_timeout_reg =
+	    ioread32(p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+	/* to store contents of BUS_SLAVE_CONTROL_REG register */
+	pch_phub_reg.bus_slave_control_reg =
+	    ioread32(p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+	/* to store contents of DEADLOCK_AVOID_TYPE register */
+	pch_phub_reg.deadlock_avoid_type_reg =
+	    ioread32(p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+	/* to store contents of INTPIN_REG_WPERMIT register 0 */
+	pch_phub_reg.intpin_reg_wpermit_reg0 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+	/* to store contents of INTPIN_REG_WPERMIT register 1 */
+	pch_phub_reg.intpin_reg_wpermit_reg1 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+	/* to store contents of INTPIN_REG_WPERMIT register 2 */
+	pch_phub_reg.intpin_reg_wpermit_reg2 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+	/* to store contents of INTPIN_REG_WPERMIT register 3 */
+	pch_phub_reg.intpin_reg_wpermit_reg3 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
+	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+		"pch_phub_reg.phub_id_reg=%x, "
+		"pch_phub_reg.q_pri_val_reg=%x, "
+		"pch_phub_reg.rc_q_maxsize_reg=%x, "
+		"pch_phub_reg.bri_q_maxsize_reg=%x, "
+		"pch_phub_reg.comp_resp_timeout_reg=%x, "
+		"pch_phub_reg.bus_slave_control_reg=%x, "
+		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n",
+		pch_phub_reg.phub_id_reg,
+		pch_phub_reg.q_pri_val_reg,
+		pch_phub_reg.rc_q_maxsize_reg,
+		pch_phub_reg.bri_q_maxsize_reg,
+		pch_phub_reg.comp_resp_timeout_reg,
+		pch_phub_reg.bus_slave_control_reg,
+		pch_phub_reg.deadlock_avoid_type_reg,
+		pch_phub_reg.intpin_reg_wpermit_reg0,
+		pch_phub_reg.intpin_reg_wpermit_reg1,
+		pch_phub_reg.intpin_reg_wpermit_reg2,
+		pch_phub_reg.intpin_reg_wpermit_reg3);
+	/* to store contents of INT_REDUCE_CONTROL registers */
+	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
+		pch_phub_reg.int_reduce_control_reg[i] =
+		    ioread32(p +
+			PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
+		dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
+			i, pch_phub_reg.int_reduce_control_reg[i]);
+	}
+	/* save clk cfg register */
+	pch_phub_reg.clkcfg_reg = ioread32(p + CLKCFG_REG_OFFSET);
+
+	return;
+}
+
+/** pch_phub_restore_reg_conf - restore register configuration
+ */
+
+static void pch_phub_restore_reg_conf(struct pci_dev *pdev)
+{
+	unsigned int i;
+	void __iomem *p = pch_phub_reg.pch_phub_base_address;
+
+	dev_dbg(&pdev->dev, "pch_phub_restore_reg_conf ENTRY\n");
+	/* to store contents of PHUB_ID register */
+	iowrite32(pch_phub_reg.phub_id_reg, p + PCH_PHUB_PHUB_ID_REG);
+	/* to store contents of QUEUE_PRI_VAL register */
+	iowrite32(pch_phub_reg.q_pri_val_reg, p + PCH_PHUB_QUEUE_PRI_VAL_REG);
+	/* to store contents of RC_QUEUE_MAXSIZE register */
+	iowrite32(pch_phub_reg.rc_q_maxsize_reg,
+					p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+	/* to store contents of BRI_QUEUE_MAXSIZE register */
+	iowrite32(pch_phub_reg.bri_q_maxsize_reg,
+					p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+	/* to store contents of COMP_RESP_TIMEOUT register */
+	iowrite32(pch_phub_reg.comp_resp_timeout_reg,
+					p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+	/* to store contents of BUS_SLAVE_CONTROL_REG register */
+	iowrite32(pch_phub_reg.bus_slave_control_reg,
+					p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+	/* to store contents of DEADLOCK_AVOID_TYPE register */
+	iowrite32(pch_phub_reg.deadlock_avoid_type_reg,
+					p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+	/* to store contents of INTPIN_REG_WPERMIT register 0 */
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg0,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+	/* to store contents of INTPIN_REG_WPERMIT register 1 */
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg1,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+	/* to store contents of INTPIN_REG_WPERMIT register 2 */
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg2,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+	/* to store contents of INTPIN_REG_WPERMIT register 3 */
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg3,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
+	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+		"pch_phub_reg.phub_id_reg=%x, "
+		"pch_phub_reg.q_pri_val_reg=%x, "
+		"pch_phub_reg.rc_q_maxsize_reg=%x, "
+		"pch_phub_reg.bri_q_maxsize_reg=%x, "
+		"pch_phub_reg.comp_resp_timeout_reg=%x, "
+		"pch_phub_reg.bus_slave_control_reg=%x, "
+		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n",
+		pch_phub_reg.phub_id_reg,
+		pch_phub_reg.q_pri_val_reg,
+		pch_phub_reg.rc_q_maxsize_reg,
+		pch_phub_reg.bri_q_maxsize_reg,
+		pch_phub_reg.comp_resp_timeout_reg,
+		pch_phub_reg.bus_slave_control_reg,
+		pch_phub_reg.deadlock_avoid_type_reg,
+		pch_phub_reg.intpin_reg_wpermit_reg0,
+		pch_phub_reg.intpin_reg_wpermit_reg1,
+		pch_phub_reg.intpin_reg_wpermit_reg2,
+		pch_phub_reg.intpin_reg_wpermit_reg3);
+	/* to store contents of INT_REDUCE_CONTROL register */
+	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
+		iowrite32(pch_phub_reg.int_reduce_control_reg[i],
+			p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
+		dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
+			i, pch_phub_reg.int_reduce_control_reg[i]);
+	}
+
+	/*restore the clock config reg */
+	iowrite32(pch_phub_reg.clkcfg_reg, p + CLKCFG_REG_OFFSET);
+
+	return;
+}
+
+/** pch_phub_read_serial_rom - Implements the functionality of reading Serial
+ *  									 ROM.
+ *  @offset_address: Contains the Serial ROM address offset value
+ *  @data: Contains the Serial ROM value
+ */
+static int pch_phub_read_serial_rom(unsigned int offset_address,
+							 unsigned char *data)
+{
+	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +\
+								 offset_address;
+
+	*data = ioread8(mem_addr);
+
+	return 0;
+}
+
+/** pch_phub_write_serial_rom - Implements the functionality of writing Serial
+ *  									 ROM.
+ *  @offset_address: Contains the Serial ROM address offset value
+ *  @data: Contains the Serial ROM value
+ */
+static int pch_phub_write_serial_rom(unsigned int offset_address,
+							 unsigned char data)
+{
+	int retval = 0;
+	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +\
+						 (offset_address & 0xFFFFFFFC);
+	int i = 0;
+	unsigned int word_data = 0;
+
+	iowrite32(PCH_PHUB_ROM_WRITE_ENABLE,
+			pch_phub_reg.pch_phub_extrom_base_address +\
+								 PHUB_CONTROL);
+
+	word_data = ioread32(mem_addr);
+
+	switch (offset_address % 4) {
+	case 0:
+		word_data &= 0xFFFFFF00;
+		iowrite32((word_data | (unsigned int)data),
+						mem_addr);
+		break;
+	case 1:
+		word_data &= 0xFFFF00FF;
+		iowrite32((word_data | ((unsigned int)data << 8)),
+						mem_addr);
+		break;
+	case 2:
+		word_data &= 0xFF00FFFF;
+		iowrite32((word_data | ((unsigned int)data << 16)),
+						mem_addr);
+		break;
+	case 3:
+		word_data &= 0x00FFFFFF;
+		iowrite32((word_data | ((unsigned int)data << 24)),
+						mem_addr);
+		break;
+	}
+	while (0x00 !=
+	       ioread8(pch_phub_reg.pch_phub_extrom_base_address +\
+							 PHUB_STATUS)) {
+		msleep(1);
+		if (PHUB_TIMEOUT == i) {
+			retval = -EPERM;
+			break;
+		}
+		i++;
+	}
+
+	iowrite32(PCH_PHUB_ROM_WRITE_DISABLE,
+			pch_phub_reg.pch_phub_extrom_base_address +\
+								 PHUB_CONTROL);
+
+	return retval;
+}
+
+/** pch_phub_read_serial_rom_val - Implements the functionality of reading
+ *  							 Serial ROM value.
+ *  @offset_address: Contains the Serial ROM address offset value
+ *  @data: Contains the Serial ROM value
+ */
+static int pch_phub_read_serial_rom_val(unsigned int offset_address,
+							 unsigned char *data)
+{
+	int retval = 0;
+	unsigned int mem_addr;
+
+	mem_addr = (offset_address / 4 * 8) + 3 -
+			(offset_address % 4) + PCH_PHUB_ROM_START_ADDR;
+	retval = pch_phub_read_serial_rom(mem_addr, data);
+
+	return retval;
+}
+
+/** pch_phub_write_serial_rom_val - Implements the functionality of writing
+ *  							 Serial ROM value.
+ *  @offset_address: Contains the Serial ROM address offset value
+ *  @data: Contains the Serial ROM value
+ */
+static int pch_phub_write_serial_rom_val(unsigned int offset_address,
+				     unsigned char data)
+{
+	int retval = 0;
+	unsigned int mem_addr;
+
+	mem_addr =
+	    (offset_address / 4 * 8) + 3 - (offset_address % 4) +
+	    PCH_PHUB_ROM_START_ADDR;
+	retval = pch_phub_write_serial_rom(mem_addr, data);
+
+	return retval;
+}
+
+/** pch_phub_gbe_serial_rom_conf - makes Serial ROM header format configuration
+ *  						for Gigabit Ethernet MAC address
+ */
+static int pch_phub_gbe_serial_rom_conf(void)
+{
+	int retval = 0;
+
+	retval |= pch_phub_write_serial_rom(0x0b, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x0a, 0x10);
+	retval |= pch_phub_write_serial_rom(0x09, 0x01);
+	retval |= pch_phub_write_serial_rom(0x08, 0x02);
+
+	retval |= pch_phub_write_serial_rom(0x0f, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0e, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0d, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0c, 0x80);
+
+	retval |= pch_phub_write_serial_rom(0x13, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x12, 0x10);
+	retval |= pch_phub_write_serial_rom(0x11, 0x01);
+	retval |= pch_phub_write_serial_rom(0x10, 0x18);
+
+	retval |= pch_phub_write_serial_rom(0x1b, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x1a, 0x10);
+	retval |= pch_phub_write_serial_rom(0x19, 0x01);
+	retval |= pch_phub_write_serial_rom(0x18, 0x19);
+
+	retval |= pch_phub_write_serial_rom(0x23, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x22, 0x10);
+	retval |= pch_phub_write_serial_rom(0x21, 0x01);
+	retval |= pch_phub_write_serial_rom(0x20, 0x3a);
+
+	retval |= pch_phub_write_serial_rom(0x27, 0x01);
+	retval |= pch_phub_write_serial_rom(0x26, 0x00);
+	retval |= pch_phub_write_serial_rom(0x25, 0x00);
+	retval |= pch_phub_write_serial_rom(0x24, 0x00);
+
+	return retval;
+}
+
+/** pch_phub_read_gbe_mac_addr - Contains the Gigabit Ethernet MAC address
+ *  								offset value
+ *  @offset_address: Gigabit Ethernet MAC address offset value
+ *  @data: Contains the Gigabit Ethernet MAC address value
+ */
+static int pch_phub_read_gbe_mac_addr(unsigned int offset_address,
+							 unsigned char *data)
+{
+	int retval = 0;
+
+	retval = pch_phub_read_serial_rom_val(offset_address, data);
+
+	return retval;
+}
+
+/** pch_phub_write_gbe_mac_addr - Write MAC address
+ *  @offset_address: Contains the Gigabit Ethernet MAC address offset value
+ *  @data: Contains the Gigabit Ethernet MAC address value
+ */
+static int pch_phub_write_gbe_mac_addr(unsigned int offset_address,
+				   unsigned char data)
+{
+	int retval = 0;
+
+	retval = pch_phub_gbe_serial_rom_conf();
+	retval |= pch_phub_write_serial_rom_val(offset_address, data);
+
+	return retval;
+}
+
+/** pch_phub_read - Implements the read functionality of the Packet Hub module.
+ *  @file: Contains the reference of the file structure
+ *  @buf: Usermode buffer pointer
+ *  @size: Usermode buffer size
+ *  @ppos: Contains the reference of the file structure
+ */
+
+static ssize_t pch_phub_read(struct file *file, char __user *buf, size_t size,
+								 loff_t *ppos)
+{
+	unsigned int rom_signature = 0;
+	unsigned char rom_length;
+	int ret_value;
+	unsigned int tmp;
+	unsigned char data;
+	unsigned int addr_offset = 0;
+	unsigned int orom_size;
+	loff_t pos = *ppos;
+
+	if (pos < 0)
+		return -EINVAL;
+
+	/*Get Rom signature*/
+	pch_phub_read_serial_rom(0x80, (unsigned char *)&rom_signature);
+	pch_phub_read_serial_rom(0x81, (unsigned char *)&tmp);
+	rom_signature |= (tmp & 0xff) << 8;
+	if (rom_signature == 0xAA55) {
+		pch_phub_read_serial_rom(0x82, &rom_length);
+		orom_size = rom_length * 512;
+		if (orom_size < pos)
+			return 0;
+
+		for (addr_offset = 0; addr_offset < size; addr_offset++) {
+			pch_phub_read_serial_rom(0x80 + addr_offset + pos,
+									 &data);
+			ret_value = copy_to_user(&buf[addr_offset], &data, 1);
+			if (ret_value)
+				return -EFAULT;
+
+			if (orom_size < pos + addr_offset) {
+				*ppos += addr_offset;
+				return addr_offset;
+			}
+
+		}
+	} else {
+		return -ENOEXEC;
+	}
+	*ppos += addr_offset;
+	return addr_offset;
+}
+
+/** pch_phub_write - Implements the write functionality of the Packet Hub
+ *  									 module.
+ *  @file: Contains the reference of the file structure
+ *  @buf: Usermode buffer pointer
+ *  @size: Usermode buffer size
+ *  @ppos: Contains the reference of the file structure
+ */
+static ssize_t pch_phub_write(struct file *file, const char __user *buf,
+						 size_t size, loff_t *ppos)
+{
+	unsigned int data;
+	int ret_value;
+	unsigned int addr_offset = 0;
+	loff_t pos = *ppos;
+
+	if (pos < 0)
+		return -EINVAL;
+
+	for (addr_offset = 0; addr_offset < size; addr_offset++) {
+		ret_value = get_user(data, &buf[addr_offset]);
+		if (ret_value)
+			return -EFAULT;
+
+		ret_value = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
+								 data);
+		if (ret_value)
+			return -EIO;
+
+		if (PCH_PHUB_OROM_SIZE < pos + addr_offset) {
+			*ppos += addr_offset;
+			return addr_offset;
+		}
+
+	}
+
+	*ppos += addr_offset;
+	return addr_offset;
+}
+
+
+/** pch_phub_ioctl - Implements the various ioctl functionalities of the Packet
+ *  								 Hub module.
+ *  @inode: Contains the reference of the inode structure
+ *  @file: Contains the reference of the file structure
+ *  @cmd: Contains the command value
+ *  @arg: Contains the command argument value
+ */
+
+
+static long pch_phub_ioctl(struct file *file, unsigned int cmd,
+							 unsigned long arg)
+{
+	int ret_value = 0;
+	__u8 mac_addr[6];
+	int ret;
+	unsigned int i;
+	void __user *varg = (void __user *)arg;
+
+	ret = mutex_lock_interruptible(&pch_phub_ioctl_mutex);
+	if (ret) {
+		ret_value = -ERESTARTSYS;
+		goto return_nomutex;
+	}
+
+	if (pch_phub_reg.pch_phub_suspended == true) {
+		ret_value = -EPERM;
+		goto return_ioctrl;
+	}
+
+	switch (cmd) {
+	case IOCTL_PHUB_READ_MAC_ADDR:
+		for (i = 0; i < 6; i++)
+			pch_phub_read_gbe_mac_addr(i, &mac_addr[i]);
+
+		ret_value = copy_to_user(varg,
+					 mac_addr, sizeof(mac_addr));
+		if (ret_value) {
+			ret_value = -EFAULT;
+			goto return_ioctrl;
+		}
+		break;
+
+	case IOCTL_PHUB_WRITE_MAC_ADDR:
+		ret_value = copy_from_user(mac_addr, varg, sizeof(mac_addr));
+
+		if (ret_value) {
+			ret_value = -EFAULT;
+			goto return_ioctrl;
+		}
+		for (i = 0; i < 6; i++)
+			pch_phub_write_gbe_mac_addr(i, mac_addr[i]);
+		break;
+
+	default:
+		ret_value = -EINVAL;
+		break;
+	}
+return_ioctrl:
+	mutex_unlock(&pch_phub_ioctl_mutex);
+return_nomutex:
+	return ret_value;
+}
+
+
+/**
+ * file_operations structure initialization
+ */
+static const struct file_operations pch_phub_fops = {
+	.owner = THIS_MODULE,
+	.read = pch_phub_read,
+	.write = pch_phub_write,
+	.unlocked_ioctl = pch_phub_ioctl,
+	.llseek = default_llseek
+};
+
+
+/** pch_phub_probe - Implements the probe functionality of the module.
+ *  @pdev: Contains the reference of the pci_dev structure
+ *  @id: Contains the reference of the pci_device_id structure
+ */
+static int __devinit pch_phub_probe(struct pci_dev *pdev,
+				       const struct pci_device_id *id)
+{
+	char *DRIVER_NAME = "pch_phub";
+	int ret;
+	unsigned int rom_size;
+
+	pch_phub_major_no = (pch_phub_major_no < 0 || pch_phub_major_no > 254) ?
+				0 : pch_phub_major_no;
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+				"\npch_phub_probe : pci_enable_device FAILED");
+		goto err_probe;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : "
+			"pci_enable_device returns %d\n", ret);
+
+	ret = pci_request_regions(pdev, DRIVER_NAME);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+				"pch_phub_probe : pci_request_regions FAILED");
+		pci_disable_device(pdev);
+		goto err_probe;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : "
+		"pci_request_regions returns %d\n", ret);
+
+	pch_phub_reg.pch_phub_base_address = \
+					(void __iomem *)pci_iomap(pdev, 1, 0);
+
+	if (pch_phub_reg.pch_phub_base_address == 0) {
+		dev_dbg(&pdev->dev, "pch_phub_probe : pci_iomap FAILED");
+		pci_release_regions(pdev);
+		pci_disable_device(pdev);
+		ret = -ENOMEM;
+		goto err_probe;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : pci_iomap SUCCESS and value "
+		"in pch_phub_base_address variable is 0x%08x\n",
+		(unsigned int)pch_phub_reg.pch_phub_base_address);
+
+	pch_phub_reg.pch_phub_extrom_base_address =
+	    (void __iomem *)pci_map_rom(pdev, &rom_size);
+	if (pch_phub_reg.pch_phub_extrom_base_address == 0) {
+		dev_dbg(&pdev->dev, "pch_phub_probe : pci_map_rom FAILED");
+		pci_iounmap(pdev, (void *)pch_phub_reg.pch_phub_base_address);
+		pci_release_regions(pdev);
+		pci_disable_device(pdev);
+		ret = -ENOMEM;
+		goto err_probe;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : "
+		"pci_map_rom SUCCESS and value in "
+		"pch_phub_extrom_base_address variable is 0x%08x\n",
+		(unsigned int)pch_phub_reg.pch_phub_extrom_base_address);
+
+	if (pch_phub_major_no) {
+		pch_phub_dev_no = MKDEV(pch_phub_major_no, 0);
+		ret = register_chrdev_region(pch_phub_dev_no,
+					   PCH_MINOR_NOS, DRIVER_NAME);
+		if (ret) {
+			dev_dbg(&pdev->dev, "pch_phub_probe : "
+				"register_chrdev_region FAILED");
+			pci_unmap_rom(pdev,
+			(void *)pch_phub_reg.pch_phub_extrom_base_address);
+			pci_iounmap(pdev,
+				(void *)pch_phub_reg.pch_phub_base_address);
+			pci_release_regions(pdev);
+			pci_disable_device(pdev);
+			goto err_probe;
+		}
+		dev_dbg(&pdev->dev, "pch_phub_probe : "
+				"register_chrdev_region returns %d\n", ret);
+	} else {
+		ret = alloc_chrdev_region(&pch_phub_dev_no, 0,
+						PCH_MINOR_NOS, DRIVER_NAME);
+		if (ret) {
+			dev_dbg(&pdev->dev, "pch_phub_probe : "
+					"alloc_chrdev_region FAILED");
+			pci_unmap_rom(pdev,
+			(void *)pch_phub_reg.pch_phub_extrom_base_address);
+			pci_iounmap(pdev,
+				    (void *)pch_phub_reg.pch_phub_base_address);
+			pci_release_regions(pdev);
+			pci_disable_device(pdev);
+			goto err_probe;
+		}
+		dev_dbg(&pdev->dev, "pch_phub_probe : "
+			"alloc_chrdev_region returns %d\n", ret);
+	}
+
+	cdev_init(&pch_phub_dev, &pch_phub_fops);
+	dev_dbg(&pdev->dev,
+			"pch_phub_probe :  cdev_init invoked successfully\n");
+
+	pch_phub_dev.owner = THIS_MODULE;
+	pch_phub_dev.ops = &pch_phub_fops;
+
+	ret = cdev_add(&pch_phub_dev, pch_phub_dev_no, PCH_MINOR_NOS);
+	if (ret) {
+		dev_dbg(&pdev->dev, "pch_phub_probe :  cdev_add FAILED");
+		unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
+		pci_unmap_rom(pdev,
+			(void *)pch_phub_reg.pch_phub_extrom_base_address);
+		pci_iounmap(pdev, (void *)pch_phub_reg.pch_phub_base_address);
+		pci_release_regions(pdev);
+		pci_disable_device(pdev);
+		goto err_probe;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe :  cdev_add returns %d\n", ret);
+
+	/*set the clock config reg if CAN clock is 50Mhz */
+	dev_dbg(&pdev->dev, "pch_phub_probe : invoking "
+		"pch_phub_read_modify_write_reg "
+		"to set CLKCFG reg for CAN clk 50Mhz\n");
+	pch_phub_read_modify_write_reg((unsigned int)CLKCFG_REG_OFFSET,
+					  CLKCFG_CAN_50MHZ, CLKCFG_CANCLK_MASK);
+
+	/* set the prefech value */
+	iowrite32(0x000ffffa, pch_phub_reg.pch_phub_base_address + 0x14);
+	/* set the interrupt delay value */
+	iowrite32(0x25, pch_phub_reg.pch_phub_base_address + 0x44);
+	return 0;
+
+err_probe:
+	dev_dbg(&pdev->dev, "pch_phub_probe returns %d\n", ret);
+	return ret;
+}
+
+/** pch_phub_remove - Implements the remove functionality of the module.
+ *  @pdev: Contains the reference of the pci_dev structure
+ */
+static void __devexit pch_phub_remove(struct pci_dev *pdev)
+{
+
+	cdev_del(&pch_phub_dev);
+	dev_dbg(&pdev->dev,
+			"pch_phub_remove - cdev_del Invoked successfully\n");
+
+	unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+		"unregister_chrdev_region Invoked successfully\n");
+
+	pci_unmap_rom(pdev, (void *)pch_phub_reg.pch_phub_extrom_base_address);
+
+	pci_iounmap(pdev, (void *)pch_phub_reg.pch_phub_base_address);
+
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+			"pci_iounmap Invoked successfully\n");
+
+	pci_release_regions(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+		"pci_release_regions Invoked successfully\n");
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+		"pci_disable_device Invoked successfully\n");
+
+}
+
+#ifdef CONFIG_PM
+
+/** pch_phub_suspend - Implements the suspend functionality of the module.
+ *  @pdev: Contains the reference of the pci_dev structure
+ *  @state: Contains the reference of the pm_message_t structure
+ */
+static int pch_phub_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	int ret;
+
+	pch_phub_reg.pch_phub_suspended = true;/* For blocking further IOCTLs */
+
+	pch_phub_save_reg_conf(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+		"pch_phub_save_reg_conf Invoked successfully\n");
+
+	ret = pci_save_state(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+			" pch_phub_suspend -pci_save_state returns-%d\n", ret);
+		return ret;
+	}
+	dev_dbg(&pdev->dev,
+			"pch_phub_suspend - pci_save_state returns %d\n", ret);
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+			"pci_enable_wake Invoked successfully\n");
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+			"pci_disable_device Invoked successfully\n");
+
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+			"pci_set_power_state Invoked successfully   "
+			"return = %d\n", 0);
+
+	return 0;
+}
+
+/** pch_phub_resume - Implements the resume functionality of the module.
+ *  @pdev: Contains the reference of the pci_dev structure
+ */
+static int pch_phub_resume(struct pci_dev *pdev)
+{
+
+	int ret;
+
+	pci_set_power_state(pdev, PCI_D0);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+		"pci_set_power_state Invoked successfully\n");
+
+	pci_restore_state(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+		"pci_restore_state Invoked successfully\n");
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+				"pch_phub_resume-pci_enable_device failed ");
+		return ret;
+	}
+
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+			"pci_enable_device returns -%d\n", ret);
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+			"pci_enable_wake Invoked successfully\n");
+
+	pch_phub_restore_reg_conf(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+		"pch_phub_restore_reg_conf Invoked successfully\n");
+
+	pch_phub_reg.pch_phub_suspended = false;
+
+	dev_dbg(&pdev->dev, "pch_phub_resume  returns- %d\n", 0);
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct pci_device_id pch_phub_pcidev_id[] = {
+
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH1_PHUB)},
+	{0,}
+};
+
+
+static struct pci_driver pch_phub_driver = {
+	.name = "pch_phub",
+	.id_table = pch_phub_pcidev_id,
+	.probe = pch_phub_probe,
+	.remove = __devexit_p(pch_phub_remove),
+#ifdef CONFIG_PM
+	.suspend = pch_phub_suspend,
+	.resume = pch_phub_resume
+#endif
+};
+
+/** pch_phub_pci_init - Implements the initialization functionality of
+ *  								 the module.
+ */
+static int __init pch_phub_pci_init(void)
+{
+	int ret;
+	ret = pci_register_driver(&pch_phub_driver);
+
+	return ret;
+}
+
+/** pch_phub_pci_exit - Implements the exit functionality of the module.
+ */
+static void __exit pch_phub_pci_exit(void)
+{
+	pci_unregister_driver(&pch_phub_driver);
+
+}
+
+module_init(pch_phub_pci_init);
+module_exit(pch_phub_pci_exit);
+module_param(pch_phub_major_no, int, S_IRUSR | S_IWUSR);
+
+MODULE_DESCRIPTION("PCH PACKET HUB PCI Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/char/pch_phub/pch_phub.h b/drivers/char/pch_phub/pch_phub.h
new file mode 100755
index 0000000..25d7c56
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.h
@@ -0,0 +1,58 @@
+#ifndef __PCH_PHUB_H__
+#define __PCH_PHUB_H__
+/*!
+ * @file pch_phub.h
+ * @brief Provides all the interfaces pertaining to the Packet Hub module.
+ * @version 1.0.0.0
+ * @section
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+/*
+ * History:
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * created:
+ *	OKI SEMICONDUCTOR 04/14/2010
+ * modified:
+ *
+ */
+
+#define PHUB_IOCTL_MAGIC 		(0xf7)
+
+/*Outlines the read mac address function signature. */
+#define IOCTL_PHUB_READ_MAC_ADDR (_IOR(PHUB_IOCTL_MAGIC, 6, __u8[6]))
+
+/*brief Outlines the write mac address function signature. */
+#define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 7, __u8[6]))
+
+
+/* Registers address offset */
+#define PCH_PHUB_PHUB_ID_REG			0x0000
+#define PCH_PHUB_QUEUE_PRI_VAL_REG		0x0004
+#define PCH_PHUB_RC_QUEUE_MAXSIZE_REG		0x0008
+#define PCH_PHUB_BRI_QUEUE_MAXSIZE_REG		0x000C
+#define PCH_PHUB_COMP_RESP_TIMEOUT_REG		0x0010
+#define PCH_PHUB_BUS_SLAVE_CONTROL_REG		0x0014
+#define PCH_PHUB_DEADLOCK_AVOID_TYPE_REG	0x0018
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG0	0x0020
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG1	0x0024
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG2	0x0028
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG3	0x002C
+#define PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE	0x0040
+#define CLKCFG_REG_OFFSET           		0x500
+
+#define PCH_PHUB_OROM_SIZE 15360
+
+#endif
-- 
1.6.0.6

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

* Re: [PATCH] Topcliff PHUB: Generate PacketHub driver
  2010-06-22  5:33 [PATCH] Topcliff PHUB: Generate PacketHub driver Masayuki Ohtak
@ 2010-06-22 10:33 ` Masayuki Ohtak
  2010-06-22 22:12   ` Andrew Morton
  2010-06-22 11:30 ` Arnd Bergmann
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 46+ messages in thread
From: Masayuki Ohtak @ 2010-06-22 10:33 UTC (permalink / raw)
  To: Arnd Bergmann, Wang, Yong Y
  Cc: qi.wang, joel.clark, andrew.chih.howe.khor, Alan Cox, LKML

Hi Arnd and Yong Y

We have updated phub patch about the following.
 * Arnd's commnets
	 * Delete PCH_READ_REG/PCH_WRITE_REG
	 * Delete '_t' prefix
	 * Modify basic type
	 * Delete needless 'static' prefix
	 * Modify returned value
	 * Care returned value of get_user()
	 * Add .llseek line

 * Yong Y's comments
	 * Applying to the latest checkpatch(2.6.35)
	 * Delete unused 'DEBUG' macro in Makefile
	 * Delete IEEE1588 lines
	 * Delete 'PCH_CAN_PCLK_50MHZ'

Thanks, Ohtake.

Kernel=2.6.33.1
Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
---
 drivers/char/Kconfig             |   12 +
 drivers/char/Makefile            |    2 +
 drivers/char/pch_phub/Makefile   |    3 +
 drivers/char/pch_phub/pch_phub.c |  937 ++++++++++++++++++++++++++++++++++++++
 drivers/char/pch_phub/pch_phub.h |   58 +++
 5 files changed, 1012 insertions(+), 0 deletions(-)
 create mode 100644 drivers/char/pch_phub/Makefile
 create mode 100755 drivers/char/pch_phub/pch_phub.c
 create mode 100755 drivers/char/pch_phub/pch_phub.h

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index e023682..7ff728a 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -4,6 +4,18 @@
 
 menu "Character devices"
 
+config PCH_PHUB
+	tristate "PCH PHUB"
+	depends on PCI
+	help
+	  If you say yes to this option, support will be included for the
+	  PCH Packet Hub Host controller.
+	  This driver is for PCH Packet hub driver for Topcliff.
+	  This driver is integrated as built-in.
+	  This driver can access GbE MAC address.
+	  This driver can access HW register.
+	  You can also be integrated as module.
+
 config VT
 	bool "Virtual terminal" if EMBEDDED
 	depends on !S390
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index f957edf..1e3eb6c 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -111,6 +111,8 @@ obj-$(CONFIG_PS3_FLASH)		+= ps3flash.o
 obj-$(CONFIG_JS_RTC)		+= js-rtc.o
 js-rtc-y = rtc.o
 
+obj-$(CONFIG_PCH_PHUB)	+= pch_phub/
+
 # Files generated that shall be removed upon make clean
 clean-files := consolemap_deftbl.c defkeymap.c
 
diff --git a/drivers/char/pch_phub/Makefile b/drivers/char/pch_phub/Makefile
new file mode 100644
index 0000000..93aaffe
--- /dev/null
+++ b/drivers/char/pch_phub/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_PCH_PHUB) += pch_phub_drv.o
+
+pch_phub_drv-objs := pch_phub.o
diff --git a/drivers/char/pch_phub/pch_phub.c b/drivers/char/pch_phub/pch_phub.c
new file mode 100755
index 0000000..1590d6b
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.c
@@ -0,0 +1,937 @@
+/*!
+ * @file pch_phub.c
+ * @brief Provides all the implementation of the interfaces pertaining to
+ *		the Packet Hub module.
+ * @version 1.0.0.0
+ * @section
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+/*
+ * History:
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * created:
+ *	OKI SEMICONDUCTOR 04/14/2010
+ * modified:
+ *
+ */
+
+/* includes */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+
+#include "pch_phub.h"
+
+/*--------------------------------------------
+	macros
+--------------------------------------------*/
+/* Status Register offset */
+#define PHUB_STATUS 0x00
+/* Control Register offset */
+#define PHUB_CONTROL 0x04
+/* Time out value for Status Register */
+#define PHUB_TIMEOUT 0x05
+/* Enabling for writing ROM */
+#define PCH_PHUB_ROM_WRITE_ENABLE 0x01
+/* Disabling for writing ROM */
+#define PCH_PHUB_ROM_WRITE_DISABLE 0x00
+/* ROM data area start address offset */
+#define PCH_PHUB_ROM_START_ADDR 0x14
+/* MAX number of INT_REDUCE_CONTROL registers */
+#define MAX_NUM_INT_REDUCE_CONTROL_REG 128
+
+#define PCI_DEVICE_ID_PCH1_PHUB 0x8801
+
+#define PCH_MINOR_NOS 1
+#define CLKCFG_CAN_50MHZ 0x12000000
+#define CLKCFG_CANCLK_MASK 0xFF000000
+
+#define MODULE_NAME "pch_phub"
+
+/*--------------------------------------------
+	global variables
+--------------------------------------------*/
+/**
+ * struct pch_phub_reg - PHUB register structure
+ * @phub_id_reg:		 PHUB_ID register val
+ * @q_pri_val_reg:		 QUEUE_PRI_VAL register val
+ * @rc_q_maxsize_reg:	 RC_QUEUE_MAXSIZE register val
+ * @bri_q_maxsize_reg:	 BRI_QUEUE_MAXSIZE register val
+ * @comp_resp_timeout_reg:	 COMP_RESP_TIMEOUT register val
+ * @bus_slave_control_reg:	 BUS_SLAVE_CONTROL_REG register val
+ * @deadlock_avoid_type_reg:  DEADLOCK_AVOID_TYPE register val
+ * @intpin_reg_wpermit_reg0:  INTPIN_REG_WPERMIT register 0 val
+ * @intpin_reg_wpermit_reg1:  INTPIN_REG_WPERMIT register 1 val
+ * @intpin_reg_wpermit_reg2:  INTPIN_REG_WPERMIT register 2 val
+ * @intpin_reg_wpermit_reg3:  INTPIN_REG_WPERMIT register 3 val
+ * @int_reduce_control_reg:	 INT_REDUCE_CONTROL registers val
+ * @clkcfg_reg:		 CLK CFG register val
+ * @pch_phub_base_address:  register base address
+ * @pch_phub_extrom_base_address:  external rom base address
+ * @pch_phub_suspended: PHUB status val
+ */
+struct pch_phub_reg {
+	unsigned int phub_id_reg;
+	unsigned int q_pri_val_reg;
+	unsigned int rc_q_maxsize_reg;
+	unsigned int bri_q_maxsize_reg;
+	unsigned int comp_resp_timeout_reg;
+	unsigned int bus_slave_control_reg;
+	unsigned int deadlock_avoid_type_reg;
+	unsigned int intpin_reg_wpermit_reg0;
+	unsigned int intpin_reg_wpermit_reg1;
+	unsigned int intpin_reg_wpermit_reg2;
+	unsigned int intpin_reg_wpermit_reg3;
+	unsigned int int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG];
+	unsigned int clkcfg_reg;
+	void __iomem *pch_phub_base_address;
+	void __iomem *pch_phub_extrom_base_address;
+	int pch_phub_suspended;
+} pch_phub_reg;
+
+static DEFINE_MUTEX(pch_phub_ioctl_mutex);
+
+/* ToDo: major number allocation via module parameter */
+static dev_t pch_phub_dev_no;
+static int pch_phub_major_no;
+static struct cdev pch_phub_dev;
+
+/*--------------------------------------------
+	functions implementations
+--------------------------------------------*/
+/** pch_phub_read_modify_write_reg - Implements the functionality of reading,
+ *					 modifying and writing register.
+ *  @reg_addr_offset:  Contains the register offset address value
+ *  @data:             Contains the writing value
+ *  @mask:             Contains the mask value
+ */
+static void pch_phub_read_modify_write_reg(unsigned int reg_addr_offset,
+				       unsigned int data, unsigned int mask)
+{
+	void __iomem *reg_addr = pch_phub_reg.pch_phub_base_address +\
+							 reg_addr_offset;
+	iowrite32(((ioread32(reg_addr) & ~mask)) | data, reg_addr);
+	return;
+}
+
+
+/** pch_phub_save_reg_conf - saves register configuration
+ */
+static void pch_phub_save_reg_conf(struct pci_dev *pdev)
+{
+	unsigned int i = 0;
+	void __iomem *p = pch_phub_reg.pch_phub_base_address;
+
+	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf ENTRY\n");
+	/* to store contents of PHUB_ID register */
+	pch_phub_reg.phub_id_reg = ioread32(p + PCH_PHUB_PHUB_ID_REG);
+	/* to store contents of QUEUE_PRI_VAL register */
+	pch_phub_reg.q_pri_val_reg = ioread32(p + PCH_PHUB_QUEUE_PRI_VAL_REG);
+	/* to store contents of RC_QUEUE_MAXSIZE register */
+	pch_phub_reg.rc_q_maxsize_reg =
+	    ioread32(p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+	/* to store contents of BRI_QUEUE_MAXSIZE register */
+	pch_phub_reg.bri_q_maxsize_reg =
+	    ioread32(p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+	/* to store contents of COMP_RESP_TIMEOUT register */
+	pch_phub_reg.comp_resp_timeout_reg =
+	    ioread32(p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+	/* to store contents of BUS_SLAVE_CONTROL_REG register */
+	pch_phub_reg.bus_slave_control_reg =
+	    ioread32(p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+	/* to store contents of DEADLOCK_AVOID_TYPE register */
+	pch_phub_reg.deadlock_avoid_type_reg =
+	    ioread32(p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+	/* to store contents of INTPIN_REG_WPERMIT register 0 */
+	pch_phub_reg.intpin_reg_wpermit_reg0 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+	/* to store contents of INTPIN_REG_WPERMIT register 1 */
+	pch_phub_reg.intpin_reg_wpermit_reg1 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+	/* to store contents of INTPIN_REG_WPERMIT register 2 */
+	pch_phub_reg.intpin_reg_wpermit_reg2 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+	/* to store contents of INTPIN_REG_WPERMIT register 3 */
+	pch_phub_reg.intpin_reg_wpermit_reg3 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
+	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+		"pch_phub_reg.phub_id_reg=%x, "
+		"pch_phub_reg.q_pri_val_reg=%x, "
+		"pch_phub_reg.rc_q_maxsize_reg=%x, "
+		"pch_phub_reg.bri_q_maxsize_reg=%x, "
+		"pch_phub_reg.comp_resp_timeout_reg=%x, "
+		"pch_phub_reg.bus_slave_control_reg=%x, "
+		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n",
+		pch_phub_reg.phub_id_reg,
+		pch_phub_reg.q_pri_val_reg,
+		pch_phub_reg.rc_q_maxsize_reg,
+		pch_phub_reg.bri_q_maxsize_reg,
+		pch_phub_reg.comp_resp_timeout_reg,
+		pch_phub_reg.bus_slave_control_reg,
+		pch_phub_reg.deadlock_avoid_type_reg,
+		pch_phub_reg.intpin_reg_wpermit_reg0,
+		pch_phub_reg.intpin_reg_wpermit_reg1,
+		pch_phub_reg.intpin_reg_wpermit_reg2,
+		pch_phub_reg.intpin_reg_wpermit_reg3);
+	/* to store contents of INT_REDUCE_CONTROL registers */
+	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
+		pch_phub_reg.int_reduce_control_reg[i] =
+		    ioread32(p +
+			PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
+		dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
+			i, pch_phub_reg.int_reduce_control_reg[i]);
+	}
+	/* save clk cfg register */
+	pch_phub_reg.clkcfg_reg = ioread32(p + CLKCFG_REG_OFFSET);
+
+	return;
+}
+
+/** pch_phub_restore_reg_conf - restore register configuration
+ */
+
+static void pch_phub_restore_reg_conf(struct pci_dev *pdev)
+{
+	unsigned int i;
+	void __iomem *p = pch_phub_reg.pch_phub_base_address;
+
+	dev_dbg(&pdev->dev, "pch_phub_restore_reg_conf ENTRY\n");
+	/* to store contents of PHUB_ID register */
+	iowrite32(pch_phub_reg.phub_id_reg, p + PCH_PHUB_PHUB_ID_REG);
+	/* to store contents of QUEUE_PRI_VAL register */
+	iowrite32(pch_phub_reg.q_pri_val_reg, p + PCH_PHUB_QUEUE_PRI_VAL_REG);
+	/* to store contents of RC_QUEUE_MAXSIZE register */
+	iowrite32(pch_phub_reg.rc_q_maxsize_reg,
+					p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+	/* to store contents of BRI_QUEUE_MAXSIZE register */
+	iowrite32(pch_phub_reg.bri_q_maxsize_reg,
+					p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+	/* to store contents of COMP_RESP_TIMEOUT register */
+	iowrite32(pch_phub_reg.comp_resp_timeout_reg,
+					p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+	/* to store contents of BUS_SLAVE_CONTROL_REG register */
+	iowrite32(pch_phub_reg.bus_slave_control_reg,
+					p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+	/* to store contents of DEADLOCK_AVOID_TYPE register */
+	iowrite32(pch_phub_reg.deadlock_avoid_type_reg,
+					p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+	/* to store contents of INTPIN_REG_WPERMIT register 0 */
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg0,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+	/* to store contents of INTPIN_REG_WPERMIT register 1 */
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg1,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+	/* to store contents of INTPIN_REG_WPERMIT register 2 */
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg2,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+	/* to store contents of INTPIN_REG_WPERMIT register 3 */
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg3,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
+	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+		"pch_phub_reg.phub_id_reg=%x, "
+		"pch_phub_reg.q_pri_val_reg=%x, "
+		"pch_phub_reg.rc_q_maxsize_reg=%x, "
+		"pch_phub_reg.bri_q_maxsize_reg=%x, "
+		"pch_phub_reg.comp_resp_timeout_reg=%x, "
+		"pch_phub_reg.bus_slave_control_reg=%x, "
+		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n",
+		pch_phub_reg.phub_id_reg,
+		pch_phub_reg.q_pri_val_reg,
+		pch_phub_reg.rc_q_maxsize_reg,
+		pch_phub_reg.bri_q_maxsize_reg,
+		pch_phub_reg.comp_resp_timeout_reg,
+		pch_phub_reg.bus_slave_control_reg,
+		pch_phub_reg.deadlock_avoid_type_reg,
+		pch_phub_reg.intpin_reg_wpermit_reg0,
+		pch_phub_reg.intpin_reg_wpermit_reg1,
+		pch_phub_reg.intpin_reg_wpermit_reg2,
+		pch_phub_reg.intpin_reg_wpermit_reg3);
+	/* to store contents of INT_REDUCE_CONTROL register */
+	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
+		iowrite32(pch_phub_reg.int_reduce_control_reg[i],
+			p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
+		dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
+			i, pch_phub_reg.int_reduce_control_reg[i]);
+	}
+
+	/*restore the clock config reg */
+	iowrite32(pch_phub_reg.clkcfg_reg, p + CLKCFG_REG_OFFSET);
+
+	return;
+}
+
+/** pch_phub_read_serial_rom - Implements the functionality of reading Serial
+ *									 ROM.
+ *  @offset_address: Contains the Serial ROM address offset value
+ *  @data: Contains the Serial ROM value
+ */
+static int pch_phub_read_serial_rom(unsigned int offset_address,
+							 unsigned char *data)
+{
+	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +\
+								 offset_address;
+
+	*data = ioread8(mem_addr);
+
+	return 0;
+}
+
+/** pch_phub_write_serial_rom - Implements the functionality of writing Serial
+ *									 ROM.
+ *  @offset_address: Contains the Serial ROM address offset value
+ *  @data: Contains the Serial ROM value
+ */
+static int pch_phub_write_serial_rom(unsigned int offset_address,
+							 unsigned char data)
+{
+	int retval = 0;
+	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +\
+						 (offset_address & 0xFFFFFFFC);
+	int i = 0;
+	unsigned int word_data = 0;
+
+	iowrite32(PCH_PHUB_ROM_WRITE_ENABLE,
+			pch_phub_reg.pch_phub_extrom_base_address +\
+								 PHUB_CONTROL);
+
+	word_data = ioread32(mem_addr);
+
+	switch (offset_address % 4) {
+	case 0:
+		word_data &= 0xFFFFFF00;
+		iowrite32((word_data | (unsigned int)data),
+						mem_addr);
+		break;
+	case 1:
+		word_data &= 0xFFFF00FF;
+		iowrite32((word_data | ((unsigned int)data << 8)),
+						mem_addr);
+		break;
+	case 2:
+		word_data &= 0xFF00FFFF;
+		iowrite32((word_data | ((unsigned int)data << 16)),
+						mem_addr);
+		break;
+	case 3:
+		word_data &= 0x00FFFFFF;
+		iowrite32((word_data | ((unsigned int)data << 24)),
+						mem_addr);
+		break;
+	}
+	while (0x00 !=
+	       ioread8(pch_phub_reg.pch_phub_extrom_base_address +\
+							 PHUB_STATUS)) {
+		msleep(1);
+		if (PHUB_TIMEOUT == i) {
+			retval = -EPERM;
+			break;
+		}
+		i++;
+	}
+
+	iowrite32(PCH_PHUB_ROM_WRITE_DISABLE,
+			pch_phub_reg.pch_phub_extrom_base_address +\
+								 PHUB_CONTROL);
+
+	return retval;
+}
+
+/** pch_phub_read_serial_rom_val - Implements the functionality of reading
+ *							 Serial ROM value.
+ *  @offset_address: Contains the Serial ROM address offset value
+ *  @data: Contains the Serial ROM value
+ */
+static int pch_phub_read_serial_rom_val(unsigned int offset_address,
+							 unsigned char *data)
+{
+	int retval = 0;
+	unsigned int mem_addr;
+
+	mem_addr = (offset_address / 4 * 8) + 3 -
+			(offset_address % 4) + PCH_PHUB_ROM_START_ADDR;
+	retval = pch_phub_read_serial_rom(mem_addr, data);
+
+	return retval;
+}
+
+/** pch_phub_write_serial_rom_val - Implements the functionality of writing
+ *							 Serial ROM value.
+ *  @offset_address: Contains the Serial ROM address offset value
+ *  @data: Contains the Serial ROM value
+ */
+static int pch_phub_write_serial_rom_val(unsigned int offset_address,
+				     unsigned char data)
+{
+	int retval = 0;
+	unsigned int mem_addr;
+
+	mem_addr =
+	    (offset_address / 4 * 8) + 3 - (offset_address % 4) +
+	    PCH_PHUB_ROM_START_ADDR;
+	retval = pch_phub_write_serial_rom(mem_addr, data);
+
+	return retval;
+}
+
+/** pch_phub_gbe_serial_rom_conf - makes Serial ROM header format configuration
+ *					 for Gigabit Ethernet MAC address
+ */
+static int pch_phub_gbe_serial_rom_conf(void)
+{
+	int retval = 0;
+
+	retval |= pch_phub_write_serial_rom(0x0b, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x0a, 0x10);
+	retval |= pch_phub_write_serial_rom(0x09, 0x01);
+	retval |= pch_phub_write_serial_rom(0x08, 0x02);
+
+	retval |= pch_phub_write_serial_rom(0x0f, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0e, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0d, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0c, 0x80);
+
+	retval |= pch_phub_write_serial_rom(0x13, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x12, 0x10);
+	retval |= pch_phub_write_serial_rom(0x11, 0x01);
+	retval |= pch_phub_write_serial_rom(0x10, 0x18);
+
+	retval |= pch_phub_write_serial_rom(0x1b, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x1a, 0x10);
+	retval |= pch_phub_write_serial_rom(0x19, 0x01);
+	retval |= pch_phub_write_serial_rom(0x18, 0x19);
+
+	retval |= pch_phub_write_serial_rom(0x23, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x22, 0x10);
+	retval |= pch_phub_write_serial_rom(0x21, 0x01);
+	retval |= pch_phub_write_serial_rom(0x20, 0x3a);
+
+	retval |= pch_phub_write_serial_rom(0x27, 0x01);
+	retval |= pch_phub_write_serial_rom(0x26, 0x00);
+	retval |= pch_phub_write_serial_rom(0x25, 0x00);
+	retval |= pch_phub_write_serial_rom(0x24, 0x00);
+
+	return retval;
+}
+
+/** pch_phub_read_gbe_mac_addr - Contains the Gigabit Ethernet MAC address
+ *							 offset value
+ *  @offset_address: Gigabit Ethernet MAC address offset value
+ *  @data: Contains the Gigabit Ethernet MAC address value
+ */
+static int pch_phub_read_gbe_mac_addr(unsigned int offset_address,
+							 unsigned char *data)
+{
+	int retval = 0;
+
+	retval = pch_phub_read_serial_rom_val(offset_address, data);
+
+	return retval;
+}
+
+/** pch_phub_write_gbe_mac_addr - Write MAC address
+ *  @offset_address: Contains the Gigabit Ethernet MAC address offset value
+ *  @data: Contains the Gigabit Ethernet MAC address value
+ */
+static int pch_phub_write_gbe_mac_addr(unsigned int offset_address,
+				   unsigned char data)
+{
+	int retval = 0;
+
+	retval = pch_phub_gbe_serial_rom_conf();
+	retval |= pch_phub_write_serial_rom_val(offset_address, data);
+
+	return retval;
+}
+
+/** pch_phub_read - Implements the read functionality of the Packet Hub module.
+ *  @file: Contains the reference of the file structure
+ *  @buf: Usermode buffer pointer
+ *  @size: Usermode buffer size
+ *  @ppos: Contains the reference of the file structure
+ */
+
+static ssize_t pch_phub_read(struct file *file, char __user *buf, size_t size,
+								 loff_t *ppos)
+{
+	unsigned int rom_signature = 0;
+	unsigned char rom_length;
+	int ret_value;
+	unsigned int tmp;
+	unsigned char data;
+	unsigned int addr_offset = 0;
+	unsigned int orom_size;
+	loff_t pos = *ppos;
+
+	if (pos < 0)
+		return -EINVAL;
+
+	/*Get Rom signature*/
+	pch_phub_read_serial_rom(0x80, (unsigned char *)&rom_signature);
+	pch_phub_read_serial_rom(0x81, (unsigned char *)&tmp);
+	rom_signature |= (tmp & 0xff) << 8;
+	if (rom_signature == 0xAA55) {
+		pch_phub_read_serial_rom(0x82, &rom_length);
+		orom_size = rom_length * 512;
+		if (orom_size < pos)
+			return 0;
+
+		for (addr_offset = 0; addr_offset < size; addr_offset++) {
+			pch_phub_read_serial_rom(0x80 + addr_offset + pos,
+									 &data);
+			ret_value = copy_to_user(&buf[addr_offset], &data, 1);
+			if (ret_value)
+				return -EFAULT;
+
+			if (orom_size < pos + addr_offset) {
+				*ppos += addr_offset;
+				return addr_offset;
+			}
+
+		}
+	} else {
+		return -ENOEXEC;
+	}
+	*ppos += addr_offset;
+	return addr_offset;
+}
+
+/** pch_phub_write - Implements the write functionality of the Packet Hub
+ *									 module.
+ *  @file: Contains the reference of the file structure
+ *  @buf: Usermode buffer pointer
+ *  @size: Usermode buffer size
+ *  @ppos: Contains the reference of the file structure
+ */
+static ssize_t pch_phub_write(struct file *file, const char __user *buf,
+						 size_t size, loff_t *ppos)
+{
+	unsigned int data;
+	int ret_value;
+	unsigned int addr_offset = 0;
+	loff_t pos = *ppos;
+
+	if (pos < 0)
+		return -EINVAL;
+
+	for (addr_offset = 0; addr_offset < size; addr_offset++) {
+		ret_value = get_user(data, &buf[addr_offset]);
+		if (ret_value)
+			return -EFAULT;
+
+		ret_value = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
+								 data);
+		if (ret_value)
+			return -EIO;
+
+		if (PCH_PHUB_OROM_SIZE < pos + addr_offset) {
+			*ppos += addr_offset;
+			return addr_offset;
+		}
+
+	}
+
+	*ppos += addr_offset;
+	return addr_offset;
+}
+
+
+/** pch_phub_ioctl - Implements the various ioctl functionalities of the Packet
+ *								 Hub module.
+ *  @inode: Contains the reference of the inode structure
+ *  @file: Contains the reference of the file structure
+ *  @cmd: Contains the command value
+ *  @arg: Contains the command argument value
+ */
+
+
+static long pch_phub_ioctl(struct file *file, unsigned int cmd,
+							 unsigned long arg)
+{
+	int ret_value = 0;
+	__u8 mac_addr[6];
+	int ret;
+	unsigned int i;
+	void __user *varg = (void __user *)arg;
+
+	ret = mutex_lock_interruptible(&pch_phub_ioctl_mutex);
+	if (ret) {
+		ret_value = -ERESTARTSYS;
+		goto return_nomutex;
+	}
+
+	if (pch_phub_reg.pch_phub_suspended == true) {
+		ret_value = -EPERM;
+		goto return_ioctrl;
+	}
+
+	switch (cmd) {
+	case IOCTL_PHUB_READ_MAC_ADDR:
+		for (i = 0; i < 6; i++)
+			pch_phub_read_gbe_mac_addr(i, &mac_addr[i]);
+
+		ret_value = copy_to_user(varg,
+					 mac_addr, sizeof(mac_addr));
+		if (ret_value) {
+			ret_value = -EFAULT;
+			goto return_ioctrl;
+		}
+		break;
+
+	case IOCTL_PHUB_WRITE_MAC_ADDR:
+		ret_value = copy_from_user(mac_addr, varg, sizeof(mac_addr));
+
+		if (ret_value) {
+			ret_value = -EFAULT;
+			goto return_ioctrl;
+		}
+		for (i = 0; i < 6; i++)
+			pch_phub_write_gbe_mac_addr(i, mac_addr[i]);
+		break;
+
+	default:
+		ret_value = -EINVAL;
+		break;
+	}
+return_ioctrl:
+	mutex_unlock(&pch_phub_ioctl_mutex);
+return_nomutex:
+	return ret_value;
+}
+
+
+/**
+ * file_operations structure initialization
+ */
+static const struct file_operations pch_phub_fops = {
+	.owner = THIS_MODULE,
+	.read = pch_phub_read,
+	.write = pch_phub_write,
+	.unlocked_ioctl = pch_phub_ioctl,
+	.llseek = default_llseek
+};
+
+
+/** pch_phub_probe - Implements the probe functionality of the module.
+ *  @pdev: Contains the reference of the pci_dev structure
+ *  @id: Contains the reference of the pci_device_id structure
+ */
+static int __devinit pch_phub_probe(struct pci_dev *pdev,
+				       const struct pci_device_id *id)
+{
+	char *DRIVER_NAME = "pch_phub";
+	int ret;
+	unsigned int rom_size;
+
+	pch_phub_major_no = (pch_phub_major_no < 0 || pch_phub_major_no > 254) ?
+				0 : pch_phub_major_no;
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+				"\npch_phub_probe : pci_enable_device FAILED");
+		goto err_probe;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : "
+			"pci_enable_device returns %d\n", ret);
+
+	ret = pci_request_regions(pdev, DRIVER_NAME);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+				"pch_phub_probe : pci_request_regions FAILED");
+		pci_disable_device(pdev);
+		goto err_probe;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : "
+		"pci_request_regions returns %d\n", ret);
+
+	pch_phub_reg.pch_phub_base_address = \
+					(void __iomem *)pci_iomap(pdev, 1, 0);
+
+	if (pch_phub_reg.pch_phub_base_address == 0) {
+		dev_dbg(&pdev->dev, "pch_phub_probe : pci_iomap FAILED");
+		pci_release_regions(pdev);
+		pci_disable_device(pdev);
+		ret = -ENOMEM;
+		goto err_probe;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : pci_iomap SUCCESS and value "
+		"in pch_phub_base_address variable is 0x%08x\n",
+		(unsigned int)pch_phub_reg.pch_phub_base_address);
+
+	pch_phub_reg.pch_phub_extrom_base_address =
+	    (void __iomem *)pci_map_rom(pdev, &rom_size);
+	if (pch_phub_reg.pch_phub_extrom_base_address == 0) {
+		dev_dbg(&pdev->dev, "pch_phub_probe : pci_map_rom FAILED");
+		pci_iounmap(pdev, (void *)pch_phub_reg.pch_phub_base_address);
+		pci_release_regions(pdev);
+		pci_disable_device(pdev);
+		ret = -ENOMEM;
+		goto err_probe;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : "
+		"pci_map_rom SUCCESS and value in "
+		"pch_phub_extrom_base_address variable is 0x%08x\n",
+		(unsigned int)pch_phub_reg.pch_phub_extrom_base_address);
+
+	if (pch_phub_major_no) {
+		pch_phub_dev_no = MKDEV(pch_phub_major_no, 0);
+		ret = register_chrdev_region(pch_phub_dev_no,
+					   PCH_MINOR_NOS, DRIVER_NAME);
+		if (ret) {
+			dev_dbg(&pdev->dev, "pch_phub_probe : "
+				"register_chrdev_region FAILED");
+			pci_unmap_rom(pdev,
+			(void *)pch_phub_reg.pch_phub_extrom_base_address);
+			pci_iounmap(pdev,
+				(void *)pch_phub_reg.pch_phub_base_address);
+			pci_release_regions(pdev);
+			pci_disable_device(pdev);
+			goto err_probe;
+		}
+		dev_dbg(&pdev->dev, "pch_phub_probe : "
+				"register_chrdev_region returns %d\n", ret);
+	} else {
+		ret = alloc_chrdev_region(&pch_phub_dev_no, 0,
+						PCH_MINOR_NOS, DRIVER_NAME);
+		if (ret) {
+			dev_dbg(&pdev->dev, "pch_phub_probe : "
+					"alloc_chrdev_region FAILED");
+			pci_unmap_rom(pdev,
+			(void *)pch_phub_reg.pch_phub_extrom_base_address);
+			pci_iounmap(pdev,
+				    (void *)pch_phub_reg.pch_phub_base_address);
+			pci_release_regions(pdev);
+			pci_disable_device(pdev);
+			goto err_probe;
+		}
+		dev_dbg(&pdev->dev, "pch_phub_probe : "
+			"alloc_chrdev_region returns %d\n", ret);
+	}
+
+	cdev_init(&pch_phub_dev, &pch_phub_fops);
+	dev_dbg(&pdev->dev,
+			"pch_phub_probe :  cdev_init invoked successfully\n");
+
+	pch_phub_dev.owner = THIS_MODULE;
+	pch_phub_dev.ops = &pch_phub_fops;
+
+	ret = cdev_add(&pch_phub_dev, pch_phub_dev_no, PCH_MINOR_NOS);
+	if (ret) {
+		dev_dbg(&pdev->dev, "pch_phub_probe :  cdev_add FAILED");
+		unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
+		pci_unmap_rom(pdev,
+			(void *)pch_phub_reg.pch_phub_extrom_base_address);
+		pci_iounmap(pdev, (void *)pch_phub_reg.pch_phub_base_address);
+		pci_release_regions(pdev);
+		pci_disable_device(pdev);
+		goto err_probe;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe :  cdev_add returns %d\n", ret);
+
+	/*set the clock config reg if CAN clock is 50Mhz */
+	dev_dbg(&pdev->dev, "pch_phub_probe : invoking "
+		"pch_phub_read_modify_write_reg "
+		"to set CLKCFG reg for CAN clk 50Mhz\n");
+	pch_phub_read_modify_write_reg((unsigned int)CLKCFG_REG_OFFSET,
+					  CLKCFG_CAN_50MHZ, CLKCFG_CANCLK_MASK);
+
+	/* set the prefech value */
+	iowrite32(0x000ffffa, pch_phub_reg.pch_phub_base_address + 0x14);
+	/* set the interrupt delay value */
+	iowrite32(0x25, pch_phub_reg.pch_phub_base_address + 0x44);
+	return 0;
+
+err_probe:
+	dev_dbg(&pdev->dev, "pch_phub_probe returns %d\n", ret);
+	return ret;
+}
+
+/** pch_phub_remove - Implements the remove functionality of the module.
+ *  @pdev: Contains the reference of the pci_dev structure
+ */
+static void __devexit pch_phub_remove(struct pci_dev *pdev)
+{
+
+	cdev_del(&pch_phub_dev);
+	dev_dbg(&pdev->dev,
+			"pch_phub_remove - cdev_del Invoked successfully\n");
+
+	unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+		"unregister_chrdev_region Invoked successfully\n");
+
+	pci_unmap_rom(pdev, (void *)pch_phub_reg.pch_phub_extrom_base_address);
+
+	pci_iounmap(pdev, (void *)pch_phub_reg.pch_phub_base_address);
+
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+			"pci_iounmap Invoked successfully\n");
+
+	pci_release_regions(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+		"pci_release_regions Invoked successfully\n");
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+		"pci_disable_device Invoked successfully\n");
+
+}
+
+#ifdef CONFIG_PM
+
+/** pch_phub_suspend - Implements the suspend functionality of the module.
+ *  @pdev: Contains the reference of the pci_dev structure
+ *  @state: Contains the reference of the pm_message_t structure
+ */
+static int pch_phub_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	int ret;
+
+	pch_phub_reg.pch_phub_suspended = true;/* For blocking further IOCTLs */
+
+	pch_phub_save_reg_conf(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+		"pch_phub_save_reg_conf Invoked successfully\n");
+
+	ret = pci_save_state(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+			" pch_phub_suspend -pci_save_state returns-%d\n", ret);
+		return ret;
+	}
+	dev_dbg(&pdev->dev,
+			"pch_phub_suspend - pci_save_state returns %d\n", ret);
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+			"pci_enable_wake Invoked successfully\n");
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+			"pci_disable_device Invoked successfully\n");
+
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+			"pci_set_power_state Invoked successfully   "
+			"return = %d\n", 0);
+
+	return 0;
+}
+
+/** pch_phub_resume - Implements the resume functionality of the module.
+ *  @pdev: Contains the reference of the pci_dev structure
+ */
+static int pch_phub_resume(struct pci_dev *pdev)
+{
+
+	int ret;
+
+	pci_set_power_state(pdev, PCI_D0);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+		"pci_set_power_state Invoked successfully\n");
+
+	pci_restore_state(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+		"pci_restore_state Invoked successfully\n");
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+				"pch_phub_resume-pci_enable_device failed ");
+		return ret;
+	}
+
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+			"pci_enable_device returns -%d\n", ret);
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+			"pci_enable_wake Invoked successfully\n");
+
+	pch_phub_restore_reg_conf(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+		"pch_phub_restore_reg_conf Invoked successfully\n");
+
+	pch_phub_reg.pch_phub_suspended = false;
+
+	dev_dbg(&pdev->dev, "pch_phub_resume  returns- %d\n", 0);
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct pci_device_id pch_phub_pcidev_id[] = {
+
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH1_PHUB)},
+	{0,}
+};
+
+
+static struct pci_driver pch_phub_driver = {
+	.name = "pch_phub",
+	.id_table = pch_phub_pcidev_id,
+	.probe = pch_phub_probe,
+	.remove = __devexit_p(pch_phub_remove),
+#ifdef CONFIG_PM
+	.suspend = pch_phub_suspend,
+	.resume = pch_phub_resume
+#endif
+};
+
+/** pch_phub_pci_init - Implements the initialization functionality of
+ *								 the module.
+ */
+static int __init pch_phub_pci_init(void)
+{
+	int ret;
+	ret = pci_register_driver(&pch_phub_driver);
+
+	return ret;
+}
+
+/** pch_phub_pci_exit - Implements the exit functionality of the module.
+ */
+static void __exit pch_phub_pci_exit(void)
+{
+	pci_unregister_driver(&pch_phub_driver);
+
+}
+
+module_init(pch_phub_pci_init);
+module_exit(pch_phub_pci_exit);
+module_param(pch_phub_major_no, int, S_IRUSR | S_IWUSR);
+
+MODULE_DESCRIPTION("PCH PACKET HUB PCI Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/char/pch_phub/pch_phub.h b/drivers/char/pch_phub/pch_phub.h
new file mode 100755
index 0000000..83986d9
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.h
@@ -0,0 +1,58 @@
+#ifndef __PCH_PHUB_H__
+#define __PCH_PHUB_H__
+/*!
+ * @file pch_phub.h
+ * @brief Provides all the interfaces pertaining to the Packet Hub module.
+ * @version 1.0.0.0
+ * @section
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+/*
+ * History:
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * created:
+ *	OKI SEMICONDUCTOR 04/14/2010
+ * modified:
+ *
+ */
+
+#define PHUB_IOCTL_MAGIC		(0xf7)
+
+/*Outlines the read mac address function signature. */
+#define IOCTL_PHUB_READ_MAC_ADDR (_IOR(PHUB_IOCTL_MAGIC, 6, __u8[6]))
+
+/*brief Outlines the write mac address function signature. */
+#define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 7, __u8[6]))
+
+
+/* Registers address offset */
+#define PCH_PHUB_PHUB_ID_REG			0x0000
+#define PCH_PHUB_QUEUE_PRI_VAL_REG		0x0004
+#define PCH_PHUB_RC_QUEUE_MAXSIZE_REG		0x0008
+#define PCH_PHUB_BRI_QUEUE_MAXSIZE_REG		0x000C
+#define PCH_PHUB_COMP_RESP_TIMEOUT_REG		0x0010
+#define PCH_PHUB_BUS_SLAVE_CONTROL_REG		0x0014
+#define PCH_PHUB_DEADLOCK_AVOID_TYPE_REG	0x0018
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG0	0x0020
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG1	0x0024
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG2	0x0028
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG3	0x002C
+#define PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE	0x0040
+#define CLKCFG_REG_OFFSET			0x500
+
+#define PCH_PHUB_OROM_SIZE 15360
+
+#endif
-- 
1.6.0.6


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

* Re: [PATCH] Topcliff PHUB: Generate PacketHub driver
  2010-06-22  5:33 [PATCH] Topcliff PHUB: Generate PacketHub driver Masayuki Ohtak
  2010-06-22 10:33 ` Masayuki Ohtak
@ 2010-06-22 11:30 ` Arnd Bergmann
  2010-06-22 13:52   ` Yong Wang
  2010-06-29 23:31 ` Andy Isaacson
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 46+ messages in thread
From: Arnd Bergmann @ 2010-06-22 11:30 UTC (permalink / raw)
  To: Masayuki Ohtak
  Cc: Wang, Yong Y, Wang Qi", Intel OTC", Andrew", Alan Cox, LKML

On Tuesday 22 June 2010, Masayuki Ohtak wrote:
> Hi, Arnd and Yong Y
> 
> I have released Phub patch file.
> Please confirm below.

You have addressed all of my comments, the driver looks ready
for inclusion from my perspective.

> Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>

Acked-by: Arnd Bergmann <arnd@arndb.de>

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

* Re: [PATCH] Topcliff PHUB: Generate PacketHub driver
  2010-06-22 11:30 ` Arnd Bergmann
@ 2010-06-22 13:52   ` Yong Wang
  0 siblings, 0 replies; 46+ messages in thread
From: Yong Wang @ 2010-06-22 13:52 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Arnd Bergmann, Masayuki Ohtak, Wang, Yong Y, Wang Qi, Intel OTC,
	Andrew, Alan Cox, LKML

On Tue, Jun 22, 2010 at 01:30:39PM +0200, Arnd Bergmann wrote:
> On Tuesday 22 June 2010, Masayuki Ohtak wrote:
> > Hi, Arnd and Yong Y
> > 
> > I have released Phub patch file.
> > Please confirm below.
> 
> You have addressed all of my comments, the driver looks ready
> for inclusion from my perspective.
> 
> > Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
> 
> Acked-by: Arnd Bergmann <arnd@arndb.de>

Hi Andrew,

Are you going to take this patch? Or could you please let us know which
maintainer will do?

Thanks
-Yong


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

* Re: [PATCH] Topcliff PHUB: Generate PacketHub driver
  2010-06-22 10:33 ` Masayuki Ohtak
@ 2010-06-22 22:12   ` Andrew Morton
  2010-06-23  0:31     ` Masayuki Ohtake
  0 siblings, 1 reply; 46+ messages in thread
From: Andrew Morton @ 2010-06-22 22:12 UTC (permalink / raw)
  To: Masayuki Ohtak
  Cc: Arnd Bergmann, Wang, Yong Y, qi.wang, joel.clark,
	andrew.chih.howe.khor, Alan Cox, LKML

On Tue, 22 Jun 2010 19:33:34 +0900
Masayuki Ohtak <masa-korg@dsn.okisemi.com> wrote:

> Hi Arnd and Yong Y
> 
> We have updated phub patch about the following.
>  * Arnd's commnets
> 	 * Delete PCH_READ_REG/PCH_WRITE_REG
> 	 * Delete '_t' prefix
> 	 * Modify basic type
> 	 * Delete needless 'static' prefix
> 	 * Modify returned value
> 	 * Care returned value of get_user()
> 	 * Add .llseek line
> 
>  * Yong Y's comments
> 	 * Applying to the latest checkpatch(2.6.35)
> 	 * Delete unused 'DEBUG' macro in Makefile
> 	 * Delete IEEE1588 lines
> 	 * Delete 'PCH_CAN_PCLK_50MHZ'
> 
> Thanks, Ohtake.
> 
> Kernel=2.6.33.1
> Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>

Please prepare a proper changelog for the patch - one which is suitabe
for inclusion in the kernel's permanent git record.  Include that
changelog with each version of the patch, perhaps after alterations.

Please describe the module parameters in that changelog.  Please
describe the major/minor number handling within the changelog -
permitting the major number to be specified on the modprobe command
line is unusual and should be fully described and justified, please.

Please ensure that the changelog tells us what the driver actually
does!  I don't even know what a "PCH packet hub" _is_ :(

>
> ...
>
> --- /dev/null
> +++ b/drivers/char/pch_phub/pch_phub.c
> @@ -0,0 +1,937 @@
> +/*!
> + * @file pch_phub.c
> + * @brief Provides all the implementation of the interfaces pertaining to
> + *		the Packet Hub module.
> + * @version 1.0.0.0
> + * @section

This appears to use a markup format whcih the kernel doesn't implement.

> + * 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; version 2 of the License.
> + *
> + * 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, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
> + */
>
> ...
>
> +/** pch_phub_read_modify_write_reg - Implements the functionality of reading,
> + *					 modifying and writing register.
> + *  @reg_addr_offset:  Contains the register offset address value
> + *  @data:             Contains the writing value
> + *  @mask:             Contains the mask value
> + */

kerneldoc comments are usually formatted as

/**
 *  foo() - do something
 *  @arg1 ...

I don't know whether the layout whcih this driver uses will be properly
handled by the kerneldoc tools, but I'd suggest that it all be
converted to the more usual style for consistency.

> +static void pch_phub_read_modify_write_reg(unsigned int reg_addr_offset,
> +				       unsigned int data, unsigned int mask)
> +{
> +	void __iomem *reg_addr = pch_phub_reg.pch_phub_base_address +\
> +							 reg_addr_offset;
> +	iowrite32(((ioread32(reg_addr) & ~mask)) | data, reg_addr);
> +	return;
> +}
> +
> +
> +/** pch_phub_save_reg_conf - saves register configuration
> + */

The driver has quite a lot of comments which use the kerneldoc toke
"/**" but which don't really look like they wre intended to be
kerneldoc comments.  So I'd suggest converting these to plain old comments:

/*
 * pch_phub_save_reg_conf - saves register configuration
 */

or

/* pch_phub_save_reg_conf - saves register configuration */


or finish off the kerneldoc work by documenting the arguments (and the
return values, please).

> +static void pch_phub_save_reg_conf(struct pci_dev *pdev)
> +{
> +	unsigned int i = 0;
> +	void __iomem *p = pch_phub_reg.pch_phub_base_address;
> +
> +	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf ENTRY\n");
> +	/* to store contents of PHUB_ID register */
> +	pch_phub_reg.phub_id_reg = ioread32(p + PCH_PHUB_PHUB_ID_REG);
> +	/* to store contents of QUEUE_PRI_VAL register */
> +	pch_phub_reg.q_pri_val_reg = ioread32(p + PCH_PHUB_QUEUE_PRI_VAL_REG);
> +	/* to store contents of RC_QUEUE_MAXSIZE register */
> +	pch_phub_reg.rc_q_maxsize_reg =
> +	    ioread32(p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
> +	/* to store contents of BRI_QUEUE_MAXSIZE register */
> +	pch_phub_reg.bri_q_maxsize_reg =
> +	    ioread32(p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
> +	/* to store contents of COMP_RESP_TIMEOUT register */
> +	pch_phub_reg.comp_resp_timeout_reg =
> +	    ioread32(p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
> +	/* to store contents of BUS_SLAVE_CONTROL_REG register */
> +	pch_phub_reg.bus_slave_control_reg =
> +	    ioread32(p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
> +	/* to store contents of DEADLOCK_AVOID_TYPE register */
> +	pch_phub_reg.deadlock_avoid_type_reg =
> +	    ioread32(p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
> +	/* to store contents of INTPIN_REG_WPERMIT register 0 */
> +	pch_phub_reg.intpin_reg_wpermit_reg0 =
> +	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
> +	/* to store contents of INTPIN_REG_WPERMIT register 1 */
> +	pch_phub_reg.intpin_reg_wpermit_reg1 =
> +	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
> +	/* to store contents of INTPIN_REG_WPERMIT register 2 */
> +	pch_phub_reg.intpin_reg_wpermit_reg2 =
> +	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
> +	/* to store contents of INTPIN_REG_WPERMIT register 3 */

s/to //g

> +	pch_phub_reg.intpin_reg_wpermit_reg3 =
> +	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
> +	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
> +		"pch_phub_reg.phub_id_reg=%x, "
> +		"pch_phub_reg.q_pri_val_reg=%x, "
> +		"pch_phub_reg.rc_q_maxsize_reg=%x, "
> +		"pch_phub_reg.bri_q_maxsize_reg=%x, "
> +		"pch_phub_reg.comp_resp_timeout_reg=%x, "
> +		"pch_phub_reg.bus_slave_control_reg=%x, "
> +		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
> +		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
> +		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
> +		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
> +		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n",
> +		pch_phub_reg.phub_id_reg,
> +		pch_phub_reg.q_pri_val_reg,
> +		pch_phub_reg.rc_q_maxsize_reg,
> +		pch_phub_reg.bri_q_maxsize_reg,
> +		pch_phub_reg.comp_resp_timeout_reg,
> +		pch_phub_reg.bus_slave_control_reg,
> +		pch_phub_reg.deadlock_avoid_type_reg,
> +		pch_phub_reg.intpin_reg_wpermit_reg0,
> +		pch_phub_reg.intpin_reg_wpermit_reg1,
> +		pch_phub_reg.intpin_reg_wpermit_reg2,
> +		pch_phub_reg.intpin_reg_wpermit_reg3);
> +	/* to store contents of INT_REDUCE_CONTROL registers */
> +	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
> +		pch_phub_reg.int_reduce_control_reg[i] =
> +		    ioread32(p +
> +			PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
> +		dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
> +			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
> +			i, pch_phub_reg.int_reduce_control_reg[i]);
> +	}
> +	/* save clk cfg register */
> +	pch_phub_reg.clkcfg_reg = ioread32(p + CLKCFG_REG_OFFSET);
> +
> +	return;
> +}
> +
>
> ...
>
> +static void pch_phub_restore_reg_conf(struct pci_dev *pdev)
> +{
> +	unsigned int i;
> +	void __iomem *p = pch_phub_reg.pch_phub_base_address;
> +
> +	dev_dbg(&pdev->dev, "pch_phub_restore_reg_conf ENTRY\n");
> +	/* to store contents of PHUB_ID register */
> +	iowrite32(pch_phub_reg.phub_id_reg, p + PCH_PHUB_PHUB_ID_REG);
> +	/* to store contents of QUEUE_PRI_VAL register */
> +	iowrite32(pch_phub_reg.q_pri_val_reg, p + PCH_PHUB_QUEUE_PRI_VAL_REG);
> +	/* to store contents of RC_QUEUE_MAXSIZE register */
> +	iowrite32(pch_phub_reg.rc_q_maxsize_reg,
> +					p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
> +	/* to store contents of BRI_QUEUE_MAXSIZE register */
> +	iowrite32(pch_phub_reg.bri_q_maxsize_reg,
> +					p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
> +	/* to store contents of COMP_RESP_TIMEOUT register */
> +	iowrite32(pch_phub_reg.comp_resp_timeout_reg,
> +					p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
> +	/* to store contents of BUS_SLAVE_CONTROL_REG register */
> +	iowrite32(pch_phub_reg.bus_slave_control_reg,
> +					p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
> +	/* to store contents of DEADLOCK_AVOID_TYPE register */
> +	iowrite32(pch_phub_reg.deadlock_avoid_type_reg,
> +					p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
> +	/* to store contents of INTPIN_REG_WPERMIT register 0 */
> +	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg0,
> +					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
> +	/* to store contents of INTPIN_REG_WPERMIT register 1 */
> +	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg1,
> +					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
> +	/* to store contents of INTPIN_REG_WPERMIT register 2 */
> +	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg2,
> +					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
> +	/* to store contents of INTPIN_REG_WPERMIT register 3 */
> +	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg3,
> +					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
> +	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
> +		"pch_phub_reg.phub_id_reg=%x, "
> +		"pch_phub_reg.q_pri_val_reg=%x, "
> +		"pch_phub_reg.rc_q_maxsize_reg=%x, "
> +		"pch_phub_reg.bri_q_maxsize_reg=%x, "
> +		"pch_phub_reg.comp_resp_timeout_reg=%x, "
> +		"pch_phub_reg.bus_slave_control_reg=%x, "
> +		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
> +		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
> +		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
> +		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
> +		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n",
> +		pch_phub_reg.phub_id_reg,
> +		pch_phub_reg.q_pri_val_reg,
> +		pch_phub_reg.rc_q_maxsize_reg,
> +		pch_phub_reg.bri_q_maxsize_reg,
> +		pch_phub_reg.comp_resp_timeout_reg,
> +		pch_phub_reg.bus_slave_control_reg,
> +		pch_phub_reg.deadlock_avoid_type_reg,
> +		pch_phub_reg.intpin_reg_wpermit_reg0,
> +		pch_phub_reg.intpin_reg_wpermit_reg1,
> +		pch_phub_reg.intpin_reg_wpermit_reg2,
> +		pch_phub_reg.intpin_reg_wpermit_reg3);
> +	/* to store contents of INT_REDUCE_CONTROL register */
> +	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
> +		iowrite32(pch_phub_reg.int_reduce_control_reg[i],
> +			p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
> +		dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
> +			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
> +			i, pch_phub_reg.int_reduce_control_reg[i]);
> +	}
> +
> +	/*restore the clock config reg */

One space after the /*, please.

> +	iowrite32(pch_phub_reg.clkcfg_reg, p + CLKCFG_REG_OFFSET);
> +
> +	return;
> +}
> +
> +/** pch_phub_read_serial_rom - Implements the functionality of reading Serial
> + *									 ROM.
> + *  @offset_address: Contains the Serial ROM address offset value
> + *  @data: Contains the Serial ROM value
> + */
> +static int pch_phub_read_serial_rom(unsigned int offset_address,
> +							 unsigned char *data)
> +{
> +	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +\
> +								 offset_address;

Unneeded \.  There are several instances of this in the driver.

> +	*data = ioread8(mem_addr);
> +
> +	return 0;
> +}
> +
> +/** pch_phub_write_serial_rom - Implements the functionality of writing Serial
> + *									 ROM.

Strange layout.  I don't know what this will look like after kerneldoc
processing - it might make a mess.  Conventional layout is

/**
 * pch_phub_write_serial_rom - Implements the functionality of writing Serial ROM.

or

/**
 * pch_phub_write_serial_rom - Implements the functionality of writing Serial
 * ROM.

(there are several instances of this).


> + *  @offset_address: Contains the Serial ROM address offset value
> + *  @data: Contains the Serial ROM value
> + */
> +static int pch_phub_write_serial_rom(unsigned int offset_address,
> +							 unsigned char data)
> +{
> +	int retval = 0;
> +	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +\
> +						 (offset_address & 0xFFFFFFFC);
> +	int i = 0;
> +	unsigned int word_data = 0;
> +
> +	iowrite32(PCH_PHUB_ROM_WRITE_ENABLE,
> +			pch_phub_reg.pch_phub_extrom_base_address +\
> +								 PHUB_CONTROL);
> +
> +	word_data = ioread32(mem_addr);

The driver tends to do

	int foo = 0;

	...

	foo = ...

in quite a lot of places.  The initialisation to zero is harmless - the
compiler will take it out again.  But it's unusual and jsut adds noise.

The compiler will warn about use of uninitialised variables anyway.  In
fact this unnecessary initalisation will suppress compiler warnings
which might have revealed real bugs.  I'd suggest that they all be
removed (there are quite a lot in this driver).

> +	switch (offset_address % 4) {
> +	case 0:
> +		word_data &= 0xFFFFFF00;
> +		iowrite32((word_data | (unsigned int)data),
> +						mem_addr);
> +		break;
> +	case 1:
> +		word_data &= 0xFFFF00FF;
> +		iowrite32((word_data | ((unsigned int)data << 8)),
> +						mem_addr);
> +		break;
> +	case 2:
> +		word_data &= 0xFF00FFFF;
> +		iowrite32((word_data | ((unsigned int)data << 16)),
> +						mem_addr);
> +		break;
> +	case 3:
> +		word_data &= 0x00FFFFFF;
> +		iowrite32((word_data | ((unsigned int)data << 24)),
> +						mem_addr);
> +		break;
> +	}
> +	while (0x00 !=
> +	       ioread8(pch_phub_reg.pch_phub_extrom_base_address +\
> +							 PHUB_STATUS)) {
> +		msleep(1);
> +		if (PHUB_TIMEOUT == i) {
> +			retval = -EPERM;
> +			break;
> +		}
> +		i++;
> +	}
> +
> +	iowrite32(PCH_PHUB_ROM_WRITE_DISABLE,
> +			pch_phub_reg.pch_phub_extrom_base_address +\
> +								 PHUB_CONTROL);
> +
> +	return retval;
> +}
> +
>
> ...
>
> +static ssize_t pch_phub_read(struct file *file, char __user *buf, size_t size,
> +								 loff_t *ppos)
> +{
> +	unsigned int rom_signature = 0;
> +	unsigned char rom_length;
> +	int ret_value;
> +	unsigned int tmp;
> +	unsigned char data;
> +	unsigned int addr_offset = 0;
> +	unsigned int orom_size;
> +	loff_t pos = *ppos;
> +
> +	if (pos < 0)
> +		return -EINVAL;

I think vfs_read() -> rw_verify_area() already did that, so indovidual
->read() implementations don't need to check for negative offset.

> +	/*Get Rom signature*/

Spaces after /* and before */, please.

> +	pch_phub_read_serial_rom(0x80, (unsigned char *)&rom_signature);
> +	pch_phub_read_serial_rom(0x81, (unsigned char *)&tmp);
> +	rom_signature |= (tmp & 0xff) << 8;
> +	if (rom_signature == 0xAA55) {
> +		pch_phub_read_serial_rom(0x82, &rom_length);
> +		orom_size = rom_length * 512;
> +		if (orom_size < pos)
> +			return 0;
> +
> +		for (addr_offset = 0; addr_offset < size; addr_offset++) {
> +			pch_phub_read_serial_rom(0x80 + addr_offset + pos,
> +									 &data);
> +			ret_value = copy_to_user(&buf[addr_offset], &data, 1);
> +			if (ret_value)
> +				return -EFAULT;
> +
> +			if (orom_size < pos + addr_offset) {
> +				*ppos += addr_offset;
> +				return addr_offset;
> +			}
> +
> +		}
> +	} else {
> +		return -ENOEXEC;

ENOEXEC means "your executable file doesn't have suitable contents". 
If this error gets propagated to userspace the poor user will be
wondering who corrupted his ELF files and would be surprised to find
out that the error in fact came from his packethub driver.

So please use a more appropriate errno here.

> +	}
> +	*ppos += addr_offset;
> +	return addr_offset;
> +}
> +
> +/** pch_phub_write - Implements the write functionality of the Packet Hub
> + *									 module.
> + *  @file: Contains the reference of the file structure
> + *  @buf: Usermode buffer pointer
> + *  @size: Usermode buffer size
> + *  @ppos: Contains the reference of the file structure
> + */
> +static ssize_t pch_phub_write(struct file *file, const char __user *buf,
> +						 size_t size, loff_t *ppos)
> +{
> +	unsigned int data;
> +	int ret_value;
> +	unsigned int addr_offset = 0;
> +	loff_t pos = *ppos;
> +
> +	if (pos < 0)
> +		return -EINVAL;

vfs_write() already did that.

> +	for (addr_offset = 0; addr_offset < size; addr_offset++) {
> +		ret_value = get_user(data, &buf[addr_offset]);
> +		if (ret_value)
> +			return -EFAULT;
> +
> +		ret_value = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
> +								 data);
> +		if (ret_value)
> +			return -EIO;

This overwrites the pch_phub_write_serial_rom() return value.  it's
generally better to propagate error return values instead of replacing
them with different ones.

otoh pch_phub_write_serial_rom() can return -EPERM.  "Operation not
permitted.  An attempt was made to perform an operation limited to
processes with appropriate privileges or to the owner of a file or
other resource.".  That doesn't sound like an appropriate errno for a
driver to use?

> +		if (PCH_PHUB_OROM_SIZE < pos + addr_offset) {
> +			*ppos += addr_offset;
> +			return addr_offset;
> +		}
> +
> +	}
> +
> +	*ppos += addr_offset;
> +	return addr_offset;
> +}
> +
> +
> +/** pch_phub_ioctl - Implements the various ioctl functionalities of the Packet
> + *								 Hub module.
> + *  @inode: Contains the reference of the inode structure
> + *  @file: Contains the reference of the file structure
> + *  @cmd: Contains the command value
> + *  @arg: Contains the command argument value
> + */
> +
> +
> +static long pch_phub_ioctl(struct file *file, unsigned int cmd,
> +							 unsigned long arg)
> +{
> +	int ret_value = 0;
> +	__u8 mac_addr[6];
> +	int ret;
> +	unsigned int i;
> +	void __user *varg = (void __user *)arg;
> +
> +	ret = mutex_lock_interruptible(&pch_phub_ioctl_mutex);
> +	if (ret) {
> +		ret_value = -ERESTARTSYS;
> +		goto return_nomutex;
> +	}

A plain old `return -ERESTARTSYS' is OK here.

> +	if (pch_phub_reg.pch_phub_suspended == true) {
> +		ret_value = -EPERM;

EPERM?

> +		goto return_ioctrl;
> +	}
> +
> +	switch (cmd) {
> +	case IOCTL_PHUB_READ_MAC_ADDR:
> +		for (i = 0; i < 6; i++)
> +			pch_phub_read_gbe_mac_addr(i, &mac_addr[i]);
> +
> +		ret_value = copy_to_user(varg,
> +					 mac_addr, sizeof(mac_addr));
> +		if (ret_value) {
> +			ret_value = -EFAULT;
> +			goto return_ioctrl;
> +		}

The goto is unneeded.

> +		break;
> +
> +	case IOCTL_PHUB_WRITE_MAC_ADDR:
> +		ret_value = copy_from_user(mac_addr, varg, sizeof(mac_addr));
> +
> +		if (ret_value) {
> +			ret_value = -EFAULT;
> +			goto return_ioctrl;
> +		}

Could do a `break'.

> +		for (i = 0; i < 6; i++)
> +			pch_phub_write_gbe_mac_addr(i, mac_addr[i]);
> +		break;
> +
> +	default:
> +		ret_value = -EINVAL;
> +		break;
> +	}
> +return_ioctrl:
> +	mutex_unlock(&pch_phub_ioctl_mutex);
> +return_nomutex:
> +	return ret_value;
> +}
> +
> +
> +/**
> + * file_operations structure initialization
> + */

That's not a kerneldoc comment, but it has the /** kerneldoc token.

> +static const struct file_operations pch_phub_fops = {
> +	.owner = THIS_MODULE,
> +	.read = pch_phub_read,
> +	.write = pch_phub_write,
> +	.unlocked_ioctl = pch_phub_ioctl,
> +	.llseek = default_llseek
> +};
> +
> +
> +/** pch_phub_probe - Implements the probe functionality of the module.
> + *  @pdev: Contains the reference of the pci_dev structure
> + *  @id: Contains the reference of the pci_device_id structure
> + */
> +static int __devinit pch_phub_probe(struct pci_dev *pdev,
> +				       const struct pci_device_id *id)
> +{
> +	char *DRIVER_NAME = "pch_phub";

hm, why was that upper case?

Maybe use MODULE_NAME instead?  Or

	static const char driver_name[] = "pch_phub";

> +	int ret;
> +	unsigned int rom_size;
> +
> +	pch_phub_major_no = (pch_phub_major_no < 0 || pch_phub_major_no > 254) ?
> +				0 : pch_phub_major_no;

This looks odd - should be explained in code comments and/or changelog.

> +	ret = pci_enable_device(pdev);
> +	if (ret) {
> +		dev_dbg(&pdev->dev,
> +				"\npch_phub_probe : pci_enable_device FAILED");
> +		goto err_probe;
> +	}
> +	dev_dbg(&pdev->dev, "pch_phub_probe : "
> +			"pci_enable_device returns %d\n", ret);
> +
> +	ret = pci_request_regions(pdev, DRIVER_NAME);
> +	if (ret) {
> +		dev_dbg(&pdev->dev,
> +				"pch_phub_probe : pci_request_regions FAILED");
> +		pci_disable_device(pdev);
> +		goto err_probe;
> +	}
> +	dev_dbg(&pdev->dev, "pch_phub_probe : "
> +		"pci_request_regions returns %d\n", ret);
> +
> +	pch_phub_reg.pch_phub_base_address = \
> +					(void __iomem *)pci_iomap(pdev, 1, 0);

Unneeded and undesirable typeast.

> +	if (pch_phub_reg.pch_phub_base_address == 0) {
> +		dev_dbg(&pdev->dev, "pch_phub_probe : pci_iomap FAILED");
> +		pci_release_regions(pdev);
> +		pci_disable_device(pdev);
> +		ret = -ENOMEM;
> +		goto err_probe;
> +	}
> +	dev_dbg(&pdev->dev, "pch_phub_probe : pci_iomap SUCCESS and value "
> +		"in pch_phub_base_address variable is 0x%08x\n",
> +		(unsigned int)pch_phub_reg.pch_phub_base_address);
> +
> +	pch_phub_reg.pch_phub_extrom_base_address =
> +	    (void __iomem *)pci_map_rom(pdev, &rom_size);

Ditto

> +	if (pch_phub_reg.pch_phub_extrom_base_address == 0) {
> +		dev_dbg(&pdev->dev, "pch_phub_probe : pci_map_rom FAILED");
> +		pci_iounmap(pdev, (void *)pch_phub_reg.pch_phub_base_address);
> +		pci_release_regions(pdev);
> +		pci_disable_device(pdev);

This is getting painful.  I'd suggest removal of the duplicated code
via the standard `goto out_iounmap' unwinding approach.

> +		ret = -ENOMEM;
> +		goto err_probe;
> +	}
> +	dev_dbg(&pdev->dev, "pch_phub_probe : "
> +		"pci_map_rom SUCCESS and value in "
> +		"pch_phub_extrom_base_address variable is 0x%08x\n",
> +		(unsigned int)pch_phub_reg.pch_phub_extrom_base_address);
> +
> +	if (pch_phub_major_no) {
> +		pch_phub_dev_no = MKDEV(pch_phub_major_no, 0);
> +		ret = register_chrdev_region(pch_phub_dev_no,
> +					   PCH_MINOR_NOS, DRIVER_NAME);
> +		if (ret) {
> +			dev_dbg(&pdev->dev, "pch_phub_probe : "
> +				"register_chrdev_region FAILED");
> +			pci_unmap_rom(pdev,
> +			(void *)pch_phub_reg.pch_phub_extrom_base_address);
> +			pci_iounmap(pdev,
> +				(void *)pch_phub_reg.pch_phub_base_address);

More unneeded typecasts.

> +			pci_release_regions(pdev);
> +			pci_disable_device(pdev);
> +			goto err_probe;
> +		}
> +		dev_dbg(&pdev->dev, "pch_phub_probe : "
> +				"register_chrdev_region returns %d\n", ret);
> +	} else {
> +		ret = alloc_chrdev_region(&pch_phub_dev_no, 0,
> +						PCH_MINOR_NOS, DRIVER_NAME);
> +		if (ret) {
> +			dev_dbg(&pdev->dev, "pch_phub_probe : "
> +					"alloc_chrdev_region FAILED");
> +			pci_unmap_rom(pdev,
> +			(void *)pch_phub_reg.pch_phub_extrom_base_address);
> +			pci_iounmap(pdev,
> +				    (void *)pch_phub_reg.pch_phub_base_address);

more..

> +			pci_release_regions(pdev);
> +			pci_disable_device(pdev);
> +			goto err_probe;
> +		}
> +		dev_dbg(&pdev->dev, "pch_phub_probe : "
> +			"alloc_chrdev_region returns %d\n", ret);
> +	}
> +
> +	cdev_init(&pch_phub_dev, &pch_phub_fops);
> +	dev_dbg(&pdev->dev,
> +			"pch_phub_probe :  cdev_init invoked successfully\n");
> +
> +	pch_phub_dev.owner = THIS_MODULE;
> +	pch_phub_dev.ops = &pch_phub_fops;
> +
> +	ret = cdev_add(&pch_phub_dev, pch_phub_dev_no, PCH_MINOR_NOS);
> +	if (ret) {
> +		dev_dbg(&pdev->dev, "pch_phub_probe :  cdev_add FAILED");
> +		unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
> +		pci_unmap_rom(pdev,
> +			(void *)pch_phub_reg.pch_phub_extrom_base_address);
> +		pci_iounmap(pdev, (void *)pch_phub_reg.pch_phub_base_address);
> +		pci_release_regions(pdev);
> +		pci_disable_device(pdev);
> +		goto err_probe;
> +	}
> +	dev_dbg(&pdev->dev, "pch_phub_probe :  cdev_add returns %d\n", ret);
> +
> +	/*set the clock config reg if CAN clock is 50Mhz */
> +	dev_dbg(&pdev->dev, "pch_phub_probe : invoking "
> +		"pch_phub_read_modify_write_reg "
> +		"to set CLKCFG reg for CAN clk 50Mhz\n");
> +	pch_phub_read_modify_write_reg((unsigned int)CLKCFG_REG_OFFSET,
> +					  CLKCFG_CAN_50MHZ, CLKCFG_CANCLK_MASK);
> +
> +	/* set the prefech value */
> +	iowrite32(0x000ffffa, pch_phub_reg.pch_phub_base_address + 0x14);
> +	/* set the interrupt delay value */
> +	iowrite32(0x25, pch_phub_reg.pch_phub_base_address + 0x44);
> +	return 0;
> +
> +err_probe:
> +	dev_dbg(&pdev->dev, "pch_phub_probe returns %d\n", ret);
> +	return ret;
> +}
> +
>
> ...
>
> +#ifdef CONFIG_PM
> +
> +/** pch_phub_suspend - Implements the suspend functionality of the module.
> + *  @pdev: Contains the reference of the pci_dev structure
> + *  @state: Contains the reference of the pm_message_t structure
> + */
> +static int pch_phub_suspend(struct pci_dev *pdev, pm_message_t state)
> +{
> +	int ret;
> +
> +	pch_phub_reg.pch_phub_suspended = true;/* For blocking further IOCTLs */
> +
> +	pch_phub_save_reg_conf(pdev);
> +	dev_dbg(&pdev->dev, "pch_phub_suspend - "
> +		"pch_phub_save_reg_conf Invoked successfully\n");
> +
> +	ret = pci_save_state(pdev);
> +	if (ret) {
> +		dev_dbg(&pdev->dev,
> +			" pch_phub_suspend -pci_save_state returns-%d\n", ret);
> +		return ret;
> +	}
> +	dev_dbg(&pdev->dev,
> +			"pch_phub_suspend - pci_save_state returns %d\n", ret);
> +	pci_enable_wake(pdev, PCI_D3hot, 0);
> +	dev_dbg(&pdev->dev, "pch_phub_suspend - "
> +			"pci_enable_wake Invoked successfully\n");
> +
> +	pci_disable_device(pdev);
> +	dev_dbg(&pdev->dev, "pch_phub_suspend - "
> +			"pci_disable_device Invoked successfully\n");
> +
> +	pci_set_power_state(pdev, pci_choose_state(pdev, state));
> +	dev_dbg(&pdev->dev, "pch_phub_suspend - "
> +			"pci_set_power_state Invoked successfully   "
> +			"return = %d\n", 0);
> +
> +	return 0;
> +}
> +
> +/** pch_phub_resume - Implements the resume functionality of the module.
> + *  @pdev: Contains the reference of the pci_dev structure
> + */
> +static int pch_phub_resume(struct pci_dev *pdev)
> +{
> +
> +	int ret;
> +
> +	pci_set_power_state(pdev, PCI_D0);
> +	dev_dbg(&pdev->dev, "pch_phub_resume - "
> +		"pci_set_power_state Invoked successfully\n");
> +
> +	pci_restore_state(pdev);
> +	dev_dbg(&pdev->dev, "pch_phub_resume - "
> +		"pci_restore_state Invoked successfully\n");
> +
> +	ret = pci_enable_device(pdev);
> +	if (ret) {
> +		dev_dbg(&pdev->dev,
> +				"pch_phub_resume-pci_enable_device failed ");
> +		return ret;
> +	}
> +
> +	dev_dbg(&pdev->dev, "pch_phub_resume - "
> +			"pci_enable_device returns -%d\n", ret);
> +
> +	pci_enable_wake(pdev, PCI_D3hot, 0);
> +	dev_dbg(&pdev->dev, "pch_phub_resume - "
> +			"pci_enable_wake Invoked successfully\n");
> +
> +	pch_phub_restore_reg_conf(pdev);
> +	dev_dbg(&pdev->dev, "pch_phub_resume - "
> +		"pch_phub_restore_reg_conf Invoked successfully\n");
> +
> +	pch_phub_reg.pch_phub_suspended = false;
> +
> +	dev_dbg(&pdev->dev, "pch_phub_resume  returns- %d\n", 0);
> +	return 0;
> +}

Here you can put

#else
#define pch_phub_suspend NULL
#define pch_phub_resume NULL

> +#endif /* CONFIG_PM */
> +
> +static struct pci_device_id pch_phub_pcidev_id[] = {
> +
> +	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH1_PHUB)},
> +	{0,}
> +};
> +
> +
> +static struct pci_driver pch_phub_driver = {
> +	.name = "pch_phub",
> +	.id_table = pch_phub_pcidev_id,
> +	.probe = pch_phub_probe,
> +	.remove = __devexit_p(pch_phub_remove),
> +#ifdef CONFIG_PM
> +	.suspend = pch_phub_suspend,
> +	.resume = pch_phub_resume
> +#endif

and then remove this ifdef.

>
> ...
>
> --- /dev/null
> +++ b/drivers/char/pch_phub/pch_phub.h
> @@ -0,0 +1,58 @@
> +#ifndef __PCH_PHUB_H__
> +#define __PCH_PHUB_H__
> +/*!
> + * @file pch_phub.h
> + * @brief Provides all the interfaces pertaining to the Packet Hub module.
> + * @version 1.0.0.0
> + * @section

?

> + * 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; version 2 of the License.
> + *
> + * 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, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
> + */
> +
> +/*
> + * History:
> + * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
> + *
> + * created:
> + *	OKI SEMICONDUCTOR 04/14/2010
> + * modified:
> + *
> + */
> +
> +#define PHUB_IOCTL_MAGIC		(0xf7)
> +
> +/*Outlines the read mac address function signature. */

Space after /*, please (check the whole patch)

> +#define IOCTL_PHUB_READ_MAC_ADDR (_IOR(PHUB_IOCTL_MAGIC, 6, __u8[6]))
> +
> +/*brief Outlines the write mac address function signature. */
> +#define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 7, __u8[6]))

"brief"?

I didn't notice any documentation for these ioctls anywhere?

> +
> +/* Registers address offset */
> +#define PCH_PHUB_PHUB_ID_REG			0x0000
> +#define PCH_PHUB_QUEUE_PRI_VAL_REG		0x0004
> +#define PCH_PHUB_RC_QUEUE_MAXSIZE_REG		0x0008
> +#define PCH_PHUB_BRI_QUEUE_MAXSIZE_REG		0x000C
> +#define PCH_PHUB_COMP_RESP_TIMEOUT_REG		0x0010
> +#define PCH_PHUB_BUS_SLAVE_CONTROL_REG		0x0014
> +#define PCH_PHUB_DEADLOCK_AVOID_TYPE_REG	0x0018
> +#define PCH_PHUB_INTPIN_REG_WPERMIT_REG0	0x0020
> +#define PCH_PHUB_INTPIN_REG_WPERMIT_REG1	0x0024
> +#define PCH_PHUB_INTPIN_REG_WPERMIT_REG2	0x0028
> +#define PCH_PHUB_INTPIN_REG_WPERMIT_REG3	0x002C
> +#define PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE	0x0040
> +#define CLKCFG_REG_OFFSET			0x500
> +
> +#define PCH_PHUB_OROM_SIZE 15360
> +
> +#endif


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

* Re: [PATCH] Topcliff PHUB: Generate PacketHub driver
  2010-06-22 22:12   ` Andrew Morton
@ 2010-06-23  0:31     ` Masayuki Ohtake
  0 siblings, 0 replies; 46+ messages in thread
From: Masayuki Ohtake @ 2010-06-23  0:31 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Arnd Bergmann, Wang, Yong Y, qi.wang, joel.clark,
	andrew.chih.howe.khor, Alan Cox, LKML

Hi Andrew

Thank you for your comments.
We will resubmit modified our patch by tomorrow or day after tomorrow at the latest.

Thanks.
Ohtake
----- Original Message ----- 
From: "Andrew Morton" <akpm@linux-foundation.org>
To: "Masayuki Ohtak" <masa-korg@dsn.okisemi.com>
Cc: "Arnd Bergmann" <arnd@arndb.de>; "Wang, Yong Y" <yong.y.wang@intel.com>; <qi.wang@intel.com>;
<joel.clark@intel.com>; <andrew.chih.howe.khor@intel.com>; "Alan Cox" <alan@lxorguk.ukuu.org.uk>; "LKML"
<linux-kernel@vger.kernel.org>
Sent: Wednesday, June 23, 2010 7:12 AM
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver


> On Tue, 22 Jun 2010 19:33:34 +0900
> Masayuki Ohtak <masa-korg@dsn.okisemi.com> wrote:
>
> > Hi Arnd and Yong Y
> >
> > We have updated phub patch about the following.
> >  * Arnd's commnets
> > * Delete PCH_READ_REG/PCH_WRITE_REG
> > * Delete '_t' prefix
> > * Modify basic type
> > * Delete needless 'static' prefix
> > * Modify returned value
> > * Care returned value of get_user()
> > * Add .llseek line
> >
> >  * Yong Y's comments
> > * Applying to the latest checkpatch(2.6.35)
> > * Delete unused 'DEBUG' macro in Makefile
> > * Delete IEEE1588 lines
> > * Delete 'PCH_CAN_PCLK_50MHZ'
> >
> > Thanks, Ohtake.
> >
> > Kernel=2.6.33.1
> > Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
>
> Please prepare a proper changelog for the patch - one which is suitabe
> for inclusion in the kernel's permanent git record.  Include that
> changelog with each version of the patch, perhaps after alterations.
>
> Please describe the module parameters in that changelog.  Please
> describe the major/minor number handling within the changelog -
> permitting the major number to be specified on the modprobe command
> line is unusual and should be fully described and justified, please.
>
> Please ensure that the changelog tells us what the driver actually
> does!  I don't even know what a "PCH packet hub" _is_ :(
>
> >
> > ...
> >
> > --- /dev/null
> > +++ b/drivers/char/pch_phub/pch_phub.c
> > @@ -0,0 +1,937 @@
> > +/*!
> > + * @file pch_phub.c
> > + * @brief Provides all the implementation of the interfaces pertaining to
> > + * the Packet Hub module.
> > + * @version 1.0.0.0
> > + * @section
>
> This appears to use a markup format whcih the kernel doesn't implement.
>
> > + * 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; version 2 of the License.
> > + *
> > + * 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, write to the Free Software
> > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
> > + */
> >
> > ...
> >
> > +/** pch_phub_read_modify_write_reg - Implements the functionality of reading,
> > + * modifying and writing register.
> > + *  @reg_addr_offset:  Contains the register offset address value
> > + *  @data:             Contains the writing value
> > + *  @mask:             Contains the mask value
> > + */
>
> kerneldoc comments are usually formatted as
>
> /**
>  *  foo() - do something
>  *  @arg1 ...
>
> I don't know whether the layout whcih this driver uses will be properly
> handled by the kerneldoc tools, but I'd suggest that it all be
> converted to the more usual style for consistency.
>
> > +static void pch_phub_read_modify_write_reg(unsigned int reg_addr_offset,
> > +        unsigned int data, unsigned int mask)
> > +{
> > + void __iomem *reg_addr = pch_phub_reg.pch_phub_base_address +\
> > + reg_addr_offset;
> > + iowrite32(((ioread32(reg_addr) & ~mask)) | data, reg_addr);
> > + return;
> > +}
> > +
> > +
> > +/** pch_phub_save_reg_conf - saves register configuration
> > + */
>
> The driver has quite a lot of comments which use the kerneldoc toke
> "/**" but which don't really look like they wre intended to be
> kerneldoc comments.  So I'd suggest converting these to plain old comments:
>
> /*
>  * pch_phub_save_reg_conf - saves register configuration
>  */
>
> or
>
> /* pch_phub_save_reg_conf - saves register configuration */
>
>
> or finish off the kerneldoc work by documenting the arguments (and the
> return values, please).
>
> > +static void pch_phub_save_reg_conf(struct pci_dev *pdev)
> > +{
> > + unsigned int i = 0;
> > + void __iomem *p = pch_phub_reg.pch_phub_base_address;
> > +
> > + dev_dbg(&pdev->dev, "pch_phub_save_reg_conf ENTRY\n");
> > + /* to store contents of PHUB_ID register */
> > + pch_phub_reg.phub_id_reg = ioread32(p + PCH_PHUB_PHUB_ID_REG);
> > + /* to store contents of QUEUE_PRI_VAL register */
> > + pch_phub_reg.q_pri_val_reg = ioread32(p + PCH_PHUB_QUEUE_PRI_VAL_REG);
> > + /* to store contents of RC_QUEUE_MAXSIZE register */
> > + pch_phub_reg.rc_q_maxsize_reg =
> > +     ioread32(p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
> > + /* to store contents of BRI_QUEUE_MAXSIZE register */
> > + pch_phub_reg.bri_q_maxsize_reg =
> > +     ioread32(p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
> > + /* to store contents of COMP_RESP_TIMEOUT register */
> > + pch_phub_reg.comp_resp_timeout_reg =
> > +     ioread32(p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
> > + /* to store contents of BUS_SLAVE_CONTROL_REG register */
> > + pch_phub_reg.bus_slave_control_reg =
> > +     ioread32(p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
> > + /* to store contents of DEADLOCK_AVOID_TYPE register */
> > + pch_phub_reg.deadlock_avoid_type_reg =
> > +     ioread32(p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
> > + /* to store contents of INTPIN_REG_WPERMIT register 0 */
> > + pch_phub_reg.intpin_reg_wpermit_reg0 =
> > +     ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
> > + /* to store contents of INTPIN_REG_WPERMIT register 1 */
> > + pch_phub_reg.intpin_reg_wpermit_reg1 =
> > +     ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
> > + /* to store contents of INTPIN_REG_WPERMIT register 2 */
> > + pch_phub_reg.intpin_reg_wpermit_reg2 =
> > +     ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
> > + /* to store contents of INTPIN_REG_WPERMIT register 3 */
>
> s/to //g
>
> > + pch_phub_reg.intpin_reg_wpermit_reg3 =
> > +     ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
> > + dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
> > + "pch_phub_reg.phub_id_reg=%x, "
> > + "pch_phub_reg.q_pri_val_reg=%x, "
> > + "pch_phub_reg.rc_q_maxsize_reg=%x, "
> > + "pch_phub_reg.bri_q_maxsize_reg=%x, "
> > + "pch_phub_reg.comp_resp_timeout_reg=%x, "
> > + "pch_phub_reg.bus_slave_control_reg=%x, "
> > + "pch_phub_reg.deadlock_avoid_type_reg=%x, "
> > + "pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
> > + "pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
> > + "pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
> > + "pch_phub_reg.intpin_reg_wpermit_reg3=%x\n",
> > + pch_phub_reg.phub_id_reg,
> > + pch_phub_reg.q_pri_val_reg,
> > + pch_phub_reg.rc_q_maxsize_reg,
> > + pch_phub_reg.bri_q_maxsize_reg,
> > + pch_phub_reg.comp_resp_timeout_reg,
> > + pch_phub_reg.bus_slave_control_reg,
> > + pch_phub_reg.deadlock_avoid_type_reg,
> > + pch_phub_reg.intpin_reg_wpermit_reg0,
> > + pch_phub_reg.intpin_reg_wpermit_reg1,
> > + pch_phub_reg.intpin_reg_wpermit_reg2,
> > + pch_phub_reg.intpin_reg_wpermit_reg3);
> > + /* to store contents of INT_REDUCE_CONTROL registers */
> > + for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
> > + pch_phub_reg.int_reduce_control_reg[i] =
> > +     ioread32(p +
> > + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
> > + dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
> > + "pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
> > + i, pch_phub_reg.int_reduce_control_reg[i]);
> > + }
> > + /* save clk cfg register */
> > + pch_phub_reg.clkcfg_reg = ioread32(p + CLKCFG_REG_OFFSET);
> > +
> > + return;
> > +}
> > +
> >
> > ...
> >
> > +static void pch_phub_restore_reg_conf(struct pci_dev *pdev)
> > +{
> > + unsigned int i;
> > + void __iomem *p = pch_phub_reg.pch_phub_base_address;
> > +
> > + dev_dbg(&pdev->dev, "pch_phub_restore_reg_conf ENTRY\n");
> > + /* to store contents of PHUB_ID register */
> > + iowrite32(pch_phub_reg.phub_id_reg, p + PCH_PHUB_PHUB_ID_REG);
> > + /* to store contents of QUEUE_PRI_VAL register */
> > + iowrite32(pch_phub_reg.q_pri_val_reg, p + PCH_PHUB_QUEUE_PRI_VAL_REG);
> > + /* to store contents of RC_QUEUE_MAXSIZE register */
> > + iowrite32(pch_phub_reg.rc_q_maxsize_reg,
> > + p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
> > + /* to store contents of BRI_QUEUE_MAXSIZE register */
> > + iowrite32(pch_phub_reg.bri_q_maxsize_reg,
> > + p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
> > + /* to store contents of COMP_RESP_TIMEOUT register */
> > + iowrite32(pch_phub_reg.comp_resp_timeout_reg,
> > + p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
> > + /* to store contents of BUS_SLAVE_CONTROL_REG register */
> > + iowrite32(pch_phub_reg.bus_slave_control_reg,
> > + p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
> > + /* to store contents of DEADLOCK_AVOID_TYPE register */
> > + iowrite32(pch_phub_reg.deadlock_avoid_type_reg,
> > + p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
> > + /* to store contents of INTPIN_REG_WPERMIT register 0 */
> > + iowrite32(pch_phub_reg.intpin_reg_wpermit_reg0,
> > + p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
> > + /* to store contents of INTPIN_REG_WPERMIT register 1 */
> > + iowrite32(pch_phub_reg.intpin_reg_wpermit_reg1,
> > + p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
> > + /* to store contents of INTPIN_REG_WPERMIT register 2 */
> > + iowrite32(pch_phub_reg.intpin_reg_wpermit_reg2,
> > + p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
> > + /* to store contents of INTPIN_REG_WPERMIT register 3 */
> > + iowrite32(pch_phub_reg.intpin_reg_wpermit_reg3,
> > + p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
> > + dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
> > + "pch_phub_reg.phub_id_reg=%x, "
> > + "pch_phub_reg.q_pri_val_reg=%x, "
> > + "pch_phub_reg.rc_q_maxsize_reg=%x, "
> > + "pch_phub_reg.bri_q_maxsize_reg=%x, "
> > + "pch_phub_reg.comp_resp_timeout_reg=%x, "
> > + "pch_phub_reg.bus_slave_control_reg=%x, "
> > + "pch_phub_reg.deadlock_avoid_type_reg=%x, "
> > + "pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
> > + "pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
> > + "pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
> > + "pch_phub_reg.intpin_reg_wpermit_reg3=%x\n",
> > + pch_phub_reg.phub_id_reg,
> > + pch_phub_reg.q_pri_val_reg,
> > + pch_phub_reg.rc_q_maxsize_reg,
> > + pch_phub_reg.bri_q_maxsize_reg,
> > + pch_phub_reg.comp_resp_timeout_reg,
> > + pch_phub_reg.bus_slave_control_reg,
> > + pch_phub_reg.deadlock_avoid_type_reg,
> > + pch_phub_reg.intpin_reg_wpermit_reg0,
> > + pch_phub_reg.intpin_reg_wpermit_reg1,
> > + pch_phub_reg.intpin_reg_wpermit_reg2,
> > + pch_phub_reg.intpin_reg_wpermit_reg3);
> > + /* to store contents of INT_REDUCE_CONTROL register */
> > + for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
> > + iowrite32(pch_phub_reg.int_reduce_control_reg[i],
> > + p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
> > + dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
> > + "pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
> > + i, pch_phub_reg.int_reduce_control_reg[i]);
> > + }
> > +
> > + /*restore the clock config reg */
>
> One space after the /*, please.
>
> > + iowrite32(pch_phub_reg.clkcfg_reg, p + CLKCFG_REG_OFFSET);
> > +
> > + return;
> > +}
> > +
> > +/** pch_phub_read_serial_rom - Implements the functionality of reading Serial
> > + * ROM.
> > + *  @offset_address: Contains the Serial ROM address offset value
> > + *  @data: Contains the Serial ROM value
> > + */
> > +static int pch_phub_read_serial_rom(unsigned int offset_address,
> > + unsigned char *data)
> > +{
> > + void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +\
> > + offset_address;
>
> Unneeded \.  There are several instances of this in the driver.
>
> > + *data = ioread8(mem_addr);
> > +
> > + return 0;
> > +}
> > +
> > +/** pch_phub_write_serial_rom - Implements the functionality of writing Serial
> > + * ROM.
>
> Strange layout.  I don't know what this will look like after kerneldoc
> processing - it might make a mess.  Conventional layout is
>
> /**
>  * pch_phub_write_serial_rom - Implements the functionality of writing Serial ROM.
>
> or
>
> /**
>  * pch_phub_write_serial_rom - Implements the functionality of writing Serial
>  * ROM.
>
> (there are several instances of this).
>
>
> > + *  @offset_address: Contains the Serial ROM address offset value
> > + *  @data: Contains the Serial ROM value
> > + */
> > +static int pch_phub_write_serial_rom(unsigned int offset_address,
> > + unsigned char data)
> > +{
> > + int retval = 0;
> > + void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +\
> > + (offset_address & 0xFFFFFFFC);
> > + int i = 0;
> > + unsigned int word_data = 0;
> > +
> > + iowrite32(PCH_PHUB_ROM_WRITE_ENABLE,
> > + pch_phub_reg.pch_phub_extrom_base_address +\
> > + PHUB_CONTROL);
> > +
> > + word_data = ioread32(mem_addr);
>
> The driver tends to do
>
> int foo = 0;
>
> ...
>
> foo = ...
>
> in quite a lot of places.  The initialisation to zero is harmless - the
> compiler will take it out again.  But it's unusual and jsut adds noise.
>
> The compiler will warn about use of uninitialised variables anyway.  In
> fact this unnecessary initalisation will suppress compiler warnings
> which might have revealed real bugs.  I'd suggest that they all be
> removed (there are quite a lot in this driver).
>
> > + switch (offset_address % 4) {
> > + case 0:
> > + word_data &= 0xFFFFFF00;
> > + iowrite32((word_data | (unsigned int)data),
> > + mem_addr);
> > + break;
> > + case 1:
> > + word_data &= 0xFFFF00FF;
> > + iowrite32((word_data | ((unsigned int)data << 8)),
> > + mem_addr);
> > + break;
> > + case 2:
> > + word_data &= 0xFF00FFFF;
> > + iowrite32((word_data | ((unsigned int)data << 16)),
> > + mem_addr);
> > + break;
> > + case 3:
> > + word_data &= 0x00FFFFFF;
> > + iowrite32((word_data | ((unsigned int)data << 24)),
> > + mem_addr);
> > + break;
> > + }
> > + while (0x00 !=
> > +        ioread8(pch_phub_reg.pch_phub_extrom_base_address +\
> > + PHUB_STATUS)) {
> > + msleep(1);
> > + if (PHUB_TIMEOUT == i) {
> > + retval = -EPERM;
> > + break;
> > + }
> > + i++;
> > + }
> > +
> > + iowrite32(PCH_PHUB_ROM_WRITE_DISABLE,
> > + pch_phub_reg.pch_phub_extrom_base_address +\
> > + PHUB_CONTROL);
> > +
> > + return retval;
> > +}
> > +
> >
> > ...
> >
> > +static ssize_t pch_phub_read(struct file *file, char __user *buf, size_t size,
> > + loff_t *ppos)
> > +{
> > + unsigned int rom_signature = 0;
> > + unsigned char rom_length;
> > + int ret_value;
> > + unsigned int tmp;
> > + unsigned char data;
> > + unsigned int addr_offset = 0;
> > + unsigned int orom_size;
> > + loff_t pos = *ppos;
> > +
> > + if (pos < 0)
> > + return -EINVAL;
>
> I think vfs_read() -> rw_verify_area() already did that, so indovidual
> ->read() implementations don't need to check for negative offset.
>
> > + /*Get Rom signature*/
>
> Spaces after /* and before */, please.
>
> > + pch_phub_read_serial_rom(0x80, (unsigned char *)&rom_signature);
> > + pch_phub_read_serial_rom(0x81, (unsigned char *)&tmp);
> > + rom_signature |= (tmp & 0xff) << 8;
> > + if (rom_signature == 0xAA55) {
> > + pch_phub_read_serial_rom(0x82, &rom_length);
> > + orom_size = rom_length * 512;
> > + if (orom_size < pos)
> > + return 0;
> > +
> > + for (addr_offset = 0; addr_offset < size; addr_offset++) {
> > + pch_phub_read_serial_rom(0x80 + addr_offset + pos,
> > + &data);
> > + ret_value = copy_to_user(&buf[addr_offset], &data, 1);
> > + if (ret_value)
> > + return -EFAULT;
> > +
> > + if (orom_size < pos + addr_offset) {
> > + *ppos += addr_offset;
> > + return addr_offset;
> > + }
> > +
> > + }
> > + } else {
> > + return -ENOEXEC;
>
> ENOEXEC means "your executable file doesn't have suitable contents".
> If this error gets propagated to userspace the poor user will be
> wondering who corrupted his ELF files and would be surprised to find
> out that the error in fact came from his packethub driver.
>
> So please use a more appropriate errno here.
>
> > + }
> > + *ppos += addr_offset;
> > + return addr_offset;
> > +}
> > +
> > +/** pch_phub_write - Implements the write functionality of the Packet Hub
> > + * module.
> > + *  @file: Contains the reference of the file structure
> > + *  @buf: Usermode buffer pointer
> > + *  @size: Usermode buffer size
> > + *  @ppos: Contains the reference of the file structure
> > + */
> > +static ssize_t pch_phub_write(struct file *file, const char __user *buf,
> > + size_t size, loff_t *ppos)
> > +{
> > + unsigned int data;
> > + int ret_value;
> > + unsigned int addr_offset = 0;
> > + loff_t pos = *ppos;
> > +
> > + if (pos < 0)
> > + return -EINVAL;
>
> vfs_write() already did that.
>
> > + for (addr_offset = 0; addr_offset < size; addr_offset++) {
> > + ret_value = get_user(data, &buf[addr_offset]);
> > + if (ret_value)
> > + return -EFAULT;
> > +
> > + ret_value = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
> > + data);
> > + if (ret_value)
> > + return -EIO;
>
> This overwrites the pch_phub_write_serial_rom() return value.  it's
> generally better to propagate error return values instead of replacing
> them with different ones.
>
> otoh pch_phub_write_serial_rom() can return -EPERM.  "Operation not
> permitted.  An attempt was made to perform an operation limited to
> processes with appropriate privileges or to the owner of a file or
> other resource.".  That doesn't sound like an appropriate errno for a
> driver to use?
>
> > + if (PCH_PHUB_OROM_SIZE < pos + addr_offset) {
> > + *ppos += addr_offset;
> > + return addr_offset;
> > + }
> > +
> > + }
> > +
> > + *ppos += addr_offset;
> > + return addr_offset;
> > +}
> > +
> > +
> > +/** pch_phub_ioctl - Implements the various ioctl functionalities of the Packet
> > + * Hub module.
> > + *  @inode: Contains the reference of the inode structure
> > + *  @file: Contains the reference of the file structure
> > + *  @cmd: Contains the command value
> > + *  @arg: Contains the command argument value
> > + */
> > +
> > +
> > +static long pch_phub_ioctl(struct file *file, unsigned int cmd,
> > + unsigned long arg)
> > +{
> > + int ret_value = 0;
> > + __u8 mac_addr[6];
> > + int ret;
> > + unsigned int i;
> > + void __user *varg = (void __user *)arg;
> > +
> > + ret = mutex_lock_interruptible(&pch_phub_ioctl_mutex);
> > + if (ret) {
> > + ret_value = -ERESTARTSYS;
> > + goto return_nomutex;
> > + }
>
> A plain old `return -ERESTARTSYS' is OK here.
>
> > + if (pch_phub_reg.pch_phub_suspended == true) {
> > + ret_value = -EPERM;
>
> EPERM?
>
> > + goto return_ioctrl;
> > + }
> > +
> > + switch (cmd) {
> > + case IOCTL_PHUB_READ_MAC_ADDR:
> > + for (i = 0; i < 6; i++)
> > + pch_phub_read_gbe_mac_addr(i, &mac_addr[i]);
> > +
> > + ret_value = copy_to_user(varg,
> > + mac_addr, sizeof(mac_addr));
> > + if (ret_value) {
> > + ret_value = -EFAULT;
> > + goto return_ioctrl;
> > + }
>
> The goto is unneeded.
>
> > + break;
> > +
> > + case IOCTL_PHUB_WRITE_MAC_ADDR:
> > + ret_value = copy_from_user(mac_addr, varg, sizeof(mac_addr));
> > +
> > + if (ret_value) {
> > + ret_value = -EFAULT;
> > + goto return_ioctrl;
> > + }
>
> Could do a `break'.
>
> > + for (i = 0; i < 6; i++)
> > + pch_phub_write_gbe_mac_addr(i, mac_addr[i]);
> > + break;
> > +
> > + default:
> > + ret_value = -EINVAL;
> > + break;
> > + }
> > +return_ioctrl:
> > + mutex_unlock(&pch_phub_ioctl_mutex);
> > +return_nomutex:
> > + return ret_value;
> > +}
> > +
> > +
> > +/**
> > + * file_operations structure initialization
> > + */
>
> That's not a kerneldoc comment, but it has the /** kerneldoc token.
>
> > +static const struct file_operations pch_phub_fops = {
> > + .owner = THIS_MODULE,
> > + .read = pch_phub_read,
> > + .write = pch_phub_write,
> > + .unlocked_ioctl = pch_phub_ioctl,
> > + .llseek = default_llseek
> > +};
> > +
> > +
> > +/** pch_phub_probe - Implements the probe functionality of the module.
> > + *  @pdev: Contains the reference of the pci_dev structure
> > + *  @id: Contains the reference of the pci_device_id structure
> > + */
> > +static int __devinit pch_phub_probe(struct pci_dev *pdev,
> > +        const struct pci_device_id *id)
> > +{
> > + char *DRIVER_NAME = "pch_phub";
>
> hm, why was that upper case?
>
> Maybe use MODULE_NAME instead?  Or
>
> static const char driver_name[] = "pch_phub";
>
> > + int ret;
> > + unsigned int rom_size;
> > +
> > + pch_phub_major_no = (pch_phub_major_no < 0 || pch_phub_major_no > 254) ?
> > + 0 : pch_phub_major_no;
>
> This looks odd - should be explained in code comments and/or changelog.
>
> > + ret = pci_enable_device(pdev);
> > + if (ret) {
> > + dev_dbg(&pdev->dev,
> > + "\npch_phub_probe : pci_enable_device FAILED");
> > + goto err_probe;
> > + }
> > + dev_dbg(&pdev->dev, "pch_phub_probe : "
> > + "pci_enable_device returns %d\n", ret);
> > +
> > + ret = pci_request_regions(pdev, DRIVER_NAME);
> > + if (ret) {
> > + dev_dbg(&pdev->dev,
> > + "pch_phub_probe : pci_request_regions FAILED");
> > + pci_disable_device(pdev);
> > + goto err_probe;
> > + }
> > + dev_dbg(&pdev->dev, "pch_phub_probe : "
> > + "pci_request_regions returns %d\n", ret);
> > +
> > + pch_phub_reg.pch_phub_base_address = \
> > + (void __iomem *)pci_iomap(pdev, 1, 0);
>
> Unneeded and undesirable typeast.
>
> > + if (pch_phub_reg.pch_phub_base_address == 0) {
> > + dev_dbg(&pdev->dev, "pch_phub_probe : pci_iomap FAILED");
> > + pci_release_regions(pdev);
> > + pci_disable_device(pdev);
> > + ret = -ENOMEM;
> > + goto err_probe;
> > + }
> > + dev_dbg(&pdev->dev, "pch_phub_probe : pci_iomap SUCCESS and value "
> > + "in pch_phub_base_address variable is 0x%08x\n",
> > + (unsigned int)pch_phub_reg.pch_phub_base_address);
> > +
> > + pch_phub_reg.pch_phub_extrom_base_address =
> > +     (void __iomem *)pci_map_rom(pdev, &rom_size);
>
> Ditto
>
> > + if (pch_phub_reg.pch_phub_extrom_base_address == 0) {
> > + dev_dbg(&pdev->dev, "pch_phub_probe : pci_map_rom FAILED");
> > + pci_iounmap(pdev, (void *)pch_phub_reg.pch_phub_base_address);
> > + pci_release_regions(pdev);
> > + pci_disable_device(pdev);
>
> This is getting painful.  I'd suggest removal of the duplicated code
> via the standard `goto out_iounmap' unwinding approach.
>
> > + ret = -ENOMEM;
> > + goto err_probe;
> > + }
> > + dev_dbg(&pdev->dev, "pch_phub_probe : "
> > + "pci_map_rom SUCCESS and value in "
> > + "pch_phub_extrom_base_address variable is 0x%08x\n",
> > + (unsigned int)pch_phub_reg.pch_phub_extrom_base_address);
> > +
> > + if (pch_phub_major_no) {
> > + pch_phub_dev_no = MKDEV(pch_phub_major_no, 0);
> > + ret = register_chrdev_region(pch_phub_dev_no,
> > +    PCH_MINOR_NOS, DRIVER_NAME);
> > + if (ret) {
> > + dev_dbg(&pdev->dev, "pch_phub_probe : "
> > + "register_chrdev_region FAILED");
> > + pci_unmap_rom(pdev,
> > + (void *)pch_phub_reg.pch_phub_extrom_base_address);
> > + pci_iounmap(pdev,
> > + (void *)pch_phub_reg.pch_phub_base_address);
>
> More unneeded typecasts.
>
> > + pci_release_regions(pdev);
> > + pci_disable_device(pdev);
> > + goto err_probe;
> > + }
> > + dev_dbg(&pdev->dev, "pch_phub_probe : "
> > + "register_chrdev_region returns %d\n", ret);
> > + } else {
> > + ret = alloc_chrdev_region(&pch_phub_dev_no, 0,
> > + PCH_MINOR_NOS, DRIVER_NAME);
> > + if (ret) {
> > + dev_dbg(&pdev->dev, "pch_phub_probe : "
> > + "alloc_chrdev_region FAILED");
> > + pci_unmap_rom(pdev,
> > + (void *)pch_phub_reg.pch_phub_extrom_base_address);
> > + pci_iounmap(pdev,
> > +     (void *)pch_phub_reg.pch_phub_base_address);
>
> more..
>
> > + pci_release_regions(pdev);
> > + pci_disable_device(pdev);
> > + goto err_probe;
> > + }
> > + dev_dbg(&pdev->dev, "pch_phub_probe : "
> > + "alloc_chrdev_region returns %d\n", ret);
> > + }
> > +
> > + cdev_init(&pch_phub_dev, &pch_phub_fops);
> > + dev_dbg(&pdev->dev,
> > + "pch_phub_probe :  cdev_init invoked successfully\n");
> > +
> > + pch_phub_dev.owner = THIS_MODULE;
> > + pch_phub_dev.ops = &pch_phub_fops;
> > +
> > + ret = cdev_add(&pch_phub_dev, pch_phub_dev_no, PCH_MINOR_NOS);
> > + if (ret) {
> > + dev_dbg(&pdev->dev, "pch_phub_probe :  cdev_add FAILED");
> > + unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
> > + pci_unmap_rom(pdev,
> > + (void *)pch_phub_reg.pch_phub_extrom_base_address);
> > + pci_iounmap(pdev, (void *)pch_phub_reg.pch_phub_base_address);
> > + pci_release_regions(pdev);
> > + pci_disable_device(pdev);
> > + goto err_probe;
> > + }
> > + dev_dbg(&pdev->dev, "pch_phub_probe :  cdev_add returns %d\n", ret);
> > +
> > + /*set the clock config reg if CAN clock is 50Mhz */
> > + dev_dbg(&pdev->dev, "pch_phub_probe : invoking "
> > + "pch_phub_read_modify_write_reg "
> > + "to set CLKCFG reg for CAN clk 50Mhz\n");
> > + pch_phub_read_modify_write_reg((unsigned int)CLKCFG_REG_OFFSET,
> > +   CLKCFG_CAN_50MHZ, CLKCFG_CANCLK_MASK);
> > +
> > + /* set the prefech value */
> > + iowrite32(0x000ffffa, pch_phub_reg.pch_phub_base_address + 0x14);
> > + /* set the interrupt delay value */
> > + iowrite32(0x25, pch_phub_reg.pch_phub_base_address + 0x44);
> > + return 0;
> > +
> > +err_probe:
> > + dev_dbg(&pdev->dev, "pch_phub_probe returns %d\n", ret);
> > + return ret;
> > +}
> > +
> >
> > ...
> >
> > +#ifdef CONFIG_PM
> > +
> > +/** pch_phub_suspend - Implements the suspend functionality of the module.
> > + *  @pdev: Contains the reference of the pci_dev structure
> > + *  @state: Contains the reference of the pm_message_t structure
> > + */
> > +static int pch_phub_suspend(struct pci_dev *pdev, pm_message_t state)
> > +{
> > + int ret;
> > +
> > + pch_phub_reg.pch_phub_suspended = true;/* For blocking further IOCTLs */
> > +
> > + pch_phub_save_reg_conf(pdev);
> > + dev_dbg(&pdev->dev, "pch_phub_suspend - "
> > + "pch_phub_save_reg_conf Invoked successfully\n");
> > +
> > + ret = pci_save_state(pdev);
> > + if (ret) {
> > + dev_dbg(&pdev->dev,
> > + " pch_phub_suspend -pci_save_state returns-%d\n", ret);
> > + return ret;
> > + }
> > + dev_dbg(&pdev->dev,
> > + "pch_phub_suspend - pci_save_state returns %d\n", ret);
> > + pci_enable_wake(pdev, PCI_D3hot, 0);
> > + dev_dbg(&pdev->dev, "pch_phub_suspend - "
> > + "pci_enable_wake Invoked successfully\n");
> > +
> > + pci_disable_device(pdev);
> > + dev_dbg(&pdev->dev, "pch_phub_suspend - "
> > + "pci_disable_device Invoked successfully\n");
> > +
> > + pci_set_power_state(pdev, pci_choose_state(pdev, state));
> > + dev_dbg(&pdev->dev, "pch_phub_suspend - "
> > + "pci_set_power_state Invoked successfully   "
> > + "return = %d\n", 0);
> > +
> > + return 0;
> > +}
> > +
> > +/** pch_phub_resume - Implements the resume functionality of the module.
> > + *  @pdev: Contains the reference of the pci_dev structure
> > + */
> > +static int pch_phub_resume(struct pci_dev *pdev)
> > +{
> > +
> > + int ret;
> > +
> > + pci_set_power_state(pdev, PCI_D0);
> > + dev_dbg(&pdev->dev, "pch_phub_resume - "
> > + "pci_set_power_state Invoked successfully\n");
> > +
> > + pci_restore_state(pdev);
> > + dev_dbg(&pdev->dev, "pch_phub_resume - "
> > + "pci_restore_state Invoked successfully\n");
> > +
> > + ret = pci_enable_device(pdev);
> > + if (ret) {
> > + dev_dbg(&pdev->dev,
> > + "pch_phub_resume-pci_enable_device failed ");
> > + return ret;
> > + }
> > +
> > + dev_dbg(&pdev->dev, "pch_phub_resume - "
> > + "pci_enable_device returns -%d\n", ret);
> > +
> > + pci_enable_wake(pdev, PCI_D3hot, 0);
> > + dev_dbg(&pdev->dev, "pch_phub_resume - "
> > + "pci_enable_wake Invoked successfully\n");
> > +
> > + pch_phub_restore_reg_conf(pdev);
> > + dev_dbg(&pdev->dev, "pch_phub_resume - "
> > + "pch_phub_restore_reg_conf Invoked successfully\n");
> > +
> > + pch_phub_reg.pch_phub_suspended = false;
> > +
> > + dev_dbg(&pdev->dev, "pch_phub_resume  returns- %d\n", 0);
> > + return 0;
> > +}
>
> Here you can put
>
> #else
> #define pch_phub_suspend NULL
> #define pch_phub_resume NULL
>
> > +#endif /* CONFIG_PM */
> > +
> > +static struct pci_device_id pch_phub_pcidev_id[] = {
> > +
> > + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH1_PHUB)},
> > + {0,}
> > +};
> > +
> > +
> > +static struct pci_driver pch_phub_driver = {
> > + .name = "pch_phub",
> > + .id_table = pch_phub_pcidev_id,
> > + .probe = pch_phub_probe,
> > + .remove = __devexit_p(pch_phub_remove),
> > +#ifdef CONFIG_PM
> > + .suspend = pch_phub_suspend,
> > + .resume = pch_phub_resume
> > +#endif
>
> and then remove this ifdef.
>
> >
> > ...
> >
> > --- /dev/null
> > +++ b/drivers/char/pch_phub/pch_phub.h
> > @@ -0,0 +1,58 @@
> > +#ifndef __PCH_PHUB_H__
> > +#define __PCH_PHUB_H__
> > +/*!
> > + * @file pch_phub.h
> > + * @brief Provides all the interfaces pertaining to the Packet Hub module.
> > + * @version 1.0.0.0
> > + * @section
>
> ?
>
> > + * 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; version 2 of the License.
> > + *
> > + * 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, write to the Free Software
> > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
> > + */
> > +
> > +/*
> > + * History:
> > + * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
> > + *
> > + * created:
> > + * OKI SEMICONDUCTOR 04/14/2010
> > + * modified:
> > + *
> > + */
> > +
> > +#define PHUB_IOCTL_MAGIC (0xf7)
> > +
> > +/*Outlines the read mac address function signature. */
>
> Space after /*, please (check the whole patch)
>
> > +#define IOCTL_PHUB_READ_MAC_ADDR (_IOR(PHUB_IOCTL_MAGIC, 6, __u8[6]))
> > +
> > +/*brief Outlines the write mac address function signature. */
> > +#define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 7, __u8[6]))
>
> "brief"?
>
> I didn't notice any documentation for these ioctls anywhere?
>
> > +
> > +/* Registers address offset */
> > +#define PCH_PHUB_PHUB_ID_REG 0x0000
> > +#define PCH_PHUB_QUEUE_PRI_VAL_REG 0x0004
> > +#define PCH_PHUB_RC_QUEUE_MAXSIZE_REG 0x0008
> > +#define PCH_PHUB_BRI_QUEUE_MAXSIZE_REG 0x000C
> > +#define PCH_PHUB_COMP_RESP_TIMEOUT_REG 0x0010
> > +#define PCH_PHUB_BUS_SLAVE_CONTROL_REG 0x0014
> > +#define PCH_PHUB_DEADLOCK_AVOID_TYPE_REG 0x0018
> > +#define PCH_PHUB_INTPIN_REG_WPERMIT_REG0 0x0020
> > +#define PCH_PHUB_INTPIN_REG_WPERMIT_REG1 0x0024
> > +#define PCH_PHUB_INTPIN_REG_WPERMIT_REG2 0x0028
> > +#define PCH_PHUB_INTPIN_REG_WPERMIT_REG3 0x002C
> > +#define PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE 0x0040
> > +#define CLKCFG_REG_OFFSET 0x500
> > +
> > +#define PCH_PHUB_OROM_SIZE 15360
> > +
> > +#endif
>



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

* Re: [PATCH] Topcliff PHUB: Generate PacketHub driver
  2010-06-22  5:33 [PATCH] Topcliff PHUB: Generate PacketHub driver Masayuki Ohtak
  2010-06-22 10:33 ` Masayuki Ohtak
  2010-06-22 11:30 ` Arnd Bergmann
@ 2010-06-29 23:31 ` Andy Isaacson
  2010-06-30  5:58   ` Masayuki Ohtake
  2010-06-30  7:51 ` [PATCH] Packet hub driver of Topcliff PCH Masayuki Ohtak
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 46+ messages in thread
From: Andy Isaacson @ 2010-06-29 23:31 UTC (permalink / raw)
  To: Masayuki Ohtak
  Cc: Arnd Bergmann, Wang, Yong Y, Wang Qi, Intel OTC, Andrew, Alan Cox, LKML

On Tue, Jun 22, 2010 at 02:33:01PM +0900, Masayuki Ohtak wrote:
> +config PCH_PHUB
> +	tristate "PCH PHUB"
> +	depends on PCI
> +	help
> +	  If you say yes to this option, support will be included for the
> +	  PCH Packet Hub Host controller.
> +	  This driver is for PCH Packet hub driver for Topcliff.

"Topcliff" is probably some kind of internal codename; please use a name
that will be visible to customers of this product.  References to what
kind of device (IEEE standards it implements, what systems it might be
present on, etc) are also appropriate.

> +	  This driver is integrated as built-in.

This sentence doesn't parse to me.  What are you trying to say?

> +	  This driver can access GbE MAC address.
> +	  This driver can access HW register.

I think you mean something more like "This driver allows access to the
GbE MAC address and HW registers of the PCH Packet Hub."

If this is a driver for an Ethernet MAC, what is it doing in
drivers/char/ ?

> +	  You can also be integrated as module.

Please look at any other tristate and copy the standard wording.

> +/*!
> + * @file pch_phub.c
> + * @brief Provides all the implementation of the interfaces pertaining to
> + *		the Packet Hub module.

We don't normally merge comment markup languages other than kernel-doc.
Please read Documentation/kernel-doc-nano-HOWTO.txt and follow it.  (Or,
provide a pointer to documentation and tools for this mysterious markup
language.)

> +/*
> + * History:
> + * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
> + *
> + * created:
> + *	OKI SEMICONDUCTOR 04/14/2010
> + * modified:

No changelogs in comments, that's what git history is for.

> +/* includes */

Useless comment.

> +/*--------------------------------------------
> +	macros
> +--------------------------------------------*/

More useless comments.

> +/* Status Register offset */
> +#define PHUB_STATUS 0x00
> +/* Control Register offset */
> +#define PHUB_CONTROL 0x04

I would format this as:

#define PHUB_STATUS     0x00    /* Status Register offset */
#define PHUB_CONTROL    0x04    /* Control Register offset */

but it's up to you.

> +struct pch_phub_reg {
> +	unsigned int phub_id_reg;

Since these are used to hold HW-defined data, you should use fixed-size
types such as u32.  Also, you should consider whether they should be
marked little endian / big endian.

> +/* ToDo: major number allocation via module parameter */
> +static dev_t pch_phub_dev_no;
> +static int pch_phub_major_no;
> +static struct cdev pch_phub_dev;

If this is to remain a chardev, use register_chrdev().  You shouldn't
need a module parameter to configure your device numbers.

> +	void __iomem *reg_addr = pch_phub_reg.pch_phub_base_address +\
> +							 reg_addr_offset;

That \ is superfluous.  There are several of these in this file.

The indentation on the second line is too large, and the fact that
"a = b + c" spills onto a second line is a clue that your struct names
are too long.

> +	iowrite32(((ioread32(reg_addr) & ~mask)) | data, reg_addr);
> +	return;

The return is unneeded if this remains a void function.  (many more
occurrences.)

> +/** pch_phub_restore_reg_conf - restore register configuration
> + */

Don't use /** unless you're using kernel-doc.

> +	unsigned int i;
> +	void __iomem *p = pch_phub_reg.pch_phub_base_address;
> +
> +	dev_dbg(&pdev->dev, "pch_phub_restore_reg_conf ENTRY\n");
> +	/* to store contents of PHUB_ID register */
> +	iowrite32(pch_phub_reg.phub_id_reg, p + PCH_PHUB_PHUB_ID_REG);

Don't include comments that just duplicate the code.  Also, rename your
constants from PCH_PHUB_PHUB_ to, I dunno, PHUB_ or something.

> +/** pch_phub_read_serial_rom - Implements the functionality of reading Serial
> + *  									 ROM.
> + *  @offset_address: Contains the Serial ROM address offset value
> + *  @data: Contains the Serial ROM value
> + */

This comment is almost correctly formatted, but there are extra words
and some whitespace problems, and it doesn't document what actually
happens.

+/**
+ * pch_phub_read_serial_rom - read PHUB Serial ROM.
+ *  @offset_address:   serial ROM offset to read.
+ *  @data:             pointer at which to store value that was read.
+ */

is more correct.

Similar problems in several function comments below.

> +static int pch_phub_read_serial_rom(unsigned int offset_address,
> +							 unsigned char *data)

The second line is indented too far.  We use 8-space tabstops, so the
"u" of unsigned is all the way over under the "t" of offset_address.  It
should either be two tabstops indented, or lined up with the previous
argument.  (This applies to several functions below too.)

It should be "u8 *data".

> +	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +\
> +								 offset_address;
> +
> +	*data = ioread8(mem_addr);
> +
> +	return 0;

If this can't fail, why not have it just return the value rather than
forcing the caller to provide a pointer?  (If you want to be futureproof
and support errorhandling in the future, that's OK.)

> +static int pch_phub_write_serial_rom(unsigned int offset_address,
> +							 unsigned char data)
> +{
> +	int retval = 0;
> +	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +\
> +						 (offset_address & 0xFFFFFFFC);
> +	int i = 0;
> +	unsigned int word_data = 0;
> +
> +	iowrite32(PCH_PHUB_ROM_WRITE_ENABLE,
> +			pch_phub_reg.pch_phub_extrom_base_address +\
> +								 PHUB_CONTROL);
> +
> +	word_data = ioread32(mem_addr);
> +
> +	switch (offset_address % 4) {
> +	case 0:
> +		word_data &= 0xFFFFFF00;
> +		iowrite32((word_data | (unsigned int)data),
> +						mem_addr);
> +		break;

This function is much larger than need be.  Remove the 0xFFFFFFFC magic
number -- something like

#define PCH_WORD_ADDR_MASK (~((1 << 4) - 1))

and implement the byte masking as something like

    pos = (offset_address % 4) * 8;
    mask = ~(0xFF << pos);
    iowrite32((word_data & mask) | (u32)data << pos, mem_addr);

(You can keep the switch if there's a significant performance benefit to
doing so, but please provide measurements.)

This is a read-modify-write cycle -- where is the locking if two users
try to update the serial ROM simultaneously?  Any required locking
should be documented in the kernel-doc comment.

> +	while (0x00 !=
> +	       ioread8(pch_phub_reg.pch_phub_extrom_base_address +\
> +							 PHUB_STATUS)) {
> +		msleep(1);
> +		if (PHUB_TIMEOUT == i) {
> +			retval = -EPERM;
> +			break;
> +		}
> +		i++;

Rewrite this as a for loop (and remove the initialization from the
declaration of i at the top of this function).  I am not sure if
msleep() is safe here -- what context will this be called from?

> +/** pch_phub_read_serial_rom_val - Implements the functionality of reading
> + *  							 Serial ROM value.
> + *  @offset_address: Contains the Serial ROM address offset value
> + *  @data: Contains the Serial ROM value
> + */
> +static int pch_phub_read_serial_rom_val(unsigned int offset_address,
> +							 unsigned char *data)
> +{
> +	int retval = 0;
> +	unsigned int mem_addr;
> +
> +	mem_addr = (offset_address / 4 * 8) + 3 -
> +			(offset_address % 4) + PCH_PHUB_ROM_START_ADDR;

Is that formula correct?   It is very suprising.

If it is correct, the formula is bizarre enough to warrant a long
comment explaining the theory of operation and/or pointing to hardware
docs.

> +static ssize_t pch_phub_read(struct file *file, char __user *buf, size_t size,
> +								 loff_t *ppos)
> +{
> +	unsigned int rom_signature = 0;
> +	unsigned char rom_length;
> +	int ret_value;
> +	unsigned int tmp;
> +	unsigned char data;
> +	unsigned int addr_offset = 0;
> +	unsigned int orom_size;
> +	loff_t pos = *ppos;
> +
> +	if (pos < 0)
> +		return -EINVAL;
> +
> +	/*Get Rom signature*/
> +	pch_phub_read_serial_rom(0x80, (unsigned char *)&rom_signature);
> +	pch_phub_read_serial_rom(0x81, (unsigned char *)&tmp);

I seriously doubt that your device is special enough to warrant a custom
/dev node with proprietary semantics.  If this is just part of an
Ethernet driver, please implement it in drivers/net/; if this is a
generic PROM accessor, there must be some semi-standardized EPROM access
interface but I don't know what it is offhand.

-andy

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

* Re: [PATCH] Topcliff PHUB: Generate PacketHub driver
  2010-06-29 23:31 ` Andy Isaacson
@ 2010-06-30  5:58   ` Masayuki Ohtake
  2010-06-30 18:28     ` Andy Isaacson
  0 siblings, 1 reply; 46+ messages in thread
From: Masayuki Ohtake @ 2010-06-30  5:58 UTC (permalink / raw)
  To: Andy Isaacson
  Cc: Arnd Bergmann, Wang, Yong Y, Wang Qi, Intel OTC, Andrew, Alan Cox, LKML

Hi Andy

Thank you for your comments.
Please refer to the following inline.
Thanks, Ohtake.

----- Original Message ----- 
From: "Andy Isaacson" <adi@hexapodia.org>
To: "Masayuki Ohtak" <masa-korg@dsn.okisemi.com>
Cc: "Arnd Bergmann" <arnd@arndb.de>; "Wang, Yong Y" <yong.y.wang@intel.com>; "Wang Qi" <qi.wang@intel.com>; "Intel OTC"
<joel.clark@intel.com>; "Andrew" <andrew.chih.howe.khor@intel.com>; "Alan Cox" <alan@lxorguk.ukuu.org.uk>; "LKML"
<linux-kernel@vger.kernel.org>
Sent: Wednesday, June 30, 2010 8:31 AM
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver


> On Tue, Jun 22, 2010 at 02:33:01PM +0900, Masayuki Ohtak wrote:
> > +config PCH_PHUB
> > + tristate "PCH PHUB"
> > + depends on PCI
> > + help
> > +   If you say yes to this option, support will be included for the
> > +   PCH Packet Hub Host controller.
> > +   This driver is for PCH Packet hub driver for Topcliff.
>
> "Topcliff" is probably some kind of internal codename; please use a name

Topcliff is not internal codename but external product name.

> that will be visible to customers of this product.  References to what
> kind of device (IEEE standards it implements, what systems it might be
> present on, etc) are also appropriate.

I will add what kinds of device.

>
> > +   This driver is integrated as built-in.
>
> This sentence doesn't parse to me.  What are you trying to say?
>

I will modify above.

> > +   This driver can access GbE MAC address.
> > +   This driver can access HW register.
>
> I think you mean something more like "This driver allows access to the
> GbE MAC address and HW registers of the PCH Packet Hub."
>
> If this is a driver for an Ethernet MAC, what is it doing in
> drivers/char/ ?

In Topcliff, MAC address is in PHUB HW not in GbE HW.

>
> > +   You can also be integrated as module.
>
> Please look at any other tristate and copy the standard wording.

I will modify above.

>
> > +/*!
> > + * @file pch_phub.c
> > + * @brief Provides all the implementation of the interfaces pertaining to
> > + * the Packet Hub module.
>
> We don't normally merge comment markup languages other than kernel-doc.
> Please read Documentation/kernel-doc-nano-HOWTO.txt and follow it.  (Or,
> provide a pointer to documentation and tools for this mysterious markup
> language.)

I will modify above.

>
> > +/*
> > + * History:
> > + * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
> > + *
> > + * created:
> > + * OKI SEMICONDUCTOR 04/14/2010
> > + * modified:
>
> No changelogs in comments, that's what git history is for.

I will modify above.

>
> > +/* includes */
>
> Useless comment.

I will delete above.

>
> > +/*--------------------------------------------
> > + macros
> > +--------------------------------------------*/
>
> More useless comments.

I will delete above.

>
> > +/* Status Register offset */
> > +#define PHUB_STATUS 0x00
> > +/* Control Register offset */
> > +#define PHUB_CONTROL 0x04
>
> I would format this as:
>
> #define PHUB_STATUS     0x00    /* Status Register offset */
> #define PHUB_CONTROL    0x04    /* Control Register offset */
>
> but it's up to you.

I will modify above.

>
> > +struct pch_phub_reg {
> > + unsigned int phub_id_reg;
>
> Since these are used to hold HW-defined data, you should use fixed-size
> types such as u32.  Also, you should consider whether they should be
> marked little endian / big endian.

I will modify above.

>
> > +/* ToDo: major number allocation via module parameter */
> > +static dev_t pch_phub_dev_no;
> > +static int pch_phub_major_no;
> > +static struct cdev pch_phub_dev;
>
> If this is to remain a chardev, use register_chrdev().  You shouldn't
> need a module parameter to configure your device numbers.

I will delete module paramter.

>
> > + void __iomem *reg_addr = pch_phub_reg.pch_phub_base_address +\
> > + reg_addr_offset;
>
> That \ is superfluous.  There are several of these in this file.

I will delete uncecessary '\'.

>
> The indentation on the second line is too large, and the fact that
> "a = b + c" spills onto a second line is a clue that your struct names
> are too long.

We don't modify structure name this time.

>
> > + iowrite32(((ioread32(reg_addr) & ~mask)) | data, reg_addr);
> > + return;
>
> The return is unneeded if this remains a void function.  (many more
> occurrences.)

I will delete 'return' of  void function.

>
> > +/** pch_phub_restore_reg_conf - restore register configuration
> > + */
>
> Don't use /** unless you're using kernel-doc.

I will modify above.

>
> > + unsigned int i;
> > + void __iomem *p = pch_phub_reg.pch_phub_base_address;
> > +
> > + dev_dbg(&pdev->dev, "pch_phub_restore_reg_conf ENTRY\n");
> > + /* to store contents of PHUB_ID register */
> > + iowrite32(pch_phub_reg.phub_id_reg, p + PCH_PHUB_PHUB_ID_REG);
>
> Don't include comments that just duplicate the code.  Also, rename your
> constants from PCH_PHUB_PHUB_ to, I dunno, PHUB_ or something.

Sorry, I can't understand your intention.
Please give us more information.

>
> > +/** pch_phub_read_serial_rom - Implements the functionality of reading Serial
> > + *  ROM.
> > + *  @offset_address: Contains the Serial ROM address offset value
> > + *  @data: Contains the Serial ROM value
> > + */
>
> This comment is almost correctly formatted, but there are extra words
> and some whitespace problems, and it doesn't document what actually
> happens.

I will modify above.

>
> +/**
> + * pch_phub_read_serial_rom - read PHUB Serial ROM.
> + *  @offset_address:   serial ROM offset to read.
> + *  @data:             pointer at which to store value that was read.
> + */
>
> is more correct.

I will modify above.

>
> Similar problems in several function comments below.
>
> > +static int pch_phub_read_serial_rom(unsigned int offset_address,
> > + unsigned char *data)
>
> The second line is indented too far.  We use 8-space tabstops, so the
> "u" of unsigned is all the way over under the "t" of offset_address.  It
> should either be two tabstops indented, or lined up with the previous
> argument.  (This applies to several functions below too.)

I will modify above.

>
> It should be "u8 *data".

I will modify above.

>
> > + void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +\
> > + offset_address;
> > +
> > + *data = ioread8(mem_addr);
> > +
> > + return 0;
>
> If this can't fail, why not have it just return the value rather than
> forcing the caller to provide a pointer?  (If you want to be futureproof
> and support errorhandling in the future, that's OK.)

I will modify to void.

>
> > +static int pch_phub_write_serial_rom(unsigned int offset_address,
> > + unsigned char data)
> > +{
> > + int retval = 0;
> > + void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +\
> > + (offset_address & 0xFFFFFFFC);
> > + int i = 0;
> > + unsigned int word_data = 0;
> > +
> > + iowrite32(PCH_PHUB_ROM_WRITE_ENABLE,
> > + pch_phub_reg.pch_phub_extrom_base_address +\
> > + PHUB_CONTROL);
> > +
> > + word_data = ioread32(mem_addr);
> > +
> > + switch (offset_address % 4) {
> > + case 0:
> > + word_data &= 0xFFFFFF00;
> > + iowrite32((word_data | (unsigned int)data),
> > + mem_addr);
> > + break;
>
> This function is much larger than need be.  Remove the 0xFFFFFFFC magic
> number -- something like
>
> #define PCH_WORD_ADDR_MASK (~((1 << 4) - 1))
>
> and implement the byte masking as something like
>
>     pos = (offset_address % 4) * 8;
>     mask = ~(0xFF << pos);
>     iowrite32((word_data & mask) | (u32)data << pos, mem_addr);
>
> (You can keep the switch if there's a significant performance benefit to
> doing so, but please provide measurements.)
I will modify.(This processing is not performance critical.)


>
> This is a read-modify-write cycle -- where is the locking if two users
> try to update the serial ROM simultaneously?  Any required locking
> should be documented in the kernel-doc comment.

I will modify using MUTEX like ioctl.

>
> > + while (0x00 !=
> > +        ioread8(pch_phub_reg.pch_phub_extrom_base_address +\
> > + PHUB_STATUS)) {
> > + msleep(1);
> > + if (PHUB_TIMEOUT == i) {
> > + retval = -EPERM;
> > + break;
> > + }
> > + i++;
>
> Rewrite this as a for loop (and remove the initialization from the
> declaration of i at the top of this function).  I am not sure if
> msleep() is safe here -- what context will this be called from?

This function is called from ioctl context(not interrput).
Thus, I think safe.

>
> > +/** pch_phub_read_serial_rom_val - Implements the functionality of reading
> > + *  Serial ROM value.
> > + *  @offset_address: Contains the Serial ROM address offset value
> > + *  @data: Contains the Serial ROM value
> > + */
> > +static int pch_phub_read_serial_rom_val(unsigned int offset_address,
> > + unsigned char *data)
> > +{
> > + int retval = 0;
> > + unsigned int mem_addr;
> > +
> > + mem_addr = (offset_address / 4 * 8) + 3 -
> > + (offset_address % 4) + PCH_PHUB_ROM_START_ADDR;
>
> Is that formula correct?   It is very suprising.
>
> If it is correct, the formula is bizarre enough to warrant a long
> comment explaining the theory of operation and/or pointing to hardware
> docs.

I will modify the above and add comments.


>
> > +static ssize_t pch_phub_read(struct file *file, char __user *buf, size_t size,
> > + loff_t *ppos)
> > +{
> > + unsigned int rom_signature = 0;
> > + unsigned char rom_length;
> > + int ret_value;
> > + unsigned int tmp;
> > + unsigned char data;
> > + unsigned int addr_offset = 0;
> > + unsigned int orom_size;
> > + loff_t pos = *ppos;
> > +
> > + if (pos < 0)
> > + return -EINVAL;
> > +
> > + /*Get Rom signature*/
> > + pch_phub_read_serial_rom(0x80, (unsigned char *)&rom_signature);
> > + pch_phub_read_serial_rom(0x81, (unsigned char *)&tmp);
>
> I seriously doubt that your device is special enough to warrant a custom
> /dev node with proprietary semantics.  If this is just part of an
> Ethernet driver, please implement it in drivers/net/; if this is a
> generic PROM accessor, there must be some semi-standardized EPROM access
> interface but I don't know what it is offhand.
Since SROM is not in GbE HW but Phub HW, Phub is not part of Ethernet driver.
Packet hub is not generic driver but special device.

>
> -andy
>






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

* [PATCH] Packet hub driver of Topcliff PCH
  2010-06-22  5:33 [PATCH] Topcliff PHUB: Generate PacketHub driver Masayuki Ohtak
                   ` (2 preceding siblings ...)
  2010-06-29 23:31 ` Andy Isaacson
@ 2010-06-30  7:51 ` Masayuki Ohtak
  2010-06-30 18:05   ` Randy Dunlap
  2010-07-01  5:14 ` Masayuki Ohtak
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 46+ messages in thread
From: Masayuki Ohtak @ 2010-06-30  7:51 UTC (permalink / raw)
  To: Andy Isaacson, Andrew Morton
  Cc: Arnd Bergmann, Wang, Yong Y, qi.wang, joel.clark,
	andrew.chih.howe.khor, Alan Cox, LKML

Hi Andy and Andrew

I have modified for your comments.
Please confirm below.

---


Packet hub driver of Topcliff PCH

Topcliff PCH is the platform controller hub that is going to be used in
Intel’s upcoming general embedded platform. All IO peripherals in
Topcliff PCH are actually devices sitting on AMBA bus. Packet hub is
a special converter device in Topcliff PCH that translate AMBA transactions
to PCI Express transactions and vice versa. Thus packet hub helps present
all IO peripherals in Topcliff PCH as PCIE devices to IA system.
Topcliff PCH have MAC address and Option ROM data.
These data are in SROM which is connected to PCIE bus.
Packet hub driver of Topcliff PCH can access MAC address and Option ROM data in
SROM.

Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
Acked-by: Arnd <Arnd’s email address>
---
 drivers/char/Kconfig             |   10 +
 drivers/char/Makefile            |    2 +
 drivers/char/pch_phub/Makefile   |    3 +
 drivers/char/pch_phub/pch_phub.c |  805 ++++++++++++++++++++++++++++++++++++++
 drivers/char/pch_phub/pch_phub.h |   48 +++
 5 files changed, 868 insertions(+), 0 deletions(-)
 create mode 100644 drivers/char/pch_phub/Makefile
 create mode 100755 drivers/char/pch_phub/pch_phub.c
 create mode 100755 drivers/char/pch_phub/pch_phub.h

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index e023682..1851e97 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -4,6 +4,16 @@
 
 menu "Character devices"
 
+config PCH_PHUB
+	tristate "PCH PHUB"
+	depends on PCI
+	help
+	  Topcliff is a IOH for x86 embedded processor. This IOH is quite
+	  different with the traditional IOH. Topcliff is a kind of ARM-based
+	  processor and connected with PCIe bus (from x86 processor).
+	  PHUB work as a gateway transform the PCIe transaction into the AMBA
+	  transaction, and vise verse, and have several transform windows also.
+
 config VT
 	bool "Virtual terminal" if EMBEDDED
 	depends on !S390
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index f957edf..1e3eb6c 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -111,6 +111,8 @@ obj-$(CONFIG_PS3_FLASH)		+= ps3flash.o
 obj-$(CONFIG_JS_RTC)		+= js-rtc.o
 js-rtc-y = rtc.o
 
+obj-$(CONFIG_PCH_PHUB)	+= pch_phub/
+
 # Files generated that shall be removed upon make clean
 clean-files := consolemap_deftbl.c defkeymap.c
 
diff --git a/drivers/char/pch_phub/Makefile b/drivers/char/pch_phub/Makefile
new file mode 100644
index 0000000..93aaffe
--- /dev/null
+++ b/drivers/char/pch_phub/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_PCH_PHUB) += pch_phub_drv.o
+
+pch_phub_drv-objs := pch_phub.o
diff --git a/drivers/char/pch_phub/pch_phub.c b/drivers/char/pch_phub/pch_phub.c
new file mode 100755
index 0000000..d443048
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.c
@@ -0,0 +1,805 @@
+/*!
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include "pch_phub.h"
+
+#define PHUB_STATUS 0x00		/* Status Register offset */
+#define PHUB_CONTROL 0x04		/* Control Register offset */
+#define PHUB_TIMEOUT 0x05		/* Time out value for Status Register */
+#define PCH_PHUB_ROM_WRITE_ENABLE 0x01	/* Enabling for writing ROM */
+#define PCH_PHUB_ROM_WRITE_DISABLE 0x00	/* Disabling for writing ROM */
+#define PCH_PHUB_ROM_START_ADDR 0x14	/* ROM data area start address offset */
+
+/* MAX number of INT_REDUCE_CONTROL registers */
+#define MAX_NUM_INT_REDUCE_CONTROL_REG 128
+#define PCI_DEVICE_ID_PCH1_PHUB 0x8801
+#define PCH_MINOR_NOS 1
+#define CLKCFG_CAN_50MHZ 0x12000000
+#define CLKCFG_CANCLK_MASK 0xFF000000
+#define MODULE_NAME "pch_phub"
+
+/**
+ * struct pch_phub_reg - PHUB register structure
+ * @phub_id_reg:			PHUB_ID register val
+ * @q_pri_val_reg:			QUEUE_PRI_VAL register val
+ * @rc_q_maxsize_reg:			RC_QUEUE_MAXSIZE register val
+ * @bri_q_maxsize_reg:			BRI_QUEUE_MAXSIZE register val
+ * @comp_resp_timeout_reg:		COMP_RESP_TIMEOUT register val
+ * @bus_slave_control_reg:		BUS_SLAVE_CONTROL_REG register val
+ * @deadlock_avoid_type_reg:		DEADLOCK_AVOID_TYPE register val
+ * @intpin_reg_wpermit_reg0:		INTPIN_REG_WPERMIT register 0 val
+ * @intpin_reg_wpermit_reg1:		INTPIN_REG_WPERMIT register 1 val
+ * @intpin_reg_wpermit_reg2:		INTPIN_REG_WPERMIT register 2 val
+ * @intpin_reg_wpermit_reg3:		INTPIN_REG_WPERMIT register 3 val
+ * @int_reduce_control_reg:		INT_REDUCE_CONTROL registers val
+ * @clkcfg_reg:				CLK CFG register val
+ * @pch_phub_base_address:		Register base address
+ * @pch_phub_extrom_base_address:	external rom base address
+ * @pch_phub_suspended:			PHUB status val
+ */
+struct pch_phub_reg {
+	u32 phub_id_reg;
+	u32 q_pri_val_reg;
+	u32 rc_q_maxsize_reg;
+	u32 bri_q_maxsize_reg;
+	u32 comp_resp_timeout_reg;
+	u32 bus_slave_control_reg;
+	u32 deadlock_avoid_type_reg;
+	u32 intpin_reg_wpermit_reg0;
+	u32 intpin_reg_wpermit_reg1;
+	u32 intpin_reg_wpermit_reg2;
+	u32 intpin_reg_wpermit_reg3;
+	u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG];
+	u32 clkcfg_reg;
+	void __iomem *pch_phub_base_address;
+	void __iomem *pch_phub_extrom_base_address;
+	int pch_phub_suspended;
+} pch_phub_reg;
+
+/* SROM SPEC for MAC address assignment offset */
+static const int pch_phub_mac_offset[6] = {0x3, 0x2, 0x1, 0x0, 0xb, 0xa};
+
+static DEFINE_MUTEX(pch_phub_ioctl_mutex);
+static DEFINE_MUTEX(pch_phub_read_mutex);
+static DEFINE_MUTEX(pch_phub_write_mutex);
+static dev_t pch_phub_dev_no;
+static struct cdev pch_phub_dev;
+
+/**
+ * pch_phub_read_modify_write_reg() - Reading modifying and writing register
+ * @reg_addr_offset:	Register offset address value.
+ * @data:		Writing value.
+ * @mask:		Mask value.
+ */
+static void pch_phub_read_modify_write_reg(unsigned int reg_addr_offset,
+					   unsigned int data, unsigned int mask)
+{
+	void __iomem *reg_addr = pch_phub_reg.pch_phub_base_address +
+					 reg_addr_offset;
+	iowrite32(((ioread32(reg_addr) & ~mask)) | data, reg_addr);
+}
+
+
+/* pch_phub_save_reg_conf - saves register configuration */
+static void pch_phub_save_reg_conf(struct pci_dev *pdev)
+{
+	unsigned int i;
+	void __iomem *p = pch_phub_reg.pch_phub_base_address;
+
+	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf ENTRY\n");
+	pch_phub_reg.phub_id_reg = ioread32(p + PCH_PHUB_ID_REG);
+	pch_phub_reg.q_pri_val_reg = ioread32(p + PCH_PHUB_QUEUE_PRI_VAL_REG);
+	pch_phub_reg.rc_q_maxsize_reg =
+	    ioread32(p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+	pch_phub_reg.bri_q_maxsize_reg =
+	    ioread32(p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+	pch_phub_reg.comp_resp_timeout_reg =
+	    ioread32(p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+	pch_phub_reg.bus_slave_control_reg =
+	    ioread32(p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+	pch_phub_reg.deadlock_avoid_type_reg =
+	    ioread32(p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+	pch_phub_reg.intpin_reg_wpermit_reg0 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+	pch_phub_reg.intpin_reg_wpermit_reg1 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+	pch_phub_reg.intpin_reg_wpermit_reg2 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+	pch_phub_reg.intpin_reg_wpermit_reg3 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
+	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+		"pch_phub_reg.phub_id_reg=%x, "
+		"pch_phub_reg.q_pri_val_reg=%x, "
+		"pch_phub_reg.rc_q_maxsize_reg=%x, "
+		"pch_phub_reg.bri_q_maxsize_reg=%x, "
+		"pch_phub_reg.comp_resp_timeout_reg=%x, "
+		"pch_phub_reg.bus_slave_control_reg=%x, "
+		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n",
+		pch_phub_reg.phub_id_reg,
+		pch_phub_reg.q_pri_val_reg,
+		pch_phub_reg.rc_q_maxsize_reg,
+		pch_phub_reg.bri_q_maxsize_reg,
+		pch_phub_reg.comp_resp_timeout_reg,
+		pch_phub_reg.bus_slave_control_reg,
+		pch_phub_reg.deadlock_avoid_type_reg,
+		pch_phub_reg.intpin_reg_wpermit_reg0,
+		pch_phub_reg.intpin_reg_wpermit_reg1,
+		pch_phub_reg.intpin_reg_wpermit_reg2,
+		pch_phub_reg.intpin_reg_wpermit_reg3);
+	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
+		pch_phub_reg.int_reduce_control_reg[i] =
+		    ioread32(p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
+		dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
+			i, pch_phub_reg.int_reduce_control_reg[i]);
+	}
+	pch_phub_reg.clkcfg_reg = ioread32(p + CLKCFG_REG_OFFSET);
+
+}
+
+/* pch_phub_restore_reg_conf - restore register configuration */
+static void pch_phub_restore_reg_conf(struct pci_dev *pdev)
+{
+	unsigned int i;
+	void __iomem *p = pch_phub_reg.pch_phub_base_address;
+
+	dev_dbg(&pdev->dev, "pch_phub_restore_reg_conf ENTRY\n");
+	iowrite32(pch_phub_reg.phub_id_reg, p + PCH_PHUB_ID_REG);
+	iowrite32(pch_phub_reg.q_pri_val_reg, p + PCH_PHUB_QUEUE_PRI_VAL_REG);
+	iowrite32(pch_phub_reg.rc_q_maxsize_reg,
+					p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+	iowrite32(pch_phub_reg.bri_q_maxsize_reg,
+					p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+	iowrite32(pch_phub_reg.comp_resp_timeout_reg,
+					p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+	iowrite32(pch_phub_reg.bus_slave_control_reg,
+					p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+	iowrite32(pch_phub_reg.deadlock_avoid_type_reg,
+					p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg0,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg1,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg2,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg3,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
+	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+		"pch_phub_reg.phub_id_reg=%x, "
+		"pch_phub_reg.q_pri_val_reg=%x, "
+		"pch_phub_reg.rc_q_maxsize_reg=%x, "
+		"pch_phub_reg.bri_q_maxsize_reg=%x, "
+		"pch_phub_reg.comp_resp_timeout_reg=%x, "
+		"pch_phub_reg.bus_slave_control_reg=%x, "
+		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n",
+		pch_phub_reg.phub_id_reg,
+		pch_phub_reg.q_pri_val_reg,
+		pch_phub_reg.rc_q_maxsize_reg,
+		pch_phub_reg.bri_q_maxsize_reg,
+		pch_phub_reg.comp_resp_timeout_reg,
+		pch_phub_reg.bus_slave_control_reg,
+		pch_phub_reg.deadlock_avoid_type_reg,
+		pch_phub_reg.intpin_reg_wpermit_reg0,
+		pch_phub_reg.intpin_reg_wpermit_reg1,
+		pch_phub_reg.intpin_reg_wpermit_reg2,
+		pch_phub_reg.intpin_reg_wpermit_reg3);
+	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
+		iowrite32(pch_phub_reg.int_reduce_control_reg[i],
+			p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
+		dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
+			i, pch_phub_reg.int_reduce_control_reg[i]);
+	}
+
+	iowrite32(pch_phub_reg.clkcfg_reg, p + CLKCFG_REG_OFFSET);
+}
+
+/**
+ * pch_phub_read_serial_rom() - Reading Serial ROM
+ * @offset_address:	Serial ROM offset address to read.
+ * @data:		Read buffer for specified Serial ROM value.
+ */
+static void pch_phub_read_serial_rom(unsigned int offset_address, u8 *data)
+{
+	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +
+					 offset_address;
+
+	*data = ioread8(mem_addr);
+}
+
+/**
+ * pch_phub_write_serial_rom() - Writing Serial ROM
+ * @offset_address:	Serial ROM offset address.
+ * @data:		Serial ROM value to write.
+ */
+static int pch_phub_write_serial_rom(unsigned int offset_address, u8 data)
+{
+	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +
+					(offset_address & PCH_WORD_ADDR_MASK);
+	int i;
+	unsigned int word_data;
+	unsigned int pos;
+	unsigned int mask;
+	pos = (offset_address % 4) * 8;
+	mask = ~(0xFF << pos);
+
+	iowrite32(PCH_PHUB_ROM_WRITE_ENABLE,
+			pch_phub_reg.pch_phub_extrom_base_address +
+			PHUB_CONTROL);
+
+	word_data = ioread32(mem_addr);
+	iowrite32((word_data & mask) | (u32)data << pos, mem_addr);
+
+	i = 0;
+	while (0x00 != ioread8(pch_phub_reg.pch_phub_extrom_base_address +
+			PHUB_STATUS)) {
+		msleep(1);
+		if (PHUB_TIMEOUT == i)
+			return -EPERM;
+		i++;
+	}
+
+	iowrite32(PCH_PHUB_ROM_WRITE_DISABLE,
+			pch_phub_reg.pch_phub_extrom_base_address +
+			PHUB_CONTROL);
+
+	return 0;
+}
+
+/**
+ * pch_phub_read_serial_rom_val() - Read Serial ROM value
+ * @offset_address:	Serial ROM address offset value.
+ * @data:		Serial ROM value to read.
+ */
+static void pch_phub_read_serial_rom_val(unsigned int offset_address, u8 *data)
+{
+	unsigned int mem_addr;
+
+	mem_addr = PCH_PHUB_ROM_START_ADDR +
+			pch_phub_mac_offset[offset_address];
+
+	pch_phub_read_serial_rom(mem_addr, data);
+
+}
+
+
+/**
+ * pch_phub_write_serial_rom_val() - writing Serial ROM value
+ * @offset_address:	Serial ROM address offset value.
+ * @data:		Serial ROM value.
+ */
+static int pch_phub_write_serial_rom_val(unsigned int offset_address, u8 data)
+{
+	int retval;
+	unsigned int mem_addr;
+
+	mem_addr = PCH_PHUB_ROM_START_ADDR +
+			pch_phub_mac_offset[offset_address];
+
+	retval = pch_phub_write_serial_rom(mem_addr, data);
+
+	return retval;
+}
+
+/* pch_phub_gbe_serial_rom_conf - makes Serial ROM header format configuration
+ * for Gigabit Ethernet MAC address
+ */
+static int pch_phub_gbe_serial_rom_conf(void)
+{
+	int retval;
+
+	retval = pch_phub_write_serial_rom(0x0b, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x0a, 0x10);
+	retval |= pch_phub_write_serial_rom(0x09, 0x01);
+	retval |= pch_phub_write_serial_rom(0x08, 0x02);
+
+	retval |= pch_phub_write_serial_rom(0x0f, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0e, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0d, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0c, 0x80);
+
+	retval |= pch_phub_write_serial_rom(0x13, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x12, 0x10);
+	retval |= pch_phub_write_serial_rom(0x11, 0x01);
+	retval |= pch_phub_write_serial_rom(0x10, 0x18);
+
+	retval |= pch_phub_write_serial_rom(0x1b, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x1a, 0x10);
+	retval |= pch_phub_write_serial_rom(0x19, 0x01);
+	retval |= pch_phub_write_serial_rom(0x18, 0x19);
+
+	retval |= pch_phub_write_serial_rom(0x23, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x22, 0x10);
+	retval |= pch_phub_write_serial_rom(0x21, 0x01);
+	retval |= pch_phub_write_serial_rom(0x20, 0x3a);
+
+	retval |= pch_phub_write_serial_rom(0x27, 0x01);
+	retval |= pch_phub_write_serial_rom(0x26, 0x00);
+	retval |= pch_phub_write_serial_rom(0x25, 0x00);
+	retval |= pch_phub_write_serial_rom(0x24, 0x00);
+
+	return retval;
+}
+
+/**
+ * pch_phub_read_gbe_mac_addr() - Read Gigabit Ethernet MAC address
+ * @offset_address:	Gigabit Ethernet MAC address offset value.
+ * @data:		Buffer of the Gigabit Ethernet MAC address value.
+ */
+static void pch_phub_read_gbe_mac_addr(unsigned int offset_address, u8 *data)
+{
+	pch_phub_read_serial_rom_val(offset_address, data);
+}
+
+/**
+ * pch_phub_write_gbe_mac_addr() - Write MAC address
+ * @offset_address:	Gigabit Ethernet MAC address offset value.
+ * @data:		Gigabit Ethernet MAC address value.
+ */
+static int pch_phub_write_gbe_mac_addr(unsigned int offset_address,
+				   u8 data)
+{
+	int retval;
+
+	retval = pch_phub_gbe_serial_rom_conf();
+	retval |= pch_phub_write_serial_rom_val(offset_address, data);
+
+	return retval;
+}
+
+static ssize_t pch_phub_read(struct file *file, char __user *buf, size_t size,
+			     loff_t *ppos)
+{
+	unsigned int rom_signature;
+	unsigned char rom_length;
+	int ret_value;
+	unsigned int tmp;
+	unsigned char data;
+	unsigned int addr_offset;
+	unsigned int orom_size;
+	int ret;
+	int err;
+	loff_t pos = *ppos;
+
+	ret = mutex_lock_interruptible(&pch_phub_read_mutex);
+	if (ret) {
+		err = -ERESTARTSYS;
+		goto return_err_nomutex;
+	}
+
+	/* Get Rom signature */
+	pch_phub_read_serial_rom(0x80, (unsigned char *)&rom_signature);
+	rom_signature &= 0xff;
+	pch_phub_read_serial_rom(0x81, (unsigned char *)&tmp);
+	rom_signature |= (tmp & 0xff) << 8;
+	if (rom_signature == 0xAA55) {
+		pch_phub_read_serial_rom(0x82, &rom_length);
+		orom_size = rom_length * 512;
+		if (orom_size < pos) {
+			addr_offset = 0;
+			goto return_ok;
+		}
+
+		for (addr_offset = 0; addr_offset < size; addr_offset++) {
+			pch_phub_read_serial_rom(0x80 + addr_offset + pos,
+									 &data);
+			ret_value = copy_to_user(&buf[addr_offset], &data, 1);
+			if (ret_value) {
+				err = -EFAULT;
+				goto return_err;
+			}
+
+			if (orom_size < pos + addr_offset) {
+				*ppos += addr_offset;
+				goto return_ok;
+			}
+
+		}
+	} else {
+		return -ENODATA;
+	}
+	*ppos += addr_offset;
+return_ok:
+	mutex_unlock(&pch_phub_read_mutex);
+	return addr_offset;
+
+return_err:
+	mutex_unlock(&pch_phub_read_mutex);
+return_err_nomutex:
+	return err;
+}
+
+
+static ssize_t pch_phub_write(struct file *file, const char __user *buf,
+			      size_t size, loff_t *ppos)
+{
+	unsigned int data;
+	int ret_value1;
+	int ret_value2;
+	int err;
+	unsigned int addr_offset;
+	loff_t pos = *ppos;
+	int ret;
+
+	ret = mutex_lock_interruptible(&pch_phub_write_mutex);
+	if (ret) {
+		err = -ERESTARTSYS;
+		goto return_err_nomutex;
+	}
+
+	for (addr_offset = 0; addr_offset < size; addr_offset++) {
+		ret_value1 = get_user(data, &buf[addr_offset]);
+		if (ret_value1) {
+			err = -EFAULT;
+			goto return_err;
+		}
+
+		ret_value2 = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
+							data);
+		if (ret_value2) {
+			err = ret_value2;
+			goto return_err;
+		}
+
+		if (PCH_PHUB_OROM_SIZE < pos + addr_offset) {
+			*ppos += addr_offset;
+			goto return_ok;
+		}
+
+	}
+
+	*ppos += addr_offset;
+
+return_ok:
+	mutex_unlock(&pch_phub_write_mutex);
+	return addr_offset;
+
+return_err:
+	mutex_unlock(&pch_phub_write_mutex);
+return_err_nomutex:
+	return err;
+}
+
+
+static long pch_phub_ioctl(struct file *file, unsigned int cmd,
+			   unsigned long arg)
+{
+	int ret_value;
+	__u8 mac_addr[6];
+	int ret;
+	unsigned int i;
+	void __user *varg = (void __user *)arg;
+
+	ret = mutex_lock_interruptible(&pch_phub_ioctl_mutex);
+	if (ret) {
+		ret_value = -ERESTARTSYS;
+		goto return_nomutex;
+	}
+
+	if (pch_phub_reg.pch_phub_suspended == true) {
+		ret_value = -EBUSY;
+		goto return_ioctrl;
+	}
+
+	switch (cmd) {
+	case IOCTL_PHUB_READ_MAC_ADDR:
+		for (i = 0; i < 6; i++)
+			pch_phub_read_gbe_mac_addr(i, &mac_addr[i]);
+
+		ret_value = copy_to_user(varg,
+					 mac_addr, sizeof(mac_addr));
+		if (ret_value)
+			ret_value = -EFAULT;
+
+		break;
+
+	case IOCTL_PHUB_WRITE_MAC_ADDR:
+		ret_value = copy_from_user(mac_addr, varg, sizeof(mac_addr));
+
+		if (ret_value)
+			ret_value = -EFAULT;
+
+		for (i = 0; i < 6; i++)
+			pch_phub_write_gbe_mac_addr(i, mac_addr[i]);
+		break;
+
+	default:
+		ret_value = -EINVAL;
+		break;
+	}
+return_ioctrl:
+	mutex_unlock(&pch_phub_ioctl_mutex);
+return_nomutex:
+	return ret_value;
+}
+
+
+/* file_operations structure initialization */
+static const struct file_operations pch_phub_fops = {
+	.owner = THIS_MODULE,
+	.read = pch_phub_read,
+	.write = pch_phub_write,
+	.unlocked_ioctl = pch_phub_ioctl,
+	.llseek = default_llseek
+};
+
+
+static int __devinit pch_phub_probe(struct pci_dev *pdev,
+				    const struct pci_device_id *id)
+{
+	int ret;
+	unsigned int rom_size;
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+				"\npch_phub_probe : pci_enable_device FAILED");
+		goto err_pci_enable_dev;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : "
+			"pci_enable_device returns %d\n", ret);
+
+	ret = pci_request_regions(pdev, MODULE_NAME);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+				"pch_phub_probe : pci_request_regions FAILED");
+		goto err_req_regions;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : "
+		"pci_request_regions returns %d\n", ret);
+
+	pch_phub_reg.pch_phub_base_address = pci_iomap(pdev, 1, 0);
+
+	if (pch_phub_reg.pch_phub_base_address == 0) {
+		dev_dbg(&pdev->dev, "pch_phub_probe : pci_iomap FAILED");
+		ret = -ENOMEM;
+		goto err_pci_iomap;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : pci_iomap SUCCESS and value "
+		"in pch_phub_base_address variable is 0x%08x\n",
+		(unsigned int)pch_phub_reg.pch_phub_base_address);
+
+	pch_phub_reg.pch_phub_extrom_base_address =
+						pci_map_rom(pdev, &rom_size);
+	if (pch_phub_reg.pch_phub_extrom_base_address == 0) {
+		dev_dbg(&pdev->dev, "pch_phub_probe : pci_map_rom FAILED");
+		ret = -ENOMEM;
+		goto err_pci_map;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : "
+		"pci_map_rom SUCCESS and value in "
+		"pch_phub_extrom_base_address variable is 0x%08x\n",
+		(unsigned int)pch_phub_reg.pch_phub_extrom_base_address);
+
+	ret = alloc_chrdev_region(&pch_phub_dev_no, 0,
+					PCH_MINOR_NOS, MODULE_NAME);
+	if (ret) {
+		dev_dbg(&pdev->dev, "pch_phub_probe : "
+				"alloc_chrdev_region FAILED");
+
+		goto err_alloc_cdev;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : "
+		"alloc_chrdev_region returns %d\n", ret);
+
+	cdev_init(&pch_phub_dev, &pch_phub_fops);
+	dev_dbg(&pdev->dev,
+			"pch_phub_probe :  cdev_init invoked successfully\n");
+
+	pch_phub_dev.owner = THIS_MODULE;
+	pch_phub_dev.ops = &pch_phub_fops;
+
+	ret = cdev_add(&pch_phub_dev, pch_phub_dev_no, PCH_MINOR_NOS);
+	if (ret) {
+		dev_dbg(&pdev->dev, "pch_phub_probe :  cdev_add FAILED");
+		goto err_cdev_add;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe :  cdev_add returns %d\n", ret);
+
+	/* set the clock config reg if CAN clock is 50Mhz */
+	dev_dbg(&pdev->dev, "pch_phub_probe : invoking "
+		"pch_phub_read_modify_write_reg "
+		"to set CLKCFG reg for CAN clk 50Mhz\n");
+	pch_phub_read_modify_write_reg((unsigned int)CLKCFG_REG_OFFSET,
+					  CLKCFG_CAN_50MHZ, CLKCFG_CANCLK_MASK);
+
+	/* set the prefech value */
+	iowrite32(0x000ffffa, pch_phub_reg.pch_phub_base_address + 0x14);
+	/* set the interrupt delay value */
+	iowrite32(0x25, pch_phub_reg.pch_phub_base_address + 0x44);
+	return 0;
+
+err_cdev_add:
+	unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
+err_alloc_cdev:
+	pci_unmap_rom(pdev, pch_phub_reg.pch_phub_extrom_base_address);
+err_pci_map:
+	pci_iounmap(pdev, pch_phub_reg.pch_phub_base_address);
+err_pci_iomap:
+	pci_release_regions(pdev);
+err_req_regions:
+	pci_disable_device(pdev);
+err_pci_enable_dev:
+	dev_dbg(&pdev->dev, "pch_phub_probe returns %d\n", ret);
+	return ret;
+}
+
+static void __devexit pch_phub_remove(struct pci_dev *pdev)
+{
+
+	cdev_del(&pch_phub_dev);
+	dev_dbg(&pdev->dev,
+			"pch_phub_remove - cdev_del Invoked successfully\n");
+
+	unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+		"unregister_chrdev_region Invoked successfully\n");
+
+	pci_unmap_rom(pdev, pch_phub_reg.pch_phub_extrom_base_address);
+
+	pci_iounmap(pdev, pch_phub_reg.pch_phub_base_address);
+
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+			"pci_iounmap Invoked successfully\n");
+
+	pci_release_regions(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+		"pci_release_regions Invoked successfully\n");
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+		"pci_disable_device Invoked successfully\n");
+
+}
+
+#ifdef CONFIG_PM
+
+static int pch_phub_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	int ret;
+
+	pch_phub_reg.pch_phub_suspended = true;/* For blocking further IOCTLs */
+
+	pch_phub_save_reg_conf(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+		"pch_phub_save_reg_conf Invoked successfully\n");
+
+	ret = pci_save_state(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+			" pch_phub_suspend -pci_save_state returns-%d\n", ret);
+		return ret;
+	}
+	dev_dbg(&pdev->dev,
+			"pch_phub_suspend - pci_save_state returns %d\n", ret);
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+			"pci_enable_wake Invoked successfully\n");
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+			"pci_disable_device Invoked successfully\n");
+
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+			"pci_set_power_state Invoked successfully   "
+			"return = %d\n", 0);
+
+	return 0;
+}
+
+static int pch_phub_resume(struct pci_dev *pdev)
+{
+
+	int ret;
+
+	pci_set_power_state(pdev, PCI_D0);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+		"pci_set_power_state Invoked successfully\n");
+
+	pci_restore_state(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+		"pci_restore_state Invoked successfully\n");
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+				"pch_phub_resume-pci_enable_device failed ");
+		return ret;
+	}
+
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+			"pci_enable_device returns -%d\n", ret);
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+			"pci_enable_wake Invoked successfully\n");
+
+	pch_phub_restore_reg_conf(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+		"pch_phub_restore_reg_conf Invoked successfully\n");
+
+	pch_phub_reg.pch_phub_suspended = false;
+
+	dev_dbg(&pdev->dev, "pch_phub_resume  returns- %d\n", 0);
+	return 0;
+}
+#else
+#define pch_phub_suspend NULL
+#define pch_phub_resume NULL
+#endif /* CONFIG_PM */
+
+static struct pci_device_id pch_phub_pcidev_id[] = {
+
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH1_PHUB)},
+	{0,}
+};
+
+
+static struct pci_driver pch_phub_driver = {
+	.name = "pch_phub",
+	.id_table = pch_phub_pcidev_id,
+	.probe = pch_phub_probe,
+	.remove = __devexit_p(pch_phub_remove),
+	.suspend = pch_phub_suspend,
+	.resume = pch_phub_resume
+};
+
+/* pch_phub_pci_init - Implements the initialization functionality of
+ * the module.
+ */
+static int __init pch_phub_pci_init(void)
+{
+	int ret;
+	ret = pci_register_driver(&pch_phub_driver);
+
+	return ret;
+}
+
+/* pch_phub_pci_exit - Implements the exit functionality of the module. */
+static void __exit pch_phub_pci_exit(void)
+{
+	pci_unregister_driver(&pch_phub_driver);
+
+}
+
+module_init(pch_phub_pci_init);
+module_exit(pch_phub_pci_exit);
+
+MODULE_DESCRIPTION("PCH PACKET HUB PCI Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/char/pch_phub/pch_phub.h b/drivers/char/pch_phub/pch_phub.h
new file mode 100755
index 0000000..7baa272
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.h
@@ -0,0 +1,48 @@
+#ifndef __PCH_PHUB_H__
+#define __PCH_PHUB_H__
+/*!
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#define PHUB_IOCTL_MAGIC		(0xf7)
+
+/* Read GbE MAC address */
+#define IOCTL_PHUB_READ_MAC_ADDR (_IOR(PHUB_IOCTL_MAGIC, 6, __u8[6]))
+
+/* Write GbE MAC address */
+#define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 7, __u8[6]))
+
+/* SROM ACCESS Macro */
+#define PCH_WORD_ADDR_MASK (~((1 << 2) - 1))
+
+/* Registers address offset */
+#define PCH_PHUB_ID_REG				0x0000
+#define PCH_PHUB_QUEUE_PRI_VAL_REG		0x0004
+#define PCH_PHUB_RC_QUEUE_MAXSIZE_REG		0x0008
+#define PCH_PHUB_BRI_QUEUE_MAXSIZE_REG		0x000C
+#define PCH_PHUB_COMP_RESP_TIMEOUT_REG		0x0010
+#define PCH_PHUB_BUS_SLAVE_CONTROL_REG		0x0014
+#define PCH_PHUB_DEADLOCK_AVOID_TYPE_REG	0x0018
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG0	0x0020
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG1	0x0024
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG2	0x0028
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG3	0x002C
+#define PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE	0x0040
+#define CLKCFG_REG_OFFSET			0x500
+
+#define PCH_PHUB_OROM_SIZE 15360
+
+#endif
-- 
1.6.0.6

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

* Re: [PATCH] Packet hub driver of Topcliff PCH
  2010-06-30  7:51 ` [PATCH] Packet hub driver of Topcliff PCH Masayuki Ohtak
@ 2010-06-30 18:05   ` Randy Dunlap
  2010-07-01  2:52     ` Masayuki Ohtake
  0 siblings, 1 reply; 46+ messages in thread
From: Randy Dunlap @ 2010-06-30 18:05 UTC (permalink / raw)
  To: Masayuki Ohtak
  Cc: Andy Isaacson, Andrew Morton, Arnd Bergmann, Wang, Yong Y,
	qi.wang, joel.clark, andrew.chih.howe.khor, Alan Cox, LKML

On Wed, 30 Jun 2010 16:51:30 +0900 Masayuki Ohtak wrote:

> Hi Andy and Andrew
> 
> I have modified for your comments.
> Please confirm below.
> 
> ---
> 
> 
> Packet hub driver of Topcliff PCH
> 
> Topcliff PCH is the platform controller hub that is going to be used in
> Intel’s upcoming general embedded platform. All IO peripherals in
> Topcliff PCH are actually devices sitting on AMBA bus. Packet hub is
> a special converter device in Topcliff PCH that translate AMBA transactions
> to PCI Express transactions and vice versa. Thus packet hub helps present
> all IO peripherals in Topcliff PCH as PCIE devices to IA system.
> Topcliff PCH have MAC address and Option ROM data.
> These data are in SROM which is connected to PCIE bus.
> Packet hub driver of Topcliff PCH can access MAC address and Option ROM data in
> SROM.
> 
> Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
> Acked-by: Arnd <Arnd’s email address>

It's listed in the MAINTAINERS file.  Please use that.


> 
> diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
> index e023682..1851e97 100644
> --- a/drivers/char/Kconfig
> +++ b/drivers/char/Kconfig
> @@ -4,6 +4,16 @@
>  
>  menu "Character devices"
>  
> +config PCH_PHUB
> +	tristate "PCH PHUB"
> +	depends on PCI
> +	help
> +	  Topcliff is a IOH for x86 embedded processor. This IOH is quite

	           is an IOH for x86 embedded processors.

> +	  different with the traditional IOH. Topcliff is a kind of ARM-based

	            from

> +	  processor and connected with PCIe bus (from x86 processor).
> +	  PHUB work as a gateway transform the PCIe transaction into the AMBA

	       works as a gateway to transform

> +	  transaction, and vise verse, and have several transform windows also.

	                   vice versa, and has several



Maybe lose some acromyns?  Overall, this help text should be about
*what* PCH_PHUB is, not *how* it accomplishes its work.
If you want to document *how* it works, put that into the source code
or Documentation/ somewhere.


---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***

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

* Re: [PATCH] Topcliff PHUB: Generate PacketHub driver
  2010-06-30  5:58   ` Masayuki Ohtake
@ 2010-06-30 18:28     ` Andy Isaacson
  2010-07-01  4:08       ` Masayuki Ohtake
  0 siblings, 1 reply; 46+ messages in thread
From: Andy Isaacson @ 2010-06-30 18:28 UTC (permalink / raw)
  To: Masayuki Ohtake
  Cc: Arnd Bergmann, Wang, Yong Y, Wang Qi, Intel OTC, Andrew, Alan Cox, LKML

On Wed, Jun 30, 2010 at 02:58:25PM +0900, Masayuki Ohtake wrote:
> > > + unsigned int i;
> > > + void __iomem *p = pch_phub_reg.pch_phub_base_address;
> > > +
> > > + dev_dbg(&pdev->dev, "pch_phub_restore_reg_conf ENTRY\n");
> > > + /* to store contents of PHUB_ID register */
> > > + iowrite32(pch_phub_reg.phub_id_reg, p + PCH_PHUB_PHUB_ID_REG);
> >
> > Don't include comments that just duplicate the code.  Also, rename your
> > constants from PCH_PHUB_PHUB_ to, I dunno, PHUB_ or something.
> 
> Sorry, I can't understand your intention.
> Please give us more information.

My mistake, I merged two comments into one paragraph, let me clarify.

1. When writing comments, do not write comments that duplicate the code.
Instead of writing
	/* store PHUB_ID */
	iowrite32(..._PHUB_ID_REG);
	/* store PHUB_FOO */
	iowrite32(..._PHUB_FOO_REG);
you should delete the line-by-line comments and just write
	iowrite32(..._PHUB_ID_REG);
	iowrite32(..._PHUB_FOO_REG);

2. your register names are very long.  Since the #define names are
private to this driver, there's no need to make them extremely
descriptive.  Instead of naming your registers 
PCH_PHUB_PHUB_ID_REG, you should change the names to be shorter, like
PHUB_ID_REG or PCH_ID_REG.  This will make your source code much more
readable by reducing linewrapping.

> > I seriously doubt that your device is special enough to warrant a custom
> > /dev node with proprietary semantics.  If this is just part of an
> > Ethernet driver, please implement it in drivers/net/; if this is a
> > generic PROM accessor, there must be some semi-standardized EPROM access
> > interface but I don't know what it is offhand.
>
> Since SROM is not in GbE HW but Phub HW, Phub is not part of Ethernet driver.
> Packet hub is not generic driver but special device.

It sounds like PHUB is a system-level device which provides access to a
SROM which contains GbE configuration data.  If that is correct, then I
have two comments:

1.  There are many other systems with similar configurations -- MIPS
SiByte, Alpha SRM, SPARC OpenFirmware, and some ARM systems, just to
name a few.  None of them expose the SROM as a custom /dev node AFAIK.
Is there a shared infrastructure that you can implement?

2. How does your GbE driver get the MAC address from the SPROM?  If
there is an in-kernel user of the PHUB interface, it might be much
easier to understand the design.

Thanks for responding to my review so quickly!
-andy

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

* Re: [PATCH] Packet hub driver of Topcliff PCH
  2010-06-30 18:05   ` Randy Dunlap
@ 2010-07-01  2:52     ` Masayuki Ohtake
  0 siblings, 0 replies; 46+ messages in thread
From: Masayuki Ohtake @ 2010-07-01  2:52 UTC (permalink / raw)
  To: Randy Dunlap
  Cc: Andy Isaacson, Andrew Morton, Arnd Bergmann, Wang, Yong Y,
	qi.wang, joel.clark, andrew.chih.howe.khor, Alan Cox, LKML

Hi Randy

> It's listed in the MAINTAINERS file.  Please use that.
I will add Arnd's e-mail address.

> Maybe lose some acromyns?  Overall, this help text should be about
> *what* PCH_PHUB is, not *how* it accomplishes its work.
> If you want to document *how* it works, put that into the source code
> or Documentation/ somewhere.
I will rewrite my help text.


Thanks, Ohtake

----- Original Message ----- 
From: "Randy Dunlap" <randy.dunlap@oracle.com>
To: "Masayuki Ohtak" <masa-korg@dsn.okisemi.com>
Cc: "Andy Isaacson" <adi@hexapodia.org>; "Andrew Morton" <akpm@linux-foundation.org>; "Arnd Bergmann" <arnd@arndb.de>;
"Wang, Yong Y" <yong.y.wang@intel.com>; <qi.wang@intel.com>; <joel.clark@intel.com>; <andrew.chih.howe.khor@intel.com>;
"Alan Cox" <alan@lxorguk.ukuu.org.uk>; "LKML" <linux-kernel@vger.kernel.org>
Sent: Thursday, July 01, 2010 3:05 AM
Subject: Re: [PATCH] Packet hub driver of Topcliff PCH


> On Wed, 30 Jun 2010 16:51:30 +0900 Masayuki Ohtak wrote:
>
> > Hi Andy and Andrew
> >
> > I have modified for your comments.
> > Please confirm below.
> >
> > ---
> >
> >
> > Packet hub driver of Topcliff PCH
> >
> > Topcliff PCH is the platform controller hub that is going to be used in
> > Intel^[$B!G^[(Bs upcoming general embedded platform. All IO peripherals in
> > Topcliff PCH are actually devices sitting on AMBA bus. Packet hub is
> > a special converter device in Topcliff PCH that translate AMBA transactions
> > to PCI Express transactions and vice versa. Thus packet hub helps present
> > all IO peripherals in Topcliff PCH as PCIE devices to IA system.
> > Topcliff PCH have MAC address and Option ROM data.
> > These data are in SROM which is connected to PCIE bus.
> > Packet hub driver of Topcliff PCH can access MAC address and Option ROM data in
> > SROM.
> >
> > Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
> > Acked-by: Arnd <Arnd^[$B!G^[(Bs email address>
>
> It's listed in the MAINTAINERS file.  Please use that.
>
>
> >
> > diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
> > index e023682..1851e97 100644
> > --- a/drivers/char/Kconfig
> > +++ b/drivers/char/Kconfig
> > @@ -4,6 +4,16 @@
> >
> >  menu "Character devices"
> >
> > +config PCH_PHUB
> > + tristate "PCH PHUB"
> > + depends on PCI
> > + help
> > +   Topcliff is a IOH for x86 embedded processor. This IOH is quite
>
>            is an IOH for x86 embedded processors.
>
> > +   different with the traditional IOH. Topcliff is a kind of ARM-based
>
>             from
>
> > +   processor and connected with PCIe bus (from x86 processor).
> > +   PHUB work as a gateway transform the PCIe transaction into the AMBA
>
>        works as a gateway to transform
>
> > +   transaction, and vise verse, and have several transform windows also.
>
>                    vice versa, and has several
>
>
>
> Maybe lose some acromyns?  Overall, this help text should be about
> *what* PCH_PHUB is, not *how* it accomplishes its work.
> If you want to document *how* it works, put that into the source code
> or Documentation/ somewhere.
>
>
> ---
> ~Randy
> *** Remember to use Documentation/SubmitChecklist when testing your code ***
>



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

* Re: [PATCH] Topcliff PHUB: Generate PacketHub driver
  2010-06-30 18:28     ` Andy Isaacson
@ 2010-07-01  4:08       ` Masayuki Ohtake
  0 siblings, 0 replies; 46+ messages in thread
From: Masayuki Ohtake @ 2010-07-01  4:08 UTC (permalink / raw)
  To: Andy Isaacson
  Cc: Arnd Bergmann, Wang, Yong Y, Wang Qi, Intel OTC, Andrew, Alan Cox, LKML

Hi Andy,

> 1. When writing comments, do not write comments that duplicate the code.
> Instead of writing
Our Phub patch I have resubmitted yesterday have been already modified above.

> 2. your register names are very long.  Since the #define names are
> private to this driver, there's no need to make them extremely
> descriptive.  Instead of naming your registers
> PCH_PHUB_PHUB_ID_REG, you should change the names to be shorter, like
> PHUB_ID_REG or PCH_ID_REG.  This will make your source code much more
> readable by reducing linewrapping.
This was our mistake.
I have modified PCH_PHUB_PHUB_ID_REG to PCH_PHUB_ID_REG.
Our Phub patch I have resubmitted yesterday have been already modified above.

> It sounds like PHUB is a system-level device which provides access to a
> SROM which contains GbE configuration data.  If that is correct, then I
> have two comments:
Yes, SROM has GbE configuration data (GbE mac address) .

>
> 1.  There are many other systems with similar configurations -- MIPS
> SiByte, Alpha SRM, SPARC OpenFirmware, and some ARM systems, just to
> name a few.  None of them expose the SROM as a custom /dev node AFAIK.
> Is there a shared infrastructure that you can implement?
Sorry, I can't understand your intension.
Please give me more detail.

>
> 2. How does your GbE driver get the MAC address from the SPROM?  If
> there is an in-kernel user of the PHUB interface, it might be much
> easier to understand the design.
PHUB HW transfers MAC address(in SROM) data to GbE register to set MAC address when boot processing.

Thanks, Ohtake
----- Original Message ----- 
From: "Andy Isaacson" <adi@hexapodia.org>
To: "Masayuki Ohtake" <masa-korg@dsn.okisemi.com>
Cc: "Arnd Bergmann" <arnd@arndb.de>; "Wang, Yong Y" <yong.y.wang@intel.com>; "Wang Qi" <qi.wang@intel.com>; "Intel OTC"
<joel.clark@intel.com>; "Andrew" <andrew.chih.howe.khor@intel.com>; "Alan Cox" <alan@lxorguk.ukuu.org.uk>; "LKML"
<linux-kernel@vger.kernel.org>
Sent: Thursday, July 01, 2010 3:28 AM
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver


> On Wed, Jun 30, 2010 at 02:58:25PM +0900, Masayuki Ohtake wrote:
> > > > + unsigned int i;
> > > > + void __iomem *p = pch_phub_reg.pch_phub_base_address;
> > > > +
> > > > + dev_dbg(&pdev->dev, "pch_phub_restore_reg_conf ENTRY\n");
> > > > + /* to store contents of PHUB_ID register */
> > > > + iowrite32(pch_phub_reg.phub_id_reg, p + PCH_PHUB_PHUB_ID_REG);
> > >
> > > Don't include comments that just duplicate the code.  Also, rename your
> > > constants from PCH_PHUB_PHUB_ to, I dunno, PHUB_ or something.
> >
> > Sorry, I can't understand your intention.
> > Please give us more information.
>
> My mistake, I merged two comments into one paragraph, let me clarify.
>
> 1. When writing comments, do not write comments that duplicate the code.
> Instead of writing
> /* store PHUB_ID */
> iowrite32(..._PHUB_ID_REG);
> /* store PHUB_FOO */
> iowrite32(..._PHUB_FOO_REG);
> you should delete the line-by-line comments and just write
> iowrite32(..._PHUB_ID_REG);
> iowrite32(..._PHUB_FOO_REG);
>
> 2. your register names are very long.  Since the #define names are
> private to this driver, there's no need to make them extremely
> descriptive.  Instead of naming your registers
> PCH_PHUB_PHUB_ID_REG, you should change the names to be shorter, like
> PHUB_ID_REG or PCH_ID_REG.  This will make your source code much more
> readable by reducing linewrapping.
>
> > > I seriously doubt that your device is special enough to warrant a custom
> > > /dev node with proprietary semantics.  If this is just part of an
> > > Ethernet driver, please implement it in drivers/net/; if this is a
> > > generic PROM accessor, there must be some semi-standardized EPROM access
> > > interface but I don't know what it is offhand.
> >
> > Since SROM is not in GbE HW but Phub HW, Phub is not part of Ethernet driver.
> > Packet hub is not generic driver but special device.
>
> It sounds like PHUB is a system-level device which provides access to a
> SROM which contains GbE configuration data.  If that is correct, then I
> have two comments:
>
> 1.  There are many other systems with similar configurations -- MIPS
> SiByte, Alpha SRM, SPARC OpenFirmware, and some ARM systems, just to
> name a few.  None of them expose the SROM as a custom /dev node AFAIK.
> Is there a shared infrastructure that you can implement?
>
> 2. How does your GbE driver get the MAC address from the SPROM?  If
> there is an in-kernel user of the PHUB interface, it might be much
> easier to understand the design.
>
> Thanks for responding to my review so quickly!
> -andy
>




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

* [PATCH] Packet hub driver of Topcliff PCH
  2010-06-22  5:33 [PATCH] Topcliff PHUB: Generate PacketHub driver Masayuki Ohtak
                   ` (3 preceding siblings ...)
  2010-06-30  7:51 ` [PATCH] Packet hub driver of Topcliff PCH Masayuki Ohtak
@ 2010-07-01  5:14 ` Masayuki Ohtak
  2010-07-01  6:58   ` Andy Isaacson
  2010-07-01 10:38 ` Masayuki Ohtak
                   ` (5 subsequent siblings)
  10 siblings, 1 reply; 46+ messages in thread
From: Masayuki Ohtak @ 2010-07-01  5:14 UTC (permalink / raw)
  To: Andy Isaacson, Randy Dunlap
  Cc: Arnd Bergmann, Wang, Yong Y, qi.wang, joel.clark,
	andrew.chih.howe.khor, Alan Cox, LKML

Hi Andy and Randy

I have modified for your comments.
Please confirm below.

Thanks, Ohtake.

---

Packet hub driver of Topcliff PCH

Topcliff PCH is the platform controller hub that is going to be used in
Intel's upcoming general embedded platform. All IO peripherals in
Topcliff PCH are actually devices sitting on AMBA bus. Packet hub is
a special converter device in Topcliff PCH that translate AMBA transactions
to PCI Express transactions and vice versa. Thus packet hub helps present
all IO peripherals in Topcliff PCH as PCIE devices to IA system.
Topcliff PCH have MAC address and Option ROM data.
These data are in SROM which is connected to PCIE bus.
Packet hub driver of Topcliff PCH can access MAC address and Option ROM data in
SROM.

Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>

---
 drivers/char/Kconfig             |    9 +
 drivers/char/Makefile            |    2 +
 drivers/char/pch_phub/Makefile   |    3 +
 drivers/char/pch_phub/pch_phub.c |  805 ++++++++++++++++++++++++++++++++++++++
 drivers/char/pch_phub/pch_phub.h |   48 +++
 5 files changed, 867 insertions(+), 0 deletions(-)
 create mode 100644 drivers/char/pch_phub/Makefile
 create mode 100755 drivers/char/pch_phub/pch_phub.c
 create mode 100755 drivers/char/pch_phub/pch_phub.h

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index e023682..9ef3c85 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -4,6 +4,15 @@
 
 menu "Character devices"
 
+config PCH_PHUB
+	tristate "PCH PHUB"
+	depends on PCI
+	help
+	  This driver is for PCH PHUB of Topcliff which is an IOH for x86
+	  embedded processor. The Topcliff has MAC address and Option ROM data
+	  in SROM. This dirver can access MAC address and Option ROM data in
+	  SROM.
+
 config VT
 	bool "Virtual terminal" if EMBEDDED
 	depends on !S390
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index f957edf..1e3eb6c 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -111,6 +111,8 @@ obj-$(CONFIG_PS3_FLASH)		+= ps3flash.o
 obj-$(CONFIG_JS_RTC)		+= js-rtc.o
 js-rtc-y = rtc.o
 
+obj-$(CONFIG_PCH_PHUB)	+= pch_phub/
+
 # Files generated that shall be removed upon make clean
 clean-files := consolemap_deftbl.c defkeymap.c
 
diff --git a/drivers/char/pch_phub/Makefile b/drivers/char/pch_phub/Makefile
new file mode 100644
index 0000000..93aaffe
--- /dev/null
+++ b/drivers/char/pch_phub/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_PCH_PHUB) += pch_phub_drv.o
+
+pch_phub_drv-objs := pch_phub.o
diff --git a/drivers/char/pch_phub/pch_phub.c b/drivers/char/pch_phub/pch_phub.c
new file mode 100755
index 0000000..d443048
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.c
@@ -0,0 +1,805 @@
+/*!
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include "pch_phub.h"
+
+#define PHUB_STATUS 0x00		/* Status Register offset */
+#define PHUB_CONTROL 0x04		/* Control Register offset */
+#define PHUB_TIMEOUT 0x05		/* Time out value for Status Register */
+#define PCH_PHUB_ROM_WRITE_ENABLE 0x01	/* Enabling for writing ROM */
+#define PCH_PHUB_ROM_WRITE_DISABLE 0x00	/* Disabling for writing ROM */
+#define PCH_PHUB_ROM_START_ADDR 0x14	/* ROM data area start address offset */
+
+/* MAX number of INT_REDUCE_CONTROL registers */
+#define MAX_NUM_INT_REDUCE_CONTROL_REG 128
+#define PCI_DEVICE_ID_PCH1_PHUB 0x8801
+#define PCH_MINOR_NOS 1
+#define CLKCFG_CAN_50MHZ 0x12000000
+#define CLKCFG_CANCLK_MASK 0xFF000000
+#define MODULE_NAME "pch_phub"
+
+/**
+ * struct pch_phub_reg - PHUB register structure
+ * @phub_id_reg:			PHUB_ID register val
+ * @q_pri_val_reg:			QUEUE_PRI_VAL register val
+ * @rc_q_maxsize_reg:			RC_QUEUE_MAXSIZE register val
+ * @bri_q_maxsize_reg:			BRI_QUEUE_MAXSIZE register val
+ * @comp_resp_timeout_reg:		COMP_RESP_TIMEOUT register val
+ * @bus_slave_control_reg:		BUS_SLAVE_CONTROL_REG register val
+ * @deadlock_avoid_type_reg:		DEADLOCK_AVOID_TYPE register val
+ * @intpin_reg_wpermit_reg0:		INTPIN_REG_WPERMIT register 0 val
+ * @intpin_reg_wpermit_reg1:		INTPIN_REG_WPERMIT register 1 val
+ * @intpin_reg_wpermit_reg2:		INTPIN_REG_WPERMIT register 2 val
+ * @intpin_reg_wpermit_reg3:		INTPIN_REG_WPERMIT register 3 val
+ * @int_reduce_control_reg:		INT_REDUCE_CONTROL registers val
+ * @clkcfg_reg:				CLK CFG register val
+ * @pch_phub_base_address:		Register base address
+ * @pch_phub_extrom_base_address:	external rom base address
+ * @pch_phub_suspended:			PHUB status val
+ */
+struct pch_phub_reg {
+	u32 phub_id_reg;
+	u32 q_pri_val_reg;
+	u32 rc_q_maxsize_reg;
+	u32 bri_q_maxsize_reg;
+	u32 comp_resp_timeout_reg;
+	u32 bus_slave_control_reg;
+	u32 deadlock_avoid_type_reg;
+	u32 intpin_reg_wpermit_reg0;
+	u32 intpin_reg_wpermit_reg1;
+	u32 intpin_reg_wpermit_reg2;
+	u32 intpin_reg_wpermit_reg3;
+	u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG];
+	u32 clkcfg_reg;
+	void __iomem *pch_phub_base_address;
+	void __iomem *pch_phub_extrom_base_address;
+	int pch_phub_suspended;
+} pch_phub_reg;
+
+/* SROM SPEC for MAC address assignment offset */
+static const int pch_phub_mac_offset[6] = {0x3, 0x2, 0x1, 0x0, 0xb, 0xa};
+
+static DEFINE_MUTEX(pch_phub_ioctl_mutex);
+static DEFINE_MUTEX(pch_phub_read_mutex);
+static DEFINE_MUTEX(pch_phub_write_mutex);
+static dev_t pch_phub_dev_no;
+static struct cdev pch_phub_dev;
+
+/**
+ * pch_phub_read_modify_write_reg() - Reading modifying and writing register
+ * @reg_addr_offset:	Register offset address value.
+ * @data:		Writing value.
+ * @mask:		Mask value.
+ */
+static void pch_phub_read_modify_write_reg(unsigned int reg_addr_offset,
+					   unsigned int data, unsigned int mask)
+{
+	void __iomem *reg_addr = pch_phub_reg.pch_phub_base_address +
+					 reg_addr_offset;
+	iowrite32(((ioread32(reg_addr) & ~mask)) | data, reg_addr);
+}
+
+
+/* pch_phub_save_reg_conf - saves register configuration */
+static void pch_phub_save_reg_conf(struct pci_dev *pdev)
+{
+	unsigned int i;
+	void __iomem *p = pch_phub_reg.pch_phub_base_address;
+
+	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf ENTRY\n");
+	pch_phub_reg.phub_id_reg = ioread32(p + PCH_PHUB_ID_REG);
+	pch_phub_reg.q_pri_val_reg = ioread32(p + PCH_PHUB_QUEUE_PRI_VAL_REG);
+	pch_phub_reg.rc_q_maxsize_reg =
+	    ioread32(p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+	pch_phub_reg.bri_q_maxsize_reg =
+	    ioread32(p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+	pch_phub_reg.comp_resp_timeout_reg =
+	    ioread32(p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+	pch_phub_reg.bus_slave_control_reg =
+	    ioread32(p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+	pch_phub_reg.deadlock_avoid_type_reg =
+	    ioread32(p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+	pch_phub_reg.intpin_reg_wpermit_reg0 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+	pch_phub_reg.intpin_reg_wpermit_reg1 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+	pch_phub_reg.intpin_reg_wpermit_reg2 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+	pch_phub_reg.intpin_reg_wpermit_reg3 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
+	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+		"pch_phub_reg.phub_id_reg=%x, "
+		"pch_phub_reg.q_pri_val_reg=%x, "
+		"pch_phub_reg.rc_q_maxsize_reg=%x, "
+		"pch_phub_reg.bri_q_maxsize_reg=%x, "
+		"pch_phub_reg.comp_resp_timeout_reg=%x, "
+		"pch_phub_reg.bus_slave_control_reg=%x, "
+		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n",
+		pch_phub_reg.phub_id_reg,
+		pch_phub_reg.q_pri_val_reg,
+		pch_phub_reg.rc_q_maxsize_reg,
+		pch_phub_reg.bri_q_maxsize_reg,
+		pch_phub_reg.comp_resp_timeout_reg,
+		pch_phub_reg.bus_slave_control_reg,
+		pch_phub_reg.deadlock_avoid_type_reg,
+		pch_phub_reg.intpin_reg_wpermit_reg0,
+		pch_phub_reg.intpin_reg_wpermit_reg1,
+		pch_phub_reg.intpin_reg_wpermit_reg2,
+		pch_phub_reg.intpin_reg_wpermit_reg3);
+	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
+		pch_phub_reg.int_reduce_control_reg[i] =
+		    ioread32(p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
+		dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
+			i, pch_phub_reg.int_reduce_control_reg[i]);
+	}
+	pch_phub_reg.clkcfg_reg = ioread32(p + CLKCFG_REG_OFFSET);
+
+}
+
+/* pch_phub_restore_reg_conf - restore register configuration */
+static void pch_phub_restore_reg_conf(struct pci_dev *pdev)
+{
+	unsigned int i;
+	void __iomem *p = pch_phub_reg.pch_phub_base_address;
+
+	dev_dbg(&pdev->dev, "pch_phub_restore_reg_conf ENTRY\n");
+	iowrite32(pch_phub_reg.phub_id_reg, p + PCH_PHUB_ID_REG);
+	iowrite32(pch_phub_reg.q_pri_val_reg, p + PCH_PHUB_QUEUE_PRI_VAL_REG);
+	iowrite32(pch_phub_reg.rc_q_maxsize_reg,
+					p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+	iowrite32(pch_phub_reg.bri_q_maxsize_reg,
+					p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+	iowrite32(pch_phub_reg.comp_resp_timeout_reg,
+					p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+	iowrite32(pch_phub_reg.bus_slave_control_reg,
+					p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+	iowrite32(pch_phub_reg.deadlock_avoid_type_reg,
+					p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg0,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg1,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg2,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg3,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
+	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+		"pch_phub_reg.phub_id_reg=%x, "
+		"pch_phub_reg.q_pri_val_reg=%x, "
+		"pch_phub_reg.rc_q_maxsize_reg=%x, "
+		"pch_phub_reg.bri_q_maxsize_reg=%x, "
+		"pch_phub_reg.comp_resp_timeout_reg=%x, "
+		"pch_phub_reg.bus_slave_control_reg=%x, "
+		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n",
+		pch_phub_reg.phub_id_reg,
+		pch_phub_reg.q_pri_val_reg,
+		pch_phub_reg.rc_q_maxsize_reg,
+		pch_phub_reg.bri_q_maxsize_reg,
+		pch_phub_reg.comp_resp_timeout_reg,
+		pch_phub_reg.bus_slave_control_reg,
+		pch_phub_reg.deadlock_avoid_type_reg,
+		pch_phub_reg.intpin_reg_wpermit_reg0,
+		pch_phub_reg.intpin_reg_wpermit_reg1,
+		pch_phub_reg.intpin_reg_wpermit_reg2,
+		pch_phub_reg.intpin_reg_wpermit_reg3);
+	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
+		iowrite32(pch_phub_reg.int_reduce_control_reg[i],
+			p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
+		dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
+			i, pch_phub_reg.int_reduce_control_reg[i]);
+	}
+
+	iowrite32(pch_phub_reg.clkcfg_reg, p + CLKCFG_REG_OFFSET);
+}
+
+/**
+ * pch_phub_read_serial_rom() - Reading Serial ROM
+ * @offset_address:	Serial ROM offset address to read.
+ * @data:		Read buffer for specified Serial ROM value.
+ */
+static void pch_phub_read_serial_rom(unsigned int offset_address, u8 *data)
+{
+	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +
+					 offset_address;
+
+	*data = ioread8(mem_addr);
+}
+
+/**
+ * pch_phub_write_serial_rom() - Writing Serial ROM
+ * @offset_address:	Serial ROM offset address.
+ * @data:		Serial ROM value to write.
+ */
+static int pch_phub_write_serial_rom(unsigned int offset_address, u8 data)
+{
+	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +
+					(offset_address & PCH_WORD_ADDR_MASK);
+	int i;
+	unsigned int word_data;
+	unsigned int pos;
+	unsigned int mask;
+	pos = (offset_address % 4) * 8;
+	mask = ~(0xFF << pos);
+
+	iowrite32(PCH_PHUB_ROM_WRITE_ENABLE,
+			pch_phub_reg.pch_phub_extrom_base_address +
+			PHUB_CONTROL);
+
+	word_data = ioread32(mem_addr);
+	iowrite32((word_data & mask) | (u32)data << pos, mem_addr);
+
+	i = 0;
+	while (0x00 != ioread8(pch_phub_reg.pch_phub_extrom_base_address +
+			PHUB_STATUS)) {
+		msleep(1);
+		if (PHUB_TIMEOUT == i)
+			return -EPERM;
+		i++;
+	}
+
+	iowrite32(PCH_PHUB_ROM_WRITE_DISABLE,
+			pch_phub_reg.pch_phub_extrom_base_address +
+			PHUB_CONTROL);
+
+	return 0;
+}
+
+/**
+ * pch_phub_read_serial_rom_val() - Read Serial ROM value
+ * @offset_address:	Serial ROM address offset value.
+ * @data:		Serial ROM value to read.
+ */
+static void pch_phub_read_serial_rom_val(unsigned int offset_address, u8 *data)
+{
+	unsigned int mem_addr;
+
+	mem_addr = PCH_PHUB_ROM_START_ADDR +
+			pch_phub_mac_offset[offset_address];
+
+	pch_phub_read_serial_rom(mem_addr, data);
+
+}
+
+
+/**
+ * pch_phub_write_serial_rom_val() - writing Serial ROM value
+ * @offset_address:	Serial ROM address offset value.
+ * @data:		Serial ROM value.
+ */
+static int pch_phub_write_serial_rom_val(unsigned int offset_address, u8 data)
+{
+	int retval;
+	unsigned int mem_addr;
+
+	mem_addr = PCH_PHUB_ROM_START_ADDR +
+			pch_phub_mac_offset[offset_address];
+
+	retval = pch_phub_write_serial_rom(mem_addr, data);
+
+	return retval;
+}
+
+/* pch_phub_gbe_serial_rom_conf - makes Serial ROM header format configuration
+ * for Gigabit Ethernet MAC address
+ */
+static int pch_phub_gbe_serial_rom_conf(void)
+{
+	int retval;
+
+	retval = pch_phub_write_serial_rom(0x0b, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x0a, 0x10);
+	retval |= pch_phub_write_serial_rom(0x09, 0x01);
+	retval |= pch_phub_write_serial_rom(0x08, 0x02);
+
+	retval |= pch_phub_write_serial_rom(0x0f, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0e, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0d, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0c, 0x80);
+
+	retval |= pch_phub_write_serial_rom(0x13, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x12, 0x10);
+	retval |= pch_phub_write_serial_rom(0x11, 0x01);
+	retval |= pch_phub_write_serial_rom(0x10, 0x18);
+
+	retval |= pch_phub_write_serial_rom(0x1b, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x1a, 0x10);
+	retval |= pch_phub_write_serial_rom(0x19, 0x01);
+	retval |= pch_phub_write_serial_rom(0x18, 0x19);
+
+	retval |= pch_phub_write_serial_rom(0x23, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x22, 0x10);
+	retval |= pch_phub_write_serial_rom(0x21, 0x01);
+	retval |= pch_phub_write_serial_rom(0x20, 0x3a);
+
+	retval |= pch_phub_write_serial_rom(0x27, 0x01);
+	retval |= pch_phub_write_serial_rom(0x26, 0x00);
+	retval |= pch_phub_write_serial_rom(0x25, 0x00);
+	retval |= pch_phub_write_serial_rom(0x24, 0x00);
+
+	return retval;
+}
+
+/**
+ * pch_phub_read_gbe_mac_addr() - Read Gigabit Ethernet MAC address
+ * @offset_address:	Gigabit Ethernet MAC address offset value.
+ * @data:		Buffer of the Gigabit Ethernet MAC address value.
+ */
+static void pch_phub_read_gbe_mac_addr(unsigned int offset_address, u8 *data)
+{
+	pch_phub_read_serial_rom_val(offset_address, data);
+}
+
+/**
+ * pch_phub_write_gbe_mac_addr() - Write MAC address
+ * @offset_address:	Gigabit Ethernet MAC address offset value.
+ * @data:		Gigabit Ethernet MAC address value.
+ */
+static int pch_phub_write_gbe_mac_addr(unsigned int offset_address,
+				   u8 data)
+{
+	int retval;
+
+	retval = pch_phub_gbe_serial_rom_conf();
+	retval |= pch_phub_write_serial_rom_val(offset_address, data);
+
+	return retval;
+}
+
+static ssize_t pch_phub_read(struct file *file, char __user *buf, size_t size,
+			     loff_t *ppos)
+{
+	unsigned int rom_signature;
+	unsigned char rom_length;
+	int ret_value;
+	unsigned int tmp;
+	unsigned char data;
+	unsigned int addr_offset;
+	unsigned int orom_size;
+	int ret;
+	int err;
+	loff_t pos = *ppos;
+
+	ret = mutex_lock_interruptible(&pch_phub_read_mutex);
+	if (ret) {
+		err = -ERESTARTSYS;
+		goto return_err_nomutex;
+	}
+
+	/* Get Rom signature */
+	pch_phub_read_serial_rom(0x80, (unsigned char *)&rom_signature);
+	rom_signature &= 0xff;
+	pch_phub_read_serial_rom(0x81, (unsigned char *)&tmp);
+	rom_signature |= (tmp & 0xff) << 8;
+	if (rom_signature == 0xAA55) {
+		pch_phub_read_serial_rom(0x82, &rom_length);
+		orom_size = rom_length * 512;
+		if (orom_size < pos) {
+			addr_offset = 0;
+			goto return_ok;
+		}
+
+		for (addr_offset = 0; addr_offset < size; addr_offset++) {
+			pch_phub_read_serial_rom(0x80 + addr_offset + pos,
+									 &data);
+			ret_value = copy_to_user(&buf[addr_offset], &data, 1);
+			if (ret_value) {
+				err = -EFAULT;
+				goto return_err;
+			}
+
+			if (orom_size < pos + addr_offset) {
+				*ppos += addr_offset;
+				goto return_ok;
+			}
+
+		}
+	} else {
+		return -ENODATA;
+	}
+	*ppos += addr_offset;
+return_ok:
+	mutex_unlock(&pch_phub_read_mutex);
+	return addr_offset;
+
+return_err:
+	mutex_unlock(&pch_phub_read_mutex);
+return_err_nomutex:
+	return err;
+}
+
+
+static ssize_t pch_phub_write(struct file *file, const char __user *buf,
+			      size_t size, loff_t *ppos)
+{
+	unsigned int data;
+	int ret_value1;
+	int ret_value2;
+	int err;
+	unsigned int addr_offset;
+	loff_t pos = *ppos;
+	int ret;
+
+	ret = mutex_lock_interruptible(&pch_phub_write_mutex);
+	if (ret) {
+		err = -ERESTARTSYS;
+		goto return_err_nomutex;
+	}
+
+	for (addr_offset = 0; addr_offset < size; addr_offset++) {
+		ret_value1 = get_user(data, &buf[addr_offset]);
+		if (ret_value1) {
+			err = -EFAULT;
+			goto return_err;
+		}
+
+		ret_value2 = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
+							data);
+		if (ret_value2) {
+			err = ret_value2;
+			goto return_err;
+		}
+
+		if (PCH_PHUB_OROM_SIZE < pos + addr_offset) {
+			*ppos += addr_offset;
+			goto return_ok;
+		}
+
+	}
+
+	*ppos += addr_offset;
+
+return_ok:
+	mutex_unlock(&pch_phub_write_mutex);
+	return addr_offset;
+
+return_err:
+	mutex_unlock(&pch_phub_write_mutex);
+return_err_nomutex:
+	return err;
+}
+
+
+static long pch_phub_ioctl(struct file *file, unsigned int cmd,
+			   unsigned long arg)
+{
+	int ret_value;
+	__u8 mac_addr[6];
+	int ret;
+	unsigned int i;
+	void __user *varg = (void __user *)arg;
+
+	ret = mutex_lock_interruptible(&pch_phub_ioctl_mutex);
+	if (ret) {
+		ret_value = -ERESTARTSYS;
+		goto return_nomutex;
+	}
+
+	if (pch_phub_reg.pch_phub_suspended == true) {
+		ret_value = -EBUSY;
+		goto return_ioctrl;
+	}
+
+	switch (cmd) {
+	case IOCTL_PHUB_READ_MAC_ADDR:
+		for (i = 0; i < 6; i++)
+			pch_phub_read_gbe_mac_addr(i, &mac_addr[i]);
+
+		ret_value = copy_to_user(varg,
+					 mac_addr, sizeof(mac_addr));
+		if (ret_value)
+			ret_value = -EFAULT;
+
+		break;
+
+	case IOCTL_PHUB_WRITE_MAC_ADDR:
+		ret_value = copy_from_user(mac_addr, varg, sizeof(mac_addr));
+
+		if (ret_value)
+			ret_value = -EFAULT;
+
+		for (i = 0; i < 6; i++)
+			pch_phub_write_gbe_mac_addr(i, mac_addr[i]);
+		break;
+
+	default:
+		ret_value = -EINVAL;
+		break;
+	}
+return_ioctrl:
+	mutex_unlock(&pch_phub_ioctl_mutex);
+return_nomutex:
+	return ret_value;
+}
+
+
+/* file_operations structure initialization */
+static const struct file_operations pch_phub_fops = {
+	.owner = THIS_MODULE,
+	.read = pch_phub_read,
+	.write = pch_phub_write,
+	.unlocked_ioctl = pch_phub_ioctl,
+	.llseek = default_llseek
+};
+
+
+static int __devinit pch_phub_probe(struct pci_dev *pdev,
+				    const struct pci_device_id *id)
+{
+	int ret;
+	unsigned int rom_size;
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+				"\npch_phub_probe : pci_enable_device FAILED");
+		goto err_pci_enable_dev;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : "
+			"pci_enable_device returns %d\n", ret);
+
+	ret = pci_request_regions(pdev, MODULE_NAME);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+				"pch_phub_probe : pci_request_regions FAILED");
+		goto err_req_regions;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : "
+		"pci_request_regions returns %d\n", ret);
+
+	pch_phub_reg.pch_phub_base_address = pci_iomap(pdev, 1, 0);
+
+	if (pch_phub_reg.pch_phub_base_address == 0) {
+		dev_dbg(&pdev->dev, "pch_phub_probe : pci_iomap FAILED");
+		ret = -ENOMEM;
+		goto err_pci_iomap;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : pci_iomap SUCCESS and value "
+		"in pch_phub_base_address variable is 0x%08x\n",
+		(unsigned int)pch_phub_reg.pch_phub_base_address);
+
+	pch_phub_reg.pch_phub_extrom_base_address =
+						pci_map_rom(pdev, &rom_size);
+	if (pch_phub_reg.pch_phub_extrom_base_address == 0) {
+		dev_dbg(&pdev->dev, "pch_phub_probe : pci_map_rom FAILED");
+		ret = -ENOMEM;
+		goto err_pci_map;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : "
+		"pci_map_rom SUCCESS and value in "
+		"pch_phub_extrom_base_address variable is 0x%08x\n",
+		(unsigned int)pch_phub_reg.pch_phub_extrom_base_address);
+
+	ret = alloc_chrdev_region(&pch_phub_dev_no, 0,
+					PCH_MINOR_NOS, MODULE_NAME);
+	if (ret) {
+		dev_dbg(&pdev->dev, "pch_phub_probe : "
+				"alloc_chrdev_region FAILED");
+
+		goto err_alloc_cdev;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : "
+		"alloc_chrdev_region returns %d\n", ret);
+
+	cdev_init(&pch_phub_dev, &pch_phub_fops);
+	dev_dbg(&pdev->dev,
+			"pch_phub_probe :  cdev_init invoked successfully\n");
+
+	pch_phub_dev.owner = THIS_MODULE;
+	pch_phub_dev.ops = &pch_phub_fops;
+
+	ret = cdev_add(&pch_phub_dev, pch_phub_dev_no, PCH_MINOR_NOS);
+	if (ret) {
+		dev_dbg(&pdev->dev, "pch_phub_probe :  cdev_add FAILED");
+		goto err_cdev_add;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe :  cdev_add returns %d\n", ret);
+
+	/* set the clock config reg if CAN clock is 50Mhz */
+	dev_dbg(&pdev->dev, "pch_phub_probe : invoking "
+		"pch_phub_read_modify_write_reg "
+		"to set CLKCFG reg for CAN clk 50Mhz\n");
+	pch_phub_read_modify_write_reg((unsigned int)CLKCFG_REG_OFFSET,
+					  CLKCFG_CAN_50MHZ, CLKCFG_CANCLK_MASK);
+
+	/* set the prefech value */
+	iowrite32(0x000ffffa, pch_phub_reg.pch_phub_base_address + 0x14);
+	/* set the interrupt delay value */
+	iowrite32(0x25, pch_phub_reg.pch_phub_base_address + 0x44);
+	return 0;
+
+err_cdev_add:
+	unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
+err_alloc_cdev:
+	pci_unmap_rom(pdev, pch_phub_reg.pch_phub_extrom_base_address);
+err_pci_map:
+	pci_iounmap(pdev, pch_phub_reg.pch_phub_base_address);
+err_pci_iomap:
+	pci_release_regions(pdev);
+err_req_regions:
+	pci_disable_device(pdev);
+err_pci_enable_dev:
+	dev_dbg(&pdev->dev, "pch_phub_probe returns %d\n", ret);
+	return ret;
+}
+
+static void __devexit pch_phub_remove(struct pci_dev *pdev)
+{
+
+	cdev_del(&pch_phub_dev);
+	dev_dbg(&pdev->dev,
+			"pch_phub_remove - cdev_del Invoked successfully\n");
+
+	unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+		"unregister_chrdev_region Invoked successfully\n");
+
+	pci_unmap_rom(pdev, pch_phub_reg.pch_phub_extrom_base_address);
+
+	pci_iounmap(pdev, pch_phub_reg.pch_phub_base_address);
+
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+			"pci_iounmap Invoked successfully\n");
+
+	pci_release_regions(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+		"pci_release_regions Invoked successfully\n");
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+		"pci_disable_device Invoked successfully\n");
+
+}
+
+#ifdef CONFIG_PM
+
+static int pch_phub_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	int ret;
+
+	pch_phub_reg.pch_phub_suspended = true;/* For blocking further IOCTLs */
+
+	pch_phub_save_reg_conf(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+		"pch_phub_save_reg_conf Invoked successfully\n");
+
+	ret = pci_save_state(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+			" pch_phub_suspend -pci_save_state returns-%d\n", ret);
+		return ret;
+	}
+	dev_dbg(&pdev->dev,
+			"pch_phub_suspend - pci_save_state returns %d\n", ret);
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+			"pci_enable_wake Invoked successfully\n");
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+			"pci_disable_device Invoked successfully\n");
+
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+			"pci_set_power_state Invoked successfully   "
+			"return = %d\n", 0);
+
+	return 0;
+}
+
+static int pch_phub_resume(struct pci_dev *pdev)
+{
+
+	int ret;
+
+	pci_set_power_state(pdev, PCI_D0);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+		"pci_set_power_state Invoked successfully\n");
+
+	pci_restore_state(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+		"pci_restore_state Invoked successfully\n");
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+				"pch_phub_resume-pci_enable_device failed ");
+		return ret;
+	}
+
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+			"pci_enable_device returns -%d\n", ret);
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+			"pci_enable_wake Invoked successfully\n");
+
+	pch_phub_restore_reg_conf(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+		"pch_phub_restore_reg_conf Invoked successfully\n");
+
+	pch_phub_reg.pch_phub_suspended = false;
+
+	dev_dbg(&pdev->dev, "pch_phub_resume  returns- %d\n", 0);
+	return 0;
+}
+#else
+#define pch_phub_suspend NULL
+#define pch_phub_resume NULL
+#endif /* CONFIG_PM */
+
+static struct pci_device_id pch_phub_pcidev_id[] = {
+
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH1_PHUB)},
+	{0,}
+};
+
+
+static struct pci_driver pch_phub_driver = {
+	.name = "pch_phub",
+	.id_table = pch_phub_pcidev_id,
+	.probe = pch_phub_probe,
+	.remove = __devexit_p(pch_phub_remove),
+	.suspend = pch_phub_suspend,
+	.resume = pch_phub_resume
+};
+
+/* pch_phub_pci_init - Implements the initialization functionality of
+ * the module.
+ */
+static int __init pch_phub_pci_init(void)
+{
+	int ret;
+	ret = pci_register_driver(&pch_phub_driver);
+
+	return ret;
+}
+
+/* pch_phub_pci_exit - Implements the exit functionality of the module. */
+static void __exit pch_phub_pci_exit(void)
+{
+	pci_unregister_driver(&pch_phub_driver);
+
+}
+
+module_init(pch_phub_pci_init);
+module_exit(pch_phub_pci_exit);
+
+MODULE_DESCRIPTION("PCH PACKET HUB PCI Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/char/pch_phub/pch_phub.h b/drivers/char/pch_phub/pch_phub.h
new file mode 100755
index 0000000..7baa272
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.h
@@ -0,0 +1,48 @@
+#ifndef __PCH_PHUB_H__
+#define __PCH_PHUB_H__
+/*!
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#define PHUB_IOCTL_MAGIC		(0xf7)
+
+/* Read GbE MAC address */
+#define IOCTL_PHUB_READ_MAC_ADDR (_IOR(PHUB_IOCTL_MAGIC, 6, __u8[6]))
+
+/* Write GbE MAC address */
+#define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 7, __u8[6]))
+
+/* SROM ACCESS Macro */
+#define PCH_WORD_ADDR_MASK (~((1 << 2) - 1))
+
+/* Registers address offset */
+#define PCH_PHUB_ID_REG				0x0000
+#define PCH_PHUB_QUEUE_PRI_VAL_REG		0x0004
+#define PCH_PHUB_RC_QUEUE_MAXSIZE_REG		0x0008
+#define PCH_PHUB_BRI_QUEUE_MAXSIZE_REG		0x000C
+#define PCH_PHUB_COMP_RESP_TIMEOUT_REG		0x0010
+#define PCH_PHUB_BUS_SLAVE_CONTROL_REG		0x0014
+#define PCH_PHUB_DEADLOCK_AVOID_TYPE_REG	0x0018
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG0	0x0020
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG1	0x0024
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG2	0x0028
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG3	0x002C
+#define PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE	0x0040
+#define CLKCFG_REG_OFFSET			0x500
+
+#define PCH_PHUB_OROM_SIZE 15360
+
+#endif
-- 
1.6.0.6

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

* Re: [PATCH] Packet hub driver of Topcliff PCH
  2010-07-01  5:14 ` Masayuki Ohtak
@ 2010-07-01  6:58   ` Andy Isaacson
  2010-07-01 10:13     ` Masayuki Ohtake
  0 siblings, 1 reply; 46+ messages in thread
From: Andy Isaacson @ 2010-07-01  6:58 UTC (permalink / raw)
  To: Masayuki Ohtak
  Cc: Randy Dunlap, Arnd Bergmann, Wang, Yong Y, qi.wang, joel.clark,
	andrew.chih.howe.khor, Alan Cox, LKML

On Thu, Jul 01, 2010 at 02:14:11PM +0900, Masayuki Ohtak wrote:
> Hi Andy and Randy
> 
> I have modified for your comments.
> Please confirm below.

Your style is looking better, and the additional documentation is much
appreciated.

I still have concerns about the userland interface design.  It still
seems to me that the MAC interface should be used by something in
drivers/net and there's no reason to expose the SROM in /dev unless it
contains more than just the MAC address.


A few more style issues I saw on a quick scan through -- this was not a
comprehensive review:


> +static long pch_phub_ioctl(struct file *file, unsigned int cmd,
> +			   unsigned long arg)
[snip]
> +		ret_value = copy_to_user(varg,
> +					 mac_addr, sizeof(mac_addr));

Reformat this to fit on one line:
+		ret_value = copy_to_user(varg, mac_addr, sizeof(mac_addr));


[snip]
> +	case IOCTL_PHUB_WRITE_MAC_ADDR:
> +		ret_value = copy_from_user(mac_addr, varg, sizeof(mac_addr));
> +
> +		if (ret_value)

Here we need to break:

+               {
> +			ret_value = -EFAULT;
+                       break;
+               }

because if copy_from_user failed ...

> +		for (i = 0; i < 6; i++)
> +			pch_phub_write_gbe_mac_addr(i, mac_addr[i]);

... we would pass garbage to pch_phub_write_gbe_mac_addr.

[snip]
> +		dev_dbg(&pdev->dev,
> +				"\npch_phub_probe : pci_enable_device FAILED");

Prefix \n is not correct.  This should be

+		dev_dbg(&pdev->dev, "pch_phub_probe: pci_enable_device FAILED");

In general dev_dbg format strings should fit on one 80-char line.  If
your format strings are longer than that, it's a clue you're doing
something wrong.  For example:

> +	dev_dbg(&pdev->dev, "pch_phub_probe : "
> +			"pci_enable_device returns %d\n", ret);

+	dev_dbg(&pdev->dev, "pch_phub_probe: pci_enable_device returns %d\n",
+		 ret);

> +	ret = pci_request_regions(pdev, MODULE_NAME);
> +	if (ret) {
> +		dev_dbg(&pdev->dev,
> +				"pch_phub_probe : pci_request_regions FAILED");

If you have a dev_dbg, please print ret.  It may give an important clue.

[snip a bunch more I don't have time to review right now]

Thanks,
-andy

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

* Re: [PATCH] Packet hub driver of Topcliff PCH
  2010-07-01  6:58   ` Andy Isaacson
@ 2010-07-01 10:13     ` Masayuki Ohtake
  0 siblings, 0 replies; 46+ messages in thread
From: Masayuki Ohtake @ 2010-07-01 10:13 UTC (permalink / raw)
  To: Andy Isaacson
  Cc: LKML, Alan Cox, andrew.chih.howe.khor, joel.clark, qi.wang, Wang,
	Yong Y, Arnd Bergmann, Randy Dunlap

Hi Andy,

> I still have concerns about the userland interface design.  It still
> seems to me that the MAC interface should be used by something in
> drivers/net and there's no reason to expose the SROM in /dev unless it
> contains more than just the MAC address.
SROM has not only MAC address but also Option ROM data.
Thus, we think SROM should be exposed.

> A few more style issues I saw on a quick scan through -- this was not a
> comprehensive review:
We are now modifying.

Thanks, Ohtake

----- Original Message ----- 
From: "Andy Isaacson" <adi@hexapodia.org>
To: "Masayuki Ohtak" <masa-korg@dsn.okisemi.com>
Cc: "Randy Dunlap" <randy.dunlap@oracle.com>; "Arnd Bergmann" <arnd@arndb.de>; "Wang, Yong Y" <yong.y.wang@intel.com>;
<qi.wang@intel.com>; <joel.clark@intel.com>; <andrew.chih.howe.khor@intel.com>; "Alan Cox" <alan@lxorguk.ukuu.org.uk>;
"LKML" <linux-kernel@vger.kernel.org>
Sent: Thursday, July 01, 2010 3:58 PM
Subject: Re: [PATCH] Packet hub driver of Topcliff PCH


> On Thu, Jul 01, 2010 at 02:14:11PM +0900, Masayuki Ohtak wrote:
> > Hi Andy and Randy
> >
> > I have modified for your comments.
> > Please confirm below.
>
> Your style is looking better, and the additional documentation is much
> appreciated.
>
> I still have concerns about the userland interface design.  It still
> seems to me that the MAC interface should be used by something in
> drivers/net and there's no reason to expose the SROM in /dev unless it
> contains more than just the MAC address.
>
>
> A few more style issues I saw on a quick scan through -- this was not a
> comprehensive review:
>
>
> > +static long pch_phub_ioctl(struct file *file, unsigned int cmd,
> > +    unsigned long arg)
> [snip]
> > + ret_value = copy_to_user(varg,
> > + mac_addr, sizeof(mac_addr));
>
> Reformat this to fit on one line:
> + ret_value = copy_to_user(varg, mac_addr, sizeof(mac_addr));
>
>
> [snip]
> > + case IOCTL_PHUB_WRITE_MAC_ADDR:
> > + ret_value = copy_from_user(mac_addr, varg, sizeof(mac_addr));
> > +
> > + if (ret_value)
>
> Here we need to break:
>
> +               {
> > + ret_value = -EFAULT;
> +                       break;
> +               }
>
> because if copy_from_user failed ...
>
> > + for (i = 0; i < 6; i++)
> > + pch_phub_write_gbe_mac_addr(i, mac_addr[i]);
>
> ... we would pass garbage to pch_phub_write_gbe_mac_addr.
>
> [snip]
> > + dev_dbg(&pdev->dev,
> > + "\npch_phub_probe : pci_enable_device FAILED");
>
> Prefix \n is not correct.  This should be
>
> + dev_dbg(&pdev->dev, "pch_phub_probe: pci_enable_device FAILED");
>
> In general dev_dbg format strings should fit on one 80-char line.  If
> your format strings are longer than that, it's a clue you're doing
> something wrong.  For example:
>
> > + dev_dbg(&pdev->dev, "pch_phub_probe : "
> > + "pci_enable_device returns %d\n", ret);
>
> + dev_dbg(&pdev->dev, "pch_phub_probe: pci_enable_device returns %d\n",
> + ret);
>
> > + ret = pci_request_regions(pdev, MODULE_NAME);
> > + if (ret) {
> > + dev_dbg(&pdev->dev,
> > + "pch_phub_probe : pci_request_regions FAILED");
>
> If you have a dev_dbg, please print ret.  It may give an important clue.
>
> [snip a bunch more I don't have time to review right now]
>
> Thanks,
> -andy
>



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

* [PATCH] Packet hub driver of Topcliff PCH
  2010-06-22  5:33 [PATCH] Topcliff PHUB: Generate PacketHub driver Masayuki Ohtak
                   ` (4 preceding siblings ...)
  2010-07-01  5:14 ` Masayuki Ohtak
@ 2010-07-01 10:38 ` Masayuki Ohtak
  2010-07-01 15:44   ` Randy Dunlap
  2010-07-05  7:20 ` Masayuki Ohtak
                   ` (4 subsequent siblings)
  10 siblings, 1 reply; 46+ messages in thread
From: Masayuki Ohtak @ 2010-07-01 10:38 UTC (permalink / raw)
  To: Andy Isaacson
  Cc: Randy Dunlap, Arnd Bergmann, Wang, Yong Y, qi.wang, joel.clark,
	andrew.chih.howe.khor, Alan Cox, LKML

Hi Andy

I have modified for your comments.
Please confirm below.

Thanks, Ohtake.

---
Packet hub driver of Topcliff PCH

Topcliff PCH is the platform controller hub that is going to be used in
Intel's upcoming general embedded platform. All IO peripherals in
Topcliff PCH are actually devices sitting on AMBA bus. Packet hub is
a special converter device in Topcliff PCH that translate AMBA transactions
to PCI Express transactions and vice versa. Thus packet hub helps present
all IO peripherals in Topcliff PCH as PCIE devices to IA system.
Topcliff PCH have MAC address and Option ROM data.
These data are in SROM which is connected to PCIE bus.
Packet hub driver of Topcliff PCH can access MAC address and Option ROM data in
SROM.

Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>

---
 drivers/char/Kconfig             |    9 +
 drivers/char/Makefile            |    2 +
 drivers/char/pch_phub/Makefile   |    3 +
 drivers/char/pch_phub/pch_phub.c |  811 ++++++++++++++++++++++++++++++++++++++
 drivers/char/pch_phub/pch_phub.h |   48 +++
 5 files changed, 873 insertions(+), 0 deletions(-)
 create mode 100644 drivers/char/pch_phub/Makefile
 create mode 100755 drivers/char/pch_phub/pch_phub.c
 create mode 100755 drivers/char/pch_phub/pch_phub.h

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index e023682..9ef3c85 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -4,6 +4,15 @@
 
 menu "Character devices"
 
+config PCH_PHUB
+	tristate "PCH PHUB"
+	depends on PCI
+	help
+	  This driver is for PCH PHUB of Topcliff which is an IOH for x86
+	  embedded processor. The Topcliff has MAC address and Option ROM data
+	  in SROM. This dirver can access MAC address and Option ROM data in
+	  SROM.
+
 config VT
 	bool "Virtual terminal" if EMBEDDED
 	depends on !S390
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index f957edf..1e3eb6c 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -111,6 +111,8 @@ obj-$(CONFIG_PS3_FLASH)		+= ps3flash.o
 obj-$(CONFIG_JS_RTC)		+= js-rtc.o
 js-rtc-y = rtc.o
 
+obj-$(CONFIG_PCH_PHUB)	+= pch_phub/
+
 # Files generated that shall be removed upon make clean
 clean-files := consolemap_deftbl.c defkeymap.c
 
diff --git a/drivers/char/pch_phub/Makefile b/drivers/char/pch_phub/Makefile
new file mode 100644
index 0000000..93aaffe
--- /dev/null
+++ b/drivers/char/pch_phub/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_PCH_PHUB) += pch_phub_drv.o
+
+pch_phub_drv-objs := pch_phub.o
diff --git a/drivers/char/pch_phub/pch_phub.c b/drivers/char/pch_phub/pch_phub.c
new file mode 100755
index 0000000..9a0f8a9
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.c
@@ -0,0 +1,811 @@
+/*!
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include "pch_phub.h"
+
+#define PHUB_STATUS 0x00		/* Status Register offset */
+#define PHUB_CONTROL 0x04		/* Control Register offset */
+#define PHUB_TIMEOUT 0x05		/* Time out value for Status Register */
+#define PCH_PHUB_ROM_WRITE_ENABLE 0x01	/* Enabling for writing ROM */
+#define PCH_PHUB_ROM_WRITE_DISABLE 0x00	/* Disabling for writing ROM */
+#define PCH_PHUB_ROM_START_ADDR 0x14	/* ROM data area start address offset */
+
+/* MAX number of INT_REDUCE_CONTROL registers */
+#define MAX_NUM_INT_REDUCE_CONTROL_REG 128
+#define PCI_DEVICE_ID_PCH1_PHUB 0x8801
+#define PCH_MINOR_NOS 1
+#define CLKCFG_CAN_50MHZ 0x12000000
+#define CLKCFG_CANCLK_MASK 0xFF000000
+#define MODULE_NAME "pch_phub"
+
+/**
+ * struct pch_phub_reg - PHUB register structure
+ * @phub_id_reg:			PHUB_ID register val
+ * @q_pri_val_reg:			QUEUE_PRI_VAL register val
+ * @rc_q_maxsize_reg:			RC_QUEUE_MAXSIZE register val
+ * @bri_q_maxsize_reg:			BRI_QUEUE_MAXSIZE register val
+ * @comp_resp_timeout_reg:		COMP_RESP_TIMEOUT register val
+ * @bus_slave_control_reg:		BUS_SLAVE_CONTROL_REG register val
+ * @deadlock_avoid_type_reg:		DEADLOCK_AVOID_TYPE register val
+ * @intpin_reg_wpermit_reg0:		INTPIN_REG_WPERMIT register 0 val
+ * @intpin_reg_wpermit_reg1:		INTPIN_REG_WPERMIT register 1 val
+ * @intpin_reg_wpermit_reg2:		INTPIN_REG_WPERMIT register 2 val
+ * @intpin_reg_wpermit_reg3:		INTPIN_REG_WPERMIT register 3 val
+ * @int_reduce_control_reg:		INT_REDUCE_CONTROL registers val
+ * @clkcfg_reg:				CLK CFG register val
+ * @pch_phub_base_address:		Register base address
+ * @pch_phub_extrom_base_address:	external rom base address
+ * @pch_phub_suspended:			PHUB status val
+ */
+struct pch_phub_reg {
+	u32 phub_id_reg;
+	u32 q_pri_val_reg;
+	u32 rc_q_maxsize_reg;
+	u32 bri_q_maxsize_reg;
+	u32 comp_resp_timeout_reg;
+	u32 bus_slave_control_reg;
+	u32 deadlock_avoid_type_reg;
+	u32 intpin_reg_wpermit_reg0;
+	u32 intpin_reg_wpermit_reg1;
+	u32 intpin_reg_wpermit_reg2;
+	u32 intpin_reg_wpermit_reg3;
+	u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG];
+	u32 clkcfg_reg;
+	void __iomem *pch_phub_base_address;
+	void __iomem *pch_phub_extrom_base_address;
+	int pch_phub_suspended;
+} pch_phub_reg;
+
+/* SROM SPEC for MAC address assignment offset */
+static const int pch_phub_mac_offset[6] = {0x3, 0x2, 0x1, 0x0, 0xb, 0xa};
+
+static DEFINE_MUTEX(pch_phub_ioctl_mutex);
+static DEFINE_MUTEX(pch_phub_read_mutex);
+static DEFINE_MUTEX(pch_phub_write_mutex);
+static dev_t pch_phub_dev_no;
+static struct cdev pch_phub_dev;
+
+/**
+ * pch_phub_read_modify_write_reg() - Reading modifying and writing register
+ * @reg_addr_offset:	Register offset address value.
+ * @data:		Writing value.
+ * @mask:		Mask value.
+ */
+static void pch_phub_read_modify_write_reg(unsigned int reg_addr_offset,
+					   unsigned int data, unsigned int mask)
+{
+	void __iomem *reg_addr = pch_phub_reg.pch_phub_base_address +
+					 reg_addr_offset;
+	iowrite32(((ioread32(reg_addr) & ~mask)) | data, reg_addr);
+}
+
+
+/* pch_phub_save_reg_conf - saves register configuration */
+static void pch_phub_save_reg_conf(struct pci_dev *pdev)
+{
+	unsigned int i;
+	void __iomem *p = pch_phub_reg.pch_phub_base_address;
+
+	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf ENTRY\n");
+	pch_phub_reg.phub_id_reg = ioread32(p + PCH_PHUB_ID_REG);
+	pch_phub_reg.q_pri_val_reg = ioread32(p + PCH_PHUB_QUEUE_PRI_VAL_REG);
+	pch_phub_reg.rc_q_maxsize_reg =
+	    ioread32(p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+	pch_phub_reg.bri_q_maxsize_reg =
+	    ioread32(p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+	pch_phub_reg.comp_resp_timeout_reg =
+	    ioread32(p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+	pch_phub_reg.bus_slave_control_reg =
+	    ioread32(p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+	pch_phub_reg.deadlock_avoid_type_reg =
+	    ioread32(p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+	pch_phub_reg.intpin_reg_wpermit_reg0 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+	pch_phub_reg.intpin_reg_wpermit_reg1 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+	pch_phub_reg.intpin_reg_wpermit_reg2 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+	pch_phub_reg.intpin_reg_wpermit_reg3 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
+	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+		"pch_phub_reg.phub_id_reg=%x, "
+		"pch_phub_reg.q_pri_val_reg=%x, "
+		"pch_phub_reg.rc_q_maxsize_reg=%x, "
+		"pch_phub_reg.bri_q_maxsize_reg=%x, "
+		"pch_phub_reg.comp_resp_timeout_reg=%x, "
+		"pch_phub_reg.bus_slave_control_reg=%x, "
+		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n",
+		pch_phub_reg.phub_id_reg,
+		pch_phub_reg.q_pri_val_reg,
+		pch_phub_reg.rc_q_maxsize_reg,
+		pch_phub_reg.bri_q_maxsize_reg,
+		pch_phub_reg.comp_resp_timeout_reg,
+		pch_phub_reg.bus_slave_control_reg,
+		pch_phub_reg.deadlock_avoid_type_reg,
+		pch_phub_reg.intpin_reg_wpermit_reg0,
+		pch_phub_reg.intpin_reg_wpermit_reg1,
+		pch_phub_reg.intpin_reg_wpermit_reg2,
+		pch_phub_reg.intpin_reg_wpermit_reg3);
+	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
+		pch_phub_reg.int_reduce_control_reg[i] =
+		    ioread32(p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
+		dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
+			i, pch_phub_reg.int_reduce_control_reg[i]);
+	}
+	pch_phub_reg.clkcfg_reg = ioread32(p + CLKCFG_REG_OFFSET);
+
+}
+
+/* pch_phub_restore_reg_conf - restore register configuration */
+static void pch_phub_restore_reg_conf(struct pci_dev *pdev)
+{
+	unsigned int i;
+	void __iomem *p = pch_phub_reg.pch_phub_base_address;
+
+	dev_dbg(&pdev->dev, "pch_phub_restore_reg_conf ENTRY\n");
+	iowrite32(pch_phub_reg.phub_id_reg, p + PCH_PHUB_ID_REG);
+	iowrite32(pch_phub_reg.q_pri_val_reg, p + PCH_PHUB_QUEUE_PRI_VAL_REG);
+	iowrite32(pch_phub_reg.rc_q_maxsize_reg,
+					p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+	iowrite32(pch_phub_reg.bri_q_maxsize_reg,
+					p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+	iowrite32(pch_phub_reg.comp_resp_timeout_reg,
+					p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+	iowrite32(pch_phub_reg.bus_slave_control_reg,
+					p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+	iowrite32(pch_phub_reg.deadlock_avoid_type_reg,
+					p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg0,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg1,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg2,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg3,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
+	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+		"pch_phub_reg.phub_id_reg=%x, "
+		"pch_phub_reg.q_pri_val_reg=%x, "
+		"pch_phub_reg.rc_q_maxsize_reg=%x, "
+		"pch_phub_reg.bri_q_maxsize_reg=%x, "
+		"pch_phub_reg.comp_resp_timeout_reg=%x, "
+		"pch_phub_reg.bus_slave_control_reg=%x, "
+		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n",
+		pch_phub_reg.phub_id_reg,
+		pch_phub_reg.q_pri_val_reg,
+		pch_phub_reg.rc_q_maxsize_reg,
+		pch_phub_reg.bri_q_maxsize_reg,
+		pch_phub_reg.comp_resp_timeout_reg,
+		pch_phub_reg.bus_slave_control_reg,
+		pch_phub_reg.deadlock_avoid_type_reg,
+		pch_phub_reg.intpin_reg_wpermit_reg0,
+		pch_phub_reg.intpin_reg_wpermit_reg1,
+		pch_phub_reg.intpin_reg_wpermit_reg2,
+		pch_phub_reg.intpin_reg_wpermit_reg3);
+	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
+		iowrite32(pch_phub_reg.int_reduce_control_reg[i],
+			p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
+		dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
+			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
+			i, pch_phub_reg.int_reduce_control_reg[i]);
+	}
+
+	iowrite32(pch_phub_reg.clkcfg_reg, p + CLKCFG_REG_OFFSET);
+}
+
+/**
+ * pch_phub_read_serial_rom() - Reading Serial ROM
+ * @offset_address:	Serial ROM offset address to read.
+ * @data:		Read buffer for specified Serial ROM value.
+ */
+static void pch_phub_read_serial_rom(unsigned int offset_address, u8 *data)
+{
+	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +
+					 offset_address;
+
+	*data = ioread8(mem_addr);
+}
+
+/**
+ * pch_phub_write_serial_rom() - Writing Serial ROM
+ * @offset_address:	Serial ROM offset address.
+ * @data:		Serial ROM value to write.
+ */
+static int pch_phub_write_serial_rom(unsigned int offset_address, u8 data)
+{
+	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +
+					(offset_address & PCH_WORD_ADDR_MASK);
+	int i;
+	unsigned int word_data;
+	unsigned int pos;
+	unsigned int mask;
+	pos = (offset_address % 4) * 8;
+	mask = ~(0xFF << pos);
+
+	iowrite32(PCH_PHUB_ROM_WRITE_ENABLE,
+			pch_phub_reg.pch_phub_extrom_base_address +
+			PHUB_CONTROL);
+
+	word_data = ioread32(mem_addr);
+	iowrite32((word_data & mask) | (u32)data << pos, mem_addr);
+
+	i = 0;
+	while (0x00 != ioread8(pch_phub_reg.pch_phub_extrom_base_address +
+			PHUB_STATUS)) {
+		msleep(1);
+		if (PHUB_TIMEOUT == i)
+			return -EPERM;
+		i++;
+	}
+
+	iowrite32(PCH_PHUB_ROM_WRITE_DISABLE,
+			pch_phub_reg.pch_phub_extrom_base_address +
+			PHUB_CONTROL);
+
+	return 0;
+}
+
+/**
+ * pch_phub_read_serial_rom_val() - Read Serial ROM value
+ * @offset_address:	Serial ROM address offset value.
+ * @data:		Serial ROM value to read.
+ */
+static void pch_phub_read_serial_rom_val(unsigned int offset_address, u8 *data)
+{
+	unsigned int mem_addr;
+
+	mem_addr = PCH_PHUB_ROM_START_ADDR +
+			pch_phub_mac_offset[offset_address];
+
+	pch_phub_read_serial_rom(mem_addr, data);
+
+}
+
+
+/**
+ * pch_phub_write_serial_rom_val() - writing Serial ROM value
+ * @offset_address:	Serial ROM address offset value.
+ * @data:		Serial ROM value.
+ */
+static int pch_phub_write_serial_rom_val(unsigned int offset_address, u8 data)
+{
+	int retval;
+	unsigned int mem_addr;
+
+	mem_addr = PCH_PHUB_ROM_START_ADDR +
+			pch_phub_mac_offset[offset_address];
+
+	retval = pch_phub_write_serial_rom(mem_addr, data);
+
+	return retval;
+}
+
+/* pch_phub_gbe_serial_rom_conf - makes Serial ROM header format configuration
+ * for Gigabit Ethernet MAC address
+ */
+static int pch_phub_gbe_serial_rom_conf(void)
+{
+	int retval;
+
+	retval = pch_phub_write_serial_rom(0x0b, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x0a, 0x10);
+	retval |= pch_phub_write_serial_rom(0x09, 0x01);
+	retval |= pch_phub_write_serial_rom(0x08, 0x02);
+
+	retval |= pch_phub_write_serial_rom(0x0f, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0e, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0d, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0c, 0x80);
+
+	retval |= pch_phub_write_serial_rom(0x13, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x12, 0x10);
+	retval |= pch_phub_write_serial_rom(0x11, 0x01);
+	retval |= pch_phub_write_serial_rom(0x10, 0x18);
+
+	retval |= pch_phub_write_serial_rom(0x1b, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x1a, 0x10);
+	retval |= pch_phub_write_serial_rom(0x19, 0x01);
+	retval |= pch_phub_write_serial_rom(0x18, 0x19);
+
+	retval |= pch_phub_write_serial_rom(0x23, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x22, 0x10);
+	retval |= pch_phub_write_serial_rom(0x21, 0x01);
+	retval |= pch_phub_write_serial_rom(0x20, 0x3a);
+
+	retval |= pch_phub_write_serial_rom(0x27, 0x01);
+	retval |= pch_phub_write_serial_rom(0x26, 0x00);
+	retval |= pch_phub_write_serial_rom(0x25, 0x00);
+	retval |= pch_phub_write_serial_rom(0x24, 0x00);
+
+	return retval;
+}
+
+/**
+ * pch_phub_read_gbe_mac_addr() - Read Gigabit Ethernet MAC address
+ * @offset_address:	Gigabit Ethernet MAC address offset value.
+ * @data:		Buffer of the Gigabit Ethernet MAC address value.
+ */
+static void pch_phub_read_gbe_mac_addr(u8 *data)
+{
+	int i;
+	for (i = 0; i < 6; i++)
+		pch_phub_read_serial_rom_val(i, &data[i]);
+}
+
+/**
+ * pch_phub_write_gbe_mac_addr() - Write MAC address
+ * @offset_address:	Gigabit Ethernet MAC address offset value.
+ * @data:		Gigabit Ethernet MAC address value.
+ */
+static int pch_phub_write_gbe_mac_addr(u8 *data)
+{
+	int retval;
+	int i;
+
+	retval = pch_phub_gbe_serial_rom_conf();
+	if (retval)
+		return retval;
+
+	for (i = 0; i < 6; i++) {
+		retval = pch_phub_write_serial_rom_val(i, data[i]);
+		if (retval)
+			return retval;
+	}
+
+	return retval;
+}
+
+static ssize_t pch_phub_read(struct file *file, char __user *buf, size_t size,
+			     loff_t *ppos)
+{
+	unsigned int rom_signature;
+	unsigned char rom_length;
+	int ret_value;
+	unsigned int tmp;
+	unsigned char data;
+	unsigned int addr_offset;
+	unsigned int orom_size;
+	int ret;
+	int err;
+	loff_t pos = *ppos;
+
+	ret = mutex_lock_interruptible(&pch_phub_read_mutex);
+	if (ret) {
+		err = -ERESTARTSYS;
+		goto return_err_nomutex;
+	}
+
+	/* Get Rom signature */
+	pch_phub_read_serial_rom(0x80, (unsigned char *)&rom_signature);
+	rom_signature &= 0xff;
+	pch_phub_read_serial_rom(0x81, (unsigned char *)&tmp);
+	rom_signature |= (tmp & 0xff) << 8;
+	if (rom_signature == 0xAA55) {
+		pch_phub_read_serial_rom(0x82, &rom_length);
+		orom_size = rom_length * 512;
+		if (orom_size < pos) {
+			addr_offset = 0;
+			goto return_ok;
+		}
+
+		for (addr_offset = 0; addr_offset < size; addr_offset++) {
+			pch_phub_read_serial_rom(0x80 + addr_offset + pos,
+									 &data);
+			ret_value = copy_to_user(&buf[addr_offset], &data, 1);
+			if (ret_value) {
+				err = -EFAULT;
+				goto return_err;
+			}
+
+			if (orom_size < pos + addr_offset) {
+				*ppos += addr_offset;
+				goto return_ok;
+			}
+
+		}
+	} else {
+		return -ENODATA;
+	}
+	*ppos += addr_offset;
+return_ok:
+	mutex_unlock(&pch_phub_read_mutex);
+	return addr_offset;
+
+return_err:
+	mutex_unlock(&pch_phub_read_mutex);
+return_err_nomutex:
+	return err;
+}
+
+
+static ssize_t pch_phub_write(struct file *file, const char __user *buf,
+			      size_t size, loff_t *ppos)
+{
+	unsigned int data;
+	int ret_value1;
+	int ret_value2;
+	int err;
+	unsigned int addr_offset;
+	loff_t pos = *ppos;
+	int ret;
+
+	ret = mutex_lock_interruptible(&pch_phub_write_mutex);
+	if (ret) {
+		err = -ERESTARTSYS;
+		goto return_err_nomutex;
+	}
+
+	for (addr_offset = 0; addr_offset < size; addr_offset++) {
+		ret_value1 = get_user(data, &buf[addr_offset]);
+		if (ret_value1) {
+			err = -EFAULT;
+			goto return_err;
+		}
+
+		ret_value2 = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
+							data);
+		if (ret_value2) {
+			err = ret_value2;
+			goto return_err;
+		}
+
+		if (PCH_PHUB_OROM_SIZE < pos + addr_offset) {
+			*ppos += addr_offset;
+			goto return_ok;
+		}
+
+	}
+
+	*ppos += addr_offset;
+
+return_ok:
+	mutex_unlock(&pch_phub_write_mutex);
+	return addr_offset;
+
+return_err:
+	mutex_unlock(&pch_phub_write_mutex);
+return_err_nomutex:
+	return err;
+}
+
+
+static long pch_phub_ioctl(struct file *file, unsigned int cmd,
+			   unsigned long arg)
+{
+	int ret_value;
+	int ret;
+	int rtn;
+	__u8 mac_addr[6];
+	void __user *varg = (void __user *)arg;
+
+	ret = mutex_lock_interruptible(&pch_phub_ioctl_mutex);
+	if (ret) {
+		ret_value = -ERESTARTSYS;
+		goto return_nomutex;
+	}
+
+	if (pch_phub_reg.pch_phub_suspended == true) {
+		ret_value = -EBUSY;
+		goto return_ioctrl;
+	}
+
+	switch (cmd) {
+	case IOCTL_PHUB_READ_MAC_ADDR:
+		pch_phub_read_gbe_mac_addr(mac_addr);
+
+		ret_value = copy_to_user(varg, mac_addr, sizeof(mac_addr));
+		break;
+
+	case IOCTL_PHUB_WRITE_MAC_ADDR:
+		rtn = copy_from_user(mac_addr, varg, sizeof(mac_addr));
+
+		if (rtn) {
+			ret_value = -EFAULT;
+			break;
+		}
+
+		ret_value = pch_phub_write_gbe_mac_addr(mac_addr);
+		break;
+
+	default:
+		ret_value = -EINVAL;
+		break;
+	}
+return_ioctrl:
+	mutex_unlock(&pch_phub_ioctl_mutex);
+return_nomutex:
+	return ret_value;
+}
+
+
+/* file_operations structure initialization */
+static const struct file_operations pch_phub_fops = {
+	.owner = THIS_MODULE,
+	.read = pch_phub_read,
+	.write = pch_phub_write,
+	.unlocked_ioctl = pch_phub_ioctl,
+	.llseek = default_llseek
+};
+
+
+static int __devinit pch_phub_probe(struct pci_dev *pdev,
+				    const struct pci_device_id *id)
+{
+	int ret;
+	unsigned int rom_size;
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+		"pch_phub_probe : pci_enable_device FAILED(ret=%d)", ret);
+		goto err_pci_enable_dev;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : pci_enable_device returns %d\n",
+		ret);
+
+	ret = pci_request_regions(pdev, MODULE_NAME);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+		"pch_phub_probe : pci_request_regions FAILED(ret=%d)", ret);
+		goto err_req_regions;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : "
+		"pci_request_regions returns %d\n", ret);
+
+	pch_phub_reg.pch_phub_base_address = pci_iomap(pdev, 1, 0);
+
+	if (pch_phub_reg.pch_phub_base_address == 0) {
+		dev_dbg(&pdev->dev, "pch_phub_probe : pci_iomap FAILED");
+		ret = -ENOMEM;
+		goto err_pci_iomap;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : pci_iomap SUCCESS and value "
+		"in pch_phub_base_address variable is 0x%08x\n",
+		(unsigned int)pch_phub_reg.pch_phub_base_address);
+
+	pch_phub_reg.pch_phub_extrom_base_address =
+						pci_map_rom(pdev, &rom_size);
+	if (pch_phub_reg.pch_phub_extrom_base_address == 0) {
+		dev_dbg(&pdev->dev, "pch_phub_probe : pci_map_rom FAILED");
+		ret = -ENOMEM;
+		goto err_pci_map;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : "
+		"pci_map_rom SUCCESS and value in "
+		"pch_phub_extrom_base_address variable is 0x%08x\n",
+		(unsigned int)pch_phub_reg.pch_phub_extrom_base_address);
+
+	ret = alloc_chrdev_region(&pch_phub_dev_no, 0,
+					PCH_MINOR_NOS, MODULE_NAME);
+	if (ret) {
+		dev_dbg(&pdev->dev, "pch_phub_probe : "
+				"alloc_chrdev_region FAILED(ret=%d)", ret);
+
+		goto err_alloc_cdev;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe : "
+		"alloc_chrdev_region returns %d\n", ret);
+
+	cdev_init(&pch_phub_dev, &pch_phub_fops);
+	dev_dbg(&pdev->dev,
+			"pch_phub_probe :  cdev_init invoked successfully\n");
+
+	pch_phub_dev.owner = THIS_MODULE;
+	pch_phub_dev.ops = &pch_phub_fops;
+
+	ret = cdev_add(&pch_phub_dev, pch_phub_dev_no, PCH_MINOR_NOS);
+	if (ret) {
+		dev_dbg(&pdev->dev, "pch_phub_probe :  cdev_add FAILED(ret=%d)",
+			ret);
+		goto err_cdev_add;
+	}
+	dev_dbg(&pdev->dev, "pch_phub_probe :  cdev_add returns %d\n", ret);
+
+	/* set the clock config reg if CAN clock is 50Mhz */
+	dev_dbg(&pdev->dev, "pch_phub_probe : invoking "
+		"pch_phub_read_modify_write_reg "
+		"to set CLKCFG reg for CAN clk 50Mhz\n");
+	pch_phub_read_modify_write_reg((unsigned int)CLKCFG_REG_OFFSET,
+					  CLKCFG_CAN_50MHZ, CLKCFG_CANCLK_MASK);
+
+	/* set the prefech value */
+	iowrite32(0x000ffffa, pch_phub_reg.pch_phub_base_address + 0x14);
+	/* set the interrupt delay value */
+	iowrite32(0x25, pch_phub_reg.pch_phub_base_address + 0x44);
+	return 0;
+
+err_cdev_add:
+	unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
+err_alloc_cdev:
+	pci_unmap_rom(pdev, pch_phub_reg.pch_phub_extrom_base_address);
+err_pci_map:
+	pci_iounmap(pdev, pch_phub_reg.pch_phub_base_address);
+err_pci_iomap:
+	pci_release_regions(pdev);
+err_req_regions:
+	pci_disable_device(pdev);
+err_pci_enable_dev:
+	dev_dbg(&pdev->dev, "pch_phub_probe returns %d\n", ret);
+	return ret;
+}
+
+static void __devexit pch_phub_remove(struct pci_dev *pdev)
+{
+
+	cdev_del(&pch_phub_dev);
+	dev_dbg(&pdev->dev,
+			"pch_phub_remove - cdev_del Invoked successfully\n");
+
+	unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+		"unregister_chrdev_region Invoked successfully\n");
+
+	pci_unmap_rom(pdev, pch_phub_reg.pch_phub_extrom_base_address);
+
+	pci_iounmap(pdev, pch_phub_reg.pch_phub_base_address);
+
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+			"pci_iounmap Invoked successfully\n");
+
+	pci_release_regions(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+		"pci_release_regions Invoked successfully\n");
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_remove - "
+		"pci_disable_device Invoked successfully\n");
+
+}
+
+#ifdef CONFIG_PM
+
+static int pch_phub_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	int ret;
+
+	pch_phub_reg.pch_phub_suspended = true;/* For blocking further IOCTLs */
+
+	pch_phub_save_reg_conf(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+		"pch_phub_save_reg_conf Invoked successfully\n");
+
+	ret = pci_save_state(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+			" pch_phub_suspend -pci_save_state returns-%d\n", ret);
+		return ret;
+	}
+	dev_dbg(&pdev->dev,
+			"pch_phub_suspend - pci_save_state returns %d\n", ret);
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+			"pci_enable_wake Invoked successfully\n");
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+			"pci_disable_device Invoked successfully\n");
+
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+	dev_dbg(&pdev->dev, "pch_phub_suspend - "
+			"pci_set_power_state Invoked successfully   "
+			"return = %d\n", 0);
+
+	return 0;
+}
+
+static int pch_phub_resume(struct pci_dev *pdev)
+{
+
+	int ret;
+
+	pci_set_power_state(pdev, PCI_D0);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+		"pci_set_power_state Invoked successfully\n");
+
+	pci_restore_state(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+		"pci_restore_state Invoked successfully\n");
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+		"pch_phub_resume-pci_enable_device failed(ret=%d) ", ret);
+		return ret;
+	}
+
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+			"pci_enable_device returns -%d\n", ret);
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+			"pci_enable_wake Invoked successfully\n");
+
+	pch_phub_restore_reg_conf(pdev);
+	dev_dbg(&pdev->dev, "pch_phub_resume - "
+		"pch_phub_restore_reg_conf Invoked successfully\n");
+
+	pch_phub_reg.pch_phub_suspended = false;
+
+	dev_dbg(&pdev->dev, "pch_phub_resume  returns- %d\n", 0);
+	return 0;
+}
+#else
+#define pch_phub_suspend NULL
+#define pch_phub_resume NULL
+#endif /* CONFIG_PM */
+
+static struct pci_device_id pch_phub_pcidev_id[] = {
+
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH1_PHUB)},
+	{0,}
+};
+
+
+static struct pci_driver pch_phub_driver = {
+	.name = "pch_phub",
+	.id_table = pch_phub_pcidev_id,
+	.probe = pch_phub_probe,
+	.remove = __devexit_p(pch_phub_remove),
+	.suspend = pch_phub_suspend,
+	.resume = pch_phub_resume
+};
+
+/* pch_phub_pci_init - Implements the initialization functionality of
+ * the module.
+ */
+static int __init pch_phub_pci_init(void)
+{
+	int ret;
+	ret = pci_register_driver(&pch_phub_driver);
+
+	return ret;
+}
+
+/* pch_phub_pci_exit - Implements the exit functionality of the module. */
+static void __exit pch_phub_pci_exit(void)
+{
+	pci_unregister_driver(&pch_phub_driver);
+
+}
+
+module_init(pch_phub_pci_init);
+module_exit(pch_phub_pci_exit);
+
+MODULE_DESCRIPTION("PCH PACKET HUB PCI Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/char/pch_phub/pch_phub.h b/drivers/char/pch_phub/pch_phub.h
new file mode 100755
index 0000000..7baa272
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.h
@@ -0,0 +1,48 @@
+#ifndef __PCH_PHUB_H__
+#define __PCH_PHUB_H__
+/*!
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#define PHUB_IOCTL_MAGIC		(0xf7)
+
+/* Read GbE MAC address */
+#define IOCTL_PHUB_READ_MAC_ADDR (_IOR(PHUB_IOCTL_MAGIC, 6, __u8[6]))
+
+/* Write GbE MAC address */
+#define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 7, __u8[6]))
+
+/* SROM ACCESS Macro */
+#define PCH_WORD_ADDR_MASK (~((1 << 2) - 1))
+
+/* Registers address offset */
+#define PCH_PHUB_ID_REG				0x0000
+#define PCH_PHUB_QUEUE_PRI_VAL_REG		0x0004
+#define PCH_PHUB_RC_QUEUE_MAXSIZE_REG		0x0008
+#define PCH_PHUB_BRI_QUEUE_MAXSIZE_REG		0x000C
+#define PCH_PHUB_COMP_RESP_TIMEOUT_REG		0x0010
+#define PCH_PHUB_BUS_SLAVE_CONTROL_REG		0x0014
+#define PCH_PHUB_DEADLOCK_AVOID_TYPE_REG	0x0018
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG0	0x0020
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG1	0x0024
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG2	0x0028
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG3	0x002C
+#define PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE	0x0040
+#define CLKCFG_REG_OFFSET			0x500
+
+#define PCH_PHUB_OROM_SIZE 15360
+
+#endif
-- 
1.6.0.6

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

* Re: [PATCH] Packet hub driver of Topcliff PCH
  2010-07-01 10:38 ` Masayuki Ohtak
@ 2010-07-01 15:44   ` Randy Dunlap
  0 siblings, 0 replies; 46+ messages in thread
From: Randy Dunlap @ 2010-07-01 15:44 UTC (permalink / raw)
  To: Masayuki Ohtak
  Cc: Andy Isaacson, Arnd Bergmann, Wang, Yong Y, qi.wang, joel.clark,
	andrew.chih.howe.khor, Alan Cox, LKML

On 07/01/10 03:38, Masayuki Ohtak wrote:
> Packet hub driver of Topcliff PCH
> 
> Topcliff PCH is the platform controller hub that is going to be used in
> Intel's upcoming general embedded platform. All IO peripherals in
> Topcliff PCH are actually devices sitting on AMBA bus. Packet hub is
> a special converter device in Topcliff PCH that translate AMBA transactions
> to PCI Express transactions and vice versa. Thus packet hub helps present
> all IO peripherals in Topcliff PCH as PCIE devices to IA system.
> Topcliff PCH have MAC address and Option ROM data.
> These data are in SROM which is connected to PCIE bus.
> Packet hub driver of Topcliff PCH can access MAC address and Option ROM data in
> SROM.
> 
> Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
> 
> ---
>  drivers/char/Kconfig             |    9 +
>  drivers/char/Makefile            |    2 +
>  drivers/char/pch_phub/Makefile   |    3 +
>  drivers/char/pch_phub/pch_phub.c |  811 ++++++++++++++++++++++++++++++++++++++
>  drivers/char/pch_phub/pch_phub.h |   48 +++
>  5 files changed, 873 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/char/pch_phub/Makefile
>  create mode 100755 drivers/char/pch_phub/pch_phub.c
>  create mode 100755 drivers/char/pch_phub/pch_phub.h
> 
> diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
> index e023682..9ef3c85 100644
> --- a/drivers/char/Kconfig
> +++ b/drivers/char/Kconfig
> @@ -4,6 +4,15 @@
>  
>  menu "Character devices"
>  
> +config PCH_PHUB
> +	tristate "PCH PHUB"
> +	depends on PCI
> +	help
> +	  This driver is for PCH PHUB of Topcliff which is an IOH for x86
> +	  embedded processor. The Topcliff has MAC address and Option ROM data
> +	  in SROM. This dirver can access MAC address and Option ROM data in

	                driver

> +	  SROM.
> +
>  config VT
>  	bool "Virtual terminal" if EMBEDDED
>  	depends on !S390

> diff --git a/drivers/char/pch_phub/pch_phub.c b/drivers/char/pch_phub/pch_phub.c
> new file mode 100755
> index 0000000..9a0f8a9
> --- /dev/null
> +++ b/drivers/char/pch_phub/pch_phub.c
> @@ -0,0 +1,811 @@
> +/*!

What is the ! for?

> + * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
> + *
> + * 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; version 2 of the License.
> + *
> + * 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, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/types.h>
> +#include <linux/fs.h>
> +#include <linux/uaccess.h>
> +#include <linux/string.h>
> +#include <linux/pci.h>
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <linux/cdev.h>
> +#include <linux/device.h>
> +#include <linux/mutex.h>
> +#include "pch_phub.h"
> +
> +#define PHUB_STATUS 0x00		/* Status Register offset */
> +#define PHUB_CONTROL 0x04		/* Control Register offset */
> +#define PHUB_TIMEOUT 0x05		/* Time out value for Status Register */
> +#define PCH_PHUB_ROM_WRITE_ENABLE 0x01	/* Enabling for writing ROM */
> +#define PCH_PHUB_ROM_WRITE_DISABLE 0x00	/* Disabling for writing ROM */
> +#define PCH_PHUB_ROM_START_ADDR 0x14	/* ROM data area start address offset */
> +
> +/* MAX number of INT_REDUCE_CONTROL registers */
> +#define MAX_NUM_INT_REDUCE_CONTROL_REG 128
> +#define PCI_DEVICE_ID_PCH1_PHUB 0x8801
> +#define PCH_MINOR_NOS 1
> +#define CLKCFG_CAN_50MHZ 0x12000000
> +#define CLKCFG_CANCLK_MASK 0xFF000000
> +#define MODULE_NAME "pch_phub"
> +
> +/**
> + * struct pch_phub_reg - PHUB register structure
> + * @phub_id_reg:			PHUB_ID register val
> + * @q_pri_val_reg:			QUEUE_PRI_VAL register val
> + * @rc_q_maxsize_reg:			RC_QUEUE_MAXSIZE register val
> + * @bri_q_maxsize_reg:			BRI_QUEUE_MAXSIZE register val
> + * @comp_resp_timeout_reg:		COMP_RESP_TIMEOUT register val
> + * @bus_slave_control_reg:		BUS_SLAVE_CONTROL_REG register val
> + * @deadlock_avoid_type_reg:		DEADLOCK_AVOID_TYPE register val
> + * @intpin_reg_wpermit_reg0:		INTPIN_REG_WPERMIT register 0 val
> + * @intpin_reg_wpermit_reg1:		INTPIN_REG_WPERMIT register 1 val
> + * @intpin_reg_wpermit_reg2:		INTPIN_REG_WPERMIT register 2 val
> + * @intpin_reg_wpermit_reg3:		INTPIN_REG_WPERMIT register 3 val
> + * @int_reduce_control_reg:		INT_REDUCE_CONTROL registers val
> + * @clkcfg_reg:				CLK CFG register val
> + * @pch_phub_base_address:		Register base address
> + * @pch_phub_extrom_base_address:	external rom base address
> + * @pch_phub_suspended:			PHUB status val
> + */
> +struct pch_phub_reg {
> +	u32 phub_id_reg;
> +	u32 q_pri_val_reg;
> +	u32 rc_q_maxsize_reg;
> +	u32 bri_q_maxsize_reg;
> +	u32 comp_resp_timeout_reg;
> +	u32 bus_slave_control_reg;
> +	u32 deadlock_avoid_type_reg;
> +	u32 intpin_reg_wpermit_reg0;
> +	u32 intpin_reg_wpermit_reg1;
> +	u32 intpin_reg_wpermit_reg2;
> +	u32 intpin_reg_wpermit_reg3;
> +	u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG];
> +	u32 clkcfg_reg;
> +	void __iomem *pch_phub_base_address;
> +	void __iomem *pch_phub_extrom_base_address;
> +	int pch_phub_suspended;
> +} pch_phub_reg;
> +
> +/* SROM SPEC for MAC address assignment offset */
> +static const int pch_phub_mac_offset[6] = {0x3, 0x2, 0x1, 0x0, 0xb, 0xa};
> +
> +static DEFINE_MUTEX(pch_phub_ioctl_mutex);
> +static DEFINE_MUTEX(pch_phub_read_mutex);
> +static DEFINE_MUTEX(pch_phub_write_mutex);
> +static dev_t pch_phub_dev_no;
> +static struct cdev pch_phub_dev;
> +
> +/**
> + * pch_phub_read_modify_write_reg() - Reading modifying and writing register
> + * @reg_addr_offset:	Register offset address value.
> + * @data:		Writing value.
> + * @mask:		Mask value.
> + */
> +static void pch_phub_read_modify_write_reg(unsigned int reg_addr_offset,
> +					   unsigned int data, unsigned int mask)
> +{
> +	void __iomem *reg_addr = pch_phub_reg.pch_phub_base_address +
> +					 reg_addr_offset;
> +	iowrite32(((ioread32(reg_addr) & ~mask)) | data, reg_addr);
> +}
> +
> +
> +/* pch_phub_save_reg_conf - saves register configuration */
> +static void pch_phub_save_reg_conf(struct pci_dev *pdev)
> +{
> +	unsigned int i;
> +	void __iomem *p = pch_phub_reg.pch_phub_base_address;
> +
> +	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf ENTRY\n");
> +	pch_phub_reg.phub_id_reg = ioread32(p + PCH_PHUB_ID_REG);
> +	pch_phub_reg.q_pri_val_reg = ioread32(p + PCH_PHUB_QUEUE_PRI_VAL_REG);
> +	pch_phub_reg.rc_q_maxsize_reg =
> +	    ioread32(p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
> +	pch_phub_reg.bri_q_maxsize_reg =
> +	    ioread32(p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
> +	pch_phub_reg.comp_resp_timeout_reg =
> +	    ioread32(p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
> +	pch_phub_reg.bus_slave_control_reg =
> +	    ioread32(p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
> +	pch_phub_reg.deadlock_avoid_type_reg =
> +	    ioread32(p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
> +	pch_phub_reg.intpin_reg_wpermit_reg0 =
> +	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
> +	pch_phub_reg.intpin_reg_wpermit_reg1 =
> +	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
> +	pch_phub_reg.intpin_reg_wpermit_reg2 =
> +	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
> +	pch_phub_reg.intpin_reg_wpermit_reg3 =
> +	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
> +	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
> +		"pch_phub_reg.phub_id_reg=%x, "
> +		"pch_phub_reg.q_pri_val_reg=%x, "
> +		"pch_phub_reg.rc_q_maxsize_reg=%x, "
> +		"pch_phub_reg.bri_q_maxsize_reg=%x, "
> +		"pch_phub_reg.comp_resp_timeout_reg=%x, "
> +		"pch_phub_reg.bus_slave_control_reg=%x, "
> +		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
> +		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
> +		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
> +		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
> +		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n",
> +		pch_phub_reg.phub_id_reg,
> +		pch_phub_reg.q_pri_val_reg,
> +		pch_phub_reg.rc_q_maxsize_reg,
> +		pch_phub_reg.bri_q_maxsize_reg,
> +		pch_phub_reg.comp_resp_timeout_reg,
> +		pch_phub_reg.bus_slave_control_reg,
> +		pch_phub_reg.deadlock_avoid_type_reg,
> +		pch_phub_reg.intpin_reg_wpermit_reg0,
> +		pch_phub_reg.intpin_reg_wpermit_reg1,
> +		pch_phub_reg.intpin_reg_wpermit_reg2,
> +		pch_phub_reg.intpin_reg_wpermit_reg3);
> +	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
> +		pch_phub_reg.int_reduce_control_reg[i] =
> +		    ioread32(p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
> +		dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
> +			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
> +			i, pch_phub_reg.int_reduce_control_reg[i]);
> +	}
> +	pch_phub_reg.clkcfg_reg = ioread32(p + CLKCFG_REG_OFFSET);
> +

drop blank line above.

> +}
> +
> +/* pch_phub_restore_reg_conf - restore register configuration */
> +static void pch_phub_restore_reg_conf(struct pci_dev *pdev)
> +{
> +	unsigned int i;
> +	void __iomem *p = pch_phub_reg.pch_phub_base_address;
> +
> +	dev_dbg(&pdev->dev, "pch_phub_restore_reg_conf ENTRY\n");
> +	iowrite32(pch_phub_reg.phub_id_reg, p + PCH_PHUB_ID_REG);
> +	iowrite32(pch_phub_reg.q_pri_val_reg, p + PCH_PHUB_QUEUE_PRI_VAL_REG);
> +	iowrite32(pch_phub_reg.rc_q_maxsize_reg,
> +					p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
> +	iowrite32(pch_phub_reg.bri_q_maxsize_reg,
> +					p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
> +	iowrite32(pch_phub_reg.comp_resp_timeout_reg,
> +					p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
> +	iowrite32(pch_phub_reg.bus_slave_control_reg,
> +					p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
> +	iowrite32(pch_phub_reg.deadlock_avoid_type_reg,
> +					p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
> +	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg0,
> +					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
> +	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg1,
> +					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
> +	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg2,
> +					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
> +	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg3,
> +					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
> +	dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
> +		"pch_phub_reg.phub_id_reg=%x, "
> +		"pch_phub_reg.q_pri_val_reg=%x, "
> +		"pch_phub_reg.rc_q_maxsize_reg=%x, "
> +		"pch_phub_reg.bri_q_maxsize_reg=%x, "
> +		"pch_phub_reg.comp_resp_timeout_reg=%x, "
> +		"pch_phub_reg.bus_slave_control_reg=%x, "
> +		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
> +		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
> +		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
> +		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
> +		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n",
> +		pch_phub_reg.phub_id_reg,
> +		pch_phub_reg.q_pri_val_reg,
> +		pch_phub_reg.rc_q_maxsize_reg,
> +		pch_phub_reg.bri_q_maxsize_reg,
> +		pch_phub_reg.comp_resp_timeout_reg,
> +		pch_phub_reg.bus_slave_control_reg,
> +		pch_phub_reg.deadlock_avoid_type_reg,
> +		pch_phub_reg.intpin_reg_wpermit_reg0,
> +		pch_phub_reg.intpin_reg_wpermit_reg1,
> +		pch_phub_reg.intpin_reg_wpermit_reg2,
> +		pch_phub_reg.intpin_reg_wpermit_reg3);
> +	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
> +		iowrite32(pch_phub_reg.int_reduce_control_reg[i],
> +			p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
> +		dev_dbg(&pdev->dev, "pch_phub_save_reg_conf : "
> +			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
> +			i, pch_phub_reg.int_reduce_control_reg[i]);
> +	}
> +
> +	iowrite32(pch_phub_reg.clkcfg_reg, p + CLKCFG_REG_OFFSET);
> +}
> +
> +/**
> + * pch_phub_read_serial_rom() - Reading Serial ROM
> + * @offset_address:	Serial ROM offset address to read.
> + * @data:		Read buffer for specified Serial ROM value.
> + */
> +static void pch_phub_read_serial_rom(unsigned int offset_address, u8 *data)
> +{
> +	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +
> +					 offset_address;
> +
> +	*data = ioread8(mem_addr);
> +}
> +
> +/**
> + * pch_phub_write_serial_rom() - Writing Serial ROM
> + * @offset_address:	Serial ROM offset address.
> + * @data:		Serial ROM value to write.
> + */
> +static int pch_phub_write_serial_rom(unsigned int offset_address, u8 data)
> +{
> +	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +
> +					(offset_address & PCH_WORD_ADDR_MASK);
> +	int i;
> +	unsigned int word_data;
> +	unsigned int pos;
> +	unsigned int mask;
> +	pos = (offset_address % 4) * 8;
> +	mask = ~(0xFF << pos);
> +
> +	iowrite32(PCH_PHUB_ROM_WRITE_ENABLE,
> +			pch_phub_reg.pch_phub_extrom_base_address +
> +			PHUB_CONTROL);
> +
> +	word_data = ioread32(mem_addr);
> +	iowrite32((word_data & mask) | (u32)data << pos, mem_addr);
> +
> +	i = 0;
> +	while (0x00 != ioread8(pch_phub_reg.pch_phub_extrom_base_address +
> +			PHUB_STATUS)) {

We prefer to have constant on right side of comparison...

> +		msleep(1);
> +		if (PHUB_TIMEOUT == i)
> +			return -EPERM;
> +		i++;
> +	}
> +
> +	iowrite32(PCH_PHUB_ROM_WRITE_DISABLE,
> +			pch_phub_reg.pch_phub_extrom_base_address +
> +			PHUB_CONTROL);
> +
> +	return 0;
> +}
> +
> +/**
> + * pch_phub_read_serial_rom_val() - Read Serial ROM value
> + * @offset_address:	Serial ROM address offset value.
> + * @data:		Serial ROM value to read.
> + */
> +static void pch_phub_read_serial_rom_val(unsigned int offset_address, u8 *data)
> +{
> +	unsigned int mem_addr;
> +
> +	mem_addr = PCH_PHUB_ROM_START_ADDR +
> +			pch_phub_mac_offset[offset_address];
> +
> +	pch_phub_read_serial_rom(mem_addr, data);
> +

drop blank line above.

> +}
> +
> +
> +/**
> + * pch_phub_write_serial_rom_val() - writing Serial ROM value
> + * @offset_address:	Serial ROM address offset value.
> + * @data:		Serial ROM value.
> + */
> +static int pch_phub_write_serial_rom_val(unsigned int offset_address, u8 data)
> +{
> +	int retval;
> +	unsigned int mem_addr;
> +
> +	mem_addr = PCH_PHUB_ROM_START_ADDR +
> +			pch_phub_mac_offset[offset_address];
> +
> +	retval = pch_phub_write_serial_rom(mem_addr, data);
> +
> +	return retval;
> +}
> +
> +/* pch_phub_gbe_serial_rom_conf - makes Serial ROM header format configuration
> + * for Gigabit Ethernet MAC address
> + */
> +static int pch_phub_gbe_serial_rom_conf(void)
> +{
> +	int retval;
> +
> +	retval = pch_phub_write_serial_rom(0x0b, 0xbc);
> +	retval |= pch_phub_write_serial_rom(0x0a, 0x10);
> +	retval |= pch_phub_write_serial_rom(0x09, 0x01);
> +	retval |= pch_phub_write_serial_rom(0x08, 0x02);
> +
> +	retval |= pch_phub_write_serial_rom(0x0f, 0x00);
> +	retval |= pch_phub_write_serial_rom(0x0e, 0x00);
> +	retval |= pch_phub_write_serial_rom(0x0d, 0x00);
> +	retval |= pch_phub_write_serial_rom(0x0c, 0x80);
> +
> +	retval |= pch_phub_write_serial_rom(0x13, 0xbc);
> +	retval |= pch_phub_write_serial_rom(0x12, 0x10);
> +	retval |= pch_phub_write_serial_rom(0x11, 0x01);
> +	retval |= pch_phub_write_serial_rom(0x10, 0x18);
> +
> +	retval |= pch_phub_write_serial_rom(0x1b, 0xbc);
> +	retval |= pch_phub_write_serial_rom(0x1a, 0x10);
> +	retval |= pch_phub_write_serial_rom(0x19, 0x01);
> +	retval |= pch_phub_write_serial_rom(0x18, 0x19);
> +
> +	retval |= pch_phub_write_serial_rom(0x23, 0xbc);
> +	retval |= pch_phub_write_serial_rom(0x22, 0x10);
> +	retval |= pch_phub_write_serial_rom(0x21, 0x01);
> +	retval |= pch_phub_write_serial_rom(0x20, 0x3a);
> +
> +	retval |= pch_phub_write_serial_rom(0x27, 0x01);
> +	retval |= pch_phub_write_serial_rom(0x26, 0x00);
> +	retval |= pch_phub_write_serial_rom(0x25, 0x00);
> +	retval |= pch_phub_write_serial_rom(0x24, 0x00);
> +
> +	return retval;
> +}
> +
> +/**
> + * pch_phub_read_gbe_mac_addr() - Read Gigabit Ethernet MAC address
> + * @offset_address:	Gigabit Ethernet MAC address offset value.
> + * @data:		Buffer of the Gigabit Ethernet MAC address value.
> + */
> +static void pch_phub_read_gbe_mac_addr(u8 *data)
> +{
> +	int i;
> +	for (i = 0; i < 6; i++)

s/6/ETH_ALEN/
(see below)

> +		pch_phub_read_serial_rom_val(i, &data[i]);
> +}
> +
> +/**
> + * pch_phub_write_gbe_mac_addr() - Write MAC address
> + * @offset_address:	Gigabit Ethernet MAC address offset value.
> + * @data:		Gigabit Ethernet MAC address value.
> + */
> +static int pch_phub_write_gbe_mac_addr(u8 *data)
> +{
> +	int retval;
> +	int i;
> +
> +	retval = pch_phub_gbe_serial_rom_conf();
> +	if (retval)
> +		return retval;
> +
> +	for (i = 0; i < 6; i++) {

s/6/ETH_ALEN/
(see below)

> +		retval = pch_phub_write_serial_rom_val(i, data[i]);
> +		if (retval)
> +			return retval;
> +	}
> +
> +	return retval;
> +}
> +
> +static ssize_t pch_phub_read(struct file *file, char __user *buf, size_t size,
> +			     loff_t *ppos)
> +{
> +	unsigned int rom_signature;
> +	unsigned char rom_length;
> +	int ret_value;
> +	unsigned int tmp;
> +	unsigned char data;
> +	unsigned int addr_offset;
> +	unsigned int orom_size;
> +	int ret;
> +	int err;
> +	loff_t pos = *ppos;
> +
> +	ret = mutex_lock_interruptible(&pch_phub_read_mutex);
> +	if (ret) {
> +		err = -ERESTARTSYS;
> +		goto return_err_nomutex;
> +	}
> +
> +	/* Get Rom signature */
> +	pch_phub_read_serial_rom(0x80, (unsigned char *)&rom_signature);
> +	rom_signature &= 0xff;
> +	pch_phub_read_serial_rom(0x81, (unsigned char *)&tmp);
> +	rom_signature |= (tmp & 0xff) << 8;
> +	if (rom_signature == 0xAA55) {
> +		pch_phub_read_serial_rom(0x82, &rom_length);
> +		orom_size = rom_length * 512;
> +		if (orom_size < pos) {
> +			addr_offset = 0;
> +			goto return_ok;
> +		}
> +
> +		for (addr_offset = 0; addr_offset < size; addr_offset++) {
> +			pch_phub_read_serial_rom(0x80 + addr_offset + pos,
> +									 &data);
> +			ret_value = copy_to_user(&buf[addr_offset], &data, 1);
> +			if (ret_value) {
> +				err = -EFAULT;
> +				goto return_err;
> +			}
> +
> +			if (orom_size < pos + addr_offset) {
> +				*ppos += addr_offset;
> +				goto return_ok;
> +			}
> +
> +		}
> +	} else {
> +		return -ENODATA;

Need to unlock mutex here also??

> +	}
> +	*ppos += addr_offset;
> +return_ok:
> +	mutex_unlock(&pch_phub_read_mutex);
> +	return addr_offset;
> +
> +return_err:
> +	mutex_unlock(&pch_phub_read_mutex);
> +return_err_nomutex:
> +	return err;
> +}
> +
> +
> +static ssize_t pch_phub_write(struct file *file, const char __user *buf,
> +			      size_t size, loff_t *ppos)
> +{
> +	unsigned int data;
> +	int ret_value1;
> +	int ret_value2;
> +	int err;
> +	unsigned int addr_offset;
> +	loff_t pos = *ppos;
> +	int ret;
> +
> +	ret = mutex_lock_interruptible(&pch_phub_write_mutex);
> +	if (ret) {
> +		err = -ERESTARTSYS;
> +		goto return_err_nomutex;
> +	}
> +
> +	for (addr_offset = 0; addr_offset < size; addr_offset++) {
> +		ret_value1 = get_user(data, &buf[addr_offset]);
> +		if (ret_value1) {
> +			err = -EFAULT;
> +			goto return_err;
> +		}
> +
> +		ret_value2 = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
> +							data);
> +		if (ret_value2) {
> +			err = ret_value2;
> +			goto return_err;
> +		}
> +
> +		if (PCH_PHUB_OROM_SIZE < pos + addr_offset) {
> +			*ppos += addr_offset;
> +			goto return_ok;
> +		}
> +
> +	}
> +
> +	*ppos += addr_offset;
> +
> +return_ok:
> +	mutex_unlock(&pch_phub_write_mutex);
> +	return addr_offset;
> +
> +return_err:
> +	mutex_unlock(&pch_phub_write_mutex);
> +return_err_nomutex:
> +	return err;
> +}
> +
> +
> +static long pch_phub_ioctl(struct file *file, unsigned int cmd,
> +			   unsigned long arg)
> +{
> +	int ret_value;
> +	int ret;
> +	int rtn;
> +	__u8 mac_addr[6];

#include <linux/if_ether.h>

	__u8 mac_addr[ETH_ALEN];


> +	void __user *varg = (void __user *)arg;
> +
> +	ret = mutex_lock_interruptible(&pch_phub_ioctl_mutex);
> +	if (ret) {
> +		ret_value = -ERESTARTSYS;
> +		goto return_nomutex;
> +	}
> +
> +	if (pch_phub_reg.pch_phub_suspended == true) {
> +		ret_value = -EBUSY;
> +		goto return_ioctrl;
> +	}
> +
> +	switch (cmd) {
> +	case IOCTL_PHUB_READ_MAC_ADDR:
> +		pch_phub_read_gbe_mac_addr(mac_addr);
> +
> +		ret_value = copy_to_user(varg, mac_addr, sizeof(mac_addr));

s/sizeof(mac_addr)/ETH_ALEN/

> +		break;
> +
> +	case IOCTL_PHUB_WRITE_MAC_ADDR:
> +		rtn = copy_from_user(mac_addr, varg, sizeof(mac_addr));

ditto.

> +
> +		if (rtn) {
> +			ret_value = -EFAULT;
> +			break;
> +		}
> +
> +		ret_value = pch_phub_write_gbe_mac_addr(mac_addr);
> +		break;
> +
> +	default:
> +		ret_value = -EINVAL;
> +		break;
> +	}
> +return_ioctrl:
> +	mutex_unlock(&pch_phub_ioctl_mutex);
> +return_nomutex:
> +	return ret_value;
> +}


> +static int __devinit pch_phub_probe(struct pci_dev *pdev,
> +				    const struct pci_device_id *id)
> +{
> +	int ret;
> +	unsigned int rom_size;
> +
> +	ret = pci_enable_device(pdev);
> +	if (ret) {
> +		dev_dbg(&pdev->dev,
> +		"pch_phub_probe : pci_enable_device FAILED(ret=%d)", ret);
> +		goto err_pci_enable_dev;
> +	}
> +	dev_dbg(&pdev->dev, "pch_phub_probe : pci_enable_device returns %d\n",
> +		ret);
> +
> +	ret = pci_request_regions(pdev, MODULE_NAME);
> +	if (ret) {
> +		dev_dbg(&pdev->dev,
> +		"pch_phub_probe : pci_request_regions FAILED(ret=%d)", ret);
> +		goto err_req_regions;
> +	}
> +	dev_dbg(&pdev->dev, "pch_phub_probe : "
> +		"pci_request_regions returns %d\n", ret);
> +
> +	pch_phub_reg.pch_phub_base_address = pci_iomap(pdev, 1, 0);
> +
> +	if (pch_phub_reg.pch_phub_base_address == 0) {
> +		dev_dbg(&pdev->dev, "pch_phub_probe : pci_iomap FAILED");
> +		ret = -ENOMEM;
> +		goto err_pci_iomap;
> +	}
> +	dev_dbg(&pdev->dev, "pch_phub_probe : pci_iomap SUCCESS and value "
> +		"in pch_phub_base_address variable is 0x%08x\n",
> +		(unsigned int)pch_phub_reg.pch_phub_base_address);
> +
> +	pch_phub_reg.pch_phub_extrom_base_address =
> +						pci_map_rom(pdev, &rom_size);
> +	if (pch_phub_reg.pch_phub_extrom_base_address == 0) {
> +		dev_dbg(&pdev->dev, "pch_phub_probe : pci_map_rom FAILED");
> +		ret = -ENOMEM;
> +		goto err_pci_map;
> +	}
> +	dev_dbg(&pdev->dev, "pch_phub_probe : "
> +		"pci_map_rom SUCCESS and value in "
> +		"pch_phub_extrom_base_address variable is 0x%08x\n",
> +		(unsigned int)pch_phub_reg.pch_phub_extrom_base_address);
> +
> +	ret = alloc_chrdev_region(&pch_phub_dev_no, 0,
> +					PCH_MINOR_NOS, MODULE_NAME);
> +	if (ret) {
> +		dev_dbg(&pdev->dev, "pch_phub_probe : "
> +				"alloc_chrdev_region FAILED(ret=%d)", ret);
> +
> +		goto err_alloc_cdev;
> +	}
> +	dev_dbg(&pdev->dev, "pch_phub_probe : "
> +		"alloc_chrdev_region returns %d\n", ret);
> +
> +	cdev_init(&pch_phub_dev, &pch_phub_fops);
> +	dev_dbg(&pdev->dev,
> +			"pch_phub_probe :  cdev_init invoked successfully\n");
> +
> +	pch_phub_dev.owner = THIS_MODULE;
> +	pch_phub_dev.ops = &pch_phub_fops;
> +
> +	ret = cdev_add(&pch_phub_dev, pch_phub_dev_no, PCH_MINOR_NOS);
> +	if (ret) {
> +		dev_dbg(&pdev->dev, "pch_phub_probe :  cdev_add FAILED(ret=%d)",
> +			ret);
> +		goto err_cdev_add;
> +	}
> +	dev_dbg(&pdev->dev, "pch_phub_probe :  cdev_add returns %d\n", ret);
> +
> +	/* set the clock config reg if CAN clock is 50Mhz */
> +	dev_dbg(&pdev->dev, "pch_phub_probe : invoking "
> +		"pch_phub_read_modify_write_reg "
> +		"to set CLKCFG reg for CAN clk 50Mhz\n");
> +	pch_phub_read_modify_write_reg((unsigned int)CLKCFG_REG_OFFSET,
> +					  CLKCFG_CAN_50MHZ, CLKCFG_CANCLK_MASK);
> +
> +	/* set the prefech value */
> +	iowrite32(0x000ffffa, pch_phub_reg.pch_phub_base_address + 0x14);
> +	/* set the interrupt delay value */
> +	iowrite32(0x25, pch_phub_reg.pch_phub_base_address + 0x44);
> +	return 0;
> +
> +err_cdev_add:
> +	unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
> +err_alloc_cdev:
> +	pci_unmap_rom(pdev, pch_phub_reg.pch_phub_extrom_base_address);
> +err_pci_map:
> +	pci_iounmap(pdev, pch_phub_reg.pch_phub_base_address);
> +err_pci_iomap:
> +	pci_release_regions(pdev);
> +err_req_regions:
> +	pci_disable_device(pdev);
> +err_pci_enable_dev:
> +	dev_dbg(&pdev->dev, "pch_phub_probe returns %d\n", ret);
> +	return ret;
> +}
> +
> +static void __devexit pch_phub_remove(struct pci_dev *pdev)
> +{
> +

drop blank line.

> +	cdev_del(&pch_phub_dev);
> +	dev_dbg(&pdev->dev,
> +			"pch_phub_remove - cdev_del Invoked successfully\n");
> +
> +	unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
> +	dev_dbg(&pdev->dev, "pch_phub_remove - "
> +		"unregister_chrdev_region Invoked successfully\n");
> +
> +	pci_unmap_rom(pdev, pch_phub_reg.pch_phub_extrom_base_address);
> +
> +	pci_iounmap(pdev, pch_phub_reg.pch_phub_base_address);
> +
> +	dev_dbg(&pdev->dev, "pch_phub_remove - "
> +			"pci_iounmap Invoked successfully\n");
> +
> +	pci_release_regions(pdev);
> +	dev_dbg(&pdev->dev, "pch_phub_remove - "
> +		"pci_release_regions Invoked successfully\n");
> +
> +	pci_disable_device(pdev);
> +	dev_dbg(&pdev->dev, "pch_phub_remove - "
> +		"pci_disable_device Invoked successfully\n");
> +
> +}
> +
> +#ifdef CONFIG_PM

> +
> +static int pch_phub_resume(struct pci_dev *pdev)
> +{
> +

drop that blank line before the local data, but leave the one after local data.

> +	int ret;
> +
> +	pci_set_power_state(pdev, PCI_D0);
> +	dev_dbg(&pdev->dev, "pch_phub_resume - "
> +		"pci_set_power_state Invoked successfully\n");
> +
> +	pci_restore_state(pdev);
> +	dev_dbg(&pdev->dev, "pch_phub_resume - "
> +		"pci_restore_state Invoked successfully\n");
> +
> +	ret = pci_enable_device(pdev);
> +	if (ret) {
> +		dev_dbg(&pdev->dev,
> +		"pch_phub_resume-pci_enable_device failed(ret=%d) ", ret);
> +		return ret;
> +	}
> +
> +	dev_dbg(&pdev->dev, "pch_phub_resume - "
> +			"pci_enable_device returns -%d\n", ret);

The '-' before %d could be confusing.

> +
> +	pci_enable_wake(pdev, PCI_D3hot, 0);
> +	dev_dbg(&pdev->dev, "pch_phub_resume - "
> +			"pci_enable_wake Invoked successfully\n");
> +
> +	pch_phub_restore_reg_conf(pdev);
> +	dev_dbg(&pdev->dev, "pch_phub_resume - "
> +		"pch_phub_restore_reg_conf Invoked successfully\n");
> +
> +	pch_phub_reg.pch_phub_suspended = false;
> +
> +	dev_dbg(&pdev->dev, "pch_phub_resume  returns- %d\n", 0);

All of these dev_dbg() calls should use __func__ to get the function name:

	dev_dbg(&pdev->dev, "%s returns: %d\n", __func__, 0);

(same for other functions)

(I dropped the '-' so that it won't look like a negative number.)

> +	return 0;
> +}
> +#else
> +#define pch_phub_suspend NULL
> +#define pch_phub_resume NULL
> +#endif /* CONFIG_PM */
> +
> +static struct pci_device_id pch_phub_pcidev_id[] = {
> +
> +	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH1_PHUB)},
> +	{0,}
> +};
> +
> +
> +static struct pci_driver pch_phub_driver = {
> +	.name = "pch_phub",
> +	.id_table = pch_phub_pcidev_id,
> +	.probe = pch_phub_probe,
> +	.remove = __devexit_p(pch_phub_remove),
> +	.suspend = pch_phub_suspend,
> +	.resume = pch_phub_resume
> +};
> +
> +/* pch_phub_pci_init - Implements the initialization functionality of
> + * the module.
> + */

drop obvious comments.

> +static int __init pch_phub_pci_init(void)
> +{
> +	int ret;
> +	ret = pci_register_driver(&pch_phub_driver);
> +
> +	return ret;
> +}
> +
> +/* pch_phub_pci_exit - Implements the exit functionality of the module. */

drop obvious comment.

> +static void __exit pch_phub_pci_exit(void)
> +{
> +	pci_unregister_driver(&pch_phub_driver);
> +

drop blank line.

> +}
> +
> +module_init(pch_phub_pci_init);
> +module_exit(pch_phub_pci_exit);
> +
> +MODULE_DESCRIPTION("PCH PACKET HUB PCI Driver");
> +MODULE_LICENSE("GPL");
> +

> diff --git a/drivers/char/pch_phub/pch_phub.h b/drivers/char/pch_phub/pch_phub.h
> new file mode 100755
> index 0000000..7baa272
> --- /dev/null
> +++ b/drivers/char/pch_phub/pch_phub.h
> @@ -0,0 +1,48 @@
> +#ifndef __PCH_PHUB_H__
> +#define __PCH_PHUB_H__
> +/*!

??

> + * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
> + *
> + * 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; version 2 of the License.
> + *
> + * 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, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
> + */
> +
> +#define PHUB_IOCTL_MAGIC		(0xf7)

ioctl numbers need to be added to Documentation/ioctl/ioctl-number.txt, please.

> +
> +/* Read GbE MAC address */
> +#define IOCTL_PHUB_READ_MAC_ADDR (_IOR(PHUB_IOCTL_MAGIC, 6, __u8[6]))
> +
> +/* Write GbE MAC address */
> +#define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 7, __u8[6]))
> +
> +/* SROM ACCESS Macro */
> +#define PCH_WORD_ADDR_MASK (~((1 << 2) - 1))
> +
> +/* Registers address offset */
> +#define PCH_PHUB_ID_REG				0x0000
> +#define PCH_PHUB_QUEUE_PRI_VAL_REG		0x0004
> +#define PCH_PHUB_RC_QUEUE_MAXSIZE_REG		0x0008
> +#define PCH_PHUB_BRI_QUEUE_MAXSIZE_REG		0x000C
> +#define PCH_PHUB_COMP_RESP_TIMEOUT_REG		0x0010
> +#define PCH_PHUB_BUS_SLAVE_CONTROL_REG		0x0014
> +#define PCH_PHUB_DEADLOCK_AVOID_TYPE_REG	0x0018
> +#define PCH_PHUB_INTPIN_REG_WPERMIT_REG0	0x0020
> +#define PCH_PHUB_INTPIN_REG_WPERMIT_REG1	0x0024
> +#define PCH_PHUB_INTPIN_REG_WPERMIT_REG2	0x0028
> +#define PCH_PHUB_INTPIN_REG_WPERMIT_REG3	0x002C
> +#define PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE	0x0040
> +#define CLKCFG_REG_OFFSET			0x500
> +
> +#define PCH_PHUB_OROM_SIZE 15360
> +
> +#endif


-- 
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***

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

* [PATCH] Packet hub driver of Topcliff PCH
  2010-06-22  5:33 [PATCH] Topcliff PHUB: Generate PacketHub driver Masayuki Ohtak
                   ` (5 preceding siblings ...)
  2010-07-01 10:38 ` Masayuki Ohtak
@ 2010-07-05  7:20 ` Masayuki Ohtak
  2010-07-05 15:04   ` Arnd Bergmann
  2010-07-06 15:58   ` Randy Dunlap
  2010-07-06  6:20 ` Masayuki Ohtak
                   ` (3 subsequent siblings)
  10 siblings, 2 replies; 46+ messages in thread
From: Masayuki Ohtak @ 2010-07-05  7:20 UTC (permalink / raw)
  To: Randy Dunlap
  Cc: Arnd Bergmann, Wang, Yong Y, qi.wang, joel.clark,
	andrew.chih.howe.khor, Alan Cox, LKML

Hi Randy

I have modified for your comments.
Please confirm below.

Thanks, Ohtake.

---

Packet hub driver of Topcliff PCH

Topcliff PCH is the platform controller hub that is going to be used in
Intel's upcoming general embedded platform. All IO peripherals in
Topcliff PCH are actually devices sitting on AMBA bus. Packet hub is
a special converter device in Topcliff PCH that translate AMBA transactions
to PCI Express transactions and vice versa. Thus packet hub helps present
all IO peripherals in Topcliff PCH as PCIE devices to IA system.
Topcliff PCH has MAC address and Option ROM data.
These data are in SROM which is connected to PCIE bus.
Packet hub driver of Topcliff PCH can access MAC address and Option ROM data in
SROM.

Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>

---
 Documentation/ioctl/ioctl-number.txt |    1 +
 drivers/char/Kconfig                 |    9 +
 drivers/char/Makefile                |    2 +
 drivers/char/pch_phub/Makefile       |    3 +
 drivers/char/pch_phub/pch_phub.c     |  803 ++++++++++++++++++++++++++++++++++
 drivers/char/pch_phub/pch_phub.h     |   48 ++
 6 files changed, 866 insertions(+), 0 deletions(-)
 create mode 100644 drivers/char/pch_phub/Makefile
 create mode 100755 drivers/char/pch_phub/pch_phub.c
 create mode 100755 drivers/char/pch_phub/pch_phub.h

diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 35cf64d..b700b17 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -320,4 +320,5 @@ Code  Seq#(hex)	Include File		Comments
 					<mailto:thomas@winischhofer.net>
 0xF4	00-1F	video/mbxfb.h		mbxfb
 					<mailto:raph@8d.com>
+0xF7	00-0F	drivers/char/pch_phub/pch_phub.h	PCH Phub driver
 0xFD	all	linux/dm-ioctl.h
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index e023682..499902f 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -4,6 +4,15 @@
 
 menu "Character devices"
 
+config PCH_PHUB
+	tristate "PCH PHUB"
+	depends on PCI
+	help
+	  This driver is for PCH PHUB of Topcliff which is an IOH for x86
+	  embedded processor. The Topcliff has MAC address and Option ROM data
+	  in SROM. This driver can access MAC address and Option ROM data in
+	  SROM.
+
 config VT
 	bool "Virtual terminal" if EMBEDDED
 	depends on !S390
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index f957edf..1e3eb6c 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -111,6 +111,8 @@ obj-$(CONFIG_PS3_FLASH)		+= ps3flash.o
 obj-$(CONFIG_JS_RTC)		+= js-rtc.o
 js-rtc-y = rtc.o
 
+obj-$(CONFIG_PCH_PHUB)	+= pch_phub/
+
 # Files generated that shall be removed upon make clean
 clean-files := consolemap_deftbl.c defkeymap.c
 
diff --git a/drivers/char/pch_phub/Makefile b/drivers/char/pch_phub/Makefile
new file mode 100644
index 0000000..93aaffe
--- /dev/null
+++ b/drivers/char/pch_phub/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_PCH_PHUB) += pch_phub_drv.o
+
+pch_phub_drv-objs := pch_phub.o
diff --git a/drivers/char/pch_phub/pch_phub.c b/drivers/char/pch_phub/pch_phub.c
new file mode 100755
index 0000000..a1868cd
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.c
@@ -0,0 +1,803 @@
+/*
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/if_ether.h>
+#include "pch_phub.h"
+
+#define PHUB_STATUS 0x00		/* Status Register offset */
+#define PHUB_CONTROL 0x04		/* Control Register offset */
+#define PHUB_TIMEOUT 0x05		/* Time out value for Status Register */
+#define PCH_PHUB_ROM_WRITE_ENABLE 0x01	/* Enabling for writing ROM */
+#define PCH_PHUB_ROM_WRITE_DISABLE 0x00	/* Disabling for writing ROM */
+#define PCH_PHUB_ROM_START_ADDR 0x14	/* ROM data area start address offset */
+
+/* MAX number of INT_REDUCE_CONTROL registers */
+#define MAX_NUM_INT_REDUCE_CONTROL_REG 128
+#define PCI_DEVICE_ID_PCH1_PHUB 0x8801
+#define PCH_MINOR_NOS 1
+#define CLKCFG_CAN_50MHZ 0x12000000
+#define CLKCFG_CANCLK_MASK 0xFF000000
+#define MODULE_NAME "pch_phub"
+
+/**
+ * struct pch_phub_reg - PHUB register structure
+ * @phub_id_reg:			PHUB_ID register val
+ * @q_pri_val_reg:			QUEUE_PRI_VAL register val
+ * @rc_q_maxsize_reg:			RC_QUEUE_MAXSIZE register val
+ * @bri_q_maxsize_reg:			BRI_QUEUE_MAXSIZE register val
+ * @comp_resp_timeout_reg:		COMP_RESP_TIMEOUT register val
+ * @bus_slave_control_reg:		BUS_SLAVE_CONTROL_REG register val
+ * @deadlock_avoid_type_reg:		DEADLOCK_AVOID_TYPE register val
+ * @intpin_reg_wpermit_reg0:		INTPIN_REG_WPERMIT register 0 val
+ * @intpin_reg_wpermit_reg1:		INTPIN_REG_WPERMIT register 1 val
+ * @intpin_reg_wpermit_reg2:		INTPIN_REG_WPERMIT register 2 val
+ * @intpin_reg_wpermit_reg3:		INTPIN_REG_WPERMIT register 3 val
+ * @int_reduce_control_reg:		INT_REDUCE_CONTROL registers val
+ * @clkcfg_reg:				CLK CFG register val
+ * @pch_phub_base_address:		Register base address
+ * @pch_phub_extrom_base_address:	external rom base address
+ * @pch_phub_suspended:			PHUB status val
+ */
+struct pch_phub_reg {
+	u32 phub_id_reg;
+	u32 q_pri_val_reg;
+	u32 rc_q_maxsize_reg;
+	u32 bri_q_maxsize_reg;
+	u32 comp_resp_timeout_reg;
+	u32 bus_slave_control_reg;
+	u32 deadlock_avoid_type_reg;
+	u32 intpin_reg_wpermit_reg0;
+	u32 intpin_reg_wpermit_reg1;
+	u32 intpin_reg_wpermit_reg2;
+	u32 intpin_reg_wpermit_reg3;
+	u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG];
+	u32 clkcfg_reg;
+	void __iomem *pch_phub_base_address;
+	void __iomem *pch_phub_extrom_base_address;
+	int pch_phub_suspended;
+} pch_phub_reg;
+
+/* SROM SPEC for MAC address assignment offset */
+static const int pch_phub_mac_offset[ETH_ALEN] = {0x3, 0x2, 0x1, 0x0, 0xb, 0xa};
+
+static DEFINE_MUTEX(pch_phub_ioctl_mutex);
+static DEFINE_MUTEX(pch_phub_read_mutex);
+static DEFINE_MUTEX(pch_phub_write_mutex);
+static dev_t pch_phub_dev_no;
+static struct cdev pch_phub_dev;
+
+/**
+ * pch_phub_read_modify_write_reg() - Reading modifying and writing register
+ * @reg_addr_offset:	Register offset address value.
+ * @data:		Writing value.
+ * @mask:		Mask value.
+ */
+static void pch_phub_read_modify_write_reg(unsigned int reg_addr_offset,
+					   unsigned int data, unsigned int mask)
+{
+	void __iomem *reg_addr = pch_phub_reg.pch_phub_base_address +
+					 reg_addr_offset;
+	iowrite32(((ioread32(reg_addr) & ~mask)) | data, reg_addr);
+}
+
+
+/* pch_phub_save_reg_conf - saves register configuration */
+static void pch_phub_save_reg_conf(struct pci_dev *pdev)
+{
+	unsigned int i;
+	void __iomem *p = pch_phub_reg.pch_phub_base_address;
+
+	dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
+	pch_phub_reg.phub_id_reg = ioread32(p + PCH_PHUB_ID_REG);
+	pch_phub_reg.q_pri_val_reg = ioread32(p + PCH_PHUB_QUEUE_PRI_VAL_REG);
+	pch_phub_reg.rc_q_maxsize_reg =
+	    ioread32(p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+	pch_phub_reg.bri_q_maxsize_reg =
+	    ioread32(p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+	pch_phub_reg.comp_resp_timeout_reg =
+	    ioread32(p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+	pch_phub_reg.bus_slave_control_reg =
+	    ioread32(p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+	pch_phub_reg.deadlock_avoid_type_reg =
+	    ioread32(p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+	pch_phub_reg.intpin_reg_wpermit_reg0 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+	pch_phub_reg.intpin_reg_wpermit_reg1 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+	pch_phub_reg.intpin_reg_wpermit_reg2 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+	pch_phub_reg.intpin_reg_wpermit_reg3 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
+	dev_dbg(&pdev->dev, "%s : "
+		"pch_phub_reg.phub_id_reg=%x, "
+		"pch_phub_reg.q_pri_val_reg=%x, "
+		"pch_phub_reg.rc_q_maxsize_reg=%x, "
+		"pch_phub_reg.bri_q_maxsize_reg=%x, "
+		"pch_phub_reg.comp_resp_timeout_reg=%x, "
+		"pch_phub_reg.bus_slave_control_reg=%x, "
+		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n", __func__,
+		pch_phub_reg.phub_id_reg,
+		pch_phub_reg.q_pri_val_reg,
+		pch_phub_reg.rc_q_maxsize_reg,
+		pch_phub_reg.bri_q_maxsize_reg,
+		pch_phub_reg.comp_resp_timeout_reg,
+		pch_phub_reg.bus_slave_control_reg,
+		pch_phub_reg.deadlock_avoid_type_reg,
+		pch_phub_reg.intpin_reg_wpermit_reg0,
+		pch_phub_reg.intpin_reg_wpermit_reg1,
+		pch_phub_reg.intpin_reg_wpermit_reg2,
+		pch_phub_reg.intpin_reg_wpermit_reg3);
+	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
+		pch_phub_reg.int_reduce_control_reg[i] =
+		    ioread32(p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
+		dev_dbg(&pdev->dev, "%s : "
+			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
+			__func__, i, pch_phub_reg.int_reduce_control_reg[i]);
+	}
+	pch_phub_reg.clkcfg_reg = ioread32(p + CLKCFG_REG_OFFSET);
+}
+
+/* pch_phub_restore_reg_conf - restore register configuration */
+static void pch_phub_restore_reg_conf(struct pci_dev *pdev)
+{
+	unsigned int i;
+	void __iomem *p = pch_phub_reg.pch_phub_base_address;
+
+	dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
+	iowrite32(pch_phub_reg.phub_id_reg, p + PCH_PHUB_ID_REG);
+	iowrite32(pch_phub_reg.q_pri_val_reg, p + PCH_PHUB_QUEUE_PRI_VAL_REG);
+	iowrite32(pch_phub_reg.rc_q_maxsize_reg,
+					p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+	iowrite32(pch_phub_reg.bri_q_maxsize_reg,
+					p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+	iowrite32(pch_phub_reg.comp_resp_timeout_reg,
+					p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+	iowrite32(pch_phub_reg.bus_slave_control_reg,
+					p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+	iowrite32(pch_phub_reg.deadlock_avoid_type_reg,
+					p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg0,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg1,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg2,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg3,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
+	dev_dbg(&pdev->dev, "%s : "
+		"pch_phub_reg.phub_id_reg=%x, "
+		"pch_phub_reg.q_pri_val_reg=%x, "
+		"pch_phub_reg.rc_q_maxsize_reg=%x, "
+		"pch_phub_reg.bri_q_maxsize_reg=%x, "
+		"pch_phub_reg.comp_resp_timeout_reg=%x, "
+		"pch_phub_reg.bus_slave_control_reg=%x, "
+		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n", __func__,
+		pch_phub_reg.phub_id_reg,
+		pch_phub_reg.q_pri_val_reg,
+		pch_phub_reg.rc_q_maxsize_reg,
+		pch_phub_reg.bri_q_maxsize_reg,
+		pch_phub_reg.comp_resp_timeout_reg,
+		pch_phub_reg.bus_slave_control_reg,
+		pch_phub_reg.deadlock_avoid_type_reg,
+		pch_phub_reg.intpin_reg_wpermit_reg0,
+		pch_phub_reg.intpin_reg_wpermit_reg1,
+		pch_phub_reg.intpin_reg_wpermit_reg2,
+		pch_phub_reg.intpin_reg_wpermit_reg3);
+	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
+		iowrite32(pch_phub_reg.int_reduce_control_reg[i],
+			p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
+		dev_dbg(&pdev->dev, "%s : "
+			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
+			__func__, i, pch_phub_reg.int_reduce_control_reg[i]);
+	}
+
+	iowrite32(pch_phub_reg.clkcfg_reg, p + CLKCFG_REG_OFFSET);
+}
+
+/**
+ * pch_phub_read_serial_rom() - Reading Serial ROM
+ * @offset_address:	Serial ROM offset address to read.
+ * @data:		Read buffer for specified Serial ROM value.
+ */
+static void pch_phub_read_serial_rom(unsigned int offset_address, u8 *data)
+{
+	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +
+					 offset_address;
+
+	*data = ioread8(mem_addr);
+}
+
+/**
+ * pch_phub_write_serial_rom() - Writing Serial ROM
+ * @offset_address:	Serial ROM offset address.
+ * @data:		Serial ROM value to write.
+ */
+static int pch_phub_write_serial_rom(unsigned int offset_address, u8 data)
+{
+	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +
+					(offset_address & PCH_WORD_ADDR_MASK);
+	int i;
+	unsigned int word_data;
+	unsigned int pos;
+	unsigned int mask;
+	pos = (offset_address % 4) * 8;
+	mask = ~(0xFF << pos);
+
+	iowrite32(PCH_PHUB_ROM_WRITE_ENABLE,
+			pch_phub_reg.pch_phub_extrom_base_address +
+			PHUB_CONTROL);
+
+	word_data = ioread32(mem_addr);
+	iowrite32((word_data & mask) | (u32)data << pos, mem_addr);
+
+	i = 0;
+	while (ioread8(pch_phub_reg.pch_phub_extrom_base_address +
+						PHUB_STATUS) != 0x00) {
+		msleep(1);
+		if (PHUB_TIMEOUT == i)
+			return -EPERM;
+		i++;
+	}
+
+	iowrite32(PCH_PHUB_ROM_WRITE_DISABLE,
+			pch_phub_reg.pch_phub_extrom_base_address +
+			PHUB_CONTROL);
+
+	return 0;
+}
+
+/**
+ * pch_phub_read_serial_rom_val() - Read Serial ROM value
+ * @offset_address:	Serial ROM address offset value.
+ * @data:		Serial ROM value to read.
+ */
+static void pch_phub_read_serial_rom_val(unsigned int offset_address, u8 *data)
+{
+	unsigned int mem_addr;
+
+	mem_addr = PCH_PHUB_ROM_START_ADDR +
+			pch_phub_mac_offset[offset_address];
+
+	pch_phub_read_serial_rom(mem_addr, data);
+}
+
+
+/**
+ * pch_phub_write_serial_rom_val() - writing Serial ROM value
+ * @offset_address:	Serial ROM address offset value.
+ * @data:		Serial ROM value.
+ */
+static int pch_phub_write_serial_rom_val(unsigned int offset_address, u8 data)
+{
+	int retval;
+	unsigned int mem_addr;
+
+	mem_addr = PCH_PHUB_ROM_START_ADDR +
+			pch_phub_mac_offset[offset_address];
+
+	retval = pch_phub_write_serial_rom(mem_addr, data);
+
+	return retval;
+}
+
+/* pch_phub_gbe_serial_rom_conf - makes Serial ROM header format configuration
+ * for Gigabit Ethernet MAC address
+ */
+static int pch_phub_gbe_serial_rom_conf(void)
+{
+	int retval;
+
+	retval = pch_phub_write_serial_rom(0x0b, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x0a, 0x10);
+	retval |= pch_phub_write_serial_rom(0x09, 0x01);
+	retval |= pch_phub_write_serial_rom(0x08, 0x02);
+
+	retval |= pch_phub_write_serial_rom(0x0f, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0e, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0d, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0c, 0x80);
+
+	retval |= pch_phub_write_serial_rom(0x13, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x12, 0x10);
+	retval |= pch_phub_write_serial_rom(0x11, 0x01);
+	retval |= pch_phub_write_serial_rom(0x10, 0x18);
+
+	retval |= pch_phub_write_serial_rom(0x1b, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x1a, 0x10);
+	retval |= pch_phub_write_serial_rom(0x19, 0x01);
+	retval |= pch_phub_write_serial_rom(0x18, 0x19);
+
+	retval |= pch_phub_write_serial_rom(0x23, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x22, 0x10);
+	retval |= pch_phub_write_serial_rom(0x21, 0x01);
+	retval |= pch_phub_write_serial_rom(0x20, 0x3a);
+
+	retval |= pch_phub_write_serial_rom(0x27, 0x01);
+	retval |= pch_phub_write_serial_rom(0x26, 0x00);
+	retval |= pch_phub_write_serial_rom(0x25, 0x00);
+	retval |= pch_phub_write_serial_rom(0x24, 0x00);
+
+	return retval;
+}
+
+/**
+ * pch_phub_read_gbe_mac_addr() - Read Gigabit Ethernet MAC address
+ * @offset_address:	Gigabit Ethernet MAC address offset value.
+ * @data:		Buffer of the Gigabit Ethernet MAC address value.
+ */
+static void pch_phub_read_gbe_mac_addr(u8 *data)
+{
+	int i;
+	for (i = 0; i < ETH_ALEN; i++)
+		pch_phub_read_serial_rom_val(i, &data[i]);
+}
+
+/**
+ * pch_phub_write_gbe_mac_addr() - Write MAC address
+ * @offset_address:	Gigabit Ethernet MAC address offset value.
+ * @data:		Gigabit Ethernet MAC address value.
+ */
+static int pch_phub_write_gbe_mac_addr(u8 *data)
+{
+	int retval;
+	int i;
+
+	retval = pch_phub_gbe_serial_rom_conf();
+	if (retval)
+		return retval;
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		retval = pch_phub_write_serial_rom_val(i, data[i]);
+		if (retval)
+			return retval;
+	}
+
+	return retval;
+}
+
+static ssize_t pch_phub_read(struct file *file, char __user *buf, size_t size,
+			     loff_t *ppos)
+{
+	unsigned int rom_signature;
+	unsigned char rom_length;
+	int ret_value;
+	unsigned int tmp;
+	unsigned char data;
+	unsigned int addr_offset;
+	unsigned int orom_size;
+	int ret;
+	int err;
+	loff_t pos = *ppos;
+
+	ret = mutex_lock_interruptible(&pch_phub_read_mutex);
+	if (ret) {
+		err = -ERESTARTSYS;
+		goto return_err_nomutex;
+	}
+
+	/* Get Rom signature */
+	pch_phub_read_serial_rom(0x80, (unsigned char *)&rom_signature);
+	rom_signature &= 0xff;
+	pch_phub_read_serial_rom(0x81, (unsigned char *)&tmp);
+	rom_signature |= (tmp & 0xff) << 8;
+	if (rom_signature == 0xAA55) {
+		pch_phub_read_serial_rom(0x82, &rom_length);
+		orom_size = rom_length * 512;
+		if (orom_size < pos) {
+			addr_offset = 0;
+			goto return_ok;
+		}
+
+		for (addr_offset = 0; addr_offset < size; addr_offset++) {
+			pch_phub_read_serial_rom(0x80 + addr_offset + pos,
+									 &data);
+			ret_value = copy_to_user(&buf[addr_offset], &data, 1);
+			if (ret_value) {
+				err = -EFAULT;
+				goto return_err;
+			}
+
+			if (orom_size < pos + addr_offset) {
+				*ppos += addr_offset;
+				goto return_ok;
+			}
+
+		}
+	} else {
+		err = -ENODATA;
+		goto return_err;
+	}
+	*ppos += addr_offset;
+return_ok:
+	mutex_unlock(&pch_phub_read_mutex);
+	return addr_offset;
+
+return_err:
+	mutex_unlock(&pch_phub_read_mutex);
+return_err_nomutex:
+	return err;
+}
+
+
+static ssize_t pch_phub_write(struct file *file, const char __user *buf,
+			      size_t size, loff_t *ppos)
+{
+	unsigned int data;
+	int ret_value1;
+	int ret_value2;
+	int err;
+	unsigned int addr_offset;
+	loff_t pos = *ppos;
+	int ret;
+
+	ret = mutex_lock_interruptible(&pch_phub_write_mutex);
+	if (ret) {
+		err = -ERESTARTSYS;
+		goto return_err_nomutex;
+	}
+
+	for (addr_offset = 0; addr_offset < size; addr_offset++) {
+		ret_value1 = get_user(data, &buf[addr_offset]);
+		if (ret_value1) {
+			err = -EFAULT;
+			goto return_err;
+		}
+
+		ret_value2 = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
+							data);
+		if (ret_value2) {
+			err = ret_value2;
+			goto return_err;
+		}
+
+		if (PCH_PHUB_OROM_SIZE < pos + addr_offset) {
+			*ppos += addr_offset;
+			goto return_ok;
+		}
+
+	}
+
+	*ppos += addr_offset;
+
+return_ok:
+	mutex_unlock(&pch_phub_write_mutex);
+	return addr_offset;
+
+return_err:
+	mutex_unlock(&pch_phub_write_mutex);
+return_err_nomutex:
+	return err;
+}
+
+
+static long pch_phub_ioctl(struct file *file, unsigned int cmd,
+			   unsigned long arg)
+{
+	int ret_value;
+	int ret;
+	int rtn;
+	__u8 mac_addr[ETH_ALEN];
+	void __user *varg = (void __user *)arg;
+
+	ret = mutex_lock_interruptible(&pch_phub_ioctl_mutex);
+	if (ret) {
+		ret_value = -ERESTARTSYS;
+		goto return_nomutex;
+	}
+
+	if (pch_phub_reg.pch_phub_suspended == true) {
+		ret_value = -EBUSY;
+		goto return_ioctrl;
+	}
+
+	switch (cmd) {
+	case IOCTL_PHUB_READ_MAC_ADDR:
+		pch_phub_read_gbe_mac_addr(mac_addr);
+
+		ret_value = copy_to_user(varg, mac_addr, ETH_ALEN);
+		break;
+
+	case IOCTL_PHUB_WRITE_MAC_ADDR:
+		rtn = copy_from_user(mac_addr, varg, ETH_ALEN);
+
+		if (rtn) {
+			ret_value = -EFAULT;
+			break;
+		}
+
+		ret_value = pch_phub_write_gbe_mac_addr(mac_addr);
+		break;
+
+	default:
+		ret_value = -EINVAL;
+		break;
+	}
+return_ioctrl:
+	mutex_unlock(&pch_phub_ioctl_mutex);
+return_nomutex:
+	return ret_value;
+}
+
+
+/* file_operations structure initialization */
+static const struct file_operations pch_phub_fops = {
+	.owner = THIS_MODULE,
+	.read = pch_phub_read,
+	.write = pch_phub_write,
+	.unlocked_ioctl = pch_phub_ioctl,
+	.llseek = default_llseek
+};
+
+
+static int __devinit pch_phub_probe(struct pci_dev *pdev,
+				    const struct pci_device_id *id)
+{
+	int ret;
+	unsigned int rom_size;
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+		"%s : pci_enable_device FAILED(ret=%d)", __func__, ret);
+		goto err_pci_enable_dev;
+	}
+	dev_dbg(&pdev->dev, "%s : pci_enable_device returns %d\n", __func__,
+		ret);
+
+	ret = pci_request_regions(pdev, MODULE_NAME);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+		"%s : pci_request_regions FAILED(ret=%d)", __func__, ret);
+		goto err_req_regions;
+	}
+	dev_dbg(&pdev->dev, "%s : "
+		"pci_request_regions returns %d\n", __func__, ret);
+
+	pch_phub_reg.pch_phub_base_address = pci_iomap(pdev, 1, 0);
+
+	if (pch_phub_reg.pch_phub_base_address == 0) {
+		dev_dbg(&pdev->dev, "%s : pci_iomap FAILED", __func__);
+		ret = -ENOMEM;
+		goto err_pci_iomap;
+	}
+	dev_dbg(&pdev->dev, "%s : pci_iomap SUCCESS and value "
+		"in pch_phub_base_address variable is 0x%08x\n", __func__,
+		(unsigned int)pch_phub_reg.pch_phub_base_address);
+
+	pch_phub_reg.pch_phub_extrom_base_address =
+						pci_map_rom(pdev, &rom_size);
+	if (pch_phub_reg.pch_phub_extrom_base_address == 0) {
+		dev_dbg(&pdev->dev, "%s : pci_map_rom FAILED", __func__);
+		ret = -ENOMEM;
+		goto err_pci_map;
+	}
+	dev_dbg(&pdev->dev, "%s : "
+		"pci_map_rom SUCCESS and value in "
+		"pch_phub_extrom_base_address variable is 0x%08x\n", __func__,
+		(unsigned int)pch_phub_reg.pch_phub_extrom_base_address);
+
+	ret = alloc_chrdev_region(&pch_phub_dev_no, 0,
+					PCH_MINOR_NOS, MODULE_NAME);
+	if (ret) {
+		dev_dbg(&pdev->dev, "%s : "
+			"alloc_chrdev_region FAILED(ret=%d)", __func__, ret);
+
+		goto err_alloc_cdev;
+	}
+	dev_dbg(&pdev->dev, "%s : "
+		"alloc_chrdev_region returns %d\n", __func__, ret);
+
+	cdev_init(&pch_phub_dev, &pch_phub_fops);
+	dev_dbg(&pdev->dev,
+			"%s : cdev_init invoked successfully\n", __func__);
+
+	pch_phub_dev.owner = THIS_MODULE;
+	pch_phub_dev.ops = &pch_phub_fops;
+
+	ret = cdev_add(&pch_phub_dev, pch_phub_dev_no, PCH_MINOR_NOS);
+	if (ret) {
+		dev_dbg(&pdev->dev, "%s :  cdev_add FAILED(ret=%d)",
+			__func__, ret);
+		goto err_cdev_add;
+	}
+	dev_dbg(&pdev->dev, "%s :  cdev_add returns %d\n", __func__, ret);
+
+	/* set the clock config reg if CAN clock is 50Mhz */
+	dev_dbg(&pdev->dev, "%s : invoking "
+		"pch_phub_read_modify_write_reg "
+		"to set CLKCFG reg for CAN clk 50Mhz\n", __func__);
+	pch_phub_read_modify_write_reg((unsigned int)CLKCFG_REG_OFFSET,
+					  CLKCFG_CAN_50MHZ, CLKCFG_CANCLK_MASK);
+
+	/* set the prefech value */
+	iowrite32(0x000ffffa, pch_phub_reg.pch_phub_base_address + 0x14);
+	/* set the interrupt delay value */
+	iowrite32(0x25, pch_phub_reg.pch_phub_base_address + 0x44);
+	return 0;
+
+err_cdev_add:
+	unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
+err_alloc_cdev:
+	pci_unmap_rom(pdev, pch_phub_reg.pch_phub_extrom_base_address);
+err_pci_map:
+	pci_iounmap(pdev, pch_phub_reg.pch_phub_base_address);
+err_pci_iomap:
+	pci_release_regions(pdev);
+err_req_regions:
+	pci_disable_device(pdev);
+err_pci_enable_dev:
+	dev_dbg(&pdev->dev, "%s returns %d\n", __func__, ret);
+	return ret;
+}
+
+static void __devexit pch_phub_remove(struct pci_dev *pdev)
+{
+	cdev_del(&pch_phub_dev);
+	dev_dbg(&pdev->dev,
+			"%s - cdev_del Invoked successfully\n", __func__);
+
+	unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
+	dev_dbg(&pdev->dev, "%s - "
+		"unregister_chrdev_region Invoked successfully\n", __func__);
+
+	pci_unmap_rom(pdev, pch_phub_reg.pch_phub_extrom_base_address);
+
+	pci_iounmap(pdev, pch_phub_reg.pch_phub_base_address);
+
+	dev_dbg(&pdev->dev, "%s - "
+			"pci_iounmap Invoked successfully\n", __func__);
+
+	pci_release_regions(pdev);
+	dev_dbg(&pdev->dev, "%s - "
+		"pci_release_regions Invoked successfully\n", __func__);
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "%s - "
+		"pci_disable_device Invoked successfully\n", __func__);
+}
+
+#ifdef CONFIG_PM
+
+static int pch_phub_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	int ret;
+
+	pch_phub_reg.pch_phub_suspended = true;/* For blocking further IOCTLs */
+
+	pch_phub_save_reg_conf(pdev);
+	dev_dbg(&pdev->dev, "%s - "
+		"pch_phub_save_reg_conf Invoked successfully\n", __func__);
+
+	ret = pci_save_state(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+			" %s -pci_save_state returns %d\n", __func__, ret);
+		return ret;
+	}
+	dev_dbg(&pdev->dev,
+			"%s - pci_save_state returns %d\n", __func__, ret);
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "%s - "
+			"pci_enable_wake Invoked successfully\n", __func__);
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "%s - "
+			"pci_disable_device Invoked successfully\n", __func__);
+
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+	dev_dbg(&pdev->dev, "%s - "
+			"pci_set_power_state Invoked successfully "
+			"return = 0\n", __func__);
+
+	return 0;
+}
+
+static int pch_phub_resume(struct pci_dev *pdev)
+{
+	int ret;
+
+	pci_set_power_state(pdev, PCI_D0);
+	dev_dbg(&pdev->dev, "%s - "
+		"pci_set_power_state Invoked successfully\n", __func__);
+
+	pci_restore_state(pdev);
+	dev_dbg(&pdev->dev, "%s - "
+		"pci_restore_state Invoked successfully\n", __func__);
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+		"%s-pci_enable_device failed(ret=%d) ", __func__, ret);
+		return ret;
+	}
+
+	dev_dbg(&pdev->dev, "%s - "
+			"pci_enable_device returns (ret=%d)\n", __func__, ret);
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "%s - "
+			"pci_enable_wake Invoked successfully\n", __func__);
+
+	pch_phub_restore_reg_conf(pdev);
+	dev_dbg(&pdev->dev, "%s - "
+		"pch_phub_restore_reg_conf Invoked successfully\n", __func__);
+
+	pch_phub_reg.pch_phub_suspended = false;
+
+	dev_dbg(&pdev->dev, "%s  returns 0\n", __func__);
+	return 0;
+}
+#else
+#define pch_phub_suspend NULL
+#define pch_phub_resume NULL
+#endif /* CONFIG_PM */
+
+static struct pci_device_id pch_phub_pcidev_id[] = {
+
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH1_PHUB)},
+	{0,}
+};
+
+
+static struct pci_driver pch_phub_driver = {
+	.name = "pch_phub",
+	.id_table = pch_phub_pcidev_id,
+	.probe = pch_phub_probe,
+	.remove = __devexit_p(pch_phub_remove),
+	.suspend = pch_phub_suspend,
+	.resume = pch_phub_resume
+};
+
+static int __init pch_phub_pci_init(void)
+{
+	int ret;
+	ret = pci_register_driver(&pch_phub_driver);
+
+	return ret;
+}
+
+static void __exit pch_phub_pci_exit(void)
+{
+	pci_unregister_driver(&pch_phub_driver);
+}
+
+module_init(pch_phub_pci_init);
+module_exit(pch_phub_pci_exit);
+
+MODULE_DESCRIPTION("PCH PACKET HUB PCI Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/char/pch_phub/pch_phub.h b/drivers/char/pch_phub/pch_phub.h
new file mode 100755
index 0000000..bf80f31
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.h
@@ -0,0 +1,48 @@
+#ifndef __PCH_PHUB_H__
+#define __PCH_PHUB_H__
+/*
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#define PHUB_IOCTL_MAGIC		0xf7
+
+/* Read GbE MAC address */
+#define IOCTL_PHUB_READ_MAC_ADDR (_IOR(PHUB_IOCTL_MAGIC, 1, __u8[ETH_ALEN]))
+
+/* Write GbE MAC address */
+#define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 2, __u8[ETH_ALEN]))
+
+/* SROM ACCESS Macro */
+#define PCH_WORD_ADDR_MASK (~((1 << 2) - 1))
+
+/* Registers address offset */
+#define PCH_PHUB_ID_REG				0x0000
+#define PCH_PHUB_QUEUE_PRI_VAL_REG		0x0004
+#define PCH_PHUB_RC_QUEUE_MAXSIZE_REG		0x0008
+#define PCH_PHUB_BRI_QUEUE_MAXSIZE_REG		0x000C
+#define PCH_PHUB_COMP_RESP_TIMEOUT_REG		0x0010
+#define PCH_PHUB_BUS_SLAVE_CONTROL_REG		0x0014
+#define PCH_PHUB_DEADLOCK_AVOID_TYPE_REG	0x0018
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG0	0x0020
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG1	0x0024
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG2	0x0028
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG3	0x002C
+#define PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE	0x0040
+#define CLKCFG_REG_OFFSET			0x500
+
+#define PCH_PHUB_OROM_SIZE 15360
+
+#endif
-- 
1.6.0.6


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

* Re: [PATCH] Packet hub driver of Topcliff PCH
  2010-07-05  7:20 ` Masayuki Ohtak
@ 2010-07-05 15:04   ` Arnd Bergmann
  2010-07-06 15:58   ` Randy Dunlap
  1 sibling, 0 replies; 46+ messages in thread
From: Arnd Bergmann @ 2010-07-05 15:04 UTC (permalink / raw)
  To: Masayuki Ohtak
  Cc: Randy Dunlap, Wang, Yong Y, qi.wang, joel.clark,
	andrew.chih.howe.khor, Alan Cox, LKML

I took another look and found some more things that should be improved.
Overall, the quality of this driver has improved enourmously, and I'm
optimistic that it will be a lot easier for you in your next device
driver with all the details you have learned about the coding style.

On Monday 05 July 2010, Masayuki Ohtak wrote:
 
> +struct pch_phub_reg {
> +	u32 phub_id_reg;
> +	u32 q_pri_val_reg;
> +	u32 rc_q_maxsize_reg;
> +	u32 bri_q_maxsize_reg;
> +	u32 comp_resp_timeout_reg;
> +	u32 bus_slave_control_reg;
> +	u32 deadlock_avoid_type_reg;
> +	u32 intpin_reg_wpermit_reg0;
> +	u32 intpin_reg_wpermit_reg1;
> +	u32 intpin_reg_wpermit_reg2;
> +	u32 intpin_reg_wpermit_reg3;
> +	u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG];
> +	u32 clkcfg_reg;
> +	void __iomem *pch_phub_base_address;
> +	void __iomem *pch_phub_extrom_base_address;
> +	int pch_phub_suspended;
> +} pch_phub_reg;

The variable you define here is in the global namespace, which it
should not be in. I'd suggest making it static and splitting the
type defintion from the variable definition to make that more obvious,
like:

struct pch_phub_reg {
	...
};

static struct pch_phub_reg pch_phub_reg;

> +static DEFINE_MUTEX(pch_phub_ioctl_mutex);
> +static DEFINE_MUTEX(pch_phub_read_mutex);
> +static DEFINE_MUTEX(pch_phub_write_mutex);

Having three mutexes here means that you have effectively no
serialization between the functions at all. The mutex only
has an effect if you use the same one in all three functions!

	Arnd

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

* [PATCH] Packet hub driver of Topcliff PCH
  2010-06-22  5:33 [PATCH] Topcliff PHUB: Generate PacketHub driver Masayuki Ohtak
                   ` (6 preceding siblings ...)
  2010-07-05  7:20 ` Masayuki Ohtak
@ 2010-07-06  6:20 ` Masayuki Ohtak
  2010-07-06  6:30   ` Arnd Bergmann
  2010-07-09 20:00   ` Andrew Morton
  2010-07-15  7:25 ` Masayuki Ohtak
                   ` (2 subsequent siblings)
  10 siblings, 2 replies; 46+ messages in thread
From: Masayuki Ohtak @ 2010-07-06  6:20 UTC (permalink / raw)
  To: Andrew Morton, Arnd Bergmann
  Cc: Wang, Yong Y, qi.wang, joel.clark, andrew.chih.howe.khor, Alan Cox, LKML

Hi Arnd

I have modified for your comments.
Please confirm below.

Thanks, Ohtake.

---
Packet hub driver of Topcliff PCH

Topcliff PCH is the platform controller hub that is going to be used in
Intel's upcoming general embedded platform. All IO peripherals in
Topcliff PCH are actually devices sitting on AMBA bus. Packet hub is
a special converter device in Topcliff PCH that translate AMBA transactions
to PCI Express transactions and vice versa. Thus packet hub helps present
all IO peripherals in Topcliff PCH as PCIE devices to IA system.
Topcliff PCH has MAC address and Option ROM data.
These data are in SROM which is connected to PCIE bus.
Packet hub driver of Topcliff PCH can access MAC address and Option ROM data in
SROM.

Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
---
 Documentation/ioctl/ioctl-number.txt |    1 +
 drivers/char/Kconfig                 |    9 +
 drivers/char/Makefile                |    2 +
 drivers/char/pch_phub/Makefile       |    3 +
 drivers/char/pch_phub/pch_phub.c     |  802 ++++++++++++++++++++++++++++++++++
 drivers/char/pch_phub/pch_phub.h     |   48 ++
 6 files changed, 865 insertions(+), 0 deletions(-)
 create mode 100644 drivers/char/pch_phub/Makefile
 create mode 100755 drivers/char/pch_phub/pch_phub.c
 create mode 100755 drivers/char/pch_phub/pch_phub.h

diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 35cf64d..b700b17 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -320,4 +320,5 @@ Code  Seq#(hex)	Include File		Comments
 					<mailto:thomas@winischhofer.net>
 0xF4	00-1F	video/mbxfb.h		mbxfb
 					<mailto:raph@8d.com>
+0xF7	00-0F	drivers/char/pch_phub/pch_phub.h	PCH Phub driver
 0xFD	all	linux/dm-ioctl.h
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index e023682..499902f 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -4,6 +4,15 @@
 
 menu "Character devices"
 
+config PCH_PHUB
+	tristate "PCH PHUB"
+	depends on PCI
+	help
+	  This driver is for PCH PHUB of Topcliff which is an IOH for x86
+	  embedded processor. The Topcliff has MAC address and Option ROM data
+	  in SROM. This driver can access MAC address and Option ROM data in
+	  SROM.
+
 config VT
 	bool "Virtual terminal" if EMBEDDED
 	depends on !S390
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index f957edf..1e3eb6c 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -111,6 +111,8 @@ obj-$(CONFIG_PS3_FLASH)		+= ps3flash.o
 obj-$(CONFIG_JS_RTC)		+= js-rtc.o
 js-rtc-y = rtc.o
 
+obj-$(CONFIG_PCH_PHUB)	+= pch_phub/
+
 # Files generated that shall be removed upon make clean
 clean-files := consolemap_deftbl.c defkeymap.c
 
diff --git a/drivers/char/pch_phub/Makefile b/drivers/char/pch_phub/Makefile
new file mode 100644
index 0000000..93aaffe
--- /dev/null
+++ b/drivers/char/pch_phub/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_PCH_PHUB) += pch_phub_drv.o
+
+pch_phub_drv-objs := pch_phub.o
diff --git a/drivers/char/pch_phub/pch_phub.c b/drivers/char/pch_phub/pch_phub.c
new file mode 100755
index 0000000..ae89167
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.c
@@ -0,0 +1,802 @@
+/*
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/if_ether.h>
+#include "pch_phub.h"
+
+#define PHUB_STATUS 0x00		/* Status Register offset */
+#define PHUB_CONTROL 0x04		/* Control Register offset */
+#define PHUB_TIMEOUT 0x05		/* Time out value for Status Register */
+#define PCH_PHUB_ROM_WRITE_ENABLE 0x01	/* Enabling for writing ROM */
+#define PCH_PHUB_ROM_WRITE_DISABLE 0x00	/* Disabling for writing ROM */
+#define PCH_PHUB_ROM_START_ADDR 0x14	/* ROM data area start address offset */
+
+/* MAX number of INT_REDUCE_CONTROL registers */
+#define MAX_NUM_INT_REDUCE_CONTROL_REG 128
+#define PCI_DEVICE_ID_PCH1_PHUB 0x8801
+#define PCH_MINOR_NOS 1
+#define CLKCFG_CAN_50MHZ 0x12000000
+#define CLKCFG_CANCLK_MASK 0xFF000000
+#define MODULE_NAME "pch_phub"
+
+/**
+ * struct pch_phub_reg - PHUB register structure
+ * @phub_id_reg:			PHUB_ID register val
+ * @q_pri_val_reg:			QUEUE_PRI_VAL register val
+ * @rc_q_maxsize_reg:			RC_QUEUE_MAXSIZE register val
+ * @bri_q_maxsize_reg:			BRI_QUEUE_MAXSIZE register val
+ * @comp_resp_timeout_reg:		COMP_RESP_TIMEOUT register val
+ * @bus_slave_control_reg:		BUS_SLAVE_CONTROL_REG register val
+ * @deadlock_avoid_type_reg:		DEADLOCK_AVOID_TYPE register val
+ * @intpin_reg_wpermit_reg0:		INTPIN_REG_WPERMIT register 0 val
+ * @intpin_reg_wpermit_reg1:		INTPIN_REG_WPERMIT register 1 val
+ * @intpin_reg_wpermit_reg2:		INTPIN_REG_WPERMIT register 2 val
+ * @intpin_reg_wpermit_reg3:		INTPIN_REG_WPERMIT register 3 val
+ * @int_reduce_control_reg:		INT_REDUCE_CONTROL registers val
+ * @clkcfg_reg:				CLK CFG register val
+ * @pch_phub_base_address:		Register base address
+ * @pch_phub_extrom_base_address:	external rom base address
+ * @pch_phub_suspended:			PHUB status val
+ */
+struct pch_phub_reg {
+	u32 phub_id_reg;
+	u32 q_pri_val_reg;
+	u32 rc_q_maxsize_reg;
+	u32 bri_q_maxsize_reg;
+	u32 comp_resp_timeout_reg;
+	u32 bus_slave_control_reg;
+	u32 deadlock_avoid_type_reg;
+	u32 intpin_reg_wpermit_reg0;
+	u32 intpin_reg_wpermit_reg1;
+	u32 intpin_reg_wpermit_reg2;
+	u32 intpin_reg_wpermit_reg3;
+	u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG];
+	u32 clkcfg_reg;
+	void __iomem *pch_phub_base_address;
+	void __iomem *pch_phub_extrom_base_address;
+	int pch_phub_suspended;
+};
+
+/* SROM SPEC for MAC address assignment offset */
+static const int pch_phub_mac_offset[ETH_ALEN] = {0x3, 0x2, 0x1, 0x0, 0xb, 0xa};
+
+static DEFINE_MUTEX(pch_phub_mutex);
+static dev_t pch_phub_dev_no;
+static struct cdev pch_phub_dev;
+static struct pch_phub_reg pch_phub_reg;
+
+/**
+ * pch_phub_read_modify_write_reg() - Reading modifying and writing register
+ * @reg_addr_offset:	Register offset address value.
+ * @data:		Writing value.
+ * @mask:		Mask value.
+ */
+static void pch_phub_read_modify_write_reg(unsigned int reg_addr_offset,
+					   unsigned int data, unsigned int mask)
+{
+	void __iomem *reg_addr = pch_phub_reg.pch_phub_base_address +
+					 reg_addr_offset;
+	iowrite32(((ioread32(reg_addr) & ~mask)) | data, reg_addr);
+}
+
+
+/* pch_phub_save_reg_conf - saves register configuration */
+static void pch_phub_save_reg_conf(struct pci_dev *pdev)
+{
+	unsigned int i;
+	void __iomem *p = pch_phub_reg.pch_phub_base_address;
+
+	dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
+	pch_phub_reg.phub_id_reg = ioread32(p + PCH_PHUB_ID_REG);
+	pch_phub_reg.q_pri_val_reg = ioread32(p + PCH_PHUB_QUEUE_PRI_VAL_REG);
+	pch_phub_reg.rc_q_maxsize_reg =
+	    ioread32(p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+	pch_phub_reg.bri_q_maxsize_reg =
+	    ioread32(p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+	pch_phub_reg.comp_resp_timeout_reg =
+	    ioread32(p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+	pch_phub_reg.bus_slave_control_reg =
+	    ioread32(p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+	pch_phub_reg.deadlock_avoid_type_reg =
+	    ioread32(p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+	pch_phub_reg.intpin_reg_wpermit_reg0 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+	pch_phub_reg.intpin_reg_wpermit_reg1 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+	pch_phub_reg.intpin_reg_wpermit_reg2 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+	pch_phub_reg.intpin_reg_wpermit_reg3 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
+	dev_dbg(&pdev->dev, "%s : "
+		"pch_phub_reg.phub_id_reg=%x, "
+		"pch_phub_reg.q_pri_val_reg=%x, "
+		"pch_phub_reg.rc_q_maxsize_reg=%x, "
+		"pch_phub_reg.bri_q_maxsize_reg=%x, "
+		"pch_phub_reg.comp_resp_timeout_reg=%x, "
+		"pch_phub_reg.bus_slave_control_reg=%x, "
+		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n", __func__,
+		pch_phub_reg.phub_id_reg,
+		pch_phub_reg.q_pri_val_reg,
+		pch_phub_reg.rc_q_maxsize_reg,
+		pch_phub_reg.bri_q_maxsize_reg,
+		pch_phub_reg.comp_resp_timeout_reg,
+		pch_phub_reg.bus_slave_control_reg,
+		pch_phub_reg.deadlock_avoid_type_reg,
+		pch_phub_reg.intpin_reg_wpermit_reg0,
+		pch_phub_reg.intpin_reg_wpermit_reg1,
+		pch_phub_reg.intpin_reg_wpermit_reg2,
+		pch_phub_reg.intpin_reg_wpermit_reg3);
+	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
+		pch_phub_reg.int_reduce_control_reg[i] =
+		    ioread32(p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
+		dev_dbg(&pdev->dev, "%s : "
+			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
+			__func__, i, pch_phub_reg.int_reduce_control_reg[i]);
+	}
+	pch_phub_reg.clkcfg_reg = ioread32(p + CLKCFG_REG_OFFSET);
+}
+
+/* pch_phub_restore_reg_conf - restore register configuration */
+static void pch_phub_restore_reg_conf(struct pci_dev *pdev)
+{
+	unsigned int i;
+	void __iomem *p = pch_phub_reg.pch_phub_base_address;
+
+	dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
+	iowrite32(pch_phub_reg.phub_id_reg, p + PCH_PHUB_ID_REG);
+	iowrite32(pch_phub_reg.q_pri_val_reg, p + PCH_PHUB_QUEUE_PRI_VAL_REG);
+	iowrite32(pch_phub_reg.rc_q_maxsize_reg,
+					p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+	iowrite32(pch_phub_reg.bri_q_maxsize_reg,
+					p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+	iowrite32(pch_phub_reg.comp_resp_timeout_reg,
+					p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+	iowrite32(pch_phub_reg.bus_slave_control_reg,
+					p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+	iowrite32(pch_phub_reg.deadlock_avoid_type_reg,
+					p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg0,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg1,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg2,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg3,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
+	dev_dbg(&pdev->dev, "%s : "
+		"pch_phub_reg.phub_id_reg=%x, "
+		"pch_phub_reg.q_pri_val_reg=%x, "
+		"pch_phub_reg.rc_q_maxsize_reg=%x, "
+		"pch_phub_reg.bri_q_maxsize_reg=%x, "
+		"pch_phub_reg.comp_resp_timeout_reg=%x, "
+		"pch_phub_reg.bus_slave_control_reg=%x, "
+		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n", __func__,
+		pch_phub_reg.phub_id_reg,
+		pch_phub_reg.q_pri_val_reg,
+		pch_phub_reg.rc_q_maxsize_reg,
+		pch_phub_reg.bri_q_maxsize_reg,
+		pch_phub_reg.comp_resp_timeout_reg,
+		pch_phub_reg.bus_slave_control_reg,
+		pch_phub_reg.deadlock_avoid_type_reg,
+		pch_phub_reg.intpin_reg_wpermit_reg0,
+		pch_phub_reg.intpin_reg_wpermit_reg1,
+		pch_phub_reg.intpin_reg_wpermit_reg2,
+		pch_phub_reg.intpin_reg_wpermit_reg3);
+	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
+		iowrite32(pch_phub_reg.int_reduce_control_reg[i],
+			p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
+		dev_dbg(&pdev->dev, "%s : "
+			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
+			__func__, i, pch_phub_reg.int_reduce_control_reg[i]);
+	}
+
+	iowrite32(pch_phub_reg.clkcfg_reg, p + CLKCFG_REG_OFFSET);
+}
+
+/**
+ * pch_phub_read_serial_rom() - Reading Serial ROM
+ * @offset_address:	Serial ROM offset address to read.
+ * @data:		Read buffer for specified Serial ROM value.
+ */
+static void pch_phub_read_serial_rom(unsigned int offset_address, u8 *data)
+{
+	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +
+					 offset_address;
+
+	*data = ioread8(mem_addr);
+}
+
+/**
+ * pch_phub_write_serial_rom() - Writing Serial ROM
+ * @offset_address:	Serial ROM offset address.
+ * @data:		Serial ROM value to write.
+ */
+static int pch_phub_write_serial_rom(unsigned int offset_address, u8 data)
+{
+	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +
+					(offset_address & PCH_WORD_ADDR_MASK);
+	int i;
+	unsigned int word_data;
+	unsigned int pos;
+	unsigned int mask;
+	pos = (offset_address % 4) * 8;
+	mask = ~(0xFF << pos);
+
+	iowrite32(PCH_PHUB_ROM_WRITE_ENABLE,
+			pch_phub_reg.pch_phub_extrom_base_address +
+			PHUB_CONTROL);
+
+	word_data = ioread32(mem_addr);
+	iowrite32((word_data & mask) | (u32)data << pos, mem_addr);
+
+	i = 0;
+	while (ioread8(pch_phub_reg.pch_phub_extrom_base_address +
+						PHUB_STATUS) != 0x00) {
+		msleep(1);
+		if (PHUB_TIMEOUT == i)
+			return -EPERM;
+		i++;
+	}
+
+	iowrite32(PCH_PHUB_ROM_WRITE_DISABLE,
+			pch_phub_reg.pch_phub_extrom_base_address +
+			PHUB_CONTROL);
+
+	return 0;
+}
+
+/**
+ * pch_phub_read_serial_rom_val() - Read Serial ROM value
+ * @offset_address:	Serial ROM address offset value.
+ * @data:		Serial ROM value to read.
+ */
+static void pch_phub_read_serial_rom_val(unsigned int offset_address, u8 *data)
+{
+	unsigned int mem_addr;
+
+	mem_addr = PCH_PHUB_ROM_START_ADDR +
+			pch_phub_mac_offset[offset_address];
+
+	pch_phub_read_serial_rom(mem_addr, data);
+}
+
+
+/**
+ * pch_phub_write_serial_rom_val() - writing Serial ROM value
+ * @offset_address:	Serial ROM address offset value.
+ * @data:		Serial ROM value.
+ */
+static int pch_phub_write_serial_rom_val(unsigned int offset_address, u8 data)
+{
+	int retval;
+	unsigned int mem_addr;
+
+	mem_addr = PCH_PHUB_ROM_START_ADDR +
+			pch_phub_mac_offset[offset_address];
+
+	retval = pch_phub_write_serial_rom(mem_addr, data);
+
+	return retval;
+}
+
+/* pch_phub_gbe_serial_rom_conf - makes Serial ROM header format configuration
+ * for Gigabit Ethernet MAC address
+ */
+static int pch_phub_gbe_serial_rom_conf(void)
+{
+	int retval;
+
+	retval = pch_phub_write_serial_rom(0x0b, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x0a, 0x10);
+	retval |= pch_phub_write_serial_rom(0x09, 0x01);
+	retval |= pch_phub_write_serial_rom(0x08, 0x02);
+
+	retval |= pch_phub_write_serial_rom(0x0f, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0e, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0d, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0c, 0x80);
+
+	retval |= pch_phub_write_serial_rom(0x13, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x12, 0x10);
+	retval |= pch_phub_write_serial_rom(0x11, 0x01);
+	retval |= pch_phub_write_serial_rom(0x10, 0x18);
+
+	retval |= pch_phub_write_serial_rom(0x1b, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x1a, 0x10);
+	retval |= pch_phub_write_serial_rom(0x19, 0x01);
+	retval |= pch_phub_write_serial_rom(0x18, 0x19);
+
+	retval |= pch_phub_write_serial_rom(0x23, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x22, 0x10);
+	retval |= pch_phub_write_serial_rom(0x21, 0x01);
+	retval |= pch_phub_write_serial_rom(0x20, 0x3a);
+
+	retval |= pch_phub_write_serial_rom(0x27, 0x01);
+	retval |= pch_phub_write_serial_rom(0x26, 0x00);
+	retval |= pch_phub_write_serial_rom(0x25, 0x00);
+	retval |= pch_phub_write_serial_rom(0x24, 0x00);
+
+	return retval;
+}
+
+/**
+ * pch_phub_read_gbe_mac_addr() - Read Gigabit Ethernet MAC address
+ * @offset_address:	Gigabit Ethernet MAC address offset value.
+ * @data:		Buffer of the Gigabit Ethernet MAC address value.
+ */
+static void pch_phub_read_gbe_mac_addr(u8 *data)
+{
+	int i;
+	for (i = 0; i < ETH_ALEN; i++)
+		pch_phub_read_serial_rom_val(i, &data[i]);
+}
+
+/**
+ * pch_phub_write_gbe_mac_addr() - Write MAC address
+ * @offset_address:	Gigabit Ethernet MAC address offset value.
+ * @data:		Gigabit Ethernet MAC address value.
+ */
+static int pch_phub_write_gbe_mac_addr(u8 *data)
+{
+	int retval;
+	int i;
+
+	retval = pch_phub_gbe_serial_rom_conf();
+	if (retval)
+		return retval;
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		retval = pch_phub_write_serial_rom_val(i, data[i]);
+		if (retval)
+			return retval;
+	}
+
+	return retval;
+}
+
+static ssize_t pch_phub_read(struct file *file, char __user *buf, size_t size,
+			     loff_t *ppos)
+{
+	unsigned int rom_signature;
+	unsigned char rom_length;
+	int ret_value;
+	unsigned int tmp;
+	unsigned char data;
+	unsigned int addr_offset;
+	unsigned int orom_size;
+	int ret;
+	int err;
+	loff_t pos = *ppos;
+
+	ret = mutex_lock_interruptible(&pch_phub_mutex);
+	if (ret) {
+		err = -ERESTARTSYS;
+		goto return_err_nomutex;
+	}
+
+	/* Get Rom signature */
+	pch_phub_read_serial_rom(0x80, (unsigned char *)&rom_signature);
+	rom_signature &= 0xff;
+	pch_phub_read_serial_rom(0x81, (unsigned char *)&tmp);
+	rom_signature |= (tmp & 0xff) << 8;
+	if (rom_signature == 0xAA55) {
+		pch_phub_read_serial_rom(0x82, &rom_length);
+		orom_size = rom_length * 512;
+		if (orom_size < pos) {
+			addr_offset = 0;
+			goto return_ok;
+		}
+
+		for (addr_offset = 0; addr_offset < size; addr_offset++) {
+			pch_phub_read_serial_rom(0x80 + addr_offset + pos,
+									 &data);
+			ret_value = copy_to_user(&buf[addr_offset], &data, 1);
+			if (ret_value) {
+				err = -EFAULT;
+				goto return_err;
+			}
+
+			if (orom_size < pos + addr_offset) {
+				*ppos += addr_offset;
+				goto return_ok;
+			}
+
+		}
+	} else {
+		err = -ENODATA;
+		goto return_err;
+	}
+	*ppos += addr_offset;
+return_ok:
+	mutex_unlock(&pch_phub_mutex);
+	return addr_offset;
+
+return_err:
+	mutex_unlock(&pch_phub_mutex);
+return_err_nomutex:
+	return err;
+}
+
+
+static ssize_t pch_phub_write(struct file *file, const char __user *buf,
+			      size_t size, loff_t *ppos)
+{
+	unsigned int data;
+	int ret_value1;
+	int ret_value2;
+	int err;
+	unsigned int addr_offset;
+	loff_t pos = *ppos;
+	int ret;
+
+	ret = mutex_lock_interruptible(&pch_phub_mutex);
+	if (ret) {
+		err = -ERESTARTSYS;
+		goto return_err_nomutex;
+	}
+
+	for (addr_offset = 0; addr_offset < size; addr_offset++) {
+		ret_value1 = get_user(data, &buf[addr_offset]);
+		if (ret_value1) {
+			err = -EFAULT;
+			goto return_err;
+		}
+
+		ret_value2 = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
+							data);
+		if (ret_value2) {
+			err = ret_value2;
+			goto return_err;
+		}
+
+		if (PCH_PHUB_OROM_SIZE < pos + addr_offset) {
+			*ppos += addr_offset;
+			goto return_ok;
+		}
+
+	}
+
+	*ppos += addr_offset;
+
+return_ok:
+	mutex_unlock(&pch_phub_mutex);
+	return addr_offset;
+
+return_err:
+	mutex_unlock(&pch_phub_mutex);
+return_err_nomutex:
+	return err;
+}
+
+
+static long pch_phub_ioctl(struct file *file, unsigned int cmd,
+			   unsigned long arg)
+{
+	int ret_value;
+	int ret;
+	int rtn;
+	__u8 mac_addr[ETH_ALEN];
+	void __user *varg = (void __user *)arg;
+
+	ret = mutex_lock_interruptible(&pch_phub_mutex);
+	if (ret) {
+		ret_value = -ERESTARTSYS;
+		goto return_nomutex;
+	}
+
+	if (pch_phub_reg.pch_phub_suspended == true) {
+		ret_value = -EBUSY;
+		goto return_ioctrl;
+	}
+
+	switch (cmd) {
+	case IOCTL_PHUB_READ_MAC_ADDR:
+		pch_phub_read_gbe_mac_addr(mac_addr);
+
+		ret_value = copy_to_user(varg, mac_addr, ETH_ALEN);
+		break;
+
+	case IOCTL_PHUB_WRITE_MAC_ADDR:
+		rtn = copy_from_user(mac_addr, varg, ETH_ALEN);
+
+		if (rtn) {
+			ret_value = -EFAULT;
+			break;
+		}
+
+		ret_value = pch_phub_write_gbe_mac_addr(mac_addr);
+		break;
+
+	default:
+		ret_value = -EINVAL;
+		break;
+	}
+return_ioctrl:
+	mutex_unlock(&pch_phub_mutex);
+return_nomutex:
+	return ret_value;
+}
+
+
+/* file_operations structure initialization */
+static const struct file_operations pch_phub_fops = {
+	.owner = THIS_MODULE,
+	.read = pch_phub_read,
+	.write = pch_phub_write,
+	.unlocked_ioctl = pch_phub_ioctl,
+	.llseek = default_llseek
+};
+
+
+static int __devinit pch_phub_probe(struct pci_dev *pdev,
+				    const struct pci_device_id *id)
+{
+	int ret;
+	unsigned int rom_size;
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+		"%s : pci_enable_device FAILED(ret=%d)", __func__, ret);
+		goto err_pci_enable_dev;
+	}
+	dev_dbg(&pdev->dev, "%s : pci_enable_device returns %d\n", __func__,
+		ret);
+
+	ret = pci_request_regions(pdev, MODULE_NAME);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+		"%s : pci_request_regions FAILED(ret=%d)", __func__, ret);
+		goto err_req_regions;
+	}
+	dev_dbg(&pdev->dev, "%s : "
+		"pci_request_regions returns %d\n", __func__, ret);
+
+	pch_phub_reg.pch_phub_base_address = pci_iomap(pdev, 1, 0);
+
+	if (pch_phub_reg.pch_phub_base_address == 0) {
+		dev_dbg(&pdev->dev, "%s : pci_iomap FAILED", __func__);
+		ret = -ENOMEM;
+		goto err_pci_iomap;
+	}
+	dev_dbg(&pdev->dev, "%s : pci_iomap SUCCESS and value "
+		"in pch_phub_base_address variable is 0x%08x\n", __func__,
+		(unsigned int)pch_phub_reg.pch_phub_base_address);
+
+	pch_phub_reg.pch_phub_extrom_base_address =
+						pci_map_rom(pdev, &rom_size);
+	if (pch_phub_reg.pch_phub_extrom_base_address == 0) {
+		dev_dbg(&pdev->dev, "%s : pci_map_rom FAILED", __func__);
+		ret = -ENOMEM;
+		goto err_pci_map;
+	}
+	dev_dbg(&pdev->dev, "%s : "
+		"pci_map_rom SUCCESS and value in "
+		"pch_phub_extrom_base_address variable is 0x%08x\n", __func__,
+		(unsigned int)pch_phub_reg.pch_phub_extrom_base_address);
+
+	ret = alloc_chrdev_region(&pch_phub_dev_no, 0,
+					PCH_MINOR_NOS, MODULE_NAME);
+	if (ret) {
+		dev_dbg(&pdev->dev, "%s : "
+			"alloc_chrdev_region FAILED(ret=%d)", __func__, ret);
+
+		goto err_alloc_cdev;
+	}
+	dev_dbg(&pdev->dev, "%s : "
+		"alloc_chrdev_region returns %d\n", __func__, ret);
+
+	cdev_init(&pch_phub_dev, &pch_phub_fops);
+	dev_dbg(&pdev->dev,
+			"%s : cdev_init invoked successfully\n", __func__);
+
+	pch_phub_dev.owner = THIS_MODULE;
+	pch_phub_dev.ops = &pch_phub_fops;
+
+	ret = cdev_add(&pch_phub_dev, pch_phub_dev_no, PCH_MINOR_NOS);
+	if (ret) {
+		dev_dbg(&pdev->dev, "%s :  cdev_add FAILED(ret=%d)",
+			__func__, ret);
+		goto err_cdev_add;
+	}
+	dev_dbg(&pdev->dev, "%s :  cdev_add returns %d\n", __func__, ret);
+
+	/* set the clock config reg if CAN clock is 50Mhz */
+	dev_dbg(&pdev->dev, "%s : invoking "
+		"pch_phub_read_modify_write_reg "
+		"to set CLKCFG reg for CAN clk 50Mhz\n", __func__);
+	pch_phub_read_modify_write_reg((unsigned int)CLKCFG_REG_OFFSET,
+					  CLKCFG_CAN_50MHZ, CLKCFG_CANCLK_MASK);
+
+	/* set the prefech value */
+	iowrite32(0x000ffffa, pch_phub_reg.pch_phub_base_address + 0x14);
+	/* set the interrupt delay value */
+	iowrite32(0x25, pch_phub_reg.pch_phub_base_address + 0x44);
+	return 0;
+
+err_cdev_add:
+	unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
+err_alloc_cdev:
+	pci_unmap_rom(pdev, pch_phub_reg.pch_phub_extrom_base_address);
+err_pci_map:
+	pci_iounmap(pdev, pch_phub_reg.pch_phub_base_address);
+err_pci_iomap:
+	pci_release_regions(pdev);
+err_req_regions:
+	pci_disable_device(pdev);
+err_pci_enable_dev:
+	dev_dbg(&pdev->dev, "%s returns %d\n", __func__, ret);
+	return ret;
+}
+
+static void __devexit pch_phub_remove(struct pci_dev *pdev)
+{
+	cdev_del(&pch_phub_dev);
+	dev_dbg(&pdev->dev,
+			"%s - cdev_del Invoked successfully\n", __func__);
+
+	unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
+	dev_dbg(&pdev->dev, "%s - "
+		"unregister_chrdev_region Invoked successfully\n", __func__);
+
+	pci_unmap_rom(pdev, pch_phub_reg.pch_phub_extrom_base_address);
+
+	pci_iounmap(pdev, pch_phub_reg.pch_phub_base_address);
+
+	dev_dbg(&pdev->dev, "%s - "
+			"pci_iounmap Invoked successfully\n", __func__);
+
+	pci_release_regions(pdev);
+	dev_dbg(&pdev->dev, "%s - "
+		"pci_release_regions Invoked successfully\n", __func__);
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "%s - "
+		"pci_disable_device Invoked successfully\n", __func__);
+}
+
+#ifdef CONFIG_PM
+
+static int pch_phub_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	int ret;
+
+	pch_phub_reg.pch_phub_suspended = true;/* For blocking further IOCTLs */
+
+	pch_phub_save_reg_conf(pdev);
+	dev_dbg(&pdev->dev, "%s - "
+		"pch_phub_save_reg_conf Invoked successfully\n", __func__);
+
+	ret = pci_save_state(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+			" %s -pci_save_state returns %d\n", __func__, ret);
+		return ret;
+	}
+	dev_dbg(&pdev->dev,
+			"%s - pci_save_state returns %d\n", __func__, ret);
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "%s - "
+			"pci_enable_wake Invoked successfully\n", __func__);
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "%s - "
+			"pci_disable_device Invoked successfully\n", __func__);
+
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+	dev_dbg(&pdev->dev, "%s - "
+			"pci_set_power_state Invoked successfully "
+			"return = 0\n", __func__);
+
+	return 0;
+}
+
+static int pch_phub_resume(struct pci_dev *pdev)
+{
+	int ret;
+
+	pci_set_power_state(pdev, PCI_D0);
+	dev_dbg(&pdev->dev, "%s - "
+		"pci_set_power_state Invoked successfully\n", __func__);
+
+	pci_restore_state(pdev);
+	dev_dbg(&pdev->dev, "%s - "
+		"pci_restore_state Invoked successfully\n", __func__);
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+		"%s-pci_enable_device failed(ret=%d) ", __func__, ret);
+		return ret;
+	}
+
+	dev_dbg(&pdev->dev, "%s - "
+			"pci_enable_device returns (ret=%d)\n", __func__, ret);
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "%s - "
+			"pci_enable_wake Invoked successfully\n", __func__);
+
+	pch_phub_restore_reg_conf(pdev);
+	dev_dbg(&pdev->dev, "%s - "
+		"pch_phub_restore_reg_conf Invoked successfully\n", __func__);
+
+	pch_phub_reg.pch_phub_suspended = false;
+
+	dev_dbg(&pdev->dev, "%s  returns 0\n", __func__);
+	return 0;
+}
+#else
+#define pch_phub_suspend NULL
+#define pch_phub_resume NULL
+#endif /* CONFIG_PM */
+
+static struct pci_device_id pch_phub_pcidev_id[] = {
+
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH1_PHUB)},
+	{0,}
+};
+
+
+static struct pci_driver pch_phub_driver = {
+	.name = "pch_phub",
+	.id_table = pch_phub_pcidev_id,
+	.probe = pch_phub_probe,
+	.remove = __devexit_p(pch_phub_remove),
+	.suspend = pch_phub_suspend,
+	.resume = pch_phub_resume
+};
+
+static int __init pch_phub_pci_init(void)
+{
+	int ret;
+	ret = pci_register_driver(&pch_phub_driver);
+
+	return ret;
+}
+
+static void __exit pch_phub_pci_exit(void)
+{
+	pci_unregister_driver(&pch_phub_driver);
+}
+
+module_init(pch_phub_pci_init);
+module_exit(pch_phub_pci_exit);
+
+MODULE_DESCRIPTION("PCH PACKET HUB PCI Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/char/pch_phub/pch_phub.h b/drivers/char/pch_phub/pch_phub.h
new file mode 100755
index 0000000..bf80f31
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.h
@@ -0,0 +1,48 @@
+#ifndef __PCH_PHUB_H__
+#define __PCH_PHUB_H__
+/*
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#define PHUB_IOCTL_MAGIC		0xf7
+
+/* Read GbE MAC address */
+#define IOCTL_PHUB_READ_MAC_ADDR (_IOR(PHUB_IOCTL_MAGIC, 1, __u8[ETH_ALEN]))
+
+/* Write GbE MAC address */
+#define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 2, __u8[ETH_ALEN]))
+
+/* SROM ACCESS Macro */
+#define PCH_WORD_ADDR_MASK (~((1 << 2) - 1))
+
+/* Registers address offset */
+#define PCH_PHUB_ID_REG				0x0000
+#define PCH_PHUB_QUEUE_PRI_VAL_REG		0x0004
+#define PCH_PHUB_RC_QUEUE_MAXSIZE_REG		0x0008
+#define PCH_PHUB_BRI_QUEUE_MAXSIZE_REG		0x000C
+#define PCH_PHUB_COMP_RESP_TIMEOUT_REG		0x0010
+#define PCH_PHUB_BUS_SLAVE_CONTROL_REG		0x0014
+#define PCH_PHUB_DEADLOCK_AVOID_TYPE_REG	0x0018
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG0	0x0020
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG1	0x0024
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG2	0x0028
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG3	0x002C
+#define PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE	0x0040
+#define CLKCFG_REG_OFFSET			0x500
+
+#define PCH_PHUB_OROM_SIZE 15360
+
+#endif
-- 
1.6.0.6


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

* Re: [PATCH] Packet hub driver of Topcliff PCH
  2010-07-06  6:20 ` Masayuki Ohtak
@ 2010-07-06  6:30   ` Arnd Bergmann
  2010-07-07  1:19     ` Yong Wang
  2010-07-09 20:00   ` Andrew Morton
  1 sibling, 1 reply; 46+ messages in thread
From: Arnd Bergmann @ 2010-07-06  6:30 UTC (permalink / raw)
  To: Masayuki Ohtak
  Cc: Andrew Morton, Wang, Yong Y, qi.wang, joel.clark,
	andrew.chih.howe.khor, Alan Cox, LKML

On Tuesday 06 July 2010 08:20:52 Masayuki Ohtak wrote:
> Hi Arnd
> 
> I have modified for your comments.
> Please confirm below.
> 

Yes, looks good. Thanks for the quick reply.

	Arnd

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

* Re: [PATCH] Packet hub driver of Topcliff PCH
  2010-07-05  7:20 ` Masayuki Ohtak
  2010-07-05 15:04   ` Arnd Bergmann
@ 2010-07-06 15:58   ` Randy Dunlap
  1 sibling, 0 replies; 46+ messages in thread
From: Randy Dunlap @ 2010-07-06 15:58 UTC (permalink / raw)
  To: Masayuki Ohtak
  Cc: Arnd Bergmann, Wang, Yong Y, qi.wang, joel.clark,
	andrew.chih.howe.khor, Alan Cox, LKML

On 07/05/10 00:20, Masayuki Ohtak wrote:
> Hi Randy
> 
> I have modified for your comments.
> Please confirm below.

Agreed, thanks for the updates.

> Thanks, Ohtake.
> 
> ---
> 
> Packet hub driver of Topcliff PCH
> 
> Topcliff PCH is the platform controller hub that is going to be used in
> Intel's upcoming general embedded platform. All IO peripherals in
> Topcliff PCH are actually devices sitting on AMBA bus. Packet hub is
> a special converter device in Topcliff PCH that translate AMBA transactions
> to PCI Express transactions and vice versa. Thus packet hub helps present
> all IO peripherals in Topcliff PCH as PCIE devices to IA system.
> Topcliff PCH has MAC address and Option ROM data.
> These data are in SROM which is connected to PCIE bus.
> Packet hub driver of Topcliff PCH can access MAC address and Option ROM data in
> SROM.
> 
> Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
> 
> ---
>  Documentation/ioctl/ioctl-number.txt |    1 +
>  drivers/char/Kconfig                 |    9 +
>  drivers/char/Makefile                |    2 +
>  drivers/char/pch_phub/Makefile       |    3 +
>  drivers/char/pch_phub/pch_phub.c     |  803 ++++++++++++++++++++++++++++++++++
>  drivers/char/pch_phub/pch_phub.h     |   48 ++
>  6 files changed, 866 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/char/pch_phub/Makefile
>  create mode 100755 drivers/char/pch_phub/pch_phub.c
>  create mode 100755 drivers/char/pch_phub/pch_phub.h
> 
> diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
> index 35cf64d..b700b17 100644
> --- a/Documentation/ioctl/ioctl-number.txt
> +++ b/Documentation/ioctl/ioctl-number.txt
> @@ -320,4 +320,5 @@ Code  Seq#(hex)	Include File		Comments
>  					<mailto:thomas@winischhofer.net>
>  0xF4	00-1F	video/mbxfb.h		mbxfb
>  					<mailto:raph@8d.com>
> +0xF7	00-0F	drivers/char/pch_phub/pch_phub.h	PCH Phub driver
>  0xFD	all	linux/dm-ioctl.h



-- 
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***

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

* Re: [PATCH] Packet hub driver of Topcliff PCH
  2010-07-06  6:30   ` Arnd Bergmann
@ 2010-07-07  1:19     ` Yong Wang
  0 siblings, 0 replies; 46+ messages in thread
From: Yong Wang @ 2010-07-07  1:19 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Masayuki Ohtak, Andrew Morton, Wang, Yong Y, qi.wang, joel.clark,
	andrew.chih.howe.khor, Alan Cox, Arnd Bergmann, LKML

On Tue, Jul 06, 2010 at 08:30:02AM +0200, Arnd Bergmann wrote:
> On Tuesday 06 July 2010 08:20:52 Masayuki Ohtak wrote:
> > Hi Arnd
> > 
> > I have modified for your comments.
> > Please confirm below.
> > 
> 
> Yes, looks good. Thanks for the quick reply.
> 
> 	Arnd

Hi Andrew,

Do you have any further comments on this patch? If so, please let us
know.

Thanks
-Yong


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

* Re: [PATCH] Packet hub driver of Topcliff PCH
  2010-07-06  6:20 ` Masayuki Ohtak
  2010-07-06  6:30   ` Arnd Bergmann
@ 2010-07-09 20:00   ` Andrew Morton
  2010-07-12  1:25     ` Masayuki Ohtake
  1 sibling, 1 reply; 46+ messages in thread
From: Andrew Morton @ 2010-07-09 20:00 UTC (permalink / raw)
  To: Masayuki Ohtak
  Cc: Arnd Bergmann, Wang, Yong Y, qi.wang, joel.clark,
	andrew.chih.howe.khor, Alan Cox, LKML

On Tue, 06 Jul 2010 15:20:52 +0900
Masayuki Ohtak <masa-korg@dsn.okisemi.com> wrote:

> Hi Arnd
> 
> I have modified for your comments.
> Please confirm below.
> 
> Thanks, Ohtake.
> 
> ---
> Packet hub driver of Topcliff PCH
> 
> Topcliff PCH is the platform controller hub that is going to be used in
> Intel's upcoming general embedded platform. All IO peripherals in
> Topcliff PCH are actually devices sitting on AMBA bus. Packet hub is
> a special converter device in Topcliff PCH that translate AMBA transactions
> to PCI Express transactions and vice versa. Thus packet hub helps present
> all IO peripherals in Topcliff PCH as PCIE devices to IA system.
> Topcliff PCH has MAC address and Option ROM data.
> These data are in SROM which is connected to PCIE bus.
> Packet hub driver of Topcliff PCH can access MAC address and Option ROM data in
> SROM.

That didn't describe the most important part of the driver: the
userspace interface.  We should add here something along the lines of

The driver creates a character device /dev/pch_phub.  That device file
supports the following operations:

read(): <document the read operation - seems to read a serial ROM?>
write():<document the write operation - seems to write a serial ROM?> 
ioctl():<document the ioctl operation - seems to read/write a MAC address?>  

>
> ...
>
> +static ssize_t pch_phub_write(struct file *file, const char __user *buf,
> +			      size_t size, loff_t *ppos)
> +{
> +	unsigned int data;
> +	int ret_value1;
> +	int ret_value2;
> +	int err;
> +	unsigned int addr_offset;
> +	loff_t pos = *ppos;
> +	int ret;
> +
> +	ret = mutex_lock_interruptible(&pch_phub_mutex);
> +	if (ret) {
> +		err = -ERESTARTSYS;
> +		goto return_err_nomutex;
> +	}
> +
> +	for (addr_offset = 0; addr_offset < size; addr_offset++) {
> +		ret_value1 = get_user(data, &buf[addr_offset]);
> +		if (ret_value1) {
> +			err = -EFAULT;
> +			goto return_err;
> +		}
> +
> +		ret_value2 = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
> +							data);

I suspect this function will do strange things if passed an initial
*ppos which is outside the range of the ROM.  It looks like it will write
a single byte into the ROM then will bale out.


> +		if (ret_value2) {
> +			err = ret_value2;
> +			goto return_err;
> +		}
> +
> +		if (PCH_PHUB_OROM_SIZE < pos + addr_offset) {

Is this off-by-one?

> +			*ppos += addr_offset;
> +			goto return_ok;
> +		}
> +
> +	}
> +
> +	*ppos += addr_offset;
> +
> +return_ok:
> +	mutex_unlock(&pch_phub_mutex);
> +	return addr_offset;
> +
> +return_err:
> +	mutex_unlock(&pch_phub_mutex);
> +return_err_nomutex:
> +	return err;
> +}
>
> ...
>

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

* Re: [PATCH] Packet hub driver of Topcliff PCH
  2010-07-09 20:00   ` Andrew Morton
@ 2010-07-12  1:25     ` Masayuki Ohtake
  0 siblings, 0 replies; 46+ messages in thread
From: Masayuki Ohtake @ 2010-07-12  1:25 UTC (permalink / raw)
  To: Andrew Morton
  Cc: LKML, Alan Cox, andrew.chih.howe.khor, Intel OTC, Wang, Qi, Wang,
	Yong Y, Arnd Bergmann

Hi Andrew Morton

> The driver creates a character device /dev/pch_phub.  That device file
> supports the following operations:
>
> read(): <document the read operation - seems to read a serial ROM?>
> write():<document the write operation - seems to write a serial ROM?>
> ioctl():<document the ioctl operation - seems to read/write a MAC address?>
We will add the above.


> I suspect this function will do strange things if passed an initial
> *ppos which is outside the range of the ROM.  It looks like it will write
> a single byte into the ROM then will bale out.
>
>
> > + if (ret_value2) {
> > + err = ret_value2;
> > + goto return_err;
> > + }
> > +
> > + if (PCH_PHUB_OROM_SIZE < pos + addr_offset) {
>
> Is this off-by-one?

I understand OROM upper size check is not enough.
If the my understanding true, We will modify like below.
Can  you accept the following our modification ?

+static ssize_t pch_phub_write(struct file *file, const char __user *buf,
+       size_t size, loff_t *ppos)
+{
+ unsigned int data;
+ int ret_value1;
+ int ret_value2;
+ int err;
+ unsigned int addr_offset;
+ loff_t pos = *ppos;
+ int ret;
+
+ ret = mutex_lock_interruptible(&pch_phub_mutex);
+ if (ret) {
+ err = -ERESTARTSYS;
+ goto return_err_nomutex;
+ }
+
+ for (addr_offset = 0; addr_offset < size; addr_offset++) {
+ if (PCH_PHUB_OROM_SIZE < pos + addr_offset) {
+ *ppos += addr_offset;
+ goto return_ok;
+ }
+ ret_value1 = get_user(data, &buf[addr_offset]);
+ if (ret_value1) {
+ err = -EFAULT;
+ goto return_err;
+ }
+
+ ret_value2 = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
+ data);
+ if (ret_value2) {
+ err = ret_value2;
+ goto return_err;
+ }
+


Thanks, Ohtake

----- Original Message ----- 
From: "Andrew Morton" <akpm@linux-foundation.org>
To: "Masayuki Ohtak" <masa-korg@dsn.okisemi.com>
Cc: "Arnd Bergmann" <arnd@arndb.de>; "Wang, Yong Y" <yong.y.wang@intel.com>; <qi.wang@intel.com>;
<joel.clark@intel.com>; <andrew.chih.howe.khor@intel.com>; "Alan Cox" <alan@lxorguk.ukuu.org.uk>; "LKML"
<linux-kernel@vger.kernel.org>
Sent: Saturday, July 10, 2010 5:00 AM
Subject: Re: [PATCH] Packet hub driver of Topcliff PCH


> On Tue, 06 Jul 2010 15:20:52 +0900
> Masayuki Ohtak <masa-korg@dsn.okisemi.com> wrote:
>
> > Hi Arnd
> >
> > I have modified for your comments.
> > Please confirm below.
> >
> > Thanks, Ohtake.
> >
> > ---
> > Packet hub driver of Topcliff PCH
> >
> > Topcliff PCH is the platform controller hub that is going to be used in
> > Intel's upcoming general embedded platform. All IO peripherals in
> > Topcliff PCH are actually devices sitting on AMBA bus. Packet hub is
> > a special converter device in Topcliff PCH that translate AMBA transactions
> > to PCI Express transactions and vice versa. Thus packet hub helps present
> > all IO peripherals in Topcliff PCH as PCIE devices to IA system.
> > Topcliff PCH has MAC address and Option ROM data.
> > These data are in SROM which is connected to PCIE bus.
> > Packet hub driver of Topcliff PCH can access MAC address and Option ROM data in
> > SROM.
>
> That didn't describe the most important part of the driver: the
> userspace interface.  We should add here something along the lines of
>
> The driver creates a character device /dev/pch_phub.  That device file
> supports the following operations:
>
> read(): <document the read operation - seems to read a serial ROM?>
> write():<document the write operation - seems to write a serial ROM?>
> ioctl():<document the ioctl operation - seems to read/write a MAC address?>
>
> >
> > ...
> >
> > +static ssize_t pch_phub_write(struct file *file, const char __user *buf,
> > +       size_t size, loff_t *ppos)
> > +{
> > + unsigned int data;
> > + int ret_value1;
> > + int ret_value2;
> > + int err;
> > + unsigned int addr_offset;
> > + loff_t pos = *ppos;
> > + int ret;
> > +
> > + ret = mutex_lock_interruptible(&pch_phub_mutex);
> > + if (ret) {
> > + err = -ERESTARTSYS;
> > + goto return_err_nomutex;
> > + }
> > +
> > + for (addr_offset = 0; addr_offset < size; addr_offset++) {
> > + ret_value1 = get_user(data, &buf[addr_offset]);
> > + if (ret_value1) {
> > + err = -EFAULT;
> > + goto return_err;
> > + }
> > +
> > + ret_value2 = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
> > + data);
>
> I suspect this function will do strange things if passed an initial
> *ppos which is outside the range of the ROM.  It looks like it will write
> a single byte into the ROM then will bale out.
>
>
> > + if (ret_value2) {
> > + err = ret_value2;
> > + goto return_err;
> > + }
> > +
> > + if (PCH_PHUB_OROM_SIZE < pos + addr_offset) {
>
> Is this off-by-one?
>
> > + *ppos += addr_offset;
> > + goto return_ok;
> > + }
> > +
> > + }
> > +
> > + *ppos += addr_offset;
> > +
> > +return_ok:
> > + mutex_unlock(&pch_phub_mutex);
> > + return addr_offset;
> > +
> > +return_err:
> > + mutex_unlock(&pch_phub_mutex);
> > +return_err_nomutex:
> > + return err;
> > +}
> >
> > ...
> >
>




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

* [PATCH] Packet hub driver of Topcliff PCH
  2010-06-22  5:33 [PATCH] Topcliff PHUB: Generate PacketHub driver Masayuki Ohtak
                   ` (7 preceding siblings ...)
  2010-07-06  6:20 ` Masayuki Ohtak
@ 2010-07-15  7:25 ` Masayuki Ohtak
  2010-07-15  7:42 ` [PATCH] I2C " Masayuki Ohtak
  2010-07-21  6:46   ` Masayuki Ohtak
  10 siblings, 0 replies; 46+ messages in thread
From: Masayuki Ohtak @ 2010-07-15  7:25 UTC (permalink / raw)
  To: Andrew Morton, LKML
  Cc: Arnd Bergmann, Wang, Yong Y, qi.wang, joel.clark,
	andrew.chih.howe.khor, Alan Cox

Hi Andrew Morton

We have modified our packet hub driver for your indication.
Please confirm below.

Thanks, Ohtake.

---
Packet hub driver of Topcliff PCH

Topcliff PCH is the platform controller hub that is going to be used in
Intel's upcoming general embedded platform. All IO peripherals in
Topcliff PCH are actually devices sitting on AMBA bus. Packet hub is
a special converter device in Topcliff PCH that translate AMBA transactions
to PCI Express transactions and vice versa. Thus packet hub helps present
all IO peripherals in Topcliff PCH as PCIE devices to IA system.
Topcliff PCH has MAC address and Option ROM data.
These data are in SROM which is connected to PCIE bus.
Packet hub driver of Topcliff PCH can access MAC address and Option ROM data in
SROM.
The driver creates a character device /dev/pch_phub. That device file
supports the following operations:

read() :Read Option ROM data of SROM
write():Write Option ROM data of SROM
ioctl():Read/Write MAC address of SROM

Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>

---
 Documentation/ioctl/ioctl-number.txt |    1 +
 drivers/char/Kconfig                 |    9 +
 drivers/char/Makefile                |    2 +
 drivers/char/pch_phub/Makefile       |    3 +
 drivers/char/pch_phub/pch_phub.c     |  800 ++++++++++++++++++++++++++++++++++
 drivers/char/pch_phub/pch_phub.h     |   48 ++
 6 files changed, 863 insertions(+), 0 deletions(-)
 create mode 100644 drivers/char/pch_phub/Makefile
 create mode 100755 drivers/char/pch_phub/pch_phub.c
 create mode 100755 drivers/char/pch_phub/pch_phub.h

diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 35cf64d..b700b17 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -320,4 +320,5 @@ Code  Seq#(hex)	Include File		Comments
 					<mailto:thomas@winischhofer.net>
 0xF4	00-1F	video/mbxfb.h		mbxfb
 					<mailto:raph@8d.com>
+0xF7	00-0F	drivers/char/pch_phub/pch_phub.h	PCH Phub driver
 0xFD	all	linux/dm-ioctl.h
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index e023682..499902f 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -4,6 +4,15 @@
 
 menu "Character devices"
 
+config PCH_PHUB
+	tristate "PCH PHUB"
+	depends on PCI
+	help
+	  This driver is for PCH PHUB of Topcliff which is an IOH for x86
+	  embedded processor. The Topcliff has MAC address and Option ROM data
+	  in SROM. This driver can access MAC address and Option ROM data in
+	  SROM.
+
 config VT
 	bool "Virtual terminal" if EMBEDDED
 	depends on !S390
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index f957edf..1e3eb6c 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -111,6 +111,8 @@ obj-$(CONFIG_PS3_FLASH)		+= ps3flash.o
 obj-$(CONFIG_JS_RTC)		+= js-rtc.o
 js-rtc-y = rtc.o
 
+obj-$(CONFIG_PCH_PHUB)	+= pch_phub/
+
 # Files generated that shall be removed upon make clean
 clean-files := consolemap_deftbl.c defkeymap.c
 
diff --git a/drivers/char/pch_phub/Makefile b/drivers/char/pch_phub/Makefile
new file mode 100644
index 0000000..93aaffe
--- /dev/null
+++ b/drivers/char/pch_phub/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_PCH_PHUB) += pch_phub_drv.o
+
+pch_phub_drv-objs := pch_phub.o
diff --git a/drivers/char/pch_phub/pch_phub.c b/drivers/char/pch_phub/pch_phub.c
new file mode 100755
index 0000000..d0ec70d
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.c
@@ -0,0 +1,800 @@
+/*
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/if_ether.h>
+#include "pch_phub.h"
+
+#define PHUB_STATUS 0x00		/* Status Register offset */
+#define PHUB_CONTROL 0x04		/* Control Register offset */
+#define PHUB_TIMEOUT 0x05		/* Time out value for Status Register */
+#define PCH_PHUB_ROM_WRITE_ENABLE 0x01	/* Enabling for writing ROM */
+#define PCH_PHUB_ROM_WRITE_DISABLE 0x00	/* Disabling for writing ROM */
+#define PCH_PHUB_ROM_START_ADDR 0x14	/* ROM data area start address offset */
+
+/* MAX number of INT_REDUCE_CONTROL registers */
+#define MAX_NUM_INT_REDUCE_CONTROL_REG 128
+#define PCI_DEVICE_ID_PCH1_PHUB 0x8801
+#define PCH_MINOR_NOS 1
+#define CLKCFG_CAN_50MHZ 0x12000000
+#define CLKCFG_CANCLK_MASK 0xFF000000
+#define MODULE_NAME "pch_phub"
+
+/**
+ * struct pch_phub_reg - PHUB register structure
+ * @phub_id_reg:			PHUB_ID register val
+ * @q_pri_val_reg:			QUEUE_PRI_VAL register val
+ * @rc_q_maxsize_reg:			RC_QUEUE_MAXSIZE register val
+ * @bri_q_maxsize_reg:			BRI_QUEUE_MAXSIZE register val
+ * @comp_resp_timeout_reg:		COMP_RESP_TIMEOUT register val
+ * @bus_slave_control_reg:		BUS_SLAVE_CONTROL_REG register val
+ * @deadlock_avoid_type_reg:		DEADLOCK_AVOID_TYPE register val
+ * @intpin_reg_wpermit_reg0:		INTPIN_REG_WPERMIT register 0 val
+ * @intpin_reg_wpermit_reg1:		INTPIN_REG_WPERMIT register 1 val
+ * @intpin_reg_wpermit_reg2:		INTPIN_REG_WPERMIT register 2 val
+ * @intpin_reg_wpermit_reg3:		INTPIN_REG_WPERMIT register 3 val
+ * @int_reduce_control_reg:		INT_REDUCE_CONTROL registers val
+ * @clkcfg_reg:				CLK CFG register val
+ * @pch_phub_base_address:		Register base address
+ * @pch_phub_extrom_base_address:	external rom base address
+ * @pch_phub_suspended:			PHUB status val
+ */
+struct pch_phub_reg {
+	u32 phub_id_reg;
+	u32 q_pri_val_reg;
+	u32 rc_q_maxsize_reg;
+	u32 bri_q_maxsize_reg;
+	u32 comp_resp_timeout_reg;
+	u32 bus_slave_control_reg;
+	u32 deadlock_avoid_type_reg;
+	u32 intpin_reg_wpermit_reg0;
+	u32 intpin_reg_wpermit_reg1;
+	u32 intpin_reg_wpermit_reg2;
+	u32 intpin_reg_wpermit_reg3;
+	u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG];
+	u32 clkcfg_reg;
+	void __iomem *pch_phub_base_address;
+	void __iomem *pch_phub_extrom_base_address;
+	int pch_phub_suspended;
+};
+
+/* SROM SPEC for MAC address assignment offset */
+static const int pch_phub_mac_offset[ETH_ALEN] = {0x3, 0x2, 0x1, 0x0, 0xb, 0xa};
+
+static DEFINE_MUTEX(pch_phub_mutex);
+static dev_t pch_phub_dev_no;
+static struct cdev pch_phub_dev;
+static struct pch_phub_reg pch_phub_reg;
+
+/**
+ * pch_phub_read_modify_write_reg() - Reading modifying and writing register
+ * @reg_addr_offset:	Register offset address value.
+ * @data:		Writing value.
+ * @mask:		Mask value.
+ */
+static void pch_phub_read_modify_write_reg(unsigned int reg_addr_offset,
+					   unsigned int data, unsigned int mask)
+{
+	void __iomem *reg_addr = pch_phub_reg.pch_phub_base_address +
+					 reg_addr_offset;
+	iowrite32(((ioread32(reg_addr) & ~mask)) | data, reg_addr);
+}
+
+
+/* pch_phub_save_reg_conf - saves register configuration */
+static void pch_phub_save_reg_conf(struct pci_dev *pdev)
+{
+	unsigned int i;
+	void __iomem *p = pch_phub_reg.pch_phub_base_address;
+
+	dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
+	pch_phub_reg.phub_id_reg = ioread32(p + PCH_PHUB_ID_REG);
+	pch_phub_reg.q_pri_val_reg = ioread32(p + PCH_PHUB_QUEUE_PRI_VAL_REG);
+	pch_phub_reg.rc_q_maxsize_reg =
+	    ioread32(p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+	pch_phub_reg.bri_q_maxsize_reg =
+	    ioread32(p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+	pch_phub_reg.comp_resp_timeout_reg =
+	    ioread32(p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+	pch_phub_reg.bus_slave_control_reg =
+	    ioread32(p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+	pch_phub_reg.deadlock_avoid_type_reg =
+	    ioread32(p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+	pch_phub_reg.intpin_reg_wpermit_reg0 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+	pch_phub_reg.intpin_reg_wpermit_reg1 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+	pch_phub_reg.intpin_reg_wpermit_reg2 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+	pch_phub_reg.intpin_reg_wpermit_reg3 =
+	    ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
+	dev_dbg(&pdev->dev, "%s : "
+		"pch_phub_reg.phub_id_reg=%x, "
+		"pch_phub_reg.q_pri_val_reg=%x, "
+		"pch_phub_reg.rc_q_maxsize_reg=%x, "
+		"pch_phub_reg.bri_q_maxsize_reg=%x, "
+		"pch_phub_reg.comp_resp_timeout_reg=%x, "
+		"pch_phub_reg.bus_slave_control_reg=%x, "
+		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n", __func__,
+		pch_phub_reg.phub_id_reg,
+		pch_phub_reg.q_pri_val_reg,
+		pch_phub_reg.rc_q_maxsize_reg,
+		pch_phub_reg.bri_q_maxsize_reg,
+		pch_phub_reg.comp_resp_timeout_reg,
+		pch_phub_reg.bus_slave_control_reg,
+		pch_phub_reg.deadlock_avoid_type_reg,
+		pch_phub_reg.intpin_reg_wpermit_reg0,
+		pch_phub_reg.intpin_reg_wpermit_reg1,
+		pch_phub_reg.intpin_reg_wpermit_reg2,
+		pch_phub_reg.intpin_reg_wpermit_reg3);
+	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
+		pch_phub_reg.int_reduce_control_reg[i] =
+		    ioread32(p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
+		dev_dbg(&pdev->dev, "%s : "
+			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
+			__func__, i, pch_phub_reg.int_reduce_control_reg[i]);
+	}
+	pch_phub_reg.clkcfg_reg = ioread32(p + CLKCFG_REG_OFFSET);
+}
+
+/* pch_phub_restore_reg_conf - restore register configuration */
+static void pch_phub_restore_reg_conf(struct pci_dev *pdev)
+{
+	unsigned int i;
+	void __iomem *p = pch_phub_reg.pch_phub_base_address;
+
+	dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
+	iowrite32(pch_phub_reg.phub_id_reg, p + PCH_PHUB_ID_REG);
+	iowrite32(pch_phub_reg.q_pri_val_reg, p + PCH_PHUB_QUEUE_PRI_VAL_REG);
+	iowrite32(pch_phub_reg.rc_q_maxsize_reg,
+					p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+	iowrite32(pch_phub_reg.bri_q_maxsize_reg,
+					p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+	iowrite32(pch_phub_reg.comp_resp_timeout_reg,
+					p + PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+	iowrite32(pch_phub_reg.bus_slave_control_reg,
+					p + PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+	iowrite32(pch_phub_reg.deadlock_avoid_type_reg,
+					p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg0,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg1,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg2,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+	iowrite32(pch_phub_reg.intpin_reg_wpermit_reg3,
+					p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
+	dev_dbg(&pdev->dev, "%s : "
+		"pch_phub_reg.phub_id_reg=%x, "
+		"pch_phub_reg.q_pri_val_reg=%x, "
+		"pch_phub_reg.rc_q_maxsize_reg=%x, "
+		"pch_phub_reg.bri_q_maxsize_reg=%x, "
+		"pch_phub_reg.comp_resp_timeout_reg=%x, "
+		"pch_phub_reg.bus_slave_control_reg=%x, "
+		"pch_phub_reg.deadlock_avoid_type_reg=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
+		"pch_phub_reg.intpin_reg_wpermit_reg3=%x\n", __func__,
+		pch_phub_reg.phub_id_reg,
+		pch_phub_reg.q_pri_val_reg,
+		pch_phub_reg.rc_q_maxsize_reg,
+		pch_phub_reg.bri_q_maxsize_reg,
+		pch_phub_reg.comp_resp_timeout_reg,
+		pch_phub_reg.bus_slave_control_reg,
+		pch_phub_reg.deadlock_avoid_type_reg,
+		pch_phub_reg.intpin_reg_wpermit_reg0,
+		pch_phub_reg.intpin_reg_wpermit_reg1,
+		pch_phub_reg.intpin_reg_wpermit_reg2,
+		pch_phub_reg.intpin_reg_wpermit_reg3);
+	for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) {
+		iowrite32(pch_phub_reg.int_reduce_control_reg[i],
+			p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
+		dev_dbg(&pdev->dev, "%s : "
+			"pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
+			__func__, i, pch_phub_reg.int_reduce_control_reg[i]);
+	}
+
+	iowrite32(pch_phub_reg.clkcfg_reg, p + CLKCFG_REG_OFFSET);
+}
+
+/**
+ * pch_phub_read_serial_rom() - Reading Serial ROM
+ * @offset_address:	Serial ROM offset address to read.
+ * @data:		Read buffer for specified Serial ROM value.
+ */
+static void pch_phub_read_serial_rom(unsigned int offset_address, u8 *data)
+{
+	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +
+					 offset_address;
+
+	*data = ioread8(mem_addr);
+}
+
+/**
+ * pch_phub_write_serial_rom() - Writing Serial ROM
+ * @offset_address:	Serial ROM offset address.
+ * @data:		Serial ROM value to write.
+ */
+static int pch_phub_write_serial_rom(unsigned int offset_address, u8 data)
+{
+	void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +
+					(offset_address & PCH_WORD_ADDR_MASK);
+	int i;
+	unsigned int word_data;
+	unsigned int pos;
+	unsigned int mask;
+	pos = (offset_address % 4) * 8;
+	mask = ~(0xFF << pos);
+
+	iowrite32(PCH_PHUB_ROM_WRITE_ENABLE,
+			pch_phub_reg.pch_phub_extrom_base_address +
+			PHUB_CONTROL);
+
+	word_data = ioread32(mem_addr);
+	iowrite32((word_data & mask) | (u32)data << pos, mem_addr);
+
+	i = 0;
+	while (ioread8(pch_phub_reg.pch_phub_extrom_base_address +
+						PHUB_STATUS) != 0x00) {
+		msleep(1);
+		if (PHUB_TIMEOUT == i)
+			return -EPERM;
+		i++;
+	}
+
+	iowrite32(PCH_PHUB_ROM_WRITE_DISABLE,
+			pch_phub_reg.pch_phub_extrom_base_address +
+			PHUB_CONTROL);
+
+	return 0;
+}
+
+/**
+ * pch_phub_read_serial_rom_val() - Read Serial ROM value
+ * @offset_address:	Serial ROM address offset value.
+ * @data:		Serial ROM value to read.
+ */
+static void pch_phub_read_serial_rom_val(unsigned int offset_address, u8 *data)
+{
+	unsigned int mem_addr;
+
+	mem_addr = PCH_PHUB_ROM_START_ADDR +
+			pch_phub_mac_offset[offset_address];
+
+	pch_phub_read_serial_rom(mem_addr, data);
+}
+
+
+/**
+ * pch_phub_write_serial_rom_val() - writing Serial ROM value
+ * @offset_address:	Serial ROM address offset value.
+ * @data:		Serial ROM value.
+ */
+static int pch_phub_write_serial_rom_val(unsigned int offset_address, u8 data)
+{
+	int retval;
+	unsigned int mem_addr;
+
+	mem_addr = PCH_PHUB_ROM_START_ADDR +
+			pch_phub_mac_offset[offset_address];
+
+	retval = pch_phub_write_serial_rom(mem_addr, data);
+
+	return retval;
+}
+
+/* pch_phub_gbe_serial_rom_conf - makes Serial ROM header format configuration
+ * for Gigabit Ethernet MAC address
+ */
+static int pch_phub_gbe_serial_rom_conf(void)
+{
+	int retval;
+
+	retval = pch_phub_write_serial_rom(0x0b, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x0a, 0x10);
+	retval |= pch_phub_write_serial_rom(0x09, 0x01);
+	retval |= pch_phub_write_serial_rom(0x08, 0x02);
+
+	retval |= pch_phub_write_serial_rom(0x0f, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0e, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0d, 0x00);
+	retval |= pch_phub_write_serial_rom(0x0c, 0x80);
+
+	retval |= pch_phub_write_serial_rom(0x13, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x12, 0x10);
+	retval |= pch_phub_write_serial_rom(0x11, 0x01);
+	retval |= pch_phub_write_serial_rom(0x10, 0x18);
+
+	retval |= pch_phub_write_serial_rom(0x1b, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x1a, 0x10);
+	retval |= pch_phub_write_serial_rom(0x19, 0x01);
+	retval |= pch_phub_write_serial_rom(0x18, 0x19);
+
+	retval |= pch_phub_write_serial_rom(0x23, 0xbc);
+	retval |= pch_phub_write_serial_rom(0x22, 0x10);
+	retval |= pch_phub_write_serial_rom(0x21, 0x01);
+	retval |= pch_phub_write_serial_rom(0x20, 0x3a);
+
+	retval |= pch_phub_write_serial_rom(0x27, 0x01);
+	retval |= pch_phub_write_serial_rom(0x26, 0x00);
+	retval |= pch_phub_write_serial_rom(0x25, 0x00);
+	retval |= pch_phub_write_serial_rom(0x24, 0x00);
+
+	return retval;
+}
+
+/**
+ * pch_phub_read_gbe_mac_addr() - Read Gigabit Ethernet MAC address
+ * @offset_address:	Gigabit Ethernet MAC address offset value.
+ * @data:		Buffer of the Gigabit Ethernet MAC address value.
+ */
+static void pch_phub_read_gbe_mac_addr(u8 *data)
+{
+	int i;
+	for (i = 0; i < ETH_ALEN; i++)
+		pch_phub_read_serial_rom_val(i, &data[i]);
+}
+
+/**
+ * pch_phub_write_gbe_mac_addr() - Write MAC address
+ * @offset_address:	Gigabit Ethernet MAC address offset value.
+ * @data:		Gigabit Ethernet MAC address value.
+ */
+static int pch_phub_write_gbe_mac_addr(u8 *data)
+{
+	int retval;
+	int i;
+
+	retval = pch_phub_gbe_serial_rom_conf();
+	if (retval)
+		return retval;
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		retval = pch_phub_write_serial_rom_val(i, data[i]);
+		if (retval)
+			return retval;
+	}
+
+	return retval;
+}
+
+static ssize_t pch_phub_read(struct file *file, char __user *buf, size_t size,
+			     loff_t *ppos)
+{
+	unsigned int rom_signature;
+	unsigned char rom_length;
+	int ret_value;
+	unsigned int tmp;
+	unsigned char data;
+	unsigned int addr_offset;
+	unsigned int orom_size;
+	int ret;
+	int err;
+	loff_t pos = *ppos;
+
+	ret = mutex_lock_interruptible(&pch_phub_mutex);
+	if (ret) {
+		err = -ERESTARTSYS;
+		goto return_err_nomutex;
+	}
+
+	/* Get Rom signature */
+	pch_phub_read_serial_rom(0x80, (unsigned char *)&rom_signature);
+	rom_signature &= 0xff;
+	pch_phub_read_serial_rom(0x81, (unsigned char *)&tmp);
+	rom_signature |= (tmp & 0xff) << 8;
+	if (rom_signature == 0xAA55) {
+		pch_phub_read_serial_rom(0x82, &rom_length);
+		orom_size = rom_length * 512;
+		if (orom_size < pos) {
+			addr_offset = 0;
+			goto return_ok;
+		}
+
+		for (addr_offset = 0; addr_offset < size; addr_offset++) {
+			pch_phub_read_serial_rom(0x80 + addr_offset + pos,
+									 &data);
+			ret_value = copy_to_user(&buf[addr_offset], &data, 1);
+			if (ret_value) {
+				err = -EFAULT;
+				goto return_err;
+			}
+
+			if (orom_size < pos + addr_offset) {
+				*ppos += addr_offset;
+				goto return_ok;
+			}
+
+		}
+	} else {
+		err = -ENODATA;
+		goto return_err;
+	}
+	*ppos += addr_offset;
+return_ok:
+	mutex_unlock(&pch_phub_mutex);
+	return addr_offset;
+
+return_err:
+	mutex_unlock(&pch_phub_mutex);
+return_err_nomutex:
+	return err;
+}
+
+
+static ssize_t pch_phub_write(struct file *file, const char __user *buf,
+			      size_t size, loff_t *ppos)
+{
+	unsigned int data;
+	int ret_value1;
+	int ret_value2;
+	int err;
+	unsigned int addr_offset;
+	loff_t pos = *ppos;
+	int ret;
+
+	ret = mutex_lock_interruptible(&pch_phub_mutex);
+	if (ret) {
+		err = -ERESTARTSYS;
+		goto return_err_nomutex;
+	}
+
+	for (addr_offset = 0; addr_offset < size; addr_offset++) {
+		if (PCH_PHUB_OROM_SIZE < pos + addr_offset) {
+			*ppos += addr_offset;
+			goto return_ok;
+		}
+		ret_value1 = get_user(data, &buf[addr_offset]);
+		if (ret_value1) {
+			err = -EFAULT;
+			goto return_err;
+		}
+
+		ret_value2 = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
+							data);
+		if (ret_value2) {
+			err = ret_value2;
+			goto return_err;
+		}
+	}
+
+	*ppos += addr_offset;
+
+return_ok:
+	mutex_unlock(&pch_phub_mutex);
+	return addr_offset;
+
+return_err:
+	mutex_unlock(&pch_phub_mutex);
+return_err_nomutex:
+	return err;
+}
+
+
+static long pch_phub_ioctl(struct file *file, unsigned int cmd,
+			   unsigned long arg)
+{
+	int ret_value;
+	int ret;
+	int rtn;
+	__u8 mac_addr[ETH_ALEN];
+	void __user *varg = (void __user *)arg;
+
+	ret = mutex_lock_interruptible(&pch_phub_mutex);
+	if (ret) {
+		ret_value = -ERESTARTSYS;
+		goto return_nomutex;
+	}
+
+	if (pch_phub_reg.pch_phub_suspended == true) {
+		ret_value = -EBUSY;
+		goto return_ioctrl;
+	}
+
+	switch (cmd) {
+	case IOCTL_PHUB_READ_MAC_ADDR:
+		pch_phub_read_gbe_mac_addr(mac_addr);
+
+		ret_value = copy_to_user(varg, mac_addr, ETH_ALEN);
+		break;
+
+	case IOCTL_PHUB_WRITE_MAC_ADDR:
+		rtn = copy_from_user(mac_addr, varg, ETH_ALEN);
+
+		if (rtn) {
+			ret_value = -EFAULT;
+			break;
+		}
+
+		ret_value = pch_phub_write_gbe_mac_addr(mac_addr);
+		break;
+
+	default:
+		ret_value = -EINVAL;
+		break;
+	}
+return_ioctrl:
+	mutex_unlock(&pch_phub_mutex);
+return_nomutex:
+	return ret_value;
+}
+
+
+/* file_operations structure initialization */
+static const struct file_operations pch_phub_fops = {
+	.owner = THIS_MODULE,
+	.read = pch_phub_read,
+	.write = pch_phub_write,
+	.unlocked_ioctl = pch_phub_ioctl,
+	.llseek = default_llseek
+};
+
+
+static int __devinit pch_phub_probe(struct pci_dev *pdev,
+				    const struct pci_device_id *id)
+{
+	int ret;
+	unsigned int rom_size;
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+		"%s : pci_enable_device FAILED(ret=%d)", __func__, ret);
+		goto err_pci_enable_dev;
+	}
+	dev_dbg(&pdev->dev, "%s : pci_enable_device returns %d\n", __func__,
+		ret);
+
+	ret = pci_request_regions(pdev, MODULE_NAME);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+		"%s : pci_request_regions FAILED(ret=%d)", __func__, ret);
+		goto err_req_regions;
+	}
+	dev_dbg(&pdev->dev, "%s : "
+		"pci_request_regions returns %d\n", __func__, ret);
+
+	pch_phub_reg.pch_phub_base_address = pci_iomap(pdev, 1, 0);
+
+	if (pch_phub_reg.pch_phub_base_address == 0) {
+		dev_dbg(&pdev->dev, "%s : pci_iomap FAILED", __func__);
+		ret = -ENOMEM;
+		goto err_pci_iomap;
+	}
+	dev_dbg(&pdev->dev, "%s : pci_iomap SUCCESS and value "
+		"in pch_phub_base_address variable is 0x%08x\n", __func__,
+		(unsigned int)pch_phub_reg.pch_phub_base_address);
+
+	pch_phub_reg.pch_phub_extrom_base_address =
+						pci_map_rom(pdev, &rom_size);
+	if (pch_phub_reg.pch_phub_extrom_base_address == 0) {
+		dev_dbg(&pdev->dev, "%s : pci_map_rom FAILED", __func__);
+		ret = -ENOMEM;
+		goto err_pci_map;
+	}
+	dev_dbg(&pdev->dev, "%s : "
+		"pci_map_rom SUCCESS and value in "
+		"pch_phub_extrom_base_address variable is 0x%08x\n", __func__,
+		(unsigned int)pch_phub_reg.pch_phub_extrom_base_address);
+
+	ret = alloc_chrdev_region(&pch_phub_dev_no, 0,
+					PCH_MINOR_NOS, MODULE_NAME);
+	if (ret) {
+		dev_dbg(&pdev->dev, "%s : "
+			"alloc_chrdev_region FAILED(ret=%d)", __func__, ret);
+
+		goto err_alloc_cdev;
+	}
+	dev_dbg(&pdev->dev, "%s : "
+		"alloc_chrdev_region returns %d\n", __func__, ret);
+
+	cdev_init(&pch_phub_dev, &pch_phub_fops);
+	dev_dbg(&pdev->dev,
+			"%s : cdev_init invoked successfully\n", __func__);
+
+	pch_phub_dev.owner = THIS_MODULE;
+	pch_phub_dev.ops = &pch_phub_fops;
+
+	ret = cdev_add(&pch_phub_dev, pch_phub_dev_no, PCH_MINOR_NOS);
+	if (ret) {
+		dev_dbg(&pdev->dev, "%s :  cdev_add FAILED(ret=%d)",
+			__func__, ret);
+		goto err_cdev_add;
+	}
+	dev_dbg(&pdev->dev, "%s :  cdev_add returns %d\n", __func__, ret);
+
+	/* set the clock config reg if CAN clock is 50Mhz */
+	dev_dbg(&pdev->dev, "%s : invoking "
+		"pch_phub_read_modify_write_reg "
+		"to set CLKCFG reg for CAN clk 50Mhz\n", __func__);
+	pch_phub_read_modify_write_reg((unsigned int)CLKCFG_REG_OFFSET,
+					  CLKCFG_CAN_50MHZ, CLKCFG_CANCLK_MASK);
+
+	/* set the prefech value */
+	iowrite32(0x000ffffa, pch_phub_reg.pch_phub_base_address + 0x14);
+	/* set the interrupt delay value */
+	iowrite32(0x25, pch_phub_reg.pch_phub_base_address + 0x44);
+	return 0;
+
+err_cdev_add:
+	unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
+err_alloc_cdev:
+	pci_unmap_rom(pdev, pch_phub_reg.pch_phub_extrom_base_address);
+err_pci_map:
+	pci_iounmap(pdev, pch_phub_reg.pch_phub_base_address);
+err_pci_iomap:
+	pci_release_regions(pdev);
+err_req_regions:
+	pci_disable_device(pdev);
+err_pci_enable_dev:
+	dev_dbg(&pdev->dev, "%s returns %d\n", __func__, ret);
+	return ret;
+}
+
+static void __devexit pch_phub_remove(struct pci_dev *pdev)
+{
+	cdev_del(&pch_phub_dev);
+	dev_dbg(&pdev->dev,
+			"%s - cdev_del Invoked successfully\n", __func__);
+
+	unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
+	dev_dbg(&pdev->dev, "%s - "
+		"unregister_chrdev_region Invoked successfully\n", __func__);
+
+	pci_unmap_rom(pdev, pch_phub_reg.pch_phub_extrom_base_address);
+
+	pci_iounmap(pdev, pch_phub_reg.pch_phub_base_address);
+
+	dev_dbg(&pdev->dev, "%s - "
+			"pci_iounmap Invoked successfully\n", __func__);
+
+	pci_release_regions(pdev);
+	dev_dbg(&pdev->dev, "%s - "
+		"pci_release_regions Invoked successfully\n", __func__);
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "%s - "
+		"pci_disable_device Invoked successfully\n", __func__);
+}
+
+#ifdef CONFIG_PM
+
+static int pch_phub_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	int ret;
+
+	pch_phub_reg.pch_phub_suspended = true;/* For blocking further IOCTLs */
+
+	pch_phub_save_reg_conf(pdev);
+	dev_dbg(&pdev->dev, "%s - "
+		"pch_phub_save_reg_conf Invoked successfully\n", __func__);
+
+	ret = pci_save_state(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+			" %s -pci_save_state returns %d\n", __func__, ret);
+		return ret;
+	}
+	dev_dbg(&pdev->dev,
+			"%s - pci_save_state returns %d\n", __func__, ret);
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "%s - "
+			"pci_enable_wake Invoked successfully\n", __func__);
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "%s - "
+			"pci_disable_device Invoked successfully\n", __func__);
+
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+	dev_dbg(&pdev->dev, "%s - "
+			"pci_set_power_state Invoked successfully "
+			"return = 0\n", __func__);
+
+	return 0;
+}
+
+static int pch_phub_resume(struct pci_dev *pdev)
+{
+	int ret;
+
+	pci_set_power_state(pdev, PCI_D0);
+	dev_dbg(&pdev->dev, "%s - "
+		"pci_set_power_state Invoked successfully\n", __func__);
+
+	pci_restore_state(pdev);
+	dev_dbg(&pdev->dev, "%s - "
+		"pci_restore_state Invoked successfully\n", __func__);
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+		"%s-pci_enable_device failed(ret=%d) ", __func__, ret);
+		return ret;
+	}
+
+	dev_dbg(&pdev->dev, "%s - "
+			"pci_enable_device returns (ret=%d)\n", __func__, ret);
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "%s - "
+			"pci_enable_wake Invoked successfully\n", __func__);
+
+	pch_phub_restore_reg_conf(pdev);
+	dev_dbg(&pdev->dev, "%s - "
+		"pch_phub_restore_reg_conf Invoked successfully\n", __func__);
+
+	pch_phub_reg.pch_phub_suspended = false;
+
+	dev_dbg(&pdev->dev, "%s  returns 0\n", __func__);
+	return 0;
+}
+#else
+#define pch_phub_suspend NULL
+#define pch_phub_resume NULL
+#endif /* CONFIG_PM */
+
+static struct pci_device_id pch_phub_pcidev_id[] = {
+
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH1_PHUB)},
+	{0,}
+};
+
+
+static struct pci_driver pch_phub_driver = {
+	.name = "pch_phub",
+	.id_table = pch_phub_pcidev_id,
+	.probe = pch_phub_probe,
+	.remove = __devexit_p(pch_phub_remove),
+	.suspend = pch_phub_suspend,
+	.resume = pch_phub_resume
+};
+
+static int __init pch_phub_pci_init(void)
+{
+	int ret;
+	ret = pci_register_driver(&pch_phub_driver);
+
+	return ret;
+}
+
+static void __exit pch_phub_pci_exit(void)
+{
+	pci_unregister_driver(&pch_phub_driver);
+}
+
+module_init(pch_phub_pci_init);
+module_exit(pch_phub_pci_exit);
+
+MODULE_DESCRIPTION("PCH PACKET HUB PCI Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/char/pch_phub/pch_phub.h b/drivers/char/pch_phub/pch_phub.h
new file mode 100755
index 0000000..bf80f31
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.h
@@ -0,0 +1,48 @@
+#ifndef __PCH_PHUB_H__
+#define __PCH_PHUB_H__
+/*
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#define PHUB_IOCTL_MAGIC		0xf7
+
+/* Read GbE MAC address */
+#define IOCTL_PHUB_READ_MAC_ADDR (_IOR(PHUB_IOCTL_MAGIC, 1, __u8[ETH_ALEN]))
+
+/* Write GbE MAC address */
+#define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 2, __u8[ETH_ALEN]))
+
+/* SROM ACCESS Macro */
+#define PCH_WORD_ADDR_MASK (~((1 << 2) - 1))
+
+/* Registers address offset */
+#define PCH_PHUB_ID_REG				0x0000
+#define PCH_PHUB_QUEUE_PRI_VAL_REG		0x0004
+#define PCH_PHUB_RC_QUEUE_MAXSIZE_REG		0x0008
+#define PCH_PHUB_BRI_QUEUE_MAXSIZE_REG		0x000C
+#define PCH_PHUB_COMP_RESP_TIMEOUT_REG		0x0010
+#define PCH_PHUB_BUS_SLAVE_CONTROL_REG		0x0014
+#define PCH_PHUB_DEADLOCK_AVOID_TYPE_REG	0x0018
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG0	0x0020
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG1	0x0024
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG2	0x0028
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG3	0x002C
+#define PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE	0x0040
+#define CLKCFG_REG_OFFSET			0x500
+
+#define PCH_PHUB_OROM_SIZE 15360
+
+#endif
-- 
1.6.0.6



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

* [PATCH] I2C driver of Topcliff PCH
  2010-06-22  5:33 [PATCH] Topcliff PHUB: Generate PacketHub driver Masayuki Ohtak
                   ` (8 preceding siblings ...)
  2010-07-15  7:25 ` Masayuki Ohtak
@ 2010-07-15  7:42 ` Masayuki Ohtak
  2010-07-15 19:35     ` Arnd Bergmann
  2010-07-21  6:46   ` Masayuki Ohtak
  10 siblings, 1 reply; 46+ messages in thread
From: Masayuki Ohtak @ 2010-07-15  7:42 UTC (permalink / raw)
  To: Jean Delvare (PC drivers, core), Ben Dooks (embedded platforms),
	linux-i2c, LKML
  Cc: qi.wang, Wang, Yong Y, joel.clark, andrew.chih.howe.khor

I2C driver of Topcliff PCH

Topcliff PCH is the platform controller hub that is going to be used in
Intel's upcoming general embedded platform. All IO peripherals in
Topcliff PCH are actually devices sitting on AMBA bus.
Topcliff PCH has I2C I/F. Using this I/F, it is able to access system
devices connected to I2C.

Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
---
 drivers/i2c/busses/Kconfig   |    8 +
 drivers/i2c/busses/Makefile  |    3 +
 drivers/i2c/busses/i2c-pch.c | 1390 ++++++++++++++++++++++++++++++++++++++++++
 drivers/i2c/busses/i2c-pch.h |  147 +++++
 drivers/i2c/i2c-dev.c        |   28 +
 5 files changed, 1576 insertions(+), 0 deletions(-)
 create mode 100644 drivers/i2c/busses/i2c-pch.c
 create mode 100644 drivers/i2c/busses/i2c-pch.h

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index bceafbf..578fd42 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -7,6 +7,14 @@ menu "I2C Hardware Bus support"
 comment "PC SMBus host controller drivers"
 	depends on PCI
 
+config PCH_I2C
+	tristate "PCH I2C"
+	depends on PCI
+	help
+	  This driver is for PCH I2C of Topcliff which is an IOH for x86
+	  embedded processor.
+	  This driver can access PCH I2C bus device.
+
 config I2C_ALI1535
 	tristate "ALI 1535"
 	depends on PCI
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 936880b..53be4b3 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -78,3 +78,6 @@ obj-$(CONFIG_SCx200_I2C)	+= scx200_i2c.o
 ifeq ($(CONFIG_I2C_DEBUG_BUS),y)
 EXTRA_CFLAGS += -DDEBUG
 endif
+
+obj-$(CONFIG_PCH_I2C) += pch_i2c.o
+pch_i2c-objs := i2c-pch.o
diff --git a/drivers/i2c/busses/i2c-pch.c b/drivers/i2c/busses/i2c-pch.c
new file mode 100644
index 0000000..58824cc
--- /dev/null
+++ b/drivers/i2c/busses/i2c-pch.c
@@ -0,0 +1,1390 @@
+/*
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+
+#include "i2c-pch.h"
+
+static int pch_i2c_speed = 100; /* I2C bus speed in Kbps */
+static int pch_clk = 50000;	/* specifies I2C clock speed in KHz */
+static wait_queue_head_t pch_event;
+static s32(*pch_cbr) (struct i2c_algo_pch_data *);
+static DEFINE_MUTEX(pch_mutex);
+
+static struct pci_device_id __devinitdata pch_pcidev_id[] = {
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH_I2C)},
+	{0,}
+};
+
+static inline void pch_setbit(void __iomem *addr, u32 offset, u32 bitmask)
+{
+	iowrite32(((ioread32(addr + offset)) | (bitmask)), (addr + offset));
+}
+
+static inline void pch_clrbit(void __iomem *addr, u32 offset, u32 bitmask)
+{
+	iowrite32(((ioread32(addr + offset)) & (~(bitmask))), (addr + offset));
+}
+
+/**
+ * pch_init() - hardware initialization of I2C module
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_init(struct i2c_algo_pch_data *adap)
+{
+	u32 pch_i2cbc;
+	u32 pch_i2ctmr;
+	u32 reg_value;
+	void __iomem *p = adap->pch_base_address;
+
+	/* reset I2C controller */
+	iowrite32(0x01, p + PCH_I2CSRST);
+	iowrite32(0x0, p + PCH_I2CSRST);
+	/* Initialize I2C registers */
+	iowrite32(CLR_REG, p + PCH_I2CCTL);
+	iowrite32(CLR_REG, p + PCH_I2CMOD);
+	iowrite32(CLR_REG, p + PCH_I2CBUFFOR);
+	iowrite32(CLR_REG, p + PCH_I2CBUFSLV);
+	iowrite32(CLR_REG, p + PCH_I2CBUFSUB);
+	iowrite32(CLR_REG, p + PCH_I2CBUFMSK);
+	iowrite32(CLR_REG, p + PCH_I2CESRFOR);
+	iowrite32(CLR_REG, p + PCH_I2CESRMSK);
+	iowrite32(0x21, p + PCH_I2CNF);
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+		"Cleared the registers PCH_I2CCTL,PCH_I2CMOD,PCH_I2CBUFFOR\n,"
+		"PCH_I2CBUFSLV,PCH_I2CBUFSUB,PCH_I2CBUFMSK,\n"
+		"PCH_I2CESRFOR,PCH_I2CESRMSK\n");
+
+	reg_value = PCH_I2CCTL_I2CMEN;
+	pch_setbit((adap->pch_base_address), PCH_I2CCTL,
+			  PCH_I2CCTL_I2CMEN);
+
+	if (pch_i2c_speed != 400)
+		pch_i2c_speed = 100;
+
+	if (pch_i2c_speed == FAST_MODE_CLK) {
+		reg_value |= FAST_MODE_EN;
+		dev_dbg(adap->pch_adapter.dev.parent, "Fast mode enabled\n");
+	}
+
+	if (pch_clk <= 0 || pch_clk > PCH_MAX_CLK)
+		pch_clk = 62500;
+
+	pch_i2cbc = ((pch_clk) + (pch_i2c_speed * 4)) / (pch_i2c_speed * 8);
+	/* Set transfer speed in I2CBC */
+	iowrite32(pch_i2cbc, p + PCH_I2CBC);
+
+	pch_i2ctmr = (pch_clk) / 8;
+	iowrite32(pch_i2ctmr, p + PCH_I2CTMR);
+
+	reg_value |= NORMAL_INTR_ENBL;	/* Enable interrupts in normal mode */
+	iowrite32(reg_value, p + PCH_I2CCTL);
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+		"%s: I2CCTL=%x pch_i2cbc=%x pch_i2ctmr=%x Enable interrupts\n",
+		__func__, ioread32(p + PCH_I2CCTL),
+		pch_i2cbc, pch_i2ctmr);
+
+	init_waitqueue_head(&pch_event);
+}
+
+/**
+ * pch_wait_for_bus_idle() - check the status of bus.
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ * @timeout:	waiting time counter (us).
+ */
+static s32 pch_wait_for_bus_idle(struct i2c_algo_pch_data *adap,
+				 s32 timeout)
+{
+	u32 reg_value;
+	void __iomem *p = adap->pch_base_address;
+
+	/* get the status of bus busy */
+	reg_value = (ioread32(p + PCH_I2CSR) & I2CMBB_BIT);
+
+	while ((timeout != 0) && (reg_value != 0)) {
+		msleep(1);	/* wait for 100 ms */
+		reg_value = ioread32(p + PCH_I2CSR) & I2CMBB_BIT;
+
+		timeout--;
+	}
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+			"%s : I2CSR = %x\n", __func__, ioread32(p + PCH_I2CSR));
+
+	if (timeout == 0) {
+		dev_err(adap->pch_adapter.dev.parent,
+					"%s :return%d\n", __func__, -ETIME);
+	} else {
+		dev_dbg(adap->pch_adapter.dev.parent,
+					"%s : return %d\n", __func__, 0);
+	}
+
+	return ((timeout <= 0) ? (-ETIME) : (0));
+}
+
+/**
+ * pch_start() - Generate I2C start condition in normal mode.
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ *
+ * Generate I2C start condition in normal mode by setting I2CCTL.I2CMSTA to 1.
+ */
+static void pch_start(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+	dev_dbg(adap->pch_adapter.dev.parent, "In %s : I2CCTL = %x\n",
+					__func__, ioread32(p + PCH_I2CCTL));
+	pch_setbit((adap->pch_base_address), PCH_I2CCTL, PCH_START);
+	dev_dbg(adap->pch_adapter.dev.parent,
+		"Invoke %s successfully. I2CCTL = %x\n", __func__, PCH_I2CCTL);
+}
+
+/**
+ * pch_wait_for_xfer_complete() - initiates a wait for the tx complete event
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static s32 pch_wait_for_xfer_complete(struct i2c_algo_pch_data *adap)
+{
+	u32 temp_flag;
+	s32 ret;
+	ret = wait_event_interruptible_timeout(pch_event,
+			(adap->pch_event_flag != 0), msecs_to_jiffies(50));
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+		"%s adap->pch_event_flag = %x", __func__, adap->pch_event_flag);
+	temp_flag = adap->pch_event_flag;
+	adap->pch_event_flag = 0;
+
+	if (ret == 0) {
+		dev_err(adap->pch_adapter.dev.parent,
+				"%s : Timeout\n", __func__);
+	} else if (ret < 0) {
+		dev_err(adap->pch_adapter.dev.parent,
+			"%s failed : Interrupted by other signal\n", __func__);
+		ret = -ERESTARTSYS;
+	} else if ((temp_flag & I2C_ERROR_MASK) == 0) {
+		ret = 0;
+	} else {
+		dev_err(adap->pch_adapter.dev.parent,
+				"%s failed : Error in transfer\n", __func__);
+	}
+
+	dev_err(adap->pch_adapter.dev.parent, "%s returns %d\n", __func__, ret);
+
+	return ret;
+}
+
+/**
+ * pch_getack() - to confirm ACK/NACK
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static s32 pch_getack(struct i2c_algo_pch_data *adap)
+{
+	u32 reg_val;
+	void __iomem *p = adap->pch_base_address;
+	reg_val = ioread32(p + PCH_I2CSR) & PCH_GETACK;
+
+	if (reg_val == 0)
+		dev_dbg(adap->pch_adapter.dev.parent, "%s : return 0\n",
+								__func__);
+	else
+		dev_dbg(adap->pch_adapter.dev.parent, "%s : return%d\n",
+							__func__, -EPROTO);
+
+	return (((reg_val) == 0) ? (0) : (-EPROTO));
+}
+
+/**
+ * pch_stop() - generate stop condition in normal mode.
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_stop(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CCTL = %x\n", __func__,
+		ioread32(p + PCH_I2CCTL));
+	/* clear the start bit */
+	pch_clrbit((adap->pch_base_address), PCH_I2CCTL, PCH_START);
+	dev_dbg(adap->pch_adapter.dev.parent, "In %s : I2CCTL = %x\n", __func__,
+						ioread32(p + PCH_I2CCTL));
+}
+
+/**
+ * pch_repstart() - generate repeated start condition in normal mode
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_repstart(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+	dev_dbg(adap->pch_adapter.dev.parent, "In %s : I2CCTL = %x\n",
+		__func__, ioread32(p + PCH_I2CCTL));
+	pch_setbit((adap->pch_base_address), PCH_I2CCTL,
+			PCH_REPSTART);
+
+	dev_dbg(adap->pch_adapter.dev.parent, "In %s : I2CCTL = %x\n", __func__,
+		ioread32(p + PCH_I2CCTL));
+}
+
+/**
+ * pch_writebytes() - write data to I2C bus in normal mode
+ * @i2c_adap:	Pointer to the struct i2c_adapter.
+ * @last:	specifies whether last message or not.
+ *		In the case of compound mode it will be 1 for last message,
+ *		otherwise 0.
+ * @first:	specifies whether first message or not.
+ *		1 for first message otherwise 0.
+ */
+static s32 pch_writebytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
+			  u32 last, u32 first)
+{
+	struct i2c_algo_pch_data *adap = i2c_adap->algo_data;
+
+	u8 *buf;
+	u32 length;
+	u32 addr;
+	u32 addr_2_msb;
+	u32 addr_8_lsb;
+	s32 wrcount;
+	void __iomem *p = adap->pch_base_address;
+	length = msgs->len;
+	buf = msgs->buf;
+	addr = msgs->addr;
+	/* enable master tx */
+	pch_setbit((adap->pch_base_address), PCH_I2CCTL, I2C_TX_MODE);
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+		"%s : I2CCTL = %x msgs->len = %d\n", __func__,
+		ioread32(p + PCH_I2CCTL), length);
+
+	if (first) {
+		if (pch_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT) == -ETIME)
+			return -ETIME;
+	}
+
+	if (msgs->flags & I2C_M_TEN) {
+		addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7);
+		iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR);
+		if (first)
+			pch_start(adap);
+		if ((pch_wait_for_xfer_complete(adap) == 0) &&
+		    (pch_getack(adap) == 0)) {
+			addr_8_lsb = (addr & I2C_ADDR_MSK);
+			iowrite32(addr_8_lsb, p + PCH_I2CDR);
+		} else {
+			pch_stop(adap);
+			return -ETIME;
+		}
+	} else {
+		/* set 7 bit slave address and R/W bit as 0 */
+		iowrite32(addr << 1, p + PCH_I2CDR);
+		if (first)
+			pch_start(adap);
+	}
+
+	if ((pch_wait_for_xfer_complete(adap) == 0) &&
+						(pch_getack(adap) == 0)) {
+		for (wrcount = 0; wrcount < length; ++wrcount) {
+			/* write buffer value to I2C data register */
+			iowrite32(buf[wrcount], p + PCH_I2CDR);
+			dev_dbg(adap->pch_adapter.dev.parent,
+				"%s : writing %x to Data register\n",
+				__func__, buf[wrcount]);
+
+			if (pch_wait_for_xfer_complete(adap) != 0) {
+				wrcount = -ETIME;
+				break;
+			}
+
+			dev_dbg(adap->pch_adapter.dev.parent,
+						"%s return %d", __func__, 0);
+
+			if (pch_getack(adap)) {
+				wrcount = -ETIME;
+				break;
+			}
+		}
+
+		/* check if this is the last message */
+		if (last)
+			pch_stop(adap);
+		else
+			pch_repstart(adap);
+	} else {
+		pch_stop(adap);
+	}
+
+	dev_err(adap->pch_adapter.dev.parent,
+					"%s return=%d\n", __func__, wrcount);
+
+	return wrcount;
+}
+
+/**
+ * pch_sendack() - send ACK
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_sendack(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CCTL = %x\n", __func__,
+		  ioread32(p + PCH_I2CCTL));
+	pch_clrbit((adap->pch_base_address), PCH_I2CCTL, PCH_ACK);
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+		"Invoke %s successfully. I2CCTL = %x\n", __func__, PCH_I2CCTL);
+}
+
+/**
+ * pch_sendnack() - send NACK
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+
+static void pch_sendnack(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CCTL = %x\n", __func__,
+		  ioread32(p + PCH_I2CCTL));
+	pch_setbit((adap->pch_base_address), PCH_I2CCTL, PCH_ACK);
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CCTL = %x\n", __func__,
+		  ioread32(p + PCH_I2CCTL));
+}
+
+/**
+ * pch_readbytes() - read data  from I2C bus in normal mode.
+ * @i2c_adap:	Pointer to the struct i2c_adapter.
+ * @msgs:	Pointer to i2c_msg structure.
+ * @last:	specifies whether last message or not.
+ * @first:	specifies whether first message or not.
+ */
+s32 pch_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
+		  u32 last, u32 first)
+{
+	struct i2c_algo_pch_data *adap = i2c_adap->algo_data;
+
+	u8 *buf;
+	u32 count;
+	u32 length;
+	u32 addr;
+	u32 addr_2_msb;
+	void __iomem *p = adap->pch_base_address;
+	length = msgs->len;
+	buf = msgs->buf;
+	addr = msgs->addr;
+
+	/* enable master reception */
+	pch_clrbit((adap->pch_base_address), PCH_I2CCTL, I2C_TX_MODE);
+
+	if (first) {
+		if (pch_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT) == -ETIME)
+			return -ETIME;
+	}
+
+	if (msgs->flags & I2C_M_TEN) {
+		addr_2_msb = (((addr & I2C_MSB_2B_MSK) >> 7) | (I2C_RD));
+		iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR);
+
+	} else {
+		/* 7 address bits + R/W bit */
+		addr = (((addr) << 1) | (I2C_RD));
+		iowrite32(addr, p + PCH_I2CDR);
+	}
+
+	/* check if it is the first message */
+	if (first)
+		pch_start(adap);
+
+	if ((pch_wait_for_xfer_complete(adap) == 0)
+					    && (pch_getack(adap) == 0)) {
+		dev_dbg(adap->pch_adapter.dev.parent,
+						"%s return %d", __func__, 0);
+
+		if (length == 0) {
+			pch_stop(adap);
+			ioread32(p + PCH_I2CDR); /* Dummy read needs */
+
+			count = length;
+		} else {
+			int read_index;
+			int loop;
+			pch_sendack(adap);
+
+			/* Dummy read */
+			for (loop = 1, read_index = 0; loop < length; loop++) {
+				buf[read_index] =
+				    ioread32(p + PCH_I2CDR);
+
+				if (loop != 1)
+					read_index++;
+
+				if (pch_wait_for_xfer_complete(adap) != 0) {
+					pch_stop(adap);
+					return -ETIME;
+				}
+			}	/* end for */
+
+			pch_sendnack(adap);
+
+			buf[read_index] = ioread32(p + PCH_I2CDR);
+
+			if (length != 1)
+				read_index++;
+
+			if (pch_wait_for_xfer_complete(adap) == 0) {
+				if (last)
+					pch_stop(adap);
+				else
+					pch_repstart(adap);
+
+				buf[read_index++] = ioread32(p + PCH_I2CDR);
+				count = read_index;
+			} else {
+				count = -ETIME;
+			}
+
+		}
+	} else {
+		count = -ETIME;
+		pch_stop(adap);
+	}
+
+	return count;
+}
+
+/**
+ * pch_buff_mode_start() - Generate I2C start condition in buffer mode
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_buff_mode_start(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CBUFCTL = %x\n", __func__,
+		ioread32(p + PCH_I2CBUFCTL));
+	pch_setbit((adap->pch_base_address), PCH_I2CBUFCTL,
+			PCH_BUFF_START);
+
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CBUFCTL = %x\n", __func__,
+		ioread32(p + PCH_I2CBUFCTL));
+}
+
+/**
+ * pch_eeprom_swrst_start() - Generate I2C start condition in EEPROM sw reset
+ *			      mode
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_eeprom_swrst_start(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CESRCTL = %x\n", __func__,
+		ioread32(p + PCH_I2CESRCTL));
+	pch_setbit((adap->pch_base_address), PCH_I2CESRCTL,
+			PCH_ESR_START);
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+		"Invoked %s successfully. I2CESRCTL = %x\n", __func__,
+		PCH_I2CESRCTL);
+}
+
+/**
+ * pch_entcb() - Function to register call back function
+ * @pch_ptr:	Pointer to call back function.
+ */
+static void pch_entcb(s32(*pch_ptr) (struct i2c_algo_pch_data *adap))
+{
+	if (pch_ptr != NULL) {
+		/* set the handler call back function */
+		pch_cbr = pch_ptr;
+	}
+}
+
+/**
+ * pch_handler() - interrupt handler for the PCH I2C controller
+ * @irq:	irq number.
+ * @pData:	cookie passed back to the handler function.
+ */
+static irqreturn_t pch_handler(int irq, void *pData)
+{
+	s32 ret;
+	u32 i;
+
+	struct adapter_info *adap_info = (struct adapter_info *)pData;
+	/* invoke the call back  */
+
+	if (pch_cbr != NULL) {
+		for (i = 0, ret = 0; i < PCH_MAX_CHN; i++)
+			ret |= (pch_cbr) (&adap_info->pch_data[i]);
+	} else {
+		dev_err(adap_info->pch_data[0].pch_adapter.dev.parent,
+				"%s Call back pointer null ...", __func__);
+		return IRQ_NONE;
+	}
+
+	dev_dbg(adap_info->pch_data[0].pch_adapter.dev.parent,
+					"%s return = %d\n", __func__, ret);
+
+	if (ret == PCH_EVENT_SET)
+		dev_dbg(adap_info->pch_data[0].pch_adapter.dev.parent,
+					"%s return IRQ_HANDLED", __func__);
+	else
+		dev_dbg(adap_info->pch_data[0].pch_adapter.dev.parent,
+					"%s return IRQ_NONE", __func__);
+
+	return (ret == PCH_EVENT_SET) ? (IRQ_HANDLED) : (IRQ_NONE);
+}
+
+/**
+ * pch_buffer_read() - Function to read data  from I2C bus in buffer mode.
+ * @i2c_adap:	Pointer to the struct i2c_adapter.
+ * @msgs:	Pointer to i2c_msg structure.
+ */
+static s32 pch_buffer_read(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs)
+{
+	struct i2c_algo_pch_data *adap = i2c_adap->algo_data;
+
+	u32 loop;
+	u32 rdcount;
+	u32 length;
+	u32 i2cbufsub;
+	u32 addr;
+	u32 i2cbufslv_7_lsb;
+	u32 i2cbufslv_10_9_bit;
+	u32 msglen;
+	void __iomem *p = adap->pch_base_address;
+	/* initialize to invalid length, so that no sub address is tx-ed */
+	u32 subaddrlen = 5;
+	u32 i2cmod_prev;
+	s32 i;
+	u32 time_interval = i2c_adap->timeout;
+	u32 i2ctmr;
+	s32 retvalue;
+	u8 *buf;
+
+	length = msgs->len;
+	buf = msgs->buf;
+	addr = msgs->addr;
+	iowrite32(BUFFER_MODE_INTR_ENBL, p + PCH_I2CBUFMSK);
+	/* get the current value of I2C mod register */
+	i2cmod_prev = ioread32(p + PCH_I2CMOD);
+
+	/* enable buffer mode */
+	iowrite32(PCH_BUFFER_MODE, p + PCH_I2CMOD);
+	if (time_interval > 10)
+		time_interval = 10;
+
+	/* value of I2CT = (Timeout interval * PCLK frequency)/ 8 */
+	i2ctmr = (time_interval * (pch_clk)) / 8;
+
+	iowrite32(i2ctmr, p + PCH_I2CTMR);
+	/* if 10 bit addressing is selected */
+
+	if (msgs->flags & I2C_M_TEN) {
+		/* get the 8 LSBits */
+		i2cbufslv_7_lsb = (addr & I2C_ADDR_MSK);
+
+		/* get the 2 MSBits */
+		i2cbufslv_10_9_bit = ((addr & I2C_MSB_2B_MSK) << 1);
+
+		iowrite32(TEN_BIT_ADDR_DEFAULT | i2cbufslv_7_lsb |
+					i2cbufslv_10_9_bit, p + PCH_I2CBUFSLV);
+	} else {
+		iowrite32((addr & I2C_ADDR_MSK) << 1, p + PCH_I2CBUFSLV);
+	}
+
+	/* get sub address length, restrict to 4 bytes max */
+	subaddrlen =
+	    (buf[0] <= SUB_ADDR_LEN_MAX) ? (buf[0]) : (SUB_ADDR_LEN_MAX);
+
+	for (i = (subaddrlen - 1), i2cbufsub = 0; i >= 0; i--) {
+		/* frame the sub address based on the length */
+		i2cbufsub |= (((u32) buf[2 - i]) << (8 * i));
+	}
+
+	msglen = length - (subaddrlen + 1);
+
+	loop = subaddrlen + 1;
+
+	/* write the sub address to the reg */
+	iowrite32(i2cbufsub, p + PCH_I2CBUFSUB);
+	/* clear buffers */
+	iowrite32(CLR_REG, p + PCH_I2CBUFLEV);
+
+	rdcount = (msglen <= BUF_LEN_MAX) ? (msglen) : (BUF_LEN_MAX);
+
+	iowrite32((rdcount << 4) | PCH_BUF_RD | subaddrlen, p + PCH_I2CBUFFOR);
+
+	if (pch_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT) == -ETIME) {
+		retvalue = -ETIME;
+		goto return_err;
+	}
+
+	pch_buff_mode_start(adap);
+
+	dev_dbg(adap->pch_adapter.dev.parent, "buffer mode start\n");
+
+	if ((ioread32(p + PCH_I2CBUFSTA) & I2CBMDZ_BIT) != 0) {
+		dev_err(adap->pch_adapter.dev.parent, "buffer read error 1\n");
+		retvalue = -EIO;
+		goto return_err;
+	}
+
+	if (pch_wait_for_xfer_complete(adap) == -ETIME) {
+		dev_err(adap->pch_adapter.dev.parent, "buffer read error2\n");
+		retvalue = -EIO;
+		goto return_err;
+	}
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+				"pch_wait_for_xfer_complete return 0\n");
+
+	retvalue = rdcount;
+
+	for (; rdcount > 0; rdcount--)
+		buf[loop++] = ioread32(p + PCH_I2CDR);
+
+return_err:
+	/* disable buffer mode interrupts */
+	iowrite32(BUFFER_MODE_INTR_DISBL, p + PCH_I2CBUFMSK);
+	/* restore the I2CMOD register */
+	iowrite32(i2cmod_prev, p + PCH_I2CMOD);
+
+	return retvalue;
+}
+
+/**
+ * pch_buffer_write() - Function to write data to I2C bus in buffer mode.
+ * @i2c_adap:	Pointer to the struct i2c_adapter.
+ * @msgs:	Pointer to i2c_msg structure.
+ */
+static s32 pch_buffer_write(struct i2c_adapter *i2c_adap,
+			    struct i2c_msg *msgs)
+{
+	struct i2c_algo_pch_data *adap = i2c_adap->algo_data;
+
+	u32 loop;
+	u32 wrcount;
+	u32 msglen;
+	u32 i2cbufsub;
+	u32 addr;
+	u32 i2cbufslv_7_lsb;
+	u32 i2cbufslv_10_9_bit;
+	void __iomem *p = adap->pch_base_address;
+
+	/* initialize to invalid length, so that no sub address is tx-ed */
+	u32 subaddrlen = 5;
+	u32 i2cmod_prev;
+	s32 i;
+	u32 time_interval = i2c_adap->timeout;
+	u32 i2ctmr;
+	s32 retvalue;
+	u8 *buf;
+
+	msglen = msgs->len;
+	buf = msgs->buf;
+	addr = msgs->addr;
+
+	/* get the current value of I2C mod register */
+	i2cmod_prev = ioread32(p + PCH_I2CMOD);
+	/* enable buffer mode */
+	iowrite32(PCH_BUFFER_MODE, p + PCH_I2CMOD);
+
+	time_interval = (time_interval <= 10) ? (time_interval) : (10);
+	/* value of I2CT = (Timeout interval * PCLK frequency)/ 8 */
+	i2ctmr = (time_interval * (pch_clk)) / 8;
+
+	iowrite32(i2ctmr, p + PCH_I2CTMR);
+
+	/* enable buffer mode interrupts */
+	iowrite32(BUFFER_MODE_INTR_ENBL, p + PCH_I2CBUFMSK);
+
+	/* if 10 bit addressing is selected */
+
+	if (msgs->flags & I2C_M_TEN) {
+		dev_dbg(adap->pch_adapter.dev.parent,
+					"%s...ten bit addressing", __func__);
+		/* get the 8 LSBits */
+		i2cbufslv_7_lsb = (addr & I2C_ADDR_MSK);
+
+		/* get the 2 MSBits */
+		i2cbufslv_10_9_bit = ((addr & I2C_MSB_2B_MSK) << 1);
+
+		iowrite32(TEN_BIT_ADDR_DEFAULT | i2cbufslv_7_lsb |
+					i2cbufslv_10_9_bit, p + PCH_I2CBUFSLV);
+	} else {
+		iowrite32((addr & I2C_ADDR_MSK) << 1, p + PCH_I2CBUFSLV);
+	}
+
+	/* get sub address length, restrict to 4 bytes max */
+	subaddrlen =
+		(buf[0] <= SUB_ADDR_LEN_MAX) ? (buf[0]) : (SUB_ADDR_LEN_MAX);
+
+	for (i = (subaddrlen - 1), i2cbufsub = 0; i >= 0; i--) {
+		/* frame the sub address based on the length */
+		i2cbufsub |= (((u32) buf[2 - i]) << (8 * i));
+	}
+
+	/* subaddrlen bytes + the 1st field  */
+	loop = subaddrlen + 1;
+
+	msglen -= loop;
+
+	/* write the sub address to the reg */
+	iowrite32(i2cbufsub, p + PCH_I2CBUFSUB);
+
+	/* clear buffers */
+	iowrite32(CLR_REG, p + PCH_I2CBUFLEV);
+
+	if (msglen >= BUF_LEN_MAX)
+		msglen = BUF_LEN_MAX;
+
+	for (wrcount = 0; wrcount < msglen; wrcount++) {
+		iowrite32(buf[loop++], p + PCH_I2CDR);
+		dev_dbg(adap->pch_adapter.dev.parent,
+					"Buffer mode %x", (buf[loop] & 0xff));
+	}
+
+	/* set the number of bytes, transmission mode and sub address length */
+	iowrite32(((wrcount << 4) & PCH_BUF_TX) | subaddrlen,
+							p + PCH_I2CBUFFOR);
+	if ((pch_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT)) == -ETIME) {
+		retvalue = -ETIME;
+		goto return_err;
+	}
+
+	/* issue start bits */
+	pch_buff_mode_start(adap);
+
+	if (ioread32(p + PCH_I2CBUFSTA) & (I2CBMDZ_BIT | I2CBMAG_BIT)) {
+		retvalue = -EIO;
+		goto return_err;
+	}
+
+	if (pch_wait_for_xfer_complete(adap) == -ETIME) {
+		retvalue = -ETIME;
+		goto return_err;
+	}
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+					"pch_wait_for_xfer_complete return 0");
+	retvalue = wrcount;
+
+return_err:
+	/* disable buffer mode interrupts */
+	iowrite32(BUFFER_MODE_INTR_DISBL, p + PCH_I2CBUFMSK);
+	/* restore the I2CMOD register */
+	iowrite32(i2cmod_prev, p + PCH_I2CMOD);
+
+	return retvalue;
+}
+
+/**
+ * pch_eeprom_sw_reset() - triggering EEPROM software reset
+ * @i2c_adap:	Pointer to the struct i2c_adapter.
+ * @msgs:	Pointer to i2c_msg structure.
+ */
+static s32 pch_eeprom_sw_reset(struct i2c_adapter *i2c_adap,
+			       struct i2c_msg *msgs)
+{
+	struct i2c_algo_pch_data *adap = i2c_adap->algo_data;
+	void __iomem *p = adap->pch_base_address;
+	u32 time_interval = i2c_adap->timeout;
+	u32 i2ctmr;
+	u32 i2cmod_prev;
+	u32 pch_pattern;
+	s32 ret_val;
+
+	/* get the current value of I2C mod register */
+	i2cmod_prev = ioread32(p + PCH_I2CMOD);
+	iowrite32(CLR_REG, p + PCH_I2CMOD);
+	pch_setbit((adap->pch_base_address), PCH_I2CMOD,
+			EEPROM_SW_RST_MODE);
+
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CMOD %x\n",
+					__func__, ioread32(p + PCH_I2CMOD));
+	iowrite32(EEPROM_RST_INTR_ENBL, p + PCH_I2CESRMSK);
+
+	if (time_interval > 10)
+		time_interval = 10;
+
+	/* value of I2CT = (Timeout interval * PCLK frequency)/ 8 */
+	i2ctmr = (time_interval * (pch_clk)) / 8;
+
+	iowrite32(i2ctmr, p + PCH_I2CTMR);
+
+	/* get the EEPROM reset pattern */
+	pch_pattern = (u32) (*(msgs->buf));
+
+	/* mode 1 & 2 are used for buffer mode selection */
+	pch_pattern -= 2;
+
+	iowrite32(pch_pattern, p + PCH_I2CESRFOR);
+
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CESRFOR %x\n",
+					__func__, ioread32(p + PCH_I2CESRFOR));
+
+	if (pch_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT) == 0) {
+		pch_eeprom_swrst_start(adap);
+		ret_val = pch_wait_for_xfer_complete(adap);
+
+		dev_dbg(adap->pch_adapter.dev.parent,
+			"pch_wait_for_xfer_complete return =%d\n", ret_val);
+		iowrite32(i2cmod_prev, p + PCH_I2CMOD);
+
+		iowrite32(EEPROM_RST_INTR_DISBL, p + PCH_I2CESRMSK);
+	}
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+					"%s return=%d\n", __func__, ret_val);
+
+	return ret_val;
+}
+
+/**
+ * pch_cb() - Interrupt handler Call back function
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static s32 pch_cb(struct i2c_algo_pch_data *adap)
+{
+	u32 reg_val;
+	u32 i2c_mode;
+	u32 i2c_interrupt;
+	void __iomem *p = adap->pch_base_address;
+
+	reg_val = ioread32(p + PCH_I2CMOD);
+	/* get the current mode of operation */
+	i2c_mode = reg_val & (BUFFER_MODE | EEPROM_SR_MODE);
+
+	i2c_interrupt = false;
+	switch (i2c_mode) {
+	case NORMAL_MODE:
+		reg_val = ioread32(p + PCH_I2CSR);
+		reg_val &= (I2CMAL_BIT | I2CMCF_BIT | I2CMIF_BIT);
+
+		if (reg_val != 0) {
+			if (I2CMAL_BIT & reg_val)
+				adap->pch_event_flag |= I2CMAL_EVENT;
+
+			if (I2CMCF_BIT & reg_val)
+				adap->pch_event_flag |= I2CMCF_EVENT;
+
+			/* clear the applicable bits */
+			pch_clrbit((adap->pch_base_address),
+							PCH_I2CSR, reg_val);
+
+			dev_dbg(adap->pch_adapter.dev.parent,
+				"%s : PCH_I2CSR = %x\n",
+				__func__, ioread32(p + PCH_I2CSR));
+
+			i2c_interrupt = true;
+		}
+		break;
+
+	case BUFFER_MODE:
+		reg_val = ioread32(p + PCH_I2CBUFSTA);
+		reg_val &= BUFFER_MODE_MASK;
+		if (reg_val != 0) {
+			/* there is a co-relation between the buffer
+			 * mode interrupt flags' bit */
+			/* positions and the flag positions in event
+			 * flag. for e.g. I2CBMFI is at position */
+			/* 0 in the I2CBUFSTA register. its position
+			 * in the event flag is 2, hence left shifting
+			 */
+			adap->pch_event_flag |= ((reg_val) << 2);
+
+			/* clear the applicable bits */
+			pch_clrbit((adap->pch_base_address),
+					  PCH_I2CBUFSTA, reg_val);
+
+			dev_dbg(adap->pch_adapter.dev.parent,
+				"%s : PCH_I2CBUFSTA = %x\n", __func__,
+				ioread32(p + PCH_I2CBUFSTA));
+
+			i2c_interrupt = true;
+		}
+		break;
+
+	case EEPROM_SR_MODE:
+		reg_val = ioread32(p + PCH_I2CESRSTA);
+		reg_val &= (I2CESRFI_BIT | I2CESRTO_BIT);
+		if (reg_val != 0) {
+			adap->pch_event_flag |= ((reg_val) << 7);
+
+			/* clear the applicable bits */
+			pch_clrbit((adap->pch_base_address),
+						  PCH_I2CESRSTA, reg_val);
+
+			dev_dbg(adap->pch_adapter.dev.parent,
+				"%s : PCH_I2CESRSTA = %x\n", __func__,
+				ioread32(p + PCH_I2CESRSTA));
+
+			i2c_interrupt = true;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	if (i2c_interrupt)
+		wake_up_interruptible(&pch_event);
+
+	return (i2c_interrupt) ? (PCH_EVENT_SET) : (PCH_EVENT_NONE);
+}
+
+
+/**
+ * pch_xfer() - transfer data through I2C bus
+ * @i2c_adap:	Pointer to the struct i2c_adapter.
+ * @msgs:	Pointer to i2c_msg structure.
+ * @num:	number of messages.
+ */
+static s32 pch_xfer(struct i2c_adapter *i2c_adap,
+		    struct i2c_msg *msgs, s32 num)
+{
+	struct i2c_msg *pmsg;
+	u32 i;
+	u32 status;
+	u32 msglen;
+	u32 subaddrlen;
+	s32 ret;
+
+	struct i2c_algo_pch_data *adap = i2c_adap->algo_data;
+
+	ret = mutex_lock_interruptible(&pch_mutex);
+	if (ret) {
+		ret = -ERESTARTSYS;
+		goto return_err_nomutex;
+	}
+	if (adap->p_adapter_info->pch_suspended == false) {
+		dev_dbg(adap->pch_adapter.dev.parent,
+			"%s adap->p_adapter_info->pch_suspended is %d\n",
+			__func__, adap->p_adapter_info->pch_suspended);
+		/* transfer not completed */
+		adap->pch_xfer_in_progress = true;
+		dev_dbg(adap->pch_adapter.dev.parent,
+			"adap->pch_xfer_in_progress is %d\n",
+			adap->pch_xfer_in_progress);
+		pmsg = &msgs[0];
+		status = pmsg->flags;
+		/* special commands for PCH I2C driver */
+		if (status &
+		    (PCH_EEPROM_SW_RST_MODE_ENABLE | PCH_BUFFER_MODE_ENABLE)) {
+			if (status & PCH_EEPROM_SW_RST_MODE_ENABLE) {
+				/* check whether EEPROM sw reset is enabled */
+				dev_dbg(adap->pch_adapter.dev.parent,
+					"%s invoking pch_eeprom_sw_reset."
+					"Invoking I2C_MODE_SEL :flag= 0x%x\n",
+					__func__, status);
+				ret = pch_eeprom_sw_reset(i2c_adap, pmsg);
+			} else {
+				adap->pch_buff_mode_en =
+				    (pmsg->buf[0] == 1) ?
+				    (PCH_BUFFER_MODE_ENABLE) : (pmsg->buf[0]);
+				ret = 0;
+			}
+			/* transfer completed */
+			adap->pch_xfer_in_progress = false;
+			dev_dbg(adap->pch_adapter.dev.parent,
+				"adap->pch_xfer_in_progress is %d. "
+				"After mode selection %s return = %d\n",
+				adap->pch_xfer_in_progress, __func__, ret);
+			goto return_ok;
+		}
+
+		ret = -EBUSY;
+		for (i = 0; i < num; i++) {
+			pmsg = &msgs[i];
+			pmsg->flags |= adap->pch_buff_mode_en;
+			status = pmsg->flags;
+			dev_dbg(adap->pch_adapter.dev.parent,
+				"After invoking I2C_MODE_SEL :flag= 0x%x\n",
+				status);
+			/* calculate sub address length and message length */
+			/* these are applicable only for buffer mode */
+			subaddrlen = pmsg->buf[0];
+			/* calculate actual message length excluding
+			 * the sub address fields */
+			msglen = (pmsg->len) - (subaddrlen + 1);
+
+			if ((status & PCH_BUFFER_MODE_ENABLE)
+			    && (msglen != 0)) {
+				/* Buffer mode cannot be used for transferring
+				 * 0 byte data. Hence when buffer mode is
+				 * enabled and 0 byte transfer is requested,
+				 * normal mode transfer will be used */
+				if (status & (I2C_M_RD)) {
+					dev_dbg(adap->pch_adapter.dev.parent,
+						"%s invoking pch_buffer_read\n",
+						 __func__);
+					ret = pch_buffer_read(i2c_adap, pmsg);
+				} else {
+					dev_dbg(adap->pch_adapter.dev.parent,
+					    "%s invoking pch_buffer_write\n",
+					    __func__);
+					ret = pch_buffer_write(i2c_adap, pmsg);
+				}
+			} else {
+				if (status & (I2C_M_RD)) {
+					dev_dbg(adap->pch_adapter.dev.parent,
+						"%s invoking pch_readbytes\n",
+						__func__);
+					ret = pch_readbytes(i2c_adap, pmsg,
+							      (i + 1 == num),
+							      (i == 0));
+				} else {
+					dev_info(adap->pch_adapter.dev.parent,
+						"%s invoking pch_writebytes\n",
+						__func__);
+					ret = pch_writebytes(i2c_adap, pmsg,
+							       (i + 1 == num),
+							       (i == 0));
+				}
+			}
+
+		}
+
+		adap->pch_xfer_in_progress = false;	/* transfer completed */
+
+		dev_dbg(adap->pch_adapter.dev.parent,
+					"adap->pch_xfer_in_progress is %d\n",
+					adap->pch_xfer_in_progress);
+	} else {
+		ret = -EBUSY;
+	}
+return_ok:
+	mutex_unlock(&pch_mutex);
+return_err_nomutex:
+	dev_dbg(adap->pch_adapter.dev.parent, "%s return:%d\n\n\n\n",
+								__func__, ret);
+	return ret;
+}
+
+/**
+ * pch_func() - return the functionality of the I2C driver
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static u32 pch_func(struct i2c_adapter *adap)
+{
+	u32 ret;
+	ret = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR;
+	return ret;
+}
+
+static struct i2c_algorithm pch_algorithm = {
+	.master_xfer = pch_xfer,
+	.functionality = pch_func
+};
+
+/**
+ * pch_disbl_int() - Disable PCH I2C interrupts
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_disbl_int(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+
+	pch_clrbit((adap->pch_base_address), PCH_I2CCTL,
+			  NORMAL_INTR_ENBL);
+
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CCTL = %x\n", __func__,
+			  ioread32(p + PCH_I2CCTL));
+
+	iowrite32(EEPROM_RST_INTR_DISBL, p + PCH_I2CESRMSK);
+
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : PCH_I2CESRMSK = %x\n",
+					__func__, ioread32(p + PCH_I2CESRMSK));
+
+	iowrite32(BUFFER_MODE_INTR_DISBL, p + PCH_I2CBUFMSK);
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : PCH_I2CBUFMSK = %x\n",
+		__func__, ioread32(p + PCH_I2CBUFMSK));
+}
+
+static int __devinit pch_probe(struct pci_dev *pdev,
+			       const struct pci_device_id *id)
+{
+	int i;
+	void __iomem *base_addr;
+	s32 ret;
+	struct adapter_info *adap_info =
+			kzalloc((sizeof(struct adapter_info)), GFP_KERNEL);
+
+	dev_dbg(&pdev->dev, "Enterred in %s\n", __func__);
+
+	if (adap_info == NULL) {
+		dev_err(&pdev->dev, "Memory allocation failed FAILED");
+		ret = -ENOMEM;
+		goto return_err;
+	}
+
+	dev_dbg(&pdev->dev,
+		"%s kzalloc invoked successfully and adap_info valu = %p\n",
+		__func__, adap_info);
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "%s : pci_enable_device FAILED", __func__);
+		goto err_pci_enable;
+	}
+
+	dev_dbg(&pdev->dev, "%s pci_enable_device returns %d\n", __func__, ret);
+
+	ret = pci_request_regions(pdev, MODULE_NAME);
+	if (ret) {
+		dev_err(&pdev->dev, "pci_request_regions FAILED");
+		goto err_pci_req;
+	}
+
+	dev_dbg(&pdev->dev, "%s pci_request_regions returns %d\n",
+								__func__, ret);
+
+	base_addr = pci_iomap(pdev, 1, 0);
+
+	if (base_addr == 0) {
+		dev_err(&pdev->dev, "pci_iomap FAILED");
+		ret = -ENOMEM;
+		goto err_pci_iomap;
+	}
+
+	dev_dbg(&pdev->dev, "%s pci_iomap invoked successfully\n", __func__);
+	adap_info->pch_suspended = false;
+
+	pch_entcb(pch_cb);
+	dev_dbg(&pdev->dev, "%s pch_entcb invoked successfully\n", __func__);
+
+	for (i = 0; i < PCH_MAX_CHN; i++) {
+		adap_info->pch_data[i].p_adapter_info = adap_info;
+
+		adap_info->pch_data[i].pch_adapter.owner = THIS_MODULE;
+		adap_info->pch_data[i].pch_adapter.class = I2C_CLASS_HWMON;
+		strcpy(adap_info->pch_data[i].pch_adapter.name, "pch_i2c");
+		adap_info->pch_data[i].pch_adapter.algo = &pch_algorithm;
+		adap_info->pch_data[i].pch_adapter.algo_data =
+							&adap_info->pch_data[i];
+
+		/* (i * 0x80) + base_addr; */
+		adap_info->pch_data[i].pch_base_address = base_addr;
+
+		adap_info->pch_data[i].pch_adapter.dev.parent = &pdev->dev;
+
+		ret = i2c_add_adapter(&(adap_info->pch_data[i].pch_adapter));
+
+		if (ret) {
+			dev_err(&pdev->dev, "i2c_add_adapter FAILED");
+			goto err_i2c_add_adapter;
+		}
+
+		dev_dbg(&pdev->dev,
+			"i2c_add_adapter returns %d for channel-%d\n", ret, i);
+		pch_init(&adap_info->pch_data[i]);
+		dev_dbg(&pdev->dev, "pch_init invoked successfully\n");
+	}
+
+	ret = request_irq(pdev->irq, &pch_handler, IRQF_SHARED,
+					  MODULE_NAME, adap_info);
+
+	if (ret) {
+		dev_err(&pdev->dev, "request_irq Failed\n");
+		goto err_request_irq;
+	}
+
+	dev_dbg(&pdev->dev, "request_irq returns %d pch_probe returns.\n", ret);
+	pci_set_drvdata(pdev, adap_info);
+	return 0;
+
+err_request_irq:
+	for (i = 0; i < PCH_MAX_CHN; i++)
+		i2c_del_adapter(&(adap_info->pch_data[i].pch_adapter));
+err_i2c_add_adapter:
+	pci_iounmap(pdev, base_addr);
+err_pci_iomap:
+	pci_release_regions(pdev);
+err_pci_req:
+	pci_disable_device(pdev);
+err_pci_enable:
+	kfree(adap_info);
+return_err:
+	return ret;
+}
+
+static void __devexit pch_remove(struct pci_dev *pdev)
+{
+	int i;
+
+	struct adapter_info *adap_info = pci_get_drvdata(pdev);
+
+	dev_dbg(&pdev->dev, "invoked function pci_get_drvdata successfully\n");
+
+	for (i = 0; i < PCH_MAX_CHN; i++) {
+		pch_disbl_int(&adap_info->pch_data[i]);
+
+		if (i == (PCH_MAX_CHN - 1)) {
+			free_irq(pdev->irq, adap_info);
+			dev_dbg(&pdev->dev, "free_irq invoked successfully\n");
+		}
+
+		i2c_del_adapter(&(adap_info->pch_data[i].pch_adapter));
+
+		dev_dbg(&pdev->dev, "invoked i2c_del_adapter successfully\n");
+	}
+
+	if (adap_info->pch_data[0].pch_base_address) {
+		pci_iounmap(pdev, adap_info->pch_data[0].pch_base_address);
+		dev_dbg(&pdev->dev, "pci_iounmap invoked successfully\n");
+		adap_info->pch_data[0].pch_base_address = 0;
+	}
+
+	pci_set_drvdata(pdev, NULL);
+
+	pci_release_regions(pdev);
+	dev_dbg(&pdev->dev, "pci_release_regions invoked successfully\n");
+
+	pci_disable_device(pdev);
+	kfree(adap_info);
+	dev_dbg(&pdev->dev,
+		"pci_disable_device invoked success.%s invoked success\n",
+		__func__);
+}
+
+#ifdef CONFIG_PM
+static int pch_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	int i;
+	int ret;
+
+	struct adapter_info *adap_info = pci_get_drvdata(pdev);
+	void __iomem *p = adap_info->pch_data[0].pch_base_address;
+
+	dev_dbg(&pdev->dev, "invoked function pci_get_drvdata successfully\n");
+
+	adap_info->pch_suspended = true;
+
+	for (i = 0; i < PCH_MAX_CHN; i++) {
+		while ((adap_info->pch_data[i].pch_xfer_in_progress)) {
+			/* It is assumed that any pending transfer will
+			 * be completed after the delay
+			 */
+			msleep(1);
+		}
+		/* Disable the i2c interrupts */
+		pch_disbl_int(&adap_info->pch_data[i]);
+	}
+
+	dev_dbg(&pdev->dev,
+		"I2CSR = %x I2CBUFSTA = %x I2CESRSTA = %x "
+		"invoked function pch_disbl_int successfully\n",
+		ioread32(p + 0x08),
+		ioread32(p + 0x30),
+		ioread32(p + 0x44));
+
+	ret = pci_save_state(pdev);
+
+	if (ret) {
+		dev_err(&pdev->dev, "pci_save_state failed\n");
+		return ret;
+	}
+
+	dev_dbg(&pdev->dev, "Invoked pci_save_state successfully\n");
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "Invoked pci_enable_wake successfully\n");
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "Invoked pci_disable_device successfully\n");
+
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+	dev_dbg(&pdev->dev,
+		"Invoked pci_set_power_state successfully. %s returns 0\n",
+		__func__);
+
+	return 0;
+}
+
+static int pch_resume(struct pci_dev *pdev)
+{
+	struct adapter_info *adap_info = pci_get_drvdata(pdev);
+	int i;
+
+	dev_dbg(&pdev->dev, "invoked function pci_get_drvdata successfully\n");
+
+	pci_set_power_state(pdev, PCI_D0);
+	dev_dbg(&pdev->dev, "Invoked pci_set_power_state successfully\n");
+
+	pci_restore_state(pdev);
+	dev_dbg(&pdev->dev, "Invoked pci_restore_state successfully\n");
+
+	if (pci_enable_device(pdev) < 0) {
+		dev_err(&pdev->dev, "pci_enable_device failed in pch_resume\n");
+		return -EIO;
+	}
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+
+	dev_dbg(&pdev->dev, "Invoked pci_enable_wake successfully\n");
+
+	for (i = 0; i < PCH_MAX_CHN; i++)
+		pch_init(&adap_info->pch_data[i]);
+
+	dev_dbg(&pdev->dev, "Invoked pch_init successfully\n");
+
+	adap_info->pch_suspended = false;
+
+	dev_dbg(&pdev->dev, "%s return 0\n", __func__);
+	return 0;
+}
+#else
+#define pch_suspend NULL
+#define pch_resume NULL
+#endif
+
+static struct pci_driver pch_pcidriver = {
+	.name = "pch_i2c",
+	.id_table = pch_pcidev_id,
+	.probe = pch_probe,
+	.remove = __devexit_p(pch_remove),
+	.suspend = pch_suspend,
+	.resume = pch_resume
+};
+
+static int __init pch_pci_init(void)
+{
+	return pci_register_driver(&pch_pcidriver);
+}
+
+static void __exit pch_pci_exit(void)
+{
+	pci_unregister_driver(&pch_pcidriver);
+}
+
+MODULE_DESCRIPTION("PCH I2C PCI Driver");
+MODULE_LICENSE("GPL");
+module_init(pch_pci_init);
+module_exit(pch_pci_exit);
+module_param(pch_i2c_speed, int, (S_IRUSR | S_IWUSR));
+module_param(pch_clk, int, (S_IRUSR | S_IWUSR));
diff --git a/drivers/i2c/busses/i2c-pch.h b/drivers/i2c/busses/i2c-pch.h
new file mode 100644
index 0000000..f140ce0
--- /dev/null
+++ b/drivers/i2c/busses/i2c-pch.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef __PCH_HAL_H__
+#define __PCH_HAL_H__
+
+#define PCH_MAX_CHN	1	/* Maximum I2C channels available */
+#define PCH_EVENT_SET	0	/* I2C Interrupt Event Set Status */
+#define PCH_EVENT_NONE	1	/* I2C Interrupt Event Clear Status */
+#define PCH_MAX_CLK		100000	/* Maximum Clock speed in MHz */
+#define PCH_BUFFER_MODE_ENABLE	0x0002	/* flag for Buffer mode enable */
+#define PCH_EEPROM_SW_RST_MODE_ENABLE	0x0008	/* EEPROM SW RST enable flag */
+
+#define I2C_MODE_SEL	0x711	/* for mode selection */
+
+#define PCH_I2CSADR	0x00	/* I2C slave address register */
+#define PCH_I2CCTL	0x04	/* I2C control register */
+#define PCH_I2CSR	0x08	/* I2C status register */
+#define PCH_I2CDR	0x0C	/* I2C data register */
+#define PCH_I2CMON	0x10	/* I2C bus monitor register */
+#define PCH_I2CBC	0x14	/* I2C bus transfer rate setup counter */
+#define PCH_I2CMOD	0x18	/* I2C mode register */
+#define PCH_I2CBUFSLV	0x1C	/* I2C buffer mode slave address register */
+#define PCH_I2CBUFSUB	0x20	/* I2C buffer mode subaddress register */
+#define PCH_I2CBUFFOR	0x24	/* I2C buffer mode format register */
+#define PCH_I2CBUFCTL	0x28	/* I2C buffer mode control register */
+#define PCH_I2CBUFMSK	0x2C	/* I2C buffer mode interrupt mask register */
+#define PCH_I2CBUFSTA	0x30	/* I2C buffer mode status register */
+#define PCH_I2CBUFLEV	0x34	/* I2C buffer mode level register */
+#define PCH_I2CESRFOR	0x38	/* EEPROM software reset mode format register */
+#define PCH_I2CESRCTL	0x3C	/* EEPROM software reset mode ctrl register */
+#define PCH_I2CESRMSK	0x40	/* EEPROM software reset mode */
+#define PCH_I2CESRSTA	0x44	/* EEPROM software reset mode status register */
+#define PCH_I2CTMR	0x48	/* I2C timer register */
+#define PCH_I2CSRST	0xFC	/* I2C reset register */
+#define PCH_I2CNF	0xF8	/* I2C noise filter register */
+
+#define BUS_IDLE_TIMEOUT	20
+#define PCH_I2CCTL_I2CMEN	0x0080
+#define TEN_BIT_ADDR_DEFAULT	0xF000
+#define TEN_BIT_ADDR_MASK	0xF0
+#define PCH_START		0x0020
+#define PCH_ESR_START		0x0001
+#define PCH_BUFF_START		0x1
+#define PCH_REPSTART		0x0004
+#define PCH_ACK			0x0008
+#define PCH_GETACK		0x0001
+#define CLR_REG			0x0
+#define I2C_RD			0x1
+#define I2CMCF_BIT		0x0080
+#define I2CMIF_BIT		0x0002
+#define I2CMAL_BIT		0x0010
+#define I2CBMFI_BIT		0x0001
+#define I2CBMAL_BIT		0x0002
+#define I2CBMNA_BIT		0x0004
+#define I2CBMTO_BIT		0x0008
+#define I2CBMIS_BIT		0x0010
+#define I2CESRFI_BIT		0X0001
+#define I2CESRTO_BIT		0x0002
+#define I2CESRFIIE_BIT		0x1
+#define I2CESRTOIE_BIT		0x2
+#define I2CBMDZ_BIT		0x0040
+#define I2CBMAG_BIT		0x0020
+#define I2CMBB_BIT		0x0020
+#define BUFFER_MODE_MASK	(I2CBMFI_BIT | I2CBMAL_BIT | I2CBMNA_BIT | \
+				I2CBMTO_BIT | I2CBMIS_BIT)
+#define I2C_ADDR_MSK		0xFF
+#define I2C_MSB_2B_MSK		0x300
+#define FAST_MODE_CLK		400
+#define FAST_MODE_EN		0x0001
+#define SUB_ADDR_LEN_MAX	4
+#define BUF_LEN_MAX		32
+#define PCH_BUFFER_MODE		0x1
+#define EEPROM_SW_RST_MODE	0x0002
+#define NORMAL_INTR_ENBL	0x0300
+#define EEPROM_RST_INTR_ENBL	(I2CESRFIIE_BIT | I2CESRTOIE_BIT)
+#define EEPROM_RST_INTR_DISBL	0x0
+#define BUFFER_MODE_INTR_ENBL	0x001F
+#define BUFFER_MODE_INTR_DISBL	0x0
+#define NORMAL_MODE		0x0
+#define BUFFER_MODE		0x1
+#define EEPROM_SR_MODE		0x2
+#define I2C_TX_MODE		0x0010
+#define PCH_BUF_TX		0xFFF7
+#define PCH_BUF_RD		0x0008
+#define I2C_ERROR_MASK	(I2CESRTO_EVENT | I2CBMIS_EVENT | I2CBMTO_EVENT | \
+			I2CBMNA_EVENT | I2CBMAL_EVENT | I2CMAL_EVENT)
+#define I2CMAL_EVENT		0x0001
+#define I2CMCF_EVENT		0x0002
+#define I2CBMFI_EVENT		0x0004
+#define I2CBMAL_EVENT		0x0008
+#define I2CBMNA_EVENT		0x0010
+#define I2CBMTO_EVENT		0x0020
+#define I2CBMIS_EVENT		0x0040
+#define I2CESRFI_EVENT		0x0080
+#define I2CESRTO_EVENT		0x0100
+
+#define MODULE_NAME		"pch_i2c"
+#define PCI_DEVICE_ID_PCH_I2C	0x8817
+
+/**
+ * struct i2c_algo_pch_data - for I2C driver functionalities
+ * @p_adapter_info:		stores the reference to adapter_info structure
+ * @pch_adapter:		stores the reference to i2c_adapter structure
+ * @pch_base_address:		specifies the remapped base address
+ * @pch_buff_mode_en:		specifies if buffer mode is enabled
+ * @pch_event_flag:		specifies occurrence of interrupt events
+ * @pch_xfer_in_progress:	specifies whether the transfer is completed
+ */
+struct i2c_algo_pch_data {
+	struct adapter_info *p_adapter_info;
+	struct i2c_adapter pch_adapter;
+	void __iomem *pch_base_address;
+	int pch_buff_mode_en;
+	u32 pch_event_flag;
+	bool pch_xfer_in_progress;
+};
+
+/**
+ * struct adapter_info - This structure holds the adapter information for the
+			 PCH i2c controller
+ * @pch_data:	stores a list of i2c_algo_pch_data
+ * @pch_suspended:	specifies whether the system is suspended or not
+ *		perhaps with more lines and words.
+ *
+ * pch_data has as many elements as maximum I2C channels
+ */
+struct adapter_info {
+	struct i2c_algo_pch_data pch_data[PCH_MAX_CHN];
+	bool pch_suspended;
+};
+
+#endif
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index e0694e4..0df77a7 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -36,6 +36,7 @@
 #include <linux/i2c-dev.h>
 #include <linux/jiffies.h>
 #include <linux/uaccess.h>
+#include "busses/i2c-pch.h"
 
 static struct i2c_driver i2cdev_driver;
 
@@ -147,6 +148,11 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
 	if (tmp == NULL)
 		return -ENOMEM;
 
+	if (copy_from_user(tmp, buf, count)) {
+		kfree(tmp);
+		return -EFAULT;
+	}
+
 	pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
 		iminor(file->f_path.dentry->d_inode), count);
 
@@ -372,6 +378,12 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	struct i2c_client *client = file->private_data;
 	unsigned long funcs;
 
+	unsigned long pch_mode;
+	int ret;
+
+	struct i2c_msg msg;
+	unsigned char msgbuf[1];
+
 	dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
 		cmd, arg);
 
@@ -427,6 +439,22 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 		 */
 		client->adapter->timeout = msecs_to_jiffies(arg * 10);
 		break;
+	case I2C_MODE_SEL:
+		pch_mode = arg;
+
+		if (pch_mode <= 4) {
+			msgbuf[0] = pch_mode;
+			msg.buf = msgbuf;
+			msg.len = 1;
+			msg.flags = (pch_mode <= 1) ? \
+					(PCH_BUFFER_MODE_ENABLE) : \
+					(PCH_EEPROM_SW_RST_MODE_ENABLE);
+			ret = i2c_transfer(client->adapter, &msg, 1);
+		} else {
+			printk(KERN_ERR "I2C mode sel:Invalid mode\n");
+			ret = -EINVAL;
+		}
+		return ret;
 	default:
 		/* NOTE:  returning a fault code here could cause trouble
 		 * in buggy userspace code.  Some old kernel bugs returned
-- 
1.6.0.6


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

* Re: [PATCH] I2C driver of Topcliff PCH
@ 2010-07-15 19:35     ` Arnd Bergmann
  0 siblings, 0 replies; 46+ messages in thread
From: Arnd Bergmann @ 2010-07-15 19:35 UTC (permalink / raw)
  To: Masayuki Ohtak
  Cc: Jean Delvare (PC drivers, core), Ben Dooks (embedded platforms),
	linux-i2c, LKML, qi.wang, Wang, Yong Y, joel.clark,
	andrew.chih.howe.khor

On Thursday 15 July 2010 09:42:36 Masayuki Ohtak wrote:
> I2C driver of Topcliff PCH
> 
> Topcliff PCH is the platform controller hub that is going to be used in
> Intel's upcoming general embedded platform. All IO peripherals in
> Topcliff PCH are actually devices sitting on AMBA bus.
> Topcliff PCH has I2C I/F. Using this I/F, it is able to access system
> devices connected to I2C.

Looks ok in general. Some minor comments follow:

> +/**
> + * pch_wait_for_bus_idle() - check the status of bus.
> + * @adap:	Pointer to struct i2c_algo_pch_data.
> + * @timeout:	waiting time counter (us).
> + */
> +static s32 pch_wait_for_bus_idle(struct i2c_algo_pch_data *adap,
> +				 s32 timeout)
> +{
> +	u32 reg_value;
> +	void __iomem *p = adap->pch_base_address;
> +
> +	/* get the status of bus busy */
> +	reg_value = (ioread32(p + PCH_I2CSR) & I2CMBB_BIT);
> +
> +	while ((timeout != 0) && (reg_value != 0)) {
> +		msleep(1);	/* wait for 100 ms */
> +		reg_value = ioread32(p + PCH_I2CSR) & I2CMBB_BIT;
> +
> +		timeout--;
> +	}


If you want to wait for a maximum amount of time, a loop with
msleep(1) may end up waiting more than twice as long as you want,
because each msleep typically returns one milisecond too late.

Better use something like:

	time_t timeout = ktime_add_ns(ktime_get(), MAX_NANOSECONDS);

	do {
		if (ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
			break;
		msleep(1);
	} while (ktime_lt(ktime_get(), timeout));

> +/**
> + * pch_wait_for_xfer_complete() - initiates a wait for the tx complete event
> + * @adap:	Pointer to struct i2c_algo_pch_data.
> + */
> +static s32 pch_wait_for_xfer_complete(struct i2c_algo_pch_data *adap)
> +{
> +	u32 temp_flag;
> +	s32 ret;
> +	ret = wait_event_interruptible_timeout(pch_event,
> +			(adap->pch_event_flag != 0), msecs_to_jiffies(50));
> +
> +	dev_dbg(adap->pch_adapter.dev.parent,
> +		"%s adap->pch_event_flag = %x", __func__, adap->pch_event_flag);
> +	temp_flag = adap->pch_event_flag;
> +	adap->pch_event_flag = 0;
> +
> +	if (ret == 0) {
> +		dev_err(adap->pch_adapter.dev.parent,
> +				"%s : Timeout\n", __func__);
> +	} else if (ret < 0) {
> +		dev_err(adap->pch_adapter.dev.parent,
> +			"%s failed : Interrupted by other signal\n", __func__);
> +		ret = -ERESTARTSYS;
> +	} else if ((temp_flag & I2C_ERROR_MASK) == 0) {
> +		ret = 0;
> +	} else {
> +		dev_err(adap->pch_adapter.dev.parent,
> +				"%s failed : Error in transfer\n", __func__);
> +	}
> +
> +	dev_err(adap->pch_adapter.dev.parent, "%s returns %d\n", __func__, ret);
> +
> +	return ret;
> +}

The control flow is much more complex than it needs to be here.
If you want to handle different kinds of error conditions, best
use goto. Also, it's very unusual to return positive values
on errors and to print dev_err messages on success.

	int ret;
	ret = wait_event_interruptible_timeout(...);
	if (ret < 0)
		goto out;

	if (ret == 0) {
		ret = -ETIMEOUT;
		goto out;
	}

	ret = 0;
	if (adap->pch_event_flag & I2C_ERROR_MASK) {
		ret = -EIO;
		dev_err(adap->pch_adapter.dev.parent, "error bits set: %lx\n", adap->pch_event_flag);
	}

out:
	return ret;

> +/**
> + * pch_handler() - interrupt handler for the PCH I2C controller
> + * @irq:	irq number.
> + * @pData:	cookie passed back to the handler function.
> + */
> +static irqreturn_t pch_handler(int irq, void *pData)
> +{
> +	s32 ret;
> +	u32 i;
> +
> +	struct adapter_info *adap_info = (struct adapter_info *)pData;
> +	/* invoke the call back  */
> +
> +	if (pch_cbr != NULL) {
> +		for (i = 0, ret = 0; i < PCH_MAX_CHN; i++)
> +			ret |= (pch_cbr) (&adap_info->pch_data[i]);
> +	} else {
> +		dev_err(adap_info->pch_data[0].pch_adapter.dev.parent,
> +				"%s Call back pointer null ...", __func__);
> +		return IRQ_NONE;
> +	}

Here you are multiplexing the interrupt yourself. If you don't
always use all the available channels, it may be better to
register the pch_cbr handler separately for each of the channels
that are actually used, so you don't have to invoke the callback
for all of them all the time.

> +	for (i = 0; i < PCH_MAX_CHN; i++) {
> +		adap_info->pch_data[i].p_adapter_info = adap_info;
> +
> +		adap_info->pch_data[i].pch_adapter.owner = THIS_MODULE;
> +		adap_info->pch_data[i].pch_adapter.class = I2C_CLASS_HWMON;
> +		strcpy(adap_info->pch_data[i].pch_adapter.name, "pch_i2c");
> +		adap_info->pch_data[i].pch_adapter.algo = &pch_algorithm;
> +		adap_info->pch_data[i].pch_adapter.algo_data =
> +							&adap_info->pch_data[i];
> +
> +		/* (i * 0x80) + base_addr; */
> +		adap_info->pch_data[i].pch_base_address = base_addr;
> +
> +		adap_info->pch_data[i].pch_adapter.dev.parent = &pdev->dev;
> +
> +		ret = i2c_add_adapter(&(adap_info->pch_data[i].pch_adapter));
> +
> +		if (ret) {
> +			dev_err(&pdev->dev, "i2c_add_adapter FAILED");
> +			goto err_i2c_add_adapter;
> +		}
> +
> +		dev_dbg(&pdev->dev,
> +			"i2c_add_adapter returns %d for channel-%d\n", ret, i);
> +		pch_init(&adap_info->pch_data[i]);
> +		dev_dbg(&pdev->dev, "pch_init invoked successfully\n");
> +	}
> +
> +	ret = request_irq(pdev->irq, &pch_handler, IRQF_SHARED,
> +					  MODULE_NAME, adap_info);

Similarly, you would create a new channel data structure for each channel here
and register it separately, and then request the interrupt with that
data structure as the info.

> @@ -147,6 +148,11 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
>  	if (tmp == NULL)
>  		return -ENOMEM;
>  
> +	if (copy_from_user(tmp, buf, count)) {
> +		kfree(tmp);
> +		return -EFAULT;
> +	}
> +
>  	pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
>  		iminor(file->f_path.dentry->d_inode), count);


A read function should not do copy_from_user, only copy_to_user.
If you are worried about returning invalid data from kernel space,
better use kzalloc instead of kmalloc to get the buffer.

	Arnd

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

* Re: [PATCH] I2C driver of Topcliff PCH
@ 2010-07-15 19:35     ` Arnd Bergmann
  0 siblings, 0 replies; 46+ messages in thread
From: Arnd Bergmann @ 2010-07-15 19:35 UTC (permalink / raw)
  To: Masayuki Ohtak
  Cc: Jean Delvare (PC drivers, core), Ben Dooks (embedded platforms),
	linux-i2c-u79uwXL29TY76Z2rM5mHXA, LKML,
	qi.wang-ral2JQCrhuEAvxtiuMwx3w, Wang, Yong Y,
	joel.clark-ral2JQCrhuEAvxtiuMwx3w,
	andrew.chih.howe.khor-ral2JQCrhuEAvxtiuMwx3w

On Thursday 15 July 2010 09:42:36 Masayuki Ohtak wrote:
> I2C driver of Topcliff PCH
> 
> Topcliff PCH is the platform controller hub that is going to be used in
> Intel's upcoming general embedded platform. All IO peripherals in
> Topcliff PCH are actually devices sitting on AMBA bus.
> Topcliff PCH has I2C I/F. Using this I/F, it is able to access system
> devices connected to I2C.

Looks ok in general. Some minor comments follow:

> +/**
> + * pch_wait_for_bus_idle() - check the status of bus.
> + * @adap:	Pointer to struct i2c_algo_pch_data.
> + * @timeout:	waiting time counter (us).
> + */
> +static s32 pch_wait_for_bus_idle(struct i2c_algo_pch_data *adap,
> +				 s32 timeout)
> +{
> +	u32 reg_value;
> +	void __iomem *p = adap->pch_base_address;
> +
> +	/* get the status of bus busy */
> +	reg_value = (ioread32(p + PCH_I2CSR) & I2CMBB_BIT);
> +
> +	while ((timeout != 0) && (reg_value != 0)) {
> +		msleep(1);	/* wait for 100 ms */
> +		reg_value = ioread32(p + PCH_I2CSR) & I2CMBB_BIT;
> +
> +		timeout--;
> +	}


If you want to wait for a maximum amount of time, a loop with
msleep(1) may end up waiting more than twice as long as you want,
because each msleep typically returns one milisecond too late.

Better use something like:

	time_t timeout = ktime_add_ns(ktime_get(), MAX_NANOSECONDS);

	do {
		if (ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
			break;
		msleep(1);
	} while (ktime_lt(ktime_get(), timeout));

> +/**
> + * pch_wait_for_xfer_complete() - initiates a wait for the tx complete event
> + * @adap:	Pointer to struct i2c_algo_pch_data.
> + */
> +static s32 pch_wait_for_xfer_complete(struct i2c_algo_pch_data *adap)
> +{
> +	u32 temp_flag;
> +	s32 ret;
> +	ret = wait_event_interruptible_timeout(pch_event,
> +			(adap->pch_event_flag != 0), msecs_to_jiffies(50));
> +
> +	dev_dbg(adap->pch_adapter.dev.parent,
> +		"%s adap->pch_event_flag = %x", __func__, adap->pch_event_flag);
> +	temp_flag = adap->pch_event_flag;
> +	adap->pch_event_flag = 0;
> +
> +	if (ret == 0) {
> +		dev_err(adap->pch_adapter.dev.parent,
> +				"%s : Timeout\n", __func__);
> +	} else if (ret < 0) {
> +		dev_err(adap->pch_adapter.dev.parent,
> +			"%s failed : Interrupted by other signal\n", __func__);
> +		ret = -ERESTARTSYS;
> +	} else if ((temp_flag & I2C_ERROR_MASK) == 0) {
> +		ret = 0;
> +	} else {
> +		dev_err(adap->pch_adapter.dev.parent,
> +				"%s failed : Error in transfer\n", __func__);
> +	}
> +
> +	dev_err(adap->pch_adapter.dev.parent, "%s returns %d\n", __func__, ret);
> +
> +	return ret;
> +}

The control flow is much more complex than it needs to be here.
If you want to handle different kinds of error conditions, best
use goto. Also, it's very unusual to return positive values
on errors and to print dev_err messages on success.

	int ret;
	ret = wait_event_interruptible_timeout(...);
	if (ret < 0)
		goto out;

	if (ret == 0) {
		ret = -ETIMEOUT;
		goto out;
	}

	ret = 0;
	if (adap->pch_event_flag & I2C_ERROR_MASK) {
		ret = -EIO;
		dev_err(adap->pch_adapter.dev.parent, "error bits set: %lx\n", adap->pch_event_flag);
	}

out:
	return ret;

> +/**
> + * pch_handler() - interrupt handler for the PCH I2C controller
> + * @irq:	irq number.
> + * @pData:	cookie passed back to the handler function.
> + */
> +static irqreturn_t pch_handler(int irq, void *pData)
> +{
> +	s32 ret;
> +	u32 i;
> +
> +	struct adapter_info *adap_info = (struct adapter_info *)pData;
> +	/* invoke the call back  */
> +
> +	if (pch_cbr != NULL) {
> +		for (i = 0, ret = 0; i < PCH_MAX_CHN; i++)
> +			ret |= (pch_cbr) (&adap_info->pch_data[i]);
> +	} else {
> +		dev_err(adap_info->pch_data[0].pch_adapter.dev.parent,
> +				"%s Call back pointer null ...", __func__);
> +		return IRQ_NONE;
> +	}

Here you are multiplexing the interrupt yourself. If you don't
always use all the available channels, it may be better to
register the pch_cbr handler separately for each of the channels
that are actually used, so you don't have to invoke the callback
for all of them all the time.

> +	for (i = 0; i < PCH_MAX_CHN; i++) {
> +		adap_info->pch_data[i].p_adapter_info = adap_info;
> +
> +		adap_info->pch_data[i].pch_adapter.owner = THIS_MODULE;
> +		adap_info->pch_data[i].pch_adapter.class = I2C_CLASS_HWMON;
> +		strcpy(adap_info->pch_data[i].pch_adapter.name, "pch_i2c");
> +		adap_info->pch_data[i].pch_adapter.algo = &pch_algorithm;
> +		adap_info->pch_data[i].pch_adapter.algo_data =
> +							&adap_info->pch_data[i];
> +
> +		/* (i * 0x80) + base_addr; */
> +		adap_info->pch_data[i].pch_base_address = base_addr;
> +
> +		adap_info->pch_data[i].pch_adapter.dev.parent = &pdev->dev;
> +
> +		ret = i2c_add_adapter(&(adap_info->pch_data[i].pch_adapter));
> +
> +		if (ret) {
> +			dev_err(&pdev->dev, "i2c_add_adapter FAILED");
> +			goto err_i2c_add_adapter;
> +		}
> +
> +		dev_dbg(&pdev->dev,
> +			"i2c_add_adapter returns %d for channel-%d\n", ret, i);
> +		pch_init(&adap_info->pch_data[i]);
> +		dev_dbg(&pdev->dev, "pch_init invoked successfully\n");
> +	}
> +
> +	ret = request_irq(pdev->irq, &pch_handler, IRQF_SHARED,
> +					  MODULE_NAME, adap_info);

Similarly, you would create a new channel data structure for each channel here
and register it separately, and then request the interrupt with that
data structure as the info.

> @@ -147,6 +148,11 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
>  	if (tmp == NULL)
>  		return -ENOMEM;
>  
> +	if (copy_from_user(tmp, buf, count)) {
> +		kfree(tmp);
> +		return -EFAULT;
> +	}
> +
>  	pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
>  		iminor(file->f_path.dentry->d_inode), count);


A read function should not do copy_from_user, only copy_to_user.
If you are worried about returning invalid data from kernel space,
better use kzalloc instead of kmalloc to get the buffer.

	Arnd

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

* Re: [PATCH] I2C driver of Topcliff PCH
  2010-07-15 19:35     ` Arnd Bergmann
@ 2010-07-20  0:05       ` Masayuki Ohtake
  -1 siblings, 0 replies; 46+ messages in thread
From: Masayuki Ohtake @ 2010-07-20  0:05 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Jean Delvare (PC drivers, core), Ben Dooks (embedded platforms),
	linux-i2c, LKML, qi.wang, Wang, Yong Y, joel.clark,
	andrew.chih.howe.khor

Hi Arnd,

Thank you for you comments.
I will modify our i2c patch by tomorrow.

Thanks, Ohtake
----- Original Message ----- 
From: "Arnd Bergmann" <arnd@arndb.de>
To: "Masayuki Ohtak" <masa-korg@dsn.okisemi.com>
Cc: "Jean Delvare (PC drivers, core)" <khali@linux-fr.org>; "Ben Dooks (embedded platforms)" <ben-linux@fluff.org>;
<linux-i2c@vger.kernel.org>; "LKML" <linux-kernel@vger.kernel.org>; <qi.wang@intel.com>; "Wang, Yong Y"
<yong.y.wang@intel.com>; <joel.clark@intel.com>; <andrew.chih.howe.khor@intel.com>
Sent: Friday, July 16, 2010 4:35 AM
Subject: Re: [PATCH] I2C driver of Topcliff PCH


> On Thursday 15 July 2010 09:42:36 Masayuki Ohtak wrote:
> > I2C driver of Topcliff PCH
> >
> > Topcliff PCH is the platform controller hub that is going to be used in
> > Intel's upcoming general embedded platform. All IO peripherals in
> > Topcliff PCH are actually devices sitting on AMBA bus.
> > Topcliff PCH has I2C I/F. Using this I/F, it is able to access system
> > devices connected to I2C.
>
> Looks ok in general. Some minor comments follow:
>
> > +/**
> > + * pch_wait_for_bus_idle() - check the status of bus.
> > + * @adap: Pointer to struct i2c_algo_pch_data.
> > + * @timeout: waiting time counter (us).
> > + */
> > +static s32 pch_wait_for_bus_idle(struct i2c_algo_pch_data *adap,
> > + s32 timeout)
> > +{
> > + u32 reg_value;
> > + void __iomem *p = adap->pch_base_address;
> > +
> > + /* get the status of bus busy */
> > + reg_value = (ioread32(p + PCH_I2CSR) & I2CMBB_BIT);
> > +
> > + while ((timeout != 0) && (reg_value != 0)) {
> > + msleep(1); /* wait for 100 ms */
> > + reg_value = ioread32(p + PCH_I2CSR) & I2CMBB_BIT;
> > +
> > + timeout--;
> > + }
>
>
> If you want to wait for a maximum amount of time, a loop with
> msleep(1) may end up waiting more than twice as long as you want,
> because each msleep typically returns one milisecond too late.
>
> Better use something like:
>
> time_t timeout = ktime_add_ns(ktime_get(), MAX_NANOSECONDS);
>
> do {
> if (ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
> break;
> msleep(1);
> } while (ktime_lt(ktime_get(), timeout));
>
> > +/**
> > + * pch_wait_for_xfer_complete() - initiates a wait for the tx complete event
> > + * @adap: Pointer to struct i2c_algo_pch_data.
> > + */
> > +static s32 pch_wait_for_xfer_complete(struct i2c_algo_pch_data *adap)
> > +{
> > + u32 temp_flag;
> > + s32 ret;
> > + ret = wait_event_interruptible_timeout(pch_event,
> > + (adap->pch_event_flag != 0), msecs_to_jiffies(50));
> > +
> > + dev_dbg(adap->pch_adapter.dev.parent,
> > + "%s adap->pch_event_flag = %x", __func__, adap->pch_event_flag);
> > + temp_flag = adap->pch_event_flag;
> > + adap->pch_event_flag = 0;
> > +
> > + if (ret == 0) {
> > + dev_err(adap->pch_adapter.dev.parent,
> > + "%s : Timeout\n", __func__);
> > + } else if (ret < 0) {
> > + dev_err(adap->pch_adapter.dev.parent,
> > + "%s failed : Interrupted by other signal\n", __func__);
> > + ret = -ERESTARTSYS;
> > + } else if ((temp_flag & I2C_ERROR_MASK) == 0) {
> > + ret = 0;
> > + } else {
> > + dev_err(adap->pch_adapter.dev.parent,
> > + "%s failed : Error in transfer\n", __func__);
> > + }
> > +
> > + dev_err(adap->pch_adapter.dev.parent, "%s returns %d\n", __func__, ret);
> > +
> > + return ret;
> > +}
>
> The control flow is much more complex than it needs to be here.
> If you want to handle different kinds of error conditions, best
> use goto. Also, it's very unusual to return positive values
> on errors and to print dev_err messages on success.
>
> int ret;
> ret = wait_event_interruptible_timeout(...);
> if (ret < 0)
> goto out;
>
> if (ret == 0) {
> ret = -ETIMEOUT;
> goto out;
> }
>
> ret = 0;
> if (adap->pch_event_flag & I2C_ERROR_MASK) {
> ret = -EIO;
> dev_err(adap->pch_adapter.dev.parent, "error bits set: %lx\n", adap->pch_event_flag);
> }
>
> out:
> return ret;
>
> > +/**
> > + * pch_handler() - interrupt handler for the PCH I2C controller
> > + * @irq: irq number.
> > + * @pData: cookie passed back to the handler function.
> > + */
> > +static irqreturn_t pch_handler(int irq, void *pData)
> > +{
> > + s32 ret;
> > + u32 i;
> > +
> > + struct adapter_info *adap_info = (struct adapter_info *)pData;
> > + /* invoke the call back  */
> > +
> > + if (pch_cbr != NULL) {
> > + for (i = 0, ret = 0; i < PCH_MAX_CHN; i++)
> > + ret |= (pch_cbr) (&adap_info->pch_data[i]);
> > + } else {
> > + dev_err(adap_info->pch_data[0].pch_adapter.dev.parent,
> > + "%s Call back pointer null ...", __func__);
> > + return IRQ_NONE;
> > + }
>
> Here you are multiplexing the interrupt yourself. If you don't
> always use all the available channels, it may be better to
> register the pch_cbr handler separately for each of the channels
> that are actually used, so you don't have to invoke the callback
> for all of them all the time.
>
> > + for (i = 0; i < PCH_MAX_CHN; i++) {
> > + adap_info->pch_data[i].p_adapter_info = adap_info;
> > +
> > + adap_info->pch_data[i].pch_adapter.owner = THIS_MODULE;
> > + adap_info->pch_data[i].pch_adapter.class = I2C_CLASS_HWMON;
> > + strcpy(adap_info->pch_data[i].pch_adapter.name, "pch_i2c");
> > + adap_info->pch_data[i].pch_adapter.algo = &pch_algorithm;
> > + adap_info->pch_data[i].pch_adapter.algo_data =
> > + &adap_info->pch_data[i];
> > +
> > + /* (i * 0x80) + base_addr; */
> > + adap_info->pch_data[i].pch_base_address = base_addr;
> > +
> > + adap_info->pch_data[i].pch_adapter.dev.parent = &pdev->dev;
> > +
> > + ret = i2c_add_adapter(&(adap_info->pch_data[i].pch_adapter));
> > +
> > + if (ret) {
> > + dev_err(&pdev->dev, "i2c_add_adapter FAILED");
> > + goto err_i2c_add_adapter;
> > + }
> > +
> > + dev_dbg(&pdev->dev,
> > + "i2c_add_adapter returns %d for channel-%d\n", ret, i);
> > + pch_init(&adap_info->pch_data[i]);
> > + dev_dbg(&pdev->dev, "pch_init invoked successfully\n");
> > + }
> > +
> > + ret = request_irq(pdev->irq, &pch_handler, IRQF_SHARED,
> > +   MODULE_NAME, adap_info);
>
> Similarly, you would create a new channel data structure for each channel here
> and register it separately, and then request the interrupt with that
> data structure as the info.
>
> > @@ -147,6 +148,11 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
> >  if (tmp == NULL)
> >  return -ENOMEM;
> >
> > + if (copy_from_user(tmp, buf, count)) {
> > + kfree(tmp);
> > + return -EFAULT;
> > + }
> > +
> >  pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
> >  iminor(file->f_path.dentry->d_inode), count);
>
>
> A read function should not do copy_from_user, only copy_to_user.
> If you are worried about returning invalid data from kernel space,
> better use kzalloc instead of kmalloc to get the buffer.
>
> Arnd
>



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

* Re: [PATCH] I2C driver of Topcliff PCH
@ 2010-07-20  0:05       ` Masayuki Ohtake
  0 siblings, 0 replies; 46+ messages in thread
From: Masayuki Ohtake @ 2010-07-20  0:05 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Jean Delvare (PC drivers, core), Ben Dooks (embedded platforms),
	linux-i2c, LKML, qi.wang, Wang, Yong Y, joel.clark,
	andrew.chih.howe.khor

Hi Arnd,

Thank you for you comments.
I will modify our i2c patch by tomorrow.

Thanks, Ohtake
----- Original Message ----- 
From: "Arnd Bergmann" <arnd@arndb.de>
To: "Masayuki Ohtak" <masa-korg@dsn.okisemi.com>
Cc: "Jean Delvare (PC drivers, core)" <khali@linux-fr.org>; "Ben Dooks (embedded platforms)" <ben-linux@fluff.org>;
<linux-i2c@vger.kernel.org>; "LKML" <linux-kernel@vger.kernel.org>; <qi.wang@intel.com>; "Wang, Yong Y"
<yong.y.wang@intel.com>; <joel.clark@intel.com>; <andrew.chih.howe.khor@intel.com>
Sent: Friday, July 16, 2010 4:35 AM
Subject: Re: [PATCH] I2C driver of Topcliff PCH


> On Thursday 15 July 2010 09:42:36 Masayuki Ohtak wrote:
> > I2C driver of Topcliff PCH
> >
> > Topcliff PCH is the platform controller hub that is going to be used in
> > Intel's upcoming general embedded platform. All IO peripherals in
> > Topcliff PCH are actually devices sitting on AMBA bus.
> > Topcliff PCH has I2C I/F. Using this I/F, it is able to access system
> > devices connected to I2C.
>
> Looks ok in general. Some minor comments follow:
>
> > +/**
> > + * pch_wait_for_bus_idle() - check the status of bus.
> > + * @adap: Pointer to struct i2c_algo_pch_data.
> > + * @timeout: waiting time counter (us).
> > + */
> > +static s32 pch_wait_for_bus_idle(struct i2c_algo_pch_data *adap,
> > + s32 timeout)
> > +{
> > + u32 reg_value;
> > + void __iomem *p = adap->pch_base_address;
> > +
> > + /* get the status of bus busy */
> > + reg_value = (ioread32(p + PCH_I2CSR) & I2CMBB_BIT);
> > +
> > + while ((timeout != 0) && (reg_value != 0)) {
> > + msleep(1); /* wait for 100 ms */
> > + reg_value = ioread32(p + PCH_I2CSR) & I2CMBB_BIT;
> > +
> > + timeout--;
> > + }
>
>
> If you want to wait for a maximum amount of time, a loop with
> msleep(1) may end up waiting more than twice as long as you want,
> because each msleep typically returns one milisecond too late.
>
> Better use something like:
>
> time_t timeout = ktime_add_ns(ktime_get(), MAX_NANOSECONDS);
>
> do {
> if (ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
> break;
> msleep(1);
> } while (ktime_lt(ktime_get(), timeout));
>
> > +/**
> > + * pch_wait_for_xfer_complete() - initiates a wait for the tx complete event
> > + * @adap: Pointer to struct i2c_algo_pch_data.
> > + */
> > +static s32 pch_wait_for_xfer_complete(struct i2c_algo_pch_data *adap)
> > +{
> > + u32 temp_flag;
> > + s32 ret;
> > + ret = wait_event_interruptible_timeout(pch_event,
> > + (adap->pch_event_flag != 0), msecs_to_jiffies(50));
> > +
> > + dev_dbg(adap->pch_adapter.dev.parent,
> > + "%s adap->pch_event_flag = %x", __func__, adap->pch_event_flag);
> > + temp_flag = adap->pch_event_flag;
> > + adap->pch_event_flag = 0;
> > +
> > + if (ret == 0) {
> > + dev_err(adap->pch_adapter.dev.parent,
> > + "%s : Timeout\n", __func__);
> > + } else if (ret < 0) {
> > + dev_err(adap->pch_adapter.dev.parent,
> > + "%s failed : Interrupted by other signal\n", __func__);
> > + ret = -ERESTARTSYS;
> > + } else if ((temp_flag & I2C_ERROR_MASK) == 0) {
> > + ret = 0;
> > + } else {
> > + dev_err(adap->pch_adapter.dev.parent,
> > + "%s failed : Error in transfer\n", __func__);
> > + }
> > +
> > + dev_err(adap->pch_adapter.dev.parent, "%s returns %d\n", __func__, ret);
> > +
> > + return ret;
> > +}
>
> The control flow is much more complex than it needs to be here.
> If you want to handle different kinds of error conditions, best
> use goto. Also, it's very unusual to return positive values
> on errors and to print dev_err messages on success.
>
> int ret;
> ret = wait_event_interruptible_timeout(...);
> if (ret < 0)
> goto out;
>
> if (ret == 0) {
> ret = -ETIMEOUT;
> goto out;
> }
>
> ret = 0;
> if (adap->pch_event_flag & I2C_ERROR_MASK) {
> ret = -EIO;
> dev_err(adap->pch_adapter.dev.parent, "error bits set: %lx\n", adap->pch_event_flag);
> }
>
> out:
> return ret;
>
> > +/**
> > + * pch_handler() - interrupt handler for the PCH I2C controller
> > + * @irq: irq number.
> > + * @pData: cookie passed back to the handler function.
> > + */
> > +static irqreturn_t pch_handler(int irq, void *pData)
> > +{
> > + s32 ret;
> > + u32 i;
> > +
> > + struct adapter_info *adap_info = (struct adapter_info *)pData;
> > + /* invoke the call back  */
> > +
> > + if (pch_cbr != NULL) {
> > + for (i = 0, ret = 0; i < PCH_MAX_CHN; i++)
> > + ret |= (pch_cbr) (&adap_info->pch_data[i]);
> > + } else {
> > + dev_err(adap_info->pch_data[0].pch_adapter.dev.parent,
> > + "%s Call back pointer null ...", __func__);
> > + return IRQ_NONE;
> > + }
>
> Here you are multiplexing the interrupt yourself. If you don't
> always use all the available channels, it may be better to
> register the pch_cbr handler separately for each of the channels
> that are actually used, so you don't have to invoke the callback
> for all of them all the time.
>
> > + for (i = 0; i < PCH_MAX_CHN; i++) {
> > + adap_info->pch_data[i].p_adapter_info = adap_info;
> > +
> > + adap_info->pch_data[i].pch_adapter.owner = THIS_MODULE;
> > + adap_info->pch_data[i].pch_adapter.class = I2C_CLASS_HWMON;
> > + strcpy(adap_info->pch_data[i].pch_adapter.name, "pch_i2c");
> > + adap_info->pch_data[i].pch_adapter.algo = &pch_algorithm;
> > + adap_info->pch_data[i].pch_adapter.algo_data =
> > + &adap_info->pch_data[i];
> > +
> > + /* (i * 0x80) + base_addr; */
> > + adap_info->pch_data[i].pch_base_address = base_addr;
> > +
> > + adap_info->pch_data[i].pch_adapter.dev.parent = &pdev->dev;
> > +
> > + ret = i2c_add_adapter(&(adap_info->pch_data[i].pch_adapter));
> > +
> > + if (ret) {
> > + dev_err(&pdev->dev, "i2c_add_adapter FAILED");
> > + goto err_i2c_add_adapter;
> > + }
> > +
> > + dev_dbg(&pdev->dev,
> > + "i2c_add_adapter returns %d for channel-%d\n", ret, i);
> > + pch_init(&adap_info->pch_data[i]);
> > + dev_dbg(&pdev->dev, "pch_init invoked successfully\n");
> > + }
> > +
> > + ret = request_irq(pdev->irq, &pch_handler, IRQF_SHARED,
> > +   MODULE_NAME, adap_info);
>
> Similarly, you would create a new channel data structure for each channel here
> and register it separately, and then request the interrupt with that
> data structure as the info.
>
> > @@ -147,6 +148,11 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
> >  if (tmp == NULL)
> >  return -ENOMEM;
> >
> > + if (copy_from_user(tmp, buf, count)) {
> > + kfree(tmp);
> > + return -EFAULT;
> > + }
> > +
> >  pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
> >  iminor(file->f_path.dentry->d_inode), count);
>
>
> A read function should not do copy_from_user, only copy_to_user.
> If you are worried about returning invalid data from kernel space,
> better use kzalloc instead of kmalloc to get the buffer.
>
> Arnd
>

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

* Re: [PATCH] I2C driver of Topcliff PCH
  2010-07-15 19:35     ` Arnd Bergmann
@ 2010-07-20  4:55       ` Masayuki Ohtake
  -1 siblings, 0 replies; 46+ messages in thread
From: Masayuki Ohtake @ 2010-07-20  4:55 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Jean Delvare (PC drivers, core), Ben Dooks (embedded platforms),
	linux-i2c, LKML, qi.wang, Wang, Yong Y, joel.clark,
	andrew.chih.howe.khor

Hi Arnd

> > + struct adapter_info *adap_info = (struct adapter_info *)pData;
> > + /* invoke the call back  */
> > +
> > + if (pch_cbr != NULL) {
> > + for (i = 0, ret = 0; i < PCH_MAX_CHN; i++)
> > + ret |= (pch_cbr) (&adap_info->pch_data[i]);
> > + } else {
> > + dev_err(adap_info->pch_data[0].pch_adapter.dev.parent,
> > + "%s Call back pointer null ...", __func__);
> > + return IRQ_NONE;
> > + }
>
> Here you are multiplexing the interrupt yourself. If you don't
> always use all the available channels, it may be better to
> register the pch_cbr handler separately for each of the channels
> that are actually used, so you don't have to invoke the callback
> for all of them all the time.
>
> > + for (i = 0; i < PCH_MAX_CHN; i++) {
> > + adap_info->pch_data[i].p_adapter_info = adap_info;
> > +
> > + adap_info->pch_data[i].pch_adapter.owner = THIS_MODULE;
> > + adap_info->pch_data[i].pch_adapter.class = I2C_CLASS_HWMON;
> > + strcpy(adap_info->pch_data[i].pch_adapter.name, "pch_i2c");
> > + adap_info->pch_data[i].pch_adapter.algo = &pch_algorithm;
> > + adap_info->pch_data[i].pch_adapter.algo_data =
> > + &adap_info->pch_data[i];
> > +
> > + /* (i * 0x80) + base_addr; */
> > + adap_info->pch_data[i].pch_base_address = base_addr;
> > +
> > + adap_info->pch_data[i].pch_adapter.dev.parent = &pdev->dev;
> > +
> > + ret = i2c_add_adapter(&(adap_info->pch_data[i].pch_adapter));
> > +
> > + if (ret) {
> > + dev_err(&pdev->dev, "i2c_add_adapter FAILED");
> > + goto err_i2c_add_adapter;
> > + }
> > +
> > + dev_dbg(&pdev->dev,
> > + "i2c_add_adapter returns %d for channel-%d\n", ret, i);
> > + pch_init(&adap_info->pch_data[i]);
> > + dev_dbg(&pdev->dev, "pch_init invoked successfully\n");
> > + }
> > +
> > + ret = request_irq(pdev->irq, &pch_handler, IRQF_SHARED,
> > +   MODULE_NAME, adap_info);
>
> Similarly, you would create a new channel data structure for each channel here
> and register it separately, and then request the interrupt with that
> data structure as the info.

With I2c multi-cahnnel IOH, IRQ number is in common.
Thus, I think our PCH I2C driver can't be implemented like above.

Thanks, Ohtake.

----- Original Message ----- 
From: "Arnd Bergmann" <arnd@arndb.de>
To: "Masayuki Ohtak" <masa-korg@dsn.okisemi.com>
Cc: "Jean Delvare (PC drivers, core)" <khali@linux-fr.org>; "Ben Dooks (embedded platforms)" <ben-linux@fluff.org>;
<linux-i2c@vger.kernel.org>; "LKML" <linux-kernel@vger.kernel.org>; <qi.wang@intel.com>; "Wang, Yong Y"
<yong.y.wang@intel.com>; <joel.clark@intel.com>; <andrew.chih.howe.khor@intel.com>
Sent: Friday, July 16, 2010 4:35 AM
Subject: Re: [PATCH] I2C driver of Topcliff PCH


> On Thursday 15 July 2010 09:42:36 Masayuki Ohtak wrote:
> > I2C driver of Topcliff PCH
> >
> > Topcliff PCH is the platform controller hub that is going to be used in
> > Intel's upcoming general embedded platform. All IO peripherals in
> > Topcliff PCH are actually devices sitting on AMBA bus.
> > Topcliff PCH has I2C I/F. Using this I/F, it is able to access system
> > devices connected to I2C.
>
> Looks ok in general. Some minor comments follow:
>
> > +/**
> > + * pch_wait_for_bus_idle() - check the status of bus.
> > + * @adap: Pointer to struct i2c_algo_pch_data.
> > + * @timeout: waiting time counter (us).
> > + */
> > +static s32 pch_wait_for_bus_idle(struct i2c_algo_pch_data *adap,
> > + s32 timeout)
> > +{
> > + u32 reg_value;
> > + void __iomem *p = adap->pch_base_address;
> > +
> > + /* get the status of bus busy */
> > + reg_value = (ioread32(p + PCH_I2CSR) & I2CMBB_BIT);
> > +
> > + while ((timeout != 0) && (reg_value != 0)) {
> > + msleep(1); /* wait for 100 ms */
> > + reg_value = ioread32(p + PCH_I2CSR) & I2CMBB_BIT;
> > +
> > + timeout--;
> > + }
>
>
> If you want to wait for a maximum amount of time, a loop with
> msleep(1) may end up waiting more than twice as long as you want,
> because each msleep typically returns one milisecond too late.
>
> Better use something like:
>
> time_t timeout = ktime_add_ns(ktime_get(), MAX_NANOSECONDS);
>
> do {
> if (ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
> break;
> msleep(1);
> } while (ktime_lt(ktime_get(), timeout));
>
> > +/**
> > + * pch_wait_for_xfer_complete() - initiates a wait for the tx complete event
> > + * @adap: Pointer to struct i2c_algo_pch_data.
> > + */
> > +static s32 pch_wait_for_xfer_complete(struct i2c_algo_pch_data *adap)
> > +{
> > + u32 temp_flag;
> > + s32 ret;
> > + ret = wait_event_interruptible_timeout(pch_event,
> > + (adap->pch_event_flag != 0), msecs_to_jiffies(50));
> > +
> > + dev_dbg(adap->pch_adapter.dev.parent,
> > + "%s adap->pch_event_flag = %x", __func__, adap->pch_event_flag);
> > + temp_flag = adap->pch_event_flag;
> > + adap->pch_event_flag = 0;
> > +
> > + if (ret == 0) {
> > + dev_err(adap->pch_adapter.dev.parent,
> > + "%s : Timeout\n", __func__);
> > + } else if (ret < 0) {
> > + dev_err(adap->pch_adapter.dev.parent,
> > + "%s failed : Interrupted by other signal\n", __func__);
> > + ret = -ERESTARTSYS;
> > + } else if ((temp_flag & I2C_ERROR_MASK) == 0) {
> > + ret = 0;
> > + } else {
> > + dev_err(adap->pch_adapter.dev.parent,
> > + "%s failed : Error in transfer\n", __func__);
> > + }
> > +
> > + dev_err(adap->pch_adapter.dev.parent, "%s returns %d\n", __func__, ret);
> > +
> > + return ret;
> > +}
>
> The control flow is much more complex than it needs to be here.
> If you want to handle different kinds of error conditions, best
> use goto. Also, it's very unusual to return positive values
> on errors and to print dev_err messages on success.
>
> int ret;
> ret = wait_event_interruptible_timeout(...);
> if (ret < 0)
> goto out;
>
> if (ret == 0) {
> ret = -ETIMEOUT;
> goto out;
> }
>
> ret = 0;
> if (adap->pch_event_flag & I2C_ERROR_MASK) {
> ret = -EIO;
> dev_err(adap->pch_adapter.dev.parent, "error bits set: %lx\n", adap->pch_event_flag);
> }
>
> out:
> return ret;
>
> > +/**
> > + * pch_handler() - interrupt handler for the PCH I2C controller
> > + * @irq: irq number.
> > + * @pData: cookie passed back to the handler function.
> > + */
> > +static irqreturn_t pch_handler(int irq, void *pData)
> > +{
> > + s32 ret;
> > + u32 i;
> > +
> > + struct adapter_info *adap_info = (struct adapter_info *)pData;
> > + /* invoke the call back  */
> > +
> > + if (pch_cbr != NULL) {
> > + for (i = 0, ret = 0; i < PCH_MAX_CHN; i++)
> > + ret |= (pch_cbr) (&adap_info->pch_data[i]);
> > + } else {
> > + dev_err(adap_info->pch_data[0].pch_adapter.dev.parent,
> > + "%s Call back pointer null ...", __func__);
> > + return IRQ_NONE;
> > + }
>
> Here you are multiplexing the interrupt yourself. If you don't
> always use all the available channels, it may be better to
> register the pch_cbr handler separately for each of the channels
> that are actually used, so you don't have to invoke the callback
> for all of them all the time.
>
> > + for (i = 0; i < PCH_MAX_CHN; i++) {
> > + adap_info->pch_data[i].p_adapter_info = adap_info;
> > +
> > + adap_info->pch_data[i].pch_adapter.owner = THIS_MODULE;
> > + adap_info->pch_data[i].pch_adapter.class = I2C_CLASS_HWMON;
> > + strcpy(adap_info->pch_data[i].pch_adapter.name, "pch_i2c");
> > + adap_info->pch_data[i].pch_adapter.algo = &pch_algorithm;
> > + adap_info->pch_data[i].pch_adapter.algo_data =
> > + &adap_info->pch_data[i];
> > +
> > + /* (i * 0x80) + base_addr; */
> > + adap_info->pch_data[i].pch_base_address = base_addr;
> > +
> > + adap_info->pch_data[i].pch_adapter.dev.parent = &pdev->dev;
> > +
> > + ret = i2c_add_adapter(&(adap_info->pch_data[i].pch_adapter));
> > +
> > + if (ret) {
> > + dev_err(&pdev->dev, "i2c_add_adapter FAILED");
> > + goto err_i2c_add_adapter;
> > + }
> > +
> > + dev_dbg(&pdev->dev,
> > + "i2c_add_adapter returns %d for channel-%d\n", ret, i);
> > + pch_init(&adap_info->pch_data[i]);
> > + dev_dbg(&pdev->dev, "pch_init invoked successfully\n");
> > + }
> > +
> > + ret = request_irq(pdev->irq, &pch_handler, IRQF_SHARED,
> > +   MODULE_NAME, adap_info);
>
> Similarly, you would create a new channel data structure for each channel here
> and register it separately, and then request the interrupt with that
> data structure as the info.
>
> > @@ -147,6 +148,11 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
> >  if (tmp == NULL)
> >  return -ENOMEM;
> >
> > + if (copy_from_user(tmp, buf, count)) {
> > + kfree(tmp);
> > + return -EFAULT;
> > + }
> > +
> >  pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
> >  iminor(file->f_path.dentry->d_inode), count);
>
>
> A read function should not do copy_from_user, only copy_to_user.
> If you are worried about returning invalid data from kernel space,
> better use kzalloc instead of kmalloc to get the buffer.
>
> Arnd
>



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

* Re: [PATCH] I2C driver of Topcliff PCH
@ 2010-07-20  4:55       ` Masayuki Ohtake
  0 siblings, 0 replies; 46+ messages in thread
From: Masayuki Ohtake @ 2010-07-20  4:55 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Jean Delvare (PC drivers, core), Ben Dooks (embedded platforms),
	linux-i2c-u79uwXL29TY76Z2rM5mHXA, LKML,
	qi.wang-ral2JQCrhuEAvxtiuMwx3w, Wang, Yong Y,
	joel.clark-ral2JQCrhuEAvxtiuMwx3w,
	andrew.chih.howe.khor-ral2JQCrhuEAvxtiuMwx3w

Hi Arnd

> > + struct adapter_info *adap_info = (struct adapter_info *)pData;
> > + /* invoke the call back  */
> > +
> > + if (pch_cbr != NULL) {
> > + for (i = 0, ret = 0; i < PCH_MAX_CHN; i++)
> > + ret |= (pch_cbr) (&adap_info->pch_data[i]);
> > + } else {
> > + dev_err(adap_info->pch_data[0].pch_adapter.dev.parent,
> > + "%s Call back pointer null ...", __func__);
> > + return IRQ_NONE;
> > + }
>
> Here you are multiplexing the interrupt yourself. If you don't
> always use all the available channels, it may be better to
> register the pch_cbr handler separately for each of the channels
> that are actually used, so you don't have to invoke the callback
> for all of them all the time.
>
> > + for (i = 0; i < PCH_MAX_CHN; i++) {
> > + adap_info->pch_data[i].p_adapter_info = adap_info;
> > +
> > + adap_info->pch_data[i].pch_adapter.owner = THIS_MODULE;
> > + adap_info->pch_data[i].pch_adapter.class = I2C_CLASS_HWMON;
> > + strcpy(adap_info->pch_data[i].pch_adapter.name, "pch_i2c");
> > + adap_info->pch_data[i].pch_adapter.algo = &pch_algorithm;
> > + adap_info->pch_data[i].pch_adapter.algo_data =
> > + &adap_info->pch_data[i];
> > +
> > + /* (i * 0x80) + base_addr; */
> > + adap_info->pch_data[i].pch_base_address = base_addr;
> > +
> > + adap_info->pch_data[i].pch_adapter.dev.parent = &pdev->dev;
> > +
> > + ret = i2c_add_adapter(&(adap_info->pch_data[i].pch_adapter));
> > +
> > + if (ret) {
> > + dev_err(&pdev->dev, "i2c_add_adapter FAILED");
> > + goto err_i2c_add_adapter;
> > + }
> > +
> > + dev_dbg(&pdev->dev,
> > + "i2c_add_adapter returns %d for channel-%d\n", ret, i);
> > + pch_init(&adap_info->pch_data[i]);
> > + dev_dbg(&pdev->dev, "pch_init invoked successfully\n");
> > + }
> > +
> > + ret = request_irq(pdev->irq, &pch_handler, IRQF_SHARED,
> > +   MODULE_NAME, adap_info);
>
> Similarly, you would create a new channel data structure for each channel here
> and register it separately, and then request the interrupt with that
> data structure as the info.

With I2c multi-cahnnel IOH, IRQ number is in common.
Thus, I think our PCH I2C driver can't be implemented like above.

Thanks, Ohtake.

----- Original Message ----- 
From: "Arnd Bergmann" <arnd-r2nGTMty4D4@public.gmane.org>
To: "Masayuki Ohtak" <masa-korg-ECg8zkTtlr0C6LszWs/t0g@public.gmane.org>
Cc: "Jean Delvare (PC drivers, core)" <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org>; "Ben Dooks (embedded platforms)" <ben-linux-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org>;
<linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>; "LKML" <linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>; <qi.wang-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>; "Wang, Yong Y"
<yong.y.wang-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>; <joel.clark-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>; <andrew.chih.howe.khor-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Sent: Friday, July 16, 2010 4:35 AM
Subject: Re: [PATCH] I2C driver of Topcliff PCH


> On Thursday 15 July 2010 09:42:36 Masayuki Ohtak wrote:
> > I2C driver of Topcliff PCH
> >
> > Topcliff PCH is the platform controller hub that is going to be used in
> > Intel's upcoming general embedded platform. All IO peripherals in
> > Topcliff PCH are actually devices sitting on AMBA bus.
> > Topcliff PCH has I2C I/F. Using this I/F, it is able to access system
> > devices connected to I2C.
>
> Looks ok in general. Some minor comments follow:
>
> > +/**
> > + * pch_wait_for_bus_idle() - check the status of bus.
> > + * @adap: Pointer to struct i2c_algo_pch_data.
> > + * @timeout: waiting time counter (us).
> > + */
> > +static s32 pch_wait_for_bus_idle(struct i2c_algo_pch_data *adap,
> > + s32 timeout)
> > +{
> > + u32 reg_value;
> > + void __iomem *p = adap->pch_base_address;
> > +
> > + /* get the status of bus busy */
> > + reg_value = (ioread32(p + PCH_I2CSR) & I2CMBB_BIT);
> > +
> > + while ((timeout != 0) && (reg_value != 0)) {
> > + msleep(1); /* wait for 100 ms */
> > + reg_value = ioread32(p + PCH_I2CSR) & I2CMBB_BIT;
> > +
> > + timeout--;
> > + }
>
>
> If you want to wait for a maximum amount of time, a loop with
> msleep(1) may end up waiting more than twice as long as you want,
> because each msleep typically returns one milisecond too late.
>
> Better use something like:
>
> time_t timeout = ktime_add_ns(ktime_get(), MAX_NANOSECONDS);
>
> do {
> if (ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
> break;
> msleep(1);
> } while (ktime_lt(ktime_get(), timeout));
>
> > +/**
> > + * pch_wait_for_xfer_complete() - initiates a wait for the tx complete event
> > + * @adap: Pointer to struct i2c_algo_pch_data.
> > + */
> > +static s32 pch_wait_for_xfer_complete(struct i2c_algo_pch_data *adap)
> > +{
> > + u32 temp_flag;
> > + s32 ret;
> > + ret = wait_event_interruptible_timeout(pch_event,
> > + (adap->pch_event_flag != 0), msecs_to_jiffies(50));
> > +
> > + dev_dbg(adap->pch_adapter.dev.parent,
> > + "%s adap->pch_event_flag = %x", __func__, adap->pch_event_flag);
> > + temp_flag = adap->pch_event_flag;
> > + adap->pch_event_flag = 0;
> > +
> > + if (ret == 0) {
> > + dev_err(adap->pch_adapter.dev.parent,
> > + "%s : Timeout\n", __func__);
> > + } else if (ret < 0) {
> > + dev_err(adap->pch_adapter.dev.parent,
> > + "%s failed : Interrupted by other signal\n", __func__);
> > + ret = -ERESTARTSYS;
> > + } else if ((temp_flag & I2C_ERROR_MASK) == 0) {
> > + ret = 0;
> > + } else {
> > + dev_err(adap->pch_adapter.dev.parent,
> > + "%s failed : Error in transfer\n", __func__);
> > + }
> > +
> > + dev_err(adap->pch_adapter.dev.parent, "%s returns %d\n", __func__, ret);
> > +
> > + return ret;
> > +}
>
> The control flow is much more complex than it needs to be here.
> If you want to handle different kinds of error conditions, best
> use goto. Also, it's very unusual to return positive values
> on errors and to print dev_err messages on success.
>
> int ret;
> ret = wait_event_interruptible_timeout(...);
> if (ret < 0)
> goto out;
>
> if (ret == 0) {
> ret = -ETIMEOUT;
> goto out;
> }
>
> ret = 0;
> if (adap->pch_event_flag & I2C_ERROR_MASK) {
> ret = -EIO;
> dev_err(adap->pch_adapter.dev.parent, "error bits set: %lx\n", adap->pch_event_flag);
> }
>
> out:
> return ret;
>
> > +/**
> > + * pch_handler() - interrupt handler for the PCH I2C controller
> > + * @irq: irq number.
> > + * @pData: cookie passed back to the handler function.
> > + */
> > +static irqreturn_t pch_handler(int irq, void *pData)
> > +{
> > + s32 ret;
> > + u32 i;
> > +
> > + struct adapter_info *adap_info = (struct adapter_info *)pData;
> > + /* invoke the call back  */
> > +
> > + if (pch_cbr != NULL) {
> > + for (i = 0, ret = 0; i < PCH_MAX_CHN; i++)
> > + ret |= (pch_cbr) (&adap_info->pch_data[i]);
> > + } else {
> > + dev_err(adap_info->pch_data[0].pch_adapter.dev.parent,
> > + "%s Call back pointer null ...", __func__);
> > + return IRQ_NONE;
> > + }
>
> Here you are multiplexing the interrupt yourself. If you don't
> always use all the available channels, it may be better to
> register the pch_cbr handler separately for each of the channels
> that are actually used, so you don't have to invoke the callback
> for all of them all the time.
>
> > + for (i = 0; i < PCH_MAX_CHN; i++) {
> > + adap_info->pch_data[i].p_adapter_info = adap_info;
> > +
> > + adap_info->pch_data[i].pch_adapter.owner = THIS_MODULE;
> > + adap_info->pch_data[i].pch_adapter.class = I2C_CLASS_HWMON;
> > + strcpy(adap_info->pch_data[i].pch_adapter.name, "pch_i2c");
> > + adap_info->pch_data[i].pch_adapter.algo = &pch_algorithm;
> > + adap_info->pch_data[i].pch_adapter.algo_data =
> > + &adap_info->pch_data[i];
> > +
> > + /* (i * 0x80) + base_addr; */
> > + adap_info->pch_data[i].pch_base_address = base_addr;
> > +
> > + adap_info->pch_data[i].pch_adapter.dev.parent = &pdev->dev;
> > +
> > + ret = i2c_add_adapter(&(adap_info->pch_data[i].pch_adapter));
> > +
> > + if (ret) {
> > + dev_err(&pdev->dev, "i2c_add_adapter FAILED");
> > + goto err_i2c_add_adapter;
> > + }
> > +
> > + dev_dbg(&pdev->dev,
> > + "i2c_add_adapter returns %d for channel-%d\n", ret, i);
> > + pch_init(&adap_info->pch_data[i]);
> > + dev_dbg(&pdev->dev, "pch_init invoked successfully\n");
> > + }
> > +
> > + ret = request_irq(pdev->irq, &pch_handler, IRQF_SHARED,
> > +   MODULE_NAME, adap_info);
>
> Similarly, you would create a new channel data structure for each channel here
> and register it separately, and then request the interrupt with that
> data structure as the info.
>
> > @@ -147,6 +148,11 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
> >  if (tmp == NULL)
> >  return -ENOMEM;
> >
> > + if (copy_from_user(tmp, buf, count)) {
> > + kfree(tmp);
> > + return -EFAULT;
> > + }
> > +
> >  pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
> >  iminor(file->f_path.dentry->d_inode), count);
>
>
> A read function should not do copy_from_user, only copy_to_user.
> If you are worried about returning invalid data from kernel space,
> better use kzalloc instead of kmalloc to get the buffer.
>
> Arnd
>

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

* Re: [PATCH] I2C driver of Topcliff PCH
  2010-07-15 19:35     ` Arnd Bergmann
@ 2010-07-20  8:19       ` Masayuki Ohtake
  -1 siblings, 0 replies; 46+ messages in thread
From: Masayuki Ohtake @ 2010-07-20  8:19 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: andrew.chih.howe.khor, joel.clark, Wang, Yong Y, qi.wang, LKML,
	linux-i2c, Ben Dooks (embedded platforms),
	Jean Delvare (PC drivers, core)

Hi Arnd,

> > @@ -147,6 +148,11 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
> >  if (tmp == NULL)
> >  return -ENOMEM;
> >
> > + if (copy_from_user(tmp, buf, count)) {
> > + kfree(tmp);
> > + return -EFAULT;
> > + }
> > +
> >  pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
> >  iminor(file->f_path.dentry->d_inode), count);
>
>
> A read function should not do copy_from_user, only copy_to_user.
> If you are worried about returning invalid data from kernel space,
> better use kzalloc instead of kmalloc to get the buffer.

Our I2C HW has special mode.
To control the mode, our i2c driver has copy_from_user.

Thanks, Ohtake.

----- Original Message ----- 
From: "Arnd Bergmann" <arnd@arndb.de>
To: "Masayuki Ohtak" <masa-korg@dsn.okisemi.com>
Cc: "Jean Delvare (PC drivers, core)" <khali@linux-fr.org>; "Ben Dooks (embedded platforms)" <ben-linux@fluff.org>;
<linux-i2c@vger.kernel.org>; "LKML" <linux-kernel@vger.kernel.org>; <qi.wang@intel.com>; "Wang, Yong Y"
<yong.y.wang@intel.com>; <joel.clark@intel.com>; <andrew.chih.howe.khor@intel.com>
Sent: Friday, July 16, 2010 4:35 AM
Subject: Re: [PATCH] I2C driver of Topcliff PCH


> On Thursday 15 July 2010 09:42:36 Masayuki Ohtak wrote:
> > I2C driver of Topcliff PCH
> >
> > Topcliff PCH is the platform controller hub that is going to be used in
> > Intel's upcoming general embedded platform. All IO peripherals in
> > Topcliff PCH are actually devices sitting on AMBA bus.
> > Topcliff PCH has I2C I/F. Using this I/F, it is able to access system
> > devices connected to I2C.
>
> Looks ok in general. Some minor comments follow:
>
> > +/**
> > + * pch_wait_for_bus_idle() - check the status of bus.
> > + * @adap: Pointer to struct i2c_algo_pch_data.
> > + * @timeout: waiting time counter (us).
> > + */
> > +static s32 pch_wait_for_bus_idle(struct i2c_algo_pch_data *adap,
> > + s32 timeout)
> > +{
> > + u32 reg_value;
> > + void __iomem *p = adap->pch_base_address;
> > +
> > + /* get the status of bus busy */
> > + reg_value = (ioread32(p + PCH_I2CSR) & I2CMBB_BIT);
> > +
> > + while ((timeout != 0) && (reg_value != 0)) {
> > + msleep(1); /* wait for 100 ms */
> > + reg_value = ioread32(p + PCH_I2CSR) & I2CMBB_BIT;
> > +
> > + timeout--;
> > + }
>
>
> If you want to wait for a maximum amount of time, a loop with
> msleep(1) may end up waiting more than twice as long as you want,
> because each msleep typically returns one milisecond too late.
>
> Better use something like:
>
> time_t timeout = ktime_add_ns(ktime_get(), MAX_NANOSECONDS);
>
> do {
> if (ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
> break;
> msleep(1);
> } while (ktime_lt(ktime_get(), timeout));
>
> > +/**
> > + * pch_wait_for_xfer_complete() - initiates a wait for the tx complete event
> > + * @adap: Pointer to struct i2c_algo_pch_data.
> > + */
> > +static s32 pch_wait_for_xfer_complete(struct i2c_algo_pch_data *adap)
> > +{
> > + u32 temp_flag;
> > + s32 ret;
> > + ret = wait_event_interruptible_timeout(pch_event,
> > + (adap->pch_event_flag != 0), msecs_to_jiffies(50));
> > +
> > + dev_dbg(adap->pch_adapter.dev.parent,
> > + "%s adap->pch_event_flag = %x", __func__, adap->pch_event_flag);
> > + temp_flag = adap->pch_event_flag;
> > + adap->pch_event_flag = 0;
> > +
> > + if (ret == 0) {
> > + dev_err(adap->pch_adapter.dev.parent,
> > + "%s : Timeout\n", __func__);
> > + } else if (ret < 0) {
> > + dev_err(adap->pch_adapter.dev.parent,
> > + "%s failed : Interrupted by other signal\n", __func__);
> > + ret = -ERESTARTSYS;
> > + } else if ((temp_flag & I2C_ERROR_MASK) == 0) {
> > + ret = 0;
> > + } else {
> > + dev_err(adap->pch_adapter.dev.parent,
> > + "%s failed : Error in transfer\n", __func__);
> > + }
> > +
> > + dev_err(adap->pch_adapter.dev.parent, "%s returns %d\n", __func__, ret);
> > +
> > + return ret;
> > +}
>
> The control flow is much more complex than it needs to be here.
> If you want to handle different kinds of error conditions, best
> use goto. Also, it's very unusual to return positive values
> on errors and to print dev_err messages on success.
>
> int ret;
> ret = wait_event_interruptible_timeout(...);
> if (ret < 0)
> goto out;
>
> if (ret == 0) {
> ret = -ETIMEOUT;
> goto out;
> }
>
> ret = 0;
> if (adap->pch_event_flag & I2C_ERROR_MASK) {
> ret = -EIO;
> dev_err(adap->pch_adapter.dev.parent, "error bits set: %lx\n", adap->pch_event_flag);
> }
>
> out:
> return ret;
>
> > +/**
> > + * pch_handler() - interrupt handler for the PCH I2C controller
> > + * @irq: irq number.
> > + * @pData: cookie passed back to the handler function.
> > + */
> > +static irqreturn_t pch_handler(int irq, void *pData)
> > +{
> > + s32 ret;
> > + u32 i;
> > +
> > + struct adapter_info *adap_info = (struct adapter_info *)pData;
> > + /* invoke the call back  */
> > +
> > + if (pch_cbr != NULL) {
> > + for (i = 0, ret = 0; i < PCH_MAX_CHN; i++)
> > + ret |= (pch_cbr) (&adap_info->pch_data[i]);
> > + } else {
> > + dev_err(adap_info->pch_data[0].pch_adapter.dev.parent,
> > + "%s Call back pointer null ...", __func__);
> > + return IRQ_NONE;
> > + }
>
> Here you are multiplexing the interrupt yourself. If you don't
> always use all the available channels, it may be better to
> register the pch_cbr handler separately for each of the channels
> that are actually used, so you don't have to invoke the callback
> for all of them all the time.
>
> > + for (i = 0; i < PCH_MAX_CHN; i++) {
> > + adap_info->pch_data[i].p_adapter_info = adap_info;
> > +
> > + adap_info->pch_data[i].pch_adapter.owner = THIS_MODULE;
> > + adap_info->pch_data[i].pch_adapter.class = I2C_CLASS_HWMON;
> > + strcpy(adap_info->pch_data[i].pch_adapter.name, "pch_i2c");
> > + adap_info->pch_data[i].pch_adapter.algo = &pch_algorithm;
> > + adap_info->pch_data[i].pch_adapter.algo_data =
> > + &adap_info->pch_data[i];
> > +
> > + /* (i * 0x80) + base_addr; */
> > + adap_info->pch_data[i].pch_base_address = base_addr;
> > +
> > + adap_info->pch_data[i].pch_adapter.dev.parent = &pdev->dev;
> > +
> > + ret = i2c_add_adapter(&(adap_info->pch_data[i].pch_adapter));
> > +
> > + if (ret) {
> > + dev_err(&pdev->dev, "i2c_add_adapter FAILED");
> > + goto err_i2c_add_adapter;
> > + }
> > +
> > + dev_dbg(&pdev->dev,
> > + "i2c_add_adapter returns %d for channel-%d\n", ret, i);
> > + pch_init(&adap_info->pch_data[i]);
> > + dev_dbg(&pdev->dev, "pch_init invoked successfully\n");
> > + }
> > +
> > + ret = request_irq(pdev->irq, &pch_handler, IRQF_SHARED,
> > +   MODULE_NAME, adap_info);
>
> Similarly, you would create a new channel data structure for each channel here
> and register it separately, and then request the interrupt with that
> data structure as the info.
>
> > @@ -147,6 +148,11 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
> >  if (tmp == NULL)
> >  return -ENOMEM;
> >
> > + if (copy_from_user(tmp, buf, count)) {
> > + kfree(tmp);
> > + return -EFAULT;
> > + }
> > +
> >  pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
> >  iminor(file->f_path.dentry->d_inode), count);
>
>
> A read function should not do copy_from_user, only copy_to_user.
> If you are worried about returning invalid data from kernel space,
> better use kzalloc instead of kmalloc to get the buffer.
>
> Arnd
>



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

* Re: [PATCH] I2C driver of Topcliff PCH
@ 2010-07-20  8:19       ` Masayuki Ohtake
  0 siblings, 0 replies; 46+ messages in thread
From: Masayuki Ohtake @ 2010-07-20  8:19 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: andrew.chih.howe.khor-ral2JQCrhuEAvxtiuMwx3w,
	joel.clark-ral2JQCrhuEAvxtiuMwx3w, Wang, Yong Y,
	qi.wang-ral2JQCrhuEAvxtiuMwx3w, LKML,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA, Ben Dooks (embedded platforms),
	Jean Delvare (PC drivers, core)

Hi Arnd,

> > @@ -147,6 +148,11 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
> >  if (tmp == NULL)
> >  return -ENOMEM;
> >
> > + if (copy_from_user(tmp, buf, count)) {
> > + kfree(tmp);
> > + return -EFAULT;
> > + }
> > +
> >  pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
> >  iminor(file->f_path.dentry->d_inode), count);
>
>
> A read function should not do copy_from_user, only copy_to_user.
> If you are worried about returning invalid data from kernel space,
> better use kzalloc instead of kmalloc to get the buffer.

Our I2C HW has special mode.
To control the mode, our i2c driver has copy_from_user.

Thanks, Ohtake.

----- Original Message ----- 
From: "Arnd Bergmann" <arnd-r2nGTMty4D4@public.gmane.org>
To: "Masayuki Ohtak" <masa-korg-ECg8zkTtlr0C6LszWs/t0g@public.gmane.org>
Cc: "Jean Delvare (PC drivers, core)" <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org>; "Ben Dooks (embedded platforms)" <ben-linux-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org>;
<linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>; "LKML" <linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>; <qi.wang-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>; "Wang, Yong Y"
<yong.y.wang-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>; <joel.clark-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>; <andrew.chih.howe.khor-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Sent: Friday, July 16, 2010 4:35 AM
Subject: Re: [PATCH] I2C driver of Topcliff PCH


> On Thursday 15 July 2010 09:42:36 Masayuki Ohtak wrote:
> > I2C driver of Topcliff PCH
> >
> > Topcliff PCH is the platform controller hub that is going to be used in
> > Intel's upcoming general embedded platform. All IO peripherals in
> > Topcliff PCH are actually devices sitting on AMBA bus.
> > Topcliff PCH has I2C I/F. Using this I/F, it is able to access system
> > devices connected to I2C.
>
> Looks ok in general. Some minor comments follow:
>
> > +/**
> > + * pch_wait_for_bus_idle() - check the status of bus.
> > + * @adap: Pointer to struct i2c_algo_pch_data.
> > + * @timeout: waiting time counter (us).
> > + */
> > +static s32 pch_wait_for_bus_idle(struct i2c_algo_pch_data *adap,
> > + s32 timeout)
> > +{
> > + u32 reg_value;
> > + void __iomem *p = adap->pch_base_address;
> > +
> > + /* get the status of bus busy */
> > + reg_value = (ioread32(p + PCH_I2CSR) & I2CMBB_BIT);
> > +
> > + while ((timeout != 0) && (reg_value != 0)) {
> > + msleep(1); /* wait for 100 ms */
> > + reg_value = ioread32(p + PCH_I2CSR) & I2CMBB_BIT;
> > +
> > + timeout--;
> > + }
>
>
> If you want to wait for a maximum amount of time, a loop with
> msleep(1) may end up waiting more than twice as long as you want,
> because each msleep typically returns one milisecond too late.
>
> Better use something like:
>
> time_t timeout = ktime_add_ns(ktime_get(), MAX_NANOSECONDS);
>
> do {
> if (ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
> break;
> msleep(1);
> } while (ktime_lt(ktime_get(), timeout));
>
> > +/**
> > + * pch_wait_for_xfer_complete() - initiates a wait for the tx complete event
> > + * @adap: Pointer to struct i2c_algo_pch_data.
> > + */
> > +static s32 pch_wait_for_xfer_complete(struct i2c_algo_pch_data *adap)
> > +{
> > + u32 temp_flag;
> > + s32 ret;
> > + ret = wait_event_interruptible_timeout(pch_event,
> > + (adap->pch_event_flag != 0), msecs_to_jiffies(50));
> > +
> > + dev_dbg(adap->pch_adapter.dev.parent,
> > + "%s adap->pch_event_flag = %x", __func__, adap->pch_event_flag);
> > + temp_flag = adap->pch_event_flag;
> > + adap->pch_event_flag = 0;
> > +
> > + if (ret == 0) {
> > + dev_err(adap->pch_adapter.dev.parent,
> > + "%s : Timeout\n", __func__);
> > + } else if (ret < 0) {
> > + dev_err(adap->pch_adapter.dev.parent,
> > + "%s failed : Interrupted by other signal\n", __func__);
> > + ret = -ERESTARTSYS;
> > + } else if ((temp_flag & I2C_ERROR_MASK) == 0) {
> > + ret = 0;
> > + } else {
> > + dev_err(adap->pch_adapter.dev.parent,
> > + "%s failed : Error in transfer\n", __func__);
> > + }
> > +
> > + dev_err(adap->pch_adapter.dev.parent, "%s returns %d\n", __func__, ret);
> > +
> > + return ret;
> > +}
>
> The control flow is much more complex than it needs to be here.
> If you want to handle different kinds of error conditions, best
> use goto. Also, it's very unusual to return positive values
> on errors and to print dev_err messages on success.
>
> int ret;
> ret = wait_event_interruptible_timeout(...);
> if (ret < 0)
> goto out;
>
> if (ret == 0) {
> ret = -ETIMEOUT;
> goto out;
> }
>
> ret = 0;
> if (adap->pch_event_flag & I2C_ERROR_MASK) {
> ret = -EIO;
> dev_err(adap->pch_adapter.dev.parent, "error bits set: %lx\n", adap->pch_event_flag);
> }
>
> out:
> return ret;
>
> > +/**
> > + * pch_handler() - interrupt handler for the PCH I2C controller
> > + * @irq: irq number.
> > + * @pData: cookie passed back to the handler function.
> > + */
> > +static irqreturn_t pch_handler(int irq, void *pData)
> > +{
> > + s32 ret;
> > + u32 i;
> > +
> > + struct adapter_info *adap_info = (struct adapter_info *)pData;
> > + /* invoke the call back  */
> > +
> > + if (pch_cbr != NULL) {
> > + for (i = 0, ret = 0; i < PCH_MAX_CHN; i++)
> > + ret |= (pch_cbr) (&adap_info->pch_data[i]);
> > + } else {
> > + dev_err(adap_info->pch_data[0].pch_adapter.dev.parent,
> > + "%s Call back pointer null ...", __func__);
> > + return IRQ_NONE;
> > + }
>
> Here you are multiplexing the interrupt yourself. If you don't
> always use all the available channels, it may be better to
> register the pch_cbr handler separately for each of the channels
> that are actually used, so you don't have to invoke the callback
> for all of them all the time.
>
> > + for (i = 0; i < PCH_MAX_CHN; i++) {
> > + adap_info->pch_data[i].p_adapter_info = adap_info;
> > +
> > + adap_info->pch_data[i].pch_adapter.owner = THIS_MODULE;
> > + adap_info->pch_data[i].pch_adapter.class = I2C_CLASS_HWMON;
> > + strcpy(adap_info->pch_data[i].pch_adapter.name, "pch_i2c");
> > + adap_info->pch_data[i].pch_adapter.algo = &pch_algorithm;
> > + adap_info->pch_data[i].pch_adapter.algo_data =
> > + &adap_info->pch_data[i];
> > +
> > + /* (i * 0x80) + base_addr; */
> > + adap_info->pch_data[i].pch_base_address = base_addr;
> > +
> > + adap_info->pch_data[i].pch_adapter.dev.parent = &pdev->dev;
> > +
> > + ret = i2c_add_adapter(&(adap_info->pch_data[i].pch_adapter));
> > +
> > + if (ret) {
> > + dev_err(&pdev->dev, "i2c_add_adapter FAILED");
> > + goto err_i2c_add_adapter;
> > + }
> > +
> > + dev_dbg(&pdev->dev,
> > + "i2c_add_adapter returns %d for channel-%d\n", ret, i);
> > + pch_init(&adap_info->pch_data[i]);
> > + dev_dbg(&pdev->dev, "pch_init invoked successfully\n");
> > + }
> > +
> > + ret = request_irq(pdev->irq, &pch_handler, IRQF_SHARED,
> > +   MODULE_NAME, adap_info);
>
> Similarly, you would create a new channel data structure for each channel here
> and register it separately, and then request the interrupt with that
> data structure as the info.
>
> > @@ -147,6 +148,11 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
> >  if (tmp == NULL)
> >  return -ENOMEM;
> >
> > + if (copy_from_user(tmp, buf, count)) {
> > + kfree(tmp);
> > + return -EFAULT;
> > + }
> > +
> >  pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
> >  iminor(file->f_path.dentry->d_inode), count);
>
>
> A read function should not do copy_from_user, only copy_to_user.
> If you are worried about returning invalid data from kernel space,
> better use kzalloc instead of kmalloc to get the buffer.
>
> Arnd
>

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

* Re: [PATCH] I2C driver of Topcliff PCH
@ 2010-07-20  9:27         ` Arnd Bergmann
  0 siblings, 0 replies; 46+ messages in thread
From: Arnd Bergmann @ 2010-07-20  9:27 UTC (permalink / raw)
  To: Masayuki Ohtake
  Cc: Jean Delvare (PC drivers, core), Ben Dooks (embedded platforms),
	linux-i2c, LKML, qi.wang, Wang, Yong Y, joel.clark,
	andrew.chih.howe.khor

On Tuesday 20 July 2010, Masayuki Ohtake wrote:
> > > +
> > > + dev_dbg(&pdev->dev,
> > > + "i2c_add_adapter returns %d for channel-%d\n", ret, i);
> > > + pch_init(&adap_info->pch_data[i]);
> > > + dev_dbg(&pdev->dev, "pch_init invoked successfully\n");
> > > + }
> > > +
> > > + ret = request_irq(pdev->irq, &pch_handler, IRQF_SHARED,
> > > +   MODULE_NAME, adap_info);
> >
> > Similarly, you would create a new channel data structure for each channel here
> > and register it separately, and then request the interrupt with that
> > data structure as the info.
> 
> With I2c multi-cahnnel IOH, IRQ number is in common.
> Thus, I think our PCH I2C driver can't be implemented like above.

If you pass IRQF_SHARED, you can register any number of handlers
for the same IRQ number using different dev pointers.

	Arnd

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

* Re: [PATCH] I2C driver of Topcliff PCH
@ 2010-07-20  9:27         ` Arnd Bergmann
  0 siblings, 0 replies; 46+ messages in thread
From: Arnd Bergmann @ 2010-07-20  9:27 UTC (permalink / raw)
  To: Masayuki Ohtake
  Cc: Jean Delvare (PC drivers, core), Ben Dooks (embedded platforms),
	linux-i2c-u79uwXL29TY76Z2rM5mHXA, LKML,
	qi.wang-ral2JQCrhuEAvxtiuMwx3w, Wang, Yong Y,
	joel.clark-ral2JQCrhuEAvxtiuMwx3w,
	andrew.chih.howe.khor-ral2JQCrhuEAvxtiuMwx3w

On Tuesday 20 July 2010, Masayuki Ohtake wrote:
> > > +
> > > + dev_dbg(&pdev->dev,
> > > + "i2c_add_adapter returns %d for channel-%d\n", ret, i);
> > > + pch_init(&adap_info->pch_data[i]);
> > > + dev_dbg(&pdev->dev, "pch_init invoked successfully\n");
> > > + }
> > > +
> > > + ret = request_irq(pdev->irq, &pch_handler, IRQF_SHARED,
> > > +   MODULE_NAME, adap_info);
> >
> > Similarly, you would create a new channel data structure for each channel here
> > and register it separately, and then request the interrupt with that
> > data structure as the info.
> 
> With I2c multi-cahnnel IOH, IRQ number is in common.
> Thus, I think our PCH I2C driver can't be implemented like above.

If you pass IRQF_SHARED, you can register any number of handlers
for the same IRQ number using different dev pointers.

	Arnd

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

* Re: [PATCH] I2C driver of Topcliff PCH
@ 2010-07-20  9:29         ` Arnd Bergmann
  0 siblings, 0 replies; 46+ messages in thread
From: Arnd Bergmann @ 2010-07-20  9:29 UTC (permalink / raw)
  To: Masayuki Ohtake
  Cc: andrew.chih.howe.khor, joel.clark, Wang, Yong Y, qi.wang, LKML,
	linux-i2c, Ben Dooks (embedded platforms),
	Jean Delvare (PC drivers, core)

On Tuesday 20 July 2010, Masayuki Ohtake wrote:
> > > @@ -147,6 +148,11 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
> > >  if (tmp == NULL)
> > >  return -ENOMEM;
> > >
> > > + if (copy_from_user(tmp, buf, count)) {
> > > + kfree(tmp);
> > > + return -EFAULT;
> > > + }
> > > +
> > >  pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
> > >  iminor(file->f_path.dentry->d_inode), count);
> >
> >
> > A read function should not do copy_from_user, only copy_to_user.
> > If you are worried about returning invalid data from kernel space,
> > better use kzalloc instead of kmalloc to get the buffer.
> 
> Our I2C HW has special mode.
> To control the mode, our i2c driver has copy_from_user.

That is a highly unusual interface and I would definitely not recommend doing
this. Please use an ioctl operation for anything that has both input and output
data.

	Arnd

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

* Re: [PATCH] I2C driver of Topcliff PCH
@ 2010-07-20  9:29         ` Arnd Bergmann
  0 siblings, 0 replies; 46+ messages in thread
From: Arnd Bergmann @ 2010-07-20  9:29 UTC (permalink / raw)
  To: Masayuki Ohtake
  Cc: andrew.chih.howe.khor-ral2JQCrhuEAvxtiuMwx3w,
	joel.clark-ral2JQCrhuEAvxtiuMwx3w, Wang, Yong Y,
	qi.wang-ral2JQCrhuEAvxtiuMwx3w, LKML,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA, Ben Dooks (embedded platforms),
	Jean Delvare (PC drivers, core)

On Tuesday 20 July 2010, Masayuki Ohtake wrote:
> > > @@ -147,6 +148,11 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
> > >  if (tmp == NULL)
> > >  return -ENOMEM;
> > >
> > > + if (copy_from_user(tmp, buf, count)) {
> > > + kfree(tmp);
> > > + return -EFAULT;
> > > + }
> > > +
> > >  pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
> > >  iminor(file->f_path.dentry->d_inode), count);
> >
> >
> > A read function should not do copy_from_user, only copy_to_user.
> > If you are worried about returning invalid data from kernel space,
> > better use kzalloc instead of kmalloc to get the buffer.
> 
> Our I2C HW has special mode.
> To control the mode, our i2c driver has copy_from_user.

That is a highly unusual interface and I would definitely not recommend doing
this. Please use an ioctl operation for anything that has both input and output
data.

	Arnd

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

* Re: [PATCH] I2C driver of Topcliff PCH
  2010-07-20  9:27         ` Arnd Bergmann
@ 2010-07-20 12:38           ` Masayuki Ohtake
  -1 siblings, 0 replies; 46+ messages in thread
From: Masayuki Ohtake @ 2010-07-20 12:38 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Jean Delvare (PC drivers, core), Ben Dooks (embedded platforms),
	linux-i2c, LKML, qi.wang, Wang, Yong Y, joel.clark,
	andrew.chih.howe.khor

Hi Arnd,

> If you pass IRQF_SHARED, you can register any number of handlers
> for the same IRQ number using different dev pointers.
I will modify like above.

Thanks, Ohtake.
----- Original Message ----- 
From: "Arnd Bergmann" <arnd@arndb.de>
To: "Masayuki Ohtake" <masa-korg@dsn.okisemi.com>
Cc: "Jean Delvare (PC drivers, core)" <khali@linux-fr.org>; "Ben Dooks (embedded platforms)" <ben-linux@fluff.org>;
<linux-i2c@vger.kernel.org>; "LKML" <linux-kernel@vger.kernel.org>; <qi.wang@intel.com>; "Wang, Yong Y"
<yong.y.wang@intel.com>; <joel.clark@intel.com>; <andrew.chih.howe.khor@intel.com>
Sent: Tuesday, July 20, 2010 6:27 PM
Subject: Re: [PATCH] I2C driver of Topcliff PCH


> On Tuesday 20 July 2010, Masayuki Ohtake wrote:
> > > > +
> > > > + dev_dbg(&pdev->dev,
> > > > + "i2c_add_adapter returns %d for channel-%d\n", ret, i);
> > > > + pch_init(&adap_info->pch_data[i]);
> > > > + dev_dbg(&pdev->dev, "pch_init invoked successfully\n");
> > > > + }
> > > > +
> > > > + ret = request_irq(pdev->irq, &pch_handler, IRQF_SHARED,
> > > > +   MODULE_NAME, adap_info);
> > >
> > > Similarly, you would create a new channel data structure for each channel here
> > > and register it separately, and then request the interrupt with that
> > > data structure as the info.
> >
> > With I2c multi-cahnnel IOH, IRQ number is in common.
> > Thus, I think our PCH I2C driver can't be implemented like above.
>
> If you pass IRQF_SHARED, you can register any number of handlers
> for the same IRQ number using different dev pointers.
>
> Arnd
>



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

* Re: [PATCH] I2C driver of Topcliff PCH
@ 2010-07-20 12:38           ` Masayuki Ohtake
  0 siblings, 0 replies; 46+ messages in thread
From: Masayuki Ohtake @ 2010-07-20 12:38 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Jean Delvare (PC drivers, core), Ben Dooks (embedded platforms),
	linux-i2c-u79uwXL29TY76Z2rM5mHXA, LKML,
	qi.wang-ral2JQCrhuEAvxtiuMwx3w, Wang, Yong Y,
	joel.clark-ral2JQCrhuEAvxtiuMwx3w,
	andrew.chih.howe.khor-ral2JQCrhuEAvxtiuMwx3w

Hi Arnd,

> If you pass IRQF_SHARED, you can register any number of handlers
> for the same IRQ number using different dev pointers.
I will modify like above.

Thanks, Ohtake.
----- Original Message ----- 
From: "Arnd Bergmann" <arnd-r2nGTMty4D4@public.gmane.org>
To: "Masayuki Ohtake" <masa-korg-ECg8zkTtlr0C6LszWs/t0g@public.gmane.org>
Cc: "Jean Delvare (PC drivers, core)" <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org>; "Ben Dooks (embedded platforms)" <ben-linux-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org>;
<linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>; "LKML" <linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>; <qi.wang-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>; "Wang, Yong Y"
<yong.y.wang-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>; <joel.clark-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>; <andrew.chih.howe.khor-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Sent: Tuesday, July 20, 2010 6:27 PM
Subject: Re: [PATCH] I2C driver of Topcliff PCH


> On Tuesday 20 July 2010, Masayuki Ohtake wrote:
> > > > +
> > > > + dev_dbg(&pdev->dev,
> > > > + "i2c_add_adapter returns %d for channel-%d\n", ret, i);
> > > > + pch_init(&adap_info->pch_data[i]);
> > > > + dev_dbg(&pdev->dev, "pch_init invoked successfully\n");
> > > > + }
> > > > +
> > > > + ret = request_irq(pdev->irq, &pch_handler, IRQF_SHARED,
> > > > +   MODULE_NAME, adap_info);
> > >
> > > Similarly, you would create a new channel data structure for each channel here
> > > and register it separately, and then request the interrupt with that
> > > data structure as the info.
> >
> > With I2c multi-cahnnel IOH, IRQ number is in common.
> > Thus, I think our PCH I2C driver can't be implemented like above.
>
> If you pass IRQF_SHARED, you can register any number of handlers
> for the same IRQ number using different dev pointers.
>
> Arnd
>

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

* Re: [PATCH] I2C driver of Topcliff PCH
  2010-07-20  9:29         ` Arnd Bergmann
@ 2010-07-20 12:40           ` Masayuki Ohtake
  -1 siblings, 0 replies; 46+ messages in thread
From: Masayuki Ohtake @ 2010-07-20 12:40 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: andrew.chih.howe.khor, joel.clark, Wang, Yong Y, qi.wang, LKML,
	linux-i2c, Ben Dooks (embedded platforms),
	Jean Delvare (PC drivers, core)

Hi Arnd,

> That is a highly unusual interface and I would definitely not recommend doing
> this. Please use an ioctl operation for anything that has both input and output
> data.
I will delete the special interface from our i2c driver.

Thanks, Ohtake
----- Original Message ----- 
From: "Arnd Bergmann" <arnd@arndb.de>
To: "Masayuki Ohtake" <masa-korg@dsn.okisemi.com>
Cc: <andrew.chih.howe.khor@intel.com>; <joel.clark@intel.com>; "Wang, Yong Y" <yong.y.wang@intel.com>;
<qi.wang@intel.com>; "LKML" <linux-kernel@vger.kernel.org>; <linux-i2c@vger.kernel.org>; "Ben Dooks (embedded
platforms)" <ben-linux@fluff.org>; "Jean Delvare (PC drivers, core)" <khali@linux-fr.org>
Sent: Tuesday, July 20, 2010 6:29 PM
Subject: Re: [PATCH] I2C driver of Topcliff PCH


> On Tuesday 20 July 2010, Masayuki Ohtake wrote:
> > > > @@ -147,6 +148,11 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
> > > >  if (tmp == NULL)
> > > >  return -ENOMEM;
> > > >
> > > > + if (copy_from_user(tmp, buf, count)) {
> > > > + kfree(tmp);
> > > > + return -EFAULT;
> > > > + }
> > > > +
> > > >  pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
> > > >  iminor(file->f_path.dentry->d_inode), count);
> > >
> > >
> > > A read function should not do copy_from_user, only copy_to_user.
> > > If you are worried about returning invalid data from kernel space,
> > > better use kzalloc instead of kmalloc to get the buffer.
> >
> > Our I2C HW has special mode.
> > To control the mode, our i2c driver has copy_from_user.
>
> That is a highly unusual interface and I would definitely not recommend doing
> this. Please use an ioctl operation for anything that has both input and output
> data.
>
> Arnd
>



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

* Re: [PATCH] I2C driver of Topcliff PCH
@ 2010-07-20 12:40           ` Masayuki Ohtake
  0 siblings, 0 replies; 46+ messages in thread
From: Masayuki Ohtake @ 2010-07-20 12:40 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: andrew.chih.howe.khor-ral2JQCrhuEAvxtiuMwx3w,
	joel.clark-ral2JQCrhuEAvxtiuMwx3w, Wang, Yong Y,
	qi.wang-ral2JQCrhuEAvxtiuMwx3w, LKML,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA, Ben Dooks (embedded platforms),
	Jean Delvare (PC drivers, core)

Hi Arnd,

> That is a highly unusual interface and I would definitely not recommend doing
> this. Please use an ioctl operation for anything that has both input and output
> data.
I will delete the special interface from our i2c driver.

Thanks, Ohtake
----- Original Message ----- 
From: "Arnd Bergmann" <arnd-r2nGTMty4D4@public.gmane.org>
To: "Masayuki Ohtake" <masa-korg-ECg8zkTtlr0C6LszWs/t0g@public.gmane.org>
Cc: <andrew.chih.howe.khor-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>; <joel.clark-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>; "Wang, Yong Y" <yong.y.wang-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>;
<qi.wang-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>; "LKML" <linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>; <linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>; "Ben Dooks (embedded
platforms)" <ben-linux-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org>; "Jean Delvare (PC drivers, core)" <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org>
Sent: Tuesday, July 20, 2010 6:29 PM
Subject: Re: [PATCH] I2C driver of Topcliff PCH


> On Tuesday 20 July 2010, Masayuki Ohtake wrote:
> > > > @@ -147,6 +148,11 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
> > > >  if (tmp == NULL)
> > > >  return -ENOMEM;
> > > >
> > > > + if (copy_from_user(tmp, buf, count)) {
> > > > + kfree(tmp);
> > > > + return -EFAULT;
> > > > + }
> > > > +
> > > >  pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
> > > >  iminor(file->f_path.dentry->d_inode), count);
> > >
> > >
> > > A read function should not do copy_from_user, only copy_to_user.
> > > If you are worried about returning invalid data from kernel space,
> > > better use kzalloc instead of kmalloc to get the buffer.
> >
> > Our I2C HW has special mode.
> > To control the mode, our i2c driver has copy_from_user.
>
> That is a highly unusual interface and I would definitely not recommend doing
> this. Please use an ioctl operation for anything that has both input and output
> data.
>
> Arnd
>

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

* [PATCH] I2C driver of Topcliff PCH
@ 2010-07-21  6:46   ` Masayuki Ohtak
  0 siblings, 0 replies; 46+ messages in thread
From: Masayuki Ohtak @ 2010-07-21  6:46 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Jean Delvare (PC drivers, core), Ben Dooks (embedded platforms),
	linux-i2c, LKML, qi.wang, Wang, Yong Y, joel.clark,
	andrew.chih.howe.khor

Hi Arnd,

I have modified for your comments.
Please confirm below.

Thanks, Ohtake

---
I2C driver of Topcliff PCH

Topcliff PCH is the platform controller hub that is going to be used in
Intel's upcoming general embedded platform. All IO peripherals in
Topcliff PCH are actually devices sitting on AMBA bus. 
Topcliff PCH has I2C I/F. Using this I/F, it is able to access system
devices connected to I2C.

Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>

---
 drivers/i2c/busses/Kconfig   |    8 +
 drivers/i2c/busses/Makefile  |    3 +
 drivers/i2c/busses/i2c-pch.c |  910 ++++++++++++++++++++++++++++++++++++++++++
 drivers/i2c/busses/i2c-pch.h |  147 +++++++
 drivers/i2c/i2c-dev.c        |   21 +
 5 files changed, 1089 insertions(+), 0 deletions(-)
 create mode 100644 drivers/i2c/busses/i2c-pch.c
 create mode 100644 drivers/i2c/busses/i2c-pch.h

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 5f318ce..98e7201 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -7,6 +7,14 @@ menu "I2C Hardware Bus support"
 comment "PC SMBus host controller drivers"
 	depends on PCI
 
+config PCH_I2C
+	tristate "PCH I2C"
+	depends on PCI
+	help
+	  This driver is for PCH I2C of Topcliff which is an IOH for x86
+	  embedded processor.
+	  This driver can access PCH I2C bus device.
+
 config I2C_ALI1535
 	tristate "ALI 1535"
 	depends on PCI
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 302c551..3e6b8d6 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -75,3 +75,6 @@ obj-$(CONFIG_SCx200_I2C)	+= scx200_i2c.o
 ifeq ($(CONFIG_I2C_DEBUG_BUS),y)
 EXTRA_CFLAGS += -DDEBUG
 endif
+
+obj-$(CONFIG_PCH_I2C) += pch_i2c.o
+pch_i2c-objs := i2c-pch.o
diff --git a/drivers/i2c/busses/i2c-pch.c b/drivers/i2c/busses/i2c-pch.c
new file mode 100644
index 0000000..7939781
--- /dev/null
+++ b/drivers/i2c/busses/i2c-pch.c
@@ -0,0 +1,910 @@
+/*
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+#include <linux/ktime.h>
+
+#include "i2c-pch.h"
+
+static int pch_i2c_speed = 100; /* I2C bus speed in Kbps */
+static int pch_clk = 50000;	/* specifies I2C clock speed in KHz */
+static wait_queue_head_t pch_event;
+static DEFINE_MUTEX(pch_mutex);
+
+static struct pci_device_id __devinitdata pch_pcidev_id[] = {
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH_I2C)},
+	{0,}
+};
+
+static irqreturn_t pch_handler_ch0(int irq, void *pData);
+static irqreturn_t(*pch_handler_list[PCH_MAX_CHN]) (int irq, void *pData) = {
+	pch_handler_ch0,
+};
+
+static inline void pch_setbit(void __iomem *addr, u32 offset, u32 bitmask)
+{
+	iowrite32(((ioread32(addr + offset)) | (bitmask)), (addr + offset));
+}
+
+static inline void pch_clrbit(void __iomem *addr, u32 offset, u32 bitmask)
+{
+	iowrite32(((ioread32(addr + offset)) & (~(bitmask))), (addr + offset));
+}
+
+/**
+ * pch_init() - hardware initialization of I2C module
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_init(struct i2c_algo_pch_data *adap)
+{
+	u32 pch_i2cbc;
+	u32 pch_i2ctmr;
+	u32 reg_value;
+	void __iomem *p = adap->pch_base_address;
+
+	/* reset I2C controller */
+	iowrite32(0x01, p + PCH_I2CSRST);
+	iowrite32(0x0, p + PCH_I2CSRST);
+	/* Initialize I2C registers */
+	iowrite32(CLR_REG, p + PCH_I2CCTL);
+	iowrite32(CLR_REG, p + PCH_I2CMOD);
+	iowrite32(CLR_REG, p + PCH_I2CBUFFOR);
+	iowrite32(CLR_REG, p + PCH_I2CBUFSLV);
+	iowrite32(CLR_REG, p + PCH_I2CBUFSUB);
+	iowrite32(CLR_REG, p + PCH_I2CBUFMSK);
+	iowrite32(CLR_REG, p + PCH_I2CESRFOR);
+	iowrite32(CLR_REG, p + PCH_I2CESRMSK);
+	iowrite32(0x21, p + PCH_I2CNF);
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+		"Cleared the registers PCH_I2CCTL,PCH_I2CMOD,PCH_I2CBUFFOR\n,"
+		"PCH_I2CBUFSLV,PCH_I2CBUFSUB,PCH_I2CBUFMSK,\n"
+		"PCH_I2CESRFOR,PCH_I2CESRMSK\n");
+
+	reg_value = PCH_I2CCTL_I2CMEN;
+	pch_setbit((adap->pch_base_address), PCH_I2CCTL,
+			  PCH_I2CCTL_I2CMEN);
+
+	if (pch_i2c_speed != 400)
+		pch_i2c_speed = 100;
+
+	if (pch_i2c_speed == FAST_MODE_CLK) {
+		reg_value |= FAST_MODE_EN;
+		dev_dbg(adap->pch_adapter.dev.parent, "Fast mode enabled\n");
+	}
+
+	if (pch_clk <= 0 || pch_clk > PCH_MAX_CLK)
+		pch_clk = 62500;
+
+	pch_i2cbc = ((pch_clk) + (pch_i2c_speed * 4)) / (pch_i2c_speed * 8);
+	/* Set transfer speed in I2CBC */
+	iowrite32(pch_i2cbc, p + PCH_I2CBC);
+
+	pch_i2ctmr = (pch_clk) / 8;
+	iowrite32(pch_i2ctmr, p + PCH_I2CTMR);
+
+	reg_value |= NORMAL_INTR_ENBL;	/* Enable interrupts in normal mode */
+	iowrite32(reg_value, p + PCH_I2CCTL);
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+		"%s: I2CCTL=%x pch_i2cbc=%x pch_i2ctmr=%x Enable interrupts\n",
+		__func__, ioread32(p + PCH_I2CCTL),
+		pch_i2cbc, pch_i2ctmr);
+
+	init_waitqueue_head(&pch_event);
+}
+
+static inline int ktime_lt(const ktime_t cmp1, const ktime_t cmp2)
+{
+	return cmp1.tv64 < cmp2.tv64;
+}
+
+/**
+ * pch_wait_for_bus_idle() - check the status of bus.
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ * @timeout:	waiting time counter (us).
+ */
+static s32 pch_wait_for_bus_idle(struct i2c_algo_pch_data *adap,
+				 s32 timeout)
+{
+	void __iomem *p = adap->pch_base_address;
+
+	/* MAX timeout value is timeout*1000*1000nsec */
+	ktime_t ns_val = ktime_add_ns(ktime_get(), timeout*1000*1000);
+	do {
+		if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
+			break;
+		msleep(1);
+	} while (ktime_lt(ktime_get(), ns_val));
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+			"%s : I2CSR = %x\n", __func__, ioread32(p + PCH_I2CSR));
+
+	if (timeout == 0) {
+		dev_err(adap->pch_adapter.dev.parent,
+					"%s :return%d\n", __func__, -ETIME);
+	} else {
+		dev_dbg(adap->pch_adapter.dev.parent,
+					"%s : return %d\n", __func__, 0);
+	}
+
+	return ((timeout <= 0) ? (-ETIME) : (0));
+}
+
+/**
+ * pch_start() - Generate I2C start condition in normal mode.
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ *
+ * Generate I2C start condition in normal mode by setting I2CCTL.I2CMSTA to 1.
+ */
+static void pch_start(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+	dev_dbg(adap->pch_adapter.dev.parent, "In %s : I2CCTL = %x\n",
+					__func__, ioread32(p + PCH_I2CCTL));
+	pch_setbit((adap->pch_base_address), PCH_I2CCTL, PCH_START);
+	dev_dbg(adap->pch_adapter.dev.parent,
+		"Invoke %s successfully. I2CCTL = %x\n", __func__, PCH_I2CCTL);
+}
+
+/**
+ * pch_wait_for_xfer_complete() - initiates a wait for the tx complete event
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static s32 pch_wait_for_xfer_complete(struct i2c_algo_pch_data *adap)
+{
+	s32 ret;
+	ret = wait_event_interruptible_timeout(pch_event,
+			(adap->pch_event_flag != 0), msecs_to_jiffies(50));
+	if (ret < 0)
+		goto out;
+
+	if (ret == 0) {
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+	if (adap->pch_event_flag & I2C_ERROR_MASK) {
+		ret = -EIO;
+		dev_err(adap->pch_adapter.dev.parent,
+				"error bits set: %x\n", adap->pch_event_flag);
+		goto out;
+	}
+
+	adap->pch_event_flag = 0;
+	ret = 0;
+out:
+	return ret;
+}
+
+/**
+ * pch_getack() - to confirm ACK/NACK
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static s32 pch_getack(struct i2c_algo_pch_data *adap)
+{
+	u32 reg_val;
+	void __iomem *p = adap->pch_base_address;
+	reg_val = ioread32(p + PCH_I2CSR) & PCH_GETACK;
+
+	if (reg_val == 0)
+		dev_dbg(adap->pch_adapter.dev.parent, "%s : return 0\n",
+								__func__);
+	else
+		dev_dbg(adap->pch_adapter.dev.parent, "%s : return%d\n",
+							__func__, -EPROTO);
+
+	return (((reg_val) == 0) ? (0) : (-EPROTO));
+}
+
+/**
+ * pch_stop() - generate stop condition in normal mode.
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_stop(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CCTL = %x\n", __func__,
+		ioread32(p + PCH_I2CCTL));
+	/* clear the start bit */
+	pch_clrbit((adap->pch_base_address), PCH_I2CCTL, PCH_START);
+	dev_dbg(adap->pch_adapter.dev.parent, "In %s : I2CCTL = %x\n", __func__,
+						ioread32(p + PCH_I2CCTL));
+}
+
+/**
+ * pch_repstart() - generate repeated start condition in normal mode
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_repstart(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+	dev_dbg(adap->pch_adapter.dev.parent, "In %s : I2CCTL = %x\n",
+					__func__, ioread32(p + PCH_I2CCTL));
+	pch_setbit((adap->pch_base_address), PCH_I2CCTL, PCH_REPSTART);
+
+	dev_dbg(adap->pch_adapter.dev.parent, "In %s : I2CCTL = %x\n", __func__,
+						ioread32(p + PCH_I2CCTL));
+}
+
+/**
+ * pch_writebytes() - write data to I2C bus in normal mode
+ * @i2c_adap:	Pointer to the struct i2c_adapter.
+ * @last:	specifies whether last message or not.
+ *		In the case of compound mode it will be 1 for last message,
+ *		otherwise 0.
+ * @first:	specifies whether first message or not.
+ *		1 for first message otherwise 0.
+ */
+static s32 pch_writebytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
+			  u32 last, u32 first)
+{
+	struct i2c_algo_pch_data *adap = i2c_adap->algo_data;
+	u8 *buf;
+	u32 length;
+	u32 addr;
+	u32 addr_2_msb;
+	u32 addr_8_lsb;
+	s32 wrcount;
+	void __iomem *p = adap->pch_base_address;
+	length = msgs->len;
+	buf = msgs->buf;
+	addr = msgs->addr;
+	/* enable master tx */
+	pch_setbit((adap->pch_base_address), PCH_I2CCTL, I2C_TX_MODE);
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+		"%s : I2CCTL = %x msgs->len = %d\n", __func__,
+		ioread32(p + PCH_I2CCTL), length);
+
+	if (first) {
+		if (pch_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT) == -ETIME)
+			return -ETIME;
+	}
+
+	if (msgs->flags & I2C_M_TEN) {
+		addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7);
+		iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR);
+		if (first)
+			pch_start(adap);
+		if ((pch_wait_for_xfer_complete(adap) == 0) &&
+		    (pch_getack(adap) == 0)) {
+			addr_8_lsb = (addr & I2C_ADDR_MSK);
+			iowrite32(addr_8_lsb, p + PCH_I2CDR);
+		} else {
+			pch_stop(adap);
+			return -ETIME;
+		}
+	} else {
+		/* set 7 bit slave address and R/W bit as 0 */
+		iowrite32(addr << 1, p + PCH_I2CDR);
+		if (first)
+			pch_start(adap);
+	}
+
+	if ((pch_wait_for_xfer_complete(adap) == 0) &&
+						(pch_getack(adap) == 0)) {
+		for (wrcount = 0; wrcount < length; ++wrcount) {
+			/* write buffer value to I2C data register */
+			iowrite32(buf[wrcount], p + PCH_I2CDR);
+			dev_dbg(adap->pch_adapter.dev.parent,
+				"%s : writing %x to Data register\n",
+				__func__, buf[wrcount]);
+
+			if (pch_wait_for_xfer_complete(adap) != 0) {
+				wrcount = -ETIME;
+				break;
+			}
+
+			dev_dbg(adap->pch_adapter.dev.parent,
+						"%s return %d", __func__, 0);
+
+			if (pch_getack(adap)) {
+				wrcount = -ETIME;
+				break;
+			}
+		}
+
+		/* check if this is the last message */
+		if (last)
+			pch_stop(adap);
+		else
+			pch_repstart(adap);
+	} else {
+		pch_stop(adap);
+	}
+
+	dev_err(adap->pch_adapter.dev.parent,
+					"%s return=%d\n", __func__, wrcount);
+
+	return wrcount;
+}
+
+/**
+ * pch_sendack() - send ACK
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_sendack(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CCTL = %x\n", __func__,
+		  ioread32(p + PCH_I2CCTL));
+	pch_clrbit((adap->pch_base_address), PCH_I2CCTL, PCH_ACK);
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+		"Invoke %s successfully. I2CCTL = %x\n", __func__, PCH_I2CCTL);
+}
+
+/**
+ * pch_sendnack() - send NACK
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_sendnack(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CCTL = %x\n", __func__,
+						  ioread32(p + PCH_I2CCTL));
+	pch_setbit((adap->pch_base_address), PCH_I2CCTL, PCH_ACK);
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CCTL = %x\n", __func__,
+						  ioread32(p + PCH_I2CCTL));
+}
+
+/**
+ * pch_readbytes() - read data  from I2C bus in normal mode.
+ * @i2c_adap:	Pointer to the struct i2c_adapter.
+ * @msgs:	Pointer to i2c_msg structure.
+ * @last:	specifies whether last message or not.
+ * @first:	specifies whether first message or not.
+ */
+s32 pch_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
+		  u32 last, u32 first)
+{
+	struct i2c_algo_pch_data *adap = i2c_adap->algo_data;
+
+	u8 *buf;
+	u32 count;
+	u32 length;
+	u32 addr;
+	u32 addr_2_msb;
+	void __iomem *p = adap->pch_base_address;
+	length = msgs->len;
+	buf = msgs->buf;
+	addr = msgs->addr;
+
+	/* enable master reception */
+	pch_clrbit((adap->pch_base_address), PCH_I2CCTL, I2C_TX_MODE);
+
+	if (first) {
+		if (pch_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT) == -ETIME)
+			return -ETIME;
+	}
+
+	if (msgs->flags & I2C_M_TEN) {
+		addr_2_msb = (((addr & I2C_MSB_2B_MSK) >> 7) | (I2C_RD));
+		iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR);
+
+	} else {
+		/* 7 address bits + R/W bit */
+		addr = (((addr) << 1) | (I2C_RD));
+		iowrite32(addr, p + PCH_I2CDR);
+	}
+
+	/* check if it is the first message */
+	if (first)
+		pch_start(adap);
+
+	if ((pch_wait_for_xfer_complete(adap) == 0)
+					    && (pch_getack(adap) == 0)) {
+		dev_dbg(adap->pch_adapter.dev.parent,
+						"%s return %d", __func__, 0);
+
+		if (length == 0) {
+			pch_stop(adap);
+			ioread32(p + PCH_I2CDR); /* Dummy read needs */
+
+			count = length;
+		} else {
+			int read_index;
+			int loop;
+			pch_sendack(adap);
+
+			/* Dummy read */
+			for (loop = 1, read_index = 0; loop < length; loop++) {
+				buf[read_index] = ioread32(p + PCH_I2CDR);
+
+				if (loop != 1)
+					read_index++;
+
+				if (pch_wait_for_xfer_complete(adap) != 0) {
+					pch_stop(adap);
+					return -ETIME;
+				}
+			}	/* end for */
+
+			pch_sendnack(adap);
+
+			buf[read_index] = ioread32(p + PCH_I2CDR);
+
+			if (length != 1)
+				read_index++;
+
+			if (pch_wait_for_xfer_complete(adap) == 0) {
+				if (last)
+					pch_stop(adap);
+				else
+					pch_repstart(adap);
+
+				buf[read_index++] = ioread32(p + PCH_I2CDR);
+				count = read_index;
+			} else {
+				count = -ETIME;
+			}
+
+		}
+	} else {
+		count = -ETIME;
+		pch_stop(adap);
+	}
+
+	return count;
+}
+
+/**
+ * pch_cb_ch0() - Interrupt handler Call back function
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_cb_ch0(struct i2c_algo_pch_data *adap)
+{
+	u32 sts;
+	void __iomem *p = adap->pch_base_address;
+
+	sts = ioread32(p + PCH_I2CSR);
+	sts &= (I2CMAL_BIT | I2CMCF_BIT | I2CMIF_BIT);
+	if (I2CMAL_BIT & sts)
+		adap->pch_event_flag |= I2CMAL_EVENT;
+
+	if (I2CMCF_BIT & sts)
+		adap->pch_event_flag |= I2CMCF_EVENT;
+
+	/* clear the applicable bits */
+	pch_clrbit((adap->pch_base_address), PCH_I2CSR, sts);
+
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : PCH_I2CSR = %x\n",
+					__func__, ioread32(p + PCH_I2CSR));
+
+	wake_up_interruptible(&pch_event);
+}
+
+/**
+ * pch_handler_ch0() - interrupt handler for the PCH I2C controller
+ * @irq:	irq number.
+ * @pData:	cookie passed back to the handler function.
+ */
+static irqreturn_t pch_handler_ch0(int irq, void *pData)
+{
+	s32 reg_val;
+
+	struct i2c_algo_pch_data *adap_data = (struct i2c_algo_pch_data *)pData;
+	void __iomem *p = adap_data->pch_base_address;
+	u32 mode = ioread32(p + PCH_I2CMOD) & (BUFFER_MODE | EEPROM_SR_MODE);
+
+	if (mode == NORMAL_MODE) {
+		reg_val = ioread32(p + PCH_I2CSR);
+		if (reg_val & (I2CMAL_BIT | I2CMCF_BIT | I2CMIF_BIT))
+			pch_cb_ch0(adap_data);
+		else
+			goto err_out;
+	} else {
+		dev_err(adap_data->pch_adapter.dev.parent,
+			"%s I2C mode is not supported\n", __func__);
+		goto err_out;
+	}
+	return IRQ_HANDLED;
+
+err_out:
+	return IRQ_NONE;
+}
+
+/**
+ * pch_xfer() - Reading adnd writing data through I2C bus
+ * @i2c_adap:	Pointer to the struct i2c_adapter.
+ * @msgs:	Pointer to i2c_msg structure.
+ * @num:	number of messages.
+ */
+static s32 pch_xfer(struct i2c_adapter *i2c_adap,
+		    struct i2c_msg *msgs, s32 num)
+{
+	struct i2c_msg *pmsg;
+	u32 i;
+	u32 status;
+	u32 msglen;
+	u32 subaddrlen;
+	s32 ret;
+
+	struct i2c_algo_pch_data *adap = i2c_adap->algo_data;
+
+	ret = mutex_lock_interruptible(&pch_mutex);
+	if (ret) {
+		ret = -ERESTARTSYS;
+		goto return_err_nomutex;
+	}
+	if (adap->p_adapter_info->pch_suspended == false) {
+		dev_dbg(adap->pch_adapter.dev.parent,
+			"%s adap->p_adapter_info->pch_suspended is %d\n",
+			__func__, adap->p_adapter_info->pch_suspended);
+		/* transfer not completed */
+		adap->pch_xfer_in_progress = true;
+		dev_dbg(adap->pch_adapter.dev.parent,
+			"adap->pch_xfer_in_progress is %d\n",
+			adap->pch_xfer_in_progress);
+
+		ret = -EBUSY;
+		for (i = 0; i < num; i++) {
+			pmsg = &msgs[i];
+			pmsg->flags |= adap->pch_buff_mode_en;
+			status = pmsg->flags;
+			dev_dbg(adap->pch_adapter.dev.parent,
+				"After invoking I2C_MODE_SEL :flag= 0x%x\n",
+				status);
+			/* calculate sub address length and message length */
+			/* these are applicable only for buffer mode */
+			subaddrlen = pmsg->buf[0];
+			/* calculate actual message length excluding
+			 * the sub address fields */
+			msglen = (pmsg->len) - (subaddrlen + 1);
+			if (status & (I2C_M_RD)) {
+				dev_dbg(adap->pch_adapter.dev.parent,
+					"%s invoking pch_readbytes\n",
+					__func__);
+				ret = pch_readbytes(i2c_adap, pmsg,
+						      (i + 1 == num),
+						      (i == 0));
+			} else {
+				dev_info(adap->pch_adapter.dev.parent,
+					"%s invoking pch_writebytes\n",
+					__func__);
+				ret = pch_writebytes(i2c_adap, pmsg,
+						       (i + 1 == num),
+						       (i == 0));
+			}
+
+		}
+
+		adap->pch_xfer_in_progress = false;	/* transfer completed */
+
+		dev_dbg(adap->pch_adapter.dev.parent,
+					"adap->pch_xfer_in_progress is %d\n",
+					adap->pch_xfer_in_progress);
+	} else {
+		ret = -EBUSY;
+	}
+
+	mutex_unlock(&pch_mutex);
+return_err_nomutex:
+	dev_dbg(adap->pch_adapter.dev.parent, "%s return:%d\n\n\n\n",
+								__func__, ret);
+	return ret;
+}
+
+/**
+ * pch_func() - return the functionality of the I2C driver
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static u32 pch_func(struct i2c_adapter *adap)
+{
+	u32 ret;
+	ret = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR;
+	return ret;
+}
+
+static struct i2c_algorithm pch_algorithm = {
+	.master_xfer = pch_xfer,
+	.functionality = pch_func
+};
+
+/**
+ * pch_disbl_int() - Disable PCH I2C interrupts
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_disbl_int(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+
+	pch_clrbit((adap->pch_base_address), PCH_I2CCTL,
+			  NORMAL_INTR_ENBL);
+
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CCTL = %x\n", __func__,
+			  ioread32(p + PCH_I2CCTL));
+
+	iowrite32(EEPROM_RST_INTR_DISBL, p + PCH_I2CESRMSK);
+
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : PCH_I2CESRMSK = %x\n",
+					__func__, ioread32(p + PCH_I2CESRMSK));
+
+	iowrite32(BUFFER_MODE_INTR_DISBL, p + PCH_I2CBUFMSK);
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : PCH_I2CBUFMSK = %x\n",
+		__func__, ioread32(p + PCH_I2CBUFMSK));
+}
+
+static int __devinit pch_probe(struct pci_dev *pdev,
+			       const struct pci_device_id *id)
+{
+	int i;
+	void __iomem *base_addr;
+	s32 ret;
+	struct adapter_info *adap_info =
+			kzalloc((sizeof(struct adapter_info)), GFP_KERNEL);
+
+	dev_dbg(&pdev->dev, "Enterred in %s\n", __func__);
+
+	if (adap_info == NULL) {
+		dev_err(&pdev->dev, "Memory allocation failed FAILED");
+		ret = -ENOMEM;
+		goto return_err;
+	}
+
+	dev_dbg(&pdev->dev,
+		"%s kzalloc invoked successfully and adap_info valu = %p\n",
+		__func__, adap_info);
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "%s : pci_enable_device FAILED", __func__);
+		goto err_pci_enable;
+	}
+
+	dev_dbg(&pdev->dev, "%s pci_enable_device returns %d\n", __func__, ret);
+
+	ret = pci_request_regions(pdev, MODULE_NAME);
+	if (ret) {
+		dev_err(&pdev->dev, "pci_request_regions FAILED");
+		goto err_pci_req;
+	}
+
+	dev_dbg(&pdev->dev, "%s pci_request_regions returns %d\n",
+								__func__, ret);
+
+	base_addr = pci_iomap(pdev, 1, 0);
+
+	if (base_addr == 0) {
+		dev_err(&pdev->dev, "pci_iomap FAILED");
+		ret = -ENOMEM;
+		goto err_pci_iomap;
+	}
+
+	dev_dbg(&pdev->dev, "%s pci_iomap invoked successfully\n", __func__);
+	adap_info->pch_suspended = false;
+
+	dev_dbg(&pdev->dev, "%s pch_entcb invoked successfully\n", __func__);
+
+	for (i = 0; i < PCH_MAX_CHN; i++) {
+		adap_info->pch_data[i].p_adapter_info = adap_info;
+
+		adap_info->pch_data[i].pch_adapter.owner = THIS_MODULE;
+		adap_info->pch_data[i].pch_adapter.class = I2C_CLASS_HWMON;
+		strcpy(adap_info->pch_data[i].pch_adapter.name, "pch_i2c");
+		adap_info->pch_data[i].pch_adapter.algo = &pch_algorithm;
+		adap_info->pch_data[i].pch_adapter.algo_data =
+							&adap_info->pch_data[i];
+
+		/* (i * 0x80) + base_addr; */
+		adap_info->pch_data[i].pch_base_address = base_addr;
+
+		adap_info->pch_data[i].pch_adapter.dev.parent = &pdev->dev;
+
+		ret = i2c_add_adapter(&(adap_info->pch_data[i].pch_adapter));
+
+		if (ret) {
+			dev_err(&pdev->dev, "i2c_add_adapter FAILED");
+			goto err_i2c_add_adapter;
+		}
+
+		dev_dbg(&pdev->dev,
+			"i2c_add_adapter returns %d for channel-%d\n", ret, i);
+		pch_init(&adap_info->pch_data[i]);
+		dev_dbg(&pdev->dev, "pch_init invoked successfully\n");
+		ret = request_irq(pdev->irq, pch_handler_list[i], IRQF_SHARED,
+			  MODULE_NAME, &adap_info->pch_data[i]);
+		if (ret) {
+			dev_err(&pdev->dev, "request_irq Failed\n");
+			goto err_request_irq;
+		}
+	}
+
+	dev_dbg(&pdev->dev, "request_irq returns %d pch_probe returns.\n", ret);
+	pci_set_drvdata(pdev, adap_info);
+	return 0;
+
+err_request_irq:
+	for (i = 0; i < PCH_MAX_CHN; i++)
+		i2c_del_adapter(&(adap_info->pch_data[i].pch_adapter));
+err_i2c_add_adapter:
+	pci_iounmap(pdev, base_addr);
+err_pci_iomap:
+	pci_release_regions(pdev);
+err_pci_req:
+	pci_disable_device(pdev);
+err_pci_enable:
+	kfree(adap_info);
+return_err:
+	return ret;
+}
+
+static void __devexit pch_remove(struct pci_dev *pdev)
+{
+	int i;
+
+	struct adapter_info *adap_info = pci_get_drvdata(pdev);
+
+	dev_dbg(&pdev->dev, "invoked function pci_get_drvdata successfully\n");
+
+	for (i = 0; i < PCH_MAX_CHN; i++) {
+		pch_disbl_int(&adap_info->pch_data[i]);
+
+		free_irq(pdev->irq, &adap_info->pch_data[i]);
+		dev_dbg(&pdev->dev, "free_irq invoked successfully\n");
+
+		i2c_del_adapter(&(adap_info->pch_data[i].pch_adapter));
+		dev_dbg(&pdev->dev, "invoked i2c_del_adapter successfully\n");
+	}
+
+	if (adap_info->pch_data[0].pch_base_address) {
+		pci_iounmap(pdev, adap_info->pch_data[0].pch_base_address);
+		dev_dbg(&pdev->dev, "pci_iounmap invoked successfully\n");
+		adap_info->pch_data[0].pch_base_address = 0;
+	}
+
+	pci_set_drvdata(pdev, NULL);
+
+	pci_release_regions(pdev);
+	dev_dbg(&pdev->dev, "pci_release_regions invoked successfully\n");
+
+	pci_disable_device(pdev);
+	kfree(adap_info);
+	dev_dbg(&pdev->dev,
+		"pci_disable_device invoked success.%s invoked success\n",
+		__func__);
+}
+
+#ifdef CONFIG_PM
+static int pch_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	int i;
+	int ret;
+
+	struct adapter_info *adap_info = pci_get_drvdata(pdev);
+	void __iomem *p = adap_info->pch_data[0].pch_base_address;
+
+	dev_dbg(&pdev->dev, "invoked function pci_get_drvdata successfully\n");
+
+	adap_info->pch_suspended = true;
+
+	for (i = 0; i < PCH_MAX_CHN; i++) {
+		while ((adap_info->pch_data[i].pch_xfer_in_progress)) {
+			/* It is assumed that any pending transfer will
+			 * be completed after the delay
+			 */
+			msleep(1);
+		}
+		/* Disable the i2c interrupts */
+		pch_disbl_int(&adap_info->pch_data[i]);
+	}
+
+	dev_dbg(&pdev->dev,
+		"I2CSR = %x I2CBUFSTA = %x I2CESRSTA = %x "
+		"invoked function pch_disbl_int successfully\n",
+		ioread32(p + 0x08),
+		ioread32(p + 0x30),
+		ioread32(p + 0x44));
+
+	ret = pci_save_state(pdev);
+
+	if (ret) {
+		dev_err(&pdev->dev, "pci_save_state failed\n");
+		return ret;
+	}
+
+	dev_dbg(&pdev->dev, "Invoked pci_save_state successfully\n");
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "Invoked pci_enable_wake successfully\n");
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "Invoked pci_disable_device successfully\n");
+
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+	dev_dbg(&pdev->dev,
+		"Invoked pci_set_power_state successfully. %s returns 0\n",
+		__func__);
+
+	return 0;
+}
+
+static int pch_resume(struct pci_dev *pdev)
+{
+	struct adapter_info *adap_info = pci_get_drvdata(pdev);
+	int i;
+
+	dev_dbg(&pdev->dev, "invoked function pci_get_drvdata successfully\n");
+
+	pci_set_power_state(pdev, PCI_D0);
+	dev_dbg(&pdev->dev, "Invoked pci_set_power_state successfully\n");
+
+	pci_restore_state(pdev);
+	dev_dbg(&pdev->dev, "Invoked pci_restore_state successfully\n");
+
+	if (pci_enable_device(pdev) < 0) {
+		dev_err(&pdev->dev, "pci_enable_device failed in pch_resume\n");
+		return -EIO;
+	}
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+
+	dev_dbg(&pdev->dev, "Invoked pci_enable_wake successfully\n");
+
+	for (i = 0; i < PCH_MAX_CHN; i++)
+		pch_init(&adap_info->pch_data[i]);
+
+	dev_dbg(&pdev->dev, "Invoked pch_init successfully\n");
+
+	adap_info->pch_suspended = false;
+
+	dev_dbg(&pdev->dev, "%s return 0\n", __func__);
+	return 0;
+}
+#else
+#define pch_suspend NULL
+#define pch_resume NULL
+#endif
+
+static struct pci_driver pch_pcidriver = {
+	.name = "pch_i2c",
+	.id_table = pch_pcidev_id,
+	.probe = pch_probe,
+	.remove = __devexit_p(pch_remove),
+	.suspend = pch_suspend,
+	.resume = pch_resume
+};
+
+static int __init pch_pci_init(void)
+{
+	return pci_register_driver(&pch_pcidriver);
+}
+
+static void __exit pch_pci_exit(void)
+{
+	pci_unregister_driver(&pch_pcidriver);
+}
+
+MODULE_DESCRIPTION("PCH I2C PCI Driver");
+MODULE_LICENSE("GPL");
+module_init(pch_pci_init);
+module_exit(pch_pci_exit);
+module_param(pch_i2c_speed, int, (S_IRUSR | S_IWUSR));
+module_param(pch_clk, int, (S_IRUSR | S_IWUSR));
diff --git a/drivers/i2c/busses/i2c-pch.h b/drivers/i2c/busses/i2c-pch.h
new file mode 100644
index 0000000..f140ce0
--- /dev/null
+++ b/drivers/i2c/busses/i2c-pch.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef __PCH_HAL_H__
+#define __PCH_HAL_H__
+
+#define PCH_MAX_CHN	1	/* Maximum I2C channels available */
+#define PCH_EVENT_SET	0	/* I2C Interrupt Event Set Status */
+#define PCH_EVENT_NONE	1	/* I2C Interrupt Event Clear Status */
+#define PCH_MAX_CLK		100000	/* Maximum Clock speed in MHz */
+#define PCH_BUFFER_MODE_ENABLE	0x0002	/* flag for Buffer mode enable */
+#define PCH_EEPROM_SW_RST_MODE_ENABLE	0x0008	/* EEPROM SW RST enable flag */
+
+#define I2C_MODE_SEL	0x711	/* for mode selection */
+
+#define PCH_I2CSADR	0x00	/* I2C slave address register */
+#define PCH_I2CCTL	0x04	/* I2C control register */
+#define PCH_I2CSR	0x08	/* I2C status register */
+#define PCH_I2CDR	0x0C	/* I2C data register */
+#define PCH_I2CMON	0x10	/* I2C bus monitor register */
+#define PCH_I2CBC	0x14	/* I2C bus transfer rate setup counter */
+#define PCH_I2CMOD	0x18	/* I2C mode register */
+#define PCH_I2CBUFSLV	0x1C	/* I2C buffer mode slave address register */
+#define PCH_I2CBUFSUB	0x20	/* I2C buffer mode subaddress register */
+#define PCH_I2CBUFFOR	0x24	/* I2C buffer mode format register */
+#define PCH_I2CBUFCTL	0x28	/* I2C buffer mode control register */
+#define PCH_I2CBUFMSK	0x2C	/* I2C buffer mode interrupt mask register */
+#define PCH_I2CBUFSTA	0x30	/* I2C buffer mode status register */
+#define PCH_I2CBUFLEV	0x34	/* I2C buffer mode level register */
+#define PCH_I2CESRFOR	0x38	/* EEPROM software reset mode format register */
+#define PCH_I2CESRCTL	0x3C	/* EEPROM software reset mode ctrl register */
+#define PCH_I2CESRMSK	0x40	/* EEPROM software reset mode */
+#define PCH_I2CESRSTA	0x44	/* EEPROM software reset mode status register */
+#define PCH_I2CTMR	0x48	/* I2C timer register */
+#define PCH_I2CSRST	0xFC	/* I2C reset register */
+#define PCH_I2CNF	0xF8	/* I2C noise filter register */
+
+#define BUS_IDLE_TIMEOUT	20
+#define PCH_I2CCTL_I2CMEN	0x0080
+#define TEN_BIT_ADDR_DEFAULT	0xF000
+#define TEN_BIT_ADDR_MASK	0xF0
+#define PCH_START		0x0020
+#define PCH_ESR_START		0x0001
+#define PCH_BUFF_START		0x1
+#define PCH_REPSTART		0x0004
+#define PCH_ACK			0x0008
+#define PCH_GETACK		0x0001
+#define CLR_REG			0x0
+#define I2C_RD			0x1
+#define I2CMCF_BIT		0x0080
+#define I2CMIF_BIT		0x0002
+#define I2CMAL_BIT		0x0010
+#define I2CBMFI_BIT		0x0001
+#define I2CBMAL_BIT		0x0002
+#define I2CBMNA_BIT		0x0004
+#define I2CBMTO_BIT		0x0008
+#define I2CBMIS_BIT		0x0010
+#define I2CESRFI_BIT		0X0001
+#define I2CESRTO_BIT		0x0002
+#define I2CESRFIIE_BIT		0x1
+#define I2CESRTOIE_BIT		0x2
+#define I2CBMDZ_BIT		0x0040
+#define I2CBMAG_BIT		0x0020
+#define I2CMBB_BIT		0x0020
+#define BUFFER_MODE_MASK	(I2CBMFI_BIT | I2CBMAL_BIT | I2CBMNA_BIT | \
+				I2CBMTO_BIT | I2CBMIS_BIT)
+#define I2C_ADDR_MSK		0xFF
+#define I2C_MSB_2B_MSK		0x300
+#define FAST_MODE_CLK		400
+#define FAST_MODE_EN		0x0001
+#define SUB_ADDR_LEN_MAX	4
+#define BUF_LEN_MAX		32
+#define PCH_BUFFER_MODE		0x1
+#define EEPROM_SW_RST_MODE	0x0002
+#define NORMAL_INTR_ENBL	0x0300
+#define EEPROM_RST_INTR_ENBL	(I2CESRFIIE_BIT | I2CESRTOIE_BIT)
+#define EEPROM_RST_INTR_DISBL	0x0
+#define BUFFER_MODE_INTR_ENBL	0x001F
+#define BUFFER_MODE_INTR_DISBL	0x0
+#define NORMAL_MODE		0x0
+#define BUFFER_MODE		0x1
+#define EEPROM_SR_MODE		0x2
+#define I2C_TX_MODE		0x0010
+#define PCH_BUF_TX		0xFFF7
+#define PCH_BUF_RD		0x0008
+#define I2C_ERROR_MASK	(I2CESRTO_EVENT | I2CBMIS_EVENT | I2CBMTO_EVENT | \
+			I2CBMNA_EVENT | I2CBMAL_EVENT | I2CMAL_EVENT)
+#define I2CMAL_EVENT		0x0001
+#define I2CMCF_EVENT		0x0002
+#define I2CBMFI_EVENT		0x0004
+#define I2CBMAL_EVENT		0x0008
+#define I2CBMNA_EVENT		0x0010
+#define I2CBMTO_EVENT		0x0020
+#define I2CBMIS_EVENT		0x0040
+#define I2CESRFI_EVENT		0x0080
+#define I2CESRTO_EVENT		0x0100
+
+#define MODULE_NAME		"pch_i2c"
+#define PCI_DEVICE_ID_PCH_I2C	0x8817
+
+/**
+ * struct i2c_algo_pch_data - for I2C driver functionalities
+ * @p_adapter_info:		stores the reference to adapter_info structure
+ * @pch_adapter:		stores the reference to i2c_adapter structure
+ * @pch_base_address:		specifies the remapped base address
+ * @pch_buff_mode_en:		specifies if buffer mode is enabled
+ * @pch_event_flag:		specifies occurrence of interrupt events
+ * @pch_xfer_in_progress:	specifies whether the transfer is completed
+ */
+struct i2c_algo_pch_data {
+	struct adapter_info *p_adapter_info;
+	struct i2c_adapter pch_adapter;
+	void __iomem *pch_base_address;
+	int pch_buff_mode_en;
+	u32 pch_event_flag;
+	bool pch_xfer_in_progress;
+};
+
+/**
+ * struct adapter_info - This structure holds the adapter information for the
+			 PCH i2c controller
+ * @pch_data:	stores a list of i2c_algo_pch_data
+ * @pch_suspended:	specifies whether the system is suspended or not
+ *		perhaps with more lines and words.
+ *
+ * pch_data has as many elements as maximum I2C channels
+ */
+struct adapter_info {
+	struct i2c_algo_pch_data pch_data[PCH_MAX_CHN];
+	bool pch_suspended;
+};
+
+#endif
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index f4110aa..53e13de 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -36,6 +36,7 @@
 #include <linux/i2c-dev.h>
 #include <linux/jiffies.h>
 #include <linux/uaccess.h>
+#include "busses/i2c-pch.h"
 
 static struct i2c_driver i2cdev_driver;
 
@@ -372,6 +373,12 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	struct i2c_client *client = file->private_data;
 	unsigned long funcs;
 
+	unsigned long pch_mode;
+	int ret;
+
+	struct i2c_msg msg;
+	unsigned char msgbuf[1];
+
 	dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
 		cmd, arg);
 
@@ -427,6 +434,20 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 		 */
 		client->adapter->timeout = msecs_to_jiffies(arg * 10);
 		break;
+	case I2C_MODE_SEL:
+		pch_mode = arg;
+
+		if (pch_mode <= 4) {
+			msgbuf[0] = pch_mode;
+			msg.buf = msgbuf;
+			msg.len = 1;
+			msg.flags = 0;
+			ret = i2c_transfer(client->adapter, &msg, 1);
+		} else {
+			printk(KERN_ERR "I2C mode sel:Invalid mode\n");
+			ret = -EINVAL;
+		}
+		return ret;
 	default:
 		/* NOTE:  returning a fault code here could cause trouble
 		 * in buggy userspace code.  Some old kernel bugs returned
-- 
1.6.0.6


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

* [PATCH] I2C driver of Topcliff PCH
@ 2010-07-21  6:46   ` Masayuki Ohtak
  0 siblings, 0 replies; 46+ messages in thread
From: Masayuki Ohtak @ 2010-07-21  6:46 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Jean Delvare (PC drivers, core), Ben Dooks (embedded platforms),
	linux-i2c-u79uwXL29TY76Z2rM5mHXA, LKML,
	qi.wang-ral2JQCrhuEAvxtiuMwx3w, Wang, Yong Y,
	joel.clark-ral2JQCrhuEAvxtiuMwx3w,
	andrew.chih.howe.khor-ral2JQCrhuEAvxtiuMwx3w

Hi Arnd,

I have modified for your comments.
Please confirm below.

Thanks, Ohtake

---
I2C driver of Topcliff PCH

Topcliff PCH is the platform controller hub that is going to be used in
Intel's upcoming general embedded platform. All IO peripherals in
Topcliff PCH are actually devices sitting on AMBA bus. 
Topcliff PCH has I2C I/F. Using this I/F, it is able to access system
devices connected to I2C.

Signed-off-by: Masayuki Ohtake <masa-korg-ECg8zkTtlr0C6LszWs/t0g@public.gmane.org>

---
 drivers/i2c/busses/Kconfig   |    8 +
 drivers/i2c/busses/Makefile  |    3 +
 drivers/i2c/busses/i2c-pch.c |  910 ++++++++++++++++++++++++++++++++++++++++++
 drivers/i2c/busses/i2c-pch.h |  147 +++++++
 drivers/i2c/i2c-dev.c        |   21 +
 5 files changed, 1089 insertions(+), 0 deletions(-)
 create mode 100644 drivers/i2c/busses/i2c-pch.c
 create mode 100644 drivers/i2c/busses/i2c-pch.h

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 5f318ce..98e7201 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -7,6 +7,14 @@ menu "I2C Hardware Bus support"
 comment "PC SMBus host controller drivers"
 	depends on PCI
 
+config PCH_I2C
+	tristate "PCH I2C"
+	depends on PCI
+	help
+	  This driver is for PCH I2C of Topcliff which is an IOH for x86
+	  embedded processor.
+	  This driver can access PCH I2C bus device.
+
 config I2C_ALI1535
 	tristate "ALI 1535"
 	depends on PCI
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 302c551..3e6b8d6 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -75,3 +75,6 @@ obj-$(CONFIG_SCx200_I2C)	+= scx200_i2c.o
 ifeq ($(CONFIG_I2C_DEBUG_BUS),y)
 EXTRA_CFLAGS += -DDEBUG
 endif
+
+obj-$(CONFIG_PCH_I2C) += pch_i2c.o
+pch_i2c-objs := i2c-pch.o
diff --git a/drivers/i2c/busses/i2c-pch.c b/drivers/i2c/busses/i2c-pch.c
new file mode 100644
index 0000000..7939781
--- /dev/null
+++ b/drivers/i2c/busses/i2c-pch.c
@@ -0,0 +1,910 @@
+/*
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+#include <linux/ktime.h>
+
+#include "i2c-pch.h"
+
+static int pch_i2c_speed = 100; /* I2C bus speed in Kbps */
+static int pch_clk = 50000;	/* specifies I2C clock speed in KHz */
+static wait_queue_head_t pch_event;
+static DEFINE_MUTEX(pch_mutex);
+
+static struct pci_device_id __devinitdata pch_pcidev_id[] = {
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH_I2C)},
+	{0,}
+};
+
+static irqreturn_t pch_handler_ch0(int irq, void *pData);
+static irqreturn_t(*pch_handler_list[PCH_MAX_CHN]) (int irq, void *pData) = {
+	pch_handler_ch0,
+};
+
+static inline void pch_setbit(void __iomem *addr, u32 offset, u32 bitmask)
+{
+	iowrite32(((ioread32(addr + offset)) | (bitmask)), (addr + offset));
+}
+
+static inline void pch_clrbit(void __iomem *addr, u32 offset, u32 bitmask)
+{
+	iowrite32(((ioread32(addr + offset)) & (~(bitmask))), (addr + offset));
+}
+
+/**
+ * pch_init() - hardware initialization of I2C module
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_init(struct i2c_algo_pch_data *adap)
+{
+	u32 pch_i2cbc;
+	u32 pch_i2ctmr;
+	u32 reg_value;
+	void __iomem *p = adap->pch_base_address;
+
+	/* reset I2C controller */
+	iowrite32(0x01, p + PCH_I2CSRST);
+	iowrite32(0x0, p + PCH_I2CSRST);
+	/* Initialize I2C registers */
+	iowrite32(CLR_REG, p + PCH_I2CCTL);
+	iowrite32(CLR_REG, p + PCH_I2CMOD);
+	iowrite32(CLR_REG, p + PCH_I2CBUFFOR);
+	iowrite32(CLR_REG, p + PCH_I2CBUFSLV);
+	iowrite32(CLR_REG, p + PCH_I2CBUFSUB);
+	iowrite32(CLR_REG, p + PCH_I2CBUFMSK);
+	iowrite32(CLR_REG, p + PCH_I2CESRFOR);
+	iowrite32(CLR_REG, p + PCH_I2CESRMSK);
+	iowrite32(0x21, p + PCH_I2CNF);
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+		"Cleared the registers PCH_I2CCTL,PCH_I2CMOD,PCH_I2CBUFFOR\n,"
+		"PCH_I2CBUFSLV,PCH_I2CBUFSUB,PCH_I2CBUFMSK,\n"
+		"PCH_I2CESRFOR,PCH_I2CESRMSK\n");
+
+	reg_value = PCH_I2CCTL_I2CMEN;
+	pch_setbit((adap->pch_base_address), PCH_I2CCTL,
+			  PCH_I2CCTL_I2CMEN);
+
+	if (pch_i2c_speed != 400)
+		pch_i2c_speed = 100;
+
+	if (pch_i2c_speed == FAST_MODE_CLK) {
+		reg_value |= FAST_MODE_EN;
+		dev_dbg(adap->pch_adapter.dev.parent, "Fast mode enabled\n");
+	}
+
+	if (pch_clk <= 0 || pch_clk > PCH_MAX_CLK)
+		pch_clk = 62500;
+
+	pch_i2cbc = ((pch_clk) + (pch_i2c_speed * 4)) / (pch_i2c_speed * 8);
+	/* Set transfer speed in I2CBC */
+	iowrite32(pch_i2cbc, p + PCH_I2CBC);
+
+	pch_i2ctmr = (pch_clk) / 8;
+	iowrite32(pch_i2ctmr, p + PCH_I2CTMR);
+
+	reg_value |= NORMAL_INTR_ENBL;	/* Enable interrupts in normal mode */
+	iowrite32(reg_value, p + PCH_I2CCTL);
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+		"%s: I2CCTL=%x pch_i2cbc=%x pch_i2ctmr=%x Enable interrupts\n",
+		__func__, ioread32(p + PCH_I2CCTL),
+		pch_i2cbc, pch_i2ctmr);
+
+	init_waitqueue_head(&pch_event);
+}
+
+static inline int ktime_lt(const ktime_t cmp1, const ktime_t cmp2)
+{
+	return cmp1.tv64 < cmp2.tv64;
+}
+
+/**
+ * pch_wait_for_bus_idle() - check the status of bus.
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ * @timeout:	waiting time counter (us).
+ */
+static s32 pch_wait_for_bus_idle(struct i2c_algo_pch_data *adap,
+				 s32 timeout)
+{
+	void __iomem *p = adap->pch_base_address;
+
+	/* MAX timeout value is timeout*1000*1000nsec */
+	ktime_t ns_val = ktime_add_ns(ktime_get(), timeout*1000*1000);
+	do {
+		if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
+			break;
+		msleep(1);
+	} while (ktime_lt(ktime_get(), ns_val));
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+			"%s : I2CSR = %x\n", __func__, ioread32(p + PCH_I2CSR));
+
+	if (timeout == 0) {
+		dev_err(adap->pch_adapter.dev.parent,
+					"%s :return%d\n", __func__, -ETIME);
+	} else {
+		dev_dbg(adap->pch_adapter.dev.parent,
+					"%s : return %d\n", __func__, 0);
+	}
+
+	return ((timeout <= 0) ? (-ETIME) : (0));
+}
+
+/**
+ * pch_start() - Generate I2C start condition in normal mode.
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ *
+ * Generate I2C start condition in normal mode by setting I2CCTL.I2CMSTA to 1.
+ */
+static void pch_start(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+	dev_dbg(adap->pch_adapter.dev.parent, "In %s : I2CCTL = %x\n",
+					__func__, ioread32(p + PCH_I2CCTL));
+	pch_setbit((adap->pch_base_address), PCH_I2CCTL, PCH_START);
+	dev_dbg(adap->pch_adapter.dev.parent,
+		"Invoke %s successfully. I2CCTL = %x\n", __func__, PCH_I2CCTL);
+}
+
+/**
+ * pch_wait_for_xfer_complete() - initiates a wait for the tx complete event
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static s32 pch_wait_for_xfer_complete(struct i2c_algo_pch_data *adap)
+{
+	s32 ret;
+	ret = wait_event_interruptible_timeout(pch_event,
+			(adap->pch_event_flag != 0), msecs_to_jiffies(50));
+	if (ret < 0)
+		goto out;
+
+	if (ret == 0) {
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+	if (adap->pch_event_flag & I2C_ERROR_MASK) {
+		ret = -EIO;
+		dev_err(adap->pch_adapter.dev.parent,
+				"error bits set: %x\n", adap->pch_event_flag);
+		goto out;
+	}
+
+	adap->pch_event_flag = 0;
+	ret = 0;
+out:
+	return ret;
+}
+
+/**
+ * pch_getack() - to confirm ACK/NACK
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static s32 pch_getack(struct i2c_algo_pch_data *adap)
+{
+	u32 reg_val;
+	void __iomem *p = adap->pch_base_address;
+	reg_val = ioread32(p + PCH_I2CSR) & PCH_GETACK;
+
+	if (reg_val == 0)
+		dev_dbg(adap->pch_adapter.dev.parent, "%s : return 0\n",
+								__func__);
+	else
+		dev_dbg(adap->pch_adapter.dev.parent, "%s : return%d\n",
+							__func__, -EPROTO);
+
+	return (((reg_val) == 0) ? (0) : (-EPROTO));
+}
+
+/**
+ * pch_stop() - generate stop condition in normal mode.
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_stop(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CCTL = %x\n", __func__,
+		ioread32(p + PCH_I2CCTL));
+	/* clear the start bit */
+	pch_clrbit((adap->pch_base_address), PCH_I2CCTL, PCH_START);
+	dev_dbg(adap->pch_adapter.dev.parent, "In %s : I2CCTL = %x\n", __func__,
+						ioread32(p + PCH_I2CCTL));
+}
+
+/**
+ * pch_repstart() - generate repeated start condition in normal mode
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_repstart(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+	dev_dbg(adap->pch_adapter.dev.parent, "In %s : I2CCTL = %x\n",
+					__func__, ioread32(p + PCH_I2CCTL));
+	pch_setbit((adap->pch_base_address), PCH_I2CCTL, PCH_REPSTART);
+
+	dev_dbg(adap->pch_adapter.dev.parent, "In %s : I2CCTL = %x\n", __func__,
+						ioread32(p + PCH_I2CCTL));
+}
+
+/**
+ * pch_writebytes() - write data to I2C bus in normal mode
+ * @i2c_adap:	Pointer to the struct i2c_adapter.
+ * @last:	specifies whether last message or not.
+ *		In the case of compound mode it will be 1 for last message,
+ *		otherwise 0.
+ * @first:	specifies whether first message or not.
+ *		1 for first message otherwise 0.
+ */
+static s32 pch_writebytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
+			  u32 last, u32 first)
+{
+	struct i2c_algo_pch_data *adap = i2c_adap->algo_data;
+	u8 *buf;
+	u32 length;
+	u32 addr;
+	u32 addr_2_msb;
+	u32 addr_8_lsb;
+	s32 wrcount;
+	void __iomem *p = adap->pch_base_address;
+	length = msgs->len;
+	buf = msgs->buf;
+	addr = msgs->addr;
+	/* enable master tx */
+	pch_setbit((adap->pch_base_address), PCH_I2CCTL, I2C_TX_MODE);
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+		"%s : I2CCTL = %x msgs->len = %d\n", __func__,
+		ioread32(p + PCH_I2CCTL), length);
+
+	if (first) {
+		if (pch_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT) == -ETIME)
+			return -ETIME;
+	}
+
+	if (msgs->flags & I2C_M_TEN) {
+		addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7);
+		iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR);
+		if (first)
+			pch_start(adap);
+		if ((pch_wait_for_xfer_complete(adap) == 0) &&
+		    (pch_getack(adap) == 0)) {
+			addr_8_lsb = (addr & I2C_ADDR_MSK);
+			iowrite32(addr_8_lsb, p + PCH_I2CDR);
+		} else {
+			pch_stop(adap);
+			return -ETIME;
+		}
+	} else {
+		/* set 7 bit slave address and R/W bit as 0 */
+		iowrite32(addr << 1, p + PCH_I2CDR);
+		if (first)
+			pch_start(adap);
+	}
+
+	if ((pch_wait_for_xfer_complete(adap) == 0) &&
+						(pch_getack(adap) == 0)) {
+		for (wrcount = 0; wrcount < length; ++wrcount) {
+			/* write buffer value to I2C data register */
+			iowrite32(buf[wrcount], p + PCH_I2CDR);
+			dev_dbg(adap->pch_adapter.dev.parent,
+				"%s : writing %x to Data register\n",
+				__func__, buf[wrcount]);
+
+			if (pch_wait_for_xfer_complete(adap) != 0) {
+				wrcount = -ETIME;
+				break;
+			}
+
+			dev_dbg(adap->pch_adapter.dev.parent,
+						"%s return %d", __func__, 0);
+
+			if (pch_getack(adap)) {
+				wrcount = -ETIME;
+				break;
+			}
+		}
+
+		/* check if this is the last message */
+		if (last)
+			pch_stop(adap);
+		else
+			pch_repstart(adap);
+	} else {
+		pch_stop(adap);
+	}
+
+	dev_err(adap->pch_adapter.dev.parent,
+					"%s return=%d\n", __func__, wrcount);
+
+	return wrcount;
+}
+
+/**
+ * pch_sendack() - send ACK
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_sendack(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CCTL = %x\n", __func__,
+		  ioread32(p + PCH_I2CCTL));
+	pch_clrbit((adap->pch_base_address), PCH_I2CCTL, PCH_ACK);
+
+	dev_dbg(adap->pch_adapter.dev.parent,
+		"Invoke %s successfully. I2CCTL = %x\n", __func__, PCH_I2CCTL);
+}
+
+/**
+ * pch_sendnack() - send NACK
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_sendnack(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CCTL = %x\n", __func__,
+						  ioread32(p + PCH_I2CCTL));
+	pch_setbit((adap->pch_base_address), PCH_I2CCTL, PCH_ACK);
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CCTL = %x\n", __func__,
+						  ioread32(p + PCH_I2CCTL));
+}
+
+/**
+ * pch_readbytes() - read data  from I2C bus in normal mode.
+ * @i2c_adap:	Pointer to the struct i2c_adapter.
+ * @msgs:	Pointer to i2c_msg structure.
+ * @last:	specifies whether last message or not.
+ * @first:	specifies whether first message or not.
+ */
+s32 pch_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
+		  u32 last, u32 first)
+{
+	struct i2c_algo_pch_data *adap = i2c_adap->algo_data;
+
+	u8 *buf;
+	u32 count;
+	u32 length;
+	u32 addr;
+	u32 addr_2_msb;
+	void __iomem *p = adap->pch_base_address;
+	length = msgs->len;
+	buf = msgs->buf;
+	addr = msgs->addr;
+
+	/* enable master reception */
+	pch_clrbit((adap->pch_base_address), PCH_I2CCTL, I2C_TX_MODE);
+
+	if (first) {
+		if (pch_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT) == -ETIME)
+			return -ETIME;
+	}
+
+	if (msgs->flags & I2C_M_TEN) {
+		addr_2_msb = (((addr & I2C_MSB_2B_MSK) >> 7) | (I2C_RD));
+		iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR);
+
+	} else {
+		/* 7 address bits + R/W bit */
+		addr = (((addr) << 1) | (I2C_RD));
+		iowrite32(addr, p + PCH_I2CDR);
+	}
+
+	/* check if it is the first message */
+	if (first)
+		pch_start(adap);
+
+	if ((pch_wait_for_xfer_complete(adap) == 0)
+					    && (pch_getack(adap) == 0)) {
+		dev_dbg(adap->pch_adapter.dev.parent,
+						"%s return %d", __func__, 0);
+
+		if (length == 0) {
+			pch_stop(adap);
+			ioread32(p + PCH_I2CDR); /* Dummy read needs */
+
+			count = length;
+		} else {
+			int read_index;
+			int loop;
+			pch_sendack(adap);
+
+			/* Dummy read */
+			for (loop = 1, read_index = 0; loop < length; loop++) {
+				buf[read_index] = ioread32(p + PCH_I2CDR);
+
+				if (loop != 1)
+					read_index++;
+
+				if (pch_wait_for_xfer_complete(adap) != 0) {
+					pch_stop(adap);
+					return -ETIME;
+				}
+			}	/* end for */
+
+			pch_sendnack(adap);
+
+			buf[read_index] = ioread32(p + PCH_I2CDR);
+
+			if (length != 1)
+				read_index++;
+
+			if (pch_wait_for_xfer_complete(adap) == 0) {
+				if (last)
+					pch_stop(adap);
+				else
+					pch_repstart(adap);
+
+				buf[read_index++] = ioread32(p + PCH_I2CDR);
+				count = read_index;
+			} else {
+				count = -ETIME;
+			}
+
+		}
+	} else {
+		count = -ETIME;
+		pch_stop(adap);
+	}
+
+	return count;
+}
+
+/**
+ * pch_cb_ch0() - Interrupt handler Call back function
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_cb_ch0(struct i2c_algo_pch_data *adap)
+{
+	u32 sts;
+	void __iomem *p = adap->pch_base_address;
+
+	sts = ioread32(p + PCH_I2CSR);
+	sts &= (I2CMAL_BIT | I2CMCF_BIT | I2CMIF_BIT);
+	if (I2CMAL_BIT & sts)
+		adap->pch_event_flag |= I2CMAL_EVENT;
+
+	if (I2CMCF_BIT & sts)
+		adap->pch_event_flag |= I2CMCF_EVENT;
+
+	/* clear the applicable bits */
+	pch_clrbit((adap->pch_base_address), PCH_I2CSR, sts);
+
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : PCH_I2CSR = %x\n",
+					__func__, ioread32(p + PCH_I2CSR));
+
+	wake_up_interruptible(&pch_event);
+}
+
+/**
+ * pch_handler_ch0() - interrupt handler for the PCH I2C controller
+ * @irq:	irq number.
+ * @pData:	cookie passed back to the handler function.
+ */
+static irqreturn_t pch_handler_ch0(int irq, void *pData)
+{
+	s32 reg_val;
+
+	struct i2c_algo_pch_data *adap_data = (struct i2c_algo_pch_data *)pData;
+	void __iomem *p = adap_data->pch_base_address;
+	u32 mode = ioread32(p + PCH_I2CMOD) & (BUFFER_MODE | EEPROM_SR_MODE);
+
+	if (mode == NORMAL_MODE) {
+		reg_val = ioread32(p + PCH_I2CSR);
+		if (reg_val & (I2CMAL_BIT | I2CMCF_BIT | I2CMIF_BIT))
+			pch_cb_ch0(adap_data);
+		else
+			goto err_out;
+	} else {
+		dev_err(adap_data->pch_adapter.dev.parent,
+			"%s I2C mode is not supported\n", __func__);
+		goto err_out;
+	}
+	return IRQ_HANDLED;
+
+err_out:
+	return IRQ_NONE;
+}
+
+/**
+ * pch_xfer() - Reading adnd writing data through I2C bus
+ * @i2c_adap:	Pointer to the struct i2c_adapter.
+ * @msgs:	Pointer to i2c_msg structure.
+ * @num:	number of messages.
+ */
+static s32 pch_xfer(struct i2c_adapter *i2c_adap,
+		    struct i2c_msg *msgs, s32 num)
+{
+	struct i2c_msg *pmsg;
+	u32 i;
+	u32 status;
+	u32 msglen;
+	u32 subaddrlen;
+	s32 ret;
+
+	struct i2c_algo_pch_data *adap = i2c_adap->algo_data;
+
+	ret = mutex_lock_interruptible(&pch_mutex);
+	if (ret) {
+		ret = -ERESTARTSYS;
+		goto return_err_nomutex;
+	}
+	if (adap->p_adapter_info->pch_suspended == false) {
+		dev_dbg(adap->pch_adapter.dev.parent,
+			"%s adap->p_adapter_info->pch_suspended is %d\n",
+			__func__, adap->p_adapter_info->pch_suspended);
+		/* transfer not completed */
+		adap->pch_xfer_in_progress = true;
+		dev_dbg(adap->pch_adapter.dev.parent,
+			"adap->pch_xfer_in_progress is %d\n",
+			adap->pch_xfer_in_progress);
+
+		ret = -EBUSY;
+		for (i = 0; i < num; i++) {
+			pmsg = &msgs[i];
+			pmsg->flags |= adap->pch_buff_mode_en;
+			status = pmsg->flags;
+			dev_dbg(adap->pch_adapter.dev.parent,
+				"After invoking I2C_MODE_SEL :flag= 0x%x\n",
+				status);
+			/* calculate sub address length and message length */
+			/* these are applicable only for buffer mode */
+			subaddrlen = pmsg->buf[0];
+			/* calculate actual message length excluding
+			 * the sub address fields */
+			msglen = (pmsg->len) - (subaddrlen + 1);
+			if (status & (I2C_M_RD)) {
+				dev_dbg(adap->pch_adapter.dev.parent,
+					"%s invoking pch_readbytes\n",
+					__func__);
+				ret = pch_readbytes(i2c_adap, pmsg,
+						      (i + 1 == num),
+						      (i == 0));
+			} else {
+				dev_info(adap->pch_adapter.dev.parent,
+					"%s invoking pch_writebytes\n",
+					__func__);
+				ret = pch_writebytes(i2c_adap, pmsg,
+						       (i + 1 == num),
+						       (i == 0));
+			}
+
+		}
+
+		adap->pch_xfer_in_progress = false;	/* transfer completed */
+
+		dev_dbg(adap->pch_adapter.dev.parent,
+					"adap->pch_xfer_in_progress is %d\n",
+					adap->pch_xfer_in_progress);
+	} else {
+		ret = -EBUSY;
+	}
+
+	mutex_unlock(&pch_mutex);
+return_err_nomutex:
+	dev_dbg(adap->pch_adapter.dev.parent, "%s return:%d\n\n\n\n",
+								__func__, ret);
+	return ret;
+}
+
+/**
+ * pch_func() - return the functionality of the I2C driver
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static u32 pch_func(struct i2c_adapter *adap)
+{
+	u32 ret;
+	ret = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR;
+	return ret;
+}
+
+static struct i2c_algorithm pch_algorithm = {
+	.master_xfer = pch_xfer,
+	.functionality = pch_func
+};
+
+/**
+ * pch_disbl_int() - Disable PCH I2C interrupts
+ * @adap:	Pointer to struct i2c_algo_pch_data.
+ */
+static void pch_disbl_int(struct i2c_algo_pch_data *adap)
+{
+	void __iomem *p = adap->pch_base_address;
+
+	pch_clrbit((adap->pch_base_address), PCH_I2CCTL,
+			  NORMAL_INTR_ENBL);
+
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : I2CCTL = %x\n", __func__,
+			  ioread32(p + PCH_I2CCTL));
+
+	iowrite32(EEPROM_RST_INTR_DISBL, p + PCH_I2CESRMSK);
+
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : PCH_I2CESRMSK = %x\n",
+					__func__, ioread32(p + PCH_I2CESRMSK));
+
+	iowrite32(BUFFER_MODE_INTR_DISBL, p + PCH_I2CBUFMSK);
+	dev_dbg(adap->pch_adapter.dev.parent, "%s : PCH_I2CBUFMSK = %x\n",
+		__func__, ioread32(p + PCH_I2CBUFMSK));
+}
+
+static int __devinit pch_probe(struct pci_dev *pdev,
+			       const struct pci_device_id *id)
+{
+	int i;
+	void __iomem *base_addr;
+	s32 ret;
+	struct adapter_info *adap_info =
+			kzalloc((sizeof(struct adapter_info)), GFP_KERNEL);
+
+	dev_dbg(&pdev->dev, "Enterred in %s\n", __func__);
+
+	if (adap_info == NULL) {
+		dev_err(&pdev->dev, "Memory allocation failed FAILED");
+		ret = -ENOMEM;
+		goto return_err;
+	}
+
+	dev_dbg(&pdev->dev,
+		"%s kzalloc invoked successfully and adap_info valu = %p\n",
+		__func__, adap_info);
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "%s : pci_enable_device FAILED", __func__);
+		goto err_pci_enable;
+	}
+
+	dev_dbg(&pdev->dev, "%s pci_enable_device returns %d\n", __func__, ret);
+
+	ret = pci_request_regions(pdev, MODULE_NAME);
+	if (ret) {
+		dev_err(&pdev->dev, "pci_request_regions FAILED");
+		goto err_pci_req;
+	}
+
+	dev_dbg(&pdev->dev, "%s pci_request_regions returns %d\n",
+								__func__, ret);
+
+	base_addr = pci_iomap(pdev, 1, 0);
+
+	if (base_addr == 0) {
+		dev_err(&pdev->dev, "pci_iomap FAILED");
+		ret = -ENOMEM;
+		goto err_pci_iomap;
+	}
+
+	dev_dbg(&pdev->dev, "%s pci_iomap invoked successfully\n", __func__);
+	adap_info->pch_suspended = false;
+
+	dev_dbg(&pdev->dev, "%s pch_entcb invoked successfully\n", __func__);
+
+	for (i = 0; i < PCH_MAX_CHN; i++) {
+		adap_info->pch_data[i].p_adapter_info = adap_info;
+
+		adap_info->pch_data[i].pch_adapter.owner = THIS_MODULE;
+		adap_info->pch_data[i].pch_adapter.class = I2C_CLASS_HWMON;
+		strcpy(adap_info->pch_data[i].pch_adapter.name, "pch_i2c");
+		adap_info->pch_data[i].pch_adapter.algo = &pch_algorithm;
+		adap_info->pch_data[i].pch_adapter.algo_data =
+							&adap_info->pch_data[i];
+
+		/* (i * 0x80) + base_addr; */
+		adap_info->pch_data[i].pch_base_address = base_addr;
+
+		adap_info->pch_data[i].pch_adapter.dev.parent = &pdev->dev;
+
+		ret = i2c_add_adapter(&(adap_info->pch_data[i].pch_adapter));
+
+		if (ret) {
+			dev_err(&pdev->dev, "i2c_add_adapter FAILED");
+			goto err_i2c_add_adapter;
+		}
+
+		dev_dbg(&pdev->dev,
+			"i2c_add_adapter returns %d for channel-%d\n", ret, i);
+		pch_init(&adap_info->pch_data[i]);
+		dev_dbg(&pdev->dev, "pch_init invoked successfully\n");
+		ret = request_irq(pdev->irq, pch_handler_list[i], IRQF_SHARED,
+			  MODULE_NAME, &adap_info->pch_data[i]);
+		if (ret) {
+			dev_err(&pdev->dev, "request_irq Failed\n");
+			goto err_request_irq;
+		}
+	}
+
+	dev_dbg(&pdev->dev, "request_irq returns %d pch_probe returns.\n", ret);
+	pci_set_drvdata(pdev, adap_info);
+	return 0;
+
+err_request_irq:
+	for (i = 0; i < PCH_MAX_CHN; i++)
+		i2c_del_adapter(&(adap_info->pch_data[i].pch_adapter));
+err_i2c_add_adapter:
+	pci_iounmap(pdev, base_addr);
+err_pci_iomap:
+	pci_release_regions(pdev);
+err_pci_req:
+	pci_disable_device(pdev);
+err_pci_enable:
+	kfree(adap_info);
+return_err:
+	return ret;
+}
+
+static void __devexit pch_remove(struct pci_dev *pdev)
+{
+	int i;
+
+	struct adapter_info *adap_info = pci_get_drvdata(pdev);
+
+	dev_dbg(&pdev->dev, "invoked function pci_get_drvdata successfully\n");
+
+	for (i = 0; i < PCH_MAX_CHN; i++) {
+		pch_disbl_int(&adap_info->pch_data[i]);
+
+		free_irq(pdev->irq, &adap_info->pch_data[i]);
+		dev_dbg(&pdev->dev, "free_irq invoked successfully\n");
+
+		i2c_del_adapter(&(adap_info->pch_data[i].pch_adapter));
+		dev_dbg(&pdev->dev, "invoked i2c_del_adapter successfully\n");
+	}
+
+	if (adap_info->pch_data[0].pch_base_address) {
+		pci_iounmap(pdev, adap_info->pch_data[0].pch_base_address);
+		dev_dbg(&pdev->dev, "pci_iounmap invoked successfully\n");
+		adap_info->pch_data[0].pch_base_address = 0;
+	}
+
+	pci_set_drvdata(pdev, NULL);
+
+	pci_release_regions(pdev);
+	dev_dbg(&pdev->dev, "pci_release_regions invoked successfully\n");
+
+	pci_disable_device(pdev);
+	kfree(adap_info);
+	dev_dbg(&pdev->dev,
+		"pci_disable_device invoked success.%s invoked success\n",
+		__func__);
+}
+
+#ifdef CONFIG_PM
+static int pch_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	int i;
+	int ret;
+
+	struct adapter_info *adap_info = pci_get_drvdata(pdev);
+	void __iomem *p = adap_info->pch_data[0].pch_base_address;
+
+	dev_dbg(&pdev->dev, "invoked function pci_get_drvdata successfully\n");
+
+	adap_info->pch_suspended = true;
+
+	for (i = 0; i < PCH_MAX_CHN; i++) {
+		while ((adap_info->pch_data[i].pch_xfer_in_progress)) {
+			/* It is assumed that any pending transfer will
+			 * be completed after the delay
+			 */
+			msleep(1);
+		}
+		/* Disable the i2c interrupts */
+		pch_disbl_int(&adap_info->pch_data[i]);
+	}
+
+	dev_dbg(&pdev->dev,
+		"I2CSR = %x I2CBUFSTA = %x I2CESRSTA = %x "
+		"invoked function pch_disbl_int successfully\n",
+		ioread32(p + 0x08),
+		ioread32(p + 0x30),
+		ioread32(p + 0x44));
+
+	ret = pci_save_state(pdev);
+
+	if (ret) {
+		dev_err(&pdev->dev, "pci_save_state failed\n");
+		return ret;
+	}
+
+	dev_dbg(&pdev->dev, "Invoked pci_save_state successfully\n");
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	dev_dbg(&pdev->dev, "Invoked pci_enable_wake successfully\n");
+
+	pci_disable_device(pdev);
+	dev_dbg(&pdev->dev, "Invoked pci_disable_device successfully\n");
+
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+	dev_dbg(&pdev->dev,
+		"Invoked pci_set_power_state successfully. %s returns 0\n",
+		__func__);
+
+	return 0;
+}
+
+static int pch_resume(struct pci_dev *pdev)
+{
+	struct adapter_info *adap_info = pci_get_drvdata(pdev);
+	int i;
+
+	dev_dbg(&pdev->dev, "invoked function pci_get_drvdata successfully\n");
+
+	pci_set_power_state(pdev, PCI_D0);
+	dev_dbg(&pdev->dev, "Invoked pci_set_power_state successfully\n");
+
+	pci_restore_state(pdev);
+	dev_dbg(&pdev->dev, "Invoked pci_restore_state successfully\n");
+
+	if (pci_enable_device(pdev) < 0) {
+		dev_err(&pdev->dev, "pci_enable_device failed in pch_resume\n");
+		return -EIO;
+	}
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+
+	dev_dbg(&pdev->dev, "Invoked pci_enable_wake successfully\n");
+
+	for (i = 0; i < PCH_MAX_CHN; i++)
+		pch_init(&adap_info->pch_data[i]);
+
+	dev_dbg(&pdev->dev, "Invoked pch_init successfully\n");
+
+	adap_info->pch_suspended = false;
+
+	dev_dbg(&pdev->dev, "%s return 0\n", __func__);
+	return 0;
+}
+#else
+#define pch_suspend NULL
+#define pch_resume NULL
+#endif
+
+static struct pci_driver pch_pcidriver = {
+	.name = "pch_i2c",
+	.id_table = pch_pcidev_id,
+	.probe = pch_probe,
+	.remove = __devexit_p(pch_remove),
+	.suspend = pch_suspend,
+	.resume = pch_resume
+};
+
+static int __init pch_pci_init(void)
+{
+	return pci_register_driver(&pch_pcidriver);
+}
+
+static void __exit pch_pci_exit(void)
+{
+	pci_unregister_driver(&pch_pcidriver);
+}
+
+MODULE_DESCRIPTION("PCH I2C PCI Driver");
+MODULE_LICENSE("GPL");
+module_init(pch_pci_init);
+module_exit(pch_pci_exit);
+module_param(pch_i2c_speed, int, (S_IRUSR | S_IWUSR));
+module_param(pch_clk, int, (S_IRUSR | S_IWUSR));
diff --git a/drivers/i2c/busses/i2c-pch.h b/drivers/i2c/busses/i2c-pch.h
new file mode 100644
index 0000000..f140ce0
--- /dev/null
+++ b/drivers/i2c/busses/i2c-pch.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef __PCH_HAL_H__
+#define __PCH_HAL_H__
+
+#define PCH_MAX_CHN	1	/* Maximum I2C channels available */
+#define PCH_EVENT_SET	0	/* I2C Interrupt Event Set Status */
+#define PCH_EVENT_NONE	1	/* I2C Interrupt Event Clear Status */
+#define PCH_MAX_CLK		100000	/* Maximum Clock speed in MHz */
+#define PCH_BUFFER_MODE_ENABLE	0x0002	/* flag for Buffer mode enable */
+#define PCH_EEPROM_SW_RST_MODE_ENABLE	0x0008	/* EEPROM SW RST enable flag */
+
+#define I2C_MODE_SEL	0x711	/* for mode selection */
+
+#define PCH_I2CSADR	0x00	/* I2C slave address register */
+#define PCH_I2CCTL	0x04	/* I2C control register */
+#define PCH_I2CSR	0x08	/* I2C status register */
+#define PCH_I2CDR	0x0C	/* I2C data register */
+#define PCH_I2CMON	0x10	/* I2C bus monitor register */
+#define PCH_I2CBC	0x14	/* I2C bus transfer rate setup counter */
+#define PCH_I2CMOD	0x18	/* I2C mode register */
+#define PCH_I2CBUFSLV	0x1C	/* I2C buffer mode slave address register */
+#define PCH_I2CBUFSUB	0x20	/* I2C buffer mode subaddress register */
+#define PCH_I2CBUFFOR	0x24	/* I2C buffer mode format register */
+#define PCH_I2CBUFCTL	0x28	/* I2C buffer mode control register */
+#define PCH_I2CBUFMSK	0x2C	/* I2C buffer mode interrupt mask register */
+#define PCH_I2CBUFSTA	0x30	/* I2C buffer mode status register */
+#define PCH_I2CBUFLEV	0x34	/* I2C buffer mode level register */
+#define PCH_I2CESRFOR	0x38	/* EEPROM software reset mode format register */
+#define PCH_I2CESRCTL	0x3C	/* EEPROM software reset mode ctrl register */
+#define PCH_I2CESRMSK	0x40	/* EEPROM software reset mode */
+#define PCH_I2CESRSTA	0x44	/* EEPROM software reset mode status register */
+#define PCH_I2CTMR	0x48	/* I2C timer register */
+#define PCH_I2CSRST	0xFC	/* I2C reset register */
+#define PCH_I2CNF	0xF8	/* I2C noise filter register */
+
+#define BUS_IDLE_TIMEOUT	20
+#define PCH_I2CCTL_I2CMEN	0x0080
+#define TEN_BIT_ADDR_DEFAULT	0xF000
+#define TEN_BIT_ADDR_MASK	0xF0
+#define PCH_START		0x0020
+#define PCH_ESR_START		0x0001
+#define PCH_BUFF_START		0x1
+#define PCH_REPSTART		0x0004
+#define PCH_ACK			0x0008
+#define PCH_GETACK		0x0001
+#define CLR_REG			0x0
+#define I2C_RD			0x1
+#define I2CMCF_BIT		0x0080
+#define I2CMIF_BIT		0x0002
+#define I2CMAL_BIT		0x0010
+#define I2CBMFI_BIT		0x0001
+#define I2CBMAL_BIT		0x0002
+#define I2CBMNA_BIT		0x0004
+#define I2CBMTO_BIT		0x0008
+#define I2CBMIS_BIT		0x0010
+#define I2CESRFI_BIT		0X0001
+#define I2CESRTO_BIT		0x0002
+#define I2CESRFIIE_BIT		0x1
+#define I2CESRTOIE_BIT		0x2
+#define I2CBMDZ_BIT		0x0040
+#define I2CBMAG_BIT		0x0020
+#define I2CMBB_BIT		0x0020
+#define BUFFER_MODE_MASK	(I2CBMFI_BIT | I2CBMAL_BIT | I2CBMNA_BIT | \
+				I2CBMTO_BIT | I2CBMIS_BIT)
+#define I2C_ADDR_MSK		0xFF
+#define I2C_MSB_2B_MSK		0x300
+#define FAST_MODE_CLK		400
+#define FAST_MODE_EN		0x0001
+#define SUB_ADDR_LEN_MAX	4
+#define BUF_LEN_MAX		32
+#define PCH_BUFFER_MODE		0x1
+#define EEPROM_SW_RST_MODE	0x0002
+#define NORMAL_INTR_ENBL	0x0300
+#define EEPROM_RST_INTR_ENBL	(I2CESRFIIE_BIT | I2CESRTOIE_BIT)
+#define EEPROM_RST_INTR_DISBL	0x0
+#define BUFFER_MODE_INTR_ENBL	0x001F
+#define BUFFER_MODE_INTR_DISBL	0x0
+#define NORMAL_MODE		0x0
+#define BUFFER_MODE		0x1
+#define EEPROM_SR_MODE		0x2
+#define I2C_TX_MODE		0x0010
+#define PCH_BUF_TX		0xFFF7
+#define PCH_BUF_RD		0x0008
+#define I2C_ERROR_MASK	(I2CESRTO_EVENT | I2CBMIS_EVENT | I2CBMTO_EVENT | \
+			I2CBMNA_EVENT | I2CBMAL_EVENT | I2CMAL_EVENT)
+#define I2CMAL_EVENT		0x0001
+#define I2CMCF_EVENT		0x0002
+#define I2CBMFI_EVENT		0x0004
+#define I2CBMAL_EVENT		0x0008
+#define I2CBMNA_EVENT		0x0010
+#define I2CBMTO_EVENT		0x0020
+#define I2CBMIS_EVENT		0x0040
+#define I2CESRFI_EVENT		0x0080
+#define I2CESRTO_EVENT		0x0100
+
+#define MODULE_NAME		"pch_i2c"
+#define PCI_DEVICE_ID_PCH_I2C	0x8817
+
+/**
+ * struct i2c_algo_pch_data - for I2C driver functionalities
+ * @p_adapter_info:		stores the reference to adapter_info structure
+ * @pch_adapter:		stores the reference to i2c_adapter structure
+ * @pch_base_address:		specifies the remapped base address
+ * @pch_buff_mode_en:		specifies if buffer mode is enabled
+ * @pch_event_flag:		specifies occurrence of interrupt events
+ * @pch_xfer_in_progress:	specifies whether the transfer is completed
+ */
+struct i2c_algo_pch_data {
+	struct adapter_info *p_adapter_info;
+	struct i2c_adapter pch_adapter;
+	void __iomem *pch_base_address;
+	int pch_buff_mode_en;
+	u32 pch_event_flag;
+	bool pch_xfer_in_progress;
+};
+
+/**
+ * struct adapter_info - This structure holds the adapter information for the
+			 PCH i2c controller
+ * @pch_data:	stores a list of i2c_algo_pch_data
+ * @pch_suspended:	specifies whether the system is suspended or not
+ *		perhaps with more lines and words.
+ *
+ * pch_data has as many elements as maximum I2C channels
+ */
+struct adapter_info {
+	struct i2c_algo_pch_data pch_data[PCH_MAX_CHN];
+	bool pch_suspended;
+};
+
+#endif
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index f4110aa..53e13de 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -36,6 +36,7 @@
 #include <linux/i2c-dev.h>
 #include <linux/jiffies.h>
 #include <linux/uaccess.h>
+#include "busses/i2c-pch.h"
 
 static struct i2c_driver i2cdev_driver;
 
@@ -372,6 +373,12 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	struct i2c_client *client = file->private_data;
 	unsigned long funcs;
 
+	unsigned long pch_mode;
+	int ret;
+
+	struct i2c_msg msg;
+	unsigned char msgbuf[1];
+
 	dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
 		cmd, arg);
 
@@ -427,6 +434,20 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 		 */
 		client->adapter->timeout = msecs_to_jiffies(arg * 10);
 		break;
+	case I2C_MODE_SEL:
+		pch_mode = arg;
+
+		if (pch_mode <= 4) {
+			msgbuf[0] = pch_mode;
+			msg.buf = msgbuf;
+			msg.len = 1;
+			msg.flags = 0;
+			ret = i2c_transfer(client->adapter, &msg, 1);
+		} else {
+			printk(KERN_ERR "I2C mode sel:Invalid mode\n");
+			ret = -EINVAL;
+		}
+		return ret;
 	default:
 		/* NOTE:  returning a fault code here could cause trouble
 		 * in buggy userspace code.  Some old kernel bugs returned
-- 
1.6.0.6

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

end of thread, other threads:[~2010-07-21  6:46 UTC | newest]

Thread overview: 46+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-06-22  5:33 [PATCH] Topcliff PHUB: Generate PacketHub driver Masayuki Ohtak
2010-06-22 10:33 ` Masayuki Ohtak
2010-06-22 22:12   ` Andrew Morton
2010-06-23  0:31     ` Masayuki Ohtake
2010-06-22 11:30 ` Arnd Bergmann
2010-06-22 13:52   ` Yong Wang
2010-06-29 23:31 ` Andy Isaacson
2010-06-30  5:58   ` Masayuki Ohtake
2010-06-30 18:28     ` Andy Isaacson
2010-07-01  4:08       ` Masayuki Ohtake
2010-06-30  7:51 ` [PATCH] Packet hub driver of Topcliff PCH Masayuki Ohtak
2010-06-30 18:05   ` Randy Dunlap
2010-07-01  2:52     ` Masayuki Ohtake
2010-07-01  5:14 ` Masayuki Ohtak
2010-07-01  6:58   ` Andy Isaacson
2010-07-01 10:13     ` Masayuki Ohtake
2010-07-01 10:38 ` Masayuki Ohtak
2010-07-01 15:44   ` Randy Dunlap
2010-07-05  7:20 ` Masayuki Ohtak
2010-07-05 15:04   ` Arnd Bergmann
2010-07-06 15:58   ` Randy Dunlap
2010-07-06  6:20 ` Masayuki Ohtak
2010-07-06  6:30   ` Arnd Bergmann
2010-07-07  1:19     ` Yong Wang
2010-07-09 20:00   ` Andrew Morton
2010-07-12  1:25     ` Masayuki Ohtake
2010-07-15  7:25 ` Masayuki Ohtak
2010-07-15  7:42 ` [PATCH] I2C " Masayuki Ohtak
2010-07-15 19:35   ` Arnd Bergmann
2010-07-15 19:35     ` Arnd Bergmann
2010-07-20  0:05     ` Masayuki Ohtake
2010-07-20  0:05       ` Masayuki Ohtake
2010-07-20  4:55     ` Masayuki Ohtake
2010-07-20  4:55       ` Masayuki Ohtake
2010-07-20  9:27       ` Arnd Bergmann
2010-07-20  9:27         ` Arnd Bergmann
2010-07-20 12:38         ` Masayuki Ohtake
2010-07-20 12:38           ` Masayuki Ohtake
2010-07-20  8:19     ` Masayuki Ohtake
2010-07-20  8:19       ` Masayuki Ohtake
2010-07-20  9:29       ` Arnd Bergmann
2010-07-20  9:29         ` Arnd Bergmann
2010-07-20 12:40         ` Masayuki Ohtake
2010-07-20 12:40           ` Masayuki Ohtake
2010-07-21  6:46 ` Masayuki Ohtak
2010-07-21  6:46   ` Masayuki Ohtak

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.