diff options
| -rw-r--r-- | drivers/i2c/algos/i2c-algo-pca.c | 88 | ||||
| -rw-r--r-- | drivers/i2c/algos/i2c-algo-pca.h | 26 | ||||
| -rw-r--r-- | drivers/i2c/busses/i2c-pca-isa.c | 51 | ||||
| -rw-r--r-- | include/linux/i2c-algo-pca.h | 37 | 
4 files changed, 95 insertions, 107 deletions
diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c index ebec91d71e1..e954a20b97a 100644 --- a/drivers/i2c/algos/i2c-algo-pca.c +++ b/drivers/i2c/algos/i2c-algo-pca.c @@ -1,6 +1,7 @@  /*   *  i2c-algo-pca.c i2c driver algorithms for PCA9564 adapters   *    Copyright (C) 2004 Arcom Control Systems + *    Copyright (C) 2008 Pengutronix   *   *  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 @@ -21,14 +22,10 @@  #include <linux/module.h>  #include <linux/moduleparam.h>  #include <linux/delay.h> -#include <linux/slab.h>  #include <linux/init.h>  #include <linux/errno.h>  #include <linux/i2c.h>  #include <linux/i2c-algo-pca.h> -#include "i2c-algo-pca.h" - -#define DRIVER "i2c-algo-pca"  #define DEB1(fmt, args...) do { if (i2c_debug>=1) printk(fmt, ## args); } while(0)  #define DEB2(fmt, args...) do { if (i2c_debug>=2) printk(fmt, ## args); } while(0) @@ -36,15 +33,15 @@  static int i2c_debug; -#define pca_outw(adap, reg, val) adap->write_byte(adap, reg, val) -#define pca_inw(adap, reg) adap->read_byte(adap, reg) +#define pca_outw(adap, reg, val) adap->write_byte(adap->data, reg, val) +#define pca_inw(adap, reg) adap->read_byte(adap->data, reg)  #define pca_status(adap) pca_inw(adap, I2C_PCA_STA) -#define pca_clock(adap) adap->get_clock(adap) -#define pca_own(adap) adap->get_own(adap) +#define pca_clock(adap) adap->i2c_clock  #define pca_set_con(adap, val) pca_outw(adap, I2C_PCA_CON, val)  #define pca_get_con(adap) pca_inw(adap, I2C_PCA_CON) -#define pca_wait(adap) adap->wait_for_interrupt(adap) +#define pca_wait(adap) adap->wait_for_completion(adap->data) +#define pca_reset(adap) adap->reset_chip(adap->data)  /*   * Generate a start condition on the i2c bus. @@ -168,15 +165,6 @@ static void pca_rx_ack(struct i2c_algo_pca_data *adap,  	pca_wait(adap);  } -/* - * Reset the i2c bus / SIO - */ -static void pca_reset(struct i2c_algo_pca_data *adap) -{ -	/* apparently only an external reset will do it. not a lot can be done */ -	printk(KERN_ERR DRIVER ": Haven't figured out how to do a reset yet\n"); -} -  static int pca_xfer(struct i2c_adapter *i2c_adap,                      struct i2c_msg *msgs,                      int num) @@ -187,7 +175,7 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,  	int numbytes = 0;  	int state;  	int ret; -	int timeout = 100; +	int timeout = i2c_adap->timeout;  	while ((state = pca_status(adap)) != 0xf8 && timeout--) {  		msleep(10); @@ -317,7 +305,7 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,  			pca_reset(adap);  			goto out;  		default: -			printk(KERN_ERR DRIVER ": unhandled SIO state 0x%02x\n", state); +			dev_err(&i2c_adap->dev, "unhandled SIO state 0x%02x\n", state);  			break;  		} @@ -337,53 +325,65 @@ static u32 pca_func(struct i2c_adapter *adap)          return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;  } -static int pca_init(struct i2c_algo_pca_data *adap) +static const struct i2c_algorithm pca_algo = { +	.master_xfer	= pca_xfer, +	.functionality	= pca_func, +}; + +static int pca_init(struct i2c_adapter *adap)  {  	static int freqs[] = {330,288,217,146,88,59,44,36}; -	int own, clock; +	int clock; +	struct i2c_algo_pca_data *pca_data = adap->algo_data; + +	if (pca_data->i2c_clock > 7) { +		printk(KERN_WARNING "%s: Invalid I2C clock speed selected. Trying default.\n", +			adap->name); +		pca_data->i2c_clock = I2C_PCA_CON_59kHz; +	} + +	adap->algo = &pca_algo; -	own = pca_own(adap); -	clock = pca_clock(adap); -	DEB1(KERN_INFO DRIVER ": own address is %#04x\n", own); -	DEB1(KERN_INFO DRIVER ": clock freqeuncy is %dkHz\n", freqs[clock]); +	pca_reset(pca_data); -	pca_outw(adap, I2C_PCA_ADR, own << 1); +	clock = pca_clock(pca_data); +	DEB1(KERN_INFO "%s: Clock frequency is %dkHz\n", adap->name, freqs[clock]); -	pca_set_con(adap, I2C_PCA_CON_ENSIO | clock); +	pca_set_con(pca_data, I2C_PCA_CON_ENSIO | clock);  	udelay(500); /* 500 us for oscilator to stabilise */  	return 0;  } -static const struct i2c_algorithm pca_algo = { -	.master_xfer	= pca_xfer, -	.functionality	= pca_func, -}; -  /*   * registering functions to load algorithms at runtime   */  int i2c_pca_add_bus(struct i2c_adapter *adap)  { -	struct i2c_algo_pca_data *pca_adap = adap->algo_data;  	int rval; -	/* register new adapter to i2c module... */ -	adap->algo = &pca_algo; +	rval = pca_init(adap); +	if (rval) +		return rval; -	adap->timeout = 100;		/* default values, should	*/ -	adap->retries = 3;		/* be replaced by defines	*/ +	return i2c_add_adapter(adap); +} +EXPORT_SYMBOL(i2c_pca_add_bus); -	if ((rval = pca_init(pca_adap))) -		return rval; +int i2c_pca_add_numbered_bus(struct i2c_adapter *adap) +{ +	int rval; -	rval = i2c_add_adapter(adap); +	rval = pca_init(adap); +	if (rval) +		return rval; -	return rval; +	return i2c_add_numbered_adapter(adap);  } -EXPORT_SYMBOL(i2c_pca_add_bus); +EXPORT_SYMBOL(i2c_pca_add_numbered_bus); -MODULE_AUTHOR("Ian Campbell <icampbell@arcom.com>"); +MODULE_AUTHOR("Ian Campbell <icampbell@arcom.com>, " +	"Wolfram Sang <w.sang@pengutronix.de>");  MODULE_DESCRIPTION("I2C-Bus PCA9564 algorithm");  MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/algos/i2c-algo-pca.h b/drivers/i2c/algos/i2c-algo-pca.h deleted file mode 100644 index 2fee07e0521..00000000000 --- a/drivers/i2c/algos/i2c-algo-pca.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef I2C_PCA9564_H -#define I2C_PCA9564_H 1 - -#define I2C_PCA_STA		0x00 /* STATUS  Read Only  */ -#define I2C_PCA_TO		0x00 /* TIMEOUT Write Only */ -#define I2C_PCA_DAT		0x01 /* DATA    Read/Write */ -#define I2C_PCA_ADR		0x02 /* OWN ADR Read/Write */ -#define I2C_PCA_CON		0x03 /* CONTROL Read/Write */ - -#define I2C_PCA_CON_AA		0x80 /* Assert Acknowledge */ -#define I2C_PCA_CON_ENSIO	0x40 /* Enable */ -#define I2C_PCA_CON_STA		0x20 /* Start */ -#define I2C_PCA_CON_STO		0x10 /* Stop */ -#define I2C_PCA_CON_SI		0x08 /* Serial Interrupt */ -#define I2C_PCA_CON_CR		0x07 /* Clock Rate (MASK) */ - -#define I2C_PCA_CON_330kHz	0x00 -#define I2C_PCA_CON_288kHz	0x01 -#define I2C_PCA_CON_217kHz	0x02 -#define I2C_PCA_CON_146kHz	0x03 -#define I2C_PCA_CON_88kHz	0x04 -#define I2C_PCA_CON_59kHz	0x05 -#define I2C_PCA_CON_44kHz	0x06 -#define I2C_PCA_CON_36kHz	0x07 - -#endif /* I2C_PCA9564_H */ diff --git a/drivers/i2c/busses/i2c-pca-isa.c b/drivers/i2c/busses/i2c-pca-isa.c index 93ed3251893..a119784bae1 100644 --- a/drivers/i2c/busses/i2c-pca-isa.c +++ b/drivers/i2c/busses/i2c-pca-isa.c @@ -1,6 +1,7 @@  /*   *  i2c-pca-isa.c driver for PCA9564 on ISA boards   *    Copyright (C) 2004 Arcom Control Systems + *    Copyright (C) 2008 Pengutronix   *   *  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 @@ -22,11 +23,9 @@  #include <linux/module.h>  #include <linux/moduleparam.h>  #include <linux/delay.h> -#include <linux/slab.h>  #include <linux/init.h>  #include <linux/interrupt.h>  #include <linux/wait.h> -  #include <linux/isa.h>  #include <linux/i2c.h>  #include <linux/i2c-algo-pca.h> @@ -34,13 +33,9 @@  #include <asm/io.h>  #include <asm/irq.h> -#include "../algos/i2c-algo-pca.h" - +#define DRIVER "i2c-pca-isa"  #define IO_SIZE 4 -#undef DEBUG_IO -//#define DEBUG_IO -  static unsigned long base   = 0x330;  static int irq 	  = 10; @@ -48,22 +43,9 @@ static int irq 	  = 10;   * in the actual clock rate */  static int clock  = I2C_PCA_CON_59kHz; -static int own    = 0x55; -  static wait_queue_head_t pca_wait; -static int pca_isa_getown(struct i2c_algo_pca_data *adap) -{ -	return (own); -} - -static int pca_isa_getclock(struct i2c_algo_pca_data *adap) -{ -	return (clock); -} - -static void -pca_isa_writebyte(struct i2c_algo_pca_data *adap, int reg, int val) +static void pca_isa_writebyte(void *pd, int reg, int val)  {  #ifdef DEBUG_IO  	static char *names[] = { "T/O", "DAT", "ADR", "CON" }; @@ -72,8 +54,7 @@ pca_isa_writebyte(struct i2c_algo_pca_data *adap, int reg, int val)  	outb(val, base+reg);  } -static int -pca_isa_readbyte(struct i2c_algo_pca_data *adap, int reg) +static int pca_isa_readbyte(void *pd, int reg)  {  	int res = inb(base+reg);  #ifdef DEBUG_IO @@ -85,31 +66,37 @@ pca_isa_readbyte(struct i2c_algo_pca_data *adap, int reg)  	return res;  } -static int pca_isa_waitforinterrupt(struct i2c_algo_pca_data *adap) +static int pca_isa_waitforcompletion(void *pd)  {  	int ret = 0;  	if (irq > -1) {  		ret = wait_event_interruptible(pca_wait, -					       pca_isa_readbyte(adap, I2C_PCA_CON) & I2C_PCA_CON_SI); +					       pca_isa_readbyte(pd, I2C_PCA_CON) & I2C_PCA_CON_SI);  	} else { -		while ((pca_isa_readbyte(adap, I2C_PCA_CON) & I2C_PCA_CON_SI) == 0) +		while ((pca_isa_readbyte(pd, I2C_PCA_CON) & I2C_PCA_CON_SI) == 0)  			udelay(100);  	}  	return ret;  } +static void pca_isa_resetchip(void *pd) +{ +	/* apparently only an external reset will do it. not a lot can be done */ +	printk(KERN_WARNING DRIVER ": Haven't figured out how to do a reset yet\n"); +} +  static irqreturn_t pca_handler(int this_irq, void *dev_id) {  	wake_up_interruptible(&pca_wait);  	return IRQ_HANDLED;  }  static struct i2c_algo_pca_data pca_isa_data = { -	.get_own		= pca_isa_getown, -	.get_clock		= pca_isa_getclock, +	/* .data intentionally left NULL, not needed with ISA */  	.write_byte		= pca_isa_writebyte,  	.read_byte		= pca_isa_readbyte, -	.wait_for_interrupt	= pca_isa_waitforinterrupt, +	.wait_for_completion	= pca_isa_waitforcompletion, +	.reset_chip		= pca_isa_resetchip,  };  static struct i2c_adapter pca_isa_ops = { @@ -117,6 +104,7 @@ static struct i2c_adapter pca_isa_ops = {  	.id		= I2C_HW_A_ISA,  	.algo_data	= &pca_isa_data,  	.name		= "PCA9564 ISA Adapter", +	.timeout	= 100,  };  static int __devinit pca_isa_probe(struct device *dev, unsigned int id) @@ -144,6 +132,7 @@ static int __devinit pca_isa_probe(struct device *dev, unsigned int id)  		}  	} +	pca_isa_data.i2c_clock = clock;  	if (i2c_pca_add_bus(&pca_isa_ops) < 0) {  		dev_err(dev, "Failed to add i2c bus\n");  		goto out_irq; @@ -178,7 +167,7 @@ static struct isa_driver pca_isa_driver = {  	.remove		= __devexit_p(pca_isa_remove),  	.driver = {  		.owner	= THIS_MODULE, -		.name	= "i2c-pca-isa", +		.name	= DRIVER,  	}  }; @@ -204,7 +193,5 @@ MODULE_PARM_DESC(irq, "IRQ");  module_param(clock, int, 0);  MODULE_PARM_DESC(clock, "Clock rate as described in table 1 of PCA9564 datasheet"); -module_param(own, int, 0); /* the driver can't do slave mode, so there's no real point in this */ -  module_init(pca_isa_init);  module_exit(pca_isa_exit); diff --git a/include/linux/i2c-algo-pca.h b/include/linux/i2c-algo-pca.h index fce47c051bb..adcb3dc7ac2 100644 --- a/include/linux/i2c-algo-pca.h +++ b/include/linux/i2c-algo-pca.h @@ -1,14 +1,41 @@  #ifndef _LINUX_I2C_ALGO_PCA_H  #define _LINUX_I2C_ALGO_PCA_H +/* Clock speeds for the bus */ +#define I2C_PCA_CON_330kHz	0x00 +#define I2C_PCA_CON_288kHz	0x01 +#define I2C_PCA_CON_217kHz	0x02 +#define I2C_PCA_CON_146kHz	0x03 +#define I2C_PCA_CON_88kHz	0x04 +#define I2C_PCA_CON_59kHz	0x05 +#define I2C_PCA_CON_44kHz	0x06 +#define I2C_PCA_CON_36kHz	0x07 + +/* PCA9564 registers */ +#define I2C_PCA_STA		0x00 /* STATUS  Read Only  */ +#define I2C_PCA_TO		0x00 /* TIMEOUT Write Only */ +#define I2C_PCA_DAT		0x01 /* DATA    Read/Write */ +#define I2C_PCA_ADR		0x02 /* OWN ADR Read/Write */ +#define I2C_PCA_CON		0x03 /* CONTROL Read/Write */ + +#define I2C_PCA_CON_AA		0x80 /* Assert Acknowledge */ +#define I2C_PCA_CON_ENSIO	0x40 /* Enable */ +#define I2C_PCA_CON_STA		0x20 /* Start */ +#define I2C_PCA_CON_STO		0x10 /* Stop */ +#define I2C_PCA_CON_SI		0x08 /* Serial Interrupt */ +#define I2C_PCA_CON_CR		0x07 /* Clock Rate (MASK) */ +  struct i2c_algo_pca_data { -	int  (*get_own)			(struct i2c_algo_pca_data *adap); /* Obtain own address */ -	int  (*get_clock)		(struct i2c_algo_pca_data *adap); -	void (*write_byte)		(struct i2c_algo_pca_data *adap, int reg, int val); -	int  (*read_byte)		(struct i2c_algo_pca_data *adap, int reg); -	int  (*wait_for_interrupt)	(struct i2c_algo_pca_data *adap); +	void 				*data;	/* private low level data */ +	void (*write_byte)		(void *data, int reg, int val); +	int  (*read_byte)		(void *data, int reg); +	int  (*wait_for_completion)	(void *data); +	void (*reset_chip)		(void *data); +	/* i2c_clock values are defined in linux/i2c-algo-pca.h */ +	unsigned int			i2c_clock;  };  int i2c_pca_add_bus(struct i2c_adapter *); +int i2c_pca_add_numbered_bus(struct i2c_adapter *);  #endif /* _LINUX_I2C_ALGO_PCA_H */  |