diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-28 08:28:50 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-28 08:28:50 -0700 |
commit | 9ffc1699e36abd5baee188c8e36b1bb27d0d4278 (patch) | |
tree | ce07bb773305b38ec672e1e37f48bd6ecfdc8dc4 /drivers | |
parent | a01386924874c4d6d67f8a34e66f04452c2abb69 (diff) | |
parent | 958585f58f675a3c2855c7d91b6fdd2875552d0b (diff) |
Merge branch 'i2c-for-linus' of git://aeryn.fluff.org.uk/bjdooks/linux
* 'i2c-for-linus' of git://aeryn.fluff.org.uk/bjdooks/linux:
i2c: Blackfin I2C Driver: Functional power management support
i2c: Documentation: upgrading clients HOWTO
i2c: S3C24XX I2C frequency scaling support.
i2c: i2c_gpio: keep probe resident for hotplugged devices.
i2c: S3C2410: Pass the I2C bus number via drivers platform data
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/i2c/busses/i2c-bfin-twi.c | 35 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-gpio.c | 9 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-s3c2410.c | 129 |
3 files changed, 144 insertions, 29 deletions
diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c index 48d084bdf7c..3c855ff2992 100644 --- a/drivers/i2c/busses/i2c-bfin-twi.c +++ b/drivers/i2c/busses/i2c-bfin-twi.c @@ -49,6 +49,8 @@ struct bfin_twi_iface { struct i2c_msg *pmsg; int msg_num; int cur_msg; + u16 saved_clkdiv; + u16 saved_control; void __iomem *regs_base; }; @@ -565,32 +567,43 @@ static u32 bfin_twi_functionality(struct i2c_adapter *adap) I2C_FUNC_I2C; } - static struct i2c_algorithm bfin_twi_algorithm = { .master_xfer = bfin_twi_master_xfer, .smbus_xfer = bfin_twi_smbus_xfer, .functionality = bfin_twi_functionality, }; - -static int i2c_bfin_twi_suspend(struct platform_device *dev, pm_message_t state) +static int i2c_bfin_twi_suspend(struct platform_device *pdev, pm_message_t state) { - struct bfin_twi_iface *iface = platform_get_drvdata(dev); + struct bfin_twi_iface *iface = platform_get_drvdata(pdev); + + iface->saved_clkdiv = read_CLKDIV(iface); + iface->saved_control = read_CONTROL(iface); + + free_irq(iface->irq, iface); /* Disable TWI */ - write_CONTROL(iface, read_CONTROL(iface) & ~TWI_ENA); - SSYNC(); + write_CONTROL(iface, iface->saved_control & ~TWI_ENA); return 0; } -static int i2c_bfin_twi_resume(struct platform_device *dev) +static int i2c_bfin_twi_resume(struct platform_device *pdev) { - struct bfin_twi_iface *iface = platform_get_drvdata(dev); + struct bfin_twi_iface *iface = platform_get_drvdata(pdev); - /* Enable TWI */ - write_CONTROL(iface, read_CONTROL(iface) | TWI_ENA); - SSYNC(); + int rc = request_irq(iface->irq, bfin_twi_interrupt_entry, + IRQF_DISABLED, pdev->name, iface); + if (rc) { + dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq); + return -ENODEV; + } + + /* Resume TWI interface clock as specified */ + write_CLKDIV(iface, iface->saved_clkdiv); + + /* Resume TWI */ + write_CONTROL(iface, iface->saved_control); return 0; } diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index 79b455a1f09..32104eac8d3 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -77,7 +77,7 @@ static int i2c_gpio_getscl(void *data) return gpio_get_value(pdata->scl_pin); } -static int __init i2c_gpio_probe(struct platform_device *pdev) +static int __devinit i2c_gpio_probe(struct platform_device *pdev) { struct i2c_gpio_platform_data *pdata; struct i2c_algo_bit_data *bit_data; @@ -174,7 +174,7 @@ err_alloc_adap: return ret; } -static int __exit i2c_gpio_remove(struct platform_device *pdev) +static int __devexit i2c_gpio_remove(struct platform_device *pdev) { struct i2c_gpio_platform_data *pdata; struct i2c_adapter *adap; @@ -196,14 +196,15 @@ static struct platform_driver i2c_gpio_driver = { .name = "i2c-gpio", .owner = THIS_MODULE, }, - .remove = __exit_p(i2c_gpio_remove), + .probe = i2c_gpio_probe, + .remove = __devexit_p(i2c_gpio_remove), }; static int __init i2c_gpio_init(void) { int ret; - ret = platform_driver_probe(&i2c_gpio_driver, i2c_gpio_probe); + ret = platform_driver_register(&i2c_gpio_driver); if (ret) printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret); diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 007390ad981..4864723c742 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -33,6 +33,7 @@ #include <linux/err.h> #include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/cpufreq.h> #include <asm/hardware.h> #include <asm/irq.h> @@ -64,6 +65,7 @@ struct s3c24xx_i2c { 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 @@ -501,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); @@ -636,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) { @@ -688,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 @@ -719,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; } @@ -730,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 */ @@ -752,9 +831,12 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) static int s3c24xx_i2c_probe(struct platform_device *pdev) { 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 = &pdev->dev; @@ -832,17 +914,34 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) 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(&pdev->dev, "failed to add bus to i2c core\n"); + 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; + } + platform_set_drvdata(pdev, i2c); dev_info(&pdev->dev, "%s: S3C I2C adapter\n", i2c->adap.dev.bus_id); return 0; + err_cpufreq: + s3c24xx_i2c_deregister_cpufreq(i2c); + err_irq: free_irq(i2c->irq->start, i2c); @@ -870,6 +969,8 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev) { struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + s3c24xx_i2c_deregister_cpufreq(i2c); + i2c_del_adapter(&i2c->adap); free_irq(i2c->irq->start, i2c); |