Merge branch 'next-i2c' of git://aeryn.fluff.org.uk/bjdooks/linux

* 'next-i2c' of git://aeryn.fluff.org.uk/bjdooks/linux:
  i2c-ocores: Can add I2C devices to the bus
  i2c-s3c2410: move to using platform idtable to match devices
  i2c: OMAP3: Better noise suppression for fast/standard modes
  i2c: OMAP2/3: Fix scll/sclh calculations
  i2c: Blackfin TWI: implement I2C_FUNC_SMBUS_I2C_BLOCK functionality
  i2c: Blackfin TWI: fix transfer errors with repeat start
  i2c: Blackfin TWI: fix REPEAT START mode doesn't repeat
  i2c: Blackfin TWI: make sure we don't end up with a CLKDIV=0
This commit is contained in:
Linus Torvalds 2009-06-13 13:15:59 -07:00
commit d32f60ed54
7 changed files with 118 additions and 54 deletions

View File

@ -20,6 +20,8 @@ platform_device with the base address and interrupt number. The
dev.platform_data of the device should also point to a struct dev.platform_data of the device should also point to a struct
ocores_i2c_platform_data (see linux/i2c-ocores.h) describing the ocores_i2c_platform_data (see linux/i2c-ocores.h) describing the
distance between registers and the input clock speed. distance between registers and the input clock speed.
There is also a possibility to attach a list of i2c_board_info which
the i2c-ocores driver will add to the bus upon creation.
E.G. something like: E.G. something like:
@ -36,9 +38,24 @@ static struct resource ocores_resources[] = {
}, },
}; };
/* optional board info */
struct i2c_board_info ocores_i2c_board_info[] = {
{
I2C_BOARD_INFO("tsc2003", 0x48),
.platform_data = &tsc2003_platform_data,
.irq = TSC_IRQ
},
{
I2C_BOARD_INFO("adv7180", 0x42 >> 1),
.irq = ADV_IRQ
}
};
static struct ocores_i2c_platform_data myi2c_data = { static struct ocores_i2c_platform_data myi2c_data = {
.regstep = 2, /* two bytes between registers */ .regstep = 2, /* two bytes between registers */
.clock_khz = 50000, /* input clock of 50MHz */ .clock_khz = 50000, /* input clock of 50MHz */
.devices = ocores_i2c_board_info, /* optional table of devices */
.num_devices = ARRAY_SIZE(ocores_i2c_board_info), /* table size */
}; };
static struct platform_device myi2c = { static struct platform_device myi2c = {

View File

@ -298,7 +298,7 @@ config I2C_BLACKFIN_TWI
config I2C_BLACKFIN_TWI_CLK_KHZ config I2C_BLACKFIN_TWI_CLK_KHZ
int "Blackfin TWI I2C clock (kHz)" int "Blackfin TWI I2C clock (kHz)"
depends on I2C_BLACKFIN_TWI depends on I2C_BLACKFIN_TWI
range 10 400 range 21 400
default 50 default 50
help help
The unit of the TWI clock is kHz. The unit of the TWI clock is kHz.

View File

@ -104,9 +104,14 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
write_MASTER_CTL(iface, write_MASTER_CTL(iface,
read_MASTER_CTL(iface) | STOP); read_MASTER_CTL(iface) | STOP);
else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
iface->cur_msg+1 < iface->msg_num) iface->cur_msg + 1 < iface->msg_num) {
write_MASTER_CTL(iface, if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
read_MASTER_CTL(iface) | RSTART); write_MASTER_CTL(iface,
read_MASTER_CTL(iface) | RSTART | MDIR);
else
write_MASTER_CTL(iface,
(read_MASTER_CTL(iface) | RSTART) & ~MDIR);
}
SSYNC(); SSYNC();
/* Clear status */ /* Clear status */
write_INT_STAT(iface, XMTSERV); write_INT_STAT(iface, XMTSERV);
@ -134,9 +139,13 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
read_MASTER_CTL(iface) | STOP); read_MASTER_CTL(iface) | STOP);
SSYNC(); SSYNC();
} else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
iface->cur_msg+1 < iface->msg_num) { iface->cur_msg + 1 < iface->msg_num) {
write_MASTER_CTL(iface, if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
read_MASTER_CTL(iface) | RSTART); write_MASTER_CTL(iface,
read_MASTER_CTL(iface) | RSTART | MDIR);
else
write_MASTER_CTL(iface,
(read_MASTER_CTL(iface) | RSTART) & ~MDIR);
SSYNC(); SSYNC();
} }
/* Clear interrupt source */ /* Clear interrupt source */
@ -196,8 +205,6 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
/* remove restart bit and enable master receive */ /* remove restart bit and enable master receive */
write_MASTER_CTL(iface, write_MASTER_CTL(iface,
read_MASTER_CTL(iface) & ~RSTART); read_MASTER_CTL(iface) & ~RSTART);
write_MASTER_CTL(iface,
read_MASTER_CTL(iface) | MEN | MDIR);
SSYNC(); SSYNC();
} else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
iface->cur_msg+1 < iface->msg_num) { iface->cur_msg+1 < iface->msg_num) {
@ -222,18 +229,19 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
} }
if (iface->pmsg[iface->cur_msg].len <= 255) if (iface->pmsg[iface->cur_msg].len <= 255)
write_MASTER_CTL(iface, write_MASTER_CTL(iface,
iface->pmsg[iface->cur_msg].len << 6); (read_MASTER_CTL(iface) &
(~(0xff << 6))) |
(iface->pmsg[iface->cur_msg].len << 6));
else { else {
write_MASTER_CTL(iface, 0xff << 6); write_MASTER_CTL(iface,
(read_MASTER_CTL(iface) |
(0xff << 6)));
iface->manual_stop = 1; iface->manual_stop = 1;
} }
/* remove restart bit and enable master receive */ /* remove restart bit and enable master receive */
write_MASTER_CTL(iface, write_MASTER_CTL(iface,
read_MASTER_CTL(iface) & ~RSTART); read_MASTER_CTL(iface) & ~RSTART);
write_MASTER_CTL(iface, read_MASTER_CTL(iface) |
MEN | ((iface->read_write == I2C_SMBUS_READ) ?
MDIR : 0));
SSYNC(); SSYNC();
} else { } else {
iface->result = 1; iface->result = 1;
@ -441,6 +449,16 @@ int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr,
} }
iface->transPtr = data->block; iface->transPtr = data->block;
break; break;
case I2C_SMBUS_I2C_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
iface->readNum = data->block[0];
iface->cur_mode = TWI_I2C_MODE_COMBINED;
} else {
iface->writeNum = data->block[0];
iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
}
iface->transPtr = (u8 *)&data->block[1];
break;
default: default:
return -1; return -1;
} }
@ -564,7 +582,7 @@ static u32 bfin_twi_functionality(struct i2c_adapter *adap)
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL |
I2C_FUNC_I2C; I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK;
} }
static struct i2c_algorithm bfin_twi_algorithm = { static struct i2c_algorithm bfin_twi_algorithm = {
@ -614,6 +632,7 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
struct i2c_adapter *p_adap; struct i2c_adapter *p_adap;
struct resource *res; struct resource *res;
int rc; int rc;
unsigned int clkhilow;
iface = kzalloc(sizeof(struct bfin_twi_iface), GFP_KERNEL); iface = kzalloc(sizeof(struct bfin_twi_iface), GFP_KERNEL);
if (!iface) { if (!iface) {
@ -675,10 +694,14 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
/* Set TWI internal clock as 10MHz */ /* Set TWI internal clock as 10MHz */
write_CONTROL(iface, ((get_sclk() / 1024 / 1024 + 5) / 10) & 0x7F); write_CONTROL(iface, ((get_sclk() / 1024 / 1024 + 5) / 10) & 0x7F);
/*
* We will not end up with a CLKDIV=0 because no one will specify
* 20kHz SCL or less in Kconfig now. (5 * 1024 / 20 = 0x100)
*/
clkhilow = 5 * 1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ;
/* Set Twi interface clock as specified */ /* Set Twi interface clock as specified */
write_CLKDIV(iface, ((5*1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ) write_CLKDIV(iface, (clkhilow << 8) | clkhilow);
<< 8) | ((5*1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ)
& 0xFF));
/* Enable TWI */ /* Enable TWI */
write_CONTROL(iface, read_CONTROL(iface) | TWI_ENA); write_CONTROL(iface, read_CONTROL(iface) | TWI_ENA);

View File

@ -216,6 +216,7 @@ static int __devinit ocores_i2c_probe(struct platform_device *pdev)
struct ocores_i2c_platform_data *pdata; struct ocores_i2c_platform_data *pdata;
struct resource *res, *res2; struct resource *res, *res2;
int ret; int ret;
int i;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) if (!res)
@ -271,6 +272,10 @@ static int __devinit ocores_i2c_probe(struct platform_device *pdev)
goto add_adapter_failed; goto add_adapter_failed;
} }
/* add in known devices to the bus */
for (i = 0; i < pdata->num_devices; i++)
i2c_new_device(&i2c->adap, pdata->devices + i);
return 0; return 0;
add_adapter_failed: add_adapter_failed:

View File

@ -333,8 +333,18 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
if (cpu_is_omap2430() || cpu_is_omap34xx()) { if (cpu_is_omap2430() || cpu_is_omap34xx()) {
/* HSI2C controller internal clk rate should be 19.2 Mhz */ /*
internal_clk = 19200; * HSI2C controller internal clk rate should be 19.2 Mhz for
* HS and for all modes on 2430. On 34xx we can use lower rate
* to get longer filter period for better noise suppression.
* The filter is iclk (fclk for HS) period.
*/
if (dev->speed > 400 || cpu_is_omap_2430())
internal_clk = 19200;
else if (dev->speed > 100)
internal_clk = 9600;
else
internal_clk = 4000;
fclk_rate = clk_get_rate(dev->fclk) / 1000; fclk_rate = clk_get_rate(dev->fclk) / 1000;
/* Compute prescaler divisor */ /* Compute prescaler divisor */
@ -343,17 +353,28 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
/* If configured for High Speed */ /* If configured for High Speed */
if (dev->speed > 400) { if (dev->speed > 400) {
unsigned long scl;
/* For first phase of HS mode */ /* For first phase of HS mode */
fsscll = internal_clk / (400 * 2) - 6; scl = internal_clk / 400;
fssclh = internal_clk / (400 * 2) - 6; fsscll = scl - (scl / 3) - 7;
fssclh = (scl / 3) - 5;
/* For second phase of HS mode */ /* For second phase of HS mode */
hsscll = fclk_rate / (dev->speed * 2) - 6; scl = fclk_rate / dev->speed;
hssclh = fclk_rate / (dev->speed * 2) - 6; hsscll = scl - (scl / 3) - 7;
hssclh = (scl / 3) - 5;
} else if (dev->speed > 100) {
unsigned long scl;
/* Fast mode */
scl = internal_clk / dev->speed;
fsscll = scl - (scl / 3) - 7;
fssclh = (scl / 3) - 5;
} else { } else {
/* To handle F/S modes */ /* Standard mode */
fsscll = internal_clk / (dev->speed * 2) - 6; fsscll = internal_clk / (dev->speed * 2) - 7;
fssclh = internal_clk / (dev->speed * 2) - 6; fssclh = internal_clk / (dev->speed * 2) - 5;
} }
scll = (hsscll << OMAP_I2C_SCLL_HSSCLL) | fsscll; scll = (hsscll << OMAP_I2C_SCLL_HSSCLL) | fsscll;
sclh = (hssclh << OMAP_I2C_SCLH_HSSCLH) | fssclh; sclh = (hssclh << OMAP_I2C_SCLH_HSSCLH) | fssclh;

View File

@ -51,6 +51,11 @@ enum s3c24xx_i2c_state {
STATE_STOP STATE_STOP
}; };
enum s3c24xx_i2c_type {
TYPE_S3C2410,
TYPE_S3C2440,
};
struct s3c24xx_i2c { struct s3c24xx_i2c {
spinlock_t lock; spinlock_t lock;
wait_queue_head_t wait; wait_queue_head_t wait;
@ -88,8 +93,10 @@ struct s3c24xx_i2c {
static inline int s3c24xx_i2c_is2440(struct s3c24xx_i2c *i2c) static inline int s3c24xx_i2c_is2440(struct s3c24xx_i2c *i2c)
{ {
struct platform_device *pdev = to_platform_device(i2c->dev); struct platform_device *pdev = to_platform_device(i2c->dev);
enum s3c24xx_i2c_type type;
return !strcmp(pdev->name, "s3c2440-i2c"); type = platform_get_device_id(pdev)->driver_data;
return type == TYPE_S3C2440;
} }
/* s3c24xx_i2c_master_complete /* s3c24xx_i2c_master_complete
@ -969,52 +976,41 @@ static int s3c24xx_i2c_resume(struct platform_device *dev)
/* device driver for platform bus bits */ /* device driver for platform bus bits */
static struct platform_driver s3c2410_i2c_driver = { static struct platform_device_id s3c24xx_driver_ids[] = {
.probe = s3c24xx_i2c_probe, {
.remove = s3c24xx_i2c_remove, .name = "s3c2410-i2c",
.suspend_late = s3c24xx_i2c_suspend_late, .driver_data = TYPE_S3C2410,
.resume = s3c24xx_i2c_resume, }, {
.driver = { .name = "s3c2440-i2c",
.owner = THIS_MODULE, .driver_data = TYPE_S3C2440,
.name = "s3c2410-i2c", }, { },
},
}; };
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
static struct platform_driver s3c2440_i2c_driver = { static struct platform_driver s3c24xx_i2c_driver = {
.probe = s3c24xx_i2c_probe, .probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove, .remove = s3c24xx_i2c_remove,
.suspend_late = s3c24xx_i2c_suspend_late, .suspend_late = s3c24xx_i2c_suspend_late,
.resume = s3c24xx_i2c_resume, .resume = s3c24xx_i2c_resume,
.id_table = s3c24xx_driver_ids,
.driver = { .driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "s3c2440-i2c", .name = "s3c-i2c",
}, },
}; };
static int __init i2c_adap_s3c_init(void) static int __init i2c_adap_s3c_init(void)
{ {
int ret; return platform_driver_register(&s3c24xx_i2c_driver);
ret = platform_driver_register(&s3c2410_i2c_driver);
if (ret == 0) {
ret = platform_driver_register(&s3c2440_i2c_driver);
if (ret)
platform_driver_unregister(&s3c2410_i2c_driver);
}
return ret;
} }
subsys_initcall(i2c_adap_s3c_init); subsys_initcall(i2c_adap_s3c_init);
static void __exit i2c_adap_s3c_exit(void) static void __exit i2c_adap_s3c_exit(void)
{ {
platform_driver_unregister(&s3c2410_i2c_driver); platform_driver_unregister(&s3c24xx_i2c_driver);
platform_driver_unregister(&s3c2440_i2c_driver);
} }
module_exit(i2c_adap_s3c_exit); module_exit(i2c_adap_s3c_exit);
MODULE_DESCRIPTION("S3C24XX I2C Bus driver"); MODULE_DESCRIPTION("S3C24XX I2C Bus driver");
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:s3c2410-i2c");
MODULE_ALIAS("platform:s3c2440-i2c");

View File

@ -14,6 +14,8 @@
struct ocores_i2c_platform_data { struct ocores_i2c_platform_data {
u32 regstep; /* distance between registers */ u32 regstep; /* distance between registers */
u32 clock_khz; /* input clock in kHz */ u32 clock_khz; /* input clock in kHz */
u8 num_devices; /* number of devices in the devices list */
struct i2c_board_info const *devices; /* devices connected to the bus */
}; };
#endif /* _LINUX_I2C_OCORES_H */ #endif /* _LINUX_I2C_OCORES_H */