2010-06-07 12:39:53

by Masayuki Ohtak

[permalink] [raw]
Subject: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Alan,

We have modified for the following your indication.
Please confirm.

> > +/*!

> > + * @ingroup PHUB_PCILayerAPI

> > + * @fn static void __exit pch_phub_pci_exit(void)

> > + * @brief Provides the functionality of exiting the module

> > + * */



> Can we please do something about all this stuff first so the code is

> easier to review by those not familiar with the kernel format.



> I sent a bit of code out last time that did most of the work



> Alan


Thanks.
Masayuki Ohtake

Signed-off-by: Masayuki Ohtake <[email protected]>
---
linux-2.6.33.1/drivers/char/Kconfig | 20 +
linux-2.6.33.1/drivers/char/Makefile | 4 +
linux-2.6.33.1/drivers/char/pch_phub/Makefile | 11 +
linux-2.6.33.1/drivers/char/pch_phub/pch_phub.c | 1232 +++++++++++++++++++++++
linux-2.6.33.1/drivers/char/pch_phub/pch_phub.h | 96 ++
5 files changed, 1363 insertions(+), 0 deletions(-)
create mode 100644 linux-2.6.33.1/drivers/char/pch_phub/Makefile
create mode 100755 linux-2.6.33.1/drivers/char/pch_phub/pch_phub.c
create mode 100755 linux-2.6.33.1/drivers/char/pch_phub/pch_phub.h

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index e023682..16a8334 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -4,6 +4,26 @@

menu "Character devices"

+config PCH_IEEE1588
+ tristate "PCH IEEE1588"
+ depends on PCI
+ help
+ If you say yes to this option, support will be included for the
+ PCH IEEE1588 Host controller.
+
+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.
+
+config PCH_CAN_PCLK_50MHZ
+ bool "CAN PCLK 50MHz"
+ depends on PCH_PHUB
+ help
+ If you say yes to this option, clock is set to 50MHz.(For CAN control)
+
config VT
bool "Virtual terminal" if EMBEDDED
depends on !S390
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index f957edf..dc092d0 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -111,6 +111,10 @@ obj-$(CONFIG_PS3_FLASH) += ps3flash.o
obj-$(CONFIG_JS_RTC) += js-rtc.o
js-rtc-y = rtc.o

+obj-$(CONFIG_PCH_IEEE1588) += pch_ieee1588/
+
+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..bfe2a2b
--- /dev/null
+++ b/drivers/char/pch_phub/Makefile
@@ -0,0 +1,11 @@
+ifeq ($(CONFIG_PHUB_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+
+obj-$(CONFIG_PCH_PHUB) += pch_phub_drv.o
+#to set CAN clock to 50Mhz
+ifdef CONFIG_PCH_CAN_PCLK_50MHZ
+EXTRA_CFLAGS +=-DPCH_CAN_PCLK_50MHZ
+endif
+
+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..da5b92e
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.c
@@ -0,0 +1,1232 @@
+/*!
+ * @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 "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"
+
+#define PCH_READ_REG(a) ioread32((pch_phub_base_address + a))
+
+#define PCH_WRITE_REG(a, b) iowrite32(a, (pch_phub_base_address + b))
+
+/*--------------------------------------------
+ global variables
+--------------------------------------------*/
+struct pch_phub_reg {
+ u32 phub_id_reg; /* PHUB_ID register val */
+ u32 q_pri_val_reg; /* QUEUE_PRI_VAL register val */
+ u32 rc_q_maxsize_reg; /* RC_QUEUE_MAXSIZE register val */
+ u32 bri_q_maxsize_reg; /* BRI_QUEUE_MAXSIZE register val */
+ u32 comp_resp_timeout_reg; /* COMP_RESP_TIMEOUT register val */
+ u32 bus_slave_control_reg; /* BUS_SLAVE_CONTROL_REG register val */
+ u32 deadlock_avoid_type_reg; /* DEADLOCK_AVOID_TYPE register val */
+ u32 intpin_reg_wpermit_reg0; /* INTPIN_REG_WPERMIT register 0 val */
+ u32 intpin_reg_wpermit_reg1; /* INTPIN_REG_WPERMIT register 1 val */
+ u32 intpin_reg_wpermit_reg2; /* INTPIN_REG_WPERMIT register 2 val */
+ u32 intpin_reg_wpermit_reg3; /* INTPIN_REG_WPERMIT register 3 val */
+ /* INT_REDUCE_CONTROL registers val */
+ u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG];
+#ifdef PCH_CAN_PCLK_50MHZ
+ u32 clkcfg_reg; /* CLK CFG register val */
+#endif
+} g_pch_phub_reg;
+
+s32 pch_phub_opencount; /* check whether opened or not */
+u32 pch_phub_base_address;
+u32 pch_phub_extrom_base_address;
+s32 pch_phub_suspended;
+
+struct device *dev_dbg;
+
+DEFINE_SPINLOCK(pch_phub_lock); /* for spin lock */
+
+/**
+ * file_operations structure initialization
+ */
+const struct file_operations pch_phub_fops = {
+ .owner = THIS_MODULE,
+ .open = pch_phub_open,
+ .release = pch_phub_release,
+ .ioctl = pch_phub_ioctl,
+};
+
+/* 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;
+
+
+/*--------------------------------------------
+ exported function prototypes
+--------------------------------------------*/
+static int __devinit pch_phub_probe(struct pci_dev *pdev, const
+ struct pci_device_id *id);
+static void __devexit pch_phub_remove(struct pci_dev *pdev);
+static int pch_phub_suspend(struct pci_dev *pdev, pm_message_t state);
+static int pch_phub_resume(struct pci_dev *pdev);
+
+/*--------------------------------------------
+ structures
+--------------------------------------------*/
+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
+};
+
+static int __init pch_phub_pci_init(void);
+static void __exit pch_phub_pci_exit(void);
+
+MODULE_DESCRIPTION("PCH PACKET HUB PCI Driver");
+MODULE_LICENSE("GPL");
+module_init(pch_phub_pci_init);
+module_exit(pch_phub_pci_exit);
+module_param(pch_phub_major_no, int, S_IRUSR | S_IWUSR);
+
+
+/*--------------------------------------------
+ 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
+ */
+void pch_phub_read_modify_write_reg(unsigned long reg_addr_offset,
+ unsigned long data, unsigned long mask)
+{
+ unsigned long reg_addr = pch_phub_base_address + reg_addr_offset;
+ iowrite32(((ioread32((void __iomem *)reg_addr) & ~mask)) | data,
+ (void __iomem *)reg_addr);
+ return;
+}
+
+
+/** pch_phub_save_reg_conf - saves register configuration
+ */
+void pch_phub_save_reg_conf(void)
+{
+ u32 i = 0;
+
+ dev_dbg(dev_dbg, "pch_phub_save_reg_conf ENTRY\n");
+ /* to store contents of PHUB_ID register */
+ g_pch_phub_reg.phub_id_reg = PCH_READ_REG(PCH_PHUB_PHUB_ID_REG);
+ /* to store contents of QUEUE_PRI_VAL register */
+ g_pch_phub_reg.q_pri_val_reg = PCH_READ_REG(PCH_PHUB_QUEUE_PRI_VAL_REG);
+ /* to store contents of RC_QUEUE_MAXSIZE register */
+ g_pch_phub_reg.rc_q_maxsize_reg =
+ PCH_READ_REG(PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+ /* to store contents of BRI_QUEUE_MAXSIZE register */
+ g_pch_phub_reg.bri_q_maxsize_reg =
+ PCH_READ_REG(PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+ /* to store contents of COMP_RESP_TIMEOUT register */
+ g_pch_phub_reg.comp_resp_timeout_reg =
+ PCH_READ_REG(PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+ /* to store contents of BUS_SLAVE_CONTROL_REG register */
+ g_pch_phub_reg.bus_slave_control_reg =
+ PCH_READ_REG(PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+ /* to store contents of DEADLOCK_AVOID_TYPE register */
+ g_pch_phub_reg.deadlock_avoid_type_reg =
+ PCH_READ_REG(PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+ /* to store contents of INTPIN_REG_WPERMIT register 0 */
+ g_pch_phub_reg.intpin_reg_wpermit_reg0 =
+ PCH_READ_REG(PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+ /* to store contents of INTPIN_REG_WPERMIT register 1 */
+ g_pch_phub_reg.intpin_reg_wpermit_reg1 =
+ PCH_READ_REG(PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+ /* to store contents of INTPIN_REG_WPERMIT register 2 */
+ g_pch_phub_reg.intpin_reg_wpermit_reg2 =
+ PCH_READ_REG(PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+ /* to store contents of INTPIN_REG_WPERMIT register 3 */
+ g_pch_phub_reg.intpin_reg_wpermit_reg3 =
+ PCH_READ_REG(PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
+ dev_dbg(dev_dbg, "pch_phub_save_reg_conf : "
+ "g_pch_phub_reg.phub_id_reg=%x, "
+ "g_pch_phub_reg.q_pri_val_reg=%x, "
+ "g_pch_phub_reg.rc_q_maxsize_reg=%x, "
+ "g_pch_phub_reg.bri_q_maxsize_reg=%x, "
+ "g_pch_phub_reg.comp_resp_timeout_reg=%x, "
+ "g_pch_phub_reg.bus_slave_control_reg=%x, "
+ "g_pch_phub_reg.deadlock_avoid_type_reg=%x, "
+ "g_pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
+ "g_pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
+ "g_pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
+ "g_pch_phub_reg.intpin_reg_wpermit_reg3=%x\n",
+ g_pch_phub_reg.phub_id_reg,
+ g_pch_phub_reg.q_pri_val_reg,
+ g_pch_phub_reg.rc_q_maxsize_reg,
+ g_pch_phub_reg.bri_q_maxsize_reg,
+ g_pch_phub_reg.comp_resp_timeout_reg,
+ g_pch_phub_reg.bus_slave_control_reg,
+ g_pch_phub_reg.deadlock_avoid_type_reg,
+ g_pch_phub_reg.intpin_reg_wpermit_reg0,
+ g_pch_phub_reg.intpin_reg_wpermit_reg1,
+ g_pch_phub_reg.intpin_reg_wpermit_reg2,
+ g_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++) {
+ g_pch_phub_reg.int_reduce_control_reg[i] =
+ PCH_READ_REG(
+ PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
+ dev_dbg(dev_dbg, "pch_phub_save_reg_conf : "
+ "g_pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
+ i, g_pch_phub_reg.int_reduce_control_reg[i]);
+ }
+#ifdef PCH_CAN_PCLK_50MHZ
+ /* save clk cfg register */
+ g_pch_phub_reg.clkcfg_reg = PCH_READ_REG(CLKCFG_REG_OFFSET);
+#endif
+ return;
+}
+
+/** pch_phub_restore_reg_conf - restore register configuration
+ */
+
+void pch_phub_restore_reg_conf(void)
+{
+ u32 i = 0;
+
+ dev_dbg(dev_dbg, "pch_phub_restore_reg_conf ENTRY\n");
+ /* to store contents of PHUB_ID register */
+ PCH_WRITE_REG(g_pch_phub_reg.phub_id_reg, PCH_PHUB_PHUB_ID_REG);
+ /* to store contents of QUEUE_PRI_VAL register */
+ PCH_WRITE_REG(g_pch_phub_reg.q_pri_val_reg, PCH_PHUB_QUEUE_PRI_VAL_REG);
+ /* to store contents of RC_QUEUE_MAXSIZE register */
+ PCH_WRITE_REG(g_pch_phub_reg.rc_q_maxsize_reg,
+ PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+ /* to store contents of BRI_QUEUE_MAXSIZE register */
+ PCH_WRITE_REG(g_pch_phub_reg.bri_q_maxsize_reg,
+ PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+ /* to store contents of COMP_RESP_TIMEOUT register */
+ PCH_WRITE_REG(g_pch_phub_reg.comp_resp_timeout_reg,
+ PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+ /* to store contents of BUS_SLAVE_CONTROL_REG register */
+ PCH_WRITE_REG(g_pch_phub_reg.bus_slave_control_reg,
+ PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+ /* to store contents of DEADLOCK_AVOID_TYPE register */
+ PCH_WRITE_REG(g_pch_phub_reg.deadlock_avoid_type_reg,
+ PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+ /* to store contents of INTPIN_REG_WPERMIT register 0 */
+ PCH_WRITE_REG(g_pch_phub_reg.intpin_reg_wpermit_reg0,
+ PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+ /* to store contents of INTPIN_REG_WPERMIT register 1 */
+ PCH_WRITE_REG(g_pch_phub_reg.intpin_reg_wpermit_reg1,
+ PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+ /* to store contents of INTPIN_REG_WPERMIT register 2 */
+ PCH_WRITE_REG(g_pch_phub_reg.intpin_reg_wpermit_reg2,
+ PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+ /* to store contents of INTPIN_REG_WPERMIT register 3 */
+ PCH_WRITE_REG(g_pch_phub_reg.intpin_reg_wpermit_reg3,
+ PCH_PHUB_INTPIN_REG_WPERMIT_REG3);
+ dev_dbg(dev_dbg, "pch_phub_save_reg_conf : "
+ "g_pch_phub_reg.phub_id_reg=%x, "
+ "g_pch_phub_reg.q_pri_val_reg=%x, "
+ "g_pch_phub_reg.rc_q_maxsize_reg=%x, "
+ "g_pch_phub_reg.bri_q_maxsize_reg=%x, "
+ "g_pch_phub_reg.comp_resp_timeout_reg=%x, "
+ "g_pch_phub_reg.bus_slave_control_reg=%x, "
+ "g_pch_phub_reg.deadlock_avoid_type_reg=%x, "
+ "g_pch_phub_reg.intpin_reg_wpermit_reg0=%x, "
+ "g_pch_phub_reg.intpin_reg_wpermit_reg1=%x, "
+ "g_pch_phub_reg.intpin_reg_wpermit_reg2=%x, "
+ "g_pch_phub_reg.intpin_reg_wpermit_reg3=%x\n",
+ g_pch_phub_reg.phub_id_reg,
+ g_pch_phub_reg.q_pri_val_reg,
+ g_pch_phub_reg.rc_q_maxsize_reg,
+ g_pch_phub_reg.bri_q_maxsize_reg,
+ g_pch_phub_reg.comp_resp_timeout_reg,
+ g_pch_phub_reg.bus_slave_control_reg,
+ g_pch_phub_reg.deadlock_avoid_type_reg,
+ g_pch_phub_reg.intpin_reg_wpermit_reg0,
+ g_pch_phub_reg.intpin_reg_wpermit_reg1,
+ g_pch_phub_reg.intpin_reg_wpermit_reg2,
+ g_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++) {
+ PCH_WRITE_REG(g_pch_phub_reg.int_reduce_control_reg[i],
+ PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i);
+ dev_dbg(dev_dbg, "pch_phub_save_reg_conf : "
+ "g_pch_phub_reg.int_reduce_control_reg[%d]=%x\n",
+ i, g_pch_phub_reg.int_reduce_control_reg[i]);
+ }
+
+#ifdef PCH_CAN_PCLK_50MHZ
+ /*restore the clock config reg */
+ PCH_WRITE_REG(g_pch_phub_reg.clkcfg_reg, CLKCFG_REG_OFFSET);
+#endif
+
+ 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
+ */
+int pch_phub_read_serial_rom(unsigned long offset_address, unsigned char *data)
+{
+ unsigned long mem_addr = pch_phub_extrom_base_address + offset_address;
+
+ dev_dbg(dev_dbg,
+ "pch_phub_read_serial_rom:mem_addr=0x%08x\n", (u32)mem_addr);
+ *data = ioread8((void __iomem *)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
+ */
+int pch_phub_write_serial_rom(unsigned long offset_address, unsigned char data)
+{
+ int retval = 0;
+ unsigned long mem_addr = pch_phub_extrom_base_address + offset_address;
+ int i = 0;
+ unsigned long word_data = 0;
+
+ dev_dbg(dev_dbg,
+ "pch_phub_write_serial_rom:mem_addr=0x%08x\n", (u32)mem_addr);
+ iowrite32(PCH_PHUB_ROM_WRITE_ENABLE, (void __iomem *)
+ (pch_phub_extrom_base_address + PHUB_CONTROL));
+
+ word_data = ioread32((void __iomem *)(mem_addr & 0xFFFFFFFC));
+ dev_dbg(dev_dbg, "word_data=0x%08x data=0x%02x\n",
+ (u32)word_data, data);
+ switch (mem_addr % 4) {
+ case 0:
+ word_data &= 0xFFFFFF00;
+ iowrite32((word_data | (unsigned long)data),
+ (void __iomem *)(mem_addr & 0xFFFFFFFC));
+ break;
+ case 1:
+ word_data &= 0xFFFF00FF;
+ iowrite32((word_data | ((unsigned long)data << 8)),
+ (void __iomem *)(mem_addr & 0xFFFFFFFC));
+ break;
+ case 2:
+ word_data &= 0xFF00FFFF;
+ iowrite32((word_data | ((unsigned long)data << 16)),
+ (void __iomem *)(mem_addr & 0xFFFFFFFC));
+ break;
+ case 3:
+ word_data &= 0x00FFFFFF;
+ iowrite32((word_data | ((unsigned long)data << 24)),
+ (void __iomem *)(mem_addr & 0xFFFFFFFC));
+ break;
+ }
+ while (0x00 !=
+ ioread8((void __iomem *)
+ (pch_phub_extrom_base_address + PHUB_STATUS))) {
+ msleep(1);
+ if (PHUB_TIMEOUT == i) {
+ retval = -EPERM;
+ break;
+ }
+ i++;
+ }
+
+ iowrite32(PCH_PHUB_ROM_WRITE_DISABLE, (void __iomem *)
+ (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
+ */
+int pch_phub_read_serial_rom_val(unsigned long offset_address,
+ unsigned char *data)
+{
+ int retval = 0;
+ unsigned long 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
+ */
+int pch_phub_write_serial_rom_val(unsigned long offset_address,
+ unsigned char data)
+{
+ int retval = 0;
+ unsigned long 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
+ */
+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
+ */
+int pch_phub_read_gbe_mac_addr(unsigned long 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
+ */
+int pch_phub_write_gbe_mac_addr(unsigned long 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_open - Implements the Initializing and opening of the Packet Hub
+ module.
+ * @inode: Contains the reference of the inode structure
+ * @file: Contains the reference of the file structure
+ */
+int pch_phub_open(struct inode *inode, struct file *file)
+{
+ int ret;
+
+ spin_lock(&pch_phub_lock);
+ dev_dbg(dev_dbg, "pch_phub_open : open count value = %d",
+ pch_phub_opencount);
+ if (pch_phub_opencount) {
+ dev_dbg(dev_dbg, "pch_phub_open : device already opened\n");
+ ret = -EBUSY;
+ } else {
+ pch_phub_opencount++;
+ ret = 0;
+ }
+ spin_unlock(&pch_phub_lock);
+
+ dev_dbg(dev_dbg, "pch_phub_open returns=%d\n", ret);
+ return ret;
+}
+
+/** pch_phub_release - Implements the release functionality of the Packet Hub
+ * module.
+ * @inode: Contains the reference of the inode structure
+ * @file: Contains the reference of the file structure
+ */
+int pch_phub_release(struct inode *inode, struct file *file)
+{
+ spin_lock(&pch_phub_lock);
+
+ if (pch_phub_opencount > 0)
+ pch_phub_opencount--;
+ spin_unlock(&pch_phub_lock);
+
+ dev_dbg(dev_dbg, "pch_phub_release : "
+ "pch_phub_opencount=%d returning=%d\n",
+ pch_phub_opencount, 0);
+ return 0;
+}
+
+/** 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
+ */
+#if 0
+int pch_phub_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret_value = 0;
+ struct pch_phub_reqt *p_pch_phub_reqt;
+ unsigned long addr_offset;
+ unsigned long data;
+ unsigned long mask;
+
+ do {
+ if (pch_phub_suspended == true) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "suspend initiated returning =%d\n",
+ -EPERM);
+ ret_value = -EPERM;
+ break;
+ }
+
+ p_pch_phub_reqt = (struct pch_phub_reqt *)arg;
+ ret_value =
+ copy_from_user((void *)&addr_offset,
+ (void *)&p_pch_phub_reqt->addr_offset,
+ sizeof(addr_offset));
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "copy_from_user fail returning =%d\n",
+ -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ }
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "copy_from_user returns =%d\n", ret_value);
+
+ /* Access area check */
+ switch (cmd) {
+ case IOCTL_PHUB_READ_REG:
+ case IOCTL_PHUB_WRITE_REG:
+ case IOCTL_PHUB_READ_MODIFY_WRITE_REG:
+ if (addr_offset >= 0x504)
+ ret_value = -EPERM;
+ break;
+ case IOCTL_PHUB_READ_OROM:
+ case IOCTL_PHUB_WRITE_OROM:
+ case IOCTL_PHUB_READ_MAC_ADDR:
+ case IOCTL_PHUB_WRITE_MAC_ADDR:
+ if (addr_offset >= 0x10000)
+ ret_value = -EPERM;
+ break;
+ }
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "access area is wrong returning =%d\n",
+ -EPERM);
+ break;
+ }
+ /* End of Access area check */
+
+ switch (cmd) {
+ case IOCTL_PHUB_READ_REG:
+ data = PCH_READ_REG(addr_offset);
+ dev_dbg(dev_dbg, "pch_phub_ioctl : Invoked "
+ "PCH_READ_REG successfully\n");
+
+ ret_value =
+ copy_to_user((void *)&p_pch_phub_reqt->data,
+ (void *)&data, sizeof(data));
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "copy_to_user fail returning =%d\n",
+ -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ }
+ break;
+ case IOCTL_PHUB_WRITE_REG:
+ ret_value = copy_from_user((void *)&data,
+ (void *)&p_pch_phub_reqt->data,
+ sizeof(data));
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "copy_from_user fail returning =%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ }
+ PCH_WRITE_REG(data, addr_offset);
+ dev_dbg(dev_dbg, "pch_phub_ioctl : Invoked "
+ "PCH_WRITE_REG successfully\n");
+ break;
+ case IOCTL_PHUB_READ_MODIFY_WRITE_REG:
+ ret_value = copy_from_user((void *)&data,
+ (void *)&p_pch_phub_reqt->
+ data, sizeof(data));
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "copy_from_user fail "
+ "returning =%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ }
+ ret_value = copy_from_user((void *)&mask,
+ (void *)&p_pch_phub_reqt->
+ mask, sizeof(mask));
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "copy_from_user fail "
+ "returning =%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ }
+ pch_phub_read_modify_write_reg(addr_offset, data, mask);
+ dev_dbg(dev_dbg, "pch_phub_ioctl : Invoked "
+ "pch_phub_read_modify_write_reg "
+ "successfully\n");
+ break;
+ case IOCTL_PHUB_READ_OROM:
+ ret_value = pch_phub_read_serial_rom(addr_offset,
+ (unsigned char *)&data);
+ if (ret_value) {
+ dev_dbg(dev_dbg,
+ "pch_phub_ioctl : Invoked "
+ "pch_phub_read_serial_rom "
+ "=%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ } else {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : Invoked "
+ "pch_phub_read_serial_rom "
+ "successfully\n");
+ }
+ ret_value = copy_to_user((void *)&p_pch_phub_reqt->
+ data, (void *)&data,
+ sizeof(data));
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "copy_to_user fail returning "
+ "=%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ }
+ break;
+ case IOCTL_PHUB_WRITE_OROM:
+ ret_value =
+ copy_from_user((void *)&data,
+ (void *)&p_pch_phub_reqt->
+ data, sizeof(data));
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "copy_from_user fail returning "
+ "=%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ }
+ ret_value =
+ pch_phub_write_serial_rom(addr_offset, data);
+ if (ret_value) {
+ dev_dbg(dev_dbg,
+ "pch_phub_ioctl : Invoked "
+ "pch_phub_write_serial_rom "
+ "=%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ } else {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : Invoked "
+ "pch_phub_write_serial_rom "
+ "successfully\n");
+ }
+ break;
+ case IOCTL_PHUB_READ_MAC_ADDR:
+ pch_phub_read_gbe_mac_addr(addr_offset,
+ (unsigned char *)&data);
+ dev_dbg(dev_dbg, "pch_phub_ioctl : Invoked "
+ "pch_phub_read_gbe_mac_addr successfully\n");
+
+ ret_value =
+ copy_to_user((void *)&p_pch_phub_reqt->data,
+ (void *)&data, sizeof(data));
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "copy_to_user fail "
+ "returning =%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ }
+ break;
+ case IOCTL_PHUB_WRITE_MAC_ADDR:
+ ret_value =
+ copy_from_user((void *)&data,
+ (void *)&p_pch_phub_reqt->data,
+ sizeof(data));
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "copy_from_user fail "
+ "returning =%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ }
+ pch_phub_write_gbe_mac_addr(addr_offset, data);
+ dev_dbg(dev_dbg, "pch_phub_ioctl : Invoked "
+ "pch_phub_write_gbe_mac_addr "
+ "successfully\n");
+ break;
+ default:
+ dev_dbg(dev_dbg, "pch_write_ioctl invalid "
+ "command returning=%d\n", -EINVAL);
+ ret_value = -EINVAL;
+ break;
+ }
+ break;
+
+ } while (0);
+
+
+ dev_dbg(dev_dbg, "pch_write_ioctl returns=%d\n", ret_value);
+ return ret_value;
+}
+#endif
+int pch_phub_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret_value = 0;
+ struct pch_phub_reqt *p_pch_phub_reqt;
+ unsigned long addr_offset;
+ unsigned long data;
+ unsigned long mask;
+
+ if (pch_phub_suspended == true) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "suspend initiated returning =%d\n", -EPERM);
+ ret_value = -EPERM;
+ goto return_ioctrl;
+ }
+
+ p_pch_phub_reqt = (struct pch_phub_reqt *)arg;
+ ret_value = copy_from_user((void *)&addr_offset,
+ (void *)&p_pch_phub_reqt->addr_offset,
+ sizeof(addr_offset));
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "copy_from_user fail returning =%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ goto return_ioctrl;
+ }
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "copy_from_user returns =%d\n", ret_value);
+
+ /* Access area check */
+ switch (cmd) {
+ case IOCTL_PHUB_READ_REG:
+ case IOCTL_PHUB_WRITE_REG:
+ case IOCTL_PHUB_READ_MODIFY_WRITE_REG:
+ if (addr_offset >= 0x504)
+ ret_value = -EPERM;
+ break;
+ case IOCTL_PHUB_READ_OROM:
+ case IOCTL_PHUB_WRITE_OROM:
+ case IOCTL_PHUB_READ_MAC_ADDR:
+ case IOCTL_PHUB_WRITE_MAC_ADDR:
+ if (addr_offset >= 0x10000)
+ ret_value = -EPERM;
+ break;
+ }
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "access area is wrong returning =%d\n", -EPERM);
+ goto return_ioctrl;
+ }
+ /* End of Access area check */
+
+ switch (cmd) {
+ case IOCTL_PHUB_READ_REG:
+ data = PCH_READ_REG((void __iomem *)addr_offset);
+ dev_dbg(dev_dbg, "pch_phub_ioctl : Invoked "
+ "PCH_READ_REG successfully\n");
+
+ ret_value = copy_to_user((void *)&p_pch_phub_reqt->data,
+ (void *)&data, sizeof(data));
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "copy_to_user fail returning =%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ }
+ break;
+ case IOCTL_PHUB_WRITE_REG:
+ ret_value = copy_from_user((void *)&data,
+ (void *)&p_pch_phub_reqt->data,
+ sizeof(data));
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "copy_from_user fail returning =%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ }
+ PCH_WRITE_REG(data, (void __iomem *)addr_offset);
+ dev_dbg(dev_dbg, "pch_phub_ioctl : Invoked "
+ "PCH_WRITE_REG successfully\n");
+ break;
+ case IOCTL_PHUB_READ_MODIFY_WRITE_REG:
+ ret_value = copy_from_user((void *)&data,
+ (void *)&p_pch_phub_reqt->data, sizeof(data));
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : copy_from_user fail "
+ "returning =%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ }
+ ret_value = copy_from_user((void *)&mask,
+ (void *)&p_pch_phub_reqt->mask, sizeof(mask));
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : copy_from_user fail "
+ "returning =%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ }
+ pch_phub_read_modify_write_reg(addr_offset,
+ data, mask);
+ dev_dbg(dev_dbg, "pch_phub_ioctl : Invoked "
+ "pch_phub_read_modify_write_reg successfully\n");
+ break;
+ case IOCTL_PHUB_READ_OROM:
+ ret_value = pch_phub_read_serial_rom(addr_offset,
+ (unsigned char *)&data);
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : Invoked "
+ "pch_phub_read_serial_rom =%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ } else {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : Invoked "
+ "pch_phub_read_serial_rom successfully\n");
+ }
+ ret_value = copy_to_user((void *)&p_pch_phub_reqt->data,
+ (void *)&data, sizeof(data));
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "copy_to_user fail returning =%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ }
+ break;
+ case IOCTL_PHUB_WRITE_OROM:
+ ret_value = copy_from_user((void *)&data,
+ (void *)&p_pch_phub_reqt->data, sizeof(data));
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : "
+ "copy_from_user fail returning =%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ }
+ ret_value = pch_phub_write_serial_rom(addr_offset, data);
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : Invoked "
+ "pch_phub_write_serial_rom =%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ } else {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : Invoked "
+ "pch_phub_write_serial_rom successfully\n");
+ }
+ break;
+ case IOCTL_PHUB_READ_MAC_ADDR:
+ pch_phub_read_gbe_mac_addr(addr_offset, (unsigned char *)&data);
+ dev_dbg(dev_dbg, "pch_phub_ioctl : Invoked "
+ "pch_phub_read_gbe_mac_addr successfully\n");
+
+ ret_value = copy_to_user((void *)&p_pch_phub_reqt->data,
+ (void *)&data, sizeof(data));
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : copy_to_user fail "
+ "returning =%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ }
+ break;
+ case IOCTL_PHUB_WRITE_MAC_ADDR:
+ ret_value = copy_from_user((void *)&data,
+ (void *)&p_pch_phub_reqt->data, sizeof(data));
+ if (ret_value) {
+ dev_dbg(dev_dbg, "pch_phub_ioctl : copy_from_user fail "
+ "returning =%d\n", -EFAULT);
+ ret_value = -EFAULT;
+ break;
+ }
+ pch_phub_write_gbe_mac_addr(addr_offset, data);
+ dev_dbg(dev_dbg, "pch_phub_ioctl : Invoked "
+ "pch_phub_write_gbe_mac_addr successfully\n");
+ break;
+ default:
+ dev_dbg(dev_dbg, "pch_write_ioctl invalid "
+ "command returning=%d\n", -EINVAL);
+ ret_value = -EINVAL;
+ break;
+ }
+return_ioctrl:
+ dev_dbg(dev_dbg, "pch_write_ioctl returns=%d\n", ret_value);
+ return ret_value;
+}
+
+/** pch_phub_pci_init - Implements the initialization functionality of
+ * the module.
+ */
+static int __init pch_phub_pci_init(void)
+{
+ s32 ret;
+ ret = pci_register_driver(&pch_phub_driver);
+ dev_dbg(dev_dbg, "pch_phub_pci_init : "
+ "Invoked pci_register_driver successfully "
+ "returns = %d\n", ret);
+ 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);
+ dev_dbg(dev_dbg, "pch_phub_pci_exit : "
+ "Invoked pci_unregister_driver successfully\n");
+}
+
+/** 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;
+
+ dev_dbg = &pdev->dev;
+
+ 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(dev_dbg, "\npch_phub_probe : pci_enable_device FAILED");
+ goto err_probe;
+ }
+ dev_dbg(dev_dbg, "pch_phub_probe : "
+ "pci_enable_device returns %d\n", ret);
+
+ ret = pci_request_regions(pdev, DRIVER_NAME);
+ if (ret) {
+ dev_dbg(dev_dbg, "pch_phub_probe : pci_request_regions FAILED");
+ pci_disable_device(pdev);
+ goto err_probe;
+ }
+ dev_dbg(dev_dbg, "pch_phub_probe : "
+ "pci_request_regions returns %d\n", ret);
+
+ pch_phub_base_address = (unsigned long)pci_iomap(pdev, 1, 0);
+
+ if (pch_phub_base_address == 0) {
+ dev_dbg(dev_dbg, "pch_phub_probe : pci_iomap FAILED");
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ ret = -ENOMEM;
+ goto err_probe;
+ }
+ dev_dbg(dev_dbg, "pch_phub_probe : pci_iomap SUCCESS and value "
+ "in pch_phub_base_address variable is 0x%08x\n",
+ pch_phub_base_address);
+
+ pch_phub_extrom_base_address =
+ (unsigned long)pci_map_rom(pdev, &rom_size);
+ if (pch_phub_extrom_base_address == 0) {
+ dev_dbg(dev_dbg, "pch_phub_probe : pci_map_rom FAILED");
+ pci_iounmap(pdev, (void *)pch_phub_base_address);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ ret = -ENOMEM;
+ goto err_probe;
+ }
+ dev_dbg(dev_dbg, "pch_phub_probe : "
+ "pci_map_rom SUCCESS and value in "
+ "pch_phub_extrom_base_address variable is 0x%08x\n",
+ 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(dev_dbg, "pch_phub_probe : "
+ "register_chrdev_region FAILED");
+ pci_unmap_rom(pdev,
+ (void *)pch_phub_extrom_base_address);
+ pci_iounmap(pdev, (void *)pch_phub_base_address);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ goto err_probe;
+ }
+ dev_dbg(dev_dbg, "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(dev_dbg, "pch_phub_probe : "
+ "alloc_chrdev_region FAILED");
+ pci_unmap_rom(pdev,
+ (void *)pch_phub_extrom_base_address);
+ pci_iounmap(pdev,
+ (void *)pch_phub_base_address);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ goto err_probe;
+ }
+ dev_dbg(dev_dbg, "pch_phub_probe : "
+ "alloc_chrdev_region returns %d\n", ret);
+ }
+
+ cdev_init(&pch_phub_dev, &pch_phub_fops);
+ dev_dbg(dev_dbg, "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(dev_dbg, "pch_phub_probe : cdev_add FAILED");
+ unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
+ pci_unmap_rom(pdev, (void *)pch_phub_extrom_base_address);
+ pci_iounmap(pdev, (void *)pch_phub_base_address);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ goto err_probe;
+ }
+ dev_dbg(dev_dbg, "pch_phub_probe : cdev_add returns %d\n", ret);
+
+#ifdef PCH_CAN_PCLK_50MHZ
+ /*set the clock config reg if CAN clock is 50Mhz */
+ dev_dbg(dev_dbg, "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 long)CLKCFG_REG_OFFSET,
+ CLKCFG_CAN_50MHZ, CLKCFG_CANCLK_MASK);
+#endif
+ /* set the prefech value */
+ PCH_WRITE_REG(0x000ffffa, (void __iomem *)0x14);
+ /* set the interrupt delay value */
+ PCH_WRITE_REG(0x25, (void __iomem *)0x44);
+ return 0;
+
+err_probe:
+ dev_dbg(dev_dbg, "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(dev_dbg, "pch_phub_remove - cdev_del Invoked successfully\n");
+
+ unregister_chrdev_region(pch_phub_dev_no, PCH_MINOR_NOS);
+ dev_dbg(dev_dbg, "pch_phub_remove - "
+ "unregister_chrdev_region Invoked successfully\n");
+
+ pci_unmap_rom(pdev, (void *)pch_phub_extrom_base_address);
+
+ pci_iounmap(pdev, (void *)pch_phub_base_address);
+
+ dev_dbg(dev_dbg, "pch_phub_remove - "
+ "pci_iounmap Invoked successfully\n");
+
+ pci_release_regions(pdev);
+ dev_dbg(dev_dbg, "pch_phub_remove - "
+ "pci_release_regions Invoked successfully\n");
+
+ pci_disable_device(pdev);
+ dev_dbg(dev_dbg, "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_suspended = true; /* For blocking further IOCTLs */
+
+ pch_phub_save_reg_conf();
+ dev_dbg(dev_dbg, "pch_phub_suspend - "
+ "pch_phub_save_reg_conf Invoked successfully\n");
+
+ ret = pci_save_state(pdev);
+ if (ret) {
+ dev_dbg(dev_dbg,
+ " pch_phub_suspend -pci_save_state returns-%d\n", ret);
+ return ret;
+ }
+ dev_dbg(dev_dbg, "pch_phub_suspend - pci_save_state returns %d\n", ret);
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ dev_dbg(dev_dbg, "pch_phub_suspend - "
+ "pci_enable_wake Invoked successfully\n");
+
+ pci_disable_device(pdev);
+ dev_dbg(dev_dbg, "pch_phub_suspend - "
+ "pci_disable_device Invoked successfully\n");
+
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+ dev_dbg(dev_dbg, "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(dev_dbg, "pch_phub_resume - "
+ "pci_set_power_state Invoked successfully\n");
+
+ pci_restore_state(pdev);
+ dev_dbg(dev_dbg, "pch_phub_resume - "
+ "pci_restore_state Invoked successfully\n");
+
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ dev_dbg(dev_dbg, "pch_phub_resume-pci_enable_device failed ");
+ return ret;
+ }
+
+ dev_dbg(dev_dbg, "pch_phub_resume - "
+ "pci_enable_device returns -%d\n", ret);
+
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ dev_dbg(dev_dbg, "pch_phub_resume - "
+ "pci_enable_wake Invoked successfully\n");
+
+ pch_phub_restore_reg_conf();
+ dev_dbg(dev_dbg, "pch_phub_resume - "
+ "pch_phub_restore_reg_conf Invoked successfully\n");
+
+ pch_phub_suspended = false;
+
+ dev_dbg(dev_dbg, "pch_phub_resume returns- %d\n", 0);
+ return 0;
+}
+#endif /* CONFIG_PM */
diff --git a/drivers/char/pch_phub/pch_phub.h b/drivers/char/pch_phub/pch_phub.h
new file mode 100755
index 0000000..62ed819
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.h
@@ -0,0 +1,96 @@
+#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 register function signature. */
+#define IOCTL_PHUB_READ_REG (_IOW(PHUB_IOCTL_MAGIC, 1, unsigned long))
+
+/*Outlines the write register function signature. */
+#define IOCTL_PHUB_WRITE_REG (_IOW(PHUB_IOCTL_MAGIC, 2, unsigned long))
+
+/*Outlines the read, modify and write register function signature. */
+#define IOCTL_PHUB_READ_MODIFY_WRITE_REG (_IOW(PHUB_IOCTL_MAGIC, 3,\
+ unsigned long))
+
+/*Outlines the read option rom function signature. */
+#define IOCTL_PHUB_READ_OROM (_IOW(PHUB_IOCTL_MAGIC, 4, unsigned long))
+
+/*Outlines the write option rom function signature. */
+#define IOCTL_PHUB_WRITE_OROM (_IOW(PHUB_IOCTL_MAGIC, 5, unsigned long))
+
+/*Outlines the read mac address function signature. */
+#define IOCTL_PHUB_READ_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 6, unsigned long))
+
+/*brief Outlines the write mac address function signature. */
+#define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 7, unsigned long))
+
+
+/* Registers address offset */
+#define PCH_PHUB_PHUB_ID_REG (void __iomem *)(0x0000)
+#define PCH_PHUB_QUEUE_PRI_VAL_REG (void __iomem *)(0x0004)
+#define PCH_PHUB_RC_QUEUE_MAXSIZE_REG (void __iomem *)(0x0008)
+#define PCH_PHUB_BRI_QUEUE_MAXSIZE_REG (void __iomem *)(0x000C)
+#define PCH_PHUB_COMP_RESP_TIMEOUT_REG (void __iomem *)(0x0010)
+#define PCH_PHUB_BUS_SLAVE_CONTROL_REG (void __iomem *)(0x0014)
+#define PCH_PHUB_DEADLOCK_AVOID_TYPE_REG (void __iomem *)(0x0018)
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG0 (void __iomem *)(0x0020)
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG1 (void __iomem *)(0x0024)
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG2 (void __iomem *)(0x0028)
+#define PCH_PHUB_INTPIN_REG_WPERMIT_REG3 (void __iomem *)(0x002C)
+#define PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE (void __iomem *)(0x0040)
+#define CLKCFG_REG_OFFSET (void __iomem *)(0x500)
+
+/*structures*/
+/*It is a structure used for perserving information related to the
+ * Packet Hub request.
+ */
+struct pch_phub_reqt {
+ unsigned long addr_offset; /*specifies the register address
+ offset */
+ unsigned long data; /*specifies the data */
+ unsigned long mask; /*specifies the mask */
+};
+
+/* exported function prototypes */
+int pch_phub_open(struct inode *inode, struct file *file);
+int pch_phub_release(struct inode *inode, struct file *file);
+int pch_phub_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg);
+
+/**global variables*/
+extern u32 pch_phub_base_address; /* base address */
+extern s32 pch_phub_suspended; /* suspend status */
+
+extern s32 pch_phub_opencount;
+extern spinlock_t pch_phub_lock;
+extern const struct file_operations pch_phub_fops;
+#endif
--
1.6.0.6


2010-06-07 14:56:54

by Alan

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

O> +/* 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)

Style: constants don't need brackets - might also look more Linux like
tabbed out a bit

> +#define PCH_READ_REG(a) ioread32((pch_phub_base_address + a))
> +
> +#define PCH_WRITE_REG(a, b) iowrite32(a, (pch_phub_base_address + b))

These on the other hand do - but not where they are now

iowrite32((a), pcb_phub_base_address + (b))

(so that if a or b are expressions they are evaluated first)


> +struct pch_phub_reg {
> + u32 phub_id_reg; /* PHUB_ID register val */
> + u32 q_pri_val_reg; /* QUEUE_PRI_VAL register val */
> + u32 rc_q_maxsize_reg; /* RC_QUEUE_MAXSIZE register val */
> + u32 bri_q_maxsize_reg; /* BRI_QUEUE_MAXSIZE register val */
> + u32 comp_resp_timeout_reg; /* COMP_RESP_TIMEOUT register val */
> + u32 bus_slave_control_reg; /* BUS_SLAVE_CONTROL_REG register val */
> + u32 deadlock_avoid_type_reg; /* DEADLOCK_AVOID_TYPE register val */
> + u32 intpin_reg_wpermit_reg0; /* INTPIN_REG_WPERMIT register 0 val */
> + u32 intpin_reg_wpermit_reg1; /* INTPIN_REG_WPERMIT register 1 val */
> + u32 intpin_reg_wpermit_reg2; /* INTPIN_REG_WPERMIT register 2 val */
> + u32 intpin_reg_wpermit_reg3; /* INTPIN_REG_WPERMIT register 3 val */
> + /* INT_REDUCE_CONTROL registers val */
> + u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG];
> +#ifdef PCH_CAN_PCLK_50MHZ
> + u32 clkcfg_reg; /* CLK CFG register val */
> +#endif
> +} g_pch_phub_reg;

Stylewise the kernel doesn't use the g_ convention that many Windows
people do, so lose the g_

> +s32 pch_phub_opencount; /* check whether opened or not */
> +u32 pch_phub_base_address;
> +u32 pch_phub_extrom_base_address;
> +s32 pch_phub_suspended;

Any reason these can't be in the struct ?
> +
> +struct device *dev_dbg;

Not a good name for a global variable as it will clash with others

> +DEFINE_SPINLOCK(pch_phub_lock); /* for spin lock */

Can this be static or in the struct ?

> +
> +/**
> + * file_operations structure initialization
> + */
> +const struct file_operations pch_phub_fops = {
> + .owner = THIS_MODULE,
> + .open = pch_phub_open,
> + .release = pch_phub_release,
> + .ioctl = pch_phub_ioctl,
> +};

static const ?

> +/*--------------------------------------------
> + exported function prototypes
> +--------------------------------------------*/

They start 'static' I don't think they are exportdd !

> +static int __devinit pch_phub_probe(struct pci_dev *pdev, const
> + struct pci_device_id *id);
> +static void __devexit pch_phub_remove(struct pci_dev *pdev);
> +static int pch_phub_suspend(struct pci_dev *pdev, pm_message_t state);
> +static int pch_phub_resume(struct pci_dev *pdev);

> +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
> +};
> +
> +static int __init pch_phub_pci_init(void);
> +static void __exit pch_phub_pci_exit(void);
> +
> +MODULE_DESCRIPTION("PCH PACKET HUB PCI Driver");
> +MODULE_LICENSE("GPL");
> +module_init(pch_phub_pci_init);
> +module_exit(pch_phub_pci_exit);
> +module_param(pch_phub_major_no, int, S_IRUSR | S_IWUSR);

If you migrated the module registration/PCI registration to the bottom of
the file you could avoid the forward declations and make the code easier
to follow ?

> +void pch_phub_read_modify_write_reg(unsigned long reg_addr_offset,
> + unsigned long data, unsigned long mask)
> +{
> + unsigned long reg_addr = pch_phub_base_address + reg_addr_offset;
> + iowrite32(((ioread32((void __iomem *)reg_addr) & ~mask)) | data,
> + (void __iomem *)reg_addr);

When you have that many casts in a line it's perhaps a hint you have the
types wrong initially. At the very least reg_addr should be void __iomem,
and probably pch_phub_base_address should be

It would probably make sense to pass a pointer to your struct pch_hub_reg
and use that to hold the base address. Then if you ever get a box with
two in some future design it won't be a disaster

> +void pch_phub_save_reg_conf(void)

Ditto

> +#ifdef PCH_CAN_PCLK_50MHZ
> + /* save clk cfg register */
> + g_pch_phub_reg.clkcfg_reg = PCH_READ_REG(CLKCFG_REG_OFFSET);
> +#endif

This makes it hard to build one kernel for everything

> + return;
> +}

> +void pch_phub_restore_reg_conf(void)
> +{
> + u32 i = 0;

Why = 0, if you do that here you may hide initialisation errors from
future changes ?

> +/** 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
> + */
> +int pch_phub_read_serial_rom(unsigned long offset_address, unsigned char *data)
> +{
> + unsigned long mem_addr = pch_phub_extrom_base_address + offset_address;
> +
> + dev_dbg(dev_dbg,
> + "pch_phub_read_serial_rom:mem_addr=0x%08x\n", (u32)mem_addr);
> + *data = ioread8((void __iomem *)mem_addr);

Same comments about casts



> +int pch_phub_ioctl(struct inode *inode, struct file *file,
> + unsigned int cmd, unsigned long arg)
> +{
> + int ret_value = 0;
> + struct pch_phub_reqt *p_pch_phub_reqt;
> + unsigned long addr_offset;

This will change size on 32/64bit boxes makign your copy a bit
problematic

> + p_pch_phub_reqt = (struct pch_phub_reqt *)arg;
> + ret_value = copy_from_user((void *)&addr_offset,
> + (void *)&p_pch_phub_reqt->addr_offset,
> + sizeof(addr_offset));
> + if (ret_value) {

> + /* End of Access area check */

Remember here ioctl isn't single threaded so you may want to double check
some of these

> +struct pch_phub_reqt {
> + unsigned long addr_offset; /*specifies the register address
> + offset */
> + unsigned long data; /*specifies the data */
> + unsigned long mask; /*specifies the mask */

If they may need to be long make them u64. That way 32 and 64bit systems
get the same ioctl structure and life is good.

> +};
> +
> +/* exported function prototypes */
> +int pch_phub_open(struct inode *inode, struct file *file);
> +int pch_phub_release(struct inode *inode, struct file *file);
> +int pch_phub_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
> + unsigned long arg);

You have various other functions that are not static - if they should be
then make them static

Alan

2010-06-08 00:19:30

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Arnd,

Thank you for your comments.
We will integrate your comments to our PHUB as soon as possible.

Ohtake..

----- Original Message -----
From: "Arnd Bergmann" <[email protected]>
To: "Masayuki Ohtake" <[email protected]>
Cc: "LKML" <[email protected]>; "Andrew" <[email protected]>; "Intel OTC"
<[email protected]>; "Wang, Qi" <[email protected]>; "Wang, Yong Y" <[email protected]>
Sent: Monday, June 07, 2010 10:37 PM
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver


> On Friday 04 June 2010, Masayuki Ohtake wrote:
> > From: Masayuki Ohtake <[email protected]>
> >
> > This patch adds the Packet Hub driver for Topcliff.
> > Patch created against 2.6.33.1
> >
> > Signed-off-by: Masayuki Ohtake <[email protected]>
>
> Most of the comments from my first review still apply to
> this version, please have another look at what I wrote
> back in april, see http://article.gmane.org/gmane.linux.network/158879
>
> Note also that patch submissions should be against the latest
> development kernel, in this case 2.6.35-rc2, not against stable
> kernel releases like 2.6.33.1.
>
> Arnd
>

2010-06-08 00:19:52

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Alan,

Thank you for your comments.
We will integrate your comments to our PHUB by today or tomorrow at the latest.

Ohtake.
----- Original Message -----
From: "Alan Cox" <[email protected]>
To: "Masayuki Ohtak" <[email protected]>
Cc: "LKML" <[email protected]>; "Andrew" <[email protected]>; "Intel OTC"
<[email protected]>; "Wang, Qi" <[email protected]>; "Wang, Yong Y" <[email protected]>
Sent: Tuesday, June 08, 2010 12:05 AM
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver


> O> +/* 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)
>
> Style: constants don't need brackets - might also look more Linux like
> tabbed out a bit
>
> > +#define PCH_READ_REG(a) ioread32((pch_phub_base_address + a))
> > +
> > +#define PCH_WRITE_REG(a, b) iowrite32(a, (pch_phub_base_address + b))
>
> These on the other hand do - but not where they are now
>
> iowrite32((a), pcb_phub_base_address + (b))
>
> (so that if a or b are expressions they are evaluated first)
>
>
> > +struct pch_phub_reg {
> > + u32 phub_id_reg; /* PHUB_ID register val */
> > + u32 q_pri_val_reg; /* QUEUE_PRI_VAL register val */
> > + u32 rc_q_maxsize_reg; /* RC_QUEUE_MAXSIZE register val */
> > + u32 bri_q_maxsize_reg; /* BRI_QUEUE_MAXSIZE register val */
> > + u32 comp_resp_timeout_reg; /* COMP_RESP_TIMEOUT register val */
> > + u32 bus_slave_control_reg; /* BUS_SLAVE_CONTROL_REG register val */
> > + u32 deadlock_avoid_type_reg; /* DEADLOCK_AVOID_TYPE register val */
> > + u32 intpin_reg_wpermit_reg0; /* INTPIN_REG_WPERMIT register 0 val */
> > + u32 intpin_reg_wpermit_reg1; /* INTPIN_REG_WPERMIT register 1 val */
> > + u32 intpin_reg_wpermit_reg2; /* INTPIN_REG_WPERMIT register 2 val */
> > + u32 intpin_reg_wpermit_reg3; /* INTPIN_REG_WPERMIT register 3 val */
> > + /* INT_REDUCE_CONTROL registers val */
> > + u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG];
> > +#ifdef PCH_CAN_PCLK_50MHZ
> > + u32 clkcfg_reg; /* CLK CFG register val */
> > +#endif
> > +} g_pch_phub_reg;
>
> Stylewise the kernel doesn't use the g_ convention that many Windows
> people do, so lose the g_
>
> > +s32 pch_phub_opencount; /* check whether opened or not */
> > +u32 pch_phub_base_address;
> > +u32 pch_phub_extrom_base_address;
> > +s32 pch_phub_suspended;
>
> Any reason these can't be in the struct ?
> > +
> > +struct device *dev_dbg;
>
> Not a good name for a global variable as it will clash with others
>
> > +DEFINE_SPINLOCK(pch_phub_lock); /* for spin lock */
>
> Can this be static or in the struct ?
>
> > +
> > +/**
> > + * file_operations structure initialization
> > + */
> > +const struct file_operations pch_phub_fops = {
> > + .owner = THIS_MODULE,
> > + .open = pch_phub_open,
> > + .release = pch_phub_release,
> > + .ioctl = pch_phub_ioctl,
> > +};
>
> static const ?
>
> > +/*--------------------------------------------
> > + exported function prototypes
> > +--------------------------------------------*/
>
> They start 'static' I don't think they are exportdd !
>
> > +static int __devinit pch_phub_probe(struct pci_dev *pdev, const
> > + struct pci_device_id *id);
> > +static void __devexit pch_phub_remove(struct pci_dev *pdev);
> > +static int pch_phub_suspend(struct pci_dev *pdev, pm_message_t state);
> > +static int pch_phub_resume(struct pci_dev *pdev);
>
> > +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
> > +};
> > +
> > +static int __init pch_phub_pci_init(void);
> > +static void __exit pch_phub_pci_exit(void);
> > +
> > +MODULE_DESCRIPTION("PCH PACKET HUB PCI Driver");
> > +MODULE_LICENSE("GPL");
> > +module_init(pch_phub_pci_init);
> > +module_exit(pch_phub_pci_exit);
> > +module_param(pch_phub_major_no, int, S_IRUSR | S_IWUSR);
>
> If you migrated the module registration/PCI registration to the bottom of
> the file you could avoid the forward declations and make the code easier
> to follow ?
>
> > +void pch_phub_read_modify_write_reg(unsigned long reg_addr_offset,
> > + unsigned long data, unsigned long mask)
> > +{
> > + unsigned long reg_addr = pch_phub_base_address + reg_addr_offset;
> > + iowrite32(((ioread32((void __iomem *)reg_addr) & ~mask)) | data,
> > + (void __iomem *)reg_addr);
>
> When you have that many casts in a line it's perhaps a hint you have the
> types wrong initially. At the very least reg_addr should be void __iomem,
> and probably pch_phub_base_address should be
>
> It would probably make sense to pass a pointer to your struct pch_hub_reg
> and use that to hold the base address. Then if you ever get a box with
> two in some future design it won't be a disaster
>
> > +void pch_phub_save_reg_conf(void)
>
> Ditto
>
> > +#ifdef PCH_CAN_PCLK_50MHZ
> > + /* save clk cfg register */
> > + g_pch_phub_reg.clkcfg_reg = PCH_READ_REG(CLKCFG_REG_OFFSET);
> > +#endif
>
> This makes it hard to build one kernel for everything
>
> > + return;
> > +}
>
> > +void pch_phub_restore_reg_conf(void)
> > +{
> > + u32 i = 0;
>
> Why = 0, if you do that here you may hide initialisation errors from
> future changes ?
>
> > +/** 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
> > + */
> > +int pch_phub_read_serial_rom(unsigned long offset_address, unsigned char *data)
> > +{
> > + unsigned long mem_addr = pch_phub_extrom_base_address + offset_address;
> > +
> > + dev_dbg(dev_dbg,
> > + "pch_phub_read_serial_rom:mem_addr=0x%08x\n", (u32)mem_addr);
> > + *data = ioread8((void __iomem *)mem_addr);
>
> Same comments about casts
>
>
>
> > +int pch_phub_ioctl(struct inode *inode, struct file *file,
> > + unsigned int cmd, unsigned long arg)
> > +{
> > + int ret_value = 0;
> > + struct pch_phub_reqt *p_pch_phub_reqt;
> > + unsigned long addr_offset;
>
> This will change size on 32/64bit boxes makign your copy a bit
> problematic
>
> > + p_pch_phub_reqt = (struct pch_phub_reqt *)arg;
> > + ret_value = copy_from_user((void *)&addr_offset,
> > + (void *)&p_pch_phub_reqt->addr_offset,
> > + sizeof(addr_offset));
> > + if (ret_value) {
>
> > + /* End of Access area check */
>
> Remember here ioctl isn't single threaded so you may want to double check
> some of these
>
> > +struct pch_phub_reqt {
> > + unsigned long addr_offset; /*specifies the register address
> > + offset */
> > + unsigned long data; /*specifies the data */
> > + unsigned long mask; /*specifies the mask */
>
> If they may need to be long make them u64. That way 32 and 64bit systems
> get the same ioctl structure and life is good.
>
> > +};
> > +
> > +/* exported function prototypes */
> > +int pch_phub_open(struct inode *inode, struct file *file);
> > +int pch_phub_release(struct inode *inode, struct file *file);
> > +int pch_phub_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
> > + unsigned long arg);
>
> You have various other functions that are not static - if they should be
> then make them static
>
> Alan
>

2010-06-08 05:01:04

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Alan

We are now updating our Phub driver.

I have a questions for your comment.

(1)
>> +#ifdef PCH_CAN_PCLK_50MHZ
>> + /* save clk cfg register */
>> + g_pch_phub_reg.clkcfg_reg = PCH_READ_REG(CLKCFG_REG_OFFSET);
>> +#endif
>
> This makes it hard to build one kernel for everything
We couldn't understand your intention.
Does the above mean we must not use "#ifdef PCH_CAN_PCLK_50MHZ" in source code ?


BTW, We show our modification policy the following email with inline.
Please confirm it.
If there is any mistake, please inform us.


Thanks.

Ohtake.


----- Original Message -----

From: "Alan Cox" <[email protected]>

To: "Masayuki Ohtak" <[email protected]>

Cc: "LKML" <[email protected]>; "Andrew" <[email protected]>; "Intel OTC" <[email protected]>; "Wang, Qi" <[email protected]>; "Wang, Yong Y" <[email protected]>

Sent: Tuesday, June 08, 2010 12:05 AM

Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver





>O> +/* 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)

>

> Style: constants don't need brackets - might also look more Linux like

> tabbed out a bit

The above brackets are deleted



>

>> +#define PCH_READ_REG(a) ioread32((pch_phub_base_address + a))

>> +

>> +#define PCH_WRITE_REG(a, b) iowrite32(a, (pch_phub_base_address + b))

>

> These on the other hand do - but not where they are now

>

> iowrite32((a), pcb_phub_base_address + (b))

>

> (so that if a or b are expressions they are evaluated first)

Modify like below.

#define PCH_READ_REG(a) ioread32(pch_phub_base_address + (a))

#define PCH_WRITE_REG(a, b) iowrite32(a, pch_phub_base_address + (b))



>

>

>> +struct pch_phub_reg {

>> + u32 phub_id_reg; /* PHUB_ID register val */

>> + u32 q_pri_val_reg; /* QUEUE_PRI_VAL register val */

>> + u32 rc_q_maxsize_reg; /* RC_QUEUE_MAXSIZE register val */

>> + u32 bri_q_maxsize_reg; /* BRI_QUEUE_MAXSIZE register val */

>> + u32 comp_resp_timeout_reg; /* COMP_RESP_TIMEOUT register val */

>> + u32 bus_slave_control_reg; /* BUS_SLAVE_CONTROL_REG register val */

>> + u32 deadlock_avoid_type_reg; /* DEADLOCK_AVOID_TYPE register val */

>> + u32 intpin_reg_wpermit_reg0; /* INTPIN_REG_WPERMIT register 0 val */

>> + u32 intpin_reg_wpermit_reg1; /* INTPIN_REG_WPERMIT register 1 val */

>> + u32 intpin_reg_wpermit_reg2; /* INTPIN_REG_WPERMIT register 2 val */

>> + u32 intpin_reg_wpermit_reg3; /* INTPIN_REG_WPERMIT register 3 val */

>> + /* INT_REDUCE_CONTROL registers val */

>> + u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG];

>> +#ifdef PCH_CAN_PCLK_50MHZ

>> + u32 clkcfg_reg; /* CLK CFG register val */

>> +#endif

>> +} g_pch_phub_reg;

>

> Stylewise the kernel doesn't use the g_ convention that many Windows

> people do, so lose the g_

Delete prefix 'g_'.





>

>> +s32 pch_phub_opencount; /* check whether opened or not */

>> +u32 pch_phub_base_address;

>> +u32 pch_phub_extrom_base_address;

>> +s32 pch_phub_suspended;

>

> Any reason these can't be in the struct ?

Move the above 4 variables into 'struct pch_phub_reg_t'.



>> +

>> +struct device *dev_dbg;

>

> Not a good name for a global variable as it will clash with others

Modify to phub_dev_dbg.



>

>> +DEFINE_SPINLOCK(pch_phub_lock); /* for spin lock */

>

> Can this be static or in the struct ?

Modify lile below.

static DEFINE_SPINLOCK(pch_phub_lock); /* for spin lock */





>

>> +

>> +/**

>> + * file_operations structure initialization

>> + */

>> +const struct file_operations pch_phub_fops = {

>> + .owner = THIS_MODULE,

>> + .open = pch_phub_open,

>> + .release = pch_phub_release,

>> + .ioctl = pch_phub_ioctl,

>> +};

>

> static const ?

>

Modify to 'static const'.





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

>> + exported function prototypes

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

>

> They start 'static' I don't think they are exportdd !

Modify comment to 'Internal function prototypes'



>

>> +static int __devinit pch_phub_probe(struct pci_dev *pdev, const

>> + struct pci_device_id *id);

>> +static void __devexit pch_phub_remove(struct pci_dev *pdev);

>> +static int pch_phub_suspend(struct pci_dev *pdev, pm_message_t state);

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

>

>> +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

>> +};

>> +

>> +static int __init pch_phub_pci_init(void);

>> +static void __exit pch_phub_pci_exit(void);

>> +

>> +MODULE_DESCRIPTION("PCH PACKET HUB PCI Driver");

>> +MODULE_LICENSE("GPL");

>> +module_init(pch_phub_pci_init);

>> +module_exit(pch_phub_pci_exit);

>> +module_param(pch_phub_major_no, int, S_IRUSR | S_IWUSR);

>

> If you migrated the module registration/PCI registration to the bottom of

> the file you could avoid the forward declations and make the code easier

> to follow ?

Move the PCI registration code to the bottom of the file.



>

>> +void pch_phub_read_modify_write_reg(unsigned long reg_addr_offset,

>> + unsigned long data, unsigned long mask)

>> +{

>> + unsigned long reg_addr = pch_phub_base_address + reg_addr_offset;

>> + iowrite32(((ioread32((void __iomem *)reg_addr) & ~mask)) | data,

>> + (void __iomem *)reg_addr);

>

> When you have that many casts in a line it's perhaps a hint you have the

> types wrong initially. At the very least reg_addr should be void __iomem,

> and probably pch_phub_base_address should be

>

> It would probably make sense to pass a pointer to your struct pch_hub_reg

> and use that to hold the base address. Then if you ever get a box with

> two in some future design it won't be a disaster

>

>> +void pch_phub_save_reg_conf(void)

>

> Ditto

reg_addr/pch_phub_base_address are defined as 'void __iomem',



>

>> +#ifdef PCH_CAN_PCLK_50MHZ

>> + /* save clk cfg register */

>> + g_pch_phub_reg.clkcfg_reg = PCH_READ_REG(CLKCFG_REG_OFFSET);

>> +#endif

>

> This makes it hard to build one kernel for everything

???



>

>> + return;

>> +}

>

>> +void pch_phub_restore_reg_conf(void)

>> +{

>> + u32 i = 0;

>

> Why = 0, if you do that here you may hide initialisation errors from

> future changes ?

Delete '=0'





>

>> +/** 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

>> + */

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

>> +{

>> + unsigned long mem_addr = pch_phub_extrom_base_address + offset_address;

>> +

>> + dev_dbg(dev_dbg,

>> + "pch_phub_read_serial_rom:mem_addr=0x%08x\n", (u32)mem_addr);

>> + *data = ioread8((void __iomem *)mem_addr);

>

> Same comments about casts

Please refer the above.(defined as 'void __iomem',)



>

>

>

>> +int pch_phub_ioctl(struct inode *inode, struct file *file,

>> + unsigned int cmd, unsigned long arg)

>> +{

>> + int ret_value = 0;

>> + struct pch_phub_reqt *p_pch_phub_reqt;

>> + unsigned long addr_offset;

>

> This will change size on 32/64bit boxes makign your copy a bit

> problematic

Modify like belwo,

u64 addr_offset;





>

>> + p_pch_phub_reqt = (struct pch_phub_reqt *)arg;

>> + ret_value = copy_from_user((void *)&addr_offset,

>> + (void *)&p_pch_phub_reqt->addr_offset,

>> + sizeof(addr_offset));

>> + if (ret_value) {

>

>> + /* End of Access area check */

>

> Remember here ioctl isn't single threaded so you may want to double check

> some of these

The above copy_from_user is Copy global variable to local variable.
Thus, We think this code has re-entrant.


>

>> +struct pch_phub_reqt {

>> + unsigned long addr_offset; /*specifies the register address

>> + offset */

>> + unsigned long data; /*specifies the data */

>> + unsigned long mask; /*specifies the mask */

>

> If they may need to be long make them u64. That way 32 and 64bit systems

> get the same ioctl structure and life is good.

Modify type of addr_offset to u64



>

>> +};

>> +

>> +/* exported function prototypes */

>> +int pch_phub_open(struct inode *inode, struct file *file);

>> +int pch_phub_release(struct inode *inode, struct file *file);

>> +int pch_phub_ioctl(struct inode *inode, struct file *file, unsigned int cmd,

>> + unsigned long arg);

>

> You have various other functions that are not static - if they should be

> then make them static

Add static description for all of internal functions.



2010-06-08 05:46:38

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Alan

> > Remember here ioctl isn't single threaded so you may want to double check
>
> > some of these
>
> The above copy_from_user is Copy global variable to local variable.
> Thus, We think this code has re-entrant.
The above is not true.
Our ioctl routine is just used under single pthread.

> Hi Andrew
Sorry, I have not checked my email.

Thanks,

----- Original Message -----
From: "Masayuki Ohtak" <[email protected]>
To: "Alan Cox" <[email protected]>
Cc: "LKML" <[email protected]>; "Andrew" <[email protected]>; "Intel OTC"
<[email protected]>; "Wang, Qi" <[email protected]>; "Wang, Yong Y" <[email protected]>; "Masayuki Ohtak"
<[email protected]>
Sent: Tuesday, June 08, 2010 2:00 PM
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver


> Hi Alan
>
> We are now updating our Phub driver.
>
> I have a questions for your comment.
>
> (1)
> >> +#ifdef PCH_CAN_PCLK_50MHZ
> >> + /* save clk cfg register */
> >> + g_pch_phub_reg.clkcfg_reg = PCH_READ_REG(CLKCFG_REG_OFFSET);
> >> +#endif
> >
> > This makes it hard to build one kernel for everything
> We couldn't understand your intention.
> Does the above mean we must not use "#ifdef PCH_CAN_PCLK_50MHZ" in source code ?
>
>
> BTW, We show our modification policy the following email with inline.
> Please confirm it.
> If there is any mistake, please inform us.
>
>
> Thanks.
>
> Ohtake.
>
>
> ----- Original Message -----
>
> From: "Alan Cox" <[email protected]>
>
> To: "Masayuki Ohtak" <[email protected]>
>
> Cc: "LKML" <[email protected]>; "Andrew" <[email protected]>; "Intel OTC"
<[email protected]>; "Wang, Qi" <[email protected]>; "Wang, Yong Y" <[email protected]>
>
> Sent: Tuesday, June 08, 2010 12:05 AM
>
> Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver
>
>
>
>
>
> >O> +/* 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)
>
> >
>
> > Style: constants don't need brackets - might also look more Linux like
>
> > tabbed out a bit
>
> The above brackets are deleted
>
>
>
> >
>
> >> +#define PCH_READ_REG(a) ioread32((pch_phub_base_address + a))
>
> >> +
>
> >> +#define PCH_WRITE_REG(a, b) iowrite32(a, (pch_phub_base_address + b))
>
> >
>
> > These on the other hand do - but not where they are now
>
> >
>
> > iowrite32((a), pcb_phub_base_address + (b))
>
> >
>
> > (so that if a or b are expressions they are evaluated first)
>
> Modify like below.
>
> #define PCH_READ_REG(a) ioread32(pch_phub_base_address + (a))
>
> #define PCH_WRITE_REG(a, b) iowrite32(a, pch_phub_base_address + (b))
>
>
>
> >
>
> >
>
> >> +struct pch_phub_reg {
>
> >> + u32 phub_id_reg; /* PHUB_ID register val */
>
> >> + u32 q_pri_val_reg; /* QUEUE_PRI_VAL register val */
>
> >> + u32 rc_q_maxsize_reg; /* RC_QUEUE_MAXSIZE register val */
>
> >> + u32 bri_q_maxsize_reg; /* BRI_QUEUE_MAXSIZE register val */
>
> >> + u32 comp_resp_timeout_reg; /* COMP_RESP_TIMEOUT register val */
>
> >> + u32 bus_slave_control_reg; /* BUS_SLAVE_CONTROL_REG register val */
>
> >> + u32 deadlock_avoid_type_reg; /* DEADLOCK_AVOID_TYPE register val */
>
> >> + u32 intpin_reg_wpermit_reg0; /* INTPIN_REG_WPERMIT register 0 val */
>
> >> + u32 intpin_reg_wpermit_reg1; /* INTPIN_REG_WPERMIT register 1 val */
>
> >> + u32 intpin_reg_wpermit_reg2; /* INTPIN_REG_WPERMIT register 2 val */
>
> >> + u32 intpin_reg_wpermit_reg3; /* INTPIN_REG_WPERMIT register 3 val */
>
> >> + /* INT_REDUCE_CONTROL registers val */
>
> >> + u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG];
>
> >> +#ifdef PCH_CAN_PCLK_50MHZ
>
> >> + u32 clkcfg_reg; /* CLK CFG register val */
>
> >> +#endif
>
> >> +} g_pch_phub_reg;
>
> >
>
> > Stylewise the kernel doesn't use the g_ convention that many Windows
>
> > people do, so lose the g_
>
> Delete prefix 'g_'.
>
>
>
>
>
> >
>
> >> +s32 pch_phub_opencount; /* check whether opened or not */
>
> >> +u32 pch_phub_base_address;
>
> >> +u32 pch_phub_extrom_base_address;
>
> >> +s32 pch_phub_suspended;
>
> >
>
> > Any reason these can't be in the struct ?
>
> Move the above 4 variables into 'struct pch_phub_reg_t'.
>
>
>
> >> +
>
> >> +struct device *dev_dbg;
>
> >
>
> > Not a good name for a global variable as it will clash with others
>
> Modify to phub_dev_dbg.
>
>
>
> >
>
> >> +DEFINE_SPINLOCK(pch_phub_lock); /* for spin lock */
>
> >
>
> > Can this be static or in the struct ?
>
> Modify lile below.
>
> static DEFINE_SPINLOCK(pch_phub_lock); /* for spin lock */
>
>
>
>
>
> >
>
> >> +
>
> >> +/**
>
> >> + * file_operations structure initialization
>
> >> + */
>
> >> +const struct file_operations pch_phub_fops = {
>
> >> + .owner = THIS_MODULE,
>
> >> + .open = pch_phub_open,
>
> >> + .release = pch_phub_release,
>
> >> + .ioctl = pch_phub_ioctl,
>
> >> +};
>
> >
>
> > static const ?
>
> >
>
> Modify to 'static const'.
>
>
>
>
>
> >> +/*--------------------------------------------
>
> >> + exported function prototypes
>
> >> +--------------------------------------------*/
>
> >
>
> > They start 'static' I don't think they are exportdd !
>
> Modify comment to 'Internal function prototypes'
>
>
>
> >
>
> >> +static int __devinit pch_phub_probe(struct pci_dev *pdev, const
>
> >> + struct pci_device_id *id);
>
> >> +static void __devexit pch_phub_remove(struct pci_dev *pdev);
>
> >> +static int pch_phub_suspend(struct pci_dev *pdev, pm_message_t state);
>
> >> +static int pch_phub_resume(struct pci_dev *pdev);
>
> >
>
> >> +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
>
> >> +};
>
> >> +
>
> >> +static int __init pch_phub_pci_init(void);
>
> >> +static void __exit pch_phub_pci_exit(void);
>
> >> +
>
> >> +MODULE_DESCRIPTION("PCH PACKET HUB PCI Driver");
>
> >> +MODULE_LICENSE("GPL");
>
> >> +module_init(pch_phub_pci_init);
>
> >> +module_exit(pch_phub_pci_exit);
>
> >> +module_param(pch_phub_major_no, int, S_IRUSR | S_IWUSR);
>
> >
>
> > If you migrated the module registration/PCI registration to the bottom of
>
> > the file you could avoid the forward declations and make the code easier
>
> > to follow ?
>
> Move the PCI registration code to the bottom of the file.
>
>
>
> >
>
> >> +void pch_phub_read_modify_write_reg(unsigned long reg_addr_offset,
>
> >> + unsigned long data, unsigned long mask)
>
> >> +{
>
> >> + unsigned long reg_addr = pch_phub_base_address + reg_addr_offset;
>
> >> + iowrite32(((ioread32((void __iomem *)reg_addr) & ~mask)) | data,
>
> >> + (void __iomem *)reg_addr);
>
> >
>
> > When you have that many casts in a line it's perhaps a hint you have the
>
> > types wrong initially. At the very least reg_addr should be void __iomem,
>
> > and probably pch_phub_base_address should be
>
> >
>
> > It would probably make sense to pass a pointer to your struct pch_hub_reg
>
> > and use that to hold the base address. Then if you ever get a box with
>
> > two in some future design it won't be a disaster
>
> >
>
> >> +void pch_phub_save_reg_conf(void)
>
> >
>
> > Ditto
>
> reg_addr/pch_phub_base_address are defined as 'void __iomem',
>
>
>
> >
>
> >> +#ifdef PCH_CAN_PCLK_50MHZ
>
> >> + /* save clk cfg register */
>
> >> + g_pch_phub_reg.clkcfg_reg = PCH_READ_REG(CLKCFG_REG_OFFSET);
>
> >> +#endif
>
> >
>
> > This makes it hard to build one kernel for everything
>
> ???
>
>
>
> >
>
> >> + return;
>
> >> +}
>
> >
>
> >> +void pch_phub_restore_reg_conf(void)
>
> >> +{
>
> >> + u32 i = 0;
>
> >
>
> > Why = 0, if you do that here you may hide initialisation errors from
>
> > future changes ?
>
> Delete '=0'
>
>
>
>
>
> >
>
> >> +/** 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
>
> >> + */
>
> >> +int pch_phub_read_serial_rom(unsigned long offset_address, unsigned char *data)
>
> >> +{
>
> >> + unsigned long mem_addr = pch_phub_extrom_base_address + offset_address;
>
> >> +
>
> >> + dev_dbg(dev_dbg,
>
> >> + "pch_phub_read_serial_rom:mem_addr=0x%08x\n", (u32)mem_addr);
>
> >> + *data = ioread8((void __iomem *)mem_addr);
>
> >
>
> > Same comments about casts
>
> Please refer the above.(defined as 'void __iomem',)
>
>
>
> >
>
> >
>
> >
>
> >> +int pch_phub_ioctl(struct inode *inode, struct file *file,
>
> >> + unsigned int cmd, unsigned long arg)
>
> >> +{
>
> >> + int ret_value = 0;
>
> >> + struct pch_phub_reqt *p_pch_phub_reqt;
>
> >> + unsigned long addr_offset;
>
> >
>
> > This will change size on 32/64bit boxes makign your copy a bit
>
> > problematic
>
> Modify like belwo,
>
> u64 addr_offset;
>
>
>
>
>
> >
>
> >> + p_pch_phub_reqt = (struct pch_phub_reqt *)arg;
>
> >> + ret_value = copy_from_user((void *)&addr_offset,
>
> >> + (void *)&p_pch_phub_reqt->addr_offset,
>
> >> + sizeof(addr_offset));
>
> >> + if (ret_value) {
>
> >
>
> >> + /* End of Access area check */
>
> >
>
> > Remember here ioctl isn't single threaded so you may want to double check
>
> > some of these
>
> The above copy_from_user is Copy global variable to local variable.
> Thus, We think this code has re-entrant.
>
>
> >
>
> >> +struct pch_phub_reqt {
>
> >> + unsigned long addr_offset; /*specifies the register address
>
> >> + offset */
>
> >> + unsigned long data; /*specifies the data */
>
> >> + unsigned long mask; /*specifies the mask */
>
> >
>
> > If they may need to be long make them u64. That way 32 and 64bit systems
>
> > get the same ioctl structure and life is good.
>
> Modify type of addr_offset to u64
>
>
>
> >
>
> >> +};
>
> >> +
>
> >> +/* exported function prototypes */
>
> >> +int pch_phub_open(struct inode *inode, struct file *file);
>
> >> +int pch_phub_release(struct inode *inode, struct file *file);
>
> >> +int pch_phub_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
>
> >> + unsigned long arg);
>
> >
>
> > You have various other functions that are not static - if they should be
>
> > then make them static
>
> Add static description for all of internal functions.
>
>
>
>

2010-06-08 07:34:14

by Yong Wang

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

On Tue, Jun 08, 2010 at 02:00:55PM +0900, Masayuki Ohtak wrote:
> Hi Alan
>
> We are now updating our Phub driver.
>
> I have a questions for your comment.
>
> (1)
> >> +#ifdef PCH_CAN_PCLK_50MHZ
> >> + /* save clk cfg register */
> >> + g_pch_phub_reg.clkcfg_reg = PCH_READ_REG(CLKCFG_REG_OFFSET);
> >> +#endif
> >
> > This makes it hard to build one kernel for everything
> We couldn't understand your intention.
> Does the above mean we must not use "#ifdef PCH_CAN_PCLK_50MHZ" in source code ?
>

I think what Alan means is that you will have to build two kernel images
for two possible configurations of how your hardware is going to be used
if you write code this way. One is the case when CAN clock runs at 50MHz
and you will build a kernel image with PCH_CAN_PCLK_50MHZ defined. The
other is when CAN clock runs at other speed and you need to build
another kernel image with PCH_CAN_PCLK_50MHZ undefined. It would be much
better if one kernel image could run on all configurations.

Why is 50MHz so special, btw? Don't you need to save and restore the
clock config register when CAN clock runs at other speed?

>
> >> +#define PCH_READ_REG(a) ioread32((pch_phub_base_address + a))
>
> >> +
>
> >> +#define PCH_WRITE_REG(a, b) iowrite32(a, (pch_phub_base_address + b))
>
> >
>
> > These on the other hand do - but not where they are now
>
> >
>
> > iowrite32((a), pcb_phub_base_address + (b))
>
> >
>
> > (so that if a or b are expressions they are evaluated first)
>
> Modify like below.
>
> #define PCH_READ_REG(a) ioread32(pch_phub_base_address + (a))
>
> #define PCH_WRITE_REG(a, b) iowrite32(a, pch_phub_base_address + (b))
>

Note that 'a' is supposed to be surrounded by brackets, too.

#define PCH_WRITE_REG(a, b) iowrite32((a), pcb_phub_base_address + (b))

Thanks
-Yong

2010-06-08 07:52:52

by Alan

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

On Tue, 8 Jun 2010 14:46:31 +0900
"Masayuki Ohtake" <[email protected]> wrote:

> Hi Alan
>
> > > Remember here ioctl isn't single threaded so you may want to double check
> >
> > > some of these
> >
> > The above copy_from_user is Copy global variable to local variable.
> > Thus, We think this code has re-entrant.
> The above is not true.
> Our ioctl routine is just used under single pthread.

What if someone else chooses to open it and run multiple threads through
it. What if the code changes. What about malicious attempts to confuse
the driver ?

The fact your own current userspace does not do this does not mean it can
be ignored.

Alan

2010-06-08 07:55:38

by Alan

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

> > This makes it hard to build one kernel for everything
> We couldn't understand your intention.
> Does the above mean we must not use "#ifdef PCH_CAN_PCLK_50MHZ" in source code ?

The problem for people building kernels is this

- They want to have a single kernel for all the boxes of a single
architecture
- They cannot do this if there are compile time settings that only work
on some boards


Ideally this should be done automatically. If not then can it be a module
parameter instead ?

Alan

2010-06-08 08:09:54

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Yong Wang

Thank you for your comment.

> Why is 50MHz so special, btw?
This code is work around for previous HW bug.
(CAN worked only 50MHz with previous HW)

Since the HW bug has been already fixed for the latest HW,
this code is unnecessary for the latest HW.

> Note that 'a' is supposed to be surrounded by brackets, too.
>
> #define PCH_WRITE_REG(a, b) iowrite32((a), pcb_phub_base_address + (b))
Thank you for your indication. We have updated.

Best Regards,
----- Original Message -----
From: "Yong Wang" <[email protected]>
To: "Masayuki Ohtak" <[email protected]>
Cc: "Alan Cox" <[email protected]>; "LKML" <[email protected]>; "Andrew"
<[email protected]>; "Intel OTC" <[email protected]>; "Wang, Qi" <[email protected]>; "Wang, Yong Y"
<[email protected]>
Sent: Tuesday, June 08, 2010 4:20 PM
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver


> On Tue, Jun 08, 2010 at 02:00:55PM +0900, Masayuki Ohtak wrote:
> > Hi Alan
> >
> > We are now updating our Phub driver.
> >
> > I have a questions for your comment.
> >
> > (1)
> > >> +#ifdef PCH_CAN_PCLK_50MHZ
> > >> + /* save clk cfg register */
> > >> + g_pch_phub_reg.clkcfg_reg = PCH_READ_REG(CLKCFG_REG_OFFSET);
> > >> +#endif
> > >
> > > This makes it hard to build one kernel for everything
> > We couldn't understand your intention.
> > Does the above mean we must not use "#ifdef PCH_CAN_PCLK_50MHZ" in source code ?
> >
>
> I think what Alan means is that you will have to build two kernel images
> for two possible configurations of how your hardware is going to be used
> if you write code this way. One is the case when CAN clock runs at 50MHz
> and you will build a kernel image with PCH_CAN_PCLK_50MHZ defined. The
> other is when CAN clock runs at other speed and you need to build
> another kernel image with PCH_CAN_PCLK_50MHZ undefined. It would be much
> better if one kernel image could run on all configurations.
>
> Why is 50MHz so special, btw? Don't you need to save and restore the
> clock config register when CAN clock runs at other speed?
>
> >
> > >> +#define PCH_READ_REG(a) ioread32((pch_phub_base_address + a))
> >
> > >> +
> >
> > >> +#define PCH_WRITE_REG(a, b) iowrite32(a, (pch_phub_base_address + b))
> >
> > >
> >
> > > These on the other hand do - but not where they are now
> >
> > >
> >
> > > iowrite32((a), pcb_phub_base_address + (b))
> >
> > >
> >
> > > (so that if a or b are expressions they are evaluated first)
> >
> > Modify like below.
> >
> > #define PCH_READ_REG(a) ioread32(pch_phub_base_address + (a))
> >
> > #define PCH_WRITE_REG(a, b) iowrite32(a, pch_phub_base_address + (b))
> >
>
> Note that 'a' is supposed to be surrounded by brackets, too.
>
> #define PCH_WRITE_REG(a, b) iowrite32((a), pcb_phub_base_address + (b))
>
> Thanks
> -Yong
>

2010-06-08 08:48:51

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Arnd

We are studying your indication.
> My feeling is that this ioctl interface is too
> low-level in general. You only export access to specific
> registers, not to functionality exposed by them.
> The best kernel interfaces are defined in a way that
> is independent of the underlying hardware and
> convert generic commands into device specific commands.

I have a question.
We don't know 'generic commands' concretely.
Let me know 'generic commands' in detail.

Thanks. Ohtake.

----- Original Message -----
From: "Arnd Bergmann" <[email protected]>
To: "Masayuki Ohtake" <[email protected]>
Cc: "LKML" <[email protected]>; "Andrew" <[email protected]>; "Intel OTC"
<[email protected]>; "Wang, Qi" <[email protected]>; "Wang, Yong Y" <[email protected]>
Sent: Monday, June 07, 2010 10:37 PM
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver


> On Friday 04 June 2010, Masayuki Ohtake wrote:
> > From: Masayuki Ohtake <[email protected]>
> >
> > This patch adds the Packet Hub driver for Topcliff.
> > Patch created against 2.6.33.1
> >
> > Signed-off-by: Masayuki Ohtake <[email protected]>
>
> Most of the comments from my first review still apply to
> this version, please have another look at what I wrote
> back in april, see http://article.gmane.org/gmane.linux.network/158879
>
> Note also that patch submissions should be against the latest
> development kernel, in this case 2.6.35-rc2, not against stable
> kernel releases like 2.6.33.1.
>
> Arnd
>

2010-06-08 09:29:56

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

On Tuesday 08 June 2010, Masayuki Ohtake wrote:
> We are studying your indication.
> > My feeling is that this ioctl interface is too
> > low-level in general. You only export access to specific
> > registers, not to functionality exposed by them.
> > The best kernel interfaces are defined in a way that
> > is independent of the underlying hardware and
> > convert generic commands into device specific commands.
>
> I have a question.
> We don't know 'generic commands' concretely.
> Let me know 'generic commands' in detail.

I have not seen the application using the driver, but
a better abstraction IMHO would be to take an abstraction
you have in your application and move it into the kernel.

I can be more specific if you tell me where to find the
source of the application. Generally speaking, you'd
transform a function like

/* the function that knows how to do 'this' */
int phub_do_this(int phub_fd, unsigned long arg)
{
struct pch_phub_req req = {
.addr_offset = SOME_ADDR_OFF,
};
ioctl(fd, IOCTL_PHUB_READ_REG, &req);

if (req.data & SOME_BITS)
return ERROR;

req.addr_offset = ANOTHER_ADDR_OFF;
req.data = arg | REALLY_DO_IT_BITMASK;

ioctl(fd, IOCTL_PHUB_WRITE_REG, &req);

return 0;
}

into another function that does the same thing but
without knowing anything about the registers:

/* the same function on the abstract interface */
int phub_do_this_new(int phub_fd, unsigned long arg)
{
return ioctl(phub_fd, IOCTL_PHUB_DO_THIS, &arg);
}

Arnd

2010-06-09 00:14:18

by Wang, Qi

[permalink] [raw]
Subject: RE: [PATCH] Topcliff PHUB: Generate PacketHub driver

Ohtake-san,

Let me explain what Arnd means.

Writing the all the registers with ioctl is discouraged. For this case, what you need is writing/updating or reading some registers, not all the registers of Topcliff. It's better that you implement as the following example.

For example, you want to update MAC register with 'aaaa'.
It's not a better way to pass the register of mac address and aaaa via the ioctl routine. It's better only pass the aaaa with the 'update mac address command' (update aaaa to mac register). Actually it's discouraged to pass the address information with ioctl routine, because it'll bring the security issue even if you check the address-range.

It's better to pass the command and relative argument to implement something, not 'the address', 'read/write register command' and 'the value to read/write'.

Any question, please let me know.

Best Regards,
Qi.

-----Original Message-----
From: Masayuki Ohtake [mailto:[email protected]]
Sent: Tuesday, June 08, 2010 4:49 PM
To: Arnd Bergmann
Cc: LKML; Khor, Andrew Chih Howe; Clark, Joel; Wang, Qi; Wang, Yong Y
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Arnd

We are studying your indication.
> My feeling is that this ioctl interface is too
> low-level in general. You only export access to specific
> registers, not to functionality exposed by them.
> The best kernel interfaces are defined in a way that
> is independent of the underlying hardware and
> convert generic commands into device specific commands.

I have a question.
We don't know 'generic commands' concretely.
Let me know 'generic commands' in detail.

Thanks. Ohtake.

----- Original Message -----
From: "Arnd Bergmann" <[email protected]>
To: "Masayuki Ohtake" <[email protected]>
Cc: "LKML" <[email protected]>; "Andrew" <[email protected]>; "Intel OTC"
<[email protected]>; "Wang, Qi" <[email protected]>; "Wang, Yong Y" <[email protected]>
Sent: Monday, June 07, 2010 10:37 PM
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver


> On Friday 04 June 2010, Masayuki Ohtake wrote:
> > From: Masayuki Ohtake <[email protected]>
> >
> > This patch adds the Packet Hub driver for Topcliff.
> > Patch created against 2.6.33.1
> >
> > Signed-off-by: Masayuki Ohtake <[email protected]>
>
> Most of the comments from my first review still apply to
> this version, please have another look at what I wrote
> back in april, see http://article.gmane.org/gmane.linux.network/158879
>
> Note also that patch submissions should be against the latest
> development kernel, in this case 2.6.35-rc2, not against stable
> kernel releases like 2.6.33.1.
>
> Arnd
>

2010-06-14 12:09:19

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi we have modified for your comments.
Please Confirm below.

Thanks.
Ohtake.


Signed-off-by: Masayuki Ohtake <[email protected]>
---
linux-2.6.33.1/drivers/char/Kconfig | 25 +
linux-2.6.33.1/drivers/char/Makefile | 4 +
linux-2.6.33.1/drivers/char/pch_phub/Makefile | 11 +
linux-2.6.33.1/drivers/char/pch_phub/pch_phub.c | 1000 +++++++++++++++++++++++
linux-2.6.33.1/drivers/char/pch_phub/pch_phub.h | 87 ++
5 files changed, 1127 insertions(+), 0 deletions(-)
create mode 100644 linux-2.6.33.1/drivers/char/pch_phub/Makefile
create mode 100755 linux-2.6.33.1/drivers/char/pch_phub/pch_phub.c
create mode 100755 linux-2.6.33.1/drivers/char/pch_phub/pch_phub.h

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index e023682..285ce78 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -4,6 +4,31 @@

menu "Character devices"

+config PCH_IEEE1588
+ tristate "PCH IEEE1588"
+ depends on PCI
+ help
+ If you say yes to this option, support will be included for the
+ PCH IEEE1588 Host controller.
+
+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 PCH_CAN_PCLK_50MHZ
+ bool "CAN PCLK 50MHz"
+ depends on PCH_PHUB
+ help
+ If you say yes to this option, clock is set to 50MHz.(For CAN control)
+
config VT
bool "Virtual terminal" if EMBEDDED
depends on !S390
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index f957edf..dc092d0 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -111,6 +111,10 @@ obj-$(CONFIG_PS3_FLASH) += ps3flash.o
obj-$(CONFIG_JS_RTC) += js-rtc.o
js-rtc-y = rtc.o

+obj-$(CONFIG_PCH_IEEE1588) += pch_ieee1588/
+
+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..bfe2a2b
--- /dev/null
+++ b/drivers/char/pch_phub/Makefile
@@ -0,0 +1,11 @@
+ifeq ($(CONFIG_PHUB_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+
+obj-$(CONFIG_PCH_PHUB) += pch_phub_drv.o
+#to set CAN clock to 50Mhz
+ifdef CONFIG_PCH_CAN_PCLK_50MHZ
+EXTRA_CFLAGS +=-DPCH_CAN_PCLK_50MHZ
+endif
+
+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..e9f5d4c
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.c
@@ -0,0 +1,1000 @@
+/*!
+ * @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"
+
+#define PCH_READ_REG(a) ioread32(pch_phub_reg.pch_phub_base_address + (a))
+
+#define PCH_WRITE_REG(a, b) iowrite32((a), pch_phub_reg.pch_phub_base_address +\
+ (b))
+/*--------------------------------------------
+ global variables
+--------------------------------------------*/
+/**
+ * struct pch_phub_reg_t - 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_opencount: check whether opened or not
+ * @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_t {
+ __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];
+#ifdef PCH_CAN_PCLK_50MHZ
+ __u32 clkcfg_reg;
+#endif
+ __s32 pch_phub_opencount;
+ void __iomem *pch_phub_base_address;
+ void __iomem *pch_phub_extrom_base_address;
+ __s32 pch_phub_suspended;
+} pch_phub_reg;
+
+DEFINE_MUTEX(pch_phub_ioctl_mutex);
+
+static DEFINE_SPINLOCK(pch_phub_lock); /* for spin lock */
+
+/* ToDo: major number allocation via module parameter */
+static dev_t pch_phub_dev_no;
+static __s32 pch_phub_major_no;
+static struct cdev pch_phub_dev;
+
+
+/*--------------------------------------------
+ internal function prototypes
+--------------------------------------------*/
+static __s32 __devinit pch_phub_probe(struct pci_dev *pdev, const
+ struct pci_device_id *id);
+static void __devexit pch_phub_remove(struct pci_dev *pdev);
+static __s32 pch_phub_suspend(struct pci_dev *pdev, pm_message_t state);
+static __s32 pch_phub_resume(struct pci_dev *pdev);
+static __s32 pch_phub_open(struct inode *inode, struct file *file);
+static __s32 pch_phub_release(struct inode *inode, struct file *file);
+static long pch_phub_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+static ssize_t pch_phub_read(struct file *, char __user *, size_t, loff_t *);
+static ssize_t pch_phub_write(struct file *, const char __user *,
+ size_t, loff_t *);
+
+/**
+ * file_operations structure initialization
+ */
+static const struct file_operations pch_phub_fops = {
+ .owner = THIS_MODULE,
+ .open = pch_phub_open,
+ .read = pch_phub_read,
+ .write = pch_phub_write,
+ .release = pch_phub_release,
+ .unlocked_ioctl = pch_phub_ioctl,
+};
+
+/*--------------------------------------------
+ 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(__u32 reg_addr_offset,
+ __u32 data, __u32 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)
+{
+ __u32 i = 0;
+
+ dev_dbg(&pdev->dev, "pch_phub_save_reg_conf ENTRY\n");
+ /* to store contents of PHUB_ID register */
+ pch_phub_reg.phub_id_reg = PCH_READ_REG(PCH_PHUB_PHUB_ID_REG);
+ /* to store contents of QUEUE_PRI_VAL register */
+ pch_phub_reg.q_pri_val_reg = PCH_READ_REG(PCH_PHUB_QUEUE_PRI_VAL_REG);
+ /* to store contents of RC_QUEUE_MAXSIZE register */
+ pch_phub_reg.rc_q_maxsize_reg =
+ PCH_READ_REG(PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+ /* to store contents of BRI_QUEUE_MAXSIZE register */
+ pch_phub_reg.bri_q_maxsize_reg =
+ PCH_READ_REG(PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+ /* to store contents of COMP_RESP_TIMEOUT register */
+ pch_phub_reg.comp_resp_timeout_reg =
+ PCH_READ_REG(PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+ /* to store contents of BUS_SLAVE_CONTROL_REG register */
+ pch_phub_reg.bus_slave_control_reg =
+ PCH_READ_REG(PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+ /* to store contents of DEADLOCK_AVOID_TYPE register */
+ pch_phub_reg.deadlock_avoid_type_reg =
+ PCH_READ_REG(PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+ /* to store contents of INTPIN_REG_WPERMIT register 0 */
+ pch_phub_reg.intpin_reg_wpermit_reg0 =
+ PCH_READ_REG(PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+ /* to store contents of INTPIN_REG_WPERMIT register 1 */
+ pch_phub_reg.intpin_reg_wpermit_reg1 =
+ PCH_READ_REG(PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+ /* to store contents of INTPIN_REG_WPERMIT register 2 */
+ pch_phub_reg.intpin_reg_wpermit_reg2 =
+ PCH_READ_REG(PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+ /* to store contents of INTPIN_REG_WPERMIT register 3 */
+ pch_phub_reg.intpin_reg_wpermit_reg3 =
+ PCH_READ_REG(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] =
+ PCH_READ_REG(
+ 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]);
+ }
+#ifdef PCH_CAN_PCLK_50MHZ
+ /* save clk cfg register */
+ pch_phub_reg.clkcfg_reg = PCH_READ_REG(CLKCFG_REG_OFFSET);
+#endif
+ return;
+}
+
+/** pch_phub_restore_reg_conf - restore register configuration
+ */
+
+static void pch_phub_restore_reg_conf(struct pci_dev *pdev)
+{
+ __u32 i;
+
+ dev_dbg(&pdev->dev, "pch_phub_restore_reg_conf ENTRY\n");
+ /* to store contents of PHUB_ID register */
+ PCH_WRITE_REG(pch_phub_reg.phub_id_reg, PCH_PHUB_PHUB_ID_REG);
+ /* to store contents of QUEUE_PRI_VAL register */
+ PCH_WRITE_REG(pch_phub_reg.q_pri_val_reg, PCH_PHUB_QUEUE_PRI_VAL_REG);
+ /* to store contents of RC_QUEUE_MAXSIZE register */
+ PCH_WRITE_REG(pch_phub_reg.rc_q_maxsize_reg,
+ PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+ /* to store contents of BRI_QUEUE_MAXSIZE register */
+ PCH_WRITE_REG(pch_phub_reg.bri_q_maxsize_reg,
+ PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+ /* to store contents of COMP_RESP_TIMEOUT register */
+ PCH_WRITE_REG(pch_phub_reg.comp_resp_timeout_reg,
+ PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+ /* to store contents of BUS_SLAVE_CONTROL_REG register */
+ PCH_WRITE_REG(pch_phub_reg.bus_slave_control_reg,
+ PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+ /* to store contents of DEADLOCK_AVOID_TYPE register */
+ PCH_WRITE_REG(pch_phub_reg.deadlock_avoid_type_reg,
+ PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+ /* to store contents of INTPIN_REG_WPERMIT register 0 */
+ PCH_WRITE_REG(pch_phub_reg.intpin_reg_wpermit_reg0,
+ PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+ /* to store contents of INTPIN_REG_WPERMIT register 1 */
+ PCH_WRITE_REG(pch_phub_reg.intpin_reg_wpermit_reg1,
+ PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+ /* to store contents of INTPIN_REG_WPERMIT register 2 */
+ PCH_WRITE_REG(pch_phub_reg.intpin_reg_wpermit_reg2,
+ PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+ /* to store contents of INTPIN_REG_WPERMIT register 3 */
+ PCH_WRITE_REG(pch_phub_reg.intpin_reg_wpermit_reg3,
+ 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++) {
+ PCH_WRITE_REG(pch_phub_reg.int_reduce_control_reg[i],
+ 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]);
+ }
+
+#ifdef PCH_CAN_PCLK_50MHZ
+ /*restore the clock config reg */
+ PCH_WRITE_REG(pch_phub_reg.clkcfg_reg, CLKCFG_REG_OFFSET);
+#endif
+
+ 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 __s32 pch_phub_read_serial_rom(__u32 offset_address, __u8 *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 __s32 pch_phub_write_serial_rom(__u32 offset_address,
+ __u8 data)
+{
+ __s32 retval = 0;
+ void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +\
+ (offset_address & 0xFFFFFFFC);
+ __s32 i = 0;
+ __u32 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 | (__u32)data),
+ mem_addr);
+ break;
+ case 1:
+ word_data &= 0xFFFF00FF;
+ iowrite32((word_data | ((__u32)data << 8)),
+ mem_addr);
+ break;
+ case 2:
+ word_data &= 0xFF00FFFF;
+ iowrite32((word_data | ((__u32)data << 16)),
+ mem_addr);
+ break;
+ case 3:
+ word_data &= 0x00FFFFFF;
+ iowrite32((word_data | ((__u32)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 __s32 pch_phub_read_serial_rom_val(__u32 offset_address, __u8 *data)
+{
+ __s32 retval = 0;
+ __u32 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 __s32 pch_phub_write_serial_rom_val(__u32 offset_address,
+ __u8 data)
+{
+ __s32 retval = 0;
+ __u32 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 __s32 pch_phub_gbe_serial_rom_conf(void)
+{
+ __s32 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 __s32 pch_phub_read_gbe_mac_addr(__u32 offset_address, __u8 *data)
+{
+ __s32 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 __s32 pch_phub_write_gbe_mac_addr(__u32 offset_address,
+ __u8 data)
+{
+ __s32 retval = 0;
+
+ retval = pch_phub_gbe_serial_rom_conf();
+ retval |= pch_phub_write_serial_rom_val(offset_address, data);
+
+ return retval;
+}
+
+/** pch_phub_open - Implements the Initializing and opening of the Packet Hub
+ module.
+ * @inode: Contains the reference of the inode structure
+ * @file: Contains the reference of the file structure
+ */
+static __s32 pch_phub_open(struct inode *inode, struct file *file)
+{
+ __s32 ret;
+
+ spin_lock(&pch_phub_lock);
+ if (pch_phub_reg.pch_phub_opencount) {
+ ret = -EBUSY;
+ } else {
+ pch_phub_reg.pch_phub_opencount++;
+ ret = 0;
+ }
+ spin_unlock(&pch_phub_lock);
+
+ return ret;
+}
+/** 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
+ * @pos: Contains the reference of the file structure
+ */
+
+static ssize_t pch_phub_read(struct file *file, char __user *buf, size_t size,
+ loff_t *pos)
+{
+ __u32 rom_signature = 0;
+ __u8 rom_length;
+ __s32 ret_value;
+ __u32 tmp;
+ __u8 data;
+ __u32 addr_offset = 0;
+
+ /*Get Rom signature*/
+ pch_phub_read_serial_rom(0x80, (__u8 *)&rom_signature);
+ pch_phub_read_serial_rom(0x81, (__u8 *)&tmp);
+ rom_signature |= (tmp & 0xff) << 8;
+ if (rom_signature == 0xAA55) {
+ pch_phub_read_serial_rom(0x82, &rom_length);
+ if (size > (rom_length * 512)+1)
+ return -ENOMEM;
+
+ for (addr_offset = 0;
+ addr_offset <= ((__u32)rom_length * 512);
+ addr_offset++) {
+ pch_phub_read_serial_rom(0x80 + addr_offset, &data);
+ ret_value = copy_to_user((void *)&buf[addr_offset],
+ (void *)&data, 1);
+ if (ret_value)
+ return -EFAULT;
+ }
+ } else {
+ return -ENOEXEC;
+ }
+
+ return rom_length * 512 + 1;
+}
+
+/** 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
+ * @pos: 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 *pos)
+{
+ static __u8 data[PCH_PHUB_OROM_SIZE];
+ __s32 ret_value;
+ __u32 addr_offset = 0;
+
+ if (size > PCH_PHUB_OROM_SIZE)
+ size = PCH_PHUB_OROM_SIZE;
+
+ ret_value = copy_from_user(data, buf, size);
+ if (ret_value)
+ return -EFAULT;
+
+ for (addr_offset = 0; addr_offset < size; addr_offset++) {
+ ret_value = pch_phub_write_serial_rom(0x80 + addr_offset,
+ data[addr_offset]);
+ }
+
+ return size;
+}
+
+
+/** pch_phub_release - Implements the release functionality of the Packet Hub
+ * module.
+ * @inode: Contains the reference of the inode structure
+ * @file: Contains the reference of the file structure
+ */
+static __s32 pch_phub_release(struct inode *inode, struct file *file)
+{
+ spin_lock(&pch_phub_lock);
+
+ if (pch_phub_reg.pch_phub_opencount > 0)
+ pch_phub_reg.pch_phub_opencount--;
+ spin_unlock(&pch_phub_lock);
+
+ return 0;
+}
+
+/** 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)
+{
+ __s32 ret_value = 0;
+ struct pch_phub_reqt __user *p_pch_phub_reqt;
+ __u32 data;
+ __u32 mac_addr[2];
+ __s32 ret;
+ __u32 i;
+ void __user *varg = (void __user *)arg;
+
+ ret = mutex_trylock(&pch_phub_ioctl_mutex);
+ if (ret == 0)
+ goto return_ioctrl;/*Can't get mutex lock*/
+
+ if (pch_phub_reg.pch_phub_suspended == true) {
+ ret_value = -EPERM;
+ goto return_ioctrl;
+ }
+
+ p_pch_phub_reqt = (struct pch_phub_reqt *)varg;
+
+ if (ret_value)
+ goto return_ioctrl;
+
+ /* End of Access area check */
+
+
+ switch (cmd) {
+
+ case IOCTL_PHUB_READ_MAC_ADDR:
+ mac_addr[0] = 0;
+ mac_addr[1] = 0;
+ for (i = 0; i < 4; i++) {
+ pch_phub_read_gbe_mac_addr(i, (__u8 *)&data);
+ mac_addr[0] |= data << i*8;
+ }
+ for (; i < 6; i++) {
+ pch_phub_read_gbe_mac_addr(i, (__u8 *)&data);
+ mac_addr[1] |= data << (i-4)*8;
+ }
+
+ ret_value = copy_to_user((void *)&p_pch_phub_reqt->data,
+ (void *)mac_addr, sizeof(mac_addr));
+ if (ret_value) {
+ ret_value = -EFAULT;
+ break;
+ }
+ break;
+ case IOCTL_PHUB_WRITE_MAC_ADDR:
+ ret_value = get_user(mac_addr[0], &p_pch_phub_reqt->data[0]);
+ ret_value += get_user(mac_addr[1], &p_pch_phub_reqt->data[1]);
+ if (ret_value) {
+ ret_value = -EFAULT;
+ break;
+ }
+ for (i = 0; i < 4; i++) {
+ data = (mac_addr[0] & (0xff << 8*i))>>(8*i);
+ pch_phub_write_gbe_mac_addr(i, data);
+ }
+ for (; i < 6; i++) {
+ data = (mac_addr[1] & (0xff << 8*(i-4)))>>(8*(i-4));
+ pch_phub_write_gbe_mac_addr(i, data);
+ }
+ break;
+ default:
+ ret_value = -EINVAL;
+ break;
+ }
+return_ioctrl:
+ mutex_unlock(&pch_phub_ioctl_mutex);
+ return ret_value;
+}
+
+
+/** 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 __s32 __devinit pch_phub_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+
+ char *DRIVER_NAME = "pch_phub";
+ __s32 ret;
+ __u32 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",
+ (__u32)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",
+ (__u32)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);
+
+#ifdef PCH_CAN_PCLK_50MHZ
+ /*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((__u32)CLKCFG_REG_OFFSET,
+ CLKCFG_CAN_50MHZ, CLKCFG_CANCLK_MASK);
+#endif
+ /* set the prefech value */
+ PCH_WRITE_REG(0x000ffffa, 0x14);
+ /* set the interrupt delay value */
+ PCH_WRITE_REG(0x25, 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 __s32 pch_phub_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ __s32 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 __s32 pch_phub_resume(struct pci_dev *pdev)
+{
+
+ __s32 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 __s32 __init pch_phub_pci_init(void)
+{
+ __s32 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..4859e2f
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.h
@@ -0,0 +1,87 @@
+#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 register function signature. */
+#define IOCTL_PHUB_READ_REG (_IOW(PHUB_IOCTL_MAGIC, 1, __u32))
+
+/*Outlines the write register function signature. */
+#define IOCTL_PHUB_WRITE_REG (_IOW(PHUB_IOCTL_MAGIC, 2, __u32))
+
+/*Outlines the read, modify and write register function signature. */
+#define IOCTL_PHUB_READ_MODIFY_WRITE_REG (_IOW(PHUB_IOCTL_MAGIC, 3,\
+ __u32))
+
+/*Outlines the read option rom function signature. */
+#define IOCTL_PHUB_READ_OROM (_IOW(PHUB_IOCTL_MAGIC, 4, __u32))
+
+/*Outlines the write option rom function signature. */
+#define IOCTL_PHUB_WRITE_OROM (_IOW(PHUB_IOCTL_MAGIC, 5, __u32))
+
+/*Outlines the read mac address function signature. */
+#define IOCTL_PHUB_READ_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 6, __u32))
+
+/*brief Outlines the write mac address function signature. */
+#define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 7, __u32))
+
+
+/* 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 15361
+/**
+ * struct pch_phub_reqt - It is a structure used for perserving information
+ * related to the Packet Hub request.
+ * @addr_offset: specifies the register address offset
+ * @data: specifies the data
+ * @mask: specifies the mask
+ *
+ */
+struct pch_phub_reqt {
+ __u32 dev_id;
+ __u32 func_id;
+ __u32 data[2];
+};
+
+#endif
--
1.6.0.6

2010-06-14 12:50:59

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

On Monday 14 June 2010, Masayuki Ohtak wrote:
> Hi we have modified for your comments.
> Please Confirm below.

Looks much better. A few more comments about the new code:

> +#to set CAN clock to 50Mhz
> +ifdef CONFIG_PCH_CAN_PCLK_50MHZ
> +EXTRA_CFLAGS +=-DPCH_CAN_PCLK_50MHZ
> +endif

This should not be necessary. Just use CONFIG_PCH_CAN_PCLK_50MHZ directly
in the code instead of the extra PCH_CAN_PCLK_50MHZ macro.
> +
> +DEFINE_MUTEX(pch_phub_ioctl_mutex);

This should probable be 'static DEFINE_MUTEX', since the symbol does not
need to be visible in the entire kernel.


> +/*--------------------------------------------
> + internal function prototypes
> +--------------------------------------------*/
> +static __s32 __devinit pch_phub_probe(struct pci_dev *pdev, const
> + struct pci_device_id *id);
> +static void __devexit pch_phub_remove(struct pci_dev *pdev);
> +static __s32 pch_phub_suspend(struct pci_dev *pdev, pm_message_t state);
> +static __s32 pch_phub_resume(struct pci_dev *pdev);
> +static __s32 pch_phub_open(struct inode *inode, struct file *file);
> +static __s32 pch_phub_release(struct inode *inode, struct file *file);
> +static long pch_phub_ioctl(struct file *file, unsigned int cmd,
> + unsigned long arg);
> +static ssize_t pch_phub_read(struct file *, char __user *, size_t, loff_t *);
> +static ssize_t pch_phub_write(struct file *, const char __user *,
> + size_t, loff_t *);

My general recommendation would be to reorder all the function
definitions so that you don't need any of these forward declarations.
That is the order used in most parts of the kernel (so you start reading
at the bottom), and it makes it easier to understand the structure of
the code IMHO.

> +/** pch_phub_open - Implements the Initializing and opening of the Packet Hub
> + module.
> + * @inode: Contains the reference of the inode structure
> + * @file: Contains the reference of the file structure
> + */
> +static __s32 pch_phub_open(struct inode *inode, struct file *file)
> +{
> + __s32 ret;
> +
> + spin_lock(&pch_phub_lock);
> + if (pch_phub_reg.pch_phub_opencount) {
> + ret = -EBUSY;
> + } else {
> + pch_phub_reg.pch_phub_opencount++;
> + ret = 0;
> + }
> + spin_unlock(&pch_phub_lock);
> +
> + return ret;
> +}

As far as I can tell, there is no longer a reason to prevent multiple
openers. Besides, even if there is only a single user, you might still
have concurrency problems, so the lock does not help and you could remove
the open function entirely.

> +/** 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
> + * @pos: Contains the reference of the file structure
> + */
> +
> +static ssize_t pch_phub_read(struct file *file, char __user *buf, size_t size,
> + loff_t *pos)
> +{
> + __u32 rom_signature = 0;
> + __u8 rom_length;
> + __s32 ret_value;
> + __u32 tmp;
> + __u8 data;
> + __u32 addr_offset = 0;
> +
> + /*Get Rom signature*/
> + pch_phub_read_serial_rom(0x80, (__u8 *)&rom_signature);
> + pch_phub_read_serial_rom(0x81, (__u8 *)&tmp);
> + rom_signature |= (tmp & 0xff) << 8;
> + if (rom_signature == 0xAA55) {
> + pch_phub_read_serial_rom(0x82, &rom_length);
> + if (size > (rom_length * 512)+1)
> + return -ENOMEM;
> +
> + for (addr_offset = 0;
> + addr_offset <= ((__u32)rom_length * 512);
> + addr_offset++) {
> + pch_phub_read_serial_rom(0x80 + addr_offset, &data);
> + ret_value = copy_to_user((void *)&buf[addr_offset],
> + (void *)&data, 1);
> + if (ret_value)
> + return -EFAULT;
> + }
> + } else {
> + return -ENOEXEC;
> + }
> +
> + return rom_length * 512 + 1;
> +}

This function has multiple problems:

- If the size argument is less than rom_length*512, you access past the
user-provided buffer.
- When the user does an llseek or pread, the *pos argument is not zero,
so you should return data from the middle, but you still return data
from the beginning.
- You don't update the *pos argument with the new position, so you cannot
continue the read where the first call left.
- Instead of returning -ENOMEM, you should just the data you have (or
0 for end-of-file).
- ENOEXEC does not seem appropriate either: The user can just check
these buffer for the signature here, so you just as well return
whatever you find in the ROM.

> +
> +/** 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
> + * @pos: 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 *pos)
> +{
> + static __u8 data[PCH_PHUB_OROM_SIZE];
> + __s32 ret_value;
> + __u32 addr_offset = 0;
> +
> + if (size > PCH_PHUB_OROM_SIZE)
> + size = PCH_PHUB_OROM_SIZE;
> +
> + ret_value = copy_from_user(data, buf, size);
> + if (ret_value)
> + return -EFAULT;
> +
> + for (addr_offset = 0; addr_offset < size; addr_offset++) {
> + ret_value = pch_phub_write_serial_rom(0x80 + addr_offset,
> + data[addr_offset]);
> + }
> +
> + return size;
> +}

This has the same problems, plus a buffer overflow: You must never have an
array of multiple kilobytes on the stack (data[PCH_PHUB_OROM_SIZE]), because
the stack itself is only a few kilobytes in the kernel. Better use a loop
with copy_from_user like the read function does.

> +/** pch_phub_release - Implements the release functionality of the Packet Hub
> + * module.
> + * @inode: Contains the reference of the inode structure
> + * @file: Contains the reference of the file structure
> + */
> +static __s32 pch_phub_release(struct inode *inode, struct file *file)
> +{
> + spin_lock(&pch_phub_lock);
> +
> + if (pch_phub_reg.pch_phub_opencount > 0)
> + pch_phub_reg.pch_phub_opencount--;
> + spin_unlock(&pch_phub_lock);
> +
> + return 0;
> +}

When you remove the open function, this one can go away as well.

> +/** 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)
> +{
> + __s32 ret_value = 0;
> + struct pch_phub_reqt __user *p_pch_phub_reqt;
> + __u32 data;
> + __u32 mac_addr[2];
> + __s32 ret;
> + __u32 i;
> + void __user *varg = (void __user *)arg;
> +
> + ret = mutex_trylock(&pch_phub_ioctl_mutex);
> + if (ret == 0)
> + goto return_ioctrl;/*Can't get mutex lock*/

mutex_trylock is probably not what you want, it returns immediately
when there is another function in the kernel.
mutex_lock_interruptible seems more appropriate, it will block
until the mutex is free or the process gets sent a signal,
which you should handle by returning -ERESTARTSYS.

In either case, you must not jump to return_ioctrl here, because
that will try to release the mutex that you do not hold here,
causing a hang the next time you enter the function.

> + if (pch_phub_reg.pch_phub_suspended == true) {
> + ret_value = -EPERM;
> + goto return_ioctrl;
> + }
> +
> + p_pch_phub_reqt = (struct pch_phub_reqt *)varg;
> +
> + if (ret_value)
> + goto return_ioctrl;

is always zero here.

> + /* End of Access area check */
> +
> +
> + switch (cmd) {
> +
> + case IOCTL_PHUB_READ_MAC_ADDR:
> + mac_addr[0] = 0;
> + mac_addr[1] = 0;
> + for (i = 0; i < 4; i++) {
> + pch_phub_read_gbe_mac_addr(i, (__u8 *)&data);
> + mac_addr[0] |= data << i*8;
> + }
> + for (; i < 6; i++) {
> + pch_phub_read_gbe_mac_addr(i, (__u8 *)&data);
> + mac_addr[1] |= data << (i-4)*8;
> + }
> +
> + ret_value = copy_to_user((void *)&p_pch_phub_reqt->data,
> + (void *)mac_addr, sizeof(mac_addr));

p_pch_phub_reqt->data has multiple problems:

- You have the typecast to (void *), which is wrong and unneeded.
- The data structure has no point at all any more when you use only one
field.

Just make this

u8 mac_addr[6];
for (i = 0; i < 4; i++)
pch_phub_read_gbe_mac_addr(i, &mac_addr[i]);
ret_value = copy_to_user(varg, mac_addr, sizeof(mac_addr));

> +#define PHUB_IOCTL_MAGIC (0xf7)
> +
> +/*Outlines the read register function signature. */
> +#define IOCTL_PHUB_READ_REG (_IOW(PHUB_IOCTL_MAGIC, 1, __u32))
> +
> +/*Outlines the write register function signature. */
> +#define IOCTL_PHUB_WRITE_REG (_IOW(PHUB_IOCTL_MAGIC, 2, __u32))
> +
> +/*Outlines the read, modify and write register function signature. */
> +#define IOCTL_PHUB_READ_MODIFY_WRITE_REG (_IOW(PHUB_IOCTL_MAGIC, 3,\
> + __u32))
> +
> +/*Outlines the read option rom function signature. */
> +#define IOCTL_PHUB_READ_OROM (_IOW(PHUB_IOCTL_MAGIC, 4, __u32))
> +
> +/*Outlines the write option rom function signature. */
> +#define IOCTL_PHUB_WRITE_OROM (_IOW(PHUB_IOCTL_MAGIC, 5, __u32))

These should all get removed now.

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

IOCTL_PHUB_READ_MAC_ADDR needs _IOR instead of _IOW, and the type
is still wrong here. Your code currently has struct pch_phub_reqt
instead of __u32, and if you change the ioctl function as I explained
above, it should become

#define IOCTL_PHUB_READ_MAC_ADDR (_IOR(PHUB_IOCTL_MAGIC, 6, __u8[6]))
#define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 7, __u8[6]))

Arnd

2010-06-14 23:56:41

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Arnd

Thank you for your quick comments.
After modifying, we will resubmit the patch.

Thanks, Ohtake.

----- Original Message -----
From: "Arnd Bergmann" <[email protected]>
To: "Masayuki Ohtak" <[email protected]>
Cc: "Alan Cox" <[email protected]>; "LKML" <[email protected]>; "Andrew"
<[email protected]>; "Intel OTC" <[email protected]>; "Wang, Qi" <[email protected]>; "Wang, Yong Y"
<[email protected]>
Sent: Monday, June 14, 2010 9:50 PM
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver


> On Monday 14 June 2010, Masayuki Ohtak wrote:
> > Hi we have modified for your comments.
> > Please Confirm below.
>
> Looks much better. A few more comments about the new code:
>
> > +#to set CAN clock to 50Mhz
> > +ifdef CONFIG_PCH_CAN_PCLK_50MHZ
> > +EXTRA_CFLAGS +=-DPCH_CAN_PCLK_50MHZ
> > +endif
>
> This should not be necessary. Just use CONFIG_PCH_CAN_PCLK_50MHZ directly
> in the code instead of the extra PCH_CAN_PCLK_50MHZ macro.
> > +
> > +DEFINE_MUTEX(pch_phub_ioctl_mutex);
>
> This should probable be 'static DEFINE_MUTEX', since the symbol does not
> need to be visible in the entire kernel.
>
>
> > +/*--------------------------------------------
> > + internal function prototypes
> > +--------------------------------------------*/
> > +static __s32 __devinit pch_phub_probe(struct pci_dev *pdev, const
> > + struct pci_device_id *id);
> > +static void __devexit pch_phub_remove(struct pci_dev *pdev);
> > +static __s32 pch_phub_suspend(struct pci_dev *pdev, pm_message_t state);
> > +static __s32 pch_phub_resume(struct pci_dev *pdev);
> > +static __s32 pch_phub_open(struct inode *inode, struct file *file);
> > +static __s32 pch_phub_release(struct inode *inode, struct file *file);
> > +static long pch_phub_ioctl(struct file *file, unsigned int cmd,
> > + unsigned long arg);
> > +static ssize_t pch_phub_read(struct file *, char __user *, size_t, loff_t *);
> > +static ssize_t pch_phub_write(struct file *, const char __user *,
> > + size_t, loff_t *);
>
> My general recommendation would be to reorder all the function
> definitions so that you don't need any of these forward declarations.
> That is the order used in most parts of the kernel (so you start reading
> at the bottom), and it makes it easier to understand the structure of
> the code IMHO.
>
> > +/** pch_phub_open - Implements the Initializing and opening of the Packet Hub
> > + module.
> > + * @inode: Contains the reference of the inode structure
> > + * @file: Contains the reference of the file structure
> > + */
> > +static __s32 pch_phub_open(struct inode *inode, struct file *file)
> > +{
> > + __s32 ret;
> > +
> > + spin_lock(&pch_phub_lock);
> > + if (pch_phub_reg.pch_phub_opencount) {
> > + ret = -EBUSY;
> > + } else {
> > + pch_phub_reg.pch_phub_opencount++;
> > + ret = 0;
> > + }
> > + spin_unlock(&pch_phub_lock);
> > +
> > + return ret;
> > +}
>
> As far as I can tell, there is no longer a reason to prevent multiple
> openers. Besides, even if there is only a single user, you might still
> have concurrency problems, so the lock does not help and you could remove
> the open function entirely.
>
> > +/** 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
> > + * @pos: Contains the reference of the file structure
> > + */
> > +
> > +static ssize_t pch_phub_read(struct file *file, char __user *buf, size_t size,
> > + loff_t *pos)
> > +{
> > + __u32 rom_signature = 0;
> > + __u8 rom_length;
> > + __s32 ret_value;
> > + __u32 tmp;
> > + __u8 data;
> > + __u32 addr_offset = 0;
> > +
> > + /*Get Rom signature*/
> > + pch_phub_read_serial_rom(0x80, (__u8 *)&rom_signature);
> > + pch_phub_read_serial_rom(0x81, (__u8 *)&tmp);
> > + rom_signature |= (tmp & 0xff) << 8;
> > + if (rom_signature == 0xAA55) {
> > + pch_phub_read_serial_rom(0x82, &rom_length);
> > + if (size > (rom_length * 512)+1)
> > + return -ENOMEM;
> > +
> > + for (addr_offset = 0;
> > + addr_offset <= ((__u32)rom_length * 512);
> > + addr_offset++) {
> > + pch_phub_read_serial_rom(0x80 + addr_offset, &data);
> > + ret_value = copy_to_user((void *)&buf[addr_offset],
> > + (void *)&data, 1);
> > + if (ret_value)
> > + return -EFAULT;
> > + }
> > + } else {
> > + return -ENOEXEC;
> > + }
> > +
> > + return rom_length * 512 + 1;
> > +}
>
> This function has multiple problems:
>
> - If the size argument is less than rom_length*512, you access past the
> user-provided buffer.
> - When the user does an llseek or pread, the *pos argument is not zero,
> so you should return data from the middle, but you still return data
> from the beginning.
> - You don't update the *pos argument with the new position, so you cannot
> continue the read where the first call left.
> - Instead of returning -ENOMEM, you should just the data you have (or
> 0 for end-of-file).
> - ENOEXEC does not seem appropriate either: The user can just check
> these buffer for the signature here, so you just as well return
> whatever you find in the ROM.
>
> > +
> > +/** 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
> > + * @pos: 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 *pos)
> > +{
> > + static __u8 data[PCH_PHUB_OROM_SIZE];
> > + __s32 ret_value;
> > + __u32 addr_offset = 0;
> > +
> > + if (size > PCH_PHUB_OROM_SIZE)
> > + size = PCH_PHUB_OROM_SIZE;
> > +
> > + ret_value = copy_from_user(data, buf, size);
> > + if (ret_value)
> > + return -EFAULT;
> > +
> > + for (addr_offset = 0; addr_offset < size; addr_offset++) {
> > + ret_value = pch_phub_write_serial_rom(0x80 + addr_offset,
> > + data[addr_offset]);
> > + }
> > +
> > + return size;
> > +}
>
> This has the same problems, plus a buffer overflow: You must never have an
> array of multiple kilobytes on the stack (data[PCH_PHUB_OROM_SIZE]), because
> the stack itself is only a few kilobytes in the kernel. Better use a loop
> with copy_from_user like the read function does.
>
> > +/** pch_phub_release - Implements the release functionality of the Packet Hub
> > + * module.
> > + * @inode: Contains the reference of the inode structure
> > + * @file: Contains the reference of the file structure
> > + */
> > +static __s32 pch_phub_release(struct inode *inode, struct file *file)
> > +{
> > + spin_lock(&pch_phub_lock);
> > +
> > + if (pch_phub_reg.pch_phub_opencount > 0)
> > + pch_phub_reg.pch_phub_opencount--;
> > + spin_unlock(&pch_phub_lock);
> > +
> > + return 0;
> > +}
>
> When you remove the open function, this one can go away as well.
>
> > +/** 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)
> > +{
> > + __s32 ret_value = 0;
> > + struct pch_phub_reqt __user *p_pch_phub_reqt;
> > + __u32 data;
> > + __u32 mac_addr[2];
> > + __s32 ret;
> > + __u32 i;
> > + void __user *varg = (void __user *)arg;
> > +
> > + ret = mutex_trylock(&pch_phub_ioctl_mutex);
> > + if (ret == 0)
> > + goto return_ioctrl;/*Can't get mutex lock*/
>
> mutex_trylock is probably not what you want, it returns immediately
> when there is another function in the kernel.
> mutex_lock_interruptible seems more appropriate, it will block
> until the mutex is free or the process gets sent a signal,
> which you should handle by returning -ERESTARTSYS.
>
> In either case, you must not jump to return_ioctrl here, because
> that will try to release the mutex that you do not hold here,
> causing a hang the next time you enter the function.
>
> > + if (pch_phub_reg.pch_phub_suspended == true) {
> > + ret_value = -EPERM;
> > + goto return_ioctrl;
> > + }
> > +
> > + p_pch_phub_reqt = (struct pch_phub_reqt *)varg;
> > +
> > + if (ret_value)
> > + goto return_ioctrl;
>
> is always zero here.
>
> > + /* End of Access area check */
> > +
> > +
> > + switch (cmd) {
> > +
> > + case IOCTL_PHUB_READ_MAC_ADDR:
> > + mac_addr[0] = 0;
> > + mac_addr[1] = 0;
> > + for (i = 0; i < 4; i++) {
> > + pch_phub_read_gbe_mac_addr(i, (__u8 *)&data);
> > + mac_addr[0] |= data << i*8;
> > + }
> > + for (; i < 6; i++) {
> > + pch_phub_read_gbe_mac_addr(i, (__u8 *)&data);
> > + mac_addr[1] |= data << (i-4)*8;
> > + }
> > +
> > + ret_value = copy_to_user((void *)&p_pch_phub_reqt->data,
> > + (void *)mac_addr, sizeof(mac_addr));
>
> p_pch_phub_reqt->data has multiple problems:
>
> - You have the typecast to (void *), which is wrong and unneeded.
> - The data structure has no point at all any more when you use only one
> field.
>
> Just make this
>
> u8 mac_addr[6];
> for (i = 0; i < 4; i++)
> pch_phub_read_gbe_mac_addr(i, &mac_addr[i]);
> ret_value = copy_to_user(varg, mac_addr, sizeof(mac_addr));
>
> > +#define PHUB_IOCTL_MAGIC (0xf7)
> > +
> > +/*Outlines the read register function signature. */
> > +#define IOCTL_PHUB_READ_REG (_IOW(PHUB_IOCTL_MAGIC, 1, __u32))
> > +
> > +/*Outlines the write register function signature. */
> > +#define IOCTL_PHUB_WRITE_REG (_IOW(PHUB_IOCTL_MAGIC, 2, __u32))
> > +
> > +/*Outlines the read, modify and write register function signature. */
> > +#define IOCTL_PHUB_READ_MODIFY_WRITE_REG (_IOW(PHUB_IOCTL_MAGIC, 3,\
> > + __u32))
> > +
> > +/*Outlines the read option rom function signature. */
> > +#define IOCTL_PHUB_READ_OROM (_IOW(PHUB_IOCTL_MAGIC, 4, __u32))
> > +
> > +/*Outlines the write option rom function signature. */
> > +#define IOCTL_PHUB_WRITE_OROM (_IOW(PHUB_IOCTL_MAGIC, 5, __u32))
>
> These should all get removed now.
>
> > +/*Outlines the read mac address function signature. */
> > +#define IOCTL_PHUB_READ_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 6, __u32))
> > +
> > +/*brief Outlines the write mac address function signature. */
> > +#define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 7, __u32))
>
> IOCTL_PHUB_READ_MAC_ADDR needs _IOR instead of _IOW, and the type
> is still wrong here. Your code currently has struct pch_phub_reqt
> instead of __u32, and if you change the ioctl function as I explained
> above, it should become
>
> #define IOCTL_PHUB_READ_MAC_ADDR (_IOR(PHUB_IOCTL_MAGIC, 6, __u8[6]))
> #define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 7, __u8[6]))
>
> Arnd
>

2010-06-15 06:25:41

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Arnd,

>> +#to set CAN clock to 50Mhz
>> +ifdef CONFIG_PCH_CAN_PCLK_50MHZ
>> +EXTRA_CFLAGS +=-DPCH_CAN_PCLK_50MHZ
>> +endif

>This should not be necessary. Just use CONFIG_PCH_CAN_PCLK_50MHZ directly
>in the code instead of the extra PCH_CAN_PCLK_50MHZ macro.

I have a question. I show the above reason.
In case CAN is integrated as MODULE, macro name is CONFIG_PCH_CAN_PCLK_50MHZ_MODULE.
On the other hand, integrated as built-in, CONFIG_PCH_CAN_PCLK_50MHZ.
To prevent PHUB source code from integrated as MODULE or BUILT-IN,
we re-define macro name in Makefile.

If use CONFIG_PCH_CAN_PCLK_50MHZ directly in the source code,
in case buit-in, behavior is not correct.
But in case module, behavior is not correct.

Please give us your opinion

Thanks,
Ohtake.
----- Original Message -----
From: "Arnd Bergmann" <[email protected]>
To: "Masayuki Ohtak" <[email protected]>
Cc: "Alan Cox" <[email protected]>; "LKML" <[email protected]>; "Andrew"
<[email protected]>; "Intel OTC" <[email protected]>; "Wang, Qi" <[email protected]>; "Wang, Yong Y"
<[email protected]>
Sent: Monday, June 14, 2010 9:50 PM
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver


> On Monday 14 June 2010, Masayuki Ohtak wrote:
> > Hi we have modified for your comments.
> > Please Confirm below.
>
> Looks much better. A few more comments about the new code:
>
> > +#to set CAN clock to 50Mhz
> > +ifdef CONFIG_PCH_CAN_PCLK_50MHZ
> > +EXTRA_CFLAGS +=-DPCH_CAN_PCLK_50MHZ
> > +endif
>
> This should not be necessary. Just use CONFIG_PCH_CAN_PCLK_50MHZ directly
> in the code instead of the extra PCH_CAN_PCLK_50MHZ macro.
> > +
> > +DEFINE_MUTEX(pch_phub_ioctl_mutex);
>
> This should probable be 'static DEFINE_MUTEX', since the symbol does not
> need to be visible in the entire kernel.
>
>
> > +/*--------------------------------------------
> > + internal function prototypes
> > +--------------------------------------------*/
> > +static __s32 __devinit pch_phub_probe(struct pci_dev *pdev, const
> > + struct pci_device_id *id);
> > +static void __devexit pch_phub_remove(struct pci_dev *pdev);
> > +static __s32 pch_phub_suspend(struct pci_dev *pdev, pm_message_t state);
> > +static __s32 pch_phub_resume(struct pci_dev *pdev);
> > +static __s32 pch_phub_open(struct inode *inode, struct file *file);
> > +static __s32 pch_phub_release(struct inode *inode, struct file *file);
> > +static long pch_phub_ioctl(struct file *file, unsigned int cmd,
> > + unsigned long arg);
> > +static ssize_t pch_phub_read(struct file *, char __user *, size_t, loff_t *);
> > +static ssize_t pch_phub_write(struct file *, const char __user *,
> > + size_t, loff_t *);
>
> My general recommendation would be to reorder all the function
> definitions so that you don't need any of these forward declarations.
> That is the order used in most parts of the kernel (so you start reading
> at the bottom), and it makes it easier to understand the structure of
> the code IMHO.
>
> > +/** pch_phub_open - Implements the Initializing and opening of the Packet Hub
> > + module.
> > + * @inode: Contains the reference of the inode structure
> > + * @file: Contains the reference of the file structure
> > + */
> > +static __s32 pch_phub_open(struct inode *inode, struct file *file)
> > +{
> > + __s32 ret;
> > +
> > + spin_lock(&pch_phub_lock);
> > + if (pch_phub_reg.pch_phub_opencount) {
> > + ret = -EBUSY;
> > + } else {
> > + pch_phub_reg.pch_phub_opencount++;
> > + ret = 0;
> > + }
> > + spin_unlock(&pch_phub_lock);
> > +
> > + return ret;
> > +}
>
> As far as I can tell, there is no longer a reason to prevent multiple
> openers. Besides, even if there is only a single user, you might still
> have concurrency problems, so the lock does not help and you could remove
> the open function entirely.
>
> > +/** 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
> > + * @pos: Contains the reference of the file structure
> > + */
> > +
> > +static ssize_t pch_phub_read(struct file *file, char __user *buf, size_t size,
> > + loff_t *pos)
> > +{
> > + __u32 rom_signature = 0;
> > + __u8 rom_length;
> > + __s32 ret_value;
> > + __u32 tmp;
> > + __u8 data;
> > + __u32 addr_offset = 0;
> > +
> > + /*Get Rom signature*/
> > + pch_phub_read_serial_rom(0x80, (__u8 *)&rom_signature);
> > + pch_phub_read_serial_rom(0x81, (__u8 *)&tmp);
> > + rom_signature |= (tmp & 0xff) << 8;
> > + if (rom_signature == 0xAA55) {
> > + pch_phub_read_serial_rom(0x82, &rom_length);
> > + if (size > (rom_length * 512)+1)
> > + return -ENOMEM;
> > +
> > + for (addr_offset = 0;
> > + addr_offset <= ((__u32)rom_length * 512);
> > + addr_offset++) {
> > + pch_phub_read_serial_rom(0x80 + addr_offset, &data);
> > + ret_value = copy_to_user((void *)&buf[addr_offset],
> > + (void *)&data, 1);
> > + if (ret_value)
> > + return -EFAULT;
> > + }
> > + } else {
> > + return -ENOEXEC;
> > + }
> > +
> > + return rom_length * 512 + 1;
> > +}
>
> This function has multiple problems:
>
> - If the size argument is less than rom_length*512, you access past the
> user-provided buffer.
> - When the user does an llseek or pread, the *pos argument is not zero,
> so you should return data from the middle, but you still return data
> from the beginning.
> - You don't update the *pos argument with the new position, so you cannot
> continue the read where the first call left.
> - Instead of returning -ENOMEM, you should just the data you have (or
> 0 for end-of-file).
> - ENOEXEC does not seem appropriate either: The user can just check
> these buffer for the signature here, so you just as well return
> whatever you find in the ROM.
>
> > +
> > +/** 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
> > + * @pos: 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 *pos)
> > +{
> > + static __u8 data[PCH_PHUB_OROM_SIZE];
> > + __s32 ret_value;
> > + __u32 addr_offset = 0;
> > +
> > + if (size > PCH_PHUB_OROM_SIZE)
> > + size = PCH_PHUB_OROM_SIZE;
> > +
> > + ret_value = copy_from_user(data, buf, size);
> > + if (ret_value)
> > + return -EFAULT;
> > +
> > + for (addr_offset = 0; addr_offset < size; addr_offset++) {
> > + ret_value = pch_phub_write_serial_rom(0x80 + addr_offset,
> > + data[addr_offset]);
> > + }
> > +
> > + return size;
> > +}
>
> This has the same problems, plus a buffer overflow: You must never have an
> array of multiple kilobytes on the stack (data[PCH_PHUB_OROM_SIZE]), because
> the stack itself is only a few kilobytes in the kernel. Better use a loop
> with copy_from_user like the read function does.
>
> > +/** pch_phub_release - Implements the release functionality of the Packet Hub
> > + * module.
> > + * @inode: Contains the reference of the inode structure
> > + * @file: Contains the reference of the file structure
> > + */
> > +static __s32 pch_phub_release(struct inode *inode, struct file *file)
> > +{
> > + spin_lock(&pch_phub_lock);
> > +
> > + if (pch_phub_reg.pch_phub_opencount > 0)
> > + pch_phub_reg.pch_phub_opencount--;
> > + spin_unlock(&pch_phub_lock);
> > +
> > + return 0;
> > +}
>
> When you remove the open function, this one can go away as well.
>
> > +/** 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)
> > +{
> > + __s32 ret_value = 0;
> > + struct pch_phub_reqt __user *p_pch_phub_reqt;
> > + __u32 data;
> > + __u32 mac_addr[2];
> > + __s32 ret;
> > + __u32 i;
> > + void __user *varg = (void __user *)arg;
> > +
> > + ret = mutex_trylock(&pch_phub_ioctl_mutex);
> > + if (ret == 0)
> > + goto return_ioctrl;/*Can't get mutex lock*/
>
> mutex_trylock is probably not what you want, it returns immediately
> when there is another function in the kernel.
> mutex_lock_interruptible seems more appropriate, it will block
> until the mutex is free or the process gets sent a signal,
> which you should handle by returning -ERESTARTSYS.
>
> In either case, you must not jump to return_ioctrl here, because
> that will try to release the mutex that you do not hold here,
> causing a hang the next time you enter the function.
>
> > + if (pch_phub_reg.pch_phub_suspended == true) {
> > + ret_value = -EPERM;
> > + goto return_ioctrl;
> > + }
> > +
> > + p_pch_phub_reqt = (struct pch_phub_reqt *)varg;
> > +
> > + if (ret_value)
> > + goto return_ioctrl;
>
> is always zero here.
>
> > + /* End of Access area check */
> > +
> > +
> > + switch (cmd) {
> > +
> > + case IOCTL_PHUB_READ_MAC_ADDR:
> > + mac_addr[0] = 0;
> > + mac_addr[1] = 0;
> > + for (i = 0; i < 4; i++) {
> > + pch_phub_read_gbe_mac_addr(i, (__u8 *)&data);
> > + mac_addr[0] |= data << i*8;
> > + }
> > + for (; i < 6; i++) {
> > + pch_phub_read_gbe_mac_addr(i, (__u8 *)&data);
> > + mac_addr[1] |= data << (i-4)*8;
> > + }
> > +
> > + ret_value = copy_to_user((void *)&p_pch_phub_reqt->data,
> > + (void *)mac_addr, sizeof(mac_addr));
>
> p_pch_phub_reqt->data has multiple problems:
>
> - You have the typecast to (void *), which is wrong and unneeded.
> - The data structure has no point at all any more when you use only one
> field.
>
> Just make this
>
> u8 mac_addr[6];
> for (i = 0; i < 4; i++)
> pch_phub_read_gbe_mac_addr(i, &mac_addr[i]);
> ret_value = copy_to_user(varg, mac_addr, sizeof(mac_addr));
>
> > +#define PHUB_IOCTL_MAGIC (0xf7)
> > +
> > +/*Outlines the read register function signature. */
> > +#define IOCTL_PHUB_READ_REG (_IOW(PHUB_IOCTL_MAGIC, 1, __u32))
> > +
> > +/*Outlines the write register function signature. */
> > +#define IOCTL_PHUB_WRITE_REG (_IOW(PHUB_IOCTL_MAGIC, 2, __u32))
> > +
> > +/*Outlines the read, modify and write register function signature. */
> > +#define IOCTL_PHUB_READ_MODIFY_WRITE_REG (_IOW(PHUB_IOCTL_MAGIC, 3,\
> > + __u32))
> > +
> > +/*Outlines the read option rom function signature. */
> > +#define IOCTL_PHUB_READ_OROM (_IOW(PHUB_IOCTL_MAGIC, 4, __u32))
> > +
> > +/*Outlines the write option rom function signature. */
> > +#define IOCTL_PHUB_WRITE_OROM (_IOW(PHUB_IOCTL_MAGIC, 5, __u32))
>
> These should all get removed now.
>
> > +/*Outlines the read mac address function signature. */
> > +#define IOCTL_PHUB_READ_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 6, __u32))
> > +
> > +/*brief Outlines the write mac address function signature. */
> > +#define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 7, __u32))
>
> IOCTL_PHUB_READ_MAC_ADDR needs _IOR instead of _IOW, and the type
> is still wrong here. Your code currently has struct pch_phub_reqt
> instead of __u32, and if you change the ioctl function as I explained
> above, it should become
>
> #define IOCTL_PHUB_READ_MAC_ADDR (_IOR(PHUB_IOCTL_MAGIC, 6, __u8[6]))
> #define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 7, __u8[6]))
>
> Arnd
>

2010-06-15 06:58:50

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Arnd,

I have additional question.
> - When the user does an llseek or pread, the *pos argument is not zero,
> so you should return data from the middle, but you still return data
> from the beginning.


Must a driver read/write method support '*pos' parameter ?
We think PHUB doesn't have to support '*pos',
and ,we think, PHUB OROM R/W function supports only whole of ROM data R/W is enough.
Please give us your opinion.

Thanks.
Ohtake

----- Original Message -----
From: "Masayuki Ohtake" <[email protected]>
To: "Arnd Bergmann" <[email protected]>
Cc: "Alan Cox" <[email protected]>; "LKML" <[email protected]>; "Andrew"
<[email protected]>; "Intel OTC" <[email protected]>; "Wang, Qi" <[email protected]>; "Wang, Yong Y"
<[email protected]>
Sent: Tuesday, June 15, 2010 3:25 PM
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver


> Hi Arnd,
>
> >> +#to set CAN clock to 50Mhz
> >> +ifdef CONFIG_PCH_CAN_PCLK_50MHZ
> >> +EXTRA_CFLAGS +=-DPCH_CAN_PCLK_50MHZ
> >> +endif
>
> >This should not be necessary. Just use CONFIG_PCH_CAN_PCLK_50MHZ directly
> >in the code instead of the extra PCH_CAN_PCLK_50MHZ macro.
>
> I have a question. I show the above reason.
> In case CAN is integrated as MODULE, macro name is CONFIG_PCH_CAN_PCLK_50MHZ_MODULE.
> On the other hand, integrated as built-in, CONFIG_PCH_CAN_PCLK_50MHZ.
> To prevent PHUB source code from integrated as MODULE or BUILT-IN,
> we re-define macro name in Makefile.
>
> If use CONFIG_PCH_CAN_PCLK_50MHZ directly in the source code,
> in case buit-in, behavior is not correct.
> But in case module, behavior is not correct.
>
> Please give us your opinion
>
> Thanks,
> Ohtake.
> ----- Original Message -----
> From: "Arnd Bergmann" <[email protected]>
> To: "Masayuki Ohtak" <[email protected]>
> Cc: "Alan Cox" <[email protected]>; "LKML" <[email protected]>; "Andrew"
> <[email protected]>; "Intel OTC" <[email protected]>; "Wang, Qi" <[email protected]>; "Wang, Yong Y"
> <[email protected]>
> Sent: Monday, June 14, 2010 9:50 PM
> Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver
>
>
> > On Monday 14 June 2010, Masayuki Ohtak wrote:
> > > Hi we have modified for your comments.
> > > Please Confirm below.
> >
> > Looks much better. A few more comments about the new code:
> >
> > > +#to set CAN clock to 50Mhz
> > > +ifdef CONFIG_PCH_CAN_PCLK_50MHZ
> > > +EXTRA_CFLAGS +=-DPCH_CAN_PCLK_50MHZ
> > > +endif
> >
> > This should not be necessary. Just use CONFIG_PCH_CAN_PCLK_50MHZ directly
> > in the code instead of the extra PCH_CAN_PCLK_50MHZ macro.
> > > +
> > > +DEFINE_MUTEX(pch_phub_ioctl_mutex);
> >
> > This should probable be 'static DEFINE_MUTEX', since the symbol does not
> > need to be visible in the entire kernel.
> >
> >
> > > +/*--------------------------------------------
> > > + internal function prototypes
> > > +--------------------------------------------*/
> > > +static __s32 __devinit pch_phub_probe(struct pci_dev *pdev, const
> > > + struct pci_device_id *id);
> > > +static void __devexit pch_phub_remove(struct pci_dev *pdev);
> > > +static __s32 pch_phub_suspend(struct pci_dev *pdev, pm_message_t state);
> > > +static __s32 pch_phub_resume(struct pci_dev *pdev);
> > > +static __s32 pch_phub_open(struct inode *inode, struct file *file);
> > > +static __s32 pch_phub_release(struct inode *inode, struct file *file);
> > > +static long pch_phub_ioctl(struct file *file, unsigned int cmd,
> > > + unsigned long arg);
> > > +static ssize_t pch_phub_read(struct file *, char __user *, size_t, loff_t *);
> > > +static ssize_t pch_phub_write(struct file *, const char __user *,
> > > + size_t, loff_t *);
> >
> > My general recommendation would be to reorder all the function
> > definitions so that you don't need any of these forward declarations.
> > That is the order used in most parts of the kernel (so you start reading
> > at the bottom), and it makes it easier to understand the structure of
> > the code IMHO.
> >
> > > +/** pch_phub_open - Implements the Initializing and opening of the Packet Hub
> > > + module.
> > > + * @inode: Contains the reference of the inode structure
> > > + * @file: Contains the reference of the file structure
> > > + */
> > > +static __s32 pch_phub_open(struct inode *inode, struct file *file)
> > > +{
> > > + __s32 ret;
> > > +
> > > + spin_lock(&pch_phub_lock);
> > > + if (pch_phub_reg.pch_phub_opencount) {
> > > + ret = -EBUSY;
> > > + } else {
> > > + pch_phub_reg.pch_phub_opencount++;
> > > + ret = 0;
> > > + }
> > > + spin_unlock(&pch_phub_lock);
> > > +
> > > + return ret;
> > > +}
> >
> > As far as I can tell, there is no longer a reason to prevent multiple
> > openers. Besides, even if there is only a single user, you might still
> > have concurrency problems, so the lock does not help and you could remove
> > the open function entirely.
> >
> > > +/** 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
> > > + * @pos: Contains the reference of the file structure
> > > + */
> > > +
> > > +static ssize_t pch_phub_read(struct file *file, char __user *buf, size_t size,
> > > + loff_t *pos)
> > > +{
> > > + __u32 rom_signature = 0;
> > > + __u8 rom_length;
> > > + __s32 ret_value;
> > > + __u32 tmp;
> > > + __u8 data;
> > > + __u32 addr_offset = 0;
> > > +
> > > + /*Get Rom signature*/
> > > + pch_phub_read_serial_rom(0x80, (__u8 *)&rom_signature);
> > > + pch_phub_read_serial_rom(0x81, (__u8 *)&tmp);
> > > + rom_signature |= (tmp & 0xff) << 8;
> > > + if (rom_signature == 0xAA55) {
> > > + pch_phub_read_serial_rom(0x82, &rom_length);
> > > + if (size > (rom_length * 512)+1)
> > > + return -ENOMEM;
> > > +
> > > + for (addr_offset = 0;
> > > + addr_offset <= ((__u32)rom_length * 512);
> > > + addr_offset++) {
> > > + pch_phub_read_serial_rom(0x80 + addr_offset, &data);
> > > + ret_value = copy_to_user((void *)&buf[addr_offset],
> > > + (void *)&data, 1);
> > > + if (ret_value)
> > > + return -EFAULT;
> > > + }
> > > + } else {
> > > + return -ENOEXEC;
> > > + }
> > > +
> > > + return rom_length * 512 + 1;
> > > +}
> >
> > This function has multiple problems:
> >
> > - If the size argument is less than rom_length*512, you access past the
> > user-provided buffer.
> > - When the user does an llseek or pread, the *pos argument is not zero,
> > so you should return data from the middle, but you still return data
> > from the beginning.
> > - You don't update the *pos argument with the new position, so you cannot
> > continue the read where the first call left.
> > - Instead of returning -ENOMEM, you should just the data you have (or
> > 0 for end-of-file).
> > - ENOEXEC does not seem appropriate either: The user can just check
> > these buffer for the signature here, so you just as well return
> > whatever you find in the ROM.
> >
> > > +
> > > +/** 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
> > > + * @pos: 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 *pos)
> > > +{
> > > + static __u8 data[PCH_PHUB_OROM_SIZE];
> > > + __s32 ret_value;
> > > + __u32 addr_offset = 0;
> > > +
> > > + if (size > PCH_PHUB_OROM_SIZE)
> > > + size = PCH_PHUB_OROM_SIZE;
> > > +
> > > + ret_value = copy_from_user(data, buf, size);
> > > + if (ret_value)
> > > + return -EFAULT;
> > > +
> > > + for (addr_offset = 0; addr_offset < size; addr_offset++) {
> > > + ret_value = pch_phub_write_serial_rom(0x80 + addr_offset,
> > > + data[addr_offset]);
> > > + }
> > > +
> > > + return size;
> > > +}
> >
> > This has the same problems, plus a buffer overflow: You must never have an
> > array of multiple kilobytes on the stack (data[PCH_PHUB_OROM_SIZE]), because
> > the stack itself is only a few kilobytes in the kernel. Better use a loop
> > with copy_from_user like the read function does.
> >
> > > +/** pch_phub_release - Implements the release functionality of the Packet Hub
> > > + * module.
> > > + * @inode: Contains the reference of the inode structure
> > > + * @file: Contains the reference of the file structure
> > > + */
> > > +static __s32 pch_phub_release(struct inode *inode, struct file *file)
> > > +{
> > > + spin_lock(&pch_phub_lock);
> > > +
> > > + if (pch_phub_reg.pch_phub_opencount > 0)
> > > + pch_phub_reg.pch_phub_opencount--;
> > > + spin_unlock(&pch_phub_lock);
> > > +
> > > + return 0;
> > > +}
> >
> > When you remove the open function, this one can go away as well.
> >
> > > +/** 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)
> > > +{
> > > + __s32 ret_value = 0;
> > > + struct pch_phub_reqt __user *p_pch_phub_reqt;
> > > + __u32 data;
> > > + __u32 mac_addr[2];
> > > + __s32 ret;
> > > + __u32 i;
> > > + void __user *varg = (void __user *)arg;
> > > +
> > > + ret = mutex_trylock(&pch_phub_ioctl_mutex);
> > > + if (ret == 0)
> > > + goto return_ioctrl;/*Can't get mutex lock*/
> >
> > mutex_trylock is probably not what you want, it returns immediately
> > when there is another function in the kernel.
> > mutex_lock_interruptible seems more appropriate, it will block
> > until the mutex is free or the process gets sent a signal,
> > which you should handle by returning -ERESTARTSYS.
> >
> > In either case, you must not jump to return_ioctrl here, because
> > that will try to release the mutex that you do not hold here,
> > causing a hang the next time you enter the function.
> >
> > > + if (pch_phub_reg.pch_phub_suspended == true) {
> > > + ret_value = -EPERM;
> > > + goto return_ioctrl;
> > > + }
> > > +
> > > + p_pch_phub_reqt = (struct pch_phub_reqt *)varg;
> > > +
> > > + if (ret_value)
> > > + goto return_ioctrl;
> >
> > is always zero here.
> >
> > > + /* End of Access area check */
> > > +
> > > +
> > > + switch (cmd) {
> > > +
> > > + case IOCTL_PHUB_READ_MAC_ADDR:
> > > + mac_addr[0] = 0;
> > > + mac_addr[1] = 0;
> > > + for (i = 0; i < 4; i++) {
> > > + pch_phub_read_gbe_mac_addr(i, (__u8 *)&data);
> > > + mac_addr[0] |= data << i*8;
> > > + }
> > > + for (; i < 6; i++) {
> > > + pch_phub_read_gbe_mac_addr(i, (__u8 *)&data);
> > > + mac_addr[1] |= data << (i-4)*8;
> > > + }
> > > +
> > > + ret_value = copy_to_user((void *)&p_pch_phub_reqt->data,
> > > + (void *)mac_addr, sizeof(mac_addr));
> >
> > p_pch_phub_reqt->data has multiple problems:
> >
> > - You have the typecast to (void *), which is wrong and unneeded.
> > - The data structure has no point at all any more when you use only one
> > field.
> >
> > Just make this
> >
> > u8 mac_addr[6];
> > for (i = 0; i < 4; i++)
> > pch_phub_read_gbe_mac_addr(i, &mac_addr[i]);
> > ret_value = copy_to_user(varg, mac_addr, sizeof(mac_addr));
> >
> > > +#define PHUB_IOCTL_MAGIC (0xf7)
> > > +
> > > +/*Outlines the read register function signature. */
> > > +#define IOCTL_PHUB_READ_REG (_IOW(PHUB_IOCTL_MAGIC, 1, __u32))
> > > +
> > > +/*Outlines the write register function signature. */
> > > +#define IOCTL_PHUB_WRITE_REG (_IOW(PHUB_IOCTL_MAGIC, 2, __u32))
> > > +
> > > +/*Outlines the read, modify and write register function signature. */
> > > +#define IOCTL_PHUB_READ_MODIFY_WRITE_REG (_IOW(PHUB_IOCTL_MAGIC, 3,\
> > > + __u32))
> > > +
> > > +/*Outlines the read option rom function signature. */
> > > +#define IOCTL_PHUB_READ_OROM (_IOW(PHUB_IOCTL_MAGIC, 4, __u32))
> > > +
> > > +/*Outlines the write option rom function signature. */
> > > +#define IOCTL_PHUB_WRITE_OROM (_IOW(PHUB_IOCTL_MAGIC, 5, __u32))
> >
> > These should all get removed now.
> >
> > > +/*Outlines the read mac address function signature. */
> > > +#define IOCTL_PHUB_READ_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 6, __u32))
> > > +
> > > +/*brief Outlines the write mac address function signature. */
> > > +#define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 7, __u32))
> >
> > IOCTL_PHUB_READ_MAC_ADDR needs _IOR instead of _IOW, and the type
> > is still wrong here. Your code currently has struct pch_phub_reqt
> > instead of __u32, and if you change the ioctl function as I explained
> > above, it should become
> >
> > #define IOCTL_PHUB_READ_MAC_ADDR (_IOR(PHUB_IOCTL_MAGIC, 6, __u8[6]))
> > #define IOCTL_PHUB_WRITE_MAC_ADDR (_IOW(PHUB_IOCTL_MAGIC, 7, __u8[6]))
> >
> > Arnd
> >
>
>

2010-06-15 10:37:38

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

On Tuesday 15 June 2010, Masayuki Ohtake wrote:
> I have additional question.
> > - When the user does an llseek or pread, the *pos argument is not zero,
> > so you should return data from the middle, but you still return data
> > from the beginning.
>
>
> Must a driver read/write method support '*pos' parameter ?
> We think PHUB doesn't have to support '*pos',
> and ,we think, PHUB OROM R/W function supports only whole of ROM data R/W is enough.
> Please give us your opinion.

While you do not strictly need to support *pos to get the functionality
you want, it should be easy enough to implement and it will make the
read/write callbacks conform to the general semantics of file based
I/O. Especially if you want to be able to use tools like 'cat', 'hexdump'
or 'dd' on the file descriptor, you need to implement support for
short reads. If you are unsure about how to do that correctly, I can
help you some more. A good reference implementation is
simple_transaction_read in fs/libfs.c, which simply returns some
private memory.

If there is a strict hardware limitation that prevents you from doing
partial writes, you could expose that in the interface and return -EIO
in case of invalid *pos or size arguements, but my impression was that
the hardware can deal with bytewise access.

Arnd

2010-06-15 10:43:28

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

On Tuesday 15 June 2010, Masayuki Ohtake wrote:
> >This should not be necessary. Just use CONFIG_PCH_CAN_PCLK_50MHZ directly
> >in the code instead of the extra PCH_CAN_PCLK_50MHZ macro.
>
> I have a question. I show the above reason.
> In case CAN is integrated as MODULE, macro name is CONFIG_PCH_CAN_PCLK_50MHZ_MODULE.
> On the other hand, integrated as built-in, CONFIG_PCH_CAN_PCLK_50MHZ.
> To prevent PHUB source code from integrated as MODULE or BUILT-IN,
> we re-define macro name in Makefile.
>
> If use CONFIG_PCH_CAN_PCLK_50MHZ directly in the source code,
> in case buit-in, behavior is not correct.
> But in case module, behavior is not correct.

I don't understand the problem, because you have the definition

config PCH_CAN_PCLK_50MHZ
bool "CAN PCLK 50MHz"
depends on PCH_PHUB

which is 'bool', not 'tristate', so it can never be a module.
If you are referring to a dependency on the CAN code that is
not part of this patch, you can express this as

config PCH_CAN_PCLK_50MHZ
bool "CAN PCLK 50MHz"
depends on PCH_PHUB || CAN != "n"

This will leave CONFIG_PCH_CAN_PCLK_50MHZ as bool and let it only
get enabled if CONFIG_CAN is either "y" or "m".
Does that answer your question?

Arnd

2010-06-15 12:12:24

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Arnd,

Thank you for your comments.
I can understand your intention.
I misunderstood about Kconfig behavior.

Thanks,
Ohtake.

----- Original Message -----
From: "Arnd Bergmann" <[email protected]>
To: "Masayuki Ohtake" <[email protected]>
Cc: "Alan Cox" <[email protected]>; "LKML" <[email protected]>; "Andrew"
<[email protected]>; "Intel OTC" <[email protected]>; "Wang, Qi" <[email protected]>; "Wang, Yong Y"
<[email protected]>
Sent: Tuesday, June 15, 2010 7:42 PM
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver


> On Tuesday 15 June 2010, Masayuki Ohtake wrote:
> > >This should not be necessary. Just use CONFIG_PCH_CAN_PCLK_50MHZ directly
> > >in the code instead of the extra PCH_CAN_PCLK_50MHZ macro.
> >
> > I have a question. I show the above reason.
> > In case CAN is integrated as MODULE, macro name is CONFIG_PCH_CAN_PCLK_50MHZ_MODULE.
> > On the other hand, integrated as built-in, CONFIG_PCH_CAN_PCLK_50MHZ.
> > To prevent PHUB source code from integrated as MODULE or BUILT-IN,
> > we re-define macro name in Makefile.
> >
> > If use CONFIG_PCH_CAN_PCLK_50MHZ directly in the source code,
> > in case buit-in, behavior is not correct.
> > But in case module, behavior is not correct.
>
> I don't understand the problem, because you have the definition
>
> config PCH_CAN_PCLK_50MHZ
> bool "CAN PCLK 50MHz"
> depends on PCH_PHUB
>
> which is 'bool', not 'tristate', so it can never be a module.
> If you are referring to a dependency on the CAN code that is
> not part of this patch, you can express this as
>
> config PCH_CAN_PCLK_50MHZ
> bool "CAN PCLK 50MHz"
> depends on PCH_PHUB || CAN != "n"
>
> This will leave CONFIG_PCH_CAN_PCLK_50MHZ as bool and let it only
> get enabled if CONFIG_CAN is either "y" or "m".
> Does that answer your question?
>
> Arnd
>

2010-06-15 12:14:07

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Arnd,

Thank you for your comments.
I will study your showed reference.

Thanks,
Ohtake

----- Original Message -----
From: "Arnd Bergmann" <[email protected]>
To: "Masayuki Ohtake" <[email protected]>
Cc: "Wang, Yong Y" <[email protected]>; "Wang, Qi" <[email protected]>; "Intel OTC" <[email protected]>; "Andrew"
<[email protected]>; "LKML" <[email protected]>; "Alan Cox" <[email protected]>
Sent: Tuesday, June 15, 2010 7:37 PM
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver


> On Tuesday 15 June 2010, Masayuki Ohtake wrote:
> > I have additional question.
> > > - When the user does an llseek or pread, the *pos argument is not zero,
> > > so you should return data from the middle, but you still return data
> > > from the beginning.
> >
> >
> > Must a driver read/write method support '*pos' parameter ?
> > We think PHUB doesn't have to support '*pos',
> > and ,we think, PHUB OROM R/W function supports only whole of ROM data R/W is enough.
> > Please give us your opinion.
>
> While you do not strictly need to support *pos to get the functionality
> you want, it should be easy enough to implement and it will make the
> read/write callbacks conform to the general semantics of file based
> I/O. Especially if you want to be able to use tools like 'cat', 'hexdump'
> or 'dd' on the file descriptor, you need to implement support for
> short reads. If you are unsure about how to do that correctly, I can
> help you some more. A good reference implementation is
> simple_transaction_read in fs/libfs.c, which simply returns some
> private memory.
>
> If there is a strict hardware limitation that prevents you from doing
> partial writes, you could expose that in the interface and return -EIO
> in case of invalid *pos or size arguements, but my impression was that
> the hardware can deal with bytewise access.
>
> Arnd
>

2010-06-16 08:58:56

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Arnd,

For user useful, I have decided to implement 'pos' processing to Phub driver.

I have a question.
> Especially if you want to be able to use tools like 'cat', 'hexdump'
> or 'dd' on the file descriptor, you need to implement support for
> short reads.
I don't know how to test(short read/write access).
Could you tell me how to do the above ?

Thanks,
Ohtake.

----- Original Message -----
From: "Arnd Bergmann" <[email protected]>
To: "Masayuki Ohtake" <[email protected]>
Cc: "Wang, Yong Y" <[email protected]>; "Wang, Qi" <[email protected]>; "Intel OTC" <[email protected]>; "Andrew"
<[email protected]>; "LKML" <[email protected]>; "Alan Cox" <[email protected]>
Sent: Tuesday, June 15, 2010 7:37 PM
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver


> On Tuesday 15 June 2010, Masayuki Ohtake wrote:
> > I have additional question.
> > > - When the user does an llseek or pread, the *pos argument is not zero,
> > > so you should return data from the middle, but you still return data
> > > from the beginning.
> >
> >
> > Must a driver read/write method support '*pos' parameter ?
> > We think PHUB doesn't have to support '*pos',
> > and ,we think, PHUB OROM R/W function supports only whole of ROM data R/W is enough.
> > Please give us your opinion.
>
> While you do not strictly need to support *pos to get the functionality
> you want, it should be easy enough to implement and it will make the
> read/write callbacks conform to the general semantics of file based
> I/O. Especially if you want to be able to use tools like 'cat', 'hexdump'
> or 'dd' on the file descriptor, you need to implement support for
> short reads. If you are unsure about how to do that correctly, I can
> help you some more. A good reference implementation is
> simple_transaction_read in fs/libfs.c, which simply returns some
> private memory.
>
> If there is a strict hardware limitation that prevents you from doing
> partial writes, you could expose that in the interface and return -EIO
> in case of invalid *pos or size arguements, but my impression was that
> the hardware can deal with bytewise access.
>
> Arnd
>

2010-06-16 10:50:58

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

On Wednesday 16 June 2010, Masayuki Ohtake wrote:
> > Especially if you want to be able to use tools like 'cat', 'hexdump'
> > or 'dd' on the file descriptor, you need to implement support for
> > short reads.
> I don't know how to test(short read/write access).
> Could you tell me how to do the above ?

The easiest way is using dd, where you can specify the block
size as well as input and output offsets.

e.g.
dd if=/dev/topcliffphub of=testfile bs=7

Should be able to read the contents of the rom completely
without error conditions, copying seven bytes at a time.

dd if=inputfile of=/dev/topcliffphub bs=1 seek=1024 count=16

would write 16 bytes from inputfile into the rom at position 1024.

Arnd

2010-06-17 00:17:42

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Arnd,

Thank you.
I will try it.

Ohtake.

----- Original Message -----
From: "Arnd Bergmann" <[email protected]>
To: "Masayuki Ohtake" <[email protected]>
Cc: "Alan Cox" <[email protected]>; "LKML" <[email protected]>; "Andrew"
<[email protected]>; "Intel OTC" <[email protected]>; "Wang, Qi" <[email protected]>; "Wang, Yong Y"
<[email protected]>
Sent: Wednesday, June 16, 2010 7:50 PM
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver


> On Wednesday 16 June 2010, Masayuki Ohtake wrote:
> > > Especially if you want to be able to use tools like 'cat', 'hexdump'
> > > or 'dd' on the file descriptor, you need to implement support for
> > > short reads.
> > I don't know how to test(short read/write access).
> > Could you tell me how to do the above ?
>
> The easiest way is using dd, where you can specify the block
> size as well as input and output offsets.
>
> e.g.
> dd if=/dev/topcliffphub of=testfile bs=7
>
> Should be able to read the contents of the rom completely
> without error conditions, copying seven bytes at a time.
>
> dd if=inputfile of=/dev/topcliffphub bs=1 seek=1024 count=16
>
> would write 16 bytes from inputfile into the rom at position 1024.
>
> Arnd
>


2010-06-17 02:43:41

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Alan,

We have modified for your comments.
Please Confirm below.

Thanks.
Ohtake.


Signed-off-by: Masayuki Ohtake <[email protected]>
---
drivers/char/Kconfig | 25 +
drivers/char/Makefile | 4 +
drivers/char/pch_phub/Makefile | 7 +
drivers/char/pch_phub/pch_phub.c | 940 +++++++++++++++++++++++
drivers/char/pch_phub/pch_phub.h | 58 ++
5 files changed, 1034 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..285ce78 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -4,6 +4,31 @@

menu "Character devices"

+config PCH_IEEE1588
+ tristate "PCH IEEE1588"
+ depends on PCI
+ help
+ If you say yes to this option, support will be included for the
+ PCH IEEE1588 Host controller.
+
+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 PCH_CAN_PCLK_50MHZ
+ bool "CAN PCLK 50MHz"
+ depends on PCH_PHUB
+ help
+ If you say yes to this option, clock is set to 50MHz.(For CAN control)
+
config VT
bool "Virtual terminal" if EMBEDDED
depends on !S390
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index f957edf..dc092d0 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -111,6 +111,10 @@ obj-$(CONFIG_PS3_FLASH) += ps3flash.o
obj-$(CONFIG_JS_RTC) += js-rtc.o
js-rtc-y = rtc.o

+obj-$(CONFIG_PCH_IEEE1588) += pch_ieee1588/
+
+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..be1bfe2
--- /dev/null
+++ b/drivers/char/pch_phub/pch_phub.c
@@ -0,0 +1,940 @@
+/*!
+ * @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"
+
+#define PCH_READ_REG(a) ioread32(pch_phub_reg.pch_phub_base_address + (a))
+
+#define PCH_WRITE_REG(a, b) iowrite32((a), pch_phub_reg.pch_phub_base_address +\
+ (b))
+/*--------------------------------------------
+ global variables
+--------------------------------------------*/
+/**
+ * struct pch_phub_reg_t - 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_t {
+ __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];
+#ifdef CONFIG_PCH_CAN_PCLK_50MHZ
+ __u32 clkcfg_reg;
+#endif
+ void __iomem *pch_phub_base_address;
+ void __iomem *pch_phub_extrom_base_address;
+ __s32 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 __s32 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(__u32 reg_addr_offset,
+ __u32 data, __u32 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)
+{
+ __u32 i = 0;
+
+ dev_dbg(&pdev->dev, "pch_phub_save_reg_conf ENTRY\n");
+ /* to store contents of PHUB_ID register */
+ pch_phub_reg.phub_id_reg = PCH_READ_REG(PCH_PHUB_PHUB_ID_REG);
+ /* to store contents of QUEUE_PRI_VAL register */
+ pch_phub_reg.q_pri_val_reg = PCH_READ_REG(PCH_PHUB_QUEUE_PRI_VAL_REG);
+ /* to store contents of RC_QUEUE_MAXSIZE register */
+ pch_phub_reg.rc_q_maxsize_reg =
+ PCH_READ_REG(PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+ /* to store contents of BRI_QUEUE_MAXSIZE register */
+ pch_phub_reg.bri_q_maxsize_reg =
+ PCH_READ_REG(PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+ /* to store contents of COMP_RESP_TIMEOUT register */
+ pch_phub_reg.comp_resp_timeout_reg =
+ PCH_READ_REG(PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+ /* to store contents of BUS_SLAVE_CONTROL_REG register */
+ pch_phub_reg.bus_slave_control_reg =
+ PCH_READ_REG(PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+ /* to store contents of DEADLOCK_AVOID_TYPE register */
+ pch_phub_reg.deadlock_avoid_type_reg =
+ PCH_READ_REG(PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+ /* to store contents of INTPIN_REG_WPERMIT register 0 */
+ pch_phub_reg.intpin_reg_wpermit_reg0 =
+ PCH_READ_REG(PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+ /* to store contents of INTPIN_REG_WPERMIT register 1 */
+ pch_phub_reg.intpin_reg_wpermit_reg1 =
+ PCH_READ_REG(PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+ /* to store contents of INTPIN_REG_WPERMIT register 2 */
+ pch_phub_reg.intpin_reg_wpermit_reg2 =
+ PCH_READ_REG(PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+ /* to store contents of INTPIN_REG_WPERMIT register 3 */
+ pch_phub_reg.intpin_reg_wpermit_reg3 =
+ PCH_READ_REG(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] =
+ PCH_READ_REG(
+ 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]);
+ }
+#ifdef CONFIG_PCH_CAN_PCLK_50MHZ
+ /* save clk cfg register */
+ pch_phub_reg.clkcfg_reg = PCH_READ_REG(CLKCFG_REG_OFFSET);
+#endif
+ return;
+}
+
+/** pch_phub_restore_reg_conf - restore register configuration
+ */
+
+static void pch_phub_restore_reg_conf(struct pci_dev *pdev)
+{
+ __u32 i;
+
+ dev_dbg(&pdev->dev, "pch_phub_restore_reg_conf ENTRY\n");
+ /* to store contents of PHUB_ID register */
+ PCH_WRITE_REG(pch_phub_reg.phub_id_reg, PCH_PHUB_PHUB_ID_REG);
+ /* to store contents of QUEUE_PRI_VAL register */
+ PCH_WRITE_REG(pch_phub_reg.q_pri_val_reg, PCH_PHUB_QUEUE_PRI_VAL_REG);
+ /* to store contents of RC_QUEUE_MAXSIZE register */
+ PCH_WRITE_REG(pch_phub_reg.rc_q_maxsize_reg,
+ PCH_PHUB_RC_QUEUE_MAXSIZE_REG);
+ /* to store contents of BRI_QUEUE_MAXSIZE register */
+ PCH_WRITE_REG(pch_phub_reg.bri_q_maxsize_reg,
+ PCH_PHUB_BRI_QUEUE_MAXSIZE_REG);
+ /* to store contents of COMP_RESP_TIMEOUT register */
+ PCH_WRITE_REG(pch_phub_reg.comp_resp_timeout_reg,
+ PCH_PHUB_COMP_RESP_TIMEOUT_REG);
+ /* to store contents of BUS_SLAVE_CONTROL_REG register */
+ PCH_WRITE_REG(pch_phub_reg.bus_slave_control_reg,
+ PCH_PHUB_BUS_SLAVE_CONTROL_REG);
+ /* to store contents of DEADLOCK_AVOID_TYPE register */
+ PCH_WRITE_REG(pch_phub_reg.deadlock_avoid_type_reg,
+ PCH_PHUB_DEADLOCK_AVOID_TYPE_REG);
+ /* to store contents of INTPIN_REG_WPERMIT register 0 */
+ PCH_WRITE_REG(pch_phub_reg.intpin_reg_wpermit_reg0,
+ PCH_PHUB_INTPIN_REG_WPERMIT_REG0);
+ /* to store contents of INTPIN_REG_WPERMIT register 1 */
+ PCH_WRITE_REG(pch_phub_reg.intpin_reg_wpermit_reg1,
+ PCH_PHUB_INTPIN_REG_WPERMIT_REG1);
+ /* to store contents of INTPIN_REG_WPERMIT register 2 */
+ PCH_WRITE_REG(pch_phub_reg.intpin_reg_wpermit_reg2,
+ PCH_PHUB_INTPIN_REG_WPERMIT_REG2);
+ /* to store contents of INTPIN_REG_WPERMIT register 3 */
+ PCH_WRITE_REG(pch_phub_reg.intpin_reg_wpermit_reg3,
+ 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++) {
+ PCH_WRITE_REG(pch_phub_reg.int_reduce_control_reg[i],
+ 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]);
+ }
+
+#ifdef CONFIG_PCH_CAN_PCLK_50MHZ
+ /*restore the clock config reg */
+ PCH_WRITE_REG(pch_phub_reg.clkcfg_reg, CLKCFG_REG_OFFSET);
+#endif
+
+ 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 __s32 pch_phub_read_serial_rom(__u32 offset_address, __u8 *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 __s32 pch_phub_write_serial_rom(__u32 offset_address,
+ __u8 data)
+{
+ __s32 retval = 0;
+ void __iomem *mem_addr = pch_phub_reg.pch_phub_extrom_base_address +\
+ (offset_address & 0xFFFFFFFC);
+ __s32 i = 0;
+ __u32 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 | (__u32)data),
+ mem_addr);
+ break;
+ case 1:
+ word_data &= 0xFFFF00FF;
+ iowrite32((word_data | ((__u32)data << 8)),
+ mem_addr);
+ break;
+ case 2:
+ word_data &= 0xFF00FFFF;
+ iowrite32((word_data | ((__u32)data << 16)),
+ mem_addr);
+ break;
+ case 3:
+ word_data &= 0x00FFFFFF;
+ iowrite32((word_data | ((__u32)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 __s32 pch_phub_read_serial_rom_val(__u32 offset_address, __u8 *data)
+{
+ __s32 retval = 0;
+ __u32 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 __s32 pch_phub_write_serial_rom_val(__u32 offset_address,
+ __u8 data)
+{
+ __s32 retval = 0;
+ __u32 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 __s32 pch_phub_gbe_serial_rom_conf(void)
+{
+ __s32 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 __s32 pch_phub_read_gbe_mac_addr(__u32 offset_address, __u8 *data)
+{
+ __s32 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 __s32 pch_phub_write_gbe_mac_addr(__u32 offset_address,
+ __u8 data)
+{
+ __s32 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)
+{
+ __u32 rom_signature = 0;
+ __u8 rom_length;
+ __s32 ret_value;
+ __u32 tmp;
+ __u8 data;
+ __u32 addr_offset = 0;
+ __u32 orom_size;
+ loff_t pos = *ppos;
+
+ if (pos < 0)
+ return -EINVAL;
+
+ /*Get Rom signature*/
+ pch_phub_read_serial_rom(0x80, (__u8 *)&rom_signature);
+ pch_phub_read_serial_rom(0x81, (__u8 *)&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)
+{
+ static __u32 data;
+ __s32 ret_value;
+ __u32 addr_offset = 0;
+ loff_t pos = *ppos;
+
+ if (pos < 0)
+ return -EINVAL;
+
+ for (addr_offset = 0; addr_offset < size; addr_offset++) {
+ get_user(data, &buf[addr_offset]);
+
+ ret_value = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
+ data);
+ if (ret_value)
+ return -EFAULT;
+
+ 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)
+{
+ __s32 ret_value = 0;
+ __u8 mac_addr[6];
+ __s32 ret;
+ __u32 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,
+};
+
+
+/** 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 __s32 __devinit pch_phub_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+
+ char *DRIVER_NAME = "pch_phub";
+ __s32 ret;
+ __u32 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",
+ (__u32)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",
+ (__u32)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);
+
+#ifdef CONFIG_PCH_CAN_PCLK_50MHZ
+ /*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((__u32)CLKCFG_REG_OFFSET,
+ CLKCFG_CAN_50MHZ, CLKCFG_CANCLK_MASK);
+#endif
+ /* set the prefech value */
+ PCH_WRITE_REG(0x000ffffa, 0x14);
+ /* set the interrupt delay value */
+ PCH_WRITE_REG(0x25, 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 __s32 pch_phub_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ __s32 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 __s32 pch_phub_resume(struct pci_dev *pdev)
+{
+
+ __s32 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 __s32 __init pch_phub_pci_init(void)
+{
+ __s32 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

2010-06-17 11:59:53

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Looks almost good to me now. Some more details (only the -EFAULT return code
is a real issue, the other ones are cosmetic changes):

On Thursday 17 June 2010, Masayuki Ohtak wrote:
> +
> +#define PCH_READ_REG(a) ioread32(pch_phub_reg.pch_phub_base_address + (a))
> +
> +#define PCH_WRITE_REG(a, b) iowrite32((a), pch_phub_reg.pch_phub_base_address +\
> + (b))

I'd recommend just getting rid of these abstractions. You only use
them in one or two functions each, so you can just as well add a local
variable there and do

void __iomem *p = pch_phub_reg.pch_phub_base_address;
foo = ioread32(p + foo_offset);
bar = ioread32(p + bar_offset);
...

Not really important, but this way it would be more conventional
without having to write extra code.

> +/*--------------------------------------------
> + global variables
> +--------------------------------------------*/
> +/**
> + * struct pch_phub_reg_t - 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_t {

It would be better to drop the _t postfix on the type name.
By convention, we use this only for typedefs on simple data
types like off_t but not for structures.

> + __u32 phub_id_reg;
> + __u32 q_pri_val_reg;
> + __u32 rc_q_maxsize_reg;

When I told you to change the ioctl arguments to use __u32 instead
of u32, I was only referring to those parts that are in the user-visible
section of the header file. While it does not hurt to use __u32 in
the kernel, you should understand the distinction.

> +/** 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)
> +{
> + static __u32 data;

No need to make data static.

> + __s32 ret_value;
> + __u32 addr_offset = 0;
> + loff_t pos = *ppos;
> +
> + if (pos < 0)
> + return -EINVAL;
> +
> + for (addr_offset = 0; addr_offset < size; addr_offset++) {
> + get_user(data, &buf[addr_offset]);
> +
> + ret_value = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
> + data);
> + if (ret_value)
> + return -EFAULT;

You should return -EFAULT if the get_user() call fails, otherwise you have
a possible security hole. If pch_phub_write_serial_rom fails, the correct
return code would be -EIO.

> +/**
> + * 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,
> +};

If would be good to add a line of
.llseek = default_llseek,
here. I have a patch to change the default llseek method to one
that disallows seeking, so if you add this line, there is one
less driver for me to patch.

Arnd

2010-06-17 23:49:41

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Arnd,

I will modify for your comments by today or tomorrow.

Thanks,
Ohtake

----- Original Message -----
From: "Arnd Bergmann" <[email protected]>
To: "Masayuki Ohtak" <[email protected]>
Cc: "Alan Cox" <[email protected]>; "LKML" <[email protected]>; "Andrew"
<[email protected]>; "Intel OTC" <[email protected]>; "Wang, Qi" <[email protected]>; "Wang, Yong Y"
<[email protected]>
Sent: Thursday, June 17, 2010 8:59 PM
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver


> Looks almost good to me now. Some more details (only the -EFAULT return code
> is a real issue, the other ones are cosmetic changes):
>
> On Thursday 17 June 2010, Masayuki Ohtak wrote:
> > +
> > +#define PCH_READ_REG(a) ioread32(pch_phub_reg.pch_phub_base_address + (a))
> > +
> > +#define PCH_WRITE_REG(a, b) iowrite32((a), pch_phub_reg.pch_phub_base_address +\
> > + (b))
>
> I'd recommend just getting rid of these abstractions. You only use
> them in one or two functions each, so you can just as well add a local
> variable there and do
>
> void __iomem *p = pch_phub_reg.pch_phub_base_address;
> foo = ioread32(p + foo_offset);
> bar = ioread32(p + bar_offset);
> ...
>
> Not really important, but this way it would be more conventional
> without having to write extra code.
>
> > +/*--------------------------------------------
> > + global variables
> > +--------------------------------------------*/
> > +/**
> > + * struct pch_phub_reg_t - 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_t {
>
> It would be better to drop the _t postfix on the type name.
> By convention, we use this only for typedefs on simple data
> types like off_t but not for structures.
>
> > + __u32 phub_id_reg;
> > + __u32 q_pri_val_reg;
> > + __u32 rc_q_maxsize_reg;
>
> When I told you to change the ioctl arguments to use __u32 instead
> of u32, I was only referring to those parts that are in the user-visible
> section of the header file. While it does not hurt to use __u32 in
> the kernel, you should understand the distinction.
>
> > +/** 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)
> > +{
> > + static __u32 data;
>
> No need to make data static.
>
> > + __s32 ret_value;
> > + __u32 addr_offset = 0;
> > + loff_t pos = *ppos;
> > +
> > + if (pos < 0)
> > + return -EINVAL;
> > +
> > + for (addr_offset = 0; addr_offset < size; addr_offset++) {
> > + get_user(data, &buf[addr_offset]);
> > +
> > + ret_value = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
> > + data);
> > + if (ret_value)
> > + return -EFAULT;
>
> You should return -EFAULT if the get_user() call fails, otherwise you have
> a possible security hole. If pch_phub_write_serial_rom fails, the correct
> return code would be -EIO.
>
> > +/**
> > + * 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,
> > +};
>
> If would be good to add a line of
> .llseek = default_llseek,
> here. I have a patch to change the default llseek method to one
> that disallows seeking, so if you add this line, there is one
> less driver for me to patch.
>
> Arnd
>


2010-06-18 08:08:41

by tip-bot for Yong Wang

[permalink] [raw]
Subject: RE: [PATCH] Topcliff PHUB: Generate PacketHub driver

> +config PCH_IEEE1588
> + tristate "PCH IEEE1588"
> + depends on PCI
> + help
> + If you say yes to this option, support will be included for the
> + PCH IEEE1588 Host controller.
> +

Is this really supposed to be part of this patch?

> +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 PCH_CAN_PCLK_50MHZ
> + bool "CAN PCLK 50MHz"
> + depends on PCH_PHUB
> + help
> + If you say yes to this option, clock is set to 50MHz.(For CAN
> control)
> +

Ohtak-san, I remember you said PCH_CAN_PCLK_50MHZ was in the patch
because you used to have a hw bug. If that hw bug has been fixed, this config
option shall also be removed. Otherwise, you have to build multiple kernel
binaries for different hardware configurations as Alan pointed out.

> diff --git a/drivers/char/Makefile b/drivers/char/Makefile
> index f957edf..dc092d0 100644
> --- a/drivers/char/Makefile
> +++ b/drivers/char/Makefile
> @@ -111,6 +111,10 @@ obj-$(CONFIG_PS3_FLASH) += ps3flash.o
> obj-$(CONFIG_JS_RTC) += js-rtc.o
> js-rtc-y = rtc.o
>
> +obj-$(CONFIG_PCH_IEEE1588) += pch_ieee1588/
> +

Ditto

2010-06-18 11:39:15

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Yong Y

>> +config PCH_IEEE1588
>> + tristate "PCH IEEE1588"
>> + depends on PCI
>> + help
>> + If you say yes to this option, support will be included for the
>> + PCH IEEE1588 Host controller.
>> +

>Is this really supposed to be part of this patch?

> +obj-$(CONFIG_PCH_IEEE1588) += pch_ieee1588/
> +

Yes, the above is true.
Our driver under 'drivers/char' has patch order restriction.
Firstly, ieee1588 must be patched.
After the above, Phub is able to be patched.

Thus, our Phub patch includes ieee1588 code.

> Ohtak-san, I remember you said PCH_CAN_PCLK_50MHZ was in the patch
> because you used to have a hw bug. If that hw bug has been fixed, this config
> option shall also be removed. Otherwise, you have to build multiple kernel
> binaries for different hardware configurations as Alan pointed out.
Sorry, I gave wrong information to you.
Our CAN HW spec is that works only 50MHz.(Not HW bug)
According to our CAN hw spec, we will delete 'PCH_CAN_PCLK_50MHZ'.

Thanks,
Ohtake

----- Original Message -----
From: "Wang, Yong Y" <[email protected]>
To: "Masayuki Ohtak" <[email protected]>
Cc: "LKML" <[email protected]>; "Khor, Andrew Chih Howe" <[email protected]>; "Clark, Joel"
<[email protected]>; "Wang, Qi" <[email protected]>
Sent: Friday, June 18, 2010 5:08 PM
Subject: RE: [PATCH] Topcliff PHUB: Generate PacketHub driver


> +config PCH_IEEE1588
> + tristate "PCH IEEE1588"
> + depends on PCI
> + help
> + If you say yes to this option, support will be included for the
> + PCH IEEE1588 Host controller.
> +

Is this really supposed to be part of this patch?

> +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 PCH_CAN_PCLK_50MHZ
> + bool "CAN PCLK 50MHz"
> + depends on PCH_PHUB
> + help
> + If you say yes to this option, clock is set to 50MHz.(For CAN
> control)
> +

Ohtak-san, I remember you said PCH_CAN_PCLK_50MHZ was in the patch
because you used to have a hw bug. If that hw bug has been fixed, this config
option shall also be removed. Otherwise, you have to build multiple kernel
binaries for different hardware configurations as Alan pointed out.

> diff --git a/drivers/char/Makefile b/drivers/char/Makefile
> index f957edf..dc092d0 100644
> --- a/drivers/char/Makefile
> +++ b/drivers/char/Makefile
> @@ -111,6 +111,10 @@ obj-$(CONFIG_PS3_FLASH) += ps3flash.o
> obj-$(CONFIG_JS_RTC) += js-rtc.o
> js-rtc-y = rtc.o
>
> +obj-$(CONFIG_PCH_IEEE1588) += pch_ieee1588/
> +

Ditto

2010-06-22 02:14:48

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Arnd,

Sorry, for late patch releasing.

I have modified like below.
We will release today.

>
> On Thursday 17 June 2010, Masayuki Ohtak wrote:
> > +
> > +#define PCH_READ_REG(a) ioread32(pch_phub_reg.pch_phub_base_address + (a))
> > +
> > +#define PCH_WRITE_REG(a, b) iowrite32((a), pch_phub_reg.pch_phub_base_address +\
> > + (b))
>
> I'd recommend just getting rid of these abstractions. You only use
> them in one or two functions each, so you can just as well add a local
> variable there and do
>
> void __iomem *p = pch_phub_reg.pch_phub_base_address;
> foo = ioread32(p + foo_offset);
> bar = ioread32(p + bar_offset);
> ...
>
> Not really important, but this way it would be more conventional
> without having to write extra code.
Modified like above.


>
> > +/*--------------------------------------------
> > + global variables
> > +--------------------------------------------*/
> > +/**
> > + * struct pch_phub_reg_t - 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_t {
>
> It would be better to drop the _t postfix on the type name.
> By convention, we use this only for typedefs on simple data
> types like off_t but not for structures.
Deleted '_t' prefix

>
> > + __u32 phub_id_reg;
> > + __u32 q_pri_val_reg;
> > + __u32 rc_q_maxsize_reg;
>
> When I told you to change the ioctl arguments to use __u32 instead
> of u32, I was only referring to those parts that are in the user-visible
> section of the header file. While it does not hurt to use __u32 in
> the kernel, you should understand the distinction.
The following types are modified like below except user-visible secton '__u8 mac_addr[6].
__u32 -> unsigned int
__s32 -> int
__u8 -> unsigned char
__s8 -> char


>
> > +/** 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)
> > +{
> > + static __u32 data;
>
> No need to make data static.
Deleted 'static'.

>
> > + __s32 ret_value;
> > + __u32 addr_offset = 0;
> > + loff_t pos = *ppos;
> > +
> > + if (pos < 0)
> > + return -EINVAL;
> > +
> > + for (addr_offset = 0; addr_offset < size; addr_offset++) {
> > + get_user(data, &buf[addr_offset]);
> > +
> > + ret_value = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
> > + data);
> > + if (ret_value)
> > + return -EFAULT;
>
> You should return -EFAULT if the get_user() call fails, otherwise you have
> a possible security hole. If pch_phub_write_serial_rom fails, the correct
> return code would be -EIO.
Modified to care returned value of get_user.
Modified retunrn code of pch_phub_write_serial_rom to -EIO.

>
> > +/**
> > + * 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,
> > +};
>
> If would be good to add a line of
> .llseek = default_llseek,
> here. I have a patch to change the default llseek method to one
> that disallows seeking, so if you add this line, there is one
> less driver for me to patch.
Added a line of '.llseek = default_llseek'.

Thanks,
Ohtake

----- Original Message -----
From: "Masayuki Ohtake" <[email protected]>
To: "Arnd Bergmann" <[email protected]>
Cc: "Wang, Yong Y" <[email protected]>; "Wang, Qi" <[email protected]>; "Intel OTC" <[email protected]>; "Andrew"
<[email protected]>; "LKML" <[email protected]>; "Alan Cox" <[email protected]>
Sent: Friday, June 18, 2010 8:49 AM
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver


> Hi Arnd,
>
> I will modify for your comments by today or tomorrow.
>
> Thanks,
> Ohtake
>
> ----- Original Message -----
> From: "Arnd Bergmann" <[email protected]>
> To: "Masayuki Ohtak" <[email protected]>
> Cc: "Alan Cox" <[email protected]>; "LKML" <[email protected]>; "Andrew"
> <[email protected]>; "Intel OTC" <[email protected]>; "Wang, Qi" <[email protected]>; "Wang, Yong Y"
> <[email protected]>
> Sent: Thursday, June 17, 2010 8:59 PM
> Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver
>
>
> > Looks almost good to me now. Some more details (only the -EFAULT return code
> > is a real issue, the other ones are cosmetic changes):
> >
> > On Thursday 17 June 2010, Masayuki Ohtak wrote:
> > > +
> > > +#define PCH_READ_REG(a) ioread32(pch_phub_reg.pch_phub_base_address + (a))
> > > +
> > > +#define PCH_WRITE_REG(a, b) iowrite32((a), pch_phub_reg.pch_phub_base_address +\
> > > + (b))
> >
> > I'd recommend just getting rid of these abstractions. You only use
> > them in one or two functions each, so you can just as well add a local
> > variable there and do
> >
> > void __iomem *p = pch_phub_reg.pch_phub_base_address;
> > foo = ioread32(p + foo_offset);
> > bar = ioread32(p + bar_offset);
> > ...
> >
> > Not really important, but this way it would be more conventional
> > without having to write extra code.
> >
> > > +/*--------------------------------------------
> > > + global variables
> > > +--------------------------------------------*/
> > > +/**
> > > + * struct pch_phub_reg_t - 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_t {
> >
> > It would be better to drop the _t postfix on the type name.
> > By convention, we use this only for typedefs on simple data
> > types like off_t but not for structures.
> >
> > > + __u32 phub_id_reg;
> > > + __u32 q_pri_val_reg;
> > > + __u32 rc_q_maxsize_reg;
> >
> > When I told you to change the ioctl arguments to use __u32 instead
> > of u32, I was only referring to those parts that are in the user-visible
> > section of the header file. While it does not hurt to use __u32 in
> > the kernel, you should understand the distinction.
> >
> > > +/** 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)
> > > +{
> > > + static __u32 data;
> >
> > No need to make data static.
> >
> > > + __s32 ret_value;
> > > + __u32 addr_offset = 0;
> > > + loff_t pos = *ppos;
> > > +
> > > + if (pos < 0)
> > > + return -EINVAL;
> > > +
> > > + for (addr_offset = 0; addr_offset < size; addr_offset++) {
> > > + get_user(data, &buf[addr_offset]);
> > > +
> > > + ret_value = pch_phub_write_serial_rom(0x80 + addr_offset + pos,
> > > + data);
> > > + if (ret_value)
> > > + return -EFAULT;
> >
> > You should return -EFAULT if the get_user() call fails, otherwise you have
> > a possible security hole. If pch_phub_write_serial_rom fails, the correct
> > return code would be -EIO.
> >
> > > +/**
> > > + * 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,
> > > +};
> >
> > If would be good to add a line of
> > .llseek = default_llseek,
> > here. I have a patch to change the default llseek method to one
> > that disallows seeking, so if you add this line, there is one
> > less driver for me to patch.
> >
> > Arnd
> >
>
>
>

2010-06-22 05:33:25

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi, Arnd and Yong Y

I have released Phub patch file.
Please confirm below.

Thanks, Ohtake.

Signed-off-by: Masayuki Ohtake <[email protected]>
---
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

2010-06-22 10:33:58

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

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 <[email protected]>
---
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

2010-06-22 11:30:42

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

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 <[email protected]>

Acked-by: Arnd Bergmann <[email protected]>

2010-06-22 14:06:41

by Yong Wang

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

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 <[email protected]>
>
> Acked-by: Arnd Bergmann <[email protected]>

Hi Andrew,

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

Thanks
-Yong

2010-06-22 22:12:51

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

On Tue, 22 Jun 2010 19:33:34 +0900
Masayuki Ohtak <[email protected]> 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 <[email protected]>

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

2010-06-23 00:31:40

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

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" <[email protected]>
To: "Masayuki Ohtak" <[email protected]>
Cc: "Arnd Bergmann" <[email protected]>; "Wang, Yong Y" <[email protected]>; <[email protected]>;
<[email protected]>; <[email protected]>; "Alan Cox" <[email protected]>; "LKML"
<[email protected]>
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 <[email protected]> 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 <[email protected]>
>
> 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
>

2010-06-29 23:31:13

by Andy Isaacson

[permalink] [raw]
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
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

2010-06-30 05:58:33

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

Hi Andy

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

----- Original Message -----
From: "Andy Isaacson" <[email protected]>
To: "Masayuki Ohtak" <[email protected]>
Cc: "Arnd Bergmann" <[email protected]>; "Wang, Yong Y" <[email protected]>; "Wang Qi" <[email protected]>; "Intel OTC"
<[email protected]>; "Andrew" <[email protected]>; "Alan Cox" <[email protected]>; "LKML"
<[email protected]>
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
>




2010-06-30 07:52:00

by Masayuki Ohtak

[permalink] [raw]
Subject: [PATCH] Packet hub driver of Topcliff PCH

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 <[email protected]>
Acked-by: Arnd <Arnd$B!G(Bs 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

2010-06-30 18:06:55

by Randy Dunlap

[permalink] [raw]
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’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 <[email protected]>
> 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 ***

2010-06-30 18:28:31

by Andy Isaacson

[permalink] [raw]
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

2010-07-01 04:06:06

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Packet hub driver of Topcliff PCH

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" <[email protected]>
To: "Masayuki Ohtak" <[email protected]>
Cc: "Andy Isaacson" <[email protected]>; "Andrew Morton" <[email protected]>; "Arnd Bergmann" <[email protected]>;
"Wang, Yong Y" <[email protected]>; <[email protected]>; <[email protected]>; <[email protected]>;
"Alan Cox" <[email protected]>; "LKML" <[email protected]>
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 <[email protected]>
> > 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 ***
>

2010-07-01 04:09:05

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Topcliff PHUB: Generate PacketHub driver

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" <[email protected]>
To: "Masayuki Ohtake" <[email protected]>
Cc: "Arnd Bergmann" <[email protected]>; "Wang, Yong Y" <[email protected]>; "Wang Qi" <[email protected]>; "Intel OTC"
<[email protected]>; "Andrew" <[email protected]>; "Alan Cox" <[email protected]>; "LKML"
<[email protected]>
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
>


2010-07-01 05:14:46

by Masayuki Ohtak

[permalink] [raw]
Subject: [PATCH] Packet hub driver of Topcliff PCH

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 <[email protected]>
Acked-by: Arnd Bergmann <[email protected]>

---
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

2010-07-01 06:58:13

by Andy Isaacson

[permalink] [raw]
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

2010-07-01 10:13:28

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Packet hub driver of Topcliff PCH

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" <[email protected]>
To: "Masayuki Ohtak" <[email protected]>
Cc: "Randy Dunlap" <[email protected]>; "Arnd Bergmann" <[email protected]>; "Wang, Yong Y" <[email protected]>;
<[email protected]>; <[email protected]>; <[email protected]>; "Alan Cox" <[email protected]>;
"LKML" <[email protected]>
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
>

2010-07-01 10:39:27

by Masayuki Ohtak

[permalink] [raw]
Subject: [PATCH] Packet hub driver of Topcliff PCH

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 <[email protected]>
Acked-by: Arnd Bergmann <[email protected]>

---
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

2010-07-01 15:46:09

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH] Packet hub driver of Topcliff PCH

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 <[email protected]>
> Acked-by: Arnd Bergmann <[email protected]>
>
> ---
> 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 ***

2010-07-05 07:19:51

by Masayuki Ohtak

[permalink] [raw]
Subject: [PATCH] Packet hub driver of Topcliff PCH

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 <[email protected]>
Acked-by: Arnd Bergmann <[email protected]>

---
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:[email protected]>
0xF4 00-1F video/mbxfb.h mbxfb
<mailto:[email protected]>
+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

2010-07-05 15:04:32

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH] Packet hub driver of Topcliff PCH

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

2010-07-06 06:20:40

by Masayuki Ohtak

[permalink] [raw]
Subject: [PATCH] Packet hub driver of Topcliff PCH

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 <[email protected]>
Acked-by: Arnd Bergmann <[email protected]>
---
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:[email protected]>
0xF4 00-1F video/mbxfb.h mbxfb
<mailto:[email protected]>
+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

2010-07-06 06:30:29

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH] Packet hub driver of Topcliff PCH

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

2010-07-06 15:58:39

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH] Packet hub driver of Topcliff PCH

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 <[email protected]>
> Acked-by: Arnd Bergmann <[email protected]>
>
> ---
> 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:[email protected]>
> 0xF4 00-1F video/mbxfb.h mbxfb
> <mailto:[email protected]>
> +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 ***

2010-07-07 01:34:43

by Yong Wang

[permalink] [raw]
Subject: Re: [PATCH] Packet hub driver of Topcliff PCH

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

2010-07-09 20:01:17

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH] Packet hub driver of Topcliff PCH

On Tue, 06 Jul 2010 15:20:52 +0900
Masayuki Ohtak <[email protected]> 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;
> +}
>
> ...
>

2010-07-12 01:25:19

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] Packet hub driver of Topcliff PCH

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" <[email protected]>
To: "Masayuki Ohtak" <[email protected]>
Cc: "Arnd Bergmann" <[email protected]>; "Wang, Yong Y" <[email protected]>; <[email protected]>;
<[email protected]>; <[email protected]>; "Alan Cox" <[email protected]>; "LKML"
<[email protected]>
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 <[email protected]> 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;
> > +}
> >
> > ...
> >
>


2010-07-15 07:24:53

by Masayuki Ohtak

[permalink] [raw]
Subject: [PATCH] Packet hub driver of Topcliff PCH

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 <[email protected]>
Acked-by: Arnd Bergmann <[email protected]>

---
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:[email protected]>
0xF4 00-1F video/mbxfb.h mbxfb
<mailto:[email protected]>
+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

2010-07-15 07:42:33

by Masayuki Ohtak

[permalink] [raw]
Subject: [PATCH] I2C driver of Topcliff PCH

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 <[email protected]>
---
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

2010-07-15 19:35:30

by Arnd Bergmann

[permalink] [raw]
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

2010-07-20 00:05:27

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] I2C driver of Topcliff PCH

Hi Arnd,

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

Thanks, Ohtake
----- Original Message -----
From: "Arnd Bergmann" <[email protected]>
To: "Masayuki Ohtak" <[email protected]>
Cc: "Jean Delvare (PC drivers, core)" <[email protected]>; "Ben Dooks (embedded platforms)" <[email protected]>;
<[email protected]>; "LKML" <[email protected]>; <[email protected]>; "Wang, Yong Y"
<[email protected]>; <[email protected]>; <[email protected]>
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
>

2010-07-20 04:55:45

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] I2C driver of Topcliff PCH

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" <[email protected]>
To: "Masayuki Ohtak" <[email protected]>
Cc: "Jean Delvare (PC drivers, core)" <[email protected]>; "Ben Dooks (embedded platforms)" <[email protected]>;
<[email protected]>; "LKML" <[email protected]>; <[email protected]>; "Wang, Yong Y"
<[email protected]>; <[email protected]>; <[email protected]>
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
>

2010-07-20 08:19:09

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] I2C driver of Topcliff PCH

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" <[email protected]>
To: "Masayuki Ohtak" <[email protected]>
Cc: "Jean Delvare (PC drivers, core)" <[email protected]>; "Ben Dooks (embedded platforms)" <[email protected]>;
<[email protected]>; "LKML" <[email protected]>; <[email protected]>; "Wang, Yong Y"
<[email protected]>; <[email protected]>; <[email protected]>
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
>

2010-07-20 09:28:29

by Arnd Bergmann

[permalink] [raw]
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

2010-07-20 09:30:07

by Arnd Bergmann

[permalink] [raw]
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

2010-07-20 12:38:30

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] I2C driver of Topcliff PCH

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" <[email protected]>
To: "Masayuki Ohtake" <[email protected]>
Cc: "Jean Delvare (PC drivers, core)" <[email protected]>; "Ben Dooks (embedded platforms)" <[email protected]>;
<[email protected]>; "LKML" <[email protected]>; <[email protected]>; "Wang, Yong Y"
<[email protected]>; <[email protected]>; <[email protected]>
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
>

2010-07-20 12:40:05

by Masayuki Ohtak

[permalink] [raw]
Subject: Re: [PATCH] I2C driver of Topcliff PCH

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" <[email protected]>
To: "Masayuki Ohtake" <[email protected]>
Cc: <[email protected]>; <[email protected]>; "Wang, Yong Y" <[email protected]>;
<[email protected]>; "LKML" <[email protected]>; <[email protected]>; "Ben Dooks (embedded
platforms)" <[email protected]>; "Jean Delvare (PC drivers, core)" <[email protected]>
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
>

2010-07-21 06:46:44

by Masayuki Ohtak

[permalink] [raw]
Subject: [PATCH] I2C driver of Topcliff PCH

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 <[email protected]>

---
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