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:
commit
d32f60ed54
|
@ -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 = {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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");
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Loading…
Reference in New Issue