Commit e786eacb authored by Ben Dooks's avatar Ben Dooks Committed by Greg Kroah-Hartman

[PATCH] S3C2410 i2c updates

This patch integrates several fixes to the s3c2410 i2c
driver

Shannon Holland:

	- write IICCON in configuration code
	- add handling for s3c2410 i2c errata
	- fix clock rate divisor calculation

Ben Dooks:

	- s3c2440 detection
	- s3c2440 IICLC register setup
	- add __exit to the module exit
	- remove return from exit code
Signed-off-by: default avatarBen Dooks <ben-linux@fluff.org>
Signed-off-by: default avatarShannon Holland <holland@loser.net>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 80d15eb2
...@@ -72,7 +72,7 @@ struct s3c24xx_i2c { ...@@ -72,7 +72,7 @@ struct s3c24xx_i2c {
struct i2c_adapter adap; struct i2c_adapter adap;
}; };
/* default platform data to use if not supplied in the platfrom_device /* default platform data to use if not supplied in the platform_device
*/ */
static struct s3c2410_platform_i2c s3c24xx_i2c_default_platform = { static struct s3c2410_platform_i2c s3c24xx_i2c_default_platform = {
...@@ -80,8 +80,21 @@ static struct s3c2410_platform_i2c s3c24xx_i2c_default_platform = { ...@@ -80,8 +80,21 @@ static struct s3c2410_platform_i2c s3c24xx_i2c_default_platform = {
.slave_addr = 0x10, .slave_addr = 0x10,
.bus_freq = 100*1000, .bus_freq = 100*1000,
.max_freq = 400*1000, .max_freq = 400*1000,
.sda_delay = S3C2410_IICLC_SDA_DELAY5 | S3C2410_IICLC_FILTER_ON,
}; };
/* s3c24xx_i2c_is2440()
*
* return true is this is an s3c2440
*/
static inline int s3c24xx_i2c_is2440(struct s3c24xx_i2c *i2c)
{
struct platform_device *pdev = to_platform_device(i2c->dev);
return !strcmp(pdev->name, "s3c2440-i2c");
}
/* s3c24xx_i2c_get_platformdata /* s3c24xx_i2c_get_platformdata
* *
...@@ -164,10 +177,9 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, ...@@ -164,10 +177,9 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
{ {
unsigned int addr = (msg->addr & 0x7f) << 1; unsigned int addr = (msg->addr & 0x7f) << 1;
unsigned long stat; unsigned long stat;
unsigned long iiccon;
stat = readl(i2c->regs + S3C2410_IICSTAT); stat = 0;
stat &= ~S3C2410_IICSTAT_MODEMASK;
stat |= S3C2410_IICSTAT_START;
stat |= S3C2410_IICSTAT_TXRXEN; stat |= S3C2410_IICSTAT_TXRXEN;
if (msg->flags & I2C_M_RD) { if (msg->flags & I2C_M_RD) {
...@@ -179,8 +191,18 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, ...@@ -179,8 +191,18 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
// todo - check for wether ack wanted or not // todo - check for wether ack wanted or not
s3c24xx_i2c_enable_ack(i2c); s3c24xx_i2c_enable_ack(i2c);
iiccon = readl(i2c->regs + S3C2410_IICCON);
writel(stat, i2c->regs + S3C2410_IICSTAT);
dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr); dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
writeb(addr, i2c->regs + S3C2410_IICDS); writeb(addr, i2c->regs + S3C2410_IICDS);
// delay a bit and reset iiccon before setting start (per samsung)
udelay(1);
dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
writel(iiccon, i2c->regs + S3C2410_IICCON);
stat |= S3C2410_IICSTAT_START;
writel(stat, i2c->regs + S3C2410_IICSTAT); writel(stat, i2c->regs + S3C2410_IICSTAT);
} }
...@@ -265,6 +287,7 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) ...@@ -265,6 +287,7 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
!(i2c->msg->flags & I2C_M_IGNORE_NAK)) { !(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
/* ack was not received... */ /* ack was not received... */
dev_err(i2c->dev, "ack was not received\n" );
s3c24xx_i2c_stop(i2c, -EREMOTEIO); s3c24xx_i2c_stop(i2c, -EREMOTEIO);
goto out_ack; goto out_ack;
} }
...@@ -408,6 +431,7 @@ static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id, ...@@ -408,6 +431,7 @@ static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id,
if (status & S3C2410_IICSTAT_ARBITR) { if (status & S3C2410_IICSTAT_ARBITR) {
// deal with arbitration loss // deal with arbitration loss
dev_err(i2c->dev, "deal with arbitration loss\n");
} }
if (i2c->state == STATE_IDLE) { if (i2c->state == STATE_IDLE) {
...@@ -575,7 +599,7 @@ static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted, ...@@ -575,7 +599,7 @@ static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted,
*divs = calc_divs; *divs = calc_divs;
*div1 = calc_div1; *div1 = calc_div1;
return clkin / (calc_divs + calc_div1); return clkin / (calc_divs * calc_div1);
} }
/* freq_acceptable /* freq_acceptable
...@@ -683,6 +707,17 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) ...@@ -683,6 +707,17 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
/* todo - check that the i2c lines aren't being dragged anywhere */ /* todo - check that the i2c lines aren't being dragged anywhere */
dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq); dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq);
dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);
writel(iicon, i2c->regs + S3C2410_IICCON);
/* check for s3c2440 i2c controller */
if (s3c24xx_i2c_is2440(i2c)) {
dev_dbg(i2c->dev, "S3C2440_IICLC=%08x\n", pdata->sda_delay);
writel(pdata->sda_delay, i2c->regs + S3C2440_IICLC);
}
return 0; return 0;
} }
...@@ -850,7 +885,7 @@ static int s3c24xx_i2c_resume(struct device *dev, u32 level) ...@@ -850,7 +885,7 @@ static int s3c24xx_i2c_resume(struct device *dev, u32 level)
/* device driver for platform bus bits */ /* device driver for platform bus bits */
static struct device_driver s3c24xx_i2c_driver = { static struct device_driver s3c2410_i2c_driver = {
.name = "s3c2410-i2c", .name = "s3c2410-i2c",
.bus = &platform_bus_type, .bus = &platform_bus_type,
.probe = s3c24xx_i2c_probe, .probe = s3c24xx_i2c_probe,
...@@ -858,14 +893,29 @@ static struct device_driver s3c24xx_i2c_driver = { ...@@ -858,14 +893,29 @@ static struct device_driver s3c24xx_i2c_driver = {
.resume = s3c24xx_i2c_resume, .resume = s3c24xx_i2c_resume,
}; };
static struct device_driver s3c2440_i2c_driver = {
.name = "s3c2440-i2c",
.bus = &platform_bus_type,
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
.resume = s3c24xx_i2c_resume,
};
static int __init i2c_adap_s3c_init(void) static int __init i2c_adap_s3c_init(void)
{ {
return driver_register(&s3c24xx_i2c_driver); int ret;
ret = driver_register(&s3c2410_i2c_driver);
if (ret == 0)
ret = driver_register(&s3c2440_i2c_driver);
return ret;
} }
static void i2c_adap_s3c_exit(void) static void __exit i2c_adap_s3c_exit(void)
{ {
return driver_unregister(&s3c24xx_i2c_driver); driver_unregister(&s3c2410_i2c_driver);
driver_unregister(&s3c2440_i2c_driver);
} }
module_init(i2c_adap_s3c_init); module_init(i2c_adap_s3c_init);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment