X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fi2c%2Fbusses%2Fi2c-s3c2410.c;h=c772e02c28036a9621006e78f66cebee27fd1e41;hb=0a346dacee18ff69f6162d9860d723a058f47321;hp=1b582262e677ed89ce68b9277e927d4f61046948;hpb=e32e28edc3d894201e15b19df627af66023aa91f;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 1b58226..c772e02 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include @@ -29,20 +28,20 @@ #include #include #include -#include #include #include #include #include +#include +#include -#include +#include #include #include -#include -#include -#include -#include +#include +#include +#include /* i2c controller state */ @@ -63,7 +62,10 @@ struct s3c24xx_i2c { unsigned int msg_idx; unsigned int msg_ptr; + unsigned int tx_setup; + enum s3c24xx_i2c_state state; + unsigned long clkrate; void __iomem *regs; struct clk *clk; @@ -71,6 +73,10 @@ struct s3c24xx_i2c { struct resource *irq; struct resource *ioarea; struct i2c_adapter adap; + +#ifdef CONFIG_CPU_FREQ + struct notifier_block freq_transition; +#endif }; /* default platform data to use if not supplied in the platform_device @@ -201,8 +207,11 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr); writeb(addr, i2c->regs + S3C2410_IICDS); - // delay a bit and reset iiccon before setting start (per samsung) - udelay(1); + /* delay here to ensure the data byte has gotten onto the bus + * before the transaction is started */ + + ndelay(i2c->tx_setup); + dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon); writel(iiccon, i2c->regs + S3C2410_IICCON); @@ -273,12 +282,12 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) switch (i2c->state) { case STATE_IDLE: - dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __FUNCTION__); + dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __func__); goto out; break; case STATE_STOP: - dev_err(i2c->dev, "%s: called in STATE_STOP\n", __FUNCTION__); + dev_err(i2c->dev, "%s: called in STATE_STOP\n", __func__); s3c24xx_i2c_disable_irq(i2c); goto out_ack; @@ -287,12 +296,12 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) * bus, or started a new i2c message */ - if (iicstat & S3C2410_IICSTAT_LASTBIT && + if (iicstat & S3C2410_IICSTAT_LASTBIT && !(i2c->msg->flags & I2C_M_IGNORE_NAK)) { /* ack was not received... */ dev_dbg(i2c->dev, "ack was not received\n"); - s3c24xx_i2c_stop(i2c, -EREMOTEIO); + s3c24xx_i2c_stop(i2c, -ENXIO); goto out_ack; } @@ -302,7 +311,7 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) i2c->state = STATE_WRITE; /* terminate the transfer if there is nothing to do - * (used by the i2c probe to find devices */ + * as this is used by the i2c probe to find devices. */ if (is_lastmsg(i2c) && i2c->msg->len == 0) { s3c24xx_i2c_stop(i2c, 0); @@ -320,11 +329,29 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) * end of the message, and if so, work out what to do */ + if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) { + if (iicstat & S3C2410_IICSTAT_LASTBIT) { + dev_dbg(i2c->dev, "WRITE: No Ack\n"); + + s3c24xx_i2c_stop(i2c, -ECONNREFUSED); + goto out_ack; + } + } + retry_write: + if (!is_msgend(i2c)) { byte = i2c->msg->buf[i2c->msg_ptr++]; writeb(byte, i2c->regs + S3C2410_IICDS); - + + /* delay after writing the byte to allow the + * data setup time on the bus, as writing the + * data to the register causes the first bit + * to appear on SDA, and SCL will change as + * soon as the interrupt is acknowledged */ + + ndelay(i2c->tx_setup); + } else if (!is_lastmsg(i2c)) { /* we need to go to the next i2c message */ @@ -366,17 +393,6 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) * going to do any more read/write */ - if (!(i2c->msg->flags & I2C_M_IGNORE_NAK) && - !(is_msglast(i2c) && is_lastmsg(i2c))) { - - if (iicstat & S3C2410_IICSTAT_LASTBIT) { - dev_dbg(i2c->dev, "READ: No Ack\n"); - - s3c24xx_i2c_stop(i2c, -ECONNREFUSED); - goto out_ack; - } - } - byte = readb(i2c->regs + S3C2410_IICDS); i2c->msg->buf[i2c->msg_ptr++] = byte; @@ -424,8 +440,7 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) * top level IRQ servicing routine */ -static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id, - struct pt_regs *regs) +static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id) { struct s3c24xx_i2c *i2c = dev_id; unsigned long status; @@ -492,6 +507,9 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int unsigned long timeout; int ret; + if (!readl(i2c->regs + S3C2410_IICCON) & S3C2410_IICCON_IRQEN) + return -EIO; + ret = s3c24xx_i2c_set_master(i2c); if (ret != 0) { dev_err(i2c->dev, "cannot get bus (error %d)\n", ret); @@ -567,20 +585,21 @@ 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, }; static struct s3c24xx_i2c s3c24xx_i2c = { - .lock = SPIN_LOCK_UNLOCKED, - .wait = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait), - .adap = { + .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock), + .wait = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait), + .tx_setup = 50, + .adap = { .name = "s3c2410-i2c", .owner = THIS_MODULE, .algo = &s3c24xx_i2c_algorithm, .retries = 2, - .class = I2C_CLASS_HWMON, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, }, }; @@ -626,27 +645,28 @@ static inline int freq_acceptable(unsigned int freq, unsigned int wanted) return (diff >= -2 && diff <= 2); } -/* s3c24xx_i2c_getdivisor +/* s3c24xx_i2c_clockrate * * work out a divisor for the user requested frequency setting, * either by the requested frequency, or scanning the acceptable * range of frequencies until something is found */ -static int s3c24xx_i2c_getdivisor(struct s3c24xx_i2c *i2c, - struct s3c2410_platform_i2c *pdata, - unsigned long *iicon, - unsigned int *got) +static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got) { + struct s3c2410_platform_i2c *pdata; unsigned long clkin = clk_get_rate(i2c->clk); - unsigned int divs, div1; + u32 iiccon; int freq; int start, end; + i2c->clkrate = clkin; + + pdata = s3c24xx_i2c_get_platformdata(i2c->adap.dev.parent); clkin /= 1000; /* clkin now in KHz */ - dev_dbg(i2c->dev, "pdata %p, freq %lu %lu..%lu\n", + dev_dbg(i2c->dev, "pdata %p, freq %lu %lu..%lu\n", pdata, pdata->bus_freq, pdata->min_freq, pdata->max_freq); if (pdata->bus_freq != 0) { @@ -678,11 +698,79 @@ static int s3c24xx_i2c_getdivisor(struct s3c24xx_i2c *i2c, found: *got = freq; - *iicon |= (divs-1); - *iicon |= (div1 == 512) ? S3C2410_IICCON_TXDIV_512 : 0; + + iiccon = readl(i2c->regs + S3C2410_IICCON); + iiccon &= ~(S3C2410_IICCON_SCALEMASK | S3C2410_IICCON_TXDIV_512); + iiccon |= (divs-1); + + if (div1 == 512) + iiccon |= S3C2410_IICCON_TXDIV_512; + + writel(iiccon, i2c->regs + S3C2410_IICCON); + + return 0; +} + +#ifdef CONFIG_CPU_FREQ + +#define freq_to_i2c(_n) container_of(_n, struct s3c24xx_i2c, freq_transition) + +static int s3c24xx_i2c_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct s3c24xx_i2c *i2c = freq_to_i2c(nb); + unsigned long flags; + unsigned int got; + int delta_f; + int ret; + + delta_f = clk_get_rate(i2c->clk) - i2c->clkrate; + + /* if we're post-change and the input clock has slowed down + * or at pre-change and the clock is about to speed up, then + * adjust our clock rate. <0 is slow, >0 speedup. + */ + + if ((val == CPUFREQ_POSTCHANGE && delta_f < 0) || + (val == CPUFREQ_PRECHANGE && delta_f > 0)) { + spin_lock_irqsave(&i2c->lock, flags); + ret = s3c24xx_i2c_clockrate(i2c, &got); + spin_unlock_irqrestore(&i2c->lock, flags); + + if (ret < 0) + dev_err(i2c->dev, "cannot find frequency\n"); + else + dev_info(i2c->dev, "setting freq %d\n", got); + } + + return 0; +} + +static inline int s3c24xx_i2c_register_cpufreq(struct s3c24xx_i2c *i2c) +{ + i2c->freq_transition.notifier_call = s3c24xx_i2c_cpufreq_transition; + + return cpufreq_register_notifier(&i2c->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +static inline void s3c24xx_i2c_deregister_cpufreq(struct s3c24xx_i2c *i2c) +{ + cpufreq_unregister_notifier(&i2c->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +#else +static inline int s3c24xx_i2c_register_cpufreq(struct s3c24xx_i2c *i2c) +{ return 0; } +static inline void s3c24xx_i2c_deregister_cpufreq(struct s3c24xx_i2c *i2c) +{ +} +#endif + /* s3c24xx_i2c_init * * initialise the controller, set the IO lines and frequency @@ -709,9 +797,12 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr); + writel(iicon, i2c->regs + S3C2410_IICCON); + /* we need to work out the divisors for the clock... */ - if (s3c24xx_i2c_getdivisor(i2c, pdata, &iicon, &freq) != 0) { + if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) { + writel(0, i2c->regs + S3C2410_IICCON); dev_err(i2c->dev, "cannot meet bus frequency required\n"); return -EINVAL; } @@ -720,8 +811,6 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) 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 */ @@ -734,92 +823,72 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) return 0; } -static void s3c24xx_i2c_free(struct s3c24xx_i2c *i2c) -{ - if (i2c->clk != NULL && !IS_ERR(i2c->clk)) { - clk_disable(i2c->clk); - clk_unuse(i2c->clk); - clk_put(i2c->clk); - i2c->clk = NULL; - } - - if (i2c->regs != NULL) { - iounmap(i2c->regs); - i2c->regs = NULL; - } - - if (i2c->ioarea != NULL) { - release_resource(i2c->ioarea); - kfree(i2c->ioarea); - i2c->ioarea = NULL; - } -} - /* s3c24xx_i2c_probe * * called by the bus driver when a suitable device is found */ -static int s3c24xx_i2c_probe(struct device *dev) +static int s3c24xx_i2c_probe(struct platform_device *pdev) { - struct platform_device *pdev = to_platform_device(dev); struct s3c24xx_i2c *i2c = &s3c24xx_i2c; + struct s3c2410_platform_i2c *pdata; struct resource *res; int ret; + pdata = s3c24xx_i2c_get_platformdata(&pdev->dev); + /* find the clock and enable it */ - i2c->dev = dev; - i2c->clk = clk_get(dev, "i2c"); + i2c->dev = &pdev->dev; + i2c->clk = clk_get(&pdev->dev, "i2c"); if (IS_ERR(i2c->clk)) { - dev_err(dev, "cannot get clock\n"); + dev_err(&pdev->dev, "cannot get clock\n"); ret = -ENOENT; - goto out; + goto err_noclk; } - dev_dbg(dev, "clock source %p\n", i2c->clk); + dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk); - clk_use(i2c->clk); clk_enable(i2c->clk); /* map the registers */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { - dev_err(dev, "cannot find IO resource\n"); + dev_err(&pdev->dev, "cannot find IO resource\n"); ret = -ENOENT; - goto out; + goto err_clk; } i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1, pdev->name); if (i2c->ioarea == NULL) { - dev_err(dev, "cannot request IO\n"); + dev_err(&pdev->dev, "cannot request IO\n"); ret = -ENXIO; - goto out; + goto err_clk; } i2c->regs = ioremap(res->start, (res->end-res->start)+1); if (i2c->regs == NULL) { - dev_err(dev, "cannot map IO\n"); + dev_err(&pdev->dev, "cannot map IO\n"); ret = -ENXIO; - goto out; + goto err_ioarea; } - dev_dbg(dev, "registers %p (%p, %p)\n", i2c->regs, i2c->ioarea, res); + dev_dbg(&pdev->dev, "registers %p (%p, %p)\n", i2c->regs, i2c->ioarea, res); /* setup info block for the i2c core */ i2c->adap.algo_data = i2c; - i2c->adap.dev.parent = dev; + i2c->adap.dev.parent = &pdev->dev; /* initialise the i2c controller */ ret = s3c24xx_i2c_init(i2c); if (ret != 0) - goto out; + goto err_iomap; /* find the IRQ for this unit (note, this relies on the init call to * ensure no current IRQs pending @@ -827,37 +896,67 @@ static int s3c24xx_i2c_probe(struct device *dev) res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { - dev_err(dev, "cannot find IRQ\n"); + dev_err(&pdev->dev, "cannot find IRQ\n"); ret = -ENOENT; - goto out; + goto err_iomap; } - ret = request_irq(res->start, s3c24xx_i2c_irq, SA_INTERRUPT, + ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED, pdev->name, i2c); if (ret != 0) { - dev_err(dev, "cannot claim IRQ\n"); - goto out; + dev_err(&pdev->dev, "cannot claim IRQ\n"); + goto err_iomap; } i2c->irq = res; - dev_dbg(dev, "irq resource %p (%ld)\n", res, res->start); + dev_dbg(&pdev->dev, "irq resource %p (%lu)\n", res, + (unsigned long)res->start); - ret = i2c_add_adapter(&i2c->adap); + ret = s3c24xx_i2c_register_cpufreq(i2c); if (ret < 0) { - dev_err(dev, "failed to add bus to i2c core\n"); - goto out; + dev_err(&pdev->dev, "failed to register cpufreq notifier\n"); + goto err_irq; + } + + /* Note, previous versions of the driver used i2c_add_adapter() + * to add the bus at any number. We now pass the bus number via + * the platform data, so if unset it will now default to always + * being bus 0. + */ + + i2c->adap.nr = pdata->bus_num; + + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add bus to i2c core\n"); + goto err_cpufreq; } - dev_set_drvdata(dev, i2c); + platform_set_drvdata(pdev, i2c); - dev_info(dev, "%s: S3C I2C adapter\n", i2c->adap.dev.bus_id); + dev_info(&pdev->dev, "%s: S3C I2C adapter\n", i2c->adap.dev.bus_id); + return 0; - out: - if (ret < 0) - s3c24xx_i2c_free(i2c); + err_cpufreq: + s3c24xx_i2c_deregister_cpufreq(i2c); + err_irq: + free_irq(i2c->irq->start, i2c); + + err_iomap: + iounmap(i2c->regs); + + err_ioarea: + release_resource(i2c->ioarea); + kfree(i2c->ioarea); + + err_clk: + clk_disable(i2c->clk); + clk_put(i2c->clk); + + err_noclk: return ret; } @@ -866,22 +965,30 @@ static int s3c24xx_i2c_probe(struct device *dev) * called when device is removed from the bus */ -static int s3c24xx_i2c_remove(struct device *dev) +static int s3c24xx_i2c_remove(struct platform_device *pdev) { - struct s3c24xx_i2c *i2c = dev_get_drvdata(dev); - - if (i2c != NULL) { - s3c24xx_i2c_free(i2c); - dev_set_drvdata(dev, NULL); - } + struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + + s3c24xx_i2c_deregister_cpufreq(i2c); + + i2c_del_adapter(&i2c->adap); + free_irq(i2c->irq->start, i2c); + + clk_disable(i2c->clk); + clk_put(i2c->clk); + + iounmap(i2c->regs); + + release_resource(i2c->ioarea); + kfree(i2c->ioarea); return 0; } #ifdef CONFIG_PM -static int s3c24xx_i2c_resume(struct device *dev) +static int s3c24xx_i2c_resume(struct platform_device *dev) { - struct s3c24xx_i2c *i2c = dev_get_drvdata(dev); + struct s3c24xx_i2c *i2c = platform_get_drvdata(dev); if (i2c != NULL) s3c24xx_i2c_init(i2c); @@ -895,33 +1002,35 @@ static int s3c24xx_i2c_resume(struct device *dev) /* device driver for platform bus bits */ -static struct device_driver s3c2410_i2c_driver = { - .owner = THIS_MODULE, - .name = "s3c2410-i2c", - .bus = &platform_bus_type, +static struct platform_driver s3c2410_i2c_driver = { .probe = s3c24xx_i2c_probe, .remove = s3c24xx_i2c_remove, .resume = s3c24xx_i2c_resume, + .driver = { + .owner = THIS_MODULE, + .name = "s3c2410-i2c", + }, }; -static struct device_driver s3c2440_i2c_driver = { - .owner = THIS_MODULE, - .name = "s3c2440-i2c", - .bus = &platform_bus_type, +static struct platform_driver s3c2440_i2c_driver = { .probe = s3c24xx_i2c_probe, .remove = s3c24xx_i2c_remove, .resume = s3c24xx_i2c_resume, + .driver = { + .owner = THIS_MODULE, + .name = "s3c2440-i2c", + }, }; static int __init i2c_adap_s3c_init(void) { int ret; - ret = driver_register(&s3c2410_i2c_driver); + ret = platform_driver_register(&s3c2410_i2c_driver); if (ret == 0) { - ret = driver_register(&s3c2440_i2c_driver); + ret = platform_driver_register(&s3c2440_i2c_driver); if (ret) - driver_unregister(&s3c2410_i2c_driver); + platform_driver_unregister(&s3c2410_i2c_driver); } return ret; @@ -929,8 +1038,8 @@ static int __init i2c_adap_s3c_init(void) static void __exit i2c_adap_s3c_exit(void) { - driver_unregister(&s3c2410_i2c_driver); - driver_unregister(&s3c2440_i2c_driver); + platform_driver_unregister(&s3c2410_i2c_driver); + platform_driver_unregister(&s3c2440_i2c_driver); } module_init(i2c_adap_s3c_init); @@ -939,3 +1048,5 @@ module_exit(i2c_adap_s3c_exit); MODULE_DESCRIPTION("S3C24XX I2C Bus driver"); MODULE_AUTHOR("Ben Dooks, "); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:s3c2410-i2c"); +MODULE_ALIAS("platform:s3c2440-i2c");