diff options
Diffstat (limited to 'drivers/i2c')
55 files changed, 1172 insertions, 416 deletions
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 24383afdda7..11935f66fcd 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -1,5 +1,5 @@ # -# Character device configuration +# I2C subsystem configuration # menu "I2C support" diff --git a/drivers/i2c/algos/Kconfig b/drivers/i2c/algos/Kconfig index 30408015d23..c034820615b 100644 --- a/drivers/i2c/algos/Kconfig +++ b/drivers/i2c/algos/Kconfig @@ -53,12 +53,6 @@ config I2C_ALGO8XX tristate "MPC8xx CPM I2C interface" depends on 8xx && I2C -config I2C_ALGO_SIBYTE - tristate "SiByte SMBus interface" - depends on SIBYTE_SB1xxx_SOC && I2C - help - Supports the SiByte SOC on-chip I2C interfaces (2 channels). - config I2C_ALGO_SGI tristate "I2C SGI interfaces" depends on I2C && (SGI_IP22 || SGI_IP32 || X86_VISWS) diff --git a/drivers/i2c/algos/Makefile b/drivers/i2c/algos/Makefile index 867fe1f6740..208be04a3db 100644 --- a/drivers/i2c/algos/Makefile +++ b/drivers/i2c/algos/Makefile @@ -6,7 +6,6 @@ obj-$(CONFIG_I2C_ALGOBIT) += i2c-algo-bit.o obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o obj-$(CONFIG_I2C_ALGOPCA) += i2c-algo-pca.o obj-$(CONFIG_I2C_ALGOITE) += i2c-algo-ite.o -obj-$(CONFIG_I2C_ALGO_SIBYTE) += i2c-algo-sibyte.o obj-$(CONFIG_I2C_ALGO_SGI) += i2c-algo-sgi.o ifeq ($(CONFIG_I2C_DEBUG_ALGO),y) diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c index ab230c033f9..21c36bfb5e6 100644 --- a/drivers/i2c/algos/i2c-algo-bit.c +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -76,17 +76,15 @@ static inline void scllo(struct i2c_algo_bit_data *adap) * Raise scl line, and do checking for delays. This is necessary for slower * devices. */ -static inline int sclhi(struct i2c_algo_bit_data *adap) +static int sclhi(struct i2c_algo_bit_data *adap) { unsigned long start; setscl(adap,1); /* Not all adapters have scl sense line... */ - if (adap->getscl == NULL ) { - udelay(adap->udelay); - return 0; - } + if (!adap->getscl) + goto done; start=jiffies; while (! getscl(adap) ) { @@ -101,6 +99,8 @@ static inline int sclhi(struct i2c_algo_bit_data *adap) cond_resched(); } DEBSTAT(printk(KERN_DEBUG "needed %ld jiffies\n", jiffies-start)); + +done: udelay(adap->udelay); return 0; } @@ -121,7 +121,6 @@ static void i2c_repstart(struct i2c_algo_bit_data *adap) DEBPROTO(printk(" Sr ")); setsda(adap,1); sclhi(adap); - udelay(adap->udelay); sdalo(adap); scllo(adap); @@ -306,7 +305,7 @@ bailout: * 0 chip did not answer * -x transmission error */ -static inline int try_address(struct i2c_adapter *i2c_adap, +static int try_address(struct i2c_adapter *i2c_adap, unsigned char addr, int retries) { struct i2c_algo_bit_data *adap = i2c_adap->algo_data; @@ -354,15 +353,11 @@ static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) return (retval<0)? retval : -EFAULT; /* got a better one ?? */ } -#if 0 - /* from asm/delay.h */ - __delay(adap->mdelay * (loops_per_sec / 1000) ); -#endif } return wrcount; } -static inline int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) +static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) { int inval; int rdcount=0; /* counts bytes read */ @@ -412,7 +407,7 @@ static inline int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) * -x an error occurred (like: -EREMOTEIO if the device did not answer, or * -ETIMEDOUT, for example if the lines are stuck...) */ -static inline int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) +static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) { unsigned short flags = msg->flags; unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK; @@ -517,7 +512,7 @@ static u32 bit_func(struct i2c_adapter *adap) /* -----exported algorithm data: ------------------------------------- */ -static struct i2c_algorithm i2c_bit_algo = { +static const struct i2c_algorithm i2c_bit_algo = { .master_xfer = bit_xfer, .functionality = bit_func, }; diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c index b88a6fcf7bd..9081c9fbcd2 100644 --- a/drivers/i2c/algos/i2c-algo-pca.c +++ b/drivers/i2c/algos/i2c-algo-pca.c @@ -355,7 +355,7 @@ static int pca_init(struct i2c_algo_pca_data *adap) return 0; } -static struct i2c_algorithm pca_algo = { +static const struct i2c_algorithm pca_algo = { .master_xfer = pca_xfer, .functionality = pca_func, }; diff --git a/drivers/i2c/algos/i2c-algo-pcf.c b/drivers/i2c/algos/i2c-algo-pcf.c index 5b24930adb5..3b200339896 100644 --- a/drivers/i2c/algos/i2c-algo-pcf.c +++ b/drivers/i2c/algos/i2c-algo-pcf.c @@ -458,7 +458,7 @@ static u32 pcf_func(struct i2c_adapter *adap) /* -----exported algorithm data: ------------------------------------- */ -static struct i2c_algorithm pcf_algo = { +static const struct i2c_algorithm pcf_algo = { .master_xfer = pcf_xfer, .functionality = pcf_func, }; diff --git a/drivers/i2c/algos/i2c-algo-sgi.c b/drivers/i2c/algos/i2c-algo-sgi.c index 932c4fa86c7..490d99997fd 100644 --- a/drivers/i2c/algos/i2c-algo-sgi.c +++ b/drivers/i2c/algos/i2c-algo-sgi.c @@ -157,7 +157,7 @@ static u32 sgi_func(struct i2c_adapter *adap) return I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm sgi_algo = { +static const struct i2c_algorithm sgi_algo = { .master_xfer = sgi_xfer, .functionality = sgi_func, }; diff --git a/drivers/i2c/algos/i2c-algo-sibyte.c b/drivers/i2c/algos/i2c-algo-sibyte.c deleted file mode 100644 index 32d41c6fac0..00000000000 --- a/drivers/i2c/algos/i2c-algo-sibyte.c +++ /dev/null @@ -1,215 +0,0 @@ -/* ------------------------------------------------------------------------- */ -/* i2c-algo-sibyte.c i2c driver algorithms for bit-shift adapters */ -/* ------------------------------------------------------------------------- */ -/* Copyright (C) 2001,2002,2003 Broadcom Corporation - Copyright (C) 1995-2000 Simon G. Vogl - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* ------------------------------------------------------------------------- */ - -/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even - Frodo Looijaard <frodol@dds.nl>. */ - -/* Ported for SiByte SOCs by Broadcom Corporation. */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> - -#include <asm/io.h> -#include <asm/sibyte/sb1250_regs.h> -#include <asm/sibyte/sb1250_smbus.h> - -#include <linux/i2c.h> -#include <linux/i2c-algo-sibyte.h> - -/* ----- global defines ----------------------------------------------- */ -#define SMB_CSR(a,r) ((long)(a->reg_base + r)) - -/* ----- global variables --------------------------------------------- */ - -/* module parameters: - */ -static int bit_scan; /* have a look at what's hanging 'round */ - - -static int smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr, - unsigned short flags, char read_write, - u8 command, int size, union i2c_smbus_data * data) -{ - struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data; - int data_bytes = 0; - int error; - - while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY) - ; - - switch (size) { - case I2C_SMBUS_QUICK: - csr_out32((V_SMB_ADDR(addr) | (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) | - V_SMB_TT_QUICKCMD), SMB_CSR(adap, R_SMB_START)); - break; - case I2C_SMBUS_BYTE: - if (read_write == I2C_SMBUS_READ) { - csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE), - SMB_CSR(adap, R_SMB_START)); - data_bytes = 1; - } else { - csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD)); - csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE), - SMB_CSR(adap, R_SMB_START)); - } - break; - case I2C_SMBUS_BYTE_DATA: - csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD)); - if (read_write == I2C_SMBUS_READ) { - csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE), - SMB_CSR(adap, R_SMB_START)); - data_bytes = 1; - } else { - csr_out32(V_SMB_LB(data->byte), SMB_CSR(adap, R_SMB_DATA)); - csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE), - SMB_CSR(adap, R_SMB_START)); - } - break; - case I2C_SMBUS_WORD_DATA: - csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD)); - if (read_write == I2C_SMBUS_READ) { - csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE), - SMB_CSR(adap, R_SMB_START)); - data_bytes = 2; - } else { - csr_out32(V_SMB_LB(data->word & 0xff), SMB_CSR(adap, R_SMB_DATA)); - csr_out32(V_SMB_MB(data->word >> 8), SMB_CSR(adap, R_SMB_DATA)); - csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE), - SMB_CSR(adap, R_SMB_START)); - } - break; - default: - return -1; /* XXXKW better error code? */ - } - - while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY) - ; - - error = csr_in32(SMB_CSR(adap, R_SMB_STATUS)); - if (error & M_SMB_ERROR) { - /* Clear error bit by writing a 1 */ - csr_out32(M_SMB_ERROR, SMB_CSR(adap, R_SMB_STATUS)); - return -1; /* XXXKW better error code? */ - } - - if (data_bytes == 1) - data->byte = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xff; - if (data_bytes == 2) - data->word = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xffff; - - return 0; -} - -static int algo_control(struct i2c_adapter *adapter, - unsigned int cmd, unsigned long arg) -{ - return 0; -} - -static u32 bit_func(struct i2c_adapter *adap) -{ - return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | - I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA); -} - - -/* -----exported algorithm data: ------------------------------------- */ - -static struct i2c_algorithm i2c_sibyte_algo = { - .smbus_xfer = smbus_xfer, - .algo_control = algo_control, /* ioctl */ - .functionality = bit_func, -}; - -/* - * registering functions to load algorithms at runtime - */ -int i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed) -{ - int i; - struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data; - - /* register new adapter to i2c module... */ - i2c_adap->algo = &i2c_sibyte_algo; - - /* Set the frequency to 100 kHz */ - csr_out32(speed, SMB_CSR(adap,R_SMB_FREQ)); - csr_out32(0, SMB_CSR(adap,R_SMB_CONTROL)); - - /* scan bus */ - if (bit_scan) { - union i2c_smbus_data data; - int rc; - printk(KERN_INFO " i2c-algo-sibyte.o: scanning bus %s.\n", - i2c_adap->name); - for (i = 0x00; i < 0x7f; i++) { - /* XXXKW is this a realistic probe? */ - rc = smbus_xfer(i2c_adap, i, 0, I2C_SMBUS_READ, 0, - I2C_SMBUS_BYTE_DATA, &data); - if (!rc) { - printk("(%02x)",i); - } else - printk("."); - } - printk("\n"); - } - - return i2c_add_adapter(i2c_adap); -} - - -int i2c_sibyte_del_bus(struct i2c_adapter *adap) -{ - int res; - - if ((res = i2c_del_adapter(adap)) < 0) - return res; - - return 0; -} - -int __init i2c_algo_sibyte_init (void) -{ - printk("i2c-algo-sibyte.o: i2c SiByte algorithm module\n"); - return 0; -} - - -EXPORT_SYMBOL(i2c_sibyte_add_bus); -EXPORT_SYMBOL(i2c_sibyte_del_bus); - -#ifdef MODULE -MODULE_AUTHOR("Kip Walker, Broadcom Corp."); -MODULE_DESCRIPTION("SiByte I2C-Bus algorithm"); -module_param(bit_scan, int, 0); -MODULE_PARM_DESC(bit_scan, "Scan for active chips on the bus"); -MODULE_LICENSE("GPL"); - -int init_module(void) -{ - return i2c_algo_sibyte_init(); -} - -void cleanup_module(void) -{ -} -#endif diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 884320e7040..9e56c3989d6 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -75,11 +75,11 @@ config I2C_AMD8111 will be called i2c-amd8111. config I2C_AU1550 - tristate "Au1550 SMBus interface" - depends on I2C && SOC_AU1550 + tristate "Au1550/Au1200 SMBus interface" + depends on I2C && (SOC_AU1550 || SOC_AU1200) help If you say yes to this option, support will be included for the - Au1550 SMBus interface. + Au1550 and Au1200 SMBus interface. This driver can also be built as a module. If so, the module will be called i2c-au1550. @@ -287,6 +287,16 @@ config I2C_OCORES This driver can also be built as a module. If so, the module will be called i2c-ocores. +config I2C_OMAP + tristate "OMAP I2C adapter" + depends on I2C && ARCH_OMAP + default y if MACH_OMAP_H3 || MACH_OMAP_OSK + help + If you say yes to this option, support will be included for the + I2C interface on the Texas Instruments OMAP1/2 family of processors. + Like OMAP1510/1610/1710/5912 and OMAP242x. + For details see http://www.ti.com/omap. + config I2C_PARPORT tristate "Parallel port adapter" depends on I2C && PARPORT @@ -482,19 +492,19 @@ config I2C_VIA will be called i2c-via. config I2C_VIAPRO - tristate "VIA 82C596/82C686/823x" + tristate "VIA 82C596/82C686/82xx" depends on I2C && PCI help If you say yes to this option, support will be included for the VIA - 82C596/82C686/823x I2C interfaces. Specifically, the following + 82C596/82C686/82xx I2C interfaces. Specifically, the following chipsets are supported: - 82C596A/B - 82C686A/B - 8231 - 8233 - 8233A - 8235 - 8237 + VT82C596A/B + VT82C686A/B + VT8231 + VT8233/A + VT8235 + VT8237R/A + VT8251 This driver can also be built as a module. If so, the module will be called i2c-viapro. diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index ac56df53155..493c87289b6 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_I2C_MPC) += i2c-mpc.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o +obj-$(CONFIG_I2C_OMAP) += i2c-omap.o obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c index d3ef46aeeb3..e75d339a348 100644 --- a/drivers/i2c/busses/i2c-ali1535.c +++ b/drivers/i2c/busses/i2c-ali1535.c @@ -468,7 +468,7 @@ static u32 ali1535_func(struct i2c_adapter *adapter) I2C_FUNC_SMBUS_BLOCK_DATA; } -static struct i2c_algorithm smbus_algorithm = { +static const struct i2c_algorithm smbus_algorithm = { .smbus_xfer = ali1535_access, .functionality = ali1535_func, }; diff --git a/drivers/i2c/busses/i2c-ali1563.c b/drivers/i2c/busses/i2c-ali1563.c index e6f63208fc4..33fbb47100a 100644 --- a/drivers/i2c/busses/i2c-ali1563.c +++ b/drivers/i2c/busses/i2c-ali1563.c @@ -367,7 +367,7 @@ static void ali1563_shutdown(struct pci_dev *dev) release_region(ali1563_smba,ALI1563_SMB_IOSIZE); } -static struct i2c_algorithm ali1563_algorithm = { +static const struct i2c_algorithm ali1563_algorithm = { .smbus_xfer = ali1563_access, .functionality = ali1563_func, }; diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c index 7a5c0941dbc..3f11b6e1a34 100644 --- a/drivers/i2c/busses/i2c-ali15x3.c +++ b/drivers/i2c/busses/i2c-ali15x3.c @@ -463,7 +463,7 @@ static u32 ali15x3_func(struct i2c_adapter *adapter) I2C_FUNC_SMBUS_BLOCK_DATA; } -static struct i2c_algorithm smbus_algorithm = { +static const struct i2c_algorithm smbus_algorithm = { .smbus_xfer = ali15x3_access, .functionality = ali15x3_func, }; diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c index 1750dedaf4b..2d21afdc5b1 100644 --- a/drivers/i2c/busses/i2c-amd756.c +++ b/drivers/i2c/busses/i2c-amd756.c @@ -294,7 +294,7 @@ static u32 amd756_func(struct i2c_adapter *adapter) I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL; } -static struct i2c_algorithm smbus_algorithm = { +static const struct i2c_algorithm smbus_algorithm = { .smbus_xfer = amd756_access, .functionality = amd756_func, }; diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c index e5ef560e686..0fbc7186c91 100644 --- a/drivers/i2c/busses/i2c-amd8111.c +++ b/drivers/i2c/busses/i2c-amd8111.c @@ -316,7 +316,7 @@ static u32 amd8111_func(struct i2c_adapter *adapter) I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC; } -static struct i2c_algorithm smbus_algorithm = { +static const struct i2c_algorithm smbus_algorithm = { .smbus_xfer = amd8111_access, .functionality = amd8111_func, }; diff --git a/drivers/i2c/busses/i2c-au1550.c b/drivers/i2c/busses/i2c-au1550.c index d06edce03bf..d7e7c359fc3 100644 --- a/drivers/i2c/busses/i2c-au1550.c +++ b/drivers/i2c/busses/i2c-au1550.c @@ -34,8 +34,7 @@ #include <linux/errno.h> #include <linux/i2c.h> -#include <asm/mach-au1x00/au1000.h> -#include <asm/mach-pb1x00/pb1550.h> +#include <asm/mach-au1x00/au1xxx.h> #include <asm/mach-au1x00/au1xxx_psc.h> #include "i2c-au1550.h" @@ -118,13 +117,19 @@ do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd) /* Reset the FIFOs, clear events. */ - sp->psc_smbpcr = PSC_SMBPCR_DC; + stat = sp->psc_smbstat; sp->psc_smbevnt = PSC_SMBEVNT_ALLCLR; au_sync(); - do { - stat = sp->psc_smbpcr; + + if (!(stat & PSC_SMBSTAT_TE) || !(stat & PSC_SMBSTAT_RE)) { + sp->psc_smbpcr = PSC_SMBPCR_DC; au_sync(); - } while ((stat & PSC_SMBPCR_DC) != 0); + do { + stat = sp->psc_smbpcr; + au_sync(); + } while ((stat & PSC_SMBPCR_DC) != 0); + udelay(50); + } /* Write out the i2c chip address and specify operation */ @@ -279,10 +284,10 @@ au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) static u32 au1550_func(struct i2c_adapter *adap) { - return I2C_FUNC_I2C; + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm au1550_algo = { +static const struct i2c_algorithm au1550_algo = { .master_xfer = au1550_xfer, .functionality = au1550_func, }; diff --git a/drivers/i2c/busses/i2c-elektor.c b/drivers/i2c/busses/i2c-elektor.c index 59f8308c235..caa8e5c8bfb 100644 --- a/drivers/i2c/busses/i2c-elektor.c +++ b/drivers/i2c/busses/i2c-elektor.c @@ -196,7 +196,6 @@ static struct i2c_algo_pcf_data pcf_isa_data = { .getclock = pcf_isa_getclock, .waitforpin = pcf_isa_waitforpin, .udelay = 10, - .mdelay = 10, .timeout = 100, }; diff --git a/drivers/i2c/busses/i2c-hydra.c b/drivers/i2c/busses/i2c-hydra.c index e0cb3b0f92f..457d48a0ab9 100644 --- a/drivers/i2c/busses/i2c-hydra.c +++ b/drivers/i2c/busses/i2c-hydra.c @@ -99,7 +99,6 @@ static struct i2c_algo_bit_data hydra_bit_data = { .getsda = hydra_bit_getsda, .getscl = hydra_bit_getscl, .udelay = 5, - .mdelay = 5, .timeout = HZ }; diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 7be1d0a3e8f..bbb2fbee836 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -434,7 +434,7 @@ static u32 i801_func(struct i2c_adapter *adapter) | (isich4 ? I2C_FUNC_SMBUS_HWPEC_CALC : 0); } -static struct i2c_algorithm smbus_algorithm = { +static const struct i2c_algorithm smbus_algorithm = { .smbus_xfer = i801_access, .functionality = i801_func, }; diff --git a/drivers/i2c/busses/i2c-i810.c b/drivers/i2c/busses/i2c-i810.c index 748be30f2ba..b66fb6bb187 100644 --- a/drivers/i2c/busses/i2c-i810.c +++ b/drivers/i2c/busses/i2c-i810.c @@ -166,7 +166,6 @@ static struct i2c_algo_bit_data i810_i2c_bit_data = { .getsda = bit_i810i2c_getsda, .getscl = bit_i810i2c_getscl, .udelay = CYCLE_DELAY, - .mdelay = CYCLE_DELAY, .timeout = TIMEOUT, }; @@ -182,7 +181,6 @@ static struct i2c_algo_bit_data i810_ddc_bit_data = { .getsda = bit_i810ddc_getsda, .getscl = bit_i810ddc_getscl, .udelay = CYCLE_DELAY, - .mdelay = CYCLE_DELAY, .timeout = TIMEOUT, }; diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c index 0599bbd65d9..5bccb5d6831 100644 --- a/drivers/i2c/busses/i2c-ibm_iic.c +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -625,7 +625,7 @@ static u32 iic_func(struct i2c_adapter *adap) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR; } -static struct i2c_algorithm iic_algo = { +static const struct i2c_algorithm iic_algo = { .master_xfer = iic_xfer, .functionality = iic_func }; diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c index 48c56939c86..8e413150af3 100644 --- a/drivers/i2c/busses/i2c-iop3xx.c +++ b/drivers/i2c/busses/i2c-iop3xx.c @@ -401,7 +401,7 @@ iop3xx_i2c_func(struct i2c_adapter *adap) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm iop3xx_i2c_algo = { +static const struct i2c_algorithm iop3xx_i2c_algo = { .master_xfer = iop3xx_i2c_master_xfer, .algo_control = iop3xx_i2c_algo_control, .functionality = iop3xx_i2c_func, diff --git a/drivers/i2c/busses/i2c-isa.c b/drivers/i2c/busses/i2c-isa.c index c3e1d3e888d..4380653748a 100644 --- a/drivers/i2c/busses/i2c-isa.c +++ b/drivers/i2c/busses/i2c-isa.c @@ -43,7 +43,7 @@ static u32 isa_func(struct i2c_adapter *adapter); /* This is the actual algorithm we define */ -static struct i2c_algorithm isa_algorithm = { +static const struct i2c_algorithm isa_algorithm = { .functionality = isa_func, }; @@ -89,9 +89,14 @@ int i2c_isa_add_driver(struct i2c_driver *driver) dev_dbg(&isa_adapter.dev, "Driver %s registered\n", driver->driver.name); /* Now look for clients */ - driver->attach_adapter(&isa_adapter); - - return 0; + res = driver->attach_adapter(&isa_adapter); + if (res) { + dev_err(&isa_adapter.dev, + "Driver %s failed to attach adapter, unregistering\n", + driver->driver.name); + driver_unregister(&driver->driver); + } + return res; } int i2c_isa_del_driver(struct i2c_driver *driver) @@ -125,6 +130,8 @@ int i2c_isa_del_driver(struct i2c_driver *driver) static int __init i2c_isa_init(void) { + int err; + mutex_init(&isa_adapter.clist_lock); INIT_LIST_HEAD(&isa_adapter.clients); @@ -133,8 +140,16 @@ static int __init i2c_isa_init(void) sprintf(isa_adapter.dev.bus_id, "i2c-%d", isa_adapter.nr); isa_adapter.dev.driver = &i2c_adapter_driver; isa_adapter.dev.release = &i2c_adapter_dev_release; - device_register(&isa_adapter.dev); - device_create_file(&isa_adapter.dev, &dev_attr_name); + err = device_register(&isa_adapter.dev); + if (err) { + printk(KERN_ERR "i2c-isa: Failed to register device\n"); + goto exit; + } + err = device_create_file(&isa_adapter.dev, &dev_attr_name); + if (err) { + printk(KERN_ERR "i2c-isa: Failed to create name file\n"); + goto exit_unregister; + } /* Add this adapter to the i2c_adapter class */ memset(&isa_adapter.class_dev, 0x00, sizeof(struct class_device)); @@ -142,11 +157,24 @@ static int __init i2c_isa_init(void) isa_adapter.class_dev.class = &i2c_adapter_class; strlcpy(isa_adapter.class_dev.class_id, isa_adapter.dev.bus_id, BUS_ID_SIZE); - class_device_register(&isa_adapter.class_dev); + err = class_device_register(&isa_adapter.class_dev); + if (err) { + printk(KERN_ERR "i2c-isa: Failed to register class device\n"); + goto exit_remove_name; + } dev_dbg(&isa_adapter.dev, "%s registered\n", isa_adapter.name); return 0; + +exit_remove_name: + device_remove_file(&isa_adapter.dev, &dev_attr_name); +exit_unregister: + init_completion(&isa_adapter.dev_released); /* Needed? */ + device_unregister(&isa_adapter.dev); + wait_for_completion(&isa_adapter.dev_released); +exit: + return err; } static void __exit i2c_isa_exit(void) diff --git a/drivers/i2c/busses/i2c-ixp2000.c b/drivers/i2c/busses/i2c-ixp2000.c index cd6f45d186a..dd3f4cd3aa6 100644 --- a/drivers/i2c/busses/i2c-ixp2000.c +++ b/drivers/i2c/busses/i2c-ixp2000.c @@ -114,7 +114,6 @@ static int ixp2000_i2c_probe(struct platform_device *plat_dev) drv_data->algo_data.getsda = ixp2000_bit_getsda; drv_data->algo_data.getscl = ixp2000_bit_getscl; drv_data->algo_data.udelay = 6; - drv_data->algo_data.mdelay = 6; drv_data->algo_data.timeout = 100; drv_data->adapter.id = I2C_HW_B_IXP2000, diff --git a/drivers/i2c/busses/i2c-ixp4xx.c b/drivers/i2c/busses/i2c-ixp4xx.c index 2ed07112d68..ab573254a8a 100644 --- a/drivers/i2c/busses/i2c-ixp4xx.c +++ b/drivers/i2c/busses/i2c-ixp4xx.c @@ -122,7 +122,6 @@ static int ixp4xx_i2c_probe(struct platform_device *plat_dev) drv_data->algo_data.getsda = ixp4xx_bit_getsda; drv_data->algo_data.getscl = ixp4xx_bit_getscl; drv_data->algo_data.udelay = 10; - drv_data->algo_data.mdelay = 10; drv_data->algo_data.timeout = 100; drv_data->adapter.id = I2C_HW_B_IXP4XX; diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 377ab40944b..155a986de51 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -272,7 +272,7 @@ static u32 mpc_functionality(struct i2c_adapter *adap) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm mpc_algo = { +static const struct i2c_algorithm mpc_algo = { .master_xfer = mpc_xfer, .functionality = mpc_functionality, }; diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index ac5cde1bbd2..eacbaf745b6 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -431,7 +431,7 @@ mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) return num; } -static struct i2c_algorithm mv64xxx_i2c_algo = { +static const struct i2c_algorithm mv64xxx_i2c_algo = { .master_xfer = mv64xxx_i2c_xfer, .functionality = mv64xxx_i2c_functionality, }; diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c index 604b49e22df..e0292e414ab 100644 --- a/drivers/i2c/busses/i2c-nforce2.c +++ b/drivers/i2c/busses/i2c-nforce2.c @@ -109,7 +109,7 @@ static s32 nforce2_access(struct i2c_adapter *adap, u16 addr, static u32 nforce2_func(struct i2c_adapter *adapter); -static struct i2c_algorithm smbus_algorithm = { +static const struct i2c_algorithm smbus_algorithm = { .smbus_xfer = nforce2_access, .functionality = nforce2_func, }; diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 592824087c4..952a28d485c 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -199,7 +199,7 @@ static u32 ocores_func(struct i2c_adapter *adap) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm ocores_algorithm = { +static const struct i2c_algorithm ocores_algorithm = { .master_xfer = ocores_xfer, .functionality = ocores_func, }; diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c new file mode 100644 index 00000000000..81d87d2c2a2 --- /dev/null +++ b/drivers/i2c/busses/i2c-omap.c @@ -0,0 +1,676 @@ +/* + * TI OMAP I2C master mode driver + * + * Copyright (C) 2003 MontaVista Software, Inc. + * Copyright (C) 2004 Texas Instruments. + * + * Updated to work with multiple I2C interfaces on 24xx by + * Tony Lindgren <tony@atomide.com> and Imre Deak <imre.deak@nokia.com> + * Copyright (C) 2005 Nokia Corporation + * + * Cleaned up by Juha Yrjölä <juha.yrjola@nokia.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/completion.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#include <asm/io.h> + +/* timeout waiting for the controller to respond */ +#define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000)) + +#define OMAP_I2C_REV_REG 0x00 +#define OMAP_I2C_IE_REG 0x04 +#define OMAP_I2C_STAT_REG 0x08 +#define OMAP_I2C_IV_REG 0x0c +#define OMAP_I2C_SYSS_REG 0x10 +#define OMAP_I2C_BUF_REG 0x14 +#define OMAP_I2C_CNT_REG 0x18 +#define OMAP_I2C_DATA_REG 0x1c +#define OMAP_I2C_SYSC_REG 0x20 +#define OMAP_I2C_CON_REG 0x24 +#define OMAP_I2C_OA_REG 0x28 +#define OMAP_I2C_SA_REG 0x2c +#define OMAP_I2C_PSC_REG 0x30 +#define OMAP_I2C_SCLL_REG 0x34 +#define OMAP_I2C_SCLH_REG 0x38 +#define OMAP_I2C_SYSTEST_REG 0x3c + +/* I2C Interrupt Enable Register (OMAP_I2C_IE): */ +#define OMAP_I2C_IE_XRDY (1 << 4) /* TX data ready int enable */ +#define OMAP_I2C_IE_RRDY (1 << 3) /* RX data ready int enable */ +#define OMAP_I2C_IE_ARDY (1 << 2) /* Access ready int enable */ +#define OMAP_I2C_IE_NACK (1 << 1) /* No ack interrupt enable */ +#define OMAP_I2C_IE_AL (1 << 0) /* Arbitration lost int ena */ + +/* I2C Status Register (OMAP_I2C_STAT): */ +#define OMAP_I2C_STAT_SBD (1 << 15) /* Single byte data */ +#define OMAP_I2C_STAT_BB (1 << 12) /* Bus busy */ +#define OMAP_I2C_STAT_ROVR (1 << 11) /* Receive overrun */ +#define OMAP_I2C_STAT_XUDF (1 << 10) /* Transmit underflow */ +#define OMAP_I2C_STAT_AAS (1 << 9) /* Address as slave */ +#define OMAP_I2C_STAT_AD0 (1 << 8) /* Address zero */ +#define OMAP_I2C_STAT_XRDY (1 << 4) /* Transmit data ready */ +#define OMAP_I2C_STAT_RRDY (1 << 3) /* Receive data ready */ +#define OMAP_I2C_STAT_ARDY (1 << 2) /* Register access ready */ +#define OMAP_I2C_STAT_NACK (1 << 1) /* No ack interrupt enable */ +#define OMAP_I2C_STAT_AL (1 << 0) /* Arbitration lost int ena */ + +/* I2C Buffer Configuration Register (OMAP_I2C_BUF): */ +#define OMAP_I2C_BUF_RDMA_EN (1 << 15) /* RX DMA channel enable */ +#define OMAP_I2C_BUF_XDMA_EN (1 << 7) /* TX DMA channel enable */ + +/* I2C Configuration Register (OMAP_I2C_CON): */ +#define OMAP_I2C_CON_EN (1 << 15) /* I2C module enable */ +#define OMAP_I2C_CON_BE (1 << 14) /* Big endian mode */ +#define OMAP_I2C_CON_STB (1 << 11) /* Start byte mode (master) */ +#define OMAP_I2C_CON_MST (1 << 10) /* Master/slave mode */ +#define OMAP_I2C_CON_TRX (1 << 9) /* TX/RX mode (master only) */ +#define OMAP_I2C_CON_XA (1 << 8) /* Expand address */ +#define OMAP_I2C_CON_RM (1 << 2) /* Repeat mode (master only) */ +#define OMAP_I2C_CON_STP (1 << 1) /* Stop cond (master only) */ +#define OMAP_I2C_CON_STT (1 << 0) /* Start condition (master) */ + +/* I2C System Test Register (OMAP_I2C_SYSTEST): */ +#ifdef DEBUG +#define OMAP_I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */ +#define OMAP_I2C_SYSTEST_FREE (1 << 14) /* Free running mode */ +#define OMAP_I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */ +#define OMAP_I2C_SYSTEST_TMODE_SHIFT (12) /* Test mode select */ +#define OMAP_I2C_SYSTEST_SCL_I (1 << 3) /* SCL line sense in */ +#define OMAP_I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive out */ +#define OMAP_I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense in */ +#define OMAP_I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive out */ +#endif + +/* I2C System Status register (OMAP_I2C_SYSS): */ +#define OMAP_I2C_SYSS_RDONE (1 << 0) /* Reset Done */ + +/* I2C System Configuration Register (OMAP_I2C_SYSC): */ +#define OMAP_I2C_SYSC_SRST (1 << 1) /* Soft Reset */ + +/* REVISIT: Use platform_data instead of module parameters */ +/* Fast Mode = 400 kHz, Standard = 100 kHz */ +static int clock = 100; /* Default: 100 kHz */ +module_param(clock, int, 0); +MODULE_PARM_DESC(clock, "Set I2C clock in kHz: 400=fast mode (default == 100)"); + +struct omap_i2c_dev { + struct device *dev; + void __iomem *base; /* virtual */ + int irq; + struct clk *iclk; /* Interface clock */ + struct clk *fclk; /* Functional clock */ + struct completion cmd_complete; + struct resource *ioarea; + u16 cmd_err; + u8 *buf; + size_t buf_len; + struct i2c_adapter adapter; + unsigned rev1:1; +}; + +static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev, + int reg, u16 val) +{ + __raw_writew(val, i2c_dev->base + reg); +} + +static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *i2c_dev, int reg) +{ + return __raw_readw(i2c_dev->base + reg); +} + +static int omap_i2c_get_clocks(struct omap_i2c_dev *dev) +{ + if (cpu_is_omap16xx() || cpu_is_omap24xx()) { + dev->iclk = clk_get(dev->dev, "i2c_ick"); + if (IS_ERR(dev->iclk)) { + dev->iclk = NULL; + return -ENODEV; + } + } + + dev->fclk = clk_get(dev->dev, "i2c_fck"); + if (IS_ERR(dev->fclk)) { + if (dev->iclk != NULL) { + clk_put(dev->iclk); + dev->iclk = NULL; + } + dev->fclk = NULL; + return -ENODEV; + } + + return 0; +} + +static void omap_i2c_put_clocks(struct omap_i2c_dev *dev) +{ + clk_put(dev->fclk); + dev->fclk = NULL; + if (dev->iclk != NULL) { + clk_put(dev->iclk); + dev->iclk = NULL; + } +} + +static void omap_i2c_enable_clocks(struct omap_i2c_dev *dev) +{ + if (dev->iclk != NULL) + clk_enable(dev->iclk); + clk_enable(dev->fclk); +} + +static void omap_i2c_disable_clocks(struct omap_i2c_dev *dev) +{ + if (dev->iclk != NULL) + clk_disable(dev->iclk); + clk_disable(dev->fclk); +} + +static int omap_i2c_init(struct omap_i2c_dev *dev) +{ + u16 psc = 0; + unsigned long fclk_rate = 12000000; + unsigned long timeout; + + if (!dev->rev1) { + omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, OMAP_I2C_SYSC_SRST); + /* For some reason we need to set the EN bit before the + * reset done bit gets set. */ + timeout = jiffies + OMAP_I2C_TIMEOUT; + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); + while (!(omap_i2c_read_reg(dev, OMAP_I2C_SYSS_REG) & + OMAP_I2C_SYSS_RDONE)) { + if (time_after(jiffies, timeout)) { + dev_warn(dev->dev, "timeout waiting" + "for controller reset\n"); + return -ETIMEDOUT; + } + msleep(1); + } + } + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); + + if (cpu_class_is_omap1()) { + struct clk *armxor_ck; + + armxor_ck = clk_get(NULL, "armxor_ck"); + if (IS_ERR(armxor_ck)) + dev_warn(dev->dev, "Could not get armxor_ck\n"); + else { + fclk_rate = clk_get_rate(armxor_ck); + clk_put(armxor_ck); + } + /* TRM for 5912 says the I2C clock must be prescaled to be + * between 7 - 12 MHz. The XOR input clock is typically + * 12, 13 or 19.2 MHz. So we should have code that produces: + * + * XOR MHz Divider Prescaler + * 12 1 0 + * 13 2 1 + * 19.2 2 1 + */ + if (fclk_rate > 16000000) + psc = (fclk_rate + 8000000) / 12000000; + } + + /* Setup clock prescaler to obtain approx 12MHz I2C module clock: */ + omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, psc); + + /* Program desired operating rate */ + fclk_rate /= (psc + 1) * 1000; + if (psc > 2) + psc = 2; + + omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, + fclk_rate / (clock * 2) - 7 + psc); + omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, + fclk_rate / (clock * 2) - 7 + psc); + + /* Take the I2C module out of reset: */ + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); + + /* Enable interrupts */ + omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, + (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY | + OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK | + OMAP_I2C_IE_AL)); + return 0; +} + +/* + * Waiting on Bus Busy + */ +static int omap_i2c_wait_for_bb(struct omap_i2c_dev *dev) +{ + unsigned long timeout; + + timeout = jiffies + OMAP_I2C_TIMEOUT; + while (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) { + if (time_after(jiffies, timeout)) { + dev_warn(dev->dev, "timeout waiting for bus ready\n"); + return -ETIMEDOUT; + } + msleep(1); + } + + return 0; +} + +/* + * Low level master read/write transaction. + */ +static int omap_i2c_xfer_msg(struct i2c_adapter *adap, + struct i2c_msg *msg, int stop) +{ + struct omap_i2c_dev *dev = i2c_get_adapdata(adap); + int r; + u16 w; + + dev_dbg(dev->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n", + msg->addr, msg->len, msg->flags, stop); + + if (msg->len == 0) + return -EINVAL; + + omap_i2c_write_reg(dev, OMAP_I2C_SA_REG, msg->addr); + + /* REVISIT: Could the STB bit of I2C_CON be used with probing? */ + dev->buf = msg->buf; + dev->buf_len = msg->len; + + omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len); + + init_completion(&dev->cmd_complete); + dev->cmd_err = 0; + + w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT; + if (msg->flags & I2C_M_TEN) + w |= OMAP_I2C_CON_XA; + if (!(msg->flags & I2C_M_RD)) + w |= OMAP_I2C_CON_TRX; + if (stop) + w |= OMAP_I2C_CON_STP; + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w); + + r = wait_for_completion_interruptible_timeout(&dev->cmd_complete, + OMAP_I2C_TIMEOUT); + dev->buf_len = 0; + if (r < 0) + return r; + if (r == 0) { + dev_err(dev->dev, "controller timed out\n"); + omap_i2c_init(dev); + return -ETIMEDOUT; + } + + if (likely(!dev->cmd_err)) + return 0; + + /* We have an error */ + if (dev->cmd_err & (OMAP_I2C_STAT_AL | OMAP_I2C_STAT_ROVR | + OMAP_I2C_STAT_XUDF)) { + omap_i2c_init(dev); + return -EIO; + } + + if (dev->cmd_err & OMAP_I2C_STAT_NACK) { + if (msg->flags & I2C_M_IGNORE_NAK) + return 0; + if (stop) { + w = omap_i2c_read_reg(dev, OMAP_I2C_CON_REG); + w |= OMAP_I2C_CON_STP; + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w); + } + return -EREMOTEIO; + } + return -EIO; +} + + +/* + * Prepare controller for a transaction and call omap_i2c_xfer_msg + * to do the work during IRQ processing. + */ +static int +omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct omap_i2c_dev *dev = i2c_get_adapdata(adap); + int i; + int r; + + omap_i2c_enable_clocks(dev); + + /* REVISIT: initialize and use adap->retries. This is an optional + * feature */ + if ((r = omap_i2c_wait_for_bb(dev)) < 0) + goto out; + + for (i = 0; i < num; i++) { + r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1))); + if (r != 0) + break; + } + + if (r == 0) + r = num; +out: + omap_i2c_disable_clocks(dev); + return r; +} + +static u32 +omap_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); +} + +static inline void +omap_i2c_complete_cmd(struct omap_i2c_dev *dev, u16 err) +{ + dev->cmd_err |= err; + complete(&dev->cmd_complete); +} + +static inline void +omap_i2c_ack_stat(struct omap_i2c_dev *dev, u16 stat) +{ + omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat); +} + +static irqreturn_t +omap_i2c_rev1_isr(int this_irq, void *dev_id, struct pt_regs *regs) +{ + struct omap_i2c_dev *dev = dev_id; + u16 iv, w; + + iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG); + switch (iv) { + case 0x00: /* None */ + break; + case 0x01: /* Arbitration lost */ + dev_err(dev->dev, "Arbitration lost\n"); + omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_AL); + break; + case 0x02: /* No acknowledgement */ + omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_NACK); + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_STP); + break; + case 0x03: /* Register access ready */ + omap_i2c_complete_cmd(dev, 0); + break; + case 0x04: /* Receive data ready */ + if (dev->buf_len) { + w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG); + *dev->buf++ = w; + dev->buf_len--; + if (dev->buf_len) { + *dev->buf++ = w >> 8; + dev->buf_len--; + } + } else + dev_err(dev->dev, "RRDY IRQ while no data requested\n"); + break; + case 0x05: /* Transmit data ready */ + if (dev->buf_len) { + w = *dev->buf++; + dev->buf_len--; + if (dev->buf_len) { + w |= *dev->buf++ << 8; + dev->buf_len--; + } + omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w); + } else + dev_err(dev->dev, "XRDY IRQ while no data to send\n"); + break; + default: + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static irqreturn_t +omap_i2c_isr(int this_irq, void *dev_id, struct pt_regs *regs) +{ + struct omap_i2c_dev *dev = dev_id; + u16 bits; + u16 stat, w; + int count = 0; + + bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG); + while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) { + dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat); + if (count++ == 100) { + dev_warn(dev->dev, "Too much work in one IRQ\n"); + break; + } + + omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat); + + if (stat & OMAP_I2C_STAT_ARDY) { + omap_i2c_complete_cmd(dev, 0); + continue; + } + if (stat & OMAP_I2C_STAT_RRDY) { + w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG); + if (dev->buf_len) { + *dev->buf++ = w; + dev->buf_len--; + if (dev->buf_len) { + *dev->buf++ = w >> 8; + dev->buf_len--; + } + } else + dev_err(dev->dev, "RRDY IRQ while no data" + "requested\n"); + omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RRDY); + continue; + } + if (stat & OMAP_I2C_STAT_XRDY) { + w = 0; + if (dev->buf_len) { + w = *dev->buf++; + dev->buf_len--; + if (dev->buf_len) { + w |= *dev->buf++ << 8; + dev->buf_len--; + } + } else + dev_err(dev->dev, "XRDY IRQ while no" + "data to send\n"); + omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w); + omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XRDY); + continue; + } + if (stat & OMAP_I2C_STAT_ROVR) { + dev_err(dev->dev, "Receive overrun\n"); + dev->cmd_err |= OMAP_I2C_STAT_ROVR; + } + if (stat & OMAP_I2C_STAT_XUDF) { + dev_err(dev->dev, "Transmit overflow\n"); + dev->cmd_err |= OMAP_I2C_STAT_XUDF; + } + if (stat & OMAP_I2C_STAT_NACK) { + omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_NACK); + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, + OMAP_I2C_CON_STP); + } + if (stat & OMAP_I2C_STAT_AL) { + dev_err(dev->dev, "Arbitration lost\n"); + omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_AL); + } + } + + return count ? IRQ_HANDLED : IRQ_NONE; +} + +static const struct i2c_algorithm omap_i2c_algo = { + .master_xfer = omap_i2c_xfer, + .functionality = omap_i2c_func, +}; + +static int +omap_i2c_probe(struct platform_device *pdev) +{ + struct omap_i2c_dev *dev; + struct i2c_adapter *adap; + struct resource *mem, *irq, *ioarea; + int r; + + /* NOTE: driver uses the static register mapping */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "no irq resource?\n"); + return -ENODEV; + } + + ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1, + pdev->name); + if (!ioarea) { + dev_err(&pdev->dev, "I2C region already claimed\n"); + return -EBUSY; + } + + if (clock > 200) + clock = 400; /* Fast mode */ + else + clock = 100; /* Standard mode */ + + dev = kzalloc(sizeof(struct omap_i2c_dev), GFP_KERNEL); + if (!dev) { + r = -ENOMEM; + goto err_release_region; + } + + dev->dev = &pdev->dev; + dev->irq = irq->start; + dev->base = (void __iomem *) IO_ADDRESS(mem->start); + platform_set_drvdata(pdev, dev); + + if ((r = omap_i2c_get_clocks(dev)) != 0) + goto err_free_mem; + + omap_i2c_enable_clocks(dev); + + if (cpu_is_omap15xx()) + dev->rev1 = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) < 0x20; + + /* reset ASAP, clearing any IRQs */ + omap_i2c_init(dev); + + r = request_irq(dev->irq, dev->rev1 ? omap_i2c_rev1_isr : omap_i2c_isr, + 0, pdev->name, dev); + + if (r) { + dev_err(dev->dev, "failure requesting irq %i\n", dev->irq); + goto err_unuse_clocks; + } + r = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff; + dev_info(dev->dev, "bus %d rev%d.%d at %d kHz\n", + pdev->id, r >> 4, r & 0xf, clock); + + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; + strncpy(adap->name, "OMAP I2C adapter", sizeof(adap->name)); + adap->algo = &omap_i2c_algo; + adap->dev.parent = &pdev->dev; + + /* i2c device drivers may be active on return from add_adapter() */ + r = i2c_add_adapter(adap); + if (r) { + dev_err(dev->dev, "failure adding adapter\n"); + goto err_free_irq; + } + + omap_i2c_disable_clocks(dev); + + return 0; + +err_free_irq: + free_irq(dev->irq, dev); +err_unuse_clocks: + omap_i2c_disable_clocks(dev); + omap_i2c_put_clocks(dev); +err_free_mem: + platform_set_drvdata(pdev, NULL); + kfree(dev); +err_release_region: + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); + release_mem_region(mem->start, (mem->end - mem->start) + 1); + + return r; +} + +static int +omap_i2c_remove(struct platform_device *pdev) +{ + struct omap_i2c_dev *dev = platform_get_drvdata(pdev); + struct resource *mem; + + platform_set_drvdata(pdev, NULL); + + free_irq(dev->irq, dev); + i2c_del_adapter(&dev->adapter); + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); + omap_i2c_put_clocks(dev); + kfree(dev); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, (mem->end - mem->start) + 1); + return 0; +} + +static struct platform_driver omap_i2c_driver = { + .probe = omap_i2c_probe, + .remove = omap_i2c_remove, + .driver = { + .name = "i2c_omap", + .owner = THIS_MODULE, + }, +}; + +/* I2C may be needed to bring up other drivers */ +static int __init +omap_i2c_init_driver(void) +{ + return platform_driver_register(&omap_i2c_driver); +} +subsys_initcall(omap_i2c_init_driver); + +static void __exit omap_i2c_exit_driver(void) +{ + platform_driver_unregister(&omap_i2c_driver); +} +module_exit(omap_i2c_exit_driver); + +MODULE_AUTHOR("MontaVista Software, Inc. (and others)"); +MODULE_DESCRIPTION("TI OMAP I2C bus adapter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-parport-light.c b/drivers/i2c/busses/i2c-parport-light.c index e09ebbb2f9f..5eb2bd294fd 100644 --- a/drivers/i2c/busses/i2c-parport-light.c +++ b/drivers/i2c/busses/i2c-parport-light.c @@ -103,7 +103,6 @@ static struct i2c_algo_bit_data parport_algo_data = { .getsda = parport_getsda, .getscl = parport_getscl, .udelay = 50, - .mdelay = 50, .timeout = HZ, }; diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c index 934bd55bae1..48a829431c7 100644 --- a/drivers/i2c/busses/i2c-parport.c +++ b/drivers/i2c/busses/i2c-parport.c @@ -138,7 +138,6 @@ static struct i2c_algo_bit_data parport_algo_data = { .getsda = parport_getsda, .getscl = parport_getscl, .udelay = 60, - .mdelay = 60, .timeout = HZ, }; diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 8f2f65b793b..30c7a1b38cb 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -376,7 +376,7 @@ static u32 piix4_func(struct i2c_adapter *adapter) I2C_FUNC_SMBUS_BLOCK_DATA; } -static struct i2c_algorithm smbus_algorithm = { +static const struct i2c_algorithm smbus_algorithm = { .smbus_xfer = piix4_access, .functionality = piix4_func, }; diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index d658d910795..a508cb962d2 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -175,7 +175,7 @@ static u32 i2c_powermac_func(struct i2c_adapter * adapter) } /* For now, we only handle smbus */ -static struct i2c_algorithm i2c_powermac_algorithm = { +static const struct i2c_algorithm i2c_powermac_algorithm = { .smbus_xfer = i2c_powermac_smbus_xfer, .master_xfer = i2c_powermac_master_xfer, .functionality = i2c_powermac_func, diff --git a/drivers/i2c/busses/i2c-prosavage.c b/drivers/i2c/busses/i2c-prosavage.c index 9479525892e..7745e21874a 100644 --- a/drivers/i2c/busses/i2c-prosavage.c +++ b/drivers/i2c/busses/i2c-prosavage.c @@ -180,7 +180,6 @@ static int i2c_register_bus(struct pci_dev *dev, struct s_i2c_bus *p, void __iom p->algo.getsda = bit_s3via_getsda; p->algo.getscl = bit_s3via_getscl; p->algo.udelay = CYCLE_DELAY; - p->algo.mdelay = CYCLE_DELAY; p->algo.timeout = TIMEOUT; p->algo.data = p; p->mmvga = mmvga; diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index ee114b48fac..cd4ad98ad51 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -926,7 +926,7 @@ static u32 i2c_pxa_functionality(struct i2c_adapter *adap) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm i2c_pxa_algorithm = { +static const struct i2c_algorithm i2c_pxa_algorithm = { .master_xfer = i2c_pxa_xfer, .functionality = i2c_pxa_functionality, }; diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 5d2950e91fc..9ebe429a0a0 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -566,7 +566,7 @@ static u32 s3c24xx_i2c_func(struct i2c_adapter *adap) /* i2c bus registration info */ -static struct i2c_algorithm s3c24xx_i2c_algorithm = { +static const struct i2c_algorithm s3c24xx_i2c_algorithm = { .master_xfer = s3c24xx_i2c_xfer, .functionality = s3c24xx_i2c_func, }; diff --git a/drivers/i2c/busses/i2c-savage4.c b/drivers/i2c/busses/i2c-savage4.c index 0c8518298e4..209f47ea175 100644 --- a/drivers/i2c/busses/i2c-savage4.c +++ b/drivers/i2c/busses/i2c-savage4.c @@ -140,7 +140,6 @@ static struct i2c_algo_bit_data sav_i2c_bit_data = { .getsda = bit_savi2c_getsda, .getscl = bit_savi2c_getscl, .udelay = CYCLE_DELAY, - .mdelay = CYCLE_DELAY, .timeout = TIMEOUT }; diff --git a/drivers/i2c/busses/i2c-sibyte.c b/drivers/i2c/busses/i2c-sibyte.c index fa503ed9f86..8f2b1f0deb8 100644 --- a/drivers/i2c/busses/i2c-sibyte.c +++ b/drivers/i2c/busses/i2c-sibyte.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2004 Steven J. Hill * Copyright (C) 2001,2002,2003 Broadcom Corporation + * Copyright (C) 1995-2000 Simon G. Vogl * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -17,11 +18,162 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <linux/kernel.h> #include <linux/module.h> -#include <linux/i2c-algo-sibyte.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <asm/io.h> #include <asm/sibyte/sb1250_regs.h> #include <asm/sibyte/sb1250_smbus.h> + +struct i2c_algo_sibyte_data { + void *data; /* private data */ + int bus; /* which bus */ + void *reg_base; /* CSR base */ +}; + +/* ----- global defines ----------------------------------------------- */ +#define SMB_CSR(a,r) ((long)(a->reg_base + r)) + +/* ----- global variables --------------------------------------------- */ + +/* module parameters: + */ +static int bit_scan; /* have a look at what's hanging 'round */ +module_param(bit_scan, int, 0); +MODULE_PARM_DESC(bit_scan, "Scan for active chips on the bus"); + + +static int smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data; + int data_bytes = 0; + int error; + + while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY) + ; + + switch (size) { + case I2C_SMBUS_QUICK: + csr_out32((V_SMB_ADDR(addr) | + (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) | + V_SMB_TT_QUICKCMD), SMB_CSR(adap, R_SMB_START)); + break; + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_READ) { + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE), + SMB_CSR(adap, R_SMB_START)); + data_bytes = 1; + } else { + csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD)); + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE), + SMB_CSR(adap, R_SMB_START)); + } + break; + case I2C_SMBUS_BYTE_DATA: + csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD)); + if (read_write == I2C_SMBUS_READ) { + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE), + SMB_CSR(adap, R_SMB_START)); + data_bytes = 1; + } else { + csr_out32(V_SMB_LB(data->byte), + SMB_CSR(adap, R_SMB_DATA)); + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE), + SMB_CSR(adap, R_SMB_START)); + } + break; + case I2C_SMBUS_WORD_DATA: + csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD)); + if (read_write == I2C_SMBUS_READ) { + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE), + SMB_CSR(adap, R_SMB_START)); + data_bytes = 2; + } else { + csr_out32(V_SMB_LB(data->word & 0xff), + SMB_CSR(adap, R_SMB_DATA)); + csr_out32(V_SMB_MB(data->word >> 8), + SMB_CSR(adap, R_SMB_DATA)); + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE), + SMB_CSR(adap, R_SMB_START)); + } + break; + default: + return -1; /* XXXKW better error code? */ + } + + while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY) + ; + + error = csr_in32(SMB_CSR(adap, R_SMB_STATUS)); + if (error & M_SMB_ERROR) { + /* Clear error bit by writing a 1 */ + csr_out32(M_SMB_ERROR, SMB_CSR(adap, R_SMB_STATUS)); + return -1; /* XXXKW better error code? */ + } + + if (data_bytes == 1) + data->byte = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xff; + if (data_bytes == 2) + data->word = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xffff; + + return 0; +} + +static u32 bit_func(struct i2c_adapter *adap) +{ + return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA); +} + + +/* -----exported algorithm data: ------------------------------------- */ + +static const struct i2c_algorithm i2c_sibyte_algo = { + .smbus_xfer = smbus_xfer, + .functionality = bit_func, +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed) +{ + int i; + struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data; + + /* register new adapter to i2c module... */ + i2c_adap->algo = &i2c_sibyte_algo; + + /* Set the frequency to 100 kHz */ + csr_out32(speed, SMB_CSR(adap,R_SMB_FREQ)); + csr_out32(0, SMB_CSR(adap,R_SMB_CONTROL)); + + /* scan bus */ + if (bit_scan) { + union i2c_smbus_data data; + int rc; + printk(KERN_INFO " i2c-algo-sibyte.o: scanning bus %s.\n", + i2c_adap->name); + for (i = 0x00; i < 0x7f; i++) { + /* XXXKW is this a realistic probe? */ + rc = smbus_xfer(i2c_adap, i, 0, I2C_SMBUS_READ, 0, + I2C_SMBUS_BYTE_DATA, &data); + if (!rc) { + printk("(%02x)",i); + } else + printk("."); + } + printk("\n"); + } + + return i2c_add_adapter(i2c_adap); +} + + static struct i2c_algo_sibyte_data sibyte_board_data[2] = { { NULL, 0, (void *) (CKSEG1+A_SMB_BASE(0)) }, { NULL, 1, (void *) (CKSEG1+A_SMB_BASE(1)) } @@ -58,13 +210,13 @@ static int __init i2c_sibyte_init(void) static void __exit i2c_sibyte_exit(void) { - i2c_sibyte_del_bus(&sibyte_board_adapter[0]); - i2c_sibyte_del_bus(&sibyte_board_adapter[1]); + i2c_del_bus(&sibyte_board_adapter[0]); + i2c_del_bus(&sibyte_board_adapter[1]); } module_init(i2c_sibyte_init); module_exit(i2c_sibyte_exit); -MODULE_AUTHOR("Kip Walker <kwalker@broadcom.com>, Steven J. Hill <sjhill@realitydiluted.com>"); +MODULE_AUTHOR("Kip Walker (Broadcom Corp.), Steven J. Hill <sjhill@realitydiluted.com>"); MODULE_DESCRIPTION("SMBus adapter routines for SiByte boards"); MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c index b57ab74d23e..38bbfd840b6 100644 --- a/drivers/i2c/busses/i2c-sis5595.c +++ b/drivers/i2c/busses/i2c-sis5595.c @@ -358,7 +358,7 @@ static u32 sis5595_func(struct i2c_adapter *adapter) I2C_FUNC_SMBUS_PROC_CALL; } -static struct i2c_algorithm smbus_algorithm = { +static const struct i2c_algorithm smbus_algorithm = { .smbus_xfer = sis5595_access, .functionality = sis5595_func, }; diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index acb75e28241..dec0bafb52a 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -450,7 +450,7 @@ exit: } -static struct i2c_algorithm smbus_algorithm = { +static const struct i2c_algorithm smbus_algorithm = { .smbus_xfer = sis630_access, .functionality = sis630_func, }; diff --git a/drivers/i2c/busses/i2c-sis96x.c b/drivers/i2c/busses/i2c-sis96x.c index 1a73c0532fc..7fd07fbac33 100644 --- a/drivers/i2c/busses/i2c-sis96x.c +++ b/drivers/i2c/busses/i2c-sis96x.c @@ -242,7 +242,7 @@ static u32 sis96x_func(struct i2c_adapter *adapter) I2C_FUNC_SMBUS_PROC_CALL; } -static struct i2c_algorithm smbus_algorithm = { +static const struct i2c_algorithm smbus_algorithm = { .smbus_xfer = sis96x_access, .functionality = sis96x_func, }; diff --git a/drivers/i2c/busses/i2c-stub.c b/drivers/i2c/busses/i2c-stub.c index 73f481e93a3..a54adc50d16 100644 --- a/drivers/i2c/busses/i2c-stub.c +++ b/drivers/i2c/busses/i2c-stub.c @@ -27,6 +27,10 @@ #include <linux/errno.h> #include <linux/i2c.h> +static unsigned short chip_addr; +module_param(chip_addr, ushort, S_IRUGO); +MODULE_PARM_DESC(chip_addr, "Chip address (between 0x03 and 0x77)\n"); + static u8 stub_pointer; static u8 stub_bytes[256]; static u16 stub_words[256]; @@ -37,6 +41,9 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags, { s32 ret; + if (addr != chip_addr) + return -ENODEV; + switch (size) { case I2C_SMBUS_QUICK: @@ -108,7 +115,7 @@ static u32 stub_func(struct i2c_adapter *adapter) I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA; } -static struct i2c_algorithm smbus_algorithm = { +static const struct i2c_algorithm smbus_algorithm = { .functionality = stub_func, .smbus_xfer = stub_xfer, }; @@ -122,7 +129,17 @@ static struct i2c_adapter stub_adapter = { static int __init i2c_stub_init(void) { - printk(KERN_INFO "i2c-stub loaded\n"); + if (!chip_addr) { + printk(KERN_ERR "i2c-stub: Please specify a chip address\n"); + return -ENODEV; + } + if (chip_addr < 0x03 || chip_addr > 0x77) { + printk(KERN_ERR "i2c-stub: Invalid chip address 0x%02x\n", + chip_addr); + return -EINVAL; + } + + printk(KERN_INFO "i2c-stub: Virtual chip at 0x%02x\n", chip_addr); return i2c_add_adapter(&stub_adapter); } diff --git a/drivers/i2c/busses/i2c-via.c b/drivers/i2c/busses/i2c-via.c index 484bbacfce6..910e200ad50 100644 --- a/drivers/i2c/busses/i2c-via.c +++ b/drivers/i2c/busses/i2c-via.c @@ -81,7 +81,6 @@ static struct i2c_algo_bit_data bit_data = { .getsda = bit_via_getsda, .getscl = bit_via_getscl, .udelay = 5, - .mdelay = 5, .timeout = HZ }; diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c index 47e52bf2c5e..efc6bbf0cc0 100644 --- a/drivers/i2c/busses/i2c-viapro.c +++ b/drivers/i2c/busses/i2c-viapro.c @@ -34,6 +34,8 @@ VT8233A 0x3147 yes? VT8235 0x3177 yes VT8237R 0x3227 yes + VT8237A 0x3337 yes + VT8251 0x3287 yes Note: we assume there can only be one device, with one SMBus interface. */ @@ -297,7 +299,7 @@ static u32 vt596_func(struct i2c_adapter *adapter) return func; } -static struct i2c_algorithm smbus_algorithm = { +static const struct i2c_algorithm smbus_algorithm = { .smbus_xfer = vt596_access, .functionality = vt596_func, }; @@ -381,7 +383,9 @@ found: dev_dbg(&pdev->dev, "VT596_smba = 0x%X\n", vt596_smba); switch (pdev->device) { + case PCI_DEVICE_ID_VIA_8251: case PCI_DEVICE_ID_VIA_8237: + case PCI_DEVICE_ID_VIA_8237A: case PCI_DEVICE_ID_VIA_8235: case PCI_DEVICE_ID_VIA_8233A: case PCI_DEVICE_ID_VIA_8233_0: @@ -432,8 +436,12 @@ static struct pci_device_id vt596_ids[] = { .driver_data = SMBBA3 }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237), .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237A), + .driver_data = SMBBA3 }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231_4), .driver_data = SMBBA1 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8251), + .driver_data = SMBBA3 }, { 0, } }; diff --git a/drivers/i2c/busses/i2c-voodoo3.c b/drivers/i2c/busses/i2c-voodoo3.c index b675773b0cc..6c8d2518338 100644 --- a/drivers/i2c/busses/i2c-voodoo3.c +++ b/drivers/i2c/busses/i2c-voodoo3.c @@ -160,7 +160,6 @@ static struct i2c_algo_bit_data voo_i2c_bit_data = { .getsda = bit_vooi2c_getsda, .getscl = bit_vooi2c_getscl, .udelay = CYCLE_DELAY, - .mdelay = CYCLE_DELAY, .timeout = TIMEOUT }; @@ -177,7 +176,6 @@ static struct i2c_algo_bit_data voo_ddc_bit_data = { .getsda = bit_vooddc_getsda, .getscl = bit_vooddc_getscl, .udelay = CYCLE_DELAY, - .mdelay = CYCLE_DELAY, .timeout = TIMEOUT }; diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c index eae9e81be37..32aab0d34ee 100644 --- a/drivers/i2c/busses/scx200_acb.c +++ b/drivers/i2c/busses/scx200_acb.c @@ -383,7 +383,7 @@ static u32 scx200_acb_func(struct i2c_adapter *adapter) } /* For now, we only handle combined mode (smbus) */ -static struct i2c_algorithm scx200_acb_algorithm = { +static const struct i2c_algorithm scx200_acb_algorithm = { .smbus_xfer = scx200_acb_smbus_xfer, .functionality = scx200_acb_func, }; diff --git a/drivers/i2c/busses/scx200_i2c.c b/drivers/i2c/busses/scx200_i2c.c index cb3ef5ac99f..8b65a5cf825 100644 --- a/drivers/i2c/busses/scx200_i2c.c +++ b/drivers/i2c/busses/scx200_i2c.c @@ -71,12 +71,12 @@ static int scx200_i2c_getsda(void *data) */ static struct i2c_algo_bit_data scx200_i2c_data = { - NULL, - scx200_i2c_setsda, - scx200_i2c_setscl, - scx200_i2c_getsda, - scx200_i2c_getscl, - 10, 10, 100, /* waits, timeout */ + .setsda = scx200_i2c_setsda, + .setscl = scx200_i2c_setscl, + .getsda = scx200_i2c_getsda, + .getscl = scx200_i2c_getscl, + .udelay = 10, + .timeout = 100, }; static struct i2c_adapter scx200_i2c_ops = { diff --git a/drivers/i2c/chips/eeprom.c b/drivers/i2c/chips/eeprom.c index 13c108269a6..cec3a0c3894 100644 --- a/drivers/i2c/chips/eeprom.c +++ b/drivers/i2c/chips/eeprom.c @@ -209,10 +209,14 @@ static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind) } /* create the sysfs eeprom file */ - sysfs_create_bin_file(&new_client->dev.kobj, &eeprom_attr); + err = sysfs_create_bin_file(&new_client->dev.kobj, &eeprom_attr); + if (err) + goto exit_detach; return 0; +exit_detach: + i2c_detach_client(new_client); exit_kfree: kfree(data); exit: @@ -223,6 +227,8 @@ static int eeprom_detach_client(struct i2c_client *client) { int err; + sysfs_remove_bin_file(&client->dev.kobj, &eeprom_attr); + err = i2c_detach_client(client); if (err) return err; diff --git a/drivers/i2c/chips/max6875.c b/drivers/i2c/chips/max6875.c index 88d2ddee449..76645c14297 100644 --- a/drivers/i2c/chips/max6875.c +++ b/drivers/i2c/chips/max6875.c @@ -199,8 +199,7 @@ static int max6875_detect(struct i2c_adapter *adapter, int address, int kind) mutex_init(&data->update_lock); /* Init fake client data */ - /* set the client data to the i2c_client so that it will get freed */ - i2c_set_clientdata(fake_client, fake_client); + i2c_set_clientdata(fake_client, NULL); fake_client->addr = address | 1; fake_client->adapter = adapter; fake_client->driver = &max6875_driver; @@ -214,13 +213,17 @@ static int max6875_detect(struct i2c_adapter *adapter, int address, int kind) goto exit_kfree2; if ((err = i2c_attach_client(fake_client)) != 0) - goto exit_detach; + goto exit_detach1; - sysfs_create_bin_file(&real_client->dev.kobj, &user_eeprom_attr); + err = sysfs_create_bin_file(&real_client->dev.kobj, &user_eeprom_attr); + if (err) + goto exit_detach2; return 0; -exit_detach: +exit_detach2: + i2c_detach_client(fake_client); +exit_detach1: i2c_detach_client(real_client); exit_kfree2: kfree(fake_client); @@ -229,14 +232,24 @@ exit_kfree1: return err; } +/* Will be called for both the real client and the fake client */ static int max6875_detach_client(struct i2c_client *client) { int err; + struct max6875_data *data = i2c_get_clientdata(client); + + /* data is NULL for the fake client */ + if (data) + sysfs_remove_bin_file(&client->dev.kobj, &user_eeprom_attr); err = i2c_detach_client(client); if (err) return err; - kfree(i2c_get_clientdata(client)); + + if (data) /* real client */ + kfree(data); + else /* fake client */ + kfree(client); return 0; } diff --git a/drivers/i2c/chips/pca9539.c b/drivers/i2c/chips/pca9539.c index cb22280cdd2..f43c4e79b55 100644 --- a/drivers/i2c/chips/pca9539.c +++ b/drivers/i2c/chips/pca9539.c @@ -148,11 +148,16 @@ static int pca9539_detect(struct i2c_adapter *adapter, int address, int kind) if ((err = i2c_attach_client(new_client))) goto exit_kfree; - /* Register sysfs hooks (don't care about failure) */ - sysfs_create_group(&new_client->dev.kobj, &pca9539_defattr_group); + /* Register sysfs hooks */ + err = sysfs_create_group(&new_client->dev.kobj, + &pca9539_defattr_group); + if (err) + goto exit_detach; return 0; +exit_detach: + i2c_detach_client(new_client); exit_kfree: kfree(data); exit: @@ -163,6 +168,8 @@ static int pca9539_detach_client(struct i2c_client *client) { int err; + sysfs_remove_group(&client->dev.kobj, &pca9539_defattr_group); + if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/i2c/chips/pcf8574.c b/drivers/i2c/chips/pcf8574.c index c3e6449c448..32b25427eab 100644 --- a/drivers/i2c/chips/pcf8574.c +++ b/drivers/i2c/chips/pcf8574.c @@ -105,6 +105,16 @@ static ssize_t set_write(struct device *dev, struct device_attribute *attr, cons static DEVICE_ATTR(write, S_IWUSR | S_IRUGO, show_write, set_write); +static struct attribute *pcf8574_attributes[] = { + &dev_attr_read.attr, + &dev_attr_write.attr, + NULL +}; + +static const struct attribute_group pcf8574_attr_group = { + .attrs = pcf8574_attributes, +}; + /* * Real code */ @@ -166,13 +176,13 @@ static int pcf8574_detect(struct i2c_adapter *adapter, int address, int kind) pcf8574_init_client(new_client); /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_read); - device_create_file(&new_client->dev, &dev_attr_write); + err = sysfs_create_group(&new_client->dev.kobj, &pcf8574_attr_group); + if (err) + goto exit_detach; return 0; -/* OK, this is not exactly good programming practice, usually. But it is - very code-efficient in this case. */ - + exit_detach: + i2c_detach_client(new_client); exit_free: kfree(data); exit: @@ -183,6 +193,8 @@ static int pcf8574_detach_client(struct i2c_client *client) { int err; + sysfs_remove_group(&client->dev.kobj, &pcf8574_attr_group); + if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/i2c/chips/pcf8591.c b/drivers/i2c/chips/pcf8591.c index 925a6b371fd..4dc36376eb3 100644 --- a/drivers/i2c/chips/pcf8591.c +++ b/drivers/i2c/chips/pcf8591.c @@ -158,6 +158,28 @@ static ssize_t set_out0_enable(struct device *dev, struct device_attribute *attr static DEVICE_ATTR(out0_enable, S_IWUSR | S_IRUGO, show_out0_enable, set_out0_enable); +static struct attribute *pcf8591_attributes[] = { + &dev_attr_out0_enable.attr, + &dev_attr_out0_output.attr, + &dev_attr_in0_input.attr, + &dev_attr_in1_input.attr, + NULL +}; + +static const struct attribute_group pcf8591_attr_group = { + .attrs = pcf8591_attributes, +}; + +static struct attribute *pcf8591_attributes_opt[] = { + &dev_attr_in2_input.attr, + &dev_attr_in3_input.attr, + NULL +}; + +static const struct attribute_group pcf8591_attr_group_opt = { + .attrs = pcf8591_attributes_opt, +}; + /* * Real code */ @@ -211,24 +233,31 @@ static int pcf8591_detect(struct i2c_adapter *adapter, int address, int kind) pcf8591_init_client(new_client); /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_out0_enable); - device_create_file(&new_client->dev, &dev_attr_out0_output); - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in1_input); + err = sysfs_create_group(&new_client->dev.kobj, &pcf8591_attr_group); + if (err) + goto exit_detach; /* Register input2 if not in "two differential inputs" mode */ - if (input_mode != 3 ) - device_create_file(&new_client->dev, &dev_attr_in2_input); - + if (input_mode != 3) { + if ((err = device_create_file(&new_client->dev, + &dev_attr_in2_input))) + goto exit_sysfs_remove; + } + /* Register input3 only in "four single ended inputs" mode */ - if (input_mode == 0) - device_create_file(&new_client->dev, &dev_attr_in3_input); - + if (input_mode == 0) { + if ((err = device_create_file(&new_client->dev, + &dev_attr_in3_input))) + goto exit_sysfs_remove; + } + return 0; - - /* OK, this is not exactly good programming practice, usually. But it is - very code-efficient in this case. */ +exit_sysfs_remove: + sysfs_remove_group(&new_client->dev.kobj, &pcf8591_attr_group_opt); + sysfs_remove_group(&new_client->dev.kobj, &pcf8591_attr_group); +exit_detach: + i2c_detach_client(new_client); exit_kfree: kfree(data); exit: @@ -239,6 +268,9 @@ static int pcf8591_detach_client(struct i2c_client *client) { int err; + sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); + sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); + if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 9cb277d6aa4..01233f0f777 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -183,15 +183,21 @@ int i2c_add_adapter(struct i2c_adapter *adap) sprintf(adap->dev.bus_id, "i2c-%d", adap->nr); adap->dev.driver = &i2c_adapter_driver; adap->dev.release = &i2c_adapter_dev_release; - device_register(&adap->dev); - device_create_file(&adap->dev, &dev_attr_name); + res = device_register(&adap->dev); + if (res) + goto out_list; + res = device_create_file(&adap->dev, &dev_attr_name); + if (res) + goto out_unregister; /* Add this adapter to the i2c_adapter class */ memset(&adap->class_dev, 0x00, sizeof(struct class_device)); adap->class_dev.dev = &adap->dev; adap->class_dev.class = &i2c_adapter_class; strlcpy(adap->class_dev.class_id, adap->dev.bus_id, BUS_ID_SIZE); - class_device_register(&adap->class_dev); + res = class_device_register(&adap->class_dev); + if (res) + goto out_remove_name; dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); @@ -206,6 +212,17 @@ int i2c_add_adapter(struct i2c_adapter *adap) out_unlock: mutex_unlock(&core_lists); return res; + +out_remove_name: + device_remove_file(&adap->dev, &dev_attr_name); +out_unregister: + init_completion(&adap->dev_released); /* Needed? */ + device_unregister(&adap->dev); + wait_for_completion(&adap->dev_released); +out_list: + list_del(&adap->list); + idr_remove(&i2c_adapter_idr, adap->nr); + goto out_unlock; } @@ -394,14 +411,14 @@ int i2c_check_addr(struct i2c_adapter *adapter, int addr) int i2c_attach_client(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; + int res = 0; mutex_lock(&adapter->clist_lock); if (__i2c_check_addr(client->adapter, client->addr)) { - mutex_unlock(&adapter->clist_lock); - return -EBUSY; + res = -EBUSY; + goto out_unlock; } list_add_tail(&client->list,&adapter->clients); - mutex_unlock(&adapter->clist_lock); if (adapter->client_register) { if (adapter->client_register(client)) { @@ -422,10 +439,26 @@ int i2c_attach_client(struct i2c_client *client) "%d-%04x", i2c_adapter_id(adapter), client->addr); dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n", client->name, client->dev.bus_id); - device_register(&client->dev); - device_create_file(&client->dev, &dev_attr_client_name); - - return 0; + res = device_register(&client->dev); + if (res) + goto out_list; + res = device_create_file(&client->dev, &dev_attr_client_name); + if (res) + goto out_unregister; + +out_unlock: + mutex_unlock(&adapter->clist_lock); + return res; + +out_unregister: + init_completion(&client->released); /* Needed? */ + device_unregister(&client->dev); + wait_for_completion(&client->released); +out_list: + list_del(&client->list); + dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x " + "(%d)\n", client->name, client->addr, res); + goto out_unlock; } @@ -674,11 +707,16 @@ static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind, /* Finally call the custom detection function */ err = found_proc(adapter, addr, kind); - /* -ENODEV can be returned if there is a chip at the given address but it isn't supported by this chip driver. We catch it here as this isn't an error. */ - return (err == -ENODEV) ? 0 : err; + if (err == -ENODEV) + err = 0; + + if (err) + dev_warn(&adapter->dev, "Client creation failed at 0x%x (%d)\n", + addr, err); + return err; } int i2c_probe(struct i2c_adapter *adapter, @@ -868,7 +906,7 @@ s32 i2c_smbus_read_byte(struct i2c_client *client) I2C_SMBUS_READ,0,I2C_SMBUS_BYTE, &data)) return -1; else - return 0x0FF & data.byte; + return data.byte; } s32 i2c_smbus_write_byte(struct i2c_client *client, u8 value) @@ -884,7 +922,7 @@ s32 i2c_smbus_read_byte_data(struct i2c_client *client, u8 command) I2C_SMBUS_READ,command, I2C_SMBUS_BYTE_DATA,&data)) return -1; else - return 0x0FF & data.byte; + return data.byte; } s32 i2c_smbus_write_byte_data(struct i2c_client *client, u8 command, u8 value) @@ -903,7 +941,7 @@ s32 i2c_smbus_read_word_data(struct i2c_client *client, u8 command) I2C_SMBUS_READ,command, I2C_SMBUS_WORD_DATA, &data)) return -1; else - return 0x0FFFF & data.word; + return data.word; } s32 i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value) @@ -1006,7 +1044,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, else { msg[0].len=3; msgbuf0[1] = data->word & 0xff; - msgbuf0[2] = (data->word >> 8) & 0xff; + msgbuf0[2] = data->word >> 8; } break; case I2C_SMBUS_PROC_CALL: @@ -1015,7 +1053,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, msg[0].len = 3; msg[1].len = 2; msgbuf0[1] = data->word & 0xff; - msgbuf0[2] = (data->word >> 8) & 0xff; + msgbuf0[2] = data->word >> 8; break; case I2C_SMBUS_BLOCK_DATA: if (read_write == I2C_SMBUS_READ) { diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 58ccddd5c23..3f869033ed7 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -32,43 +32,35 @@ #include <linux/slab.h> #include <linux/smp_lock.h> #include <linux/init.h> +#include <linux/list.h> #include <linux/i2c.h> #include <linux/i2c-dev.h> -#include <linux/platform_device.h> #include <asm/uaccess.h> -static struct i2c_client i2cdev_client_template; +static struct i2c_driver i2cdev_driver; struct i2c_dev { - int minor; + struct list_head list; struct i2c_adapter *adap; struct class_device *class_dev; }; -#define to_i2c_dev(d) container_of(d, struct i2c_dev, class_dev) #define I2C_MINORS 256 -static struct i2c_dev *i2c_dev_array[I2C_MINORS]; -static DEFINE_SPINLOCK(i2c_dev_array_lock); +static LIST_HEAD(i2c_dev_list); +static DEFINE_SPINLOCK(i2c_dev_list_lock); static struct i2c_dev *i2c_dev_get_by_minor(unsigned index) { struct i2c_dev *i2c_dev; - spin_lock(&i2c_dev_array_lock); - i2c_dev = i2c_dev_array[index]; - spin_unlock(&i2c_dev_array_lock); - return i2c_dev; -} - -static struct i2c_dev *i2c_dev_get_by_adapter(struct i2c_adapter *adap) -{ - struct i2c_dev *i2c_dev = NULL; - - spin_lock(&i2c_dev_array_lock); - if ((i2c_dev_array[adap->nr]) && - (i2c_dev_array[adap->nr]->adap == adap)) - i2c_dev = i2c_dev_array[adap->nr]; - spin_unlock(&i2c_dev_array_lock); + spin_lock(&i2c_dev_list_lock); + list_for_each_entry(i2c_dev, &i2c_dev_list, list) { + if (i2c_dev->adap->nr == index) + goto found; + } + i2c_dev = NULL; +found: + spin_unlock(&i2c_dev_list_lock); return i2c_dev; } @@ -76,30 +68,28 @@ static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap) { struct i2c_dev *i2c_dev; + if (adap->nr >= I2C_MINORS) { + printk(KERN_ERR "i2c-dev: Out of device minors (%d)\n", + adap->nr); + return ERR_PTR(-ENODEV); + } + i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL); if (!i2c_dev) return ERR_PTR(-ENOMEM); + i2c_dev->adap = adap; - spin_lock(&i2c_dev_array_lock); - if (i2c_dev_array[adap->nr]) { - spin_unlock(&i2c_dev_array_lock); - dev_err(&adap->dev, "i2c-dev already has a device assigned to this adapter\n"); - goto error; - } - i2c_dev->minor = adap->nr; - i2c_dev_array[adap->nr] = i2c_dev; - spin_unlock(&i2c_dev_array_lock); + spin_lock(&i2c_dev_list_lock); + list_add_tail(&i2c_dev->list, &i2c_dev_list); + spin_unlock(&i2c_dev_list_lock); return i2c_dev; -error: - kfree(i2c_dev); - return ERR_PTR(-ENODEV); } static void return_i2c_dev(struct i2c_dev *i2c_dev) { - spin_lock(&i2c_dev_array_lock); - i2c_dev_array[i2c_dev->minor] = NULL; - spin_unlock(&i2c_dev_array_lock); + spin_lock(&i2c_dev_list_lock); + list_del(&i2c_dev->list); + spin_unlock(&i2c_dev_list_lock); } static ssize_t show_adapter_name(struct class_device *class_dev, char *buf) @@ -375,12 +365,13 @@ static int i2cdev_open(struct inode *inode, struct file *file) if (!adap) return -ENODEV; - client = kmalloc(sizeof(*client), GFP_KERNEL); + client = kzalloc(sizeof(*client), GFP_KERNEL); if (!client) { i2c_put_adapter(adap); return -ENOMEM; } - memcpy(client, &i2cdev_client_template, sizeof(*client)); + snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr); + client->driver = &i2cdev_driver; /* registered with adapter, passed as client to user */ client->adapter = adap; @@ -415,41 +406,47 @@ static struct class *i2c_dev_class; static int i2cdev_attach_adapter(struct i2c_adapter *adap) { struct i2c_dev *i2c_dev; - struct device *dev; + int res; i2c_dev = get_free_i2c_dev(adap); if (IS_ERR(i2c_dev)) return PTR_ERR(i2c_dev); - pr_debug("i2c-dev: adapter [%s] registered as minor %d\n", - adap->name, i2c_dev->minor); - /* register this i2c device with the driver core */ - i2c_dev->adap = adap; - dev = &adap->dev; i2c_dev->class_dev = class_device_create(i2c_dev_class, NULL, - MKDEV(I2C_MAJOR, i2c_dev->minor), - dev, "i2c-%d", i2c_dev->minor); - if (!i2c_dev->class_dev) + MKDEV(I2C_MAJOR, adap->nr), + &adap->dev, "i2c-%d", + adap->nr); + if (!i2c_dev->class_dev) { + res = -ENODEV; goto error; - class_device_create_file(i2c_dev->class_dev, &class_device_attr_name); + } + res = class_device_create_file(i2c_dev->class_dev, &class_device_attr_name); + if (res) + goto error_destroy; + + pr_debug("i2c-dev: adapter [%s] registered as minor %d\n", + adap->name, adap->nr); return 0; +error_destroy: + class_device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); error: return_i2c_dev(i2c_dev); kfree(i2c_dev); - return -ENODEV; + return res; } static int i2cdev_detach_adapter(struct i2c_adapter *adap) { struct i2c_dev *i2c_dev; - i2c_dev = i2c_dev_get_by_adapter(adap); - if (!i2c_dev) - return -ENODEV; + i2c_dev = i2c_dev_get_by_minor(adap->nr); + if (!i2c_dev) /* attach_adapter must have failed */ + return 0; + class_device_remove_file(i2c_dev->class_dev, &class_device_attr_name); return_i2c_dev(i2c_dev); - class_device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, i2c_dev->minor)); + class_device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); kfree(i2c_dev); pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name); @@ -471,12 +468,6 @@ static struct i2c_driver i2cdev_driver = { .detach_client = i2cdev_detach_client, }; -static struct i2c_client i2cdev_client_template = { - .name = "I2C /dev entry", - .addr = -1, - .driver = &i2cdev_driver, -}; - static int __init i2c_dev_init(void) { int res; |