From: Masayuki Ohtake Subject: OKI Semiconductor PCH I2C driver This driver implements I2C controls for PCH. Signed-off-by: Masayuki Ohtake Acked-by: Wang Qi --- drivers/i2c/busses/Kconfig | 7 + drivers/i2c/busses/Makefile | 3 drivers/i2c/busses/pch_common.h | 146 drivers/i2c/busses/pch_debug.h | 60 drivers/i2c/busses/pch_i2c_hal.c | 1930 drivers/i2c/busses/pch_i2c_hal.h | 337 drivers/i2c/busses/pch_i2c_main.c | 247 drivers/i2c/busses/pch_i2c_pci.c | 583 drivers/i2c/i2c-dev.c | 28 +++++++++++++++++++++++++++++++ 9 files changed, yy insertions(+) diff -urN linux-2.6.33.1/drivers/i2c/busses/Kconfig topcliff-2.6.33.1/drivers/i2c/busses/Kconfig --- linux-2.6.33.1/drivers/i2c/busses/Kconfig 2010-03-16 01:09:39.000000000 +0900 +++ topcliff-2.6.33.1/drivers/i2c/busses/Kconfig 2010-03-23 10:40:18.000000000 +0900 @@ -7,6 +7,13 @@ comment "PC SMBus host controller drivers" depends on PCI +config PCH_I2C + tristate "PCH I2C" + depends on PCI + help + If you say yes to this option, support will be included for the SMB + PCH I2C Host controller. + config I2C_ALI1535 tristate "ALI 1535" depends on PCI diff -urN linux-2.6.33.1/drivers/i2c/busses/Makefile topcliff-2.6.33.1/drivers/i2c/busses/Makefile --- linux-2.6.33.1/drivers/i2c/busses/Makefile 2010-03-16 01:09:39.000000000 +0900 +++ topcliff-2.6.33.1/drivers/i2c/busses/Makefile 2010-03-23 10:40:18.000000000 +0900 @@ -75,3 +75,6 @@ ifeq ($(CONFIG_I2C_DEBUG_BUS),y) EXTRA_CFLAGS += -DDEBUG endif + +obj-$(CONFIG_PCH_I2C) += pch_i2c.o +pch_i2c-objs := pch_i2c_main.o pch_i2c_pci.o pch_i2c_hal.o diff -urN linux-2.6.33.1/drivers/i2c/busses/pch_common.h topcliff-2.6.33.1/drivers/i2c/busses/pch_common.h --- linux-2.6.33.1/drivers/i2c/busses/pch_common.h 1970-01-01 09:00:00.000000000 +0900 +++ topcliff-2.6.33.1/drivers/i2c/busses/pch_common.h 2010-03-23 10:40:18.000000000 +0900 @@ -0,0 +1,146 @@ +/*! + * @file ioh_common.h + * @brief Provides the macro definitions used by all files. + * @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) 2008 OKI SEMICONDUCTOR Co., LTD. + * All rights reserved. + * + * created: + * WIPRO 03/07/2009 + * modified: + * WIPRO 05/08/2009 + * + */ + +#ifndef __IOH_COMMON_H__ +#define __IOH_COMMON_H__ + +/*! @ingroup Global +@def IOH_WRITE8 +@brief Macro for writing 8 bit data to an io/mem address +*/ +#define IOH_WRITE8(val, addr) iowrite8((val), (void __iomem *)(addr)) +/*! @ingroup Global +@def IOH_LOG +@brief Macro for writing 16 bit data to an io/mem address +*/ +#define IOH_WRITE16(val, addr) iowrite16((val), (void __iomem *)(addr)) +/*! @ingroup Global +@def IOH_LOG +@brief Macro for writing 32 bit data to an io/mem address +*/ +#define IOH_WRITE32(val, addr) iowrite32((val), (void __iomem *)(addr)) + +/*! @ingroup Global +@def IOH_READ8 +@brief Macro for reading 8 bit data from an io/mem address +*/ +#define IOH_READ8(addr) ioread8((void __iomem *)(addr)) +/*! @ingroup Global +@def IOH_READ16 +@brief Macro for reading 16 bit data from an io/mem address +*/ +#define IOH_READ16(addr) ioread16((void __iomem *)(addr)) +/*! @ingroup Global +@def IOH_READ32 +@brief Macro for reading 32 bit data from an io/mem address +*/ +#define IOH_READ32(addr) ioread32((void __iomem *)(addr)) +/*! @ingroup Global +@def IOH_WRITE32_F +@brief Macro for writing 32 bit data to an io/mem address +*/ +#define IOH_WRITE32_F(val, addr) do \ + { IOH_WRITE32((val), (addr)); (void)IOH_READ32((addr)); } while (0); + +/*! @ingroup Global +@def IOH_WRITE_BYTE +@brief Macro for writing 1 byte data to an io/mem address +*/ +#define IOH_WRITE_BYTE IOH_WRITE8 +/*! @ingroup Global +@def IOH_WRITE_WORD +@brief Macro for writing 1 word data to an io/mem address +*/ +#define IOH_WRITE_WORD IOH_WRITE16 +/*! @ingroup Global +@def IOH_WRITE_LONG +@brief Macro for writing long data to an io/mem address +*/ +#define IOH_WRITE_LONG IOH_WRITE32 + +/*! @ingroup Global +@def IOH_READ_BYTE +@brief Macro for reading 1 byte data from an io/mem address +*/ +#define IOH_READ_BYTE IOH_READ8 +/*! @ingroup Global +@def IOH_READ_WORD +@brief Macro for reading 1 word data from an io/mem address +*/ +#define IOH_READ_WORD IOH_READ16 +/*! @ingroup Global +@def IOH_READ_LONG +@brief Macro for reading long data from an io/mem address +*/ +#define IOH_READ_LONG IOH_READ32 + +/* Bit Manipulation Macros */ + +/*! @ingroup Global +@def IOH_READ_LONG +@brief macro to set a specified bit(mask) at the + specified address +*/ +#define IOH_SET_ADDR_BIT(addr, bitmask) IOH_WRITE_LONG((IOH_READ_LONG(addr) |\ + (bitmask)), (addr)) + +/*! @ingroup Global +@def IOH_READ_LONG +@brief macro to clear a specified bit(mask) at the specified address +*/ +#define IOH_CLR_ADDR_BIT(addr, bitmask) IOH_WRITE_LONG((IOH_READ_LONG(addr) &\ + ~(bitmask)), (addr)) + +/*! @ingroup Global +@def IOH_READ_LONG +@brief macro to set a specified bitmask for a variable +*/ +#define IOH_SET_BITMSK(var, bitmask) ((var) |= (bitmask)) + +/*! @ingroup Global +@def IOH_READ_LONG +@brief macro to clear a specified bitmask for a variable +*/ +#define IOH_CLR_BITMSK(var, bitmask) ((var) &= (~(bitmask))) + +/*! @ingroup Global +@def IOH_READ_LONG +@brief macro to set a specified bit for a variable +*/ +#define IOH_SET_BIT(var, bit) ((var) |= (1<<(bit))) + +/*! @ingroup Global +@def IOH_READ_LONG +@brief macro to clear a specified bit for a variable +*/ +#define IOH_CLR_BIT(var, bit) ((var) &= ~(1<<(bit))) + +#endif diff -urN linux-2.6.33.1/drivers/i2c/busses/pch_debug.h topcliff-2.6.33.1/drivers/i2c/busses/pch_debug.h --- linux-2.6.33.1/drivers/i2c/busses/pch_debug.h 1970-01-01 09:00:00.000000000 +0900 +++ topcliff-2.6.33.1/drivers/i2c/busses/pch_debug.h 2010-03-23 10:40:18.000000000 +0900 @@ -0,0 +1,60 @@ +/*! + * @file ioh_debug.h + * @brief Provides the macro definitions used for debugging. + * @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) 2008 OKI SEMICONDUCTOR Co., LTD. + * All rights reserved. + * + * created: + * WIPRO 03/07/2009 + * modified: + * WIPRO 05/08/2009 + * + */ + +#ifndef __IOH_DEBUG_H__ +#define __IOH_DEBUG_H__ + +#ifdef MODULE +#define IOH_LOG(level, fmt, args...) printk(level "%s:" fmt "\n",\ + THIS_MODULE->name, ##args) +#else +#define IOH_LOG(level, fmt, args...) printk(level "%s:" fmt "\n" ,\ + __FILE__, ##args) +#endif + + +#ifdef DEBUG + #define IOH_DEBUG(fmt, args...) IOH_LOG(KERN_DEBUG, fmt, ##args) +#else + #define IOH_DEBUG(fmt, args...) +#endif + +#ifdef IOH_TRACE_ENABLED + #define IOH_TRACE IOH_DEBUG +#else + #define IOH_TRACE(fmt, args...) +#endif + +#define IOH_TRACE_ENTER IOH_TRACE("Enter %s", __func__) +#define IOH_TRACE_EXIT IOH_TRACE("Exit %s", __func__) + + +#endif diff -urN linux-2.6.33.1/drivers/i2c/busses/pch_i2c_hal.c topcliff-2.6.33.1/drivers/i2c/busses/pch_i2c_hal.c --- linux-2.6.33.1/drivers/i2c/busses/pch_i2c_hal.c 1970-01-01 09:00:00.000000000 +0900 +++ topcliff-2.6.33.1/drivers/i2c/busses/pch_i2c_hal.c 2010-03-23 10:40:18.000000000 +0900 @@ -0,0 +1,1930 @@ +/*! +* @file ioh_i2c_hal.c +* @brief This file contains definitions of HAL Layer APIs and +* Internal functions +* @version 0.95 +* @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) 2008 OKI SEMICONDUCTOR Co., LTD. +* All rights reserved. +* +* created: +* WIPRO 02/20/2009 +* modified: +* WIPRO 05/21/2009 +* +*/ + +/*includes*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pch_i2c_hal.h" +#include "pch_common.h" +#include "pch_debug.h" + +/** + *macro definition + */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CSADR +@brief I2CSADR register offset +*/ +#define IOH_I2CSADR (0x00) /* I2C slave address register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CCTL +@brief I2CCTL register offset +*/ +#define IOH_I2CCTL (0x04) /* I2C control register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CSR +@brief I2CSR register offset +*/ +#define IOH_I2CSR (0x08) /* I2C status register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CDR +@brief I2CDR register offset +*/ +#define IOH_I2CDR (0x0C) /* I2C data register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CMON +@brief I2CMON register offset +*/ +#define IOH_I2CMON (0x10) /* I2C bus monitor register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CBC +@brief I2CBC register offset +*/ +#define IOH_I2CBC (0x14) /* I2C bus transfer rate setup counter */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CMOD +@brief I2CMOD register offset +*/ +#define IOH_I2CMOD (0x18) /* I2C mode register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CBUFSLV +@brief I2CBUFSLV register offset +*/ +#define IOH_I2CBUFSLV (0x1C) /* I2C buffer mode slave address register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CBUFSUB +@brief I2CBUFSUB register offset +*/ +#define IOH_I2CBUFSUB (0x20) /* I2C buffer mode subaddress register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CBUFFOR +@brief I2CBUFFOR register offset +*/ +#define IOH_I2CBUFFOR (0x24) /* I2C buffer mode format register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CBUFCTL +@brief I2CBUFCTL register offset +*/ +#define IOH_I2CBUFCTL (0x28) /* I2C buffer mode control register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CBUFMSK +@brief I2CBUFMSK register offset +*/ +#define IOH_I2CBUFMSK (0x2C) /* I2C buffer mode interrupt mask register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CBUFSTA +@brief I2CBUFSTA register offset +*/ +#define IOH_I2CBUFSTA (0x30) /* I2C buffer mode status register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CBUFLEV +@brief I2CBUFLEV register offset +*/ +#define IOH_I2CBUFLEV (0x34) /* I2C buffer mode level register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CESRFOR +@brief I2CESRFOR register offset +*/ +#define IOH_I2CESRFOR (0x38) /* EEPROM software reset mode format register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CESRCTL +@brief I2CESRCTL register offset +*/ +#define IOH_I2CESRCTL (0x3C) /* EEPROM software reset mode control register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CESRMSK +@brief I2CESRMSK register offset +*/ +#define IOH_I2CESRMSK (0x40) /* EEPROM software reset mode + * interrupt mask register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CESRSTA +@brief I2CESRSTA register offset +*/ +#define IOH_I2CESRSTA (0x44) /* EEPROM software reset mode status register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CTMR +@brief I2CTMR register offset +*/ +#define IOH_I2CTMR (0x48) /* I2C timer register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CSRST +@brief I2CSRST register offset +*/ +#define IOH_I2CSRST (0xFC) /* I2C reset register */ + +/*! @ingroup I2C_HALLayer +@def IOH_I2CNF +@brief I2CNF register offset +*/ +#define IOH_I2CNF (0xF8) /* I2C noise filter register */ + +/*! @ingroup I2C_HALLayer +@def BUS_IDLE_TIMEOUT +@brief Time out value when waiting for Bus Idle +*/ +#define BUS_IDLE_TIMEOUT (20) + +/*! @ingroup I2C_HALLayer +@def IOH_I2CCTL_I2CMEN +@brief Bitmask to enable I2CMEN bit +*/ +#define IOH_I2CCTL_I2CMEN (0x0080) + +/*! @ingroup I2C_HALLayer +@def TEN_BIT_ADDR_DEFAULT +@brief Default bits to be added for 10 bit addressing +*/ +#define TEN_BIT_ADDR_DEFAULT (0xF000) + +/*! @ingroup I2C_HALLayer +@def TEN_BIT_ADDR_MASK +@brief 10 bit address mask +*/ +#define TEN_BIT_ADDR_MASK (0xF0) + +/*! @ingroup I2C_HALLayer +@def IOH_START +@brief Set the start bit in Normal mode +*/ +#define IOH_START (0x0020) + +/*! @ingroup I2C_HALLayer +@def IOH_ESR_START +@brief Bitmask to set Start bit in EEPROM Software Reset mode +*/ +#define IOH_ESR_START (0x0001) + +/*! @ingroup I2C_HALLayer +@def IOH_BUFF_START +@brief Bitmask to set Start bit in Buffer mode +*/ +#define IOH_BUFF_START (0x1) + +/*! @ingroup I2C_HALLayer +@def IOH_REPSTART +@brief Bitmask to set repeated start bit +*/ +#define IOH_REPSTART (0x0004) + +/*! @ingroup I2C_HALLayer +@def IOH_ACK +@brief Ack bit position in I2CCTL register +*/ +#define IOH_ACK (0x0008) + +/*! @ingroup I2C_HALLayer +@def IOH_GETACK +@brief Mask to extract the ack bit +*/ +#define IOH_GETACK (0x0001) + +/*! @ingroup I2C_HALLayer +@def CLR_REG +@brief Mask for register reset +*/ +#define CLR_REG (0x0) +/*! @ingroup I2C_HALLayer +@def I2C_RD +@brief Set read bit in I2CDR with slave address +*/ +#define I2C_RD (0x1) + +/*! @ingroup I2C_HALLayer +@def I2CMCF_BIT +@brief Mask for I2CMCF bit +*/ +#define I2CMCF_BIT (0x0080) + +/*! @ingroup I2C_HALLayer +@def I2CMIF_BIT +@brief Mask for I2CMIF bit +*/ +#define I2CMIF_BIT (0x0002) + +/*! @ingroup I2C_HALLayer +@def I2CMAL_BIT +@brief Mask for I2CMAL bit +*/ +#define I2CMAL_BIT (0x0010) + +/*! @ingroup I2C_HALLayer +@def I2CBMFI_BIT +@brief Mask for I2CBMFI bit +*/ +#define I2CBMFI_BIT (0x0001) + +/*! @ingroup I2C_HALLayer +@def I2CBMAL_BIT +@brief Mask for I2CBMAL bit +*/ +#define I2CBMAL_BIT (0x0002) + +/*! @ingroup I2C_HALLayer +@def I2CBMNA_BIT +@brief Mask for I2CBMNA bit +*/ +#define I2CBMNA_BIT (0x0004) + +/*! @ingroup I2C_HALLayer +@def I2CBMTO_BIT +@brief Mask for I2CBMTO bit +*/ +#define I2CBMTO_BIT (0x0008) + +/*! @ingroup I2C_HALLayer +@def I2CBMIS_BIT +@brief Mask for I2CBMIS bit +*/ +#define I2CBMIS_BIT (0x0010) + +/*! @ingroup I2C_HALLayer +@def I2CESRFI_BIT +@brief Mask for I2CESRFI bit +*/ +#define I2CESRFI_BIT (0X0001) + +/*! @ingroup I2C_HALLayer +@def I2CESRTO_BIT +@brief Mask for I2CESRTO bit +*/ +#define I2CESRTO_BIT (0x0002) + +/*! @ingroup I2C_HALLayer +@def I2CESRFIIE_BIT +@brief Mask for I2CESRFIIE bit +*/ +#define I2CESRFIIE_BIT (0x1) + +/*! @ingroup I2C_HALLayer +@def I2CESRTOIE_BIT +@brief Mask for I2CESRTOIE bit +*/ +#define I2CESRTOIE_BIT (0x2) + +/*! @ingroup I2C_HALLayer +@def I2CBMDZ_BIT +@brief Mask for I2CBMDZ bit +*/ +#define I2CBMDZ_BIT (0x0040) + +/*! @ingroup I2C_HALLayer +@def I2CBMAG_BIT +@brief Mask for I2CBMAG bit +*/ +#define I2CBMAG_BIT (0x0020) + +/*! @ingroup I2C_HALLayer +@def I2CMBB_BIT +@brief Mask for I2CMBB bit +*/ +#define I2CMBB_BIT (0x0020) + +/*! @ingroup I2C_HALLayer +@def BUFFER_MODE_MASK +@brief Status bit mask in buffer mode +*/ +#define BUFFER_MODE_MASK (I2CBMFI_BIT | I2CBMAL_BIT | I2CBMNA_BIT | \ + I2CBMTO_BIT | I2CBMIS_BIT) + +/*! @ingroup I2C_HALLayer +@def I2C_ADDR_MSK +@brief Mask to get the 8 LSB bits in 10 bit addressing +*/ +#define I2C_ADDR_MSK (0xFF) + +/*! @ingroup I2C_HALLayer +@def I2C_MSB_2B_MSK +@brief Mask to get the 2 MSB bits in 10 bit addressing +*/ +#define I2C_MSB_2B_MSK (0x300) + +/*! @ingroup I2C_HALLayer +@def FAST_MODE_CLK +@brief Fast mode clock in KHz +*/ +#define FAST_MODE_CLK (400) + +/*! @ingroup I2C_HALLayer +@def FAST_MODE_EN +@brief Enable the fast mode +*/ +#define FAST_MODE_EN (0x0001) + +/*! @ingroup I2C_HALLayer +@def SUB_ADDR_LEN_MAX +@brief Maximum sub address length +*/ +#define SUB_ADDR_LEN_MAX (4) + +/*! @ingroup I2C_HALLayer +@def BUF_LEN_MAX +@brief Maximum buffer length in buffer mode +*/ +#define BUF_LEN_MAX (32) + +/*! @ingroup I2C_HALLayer +@def IOH_BUFFER_MODE +@brief To enable the buffer mode +*/ +#define IOH_BUFFER_MODE (0x1) + +/*! @ingroup I2C_HALLayer +@def EEPROM_SW_RST_MODE +@brief Mask to enable the EEPROM Software Reset mode +*/ +#define EEPROM_SW_RST_MODE (0x0002) + +/*! @ingroup I2C_HALLayer +@def NORMAL_INTR_ENBL +@brief Mask to enable the I2C interrupts in normal mode +*/ +#define NORMAL_INTR_ENBL (0x0300) + +/*! @ingroup I2C_HALLayer +@def EEPROM_RST_INTR_ENBL +@brief Mask to enable I2CESRFI, I2CESRTO interrupts + in EEPROM Software Reset mode +*/ +#define EEPROM_RST_INTR_ENBL (I2CESRFIIE_BIT | I2CESRTOIE_BIT) + +/*! @ingroup I2C_HALLayer +@def EEPROM_RST_INTR_DISBL +@brief Mask to disable interrupts in EEPROM Software Reset mode +*/ +#define EEPROM_RST_INTR_DISBL (0x0) + +/*! @ingroup I2C_HALLayer +@def BUFFER_MODE_INTR_ENBL +@brief Mask to enable I2CBMIS,I2CBMTO,I2CBMNA,I2CBMAL,I2CBMFI + interrupts in Buffer mode +*/ +#define BUFFER_MODE_INTR_ENBL (0x001F) + +/*! @ingroup I2C_HALLayer +@def BUFFER_MODE_INTR_DISBL +@brief Mask to disable all interrupts in Buffer mode +*/ +#define BUFFER_MODE_INTR_DISBL (0x0) + +/*! @ingroup I2C_HALLayer +@def NORMAL_MODE +@brief Specifies Normal mode +*/ +#define NORMAL_MODE (0x0) + +/*! @ingroup I2C_HALLayer +@def BUFFER_MODE +@brief Specifies Buffer mode +*/ +#define BUFFER_MODE (0x1) + +/*! @ingroup I2C_HALLayer +@def EEPROM_SR_MODE +@brief Specifies EEPROM software reset mode +*/ +#define EEPROM_SR_MODE (0x2) + +/*! @ingroup I2C_HALLayer +@def I2C_TX_MODE +@brief Specifies Master transmission mode +*/ +#define I2C_TX_MODE (0x0010) + +/*! @ingroup I2C_HALLayer +@def IOH_BUF_TX +@brief Specifies Buffer transmission mode +*/ +#define IOH_BUF_TX (0xFFF7) + +/*! @ingroup I2C_HALLayer +@def IOH_BUF_RD +@brief Specifies Buffer reception mode +*/ +#define IOH_BUF_RD (0x0008) + +/*! @ingroup I2C_HALLayer +@def I2C_ERROR_MASK +@brief Mask for errors in all modes +*/ +#define I2C_ERROR_MASK (I2CESRTO_EVENT | I2CBMIS_EVENT | I2CBMTO_EVENT | \ + I2CBMNA_EVENT | I2CBMAL_EVENT | I2CMAL_EVENT) + +/*! @ingroup I2C_HALLayer +@def I2CMAL_EVENT +@brief MAL bit position in event flag +*/ +#define I2CMAL_EVENT (0x0001) + +/*! @ingroup I2C_HALLayer +@def I2CMCF_EVENT +@brief MCF bit position in event flag +*/ +#define I2CMCF_EVENT (0x0002) + +/*! @ingroup I2C_HALLayer +@def I2CBMFI_EVENT +@brief I2CBMFI bit position in event flag +*/ +#define I2CBMFI_EVENT (0x0004) + +/*! @ingroup I2C_HALLayer +@def I2CBMAL_EVENT +@brief I2CBMAL bit position in event flag +*/ +#define I2CBMAL_EVENT (0x0008) + +/*! @ingroup I2C_HALLayer +@def I2CBMNA_EVENT +@brief I2CBMNA bit position in event flag +*/ +#define I2CBMNA_EVENT (0x0010) + +/*! @ingroup I2C_HALLayer +@def I2CBMTO_EVENT +@brief I2CBMTO bit position in event flag +*/ +#define I2CBMTO_EVENT (0x0020) + +/*! @ingroup I2C_HALLayer +@def I2CBMIS_EVENT +@brief I2CBMIS bit position in event flag +*/ +#define I2CBMIS_EVENT (0x0040) + +/*! @ingroup I2C_HALLayer +@def I2CESRFI_EVENT +@brief I2CESRFI bit position in event flag +*/ +#define I2CESRFI_EVENT (0x0080) + +/*! @ingroup I2C_HALLayer +@def I2CESRTO_EVENT +@brief I2CESRTO bit position in event flag +*/ +#define I2CESRTO_EVENT (0x0100) + +/* + * wait queue head + */ + +/*! @ingroup I2C_UtilitiesAPI +@var ioh_i2c_event +@brief Wait queue head +@remarks This global variable is used to synchronize + data handling with interrupts +@see - ioh_i2c_init + - ioh_i2c_cb +*/ +static wait_queue_head_t ioh_i2c_event; + +/* Function prototypes */ + +/*! @ingroup I2C_UtilitiesAPI +@fn ioh_i2c_start(struct i2c_algo_ioh_data * adap) +@brief Function to generate start condition in normal mode +*/ +static void ioh_i2c_start(struct i2c_algo_ioh_data *adap); + +/*! @ingroup I2C_UtilitiesAPI +@fn ioh_i2c_buff_mode_start(struct i2c_algo_ioh_data * adap) +@brief Function to generate start condition in buffer mode +*/ +static void ioh_i2c_buff_mode_start(struct i2c_algo_ioh_data *adap); + +/*! @ingroup I2C_UtilitiesAPI +@fn ioh_i2c_eeprom_swrst_start(struct i2c_algo_ioh_data * adap) +@brief Function to generate start condition in EEPROM Software + Reset mode +*/ +static void ioh_i2c_eeprom_swrst_start(struct i2c_algo_ioh_data *adap); + +/*! @ingroup I2C_UtilitiesAPI +@fn ioh_i2c_stop(struct i2c_algo_ioh_data *adap) +@brief Function to generate stop condition in normal mode +*/ +static void ioh_i2c_stop(struct i2c_algo_ioh_data *adap); + +/*! @ingroup I2C_UtilitiesAPI +@fn ioh_i2c_repstart(struct i2c_algo_ioh_data *adap) +@brief Function to generate repeated start condition in normal mode +*/ +static void ioh_i2c_repstart(struct i2c_algo_ioh_data *adap); + +/*! @ingroup I2C_UtilitiesAPI +@fn ioh_i2c_getack(struct i2c_algo_ioh_data *adap) +@brief Function to confirm ACK/NACK +*/ +static s32 ioh_i2c_getack(struct i2c_algo_ioh_data *adap); + +/*! @ingroup I2C_UtilitiesAPI +@fn ioh_i2c_sendack(struct i2c_algo_ioh_data *adap) +@brief Function to send ACK +*/ +static void ioh_i2c_sendack(struct i2c_algo_ioh_data *adap); + +/*! @ingroup I2C_UtilitiesAPI +@fn ioh_i2c_sendnack(struct i2c_algo_ioh_data *adap) +@brief Function to send NACK +*/ +static void ioh_i2c_sendnack(struct i2c_algo_ioh_data *adap); + +/*! @ingroup I2C_UtilitiesAPI +@fn ioh_i2c_wait_for_bus_idle + (struct i2c_algo_ioh_data *adap,s32 timeout) +@brief Function to check the status of bus +*/ +static s32 ioh_i2c_wait_for_bus_idle(struct i2c_algo_ioh_data *adap, + s32 timeout); + +/*! @ingroup I2C_UtilitiesAPI +@fn ioh_i2c_wait_for_xfer_complete(struct i2c_algo_ioh_data *adap) +@brief Function to wait till transfer complete. +*/ +static s32 ioh_i2c_wait_for_xfer_complete(struct i2c_algo_ioh_data *adap); + +/*! @ingroup I2C_HALLayerAPI + @fn ioh_i2c_init(struct i2c_algo_ioh_data * adap) + @remarks Implements the hardware initialization of I2C module. + The main tasks performed by this method are: + - Clear I2CCTL,I2CMOD,I2CBUFFOR,I2CBUFSLV,I2CBUFSUB,I2CBUFMSK, + I2CESRFOR,I2CESRMSK registers. + - Set I2CMEN in I2CCTL to 1. + - Set bus speed based on module parameter. + - Enable required interrupts. + - Initialize wait queue head. + @note This function always returns @ref IOH_I2C_SUCCESS + @param adap [@ref IN] Contains reference to struct i2c_algo_ioh_data + @retval s32 + - @ref IOH_I2C_SUCCESS Function returns successfully. + @see - ioh_i2c_probe + - ioh_i2c_resume +
+ */ +s32 ioh_i2c_init(struct i2c_algo_ioh_data *adap) +{ + u32 ioh_i2cbc; + u32 ioh_i2ctmr; + u32 reg_value = 0; + +#ifndef FPGA + /*reset I2C controller */ + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CSRST, 0x1); + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CSRST, 0x0); +#endif + /* Initialize I2C registers */ + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CCTL, CLR_REG); + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CMOD, CLR_REG); + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFFOR, CLR_REG); + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFSLV, CLR_REG); + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFSUB, CLR_REG); + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFMSK, CLR_REG); + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CESRFOR, CLR_REG); + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CESRMSK, CLR_REG); + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CNF, 0x21); + IOH_DEBUG + ("Cleared the registers IOH_I2CCTL,IOH_I2CMOD,IOH_I2CBUFFOR\n," + "IOH_I2CBUFSLV,IOH_I2CBUFSUB,IOH_I2CBUFMSK," + "\nIOH_I2CESRFOR,IOH_I2CESRMSK\n"); + + reg_value |= IOH_I2CCTL_I2CMEN; + adap->set_reg_bit((adap->ioh_i2c_base_address), IOH_I2CCTL, + IOH_I2CCTL_I2CMEN); + + ioh_i2c_speed = (ioh_i2c_speed == 400) ? 400 : 100; + + if (ioh_i2c_speed == FAST_MODE_CLK) { + reg_value |= FAST_MODE_EN; + IOH_DEBUG("Fast mode enabled\n"); + } + + ioh_i2c_clk = (ioh_i2c_clk <= 0 + || ioh_i2c_clk > IOH_I2C_MAX_CLK) ? 62500 : ioh_i2c_clk; + + ioh_i2cbc = ((ioh_i2c_clk) + (ioh_i2c_speed * 4)) / (ioh_i2c_speed * 8); + /* Set transfer speed in I2CBC */ + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBC, ioh_i2cbc); + + ioh_i2ctmr = (ioh_i2c_clk) / 8; + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CTMR, ioh_i2ctmr); + + reg_value |= NORMAL_INTR_ENBL; /* Enable interrupts in normal mode */ + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CCTL, reg_value); + + IOH_DEBUG("In ioh_i2c_init: I2CCTL =%x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CCTL))); + IOH_DEBUG("In ioh_i2c_init: ioh_i2cbc =%x\n", ioh_i2cbc); + IOH_DEBUG("In ioh_i2c_init: ioh_i2ctmr =%x\n", ioh_i2ctmr); + + IOH_DEBUG("Enable interrupts\n"); + init_waitqueue_head(&ioh_i2c_event); + return IOH_I2C_SUCCESS; +} + +/*! @ingroup I2C_HALLayerAPI + @fn ioh_i2c_writebytes(struct i2c_adapter *i2c_adap , + struct i2c_msg *msgs, u32 last, u32 first) + @remarks Function to write data to I2C bus in normal mode. + The main tasks performed by this method are: + - Enable transmission mode. + - Send out the slave address. + - Wait for Bus idle and send out Start signal + - Perform data write operation. + - Send stop or repeat start as necessary, depending on whether + the current message is the last message or not. + - Return with number of bytes transferred successfully or + the error code + @param i2c_adap [@ref IN] contains reference to the struct i2c_adapter + @param msgs [@ref IN] contains reference to i2c_msg structure + @param last [@ref IN] specifies whether last message or not + In the case of compound mode it will be + 1 for last message, otherwise 0. + @param first [@ref IN] specifies whether first message or not + 1 for first message otherwise 0. + @retval s32 + - Number of bytes transferred successfully + - @ref IOH_I2C_FAIL @ref ioh_i2c_wait_for_bus_idle, + @ref ioh_i2c_wait_for_xfer_complete, + @ref ioh_i2c_getack fails + - -ERESTARTSYS + @ref ioh_i2c_wait_for_xfer_complete was interrupted by a signal + @see ioh_i2c_xfer +
+ */ +s32 ioh_i2c_writebytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, + u32 last, u32 first) +{ + + struct i2c_algo_ioh_data *adap = i2c_adap->algo_data; + + u8 *buf; + u32 length; + u32 addr; + u32 addr_2_msb; + u32 addr_8_lsb; + s32 wrcount = IOH_I2C_FAIL; + length = msgs->len; + buf = msgs->buf; + addr = msgs->addr; + /* enable master tx */ + adap->set_reg_bit((adap->ioh_i2c_base_address), IOH_I2CCTL, + I2C_TX_MODE); + + IOH_DEBUG("In ioh_i2c_writebytes : I2CCTL = %x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CCTL))); + IOH_DEBUG("In ioh_i2c_writebytes : msgs->len = %d\n", length); + + if (first) { + if (ioh_i2c_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT) == + IOH_I2C_FAIL) { + return IOH_I2C_FAIL; + } + } + + if ((msgs->flags & I2C_M_TEN) != false) { + addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7); + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CDR, + (addr_2_msb | TEN_BIT_ADDR_MASK)); + + if (first) + ioh_i2c_start(adap); + if ((ioh_i2c_wait_for_xfer_complete(adap) == IOH_I2C_SUCCESS) && + (ioh_i2c_getack(adap) == IOH_I2C_SUCCESS)) { + addr_8_lsb = (addr & I2C_ADDR_MSK); + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CDR, + (addr_8_lsb)); + + } else { + ioh_i2c_stop(adap); + return IOH_I2C_FAIL; + } + } else { + /* set 7 bit slave address and R/W bit as 0 */ + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CDR, + ((addr) << 1)); + if (first) + ioh_i2c_start(adap); + } + + if ((ioh_i2c_wait_for_xfer_complete(adap) == IOH_I2C_SUCCESS) && + (ioh_i2c_getack(adap) == IOH_I2C_SUCCESS)) { + for (wrcount = 0; wrcount < length; ++wrcount) { + /* write buffer value to I2C data register */ + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CDR, + buf[wrcount]); + IOH_DEBUG + ("ioh_i2c_writebytes : writing %x to Data register\n", + buf[wrcount]); + + if (ioh_i2c_wait_for_xfer_complete(adap) != + IOH_I2C_SUCCESS) { + wrcount = IOH_I2C_FAIL; + break; + } + + IOH_DEBUG("ioh_i2c_wait_for_xfer_complete return %d", + IOH_I2C_SUCCESS); + + if (ioh_i2c_getack(adap)) { + wrcount = IOH_I2C_FAIL; + break; + } + } + + /* check if this is the last message */ + if (last) + ioh_i2c_stop(adap); + else + ioh_i2c_repstart(adap); + } else { + ioh_i2c_stop(adap); + } + + IOH_DEBUG(KERN_INFO, "ioh_i2c_writebytes return=%d\n", wrcount); + + return wrcount; +} + +/*! @ingroup I2C_HALLayerAPI + @fn ioh_i2c_readbytes(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, u32 last, u32 first) + @remarks Function to read data from I2C bus in normal mode. + The main tasks performed by this method are: + - Enable Reception mode. + - Send out the slave address. + - Wait for Bus idle and send out Start signal + - Perform data reads. + - Send stop or repeat start as necessary, depending on whether + the current + message read is the last message or not + - Return with number of bytes read (if successful) or + the error code + @param i2c_adap [@ref IN] contains reference to the struct i2c_adapter + @param msgs [@ref INOUT] contains reference to i2c_msg structure + @param last [@ref IN] specifies whether last message or not + @param first [@ref IN] specifies whether first message or not + @retval s32 - Number of Bytes read successfully + - @ref IOH_I2C_FAIL @ref ioh_i2c_wait_for_bus_idle, + @ref ioh_i2c_wait_for_xfer_complete, + @ref ioh_i2c_getack fails + - -ERESTARTSYS + @ref ioh_i2c_wait_for_xfer_complete was interrupted by a signal + @see ioh_i2c_xfer +
+ */ +s32 ioh_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, + u32 last, u32 first) +{ + + struct i2c_algo_ioh_data *adap = i2c_adap->algo_data; + + u8 *buf; + u32 count = IOH_I2C_FAIL; + u32 length; + u32 addr; + u32 addr_2_msb; + length = msgs->len; + buf = msgs->buf; + addr = msgs->addr; + + /* enable master reception */ + adap->clr_reg_bit((adap->ioh_i2c_base_address), IOH_I2CCTL, + I2C_TX_MODE); + + if (first) { + if (ioh_i2c_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT) == + IOH_I2C_FAIL) { + return IOH_I2C_FAIL; + } + } + + if ((msgs->flags & I2C_M_TEN) != false) { + addr_2_msb = (((addr & I2C_MSB_2B_MSK) >> 7) | (I2C_RD)); + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CDR, + (addr_2_msb | TEN_BIT_ADDR_MASK)); + + } else { + /* 7 address bits + R/W bit */ + addr = (((addr) << 1) | (I2C_RD)); + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CDR, addr); + } + + /* check if it is the first message */ + if (first == true) + ioh_i2c_start(adap); + + if ((ioh_i2c_wait_for_xfer_complete(adap) == IOH_I2C_SUCCESS) + && (ioh_i2c_getack(adap) == IOH_I2C_SUCCESS)) { + IOH_DEBUG("ioh_i2c_wait_for_xfer_complete return %d", + IOH_I2C_SUCCESS); + + if (length == 0) { + + ioh_i2c_stop(adap); + (void)adap->readreg((adap->ioh_i2c_base_address), + IOH_I2CDR); + + count = length; + } else { + int read_index = 0; + int loop; + ioh_i2c_sendack(adap); + + /* Dummy read */ + + for (loop = 1; loop < length; loop++) { + buf[read_index] = + adap->readreg((adap->ioh_i2c_base_address), + IOH_I2CDR); + + if (loop != 1) + read_index++; + + if (ioh_i2c_wait_for_xfer_complete(adap) != + IOH_I2C_SUCCESS) { + ioh_i2c_stop(adap); + return IOH_I2C_FAIL; + } + + } /* end for */ + + ioh_i2c_sendnack(adap); + + buf[read_index] = + adap->readreg((adap->ioh_i2c_base_address), + IOH_I2CDR); + + if (length != 1) + read_index++; + + if (ioh_i2c_wait_for_xfer_complete(adap) == + IOH_I2C_SUCCESS) { + if (last) + ioh_i2c_stop(adap); + else + ioh_i2c_repstart(adap); + + buf[read_index++] = + adap->readreg((adap->ioh_i2c_base_address), + IOH_I2CDR); + count = read_index; + } + + } + } else { + ioh_i2c_stop(adap); + } + + return count; +} + +/*! @ingroup I2C_HALLayerAPI + @fn ioh_i2c_entcb(s32(*ioh_i2c_ptr)(struct i2c_algo_ioh_data *adap)) + @remarks Function to register call back function. + The main tasks performed by this method are: + - Validate ioh_i2c_ptr + - Update the reference of the callback function in the callback + function pointer. + @param ioh_i2c_ptr [@ref IN] Contains reference to call back function + @retval None + @see ioh_i2c_probe +
+ */ +void ioh_i2c_entcb(s32(*ioh_i2c_ptr) (struct i2c_algo_ioh_data *adap)) +{ + if (ioh_i2c_ptr != NULL) { + IOH_DEBUG("value in ioh_i2c_ptr = %p", ioh_i2c_ptr); + /* set the handler call back function */ + ioh_i2c_cbr = ioh_i2c_ptr; + IOH_DEBUG("value updated in ioh_i2c_cbr = %p", ioh_i2c_cbr); + IOH_DEBUG("Invoked ioh_i2c_entcb successfully"); + + } +} + +/*! @ingroup I2C_HALLayerAPI + @fn ioh_i2c_handler(int irq,void * pData) + @remarks This function implements the interrupt handler for + the IOH I2C controller. + The main tasks performed by this method are: + - Invoke callback function. + - Based on return value of callback function, + return IRQ_NONE or IRQ_HANDLED + @param irq [@ref IN] irq number + @param pData [@ref IN] cookie passed back to the handler function + @retval irqreturn_t + - IRQ_NONE Not our interrupt + - IRQ_HANDLED Interrupt serviced + @see ioh_i2c_probe +
+ */ +irqreturn_t ioh_i2c_handler(int irq, void *pData) +{ + s32 ret = 0; + u32 i; + + struct adapter_info *adap_info = (struct adapter_info *)pData; + /* invoke the call back */ + + if (ioh_i2c_cbr != NULL) { + for (i = 0; i < IOH_I2C_MAX_CHN; i++) + ret |= (ioh_i2c_cbr) (&adap_info->ioh_i2c_data[i]); + } else { + IOH_LOG(KERN_ERR, " Call back pointer null ..."); + } + + IOH_DEBUG("ioh_i2c_cb return = %d\n", ret); + + if (ret == IOH_I2C_EVENT_SET) + IOH_DEBUG(" ioh_i2c_handler return IRQ_HANDLED"); + else + IOH_DEBUG("ioh_i2c_handler return IRQ_NONE"); + + return (ret == IOH_I2C_EVENT_SET) ? (IRQ_HANDLED) : (IRQ_NONE); +} + +/*! @ingroup I2C_HALLayerAPI + @fn ioh_i2c_buffer_read + (struct i2c_adapter * i2c_adap,struct i2c_msg *msgs) + @remarks Function to read data from I2C bus in buffer mode. + The main tasks performed by this method are: + - Enable Buffer Mode. + - Set timeout interval in I2CTMR register. + - Enable buffer mode interrupts. + - Set the I2C Slave Address in the I2CBUFSLV register. + - Set the number of bytes, transmission mode and + sub-address length in I2CBUFFOR register. + - Perform the data read. + - Disable buffer mode interrupts. + @param i2c_adap [@ref IN] contains reference to the struct i2c_adapter + @param msgs [@ref INOUT] contains reference to i2c_msg structure + @retval s32 + - @ref IOH_I2C_SUCCESS Function returns successfully + - @ref IOH_I2C_FAIL @ref ioh_i2c_wait_for_bus_idle, + @ref ioh_i2c_wait_for_xfer_complete, + @ref ioh_i2c_getack fails + - -ERESTARTSYS + @ref ioh_i2c_wait_for_xfer_complete was interrupted by a signal + @see ioh_i2c_xfer +
+ */ +s32 ioh_i2c_buffer_read(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs) +{ + + struct i2c_algo_ioh_data *adap = i2c_adap->algo_data; + + u32 loop; + u32 rdcount = 0; + u32 length; + u32 i2cbufsub = 0; + u32 addr; + u32 i2cbufslv_7_lsb; + u32 i2cbufslv_10_9_bit; + u32 msglen; + /* 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 = IOH_I2C_FAIL; + u8 *buf; + + length = msgs->len; + buf = msgs->buf; + addr = msgs->addr; + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFMSK, + BUFFER_MODE_INTR_ENBL); + + /* get the current value of I2C mod register */ + i2cmod_prev = adap->readreg((adap->ioh_i2c_base_address), IOH_I2CMOD); + + /* enable buffer mode */ + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CMOD, + IOH_BUFFER_MODE); + + time_interval = (time_interval <= 10) ? (time_interval) : (10); + + /* value of I2CT = (Timeout interval * PCLK frequency)/ 8 */ + i2ctmr = (time_interval * (ioh_i2c_clk)) / 8; + + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CTMR, i2ctmr); + + /* if 10 bit addressing is selected */ + + if ((msgs->flags & I2C_M_TEN) != false) { + /* 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); + + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFSLV, + (TEN_BIT_ADDR_DEFAULT | i2cbufslv_7_lsb | + i2cbufslv_10_9_bit)); + } else { + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFSLV, + ((addr & I2C_ADDR_MSK) << 1)); + } + + /* 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); 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 */ + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFSUB, i2cbufsub); + /* clear buffers */ + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFLEV, CLR_REG); + + rdcount = (msglen <= BUF_LEN_MAX) ? (msglen) : (BUF_LEN_MAX); + + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFFOR, + ((rdcount << 4) | (IOH_BUF_RD) | (subaddrlen))); + + do { + if (ioh_i2c_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT) == + IOH_I2C_FAIL) { + break; + } + + ioh_i2c_buff_mode_start(adap); + + IOH_DEBUG("buffer mode start"); + + if ((adap->readreg((adap->ioh_i2c_base_address), + IOH_I2CBUFSTA) & I2CBMDZ_BIT) != 0) { + IOH_DEBUG("buffer read error 1"); + break; + } + + if (ioh_i2c_wait_for_xfer_complete(adap) == IOH_I2C_FAIL) { + IOH_DEBUG("buffer read error2"); + break; + } + + IOH_DEBUG("ioh_i2c_wait_for_xfer_complete return %d", + IOH_I2C_SUCCESS); + + retvalue = rdcount; + + for (; rdcount > 0; rdcount--, loop++) { + buf[loop] = + adap->readreg((adap->ioh_i2c_base_address), + IOH_I2CDR); + + } + } while (0); + + /* disable buffer mode interrupts */ + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFMSK, + BUFFER_MODE_INTR_DISBL); + /* restore the I2CMOD register */ + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CMOD, i2cmod_prev); + + return retvalue; +} + +/*! @ingroup I2C_HALLayerAPI + @fn ioh_i2c_buffer_write + (struct i2c_adapter * i2c_adap,struct i2c_msg * msgs) + @remarks Function to write data to I2C bus in buffer mode. + The main tasks performed by this method are: + - Enable Buffer Mode. + - Set timeout interval in I2CTMR register. + - Enable buffer mode interrupts. + - Set the I2C Slave Address in the I2CBUFSLV register. + - Set the number of bytes, transmission mode and + subaddress length in I2CBUFFOR register. + - Perform data transfer. + - Disable the buffer mode interrupts. + @param i2c_adap [@ref IN] contains reference to the struct i2c_adapter + @param msgs [@ref INOUT] contains reference to i2c_msg structure + @retval s32 + - @ref IOH_I2C_SUCCESS Function returns successfully + - @ref IOH_I2C_FAIL @ref ioh_i2c_wait_for_bus_idle, + @ref ioh_i2c_wait_for_xfer_complete, + @ref ioh_i2c_getack fails + - -ERESTARTSYS + @ref ioh_i2c_wait_for_xfer_complete was interrupted by a signal + @see ioh_i2c_xfer +
+ */ +s32 ioh_i2c_buffer_write(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs) +{ + struct i2c_algo_ioh_data *adap = i2c_adap->algo_data; + + u32 loop = 0; + u32 wrcount = 0; + u32 msglen; + u32 i2cbufsub = 0; + u32 addr; + u32 i2cbufslv_7_lsb; + u32 i2cbufslv_10_9_bit; + + /* 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 = IOH_I2C_FAIL; + u8 *buf; + + msglen = msgs->len; + buf = msgs->buf; + addr = msgs->addr; + + /* get the current value of I2C mod register */ + i2cmod_prev = adap->readreg((adap->ioh_i2c_base_address), IOH_I2CMOD); + /* enable buffer mode */ + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CMOD, + IOH_BUFFER_MODE); + + time_interval = (time_interval <= 10) ? (time_interval) : (10); + /* value of I2CT = (Timeout interval * PCLK frequency)/ 8 */ + i2ctmr = (time_interval * (ioh_i2c_clk)) / 8; + + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CTMR, i2ctmr); + + /* enable buffer mode interrupts */ + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFMSK, + BUFFER_MODE_INTR_ENBL); + + /* if 10 bit addressing is selected */ + + if ((msgs->flags & I2C_M_TEN) != false) { + IOH_DEBUG("ioh_i2c_buffer_write...ten bit addressing"); + /* 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); + + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFSLV, + (TEN_BIT_ADDR_DEFAULT | i2cbufslv_7_lsb | + i2cbufslv_10_9_bit)); + } else { + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFSLV, + ((addr & I2C_ADDR_MSK) << 1)); + + } + + /* 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); 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 = msglen - loop; + + /* write the sub address to the reg */ + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFSUB, i2cbufsub); + + /* clear buffers */ + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFLEV, CLR_REG); + + msglen = (msglen < BUF_LEN_MAX) ? (msglen) : (BUF_LEN_MAX); + + for (wrcount = 0; wrcount < msglen; wrcount++) { + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CDR, + buf[loop]); + IOH_DEBUG("Buffer mode %x", (buf[loop] & 0xff)); + loop++; + } + + /* set the number of bytes, transmission mode and sub address length */ + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFFOR, + ((((wrcount << 4) & (IOH_BUF_TX)) | (subaddrlen)))); + + do { + if ((ioh_i2c_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT)) == + IOH_I2C_FAIL) { + break; + } + + /* issue start bits */ + ioh_i2c_buff_mode_start(adap); + + if (((adap->readreg((adap->ioh_i2c_base_address), + IOH_I2CBUFSTA)) & (I2CBMDZ_BIT | + I2CBMAG_BIT)) != false) { + break; + } + + if (ioh_i2c_wait_for_xfer_complete(adap) == IOH_I2C_FAIL) + break; + + IOH_DEBUG("ioh_i2c_wait_for_xfer_complete return %d", + IOH_I2C_SUCCESS); + retvalue = wrcount; + } while (0); + + /* disable buffer mode interrupts */ + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFMSK, + BUFFER_MODE_INTR_DISBL); + /* restore the I2CMOD register */ + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CMOD, i2cmod_prev); + + return retvalue; +} + +/*! @ingroup I2C_HALLayerAPI + @fn ioh_i2c_eeprom_sw_reset + (struct i2c_adapter * i2c_adap,struct i2c_msg *msgs) + @remarks Function for triggering EEPROM software reset. + The main tasks performed by this method are: + - Enable EEPROM software reset mode. + - Enable the required interrupts. + - Update timeout value in I2CTMR register. + - Invoke @ref ioh_i2c_eeprom_swrst_start to + send software reset pattern. + - Disable interrupts. + @param i2c_adap [@ref IN] contains reference to the struct i2c_adapter + @param msgs [@ref IN] contains reference to i2c_msg structure + @retval s32 + - @ref IOH_I2C_SUCCESS Function returns successfully + - i@ref IOH_I2C_FAIL @ref ioh_i2c_wait_for_bus_idle, + @ref ioh_i2c_wait_for_xfer_complete, + - -ERESTARTSYS + @ref ioh_i2c_wait_for_xfer_complete was interrupted by a signal + @see ioh_i2c_xfer +
+ */ +s32 ioh_i2c_eeprom_sw_reset(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs) +{ + + struct i2c_algo_ioh_data *adap = i2c_adap->algo_data; + + u32 time_interval = i2c_adap->timeout; + u32 i2ctmr; + u32 i2cmod_prev; + u32 ioh_pattern; + + s32 ret_val = IOH_I2C_FAIL; /* init return value to error */ + + /* get the current value of I2C mod register */ + i2cmod_prev = adap->readreg((adap->ioh_i2c_base_address), IOH_I2CMOD); + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CMOD, CLR_REG); + adap->set_reg_bit((adap->ioh_i2c_base_address), IOH_I2CMOD, + EEPROM_SW_RST_MODE); + + IOH_DEBUG("ioh_i2c_eeprom_sw_reset : I2CMOD %x\n", + adap->readreg((adap->ioh_i2c_base_address), IOH_I2CMOD)); + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CESRMSK, + EEPROM_RST_INTR_ENBL); + + time_interval = (time_interval <= 10) ? (time_interval) : (10); + + /* value of I2CT = (Timeout interval * PCLK frequency)/ 8 */ + i2ctmr = (time_interval * (ioh_i2c_clk)) / 8; + + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CTMR, i2ctmr); + + /* get the EEPROM reset pattern */ + ioh_pattern = (u32) (*(msgs->buf)); + + /* mode 1 & 2 are used for buffer mode selection */ + ioh_pattern -= 2; + + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CESRFOR, + ioh_pattern); + + IOH_DEBUG("ioh_i2c_eeprom_sw_reset : I2CESRFOR %x\n", + adap->readreg((adap->ioh_i2c_base_address), IOH_I2CESRFOR)); + + if (ioh_i2c_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT) == + IOH_I2C_SUCCESS) { + + ioh_i2c_eeprom_swrst_start(adap); + ret_val = ioh_i2c_wait_for_xfer_complete(adap); + + IOH_DEBUG("ioh_i2c_wait_for_xfer_complete return =%d\n", + ret_val); + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CMOD, + i2cmod_prev); + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CESRMSK, + EEPROM_RST_INTR_DISBL); + } + + IOH_DEBUG("ioh_i2c_eeprom_sw_reset return=%d\n", ret_val); + + return ret_val; +} + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_cb(struct i2c_algo_ioh_data * adap) + @remarks Interrupt handler Call back function. + The main tasks performed by this method are: + - Get the current operation mode. + - For the current mode ,check if any of the required interrupt + bits are set. + - Invoke wake_up_interruptible function to unblock the functions + waiting for these events. + @param adap [@ref IN] Contains reference to struct i2c_algo_ioh_data + @retval s32 + - @ref IOH_I2C_EVENT_SET Valid I2C event recognized and flagged + - @ref IOH_I2C_EVENT_NONE No valid I2C event + @see ioh_i2c_probe +
+ */ +s32 ioh_i2c_cb(struct i2c_algo_ioh_data *adap) +{ + u32 reg_val; + u32 i2c_mode; + u32 i2c_interrupt = false; + + reg_val = adap->readreg((adap->ioh_i2c_base_address), IOH_I2CMOD); + /* get the current mode of operation */ + i2c_mode = reg_val & (BUFFER_MODE | EEPROM_SR_MODE); + + switch (i2c_mode) { + + case NORMAL_MODE: + { + reg_val = + adap->readreg((adap->ioh_i2c_base_address), + IOH_I2CSR); + reg_val &= (I2CMAL_BIT | I2CMCF_BIT | I2CMIF_BIT); + + if (reg_val != 0) { + + if (I2CMAL_BIT & reg_val) { + adap->ioh_i2c_event_flag |= + I2CMAL_EVENT; + } + + if (I2CMCF_BIT & reg_val) { + adap->ioh_i2c_event_flag |= + I2CMCF_EVENT; + } + + /* clear the applicable bits */ + adap->clr_reg_bit((adap->ioh_i2c_base_address), + IOH_I2CSR, reg_val); + + IOH_DEBUG("ioh_i2c_cb : IOH_I2CSR = %x\n", + (adap-> + readreg(adap->ioh_i2c_base_address, + IOH_I2CSR))); + + i2c_interrupt = true; + } + + break; + } + + case BUFFER_MODE: + { + reg_val = + adap->readreg((adap->ioh_i2c_base_address), + IOH_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->ioh_i2c_event_flag |= ((reg_val) << 2); + + /* clear the applicable bits */ + adap->clr_reg_bit((adap->ioh_i2c_base_address), + IOH_I2CBUFSTA, reg_val); + + IOH_DEBUG("ioh_i2c_cb : IOH_I2CBUFSTA = %x\n", + (adap-> + readreg(adap->ioh_i2c_base_address, + IOH_I2CBUFSTA))); + + i2c_interrupt = true; + } + + break; + + } + + case EEPROM_SR_MODE: + { + reg_val = + adap->readreg((adap->ioh_i2c_base_address), + IOH_I2CESRSTA); + reg_val &= (I2CESRFI_BIT | I2CESRTO_BIT); + if (reg_val != 0) { + + adap->ioh_i2c_event_flag |= ((reg_val) << 7); + + /* clear the applicable bits */ + adap->clr_reg_bit((adap->ioh_i2c_base_address), + IOH_I2CESRSTA, reg_val); + + IOH_DEBUG("ioh_i2c_cb : IOH_I2CESRSTA = %x\n", + (adap-> + readreg(adap->ioh_i2c_base_address, + IOH_I2CESRSTA))); + + i2c_interrupt = true; + } + + break; + } + + default: + { + break; + } + } /* end switch */ + + if (i2c_interrupt == true) + wake_up_interruptible(&ioh_i2c_event); + + return ((i2c_interrupt == + true) ? (IOH_I2C_EVENT_SET) : (IOH_I2C_EVENT_NONE)); +} + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_start(struct i2c_algo_ioh_data * adap) + @remarks The main tasks performed by this method are: + - Generate I2C start condition in normal mode + by setting I2CCTL.I2CMSTA to 1. + @param adap [@ref IN] Contains reference to struct i2c_algo_ioh_data + @retval None + @see - ioh_i2c_readbytes + - ioh_i2c_writebytes +
+ */ +static void ioh_i2c_start(struct i2c_algo_ioh_data *adap) +{ + IOH_DEBUG("In ioh_i2c_start : I2CCTL = %x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CCTL))); + adap->set_reg_bit((adap->ioh_i2c_base_address), IOH_I2CCTL, IOH_START); + IOH_DEBUG(" Invoke ioh_i2c_start successfully \n"); + IOH_DEBUG("In ioh_i2c_start : I2CCTL = %x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CCTL))); +} + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_buff_mode_start(struct i2c_algo_ioh_data * adap) + @remarks The main tasks performed by this method are: + - Generate I2C start condition in buffer mode + by setting I2CBUFCTL.I2CBMSTA to 1. + @param adap [@ref IN] Contains reference to struct i2c_algo_ioh_data + @retval None + @see - ioh_i2c_buffer_read + - ioh_i2c_buffer_write +
+ */ +static void ioh_i2c_buff_mode_start(struct i2c_algo_ioh_data *adap) +{ + IOH_DEBUG("In ioh_i2c_buff_mode_start : I2CBUFCTL = %x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CBUFCTL))); + adap->set_reg_bit((adap->ioh_i2c_base_address), IOH_I2CBUFCTL, + IOH_BUFF_START); + + IOH_DEBUG(" Invoke ioh_i2c_buff_mode_start successfully \n"); + IOH_DEBUG("In ioh_i2c_buff_mode_start : I2CBUFCTL = %x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CBUFCTL))); +} + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_eeprom_swrst_start(struct i2c_algo_ioh_data * adap) + @remarks The main tasks performed by this method are: + - Generate I2C start condition in EEPROM sw reset mode + by setting I2CESRCTL.I2CSTA to 1. + @param adap [@ref IN] Contains reference to struct i2c_algo_ioh_data + @retval None + @see ioh_i2c_eeprom_sw_reset +
+ */ +static void ioh_i2c_eeprom_swrst_start(struct i2c_algo_ioh_data *adap) +{ + IOH_DEBUG("In ioh_i2c_eeprom_swrst_start : I2CESRCTL = %x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CESRCTL))); + adap->set_reg_bit((adap->ioh_i2c_base_address), IOH_I2CESRCTL, + IOH_ESR_START); + + IOH_DEBUG(" Invoked ioh_i2c_eeprom_swrst_start successfully\n"); + IOH_DEBUG("In ioh_i2c_eeprom_swrst_start : I2CESRCTL = %x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CESRCTL))); + +} + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_stop(struct i2c_algo_ioh_data *adap) + @remarks Function to generate stop condition in normal mode. + The main tasks performed by this method are: + - Generate I2C stop condition by setting I2CCTL.I2CMSTA to 0. + @param adap [@ref IN] Contains reference to struct i2c_algo_ioh_data + @retval None + @see - ioh_i2c_readbytes + - ioh_i2c_writebytes +
+ */ +static void ioh_i2c_stop(struct i2c_algo_ioh_data *adap) +{ + IOH_DEBUG("In ioh_i2c_stop : I2CCTL = %x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CCTL))); + /* clear the start bit */ + adap->clr_reg_bit((adap->ioh_i2c_base_address), IOH_I2CCTL, IOH_START); + IOH_DEBUG(" Invoke ioh_i2c_stop successfully \n"); + IOH_DEBUG("In ioh_i2c_stop : I2CCTL = %x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CCTL))); + +} + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_repstart(struct i2c_algo_ioh_data *adap) + @remarks Function to generate repeated start condition in normal mode. + The main tasks performed by this method are: + - Generate repeated start condition by setting using + I2CCTL.I2CRSTA to 1. + @param adap [@ref IN] Contains reference to struct i2c_algo_ioh_data + @retval None + @see - ioh_i2c_readbytes + - ioh_i2c_writebytes +
+ */ +static void ioh_i2c_repstart(struct i2c_algo_ioh_data *adap) +{ + IOH_DEBUG("In ioh_i2c_repstart : I2CCTL = %x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CCTL))); + adap->set_reg_bit((adap->ioh_i2c_base_address), IOH_I2CCTL, + IOH_REPSTART); + + IOH_DEBUG(" Invoke ioh_i2c_repstart successfully \n"); + IOH_DEBUG("In ioh_i2c_repstart : I2CCTL = %x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CCTL))); + +} + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_getack(struct i2c_algo_ioh_data *adap) + @remarks Function to confirm ACK/NACK. + The main tasks performed by this method are: + - Get the ACK status from I2CSR. + - Return success if ACK received or failure otherwise. + @param adap [@ref IN] Contains reference to struct i2c_algo_ioh_data + @retval s32 + - @ref IOH_I2C_SUCCESS Acknowledgement was received. + - @ref IOH_I2C_FAIL No acknowledgement received. + @see - ioh_i2c_readbytes + - ioh_i2c_writebytes +
+ */ +static s32 ioh_i2c_getack(struct i2c_algo_ioh_data *adap) +{ + u32 reg_val; + reg_val = + (adap->readreg((adap->ioh_i2c_base_address), IOH_I2CSR) & + IOH_GETACK); + + if (reg_val == 0) + IOH_DEBUG("ioh_i2c_getack : return%d \n", IOH_I2C_SUCCESS); + else + IOH_DEBUG("ioh_i2c_getack : return%d \n", IOH_I2C_FAIL); + + return (((reg_val) == 0) ? (IOH_I2C_SUCCESS) : (IOH_I2C_FAIL)); + +} + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_sendack(struct i2c_algo_ioh_data *adap) + @remarks Function to send ACK. + The main tasks performed by this method are: + - Clear the I2C TXAK bit in I2CCTL register . + @param adap [@ref IN] Contains reference to struct i2c_algo_ioh_data + @retval None + @see ioh_i2c_readbytes +
+ */ +static void ioh_i2c_sendack(struct i2c_algo_ioh_data *adap) +{ + IOH_DEBUG("In ioh_i2c_sendack : I2CCTL = %x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CCTL))); + adap->clr_reg_bit((adap->ioh_i2c_base_address), IOH_I2CCTL, IOH_ACK); + + IOH_DEBUG("Invoke ioh_i2c_sendack successfully\n"); + IOH_DEBUG("In ioh_i2c_sendack : I2CCTL = %x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CCTL))); + +} + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_sendnack(struct i2c_algo_ioh_data *adap) + @remarks Function to send NACK. + The main tasks performed by this method are: + - Set the I2C TXAK bit in I2CCTL register . + @param adap [@ref IN] Contains reference to struct i2c_algo_ioh_data + @retval None + @see ioh_i2c_readbytes +
+ */ +static void ioh_i2c_sendnack(struct i2c_algo_ioh_data *adap) +{ + IOH_DEBUG("In ioh_i2c_sendnack : I2CCTL = %x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CCTL))); + adap->set_reg_bit((adap->ioh_i2c_base_address), IOH_I2CCTL, IOH_ACK); + IOH_DEBUG("Invoke ioh_i2c_sendnack successfully\n"); + IOH_DEBUG("In ioh_i2c_sendnack : I2CCTL = %x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CCTL))); + +} + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_wait_for_bus_idle + (struct i2c_algo_ioh_data *adap,s32 timeout) + @remarks Function to check the status of bus. + The main tasks performed by this method are: + - Get the status of Bus Busy. + - If bus is busy sleep for 1 msec and again check. + - Repeat until bus is free or timeout happens. + @param adap [@ref IN] Contains reference to struct i2c_algo_ioh_data + @param timeout [@ref IN] waiting time counter (us) + @retval s32 + - @ref IOH_I2C_SUCCESS The function returns successfully. + - @ref IOH_I2C_FAIL The bus is still idle. + @see - ioh_i2c_readbytes + - ioh_i2c_writebytes + - ioh_i2c_buffer_read + - ioh_i2c_buffer_write + - ioh_i2c_eeprom_sw_reset +
+ */ +static s32 ioh_i2c_wait_for_bus_idle(struct i2c_algo_ioh_data *adap, + s32 timeout) +{ + u32 reg_value; + + /* get the status of bus busy */ + reg_value = + (adap->readreg((adap->ioh_i2c_base_address), IOH_I2CSR) & + (I2CMBB_BIT)); + + while ((timeout != 0) && (reg_value != 0)) { + msleep(1); /* wait for 100 ms */ + reg_value = + (adap->readreg((adap->ioh_i2c_base_address), + IOH_I2CSR) & (I2CMBB_BIT)); + timeout--; + } + + IOH_DEBUG("In ioh_i2c_wait_for_bus_idle : I2CSR = %x\n", + adap->readreg((adap->ioh_i2c_base_address), IOH_I2CSR)); + + if (timeout == 0) { + IOH_LOG(KERN_ERR, "ioh_i2c_wait_for_bus_idle :return%d\n", + IOH_I2C_FAIL); + } else { + IOH_DEBUG("ioh_i2c_wait_for_bus_idle : return %d\n", + IOH_I2C_SUCCESS); + } + + return ((timeout <= 0) ? (IOH_I2C_FAIL) : (IOH_I2C_SUCCESS)); +} + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_wait_for_xfer_complete(struct i2c_algo_ioh_data * adap) + @remarks This functions initiates a wait for the transfer complete event + @param adap [@ref IN] Contains reference to struct i2c_algo_ioh_data + @retval s32 + - @ref IOH_I2C_SUCCESS Function returns successfully. + - @ref IOH_I2C_FAIL Any error occurs. + - -ERESTARTSYS wait_event_interruptible_timeout + API was interrupted + @see - ioh_i2c_readbytes + - ioh_i2c_writebytes + - ioh_i2c_buffer_read + - ioh_i2c_buffer_write + - ioh_i2c_eeprom_sw_reset +
+*/ +static s32 ioh_i2c_wait_for_xfer_complete(struct i2c_algo_ioh_data *adap) +{ + + u32 temp_flag; + s32 ret = IOH_I2C_FAIL; + ret = + wait_event_interruptible_timeout(ioh_i2c_event, + (adap->ioh_i2c_event_flag != 0), + msecs_to_jiffies(50)); + + IOH_DEBUG + ("adap->ioh_i2c_event_flag in ioh_i2c_wait_for_xfer_complete=%x", + adap->ioh_i2c_event_flag); + temp_flag = adap->ioh_i2c_event_flag; + adap->ioh_i2c_event_flag = 0; + + if (ret == 0) { + IOH_LOG(KERN_ERR, "ioh_i2c_wait_for_xfer_complete : Timeout\n"); + } else if (ret < 0) { + IOH_LOG(KERN_ERR, + "ioh_i2c_wait_for_xfer_complete failed : " + "Interrupted by other signal\n"); + ret = -ERESTARTSYS; + } else if ((temp_flag & I2C_ERROR_MASK) == 0) { + ret = IOH_I2C_SUCCESS; + } else { + IOH_LOG(KERN_ERR, + "ioh_i2c_wait_for_xfer_complete failed : " + "Error in transfer\n"); + } + + IOH_DEBUG(KERN_ERR, "ioh_i2c_wait_for_xfer_complete returns %d\n", ret); + + return ret; +} + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_writereg(u32 addr,u32 offset,u32 val) + @remarks Function for writing data to register. + The main tasks performed by this method are: + - Compute the target address by adding the offset to + the base address. + - Write the specified value to the target address. + @param addr [@ref IN] Base address for the I2C channel + @param offset [@ref IN] offset for the register + @param val [@ref IN] Value to be written + @retval None + @see ioh_i2c_probe +
+ */ +void ioh_i2c_writereg(u32 addr, u32 offset, u32 val) +{ + IOH_WRITE_LONG(val, (addr + offset)); +} + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_readreg(u32 addr,u32 offset) + @remarks Function for reading data from register. + The main tasks performed by this method are: + - Compute the target address by adding the offset to + the base address. + - Read the register value and return the same. + @param addr [@ref IN] Base address for the I2C channel + @param offset [@ref IN] offset for the register + @retval u32 + The content of the register that is read. + @see ioh_i2c_probe +
+ */ +u32 ioh_i2c_readreg(u32 addr, u32 offset) +{ + u32 ret; + ret = IOH_READ_LONG(addr + offset); + return ret; +} + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_setbit(u32 addr,u32 offset,u32 bitmask) + @remarks Function to set particular bit in register. + The main tasks performed by this method are: + - Compute the target address by adding the offset + to the base address. + - Read the register value at the target address. + - Perform logical OR with bitmask and write back + to the target address. + @param addr [@ref IN] Base address for the I2C channel + @param offset [@ref IN] offset for the register + @param bitmask [@ref IN] bit position + @retval None + @see ioh_i2c_probe +
+ */ +void ioh_i2c_setbit(u32 addr, u32 offset, u32 bitmask) +{ + IOH_WRITE_LONG(((IOH_READ_LONG(addr + offset)) | (bitmask)), + (addr + offset)); +} + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_clrbit(u32 addr,u32 off,u32 bitmask) + @remarks Function to reset particular bit in register. + The main tasks performed by this method are: + - Compute the target address by adding the offset + to the base address. + - Read the register value at the target address. + - Perform logical AND with bitmask and write back + to the target address. + @param addr [@ref IN] Base address for the I2C channel + @param offset [@ref IN] offset for the register + @param bitmask [@ref IN] bit position + @retval None + @see ioh_i2c_probe +
+ */ +void ioh_i2c_clrbit(u32 addr, u32 offset, u32 bitmask) +{ + IOH_WRITE_LONG(((IOH_READ_LONG(addr + offset)) & (~(bitmask))), + (addr + offset)); +} + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_disbl_int(struct i2c_algo_ioh_data * adap) + @remarks Function to disable IOH I2C interrupts. + The main tasks performed by this method are: + - Disable the following interrupts: + MAL,MCF,I2CESRFI,I2CESRTO,I2CBMIS,I2CBMTO,I2CBMNA, + I2CBMAL and I2CBMFI. + @param adap [@ref IN] Contains reference to struct i2c_algo_ioh_data + @retval None + @see - ioh_i2c_remove + - ioh_i2c_suspend +
+*/ +void ioh_i2c_disbl_int(struct i2c_algo_ioh_data *adap) +{ + + adap->clr_reg_bit((adap->ioh_i2c_base_address), IOH_I2CCTL, + NORMAL_INTR_ENBL); + + IOH_DEBUG("ioh_i2c_disbl_int : I2CCTL = %x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CCTL))); + + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CESRMSK, + EEPROM_RST_INTR_DISBL); + + IOH_DEBUG("ioh_i2c_disbl_int : IOH_I2CESRMSK = %x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CESRMSK))); + + adap->writereg((adap->ioh_i2c_base_address), IOH_I2CBUFMSK, + BUFFER_MODE_INTR_DISBL); + + IOH_DEBUG("ioh_i2c_disbl_int : IOH_I2CBUFMSK = %x\n", + (adap->readreg(adap->ioh_i2c_base_address, IOH_I2CBUFMSK))); + +} diff -urN linux-2.6.33.1/drivers/i2c/busses/pch_i2c_hal.h topcliff-2.6.33.1/drivers/i2c/busses/pch_i2c_hal.h --- linux-2.6.33.1/drivers/i2c/busses/pch_i2c_hal.h 1970-01-01 09:00:00.000000000 +0900 +++ topcliff-2.6.33.1/drivers/i2c/busses/pch_i2c_hal.h 2010-03-23 10:40:18.000000000 +0900 @@ -0,0 +1,337 @@ +#ifndef __IOH_I2C_HAL_H__ +#define __IOH_I2C_HAL_H__ +/*! +* @file ioh_i2c_hal.h +* @brief This file provides the function prototypes and macros to the I2C module. +* @version 0.95 +* @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) 2008 OKI SEMICONDUCTOR Co., LTD. +* All rights reserved. +* +* created: +* WIPRO 02/20/2009 +* modified: +* WIPRO 05/21/2009 +* +*/ + +/*! @defgroup I2C*/ + +/*! @defgroup I2C_Global +@ingroup I2C +@brief This group describes the global entities within + the module. +@remarks This group includes all the global data structures + used within the modules. These are mainly used to + store the device related information which is used + through out the module. +
+*/ + +/*! @defgroup I2C_PCILayer +@ingroup I2C +@brief This group describes the PCI layer interface + functionalities. +@remarks This group contains the functions and data structures + that are used to interface the module with PCI Layer + subsystem of the Kernel. +
+*/ + +/*! @defgroup I2C_InterfaceLayer +@ingroup I2C +@brief This group describes the Driver interface functionalities. +@remarks This group contains the data structures and functions used + to interface the module driver with the kernel subsystem. +
+*/ + +/*! @defgroup I2C_HALLayer +@ingroup I2C +@brief This group describes the hardware specific functionalities. +@remarks This group contains the functions and data structures used + by the module to communicate with the hardware. These + functions are device specific and designed according to the + device specifications. +
+*/ + +/*! @defgroup I2C_Utilities +@ingroup I2C +@brief This group describes the utility functionalities. +@remarks This group contains the functions and data structures used + to assist the other functionalities in their operations. +
+*/ + +/*! @defgroup I2C_PCILayerAPI +@ingroup I2C_PCILayer +@brief This group contains the API(functions) used as the PCI + interface between the Kernel subsystem and the module. +
+*/ + +/*! @defgroup I2C_PCILayerFacilitators +@ingroup I2C_PCILayer +@brief This group contains the data structures used by the PCI + Layer APIs for their functionalities. +
+*/ + +/*! @defgroup I2C_InterfaceLayerAPI +@ingroup I2C_InterfaceLayer +@brief This group contains the API(functions) used as the Driver + interface between the Kernel subsystem and the module. +
+*/ + +/*! @defgroup I2C_InterfaceLayerFacilitators +@ingroup I2C_InterfaceLayer +@brief This group contains the data structures used by the Driver + interface APIs for their functionalities. +
+*/ + +/*! @defgroup I2C_HALLayerAPI +@ingroup I2C_HALLayer +@brief This group contains the APIs(functions) used to interact with + the hardware. These APIs act as an interface between the + hardware and the other driver functions. +
+*/ + +/*! @defgroup I2C_UtilitiesAPI +@ingroup I2C_Utilities +@brief This group contains the APIs(functions) used by other functions. +
+*/ + +/*includes*/ +#include + +/*! @ingroup I2C_Global +@def IOH_I2C_SUCCESS +@brief Success status code +*/ +#define IOH_I2C_SUCCESS (0) + +/*! @ingroup I2C_Global +@def IOH_I2C_FAIL +@brief Error status code +*/ +#define IOH_I2C_FAIL (-1) + +/*! @ingroup I2C_Global +@def IOH_I2C_MAX_CHN +@brief Maximum I2C channels available +*/ +#define IOH_I2C_MAX_CHN (1) + +/*! @ingroup I2C_Global +@def IOH_I2C_EVENT_SET +@brief I2C Interrupt Event Set Status +*/ +#define IOH_I2C_EVENT_SET (0) + +/*! @ingroup I2C_Global +@def IOH_I2C_EVENT_NONE +@brief I2C Interrupt Event Clear Status +*/ +#define IOH_I2C_EVENT_NONE (1) + +/*! @ingroup I2C_Global +@def IOH_I2C_MAX_CLK +@brief Maximum peripheral Clock speed supported in MHz +*/ +#define IOH_I2C_MAX_CLK (100000) + + +/* flag for Buffer mode enable */ +#define IOH_BUFFER_MODE_ENABLE (0x0002) + +/* flag for EEPROM SW RST enable */ +#define IOH_EEPROM_SW_RST_MODE_ENABLE (0x0008) + +/* for mode selection */ +#define I2C_MODE_SEL (0x711) + +/*structures*/ +/*! @ingroup I2C_HALLayer +@struct i2c_algo_ioh_data +@brief This structure contains references to methods implementing + I2C driver functionalities. +@note The concerned details should be provided during + the data transfer. +@see - ioh_i2c_init + - ioh_i2c_entcb + - ioh_i2c_cb + - ioh_i2c_disbl_int +*/ + +struct i2c_algo_ioh_data { + + struct adapter_info *p_adapter_info; + /**< stores the reference to adapter_info structure*/ + + struct i2c_adapter ioh_i2c_adapter; + /**< stores the reference to i2c_adapter structure*/ + + u32 ioh_i2c_base_address; /**< specifies the remapped base address*/ + int ioh_i2c_buff_mode_en; /**< specifies if buffer mode is enabled*/ + u32 ioh_i2c_event_flag; /**< specifies occurrence of interrupt events*/ + + bool ioh_i2c_xfer_in_progress; + /**< specifies whether the transfer is completed */ + + void (*writereg) (u32 addr, u32 off, u32 val); + /**< stores the reference to register write function*/ + + u32(*readreg) (u32 addr, u32 off); + /**< stores the reference to register read function*/ + + void (*set_reg_bit) (u32 addr, u32 off, u32 bitmsk); + /**< stores the reference to register bit setting function*/ + + void (*clr_reg_bit) (u32 addr, u32 off, u32 bitmsk); + /**< stores the reference to register bit clearing function*/ +}; + +/*! @ingroup I2C_HALLayer +@struct adapter_info +@brief This structure holds the adapter information + for the IOH i2c controller. +@note This structure contains instances of struct i2c_algo_ioh_data + for the available I2C channels and also a variable for saving + the suspend status. +@see - ioh_i2c_probe + - ioh_i2c_remove + - ioh_i2c_suspend + - ioh_i2c_resume +*/ + +struct adapter_info { + + struct i2c_algo_ioh_data ioh_i2c_data[IOH_I2C_MAX_CHN]; + /**< stores a list of i2c_algo_ioh_data; + there will be as many elements as maximum I2C channels*/ + + bool ioh_i2c_suspended; + /**< specifies whether the system is suspended or not*/ +}; + +/**global variables*/ +extern int ioh_i2c_speed; +extern int ioh_i2c_clk; +extern s32(*ioh_i2c_cbr) (struct i2c_algo_ioh_data *); + +extern struct i2c_algorithm ioh_i2c_algorithm; + +/* Function prototypes */ +/*! @ingroup I2C_HALLayerAPI +@fn s32 ioh_i2c_init(struct i2c_algo_ioh_data *adap) +@brief Function to initialize IOH I2C hardware +*/ +s32 ioh_i2c_init(struct i2c_algo_ioh_data *adap); + +/*! @ingroup I2C_HALLayerAPI +@fn s32 ioh_i2c_writebytes(struct i2c_adapter *i2c_adap , + struct i2c_msg *msgs,u32 last, u32 first) +@brief Function for data write in normal mode +*/ +s32 ioh_i2c_writebytes(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, u32 last, u32 first); + +/*! @ingroup I2C_HALLayerAPI +@fn s32 ioh_i2c_readbytes(struct i2c_adapter *i2c_adap , + struct i2c_msg *msgs,u32 last, u32 first) +@brief Function for data read in normal mode +*/ +s32 ioh_i2c_readbytes(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, u32 last, u32 first); + +/*! @ingroup I2C_HALLayerAPI +@fn s32 ioh_i2c_eeprom_sw_reset(struct i2c_adapter * i2c_adap, + struct i2c_msg *msgs) +@brief Function for triggering EEPROM software reset mode +*/ +s32 ioh_i2c_eeprom_sw_reset(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs); + +/*! @ingroup I2C_HALLayerAPI +@fn s32 ioh_i2c_buffer_write + (struct i2c_adapter * i2c_adap,struct i2c_msg *msgs) +@brief Function for data write in buffer mode +*/ +s32 ioh_i2c_buffer_write(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs); + +/*! @ingroup I2C_HALLayerAPI +@fn s32 ioh_i2c_buffer_read + (struct i2c_adapter * i2c_adap,struct i2c_msg *msgs) +@brief Function for data read in buffer mode +*/ +s32 ioh_i2c_buffer_read(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs); + +/*! @ingroup I2C_HALLayerAPI +@fn irqreturn_t ioh_i2c_handler(int irq,void *pData) +@brief Interrupt handler +*/ +irqreturn_t ioh_i2c_handler(int irq, void *pData); + +/*! @ingroup I2C_HALLayerAPI +@fn void ioh_i2c_entcb + (s32(*ioh_i2c_ptr)(struct i2c_algo_ioh_data *adap)) +@brief Function for registering the interrupt handler call back +*/ +void ioh_i2c_entcb(s32(*ioh_i2c_ptr) (struct i2c_algo_ioh_data *adap)); + +/*! @ingroup I2C_UtilitiesAPI +@fn s32 ioh_i2c_cb(struct i2c_algo_ioh_data * adap) +@brief Call back function invoked from interrupt handler +*/ +s32 ioh_i2c_cb(struct i2c_algo_ioh_data *adap); + +/*! @ingroup I2C_UtilitiesAPI +@fn void ioh_i2c_disbl_int(struct i2c_algo_ioh_data *adap) +@brief Function for disabling the interrupt +*/ +void ioh_i2c_disbl_int(struct i2c_algo_ioh_data *adap); + +/*! @ingroup I2C_UtilitiesAPI +@fn void ioh_i2c_writereg(u32 addr,u32 off,u32 val) +@brief Function for writing data to register +*/ +void ioh_i2c_writereg(u32 addr, u32 off, u32 val); + +/*! @ingroup I2C_UtilitiesAPI +@fn u32 ioh_i2c_readreg(u32 addr,u32 off) +@brief Function for reading data from register +*/ +u32 ioh_i2c_readreg(u32 addr, u32 off); + +/*! @ingroup I2C_UtilitiesAPI +@fn void ioh_i2c_setbit(u32 addr,u32 off,u32 bitmsk) +@brief Function to set a particular bit in a register +*/ +void ioh_i2c_setbit(u32 addr, u32 off, u32 bitmsk); + +/*! @ingroup I2C_UtilitiesAPI +@fn void ioh_i2c_clrbit(u32 addr,u32 off,u32 bitmsk) +@brief Function to clear a particular bit in a register +*/ +void ioh_i2c_clrbit(u32 addr, u32 off, u32 bitmsk); +#endif diff -urN linux-2.6.33.1/drivers/i2c/busses/pch_i2c_main.c topcliff-2.6.33.1/drivers/i2c/busses/pch_i2c_main.c --- linux-2.6.33.1/drivers/i2c/busses/pch_i2c_main.c 1970-01-01 09:00:00.000000000 +0900 +++ topcliff-2.6.33.1/drivers/i2c/busses/pch_i2c_main.c 2010-03-23 10:40:18.000000000 +0900 @@ -0,0 +1,247 @@ +/*! + * @file ioh_i2c_main.c + * @brief This file contains the definitions + * of Interface Layer APIs for IOH I2C driver. + * @version 0.95 + * @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) 2008 OKI SEMICONDUCTOR Co., LTD. + * All rights reserved. + * + * created: + * WIPRO 02/20/2009 + * modified: + * WIPRO 05/21/2009 + * + */ + +/*includes*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pch_i2c_hal.h" +#include "pch_common.h" +#include "pch_debug.h" + +/* Function prototypes */ + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_func(struct i2c_adapter *adap) + @brief This function returns the functionalities supported + by I2C driver. + */ +static u32 ioh_i2c_func(struct i2c_adapter *adap); + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs,s32 num) + @brief This function handles data transfer through I2C bus + */ +static s32 ioh_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, + s32 num); + +/*structures*/ + +/*! @ingroup I2C_Global + @struct ioh_i2c_algorithm + @brief This an instance of the kernel structure i2c_algorithm structure + and it stores the properties of the IOH I2C algorithm driver. + @note This structure stores the references of the @ref ioh_i2c_xfer + and @ref ioh_i2c_func functions. + @see ioh_i2c_probe + */ + +struct i2c_algorithm ioh_i2c_algorithm = { + .master_xfer = ioh_i2c_xfer, + .functionality = ioh_i2c_func +}; + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_func(struct i2c_adapter *adap) + @brief Function return the functionality of the I2C driver + @remarks Returns (I2C_FUNC_I2C) | (I2C_FUNC_SMBUS_EMUL) | + (I2C_FUNC_10BIT_ADDR) + @param adap [@ref IN] Contains reference to i2c_adapter structure + @retval u32 + - Bitwise OR of the feature status codes supported + by this algorithm driver. + @see ioh_i2c_algorithm + */ +static u32 ioh_i2c_func(struct i2c_adapter *adap) +{ + u32 ret; + ret = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR; + return ret; +} + +/*! @ingroup I2C_UtilitiesAPI + @fn ioh_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs,s32 num) + @brief Function to transfer data through I2C bus + @remarks Function to transfer data through I2C bus + The main tasks performed by this method are: + - Check if system is suspended. + - If EEPROM software reset command is received, + then invoke function ioh_i2c_eeprom_sw_reset. + - If Buffer mode selection command is received, + check the value of msgs[0]->buf[0]. If set, + enable buffer mode, by setting the variable + adap->ioh_i2c_buff_mode_en. Otherwise reset the flag. + - If no special command, perform the requested + data transfer operation. + @note The master transfer function ioh_i2c_xfer + is invoked by the Linux I2C core, whenever + communication/data transfer with the IOH I2C + driver is necessary. The Linux I2C core + ensures that the function is called with + valid parameters only. + @param i2c_adap [@ref IN] contains reference to the struct i2c_adapter + @param msgs [@ref IN] contains reference to i2c_msg structure + @param num [@ref IN] number of messages + @retval s32 + - @ref IOH_I2C_SUCCESS + Function returns successfully for EEPROM sw reset mode, + buffer mode selection commands. + - The number of bytes transferred for successful operation + of read/write calls. + - @ref IOH_I2C_FAIL + Any error occurs during the execution of the function. + @see ioh_i2c_algorithm +
+ */ + +static s32 ioh_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, s32 num) +{ + + struct i2c_msg *pmsg; + u32 i = 0; + u32 status; + u32 msglen; + u32 subaddrlen; + s32 ret = IOH_I2C_FAIL; + + struct i2c_algo_ioh_data *adap = i2c_adap->algo_data; + + if (adap->p_adapter_info->ioh_i2c_suspended == false) { + IOH_DEBUG("ioh_i2c_xfer " + "adap->p_adapter_info->ioh_i2c_suspended is %d\n", + adap->p_adapter_info->ioh_i2c_suspended); + /* transfer not completed */ + adap->ioh_i2c_xfer_in_progress = true; + IOH_DEBUG(" adap->ioh_i2c_xfer_in_progress is %d\n", + adap->ioh_i2c_xfer_in_progress); + pmsg = &msgs[0]; + status = pmsg->flags; + /* special commands for IOH I2C driver */ + if ((status & + (IOH_EEPROM_SW_RST_MODE_ENABLE | IOH_BUFFER_MODE_ENABLE)) + != false) { + if ((status & IOH_EEPROM_SW_RST_MODE_ENABLE) != false) { + /* check whether EEPROM sw reset is enabled */ + IOH_DEBUG("ioh_i2c_xfer invoking " + "ioh_i2c_eeprom_sw_reset\n"); + IOH_DEBUG("After invoking " + "I2C_MODE_SEL :flag= 0x%x\n", status); + ret = ioh_i2c_eeprom_sw_reset(i2c_adap, pmsg); + } else { + adap->ioh_i2c_buff_mode_en = + (pmsg->buf[0] == 1) ? + (IOH_BUFFER_MODE_ENABLE) : (pmsg->buf[0]); + ret = IOH_I2C_SUCCESS; + } + /* transfer completed */ + adap->ioh_i2c_xfer_in_progress = false; + IOH_DEBUG("adap->ioh_i2c_xfer_in_progress is %d\n", + adap->ioh_i2c_xfer_in_progress); + IOH_DEBUG(KERN_INFO, + "After mode selection " + "ioh_i2c_xfer return = %d\n", ret); + return ret; + } + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + pmsg->flags |= adap->ioh_i2c_buff_mode_en; + status = pmsg->flags; + IOH_DEBUG("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 & IOH_BUFFER_MODE_ENABLE) != false) + && (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)) != false) { + IOH_DEBUG(KERN_INFO, + "ioh_i2c_xfer invoking " + "ioh_i2c_buffer_read\n"); + ret = + ioh_i2c_buffer_read(i2c_adap, pmsg); + } else { + IOH_DEBUG(KERN_INFO, + "ioh_i2c_xfer invoking " + "ioh_i2c_buffer_write\n"); + ret = + ioh_i2c_buffer_write(i2c_adap, pmsg); + } + } else { + if ((status & (I2C_M_RD)) != false) { + IOH_DEBUG(KERN_INFO, + "ioh_i2c_xfer invoking " + "ioh_i2c_readbytes\n"); + ret = + ioh_i2c_readbytes(i2c_adap, pmsg, + (i + 1 == num), + (i == 0)); + } else { + IOH_DEBUG(KERN_INFO, + "ioh_i2c_xfer invoking " + "ioh_i2c_writebytes\n"); + ret = + ioh_i2c_writebytes(i2c_adap, pmsg, + (i + 1 == num), + (i == 0)); + } + } + + } + + adap->ioh_i2c_xfer_in_progress = false; /* transfer completed */ + + IOH_DEBUG(" adap->ioh_i2c_xfer_in_progress is %d\n", + adap->ioh_i2c_xfer_in_progress); + } + IOH_DEBUG(KERN_INFO, "ioh_i2c_xfer return:%d\n\n\n\n", ret); + + return ret; +} diff -urN linux-2.6.33.1/drivers/i2c/busses/pch_i2c_pci.c topcliff-2.6.33.1/drivers/i2c/busses/pch_i2c_pci.c --- linux-2.6.33.1/drivers/i2c/busses/pch_i2c_pci.c 1970-01-01 09:00:00.000000000 +0900 +++ topcliff-2.6.33.1/drivers/i2c/busses/pch_i2c_pci.c 2010-03-23 10:40:18.000000000 +0900 @@ -0,0 +1,583 @@ +/*! +* @file ioh_i2c_pci.c +* @brief This file contains the definitions of I2C_PCILayer APIs. +* @version 0.95 +* @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) 2008 OKI SEMICONDUCTOR Co., LTD. +* All rights reserved. +* +* created: +* WIPRO 02/20/2009 +* modified: +* WIPRO 05/21/2009 +* +*/ + +/*includes*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pch_i2c_hal.h" +#include "pch_common.h" +#include "pch_debug.h" + +/** + *macro definition + */ + +/*! @ingroup I2C_PCILayer +@def PCI_DEVICE_ID_IOH_I2C +@brief Device ID of the device supported by IOH I2C + driver in GE configuration. +*/ +#define PCI_DEVICE_ID_IOH_I2C (0x8817) + +/* + * variable declaration + */ +/*! @ingroup I2C_Global +@var ioh_i2c_speed +@brief specifies I2C bus speed in Kbps +@note This parameter is provided as module parameter + while loading the driver. If no value is provided, + by default the speed is set to 100 kbps. +@see ioh_i2c_init +
+*/ +int ioh_i2c_speed = 100; + +/*! @ingroup I2C_Global +@var ioh_i2c_clock +@brief specifies I2C clock speed in KHz +@note This parameter is provided as module parameter + while inserting the driver. If no value is provided, + by default the speed is set to 62500KHz. +@see ioh_i2c_init +
+*/ +/* int ioh_i2c_clk = 62500; */ +int ioh_i2c_clk = 50000; + +/*! @ingroup I2C_Global +@var ioh_i2c_cbr +@brief I2C_Global function pointer to save reference to + callback function +@see ioh_i2c_entcb +
+*/ +s32(*ioh_i2c_cbr) (struct i2c_algo_ioh_data *); + +/*! @ingroup I2C_Global +@var MODULE_NAME +@brief I2C_Global variable storing the name of this driver +@see ioh_i2c_probe +
+*/ +#define MODULE_NAME "pch_i2c" /* name for the driver */ + +/* Function prototypes */ +/*! @ingroup I2C_PCILayerAPI +@fn ioh_i2c_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +@brief This function implements the probe routine + for IOH I2C driver module +*/ +static int __devinit ioh_i2c_probe(struct pci_dev *pdev, + const struct pci_device_id *id); + +/*! @ingroup I2C_PCILayerAPI +@fn ioh_i2c_remove(struct pci_dev *pdev) +@brief This function implements the remove routine + for IOH I2C driver module. +*/ +static void __devexit ioh_i2c_remove(struct pci_dev *pdev); + +/*! @ingroup I2C_PCILayerAPI +@fn ioh_i2c_suspend(struct pci_dev* pdev,pm_message_t state) +@brief This function implements the suspend routine + for IOH I2C driver module +*/ +static int ioh_i2c_suspend(struct pci_dev *pdev, pm_message_t state); + +/*! @ingroup I2C_PCILayerAPI +@fn ioh_i2c_resume(struct pci_dev* pdev) +@brief This function implements the resume routine + for IOH I2C driver module +*/ +static int ioh_i2c_resume(struct pci_dev *pdev); + +/*structures*/ +/*! @ingroup I2C_PCILayerFacilitators +@struct ioh_i2c_pcidev_id +@brief Store information of supported PCI devices +@note This is an instance of pci_device_id structure and + holds information of the PCI devices that are supported + by this driver +@see ioh_i2c_pcidriver +*/ + +static struct pci_device_id __devinitdata ioh_i2c_pcidev_id[] = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_IOH_I2C)}, + {0,} +}; + +/*! @ingroup I2C_PCILayerFacilitators +@struct ioh_i2c_pcidriver +@brief Store the references of PCI driver interfaces to kernel +@note This is an instance of pci_driver and this structure specifies + the driver details to be registered with the kernel +@see - ioh_i2c_pci_init + - ioh_i2c_pci_exit +
+*/ + +static struct pci_driver ioh_i2c_pcidriver = { + .name = "ioh_i2c", + .id_table = ioh_i2c_pcidev_id, + .probe = ioh_i2c_probe, + .remove = __devexit_p(ioh_i2c_remove), +#ifdef CONFIG_PM + .suspend = ioh_i2c_suspend, + .resume = ioh_i2c_resume +#endif +}; + +/*! @ingroup I2C_PCILayerAPI + @fn ioh_i2c_probe(struct pci_dev *pdev, const struct + pci_device_id *id) + @remarks The main tasks performed by this method are: + - Allocate memory for driver private data. + - Enable the PCI device. + - Reserve the PCI regions. + - Map the device address of the IO BAR. + - Register the interrupt handler. + - Initialize the members in adap_info->ioh_i2c_data. + - Register the ioh_i2c_adapter. + - Initialize the IOH I2C hardware. + @note This function is invoked by the PCI core when a device is + found for this driver to control + @param pdev [@ref IN] contains reference to + PCI device descriptor for the peripheral + @param id [@ref IN] contains reference to + the pci_device_id table of matching peripheral + @retval int + - @ref IOH_I2C_SUCCESS Function returns successfully. + - -EIO pci_enable_device fails + - -EINVAL pci_enable_device/request_irq fails + - -EBUSY pci_request_regions/request_irq fails + - -ENOMEM i2c_add_adapter/request_irq/pci_iomap/kzalloc fails + - -EAGAIN i2c_add_adapter fails + - -ENOSYS request_irq fails + @see ioh_i2c_pcidriver +
+ */ +static int __devinit ioh_i2c_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + + int i; + u32 base_addr; + s32 ret = IOH_I2C_SUCCESS; + + IOH_DEBUG("Enterred in i2c_probe\n"); + + do { + struct adapter_info *adap_info = + kzalloc((sizeof(struct adapter_info)), GFP_KERNEL); + if (adap_info == NULL) { + IOH_LOG(KERN_ERR, "Memory allocation failed FAILED"); + ret = -ENOMEM; + break; + } + + IOH_DEBUG + ("Function kzalloc invoked successfully " + "and adap_info valu = %p\n", + adap_info); + + ret = pci_enable_device(pdev); + + if (ret) { + IOH_LOG(KERN_ERR, "pci_enable_device FAILED"); + kfree(adap_info); + break; + } + + IOH_DEBUG("pci_enable_device returns %d\n", ret); + + ret = pci_request_regions(pdev, MODULE_NAME); + if (ret) { + IOH_LOG(KERN_ERR, "pci_request_regions FAILED"); + pci_disable_device(pdev); + kfree(adap_info); + break; + } + + IOH_DEBUG("pci_request_regions returns %d\n", ret); + + /* Wipro 1/13/2010 Use Mem BAR */ + base_addr = (unsigned long)pci_iomap(pdev, 1, 0); + + if (base_addr == 0) { + IOH_LOG(KERN_ERR, "pci_iomap FAILED"); + pci_release_regions(pdev); + pci_disable_device(pdev); + kfree(adap_info); + ret = -ENOMEM; + break; + } + + IOH_DEBUG("pci_iomap invoked successfully\n"); + + ioh_i2c_entcb(ioh_i2c_cb); + IOH_DEBUG("ioh_i2c_entcb invoked successfully\n"); + + for (i = 0; i < IOH_I2C_MAX_CHN; i++) { + adap_info->ioh_i2c_data[i].p_adapter_info = adap_info; + adap_info->ioh_i2c_data[i].writereg = ioh_i2c_writereg; + adap_info->ioh_i2c_data[i].readreg = ioh_i2c_readreg; + adap_info->ioh_i2c_data[i].set_reg_bit = ioh_i2c_setbit; + adap_info->ioh_i2c_data[i].clr_reg_bit = ioh_i2c_clrbit; + + adap_info->ioh_i2c_data[i].ioh_i2c_adapter.owner = + THIS_MODULE; + adap_info->ioh_i2c_data[i].ioh_i2c_adapter.class = + I2C_CLASS_HWMON; + strcpy(adap_info->ioh_i2c_data[i].ioh_i2c_adapter.name, + "ioh_i2c"); + adap_info->ioh_i2c_data[i].ioh_i2c_adapter.algo = + &ioh_i2c_algorithm; + adap_info->ioh_i2c_data[i].ioh_i2c_adapter.algo_data = + &adap_info->ioh_i2c_data[i]; + + /* (i * 0x80) + base_addr; */ + adap_info->ioh_i2c_data[i].ioh_i2c_base_address = + base_addr; + + adap_info->ioh_i2c_data[i].ioh_i2c_adapter.dev.parent = + &pdev->dev; + + ret = + i2c_add_adapter(& + (adap_info->ioh_i2c_data[i]. + ioh_i2c_adapter)); + + if (ret) { + IOH_LOG(KERN_ERR, "i2c_add_adapter FAILED"); + + pci_iounmap(pdev, (void *)base_addr); + pci_release_regions(pdev); + pci_disable_device(pdev); + kfree(adap_info); + break; + } + + IOH_DEBUG("i2c_add_adapter returns %d for channel-%d\n", + ret, i); + (void)ioh_i2c_init(&adap_info->ioh_i2c_data[i]); + IOH_DEBUG("ioh_i2c_init invoked successfully \n"); + + } + + if (ret) + break; + + ret = request_irq(pdev->irq, &ioh_i2c_handler, IRQF_SHARED, + MODULE_NAME, (void *)adap_info); + + if (ret) { + IOH_DEBUG("request_irq Failed\n"); + + for (i = 0; i < IOH_I2C_MAX_CHN; i++) { + i2c_del_adapter(& + (adap_info-> + ioh_i2c_data + [i].ioh_i2c_adapter)); + } + + pci_iounmap(pdev, (void *)base_addr); + + pci_release_regions(pdev); + pci_disable_device(pdev); + kfree(adap_info); + break; + } + + IOH_DEBUG("request_irq returns %d\n", ret); + + IOH_DEBUG("ioh_i2c_probe returns %d\n", IOH_I2C_SUCCESS); + pci_set_drvdata(pdev, (void *)adap_info); + return IOH_I2C_SUCCESS; + } while (0); + + return ret; +} + +/*! @ingroup I2C_PCILayerAPI + @fn ioh_i2c_remove(struct pci_dev *pdev) + @remarks The main tasks performed by this method are: + - Disable interrupts. + - Unregister interrupt handler. + - Unregister i2c_adapter. + - Release IO memory. + - Release PCI regions. + - Disable PCI device. + @note This function is invoked when the IOH I2C driver module is + unloaded from the system using rmmod command or when the + IOH I2C device is removed from the system. + @param pdev [@ref INOUT] contains reference to + PCI device descriptor for the peripheral + @retval None + @see ioh_i2c_pcidriver +
+ */ + +static void __devexit ioh_i2c_remove(struct pci_dev *pdev) +{ + int i; + + struct adapter_info *adap_info = pci_get_drvdata(pdev); + + IOH_DEBUG(" invoked function pci_get_drvdata successfully\n"); + + for (i = 0; i < IOH_I2C_MAX_CHN; i++) { + ioh_i2c_disbl_int(&adap_info->ioh_i2c_data[i]); + + if (i == (IOH_I2C_MAX_CHN - 1)) { + free_irq(pdev->irq, (void *)adap_info); + IOH_DEBUG(" free_irq invoked successfully\n"); + } + + i2c_del_adapter(&(adap_info->ioh_i2c_data[i].ioh_i2c_adapter)); + + IOH_DEBUG(" invoked i2c_del_adapter successfully\n"); + + } + + if (adap_info->ioh_i2c_data[0].ioh_i2c_base_address) { + pci_iounmap(pdev, + (void *)adap_info->ioh_i2c_data[0]. + ioh_i2c_base_address); + IOH_DEBUG(" pci_iounmap invoked successfully\n"); + adap_info->ioh_i2c_data[0].ioh_i2c_base_address = 0; + } + + pci_set_drvdata(pdev, NULL); + + pci_release_regions(pdev); + IOH_DEBUG(" pci_release_regions invoked successfully\n"); + + pci_disable_device(pdev); + kfree(adap_info); + IOH_DEBUG(" pci_disable_device invoked successfully\n"); + IOH_DEBUG(" ioh_i2c_remove invoked successfully\n"); +} + +#ifdef CONFIG_PM + +/*! @ingroup I2C_PCILayerAPI + @fn ioh_i2c_suspend(struct pci_dev* pdev,pm_message_t state) + @remarks The main tasks performed by this method are: + - Wait for any transfer in progress to complete. + - Disable interrupts. + - Save PCI device state. + - Disable PM notifications. + - Disable the PCI device. + - Move the device to D3Hot power state. + @note This function is invoked by the kernel when the system is + transitioning to low power state. + @param pdev [@ref INOUT] + contains reference to PCI device descriptor for the peripheral + @param state [@ref IN] + Represents the low power state the system is transitioning to. + @retval int + - @ref IOH_I2C_SUCCESS Function returns successfully. + - -ENOMEM pci_save_state fails. + @see ioh_i2c_pcidriver + */ +static int ioh_i2c_suspend(struct pci_dev *pdev, pm_message_t state) +{ + + int i; + int ret; + + struct adapter_info *adap_info = pci_get_drvdata(pdev); + + IOH_DEBUG(" invoked function pci_get_drvdata successfully\n"); + + adap_info->ioh_i2c_suspended = true; + + for (i = 0; i < IOH_I2C_MAX_CHN; i++) { + while ((adap_info->ioh_i2c_data[i].ioh_i2c_xfer_in_progress == + true)) { + /* It is assumed that any pending transfer will + * be completed after the delay + */ + msleep(1); + } + /* Disable the i2c interrupts */ + ioh_i2c_disbl_int(&adap_info->ioh_i2c_data[i]); + } + + IOH_DEBUG("I2CSR = %x\n", + ioh_i2c_readreg(adap_info-> + ioh_i2c_data[0].ioh_i2c_base_address, 0x08)); + IOH_DEBUG("I2CBUFSTA = %x\n", + ioh_i2c_readreg(adap_info-> + ioh_i2c_data[0].ioh_i2c_base_address, 0x30)); + IOH_DEBUG("I2CESRSTA = %x\n", + ioh_i2c_readreg(adap_info-> + ioh_i2c_data[0].ioh_i2c_base_address, 0x44)); + + IOH_DEBUG(" invoked function ioh_i2c_disbl_int successfully\n"); + + ret = pci_save_state(pdev); + + if (ret) { + IOH_LOG(KERN_ERR, "pci_save_state failed\n"); + return ret; + } + + IOH_DEBUG("Invoked pci_save_state successfully\n"); + + pci_enable_wake(pdev, PCI_D3hot, 0); + IOH_DEBUG("Invoked pci_enable_wake successfully\n"); + + pci_disable_device(pdev); + IOH_DEBUG("Invoked pci_disable_device successfully\n"); + + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + IOH_DEBUG("Invoked pci_set_power_state successfully\n"); + IOH_DEBUG("ioh_i2c_suspend returns %d\n", IOH_I2C_SUCCESS); + + return IOH_I2C_SUCCESS; + +} + +/*! @ingroup I2C_PCILayerAPI + @fn ioh_i2c_resume(struct pci_dev* pdev) + @remarks The main tasks performed by this method are: + - Move device to D0 power state. + - Restore PCI device state. + - Enable the PCI device state. + - Disable PM notifications. + - Initialize IOH I2C device. + @note This function is invoked by the kernel when the system is + transitioning to normal power state from a lower power state. + @param pdev [@ref INOUT] + contains reference to PCI device descriptor for the peripheral + @retval int + - @ref IOH_I2C_SUCCESS Function returns successfully. + - -EIO pci_enable_device fails. + - -EINVAL pci_enable_device fails. + @see ioh_i2c_pcidriver +
+ */ +static int ioh_i2c_resume(struct pci_dev *pdev) +{ + + struct adapter_info *adap_info = pci_get_drvdata(pdev); + int i; + + IOH_DEBUG(" invoked function pci_get_drvdata successfully\n"); + + pci_set_power_state(pdev, PCI_D0); + IOH_DEBUG("Invoked pci_set_power_state successfully\n"); + + pci_restore_state(pdev); + IOH_DEBUG("Invoked pci_restore_state successfully\n"); + + if (pci_enable_device(pdev) < 0) { + IOH_LOG(KERN_ERR, + "pci_enable_device failed in ioh_i2c_resume\n"); + return -EIO; + } + + pci_enable_wake(pdev, PCI_D3hot, 0); + + IOH_DEBUG("Invoked pci_enable_wake successfully\n"); + + for (i = 0; i < IOH_I2C_MAX_CHN; i++) + (void)ioh_i2c_init(&adap_info->ioh_i2c_data[i]); + + IOH_DEBUG("Invoked ioh_i2c_init successfully\n"); + + adap_info->ioh_i2c_suspended = false; + + IOH_DEBUG("ioh_i2c_resume return %d\n", IOH_I2C_SUCCESS); + return IOH_I2C_SUCCESS; +} + +#endif + +/*! @ingroup I2C_InterfaceLayerAPI + @fn ioh_i2c_pci_init(void) + @brief This function implements the module entry point. + @remarks This function invoked at module insertion + The main task performed by this method: + - Register the PCI driver with PCI core + using pci_register_driver API. + @param None + @retval int + - 0 Function returns successfully. + - -EEXIST pci_register_driver fails + - -EINVAL pci_register_driver fails + - -ENOMEM pci_register_driver fails +
+ */ +static int __init ioh_i2c_pci_init(void) +{ + + IOH_DEBUG + ("ioh_i2c_pci_init : Invoked pci_register_driver successfully\n"); + return pci_register_driver(&ioh_i2c_pcidriver); +} + +/*! @ingroup I2C_InterfaceLayerAPI + @fn ioh_i2c_pci_exit(void) + @brief This function implements the module exit point. + @remarks This function is invoked when IOH I2C driver module is being + removed from the system. The main task performed by this method: + - Unregister the PCI driver with PCI core using + the pci_unregister_driver API + @param None + @retval None + */ +static void __exit ioh_i2c_pci_exit(void) +{ + IOH_DEBUG + ("ioh_i2c_pci_exit : Invoked pci_unregister_driver successfully \n "); + pci_unregister_driver(&ioh_i2c_pcidriver); + +} + +MODULE_DESCRIPTION("IOH I2C PCI Driver"); +MODULE_LICENSE("GPL"); +module_init(ioh_i2c_pci_init); +module_exit(ioh_i2c_pci_exit); +module_param(ioh_i2c_speed, int, (S_IRUSR | S_IWUSR)); +module_param(ioh_i2c_clk, int, (S_IRUSR | S_IWUSR)); diff -urN linux-2.6.33.1/drivers/i2c/i2c-dev.c topcliff-2.6.33.1/drivers/i2c/i2c-dev.c --- linux-2.6.33.1/drivers/i2c/i2c-dev.c 2010-03-16 01:09:39.000000000 +0900 +++ topcliff-2.6.33.1/drivers/i2c/i2c-dev.c 2010-03-24 11:21:29.000000000 +0900 @@ -36,7 +36,7 @@ #include #include #include - +#include "busses/pch_i2c_hal.h" static struct i2c_driver i2cdev_driver; /* @@ -147,6 +147,11 @@ 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 +377,12 @@ struct i2c_client *client = (struct i2c_client *)file->private_data; unsigned long funcs; + unsigned long ioh_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 +438,22 @@ */ client->adapter->timeout = msecs_to_jiffies(arg * 10); break; + case I2C_MODE_SEL: + ioh_mode = arg; + + if (ioh_mode <= 4) { + msgbuf[0] = ioh_mode; + msg.buf = msgbuf; + msg.len = 1; + msg.flags = (ioh_mode <=1) ? \ + (IOH_BUFFER_MODE_ENABLE) : \ + (IOH_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