diff options
Diffstat (limited to 'drivers/i2c/busses')
-rw-r--r-- | drivers/i2c/busses/Kconfig | 20 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-highlander.c | 498 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-parport-light.c | 39 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-pca-isa.c | 20 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-pxa.c | 94 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-viapro.c | 17 |
7 files changed, 634 insertions, 55 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 6ee997b2817..acadbc51fc0 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -55,7 +55,7 @@ config I2C_AMD756 config I2C_AMD756_S4882 tristate "SMBus multiplexing on the Tyan S4882" - depends on I2C_AMD756 && EXPERIMENTAL + depends on I2C_AMD756 && X86 && EXPERIMENTAL help Enabling this option will add specific SMBus support for the Tyan S4882 motherboard. On this 4-CPU board, the SMBus is multiplexed @@ -148,7 +148,7 @@ config I2C_NFORCE2 config I2C_NFORCE2_S4985 tristate "SMBus multiplexing on the Tyan S4985" - depends on I2C_NFORCE2 && EXPERIMENTAL + depends on I2C_NFORCE2 && X86 && EXPERIMENTAL help Enabling this option will add specific SMBus support for the Tyan S4985 motherboard. On this 4-CPU board, the SMBus is multiplexed @@ -209,7 +209,7 @@ config I2C_VIA will be called i2c-via. config I2C_VIAPRO - tristate "VIA VT82C596/82C686/82xx and CX700" + tristate "VIA VT82C596/82C686/82xx and CX700/VX800/VX820" depends on PCI help If you say yes to this option, support will be included for the VIA @@ -223,6 +223,8 @@ config I2C_VIAPRO VT8237R/A/S VT8251 CX700 + VX800 + VX820 This driver can also be built as a module. If so, the module will be called i2c-viapro. @@ -330,6 +332,18 @@ config I2C_GPIO This is a very simple bitbanging I2C driver utilizing the arch-neutral GPIO API to control the SCL and SDA lines. +config I2C_HIGHLANDER + tristate "Highlander FPGA SMBus interface" + depends on SH_HIGHLANDER + help + If you say yes to this option, support will be included for + the SMBus interface located in the FPGA on various Highlander + boards, particularly the R0P7780LC0011RL and R0P7785LC0011RL + FPGAs. This is wholly unrelated to the SoC I2C. + + This driver can also be built as a module. If so, the module + will be called i2c-highlander. + config I2C_IBM_IIC tristate "IBM PPC 4xx on-chip I2C interface" depends on 4xx diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 97dbfa2107f..0c2c4b26cdf 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o obj-$(CONFIG_I2C_CPM) += i2c-cpm.o obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o +obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o diff --git a/drivers/i2c/busses/i2c-highlander.c b/drivers/i2c/busses/i2c-highlander.c new file mode 100644 index 00000000000..f4d22ae9d29 --- /dev/null +++ b/drivers/i2c/busses/i2c-highlander.c @@ -0,0 +1,498 @@ +/* + * Renesas Solutions Highlander FPGA I2C/SMBus support. + * + * Supported devices: R0P7780LC0011RL, R0P7785LC0011RL + * + * Copyright (C) 2008 Paul Mundt + * Copyright (C) 2008 Renesas Solutions Corp. + * Copyright (C) 2008 Atom Create Engineering Co., Ltd. + * + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file "COPYING" in the main directory + * of this archive for more details. + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/completion.h> +#include <linux/io.h> +#include <linux/delay.h> + +#define SMCR 0x00 +#define SMCR_START (1 << 0) +#define SMCR_IRIC (1 << 1) +#define SMCR_BBSY (1 << 2) +#define SMCR_ACKE (1 << 3) +#define SMCR_RST (1 << 4) +#define SMCR_IEIC (1 << 6) + +#define SMSMADR 0x02 + +#define SMMR 0x04 +#define SMMR_MODE0 (1 << 0) +#define SMMR_MODE1 (1 << 1) +#define SMMR_CAP (1 << 3) +#define SMMR_TMMD (1 << 4) +#define SMMR_SP (1 << 7) + +#define SMSADR 0x06 +#define SMTRDR 0x46 + +struct highlander_i2c_dev { + struct device *dev; + void __iomem *base; + struct i2c_adapter adapter; + struct completion cmd_complete; + unsigned long last_read_time; + int irq; + u8 *buf; + size_t buf_len; +}; + +static int iic_force_poll, iic_force_normal; +static int iic_timeout = 1000, iic_read_delay; + +static inline void highlander_i2c_irq_enable(struct highlander_i2c_dev *dev) +{ + iowrite16(ioread16(dev->base + SMCR) | SMCR_IEIC, dev->base + SMCR); +} + +static inline void highlander_i2c_irq_disable(struct highlander_i2c_dev *dev) +{ + iowrite16(ioread16(dev->base + SMCR) & ~SMCR_IEIC, dev->base + SMCR); +} + +static inline void highlander_i2c_start(struct highlander_i2c_dev *dev) +{ + iowrite16(ioread16(dev->base + SMCR) | SMCR_START, dev->base + SMCR); +} + +static inline void highlander_i2c_done(struct highlander_i2c_dev *dev) +{ + iowrite16(ioread16(dev->base + SMCR) | SMCR_IRIC, dev->base + SMCR); +} + +static void highlander_i2c_setup(struct highlander_i2c_dev *dev) +{ + u16 smmr; + + smmr = ioread16(dev->base + SMMR); + smmr |= SMMR_TMMD; + + if (iic_force_normal) + smmr &= ~SMMR_SP; + else + smmr |= SMMR_SP; + + iowrite16(smmr, dev->base + SMMR); +} + +static void smbus_write_data(u8 *src, u16 *dst, int len) +{ + for (; len > 1; len -= 2) { + *dst++ = be16_to_cpup((u16 *)src); + src += 2; + } + + if (len) + *dst = *src << 8; +} + +static void smbus_read_data(u16 *src, u8 *dst, int len) +{ + for (; len > 1; len -= 2) { + *(u16 *)dst = cpu_to_be16p(src++); + dst += 2; + } + + if (len) + *dst = *src >> 8; +} + +static void highlander_i2c_command(struct highlander_i2c_dev *dev, + u8 command, int len) +{ + unsigned int i; + u16 cmd = (command << 8) | command; + + for (i = 0; i < len; i += 2) { + if (len - i == 1) + cmd = command << 8; + iowrite16(cmd, dev->base + SMSADR + i); + dev_dbg(dev->dev, "command data[%x] 0x%04x\n", i/2, cmd); + } +} + +static int highlander_i2c_wait_for_bbsy(struct highlander_i2c_dev *dev) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(iic_timeout); + while (ioread16(dev->base + SMCR) & SMCR_BBSY) { + if (time_after(jiffies, timeout)) { + dev_warn(dev->dev, "timeout waiting for bus ready\n"); + return -ETIMEDOUT; + } + + msleep(1); + } + + return 0; +} + +static int highlander_i2c_reset(struct highlander_i2c_dev *dev) +{ + iowrite16(ioread16(dev->base + SMCR) | SMCR_RST, dev->base + SMCR); + return highlander_i2c_wait_for_bbsy(dev); +} + +static int highlander_i2c_wait_for_ack(struct highlander_i2c_dev *dev) +{ + u16 tmp = ioread16(dev->base + SMCR); + + if ((tmp & (SMCR_IRIC | SMCR_ACKE)) == SMCR_ACKE) { + dev_warn(dev->dev, "ack abnormality\n"); + return highlander_i2c_reset(dev); + } + + return 0; +} + +static irqreturn_t highlander_i2c_irq(int irq, void *dev_id) +{ + struct highlander_i2c_dev *dev = dev_id; + + highlander_i2c_done(dev); + complete(&dev->cmd_complete); + + return IRQ_HANDLED; +} + +static void highlander_i2c_poll(struct highlander_i2c_dev *dev) +{ + unsigned long timeout; + u16 smcr; + + timeout = jiffies + msecs_to_jiffies(iic_timeout); + for (;;) { + smcr = ioread16(dev->base + SMCR); + + /* + * Don't bother checking ACKE here, this and the reset + * are handled in highlander_i2c_wait_xfer_done() when + * waiting for the ACK. + */ + + if (smcr & SMCR_IRIC) + return; + if (time_after(jiffies, timeout)) + break; + + cpu_relax(); + cond_resched(); + } + + dev_err(dev->dev, "polling timed out\n"); +} + +static inline int highlander_i2c_wait_xfer_done(struct highlander_i2c_dev *dev) +{ + if (dev->irq) + wait_for_completion_timeout(&dev->cmd_complete, + msecs_to_jiffies(iic_timeout)); + else + /* busy looping, the IRQ of champions */ + highlander_i2c_poll(dev); + + return highlander_i2c_wait_for_ack(dev); +} + +static int highlander_i2c_read(struct highlander_i2c_dev *dev) +{ + int i, cnt; + u16 data[16]; + + if (highlander_i2c_wait_for_bbsy(dev)) + return -EAGAIN; + + highlander_i2c_start(dev); + + if (highlander_i2c_wait_xfer_done(dev)) { + dev_err(dev->dev, "Arbitration loss\n"); + return -EAGAIN; + } + + /* + * The R0P7780LC0011RL FPGA needs a significant delay between + * data read cycles, otherwise the transciever gets confused and + * garbage is returned when the read is subsequently aborted. + * + * It is not sufficient to wait for BBSY. + * + * While this generally only applies to the older SH7780-based + * Highlanders, the same issue can be observed on SH7785 ones, + * albeit less frequently. SH7780-based Highlanders may need + * this to be as high as 1000 ms. + */ + if (iic_read_delay && time_before(jiffies, dev->last_read_time + + msecs_to_jiffies(iic_read_delay))) + msleep(jiffies_to_msecs((dev->last_read_time + + msecs_to_jiffies(iic_read_delay)) - jiffies)); + + cnt = (dev->buf_len + 1) >> 1; + for (i = 0; i < cnt; i++) { + data[i] = ioread16(dev->base + SMTRDR + (i * sizeof(u16))); + dev_dbg(dev->dev, "read data[%x] 0x%04x\n", i, data[i]); + } + + smbus_read_data(data, dev->buf, dev->buf_len); + + dev->last_read_time = jiffies; + + return 0; +} + +static int highlander_i2c_write(struct highlander_i2c_dev *dev) +{ + int i, cnt; + u16 data[16]; + + smbus_write_data(dev->buf, data, dev->buf_len); + + cnt = (dev->buf_len + 1) >> 1; + for (i = 0; i < cnt; i++) { + iowrite16(data[i], dev->base + SMTRDR + (i * sizeof(u16))); + dev_dbg(dev->dev, "write data[%x] 0x%04x\n", i, data[i]); + } + + if (highlander_i2c_wait_for_bbsy(dev)) + return -EAGAIN; + + highlander_i2c_start(dev); + + return highlander_i2c_wait_xfer_done(dev); +} + +static int highlander_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data) +{ + struct highlander_i2c_dev *dev = i2c_get_adapdata(adap); + int read = read_write & I2C_SMBUS_READ; + u16 tmp; + + init_completion(&dev->cmd_complete); + + dev_dbg(dev->dev, "addr %04x, command %02x, read_write %d, size %d\n", + addr, command, read_write, size); + + /* + * Set up the buffer and transfer size + */ + switch (size) { + case I2C_SMBUS_BYTE_DATA: + dev->buf = &data->byte; + dev->buf_len = 1; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + dev->buf = &data->block[1]; + dev->buf_len = data->block[0]; + break; + default: + dev_err(dev->dev, "unsupported command %d\n", size); + return -EINVAL; + } + + /* + * Encode the mode setting + */ + tmp = ioread16(dev->base + SMMR); + tmp &= ~(SMMR_MODE0 | SMMR_MODE1); + + switch (dev->buf_len) { + case 1: + /* default */ + break; + case 8: + tmp |= SMMR_MODE0; + break; + case 16: + tmp |= SMMR_MODE1; + break; + case 32: + tmp |= (SMMR_MODE0 | SMMR_MODE1); + break; + default: + dev_err(dev->dev, "unsupported xfer size %d\n", dev->buf_len); + return -EINVAL; + } + + iowrite16(tmp, dev->base + SMMR); + + /* Ensure we're in a sane state */ + highlander_i2c_done(dev); + + /* Set slave address */ + iowrite16((addr << 1) | read, dev->base + SMSMADR); + + highlander_i2c_command(dev, command, dev->buf_len); + + if (read) + return highlander_i2c_read(dev); + else + return highlander_i2c_write(dev); +} + +static u32 highlander_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static const struct i2c_algorithm highlander_i2c_algo = { + .smbus_xfer = highlander_i2c_smbus_xfer, + .functionality = highlander_i2c_func, +}; + +static int __devinit highlander_i2c_probe(struct platform_device *pdev) +{ + struct highlander_i2c_dev *dev; + struct i2c_adapter *adap; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!res)) { + dev_err(&pdev->dev, "no mem resource\n"); + return -ENODEV; + } + + dev = kzalloc(sizeof(struct highlander_i2c_dev), GFP_KERNEL); + if (unlikely(!dev)) + return -ENOMEM; + + dev->base = ioremap_nocache(res->start, res->end - res->start + 1); + if (unlikely(!dev->base)) { + ret = -ENXIO; + goto err; + } + + dev->dev = &pdev->dev; + platform_set_drvdata(pdev, dev); + + dev->irq = platform_get_irq(pdev, 0); + if (iic_force_poll) + dev->irq = 0; + + if (dev->irq) { + ret = request_irq(dev->irq, highlander_i2c_irq, IRQF_DISABLED, + pdev->name, dev); + if (unlikely(ret)) + goto err_unmap; + + highlander_i2c_irq_enable(dev); + } else { + dev_notice(&pdev->dev, "no IRQ, using polling mode\n"); + highlander_i2c_irq_disable(dev); + } + + dev->last_read_time = jiffies; /* initial read jiffies */ + + highlander_i2c_setup(dev); + + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; + strlcpy(adap->name, "HL FPGA I2C adapter", sizeof(adap->name)); + adap->algo = &highlander_i2c_algo; + adap->dev.parent = &pdev->dev; + adap->nr = pdev->id; + + /* + * Reset the adapter + */ + ret = highlander_i2c_reset(dev); + if (unlikely(ret)) { + dev_err(&pdev->dev, "controller didn't come up\n"); + goto err_free_irq; + } + + ret = i2c_add_numbered_adapter(adap); + if (unlikely(ret)) { + dev_err(&pdev->dev, "failure adding adapter\n"); + goto err_free_irq; + } + + return 0; + +err_free_irq: + if (dev->irq) + free_irq(dev->irq, dev); +err_unmap: + iounmap(dev->base); +err: + kfree(dev); + + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static int __devexit highlander_i2c_remove(struct platform_device *pdev) +{ + struct highlander_i2c_dev *dev = platform_get_drvdata(pdev); + + i2c_del_adapter(&dev->adapter); + + if (dev->irq) + free_irq(dev->irq, dev); + + iounmap(dev->base); + kfree(dev); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver highlander_i2c_driver = { + .driver = { + .name = "i2c-highlander", + .owner = THIS_MODULE, + }, + + .probe = highlander_i2c_probe, + .remove = __devexit_p(highlander_i2c_remove), +}; + +static int __init highlander_i2c_init(void) +{ + return platform_driver_register(&highlander_i2c_driver); +} + +static void __exit highlander_i2c_exit(void) +{ + platform_driver_unregister(&highlander_i2c_driver); +} + +module_init(highlander_i2c_init); +module_exit(highlander_i2c_exit); + +MODULE_AUTHOR("Paul Mundt"); +MODULE_DESCRIPTION("Renesas Highlander FPGA I2C/SMBus adapter"); +MODULE_LICENSE("GPL v2"); + +module_param(iic_force_poll, bool, 0); +module_param(iic_force_normal, bool, 0); +module_param(iic_timeout, int, 0); +module_param(iic_read_delay, int, 0); + +MODULE_PARM_DESC(iic_force_poll, "Force polling mode"); +MODULE_PARM_DESC(iic_force_normal, + "Force normal mode (100 kHz), default is fast mode (400 kHz)"); +MODULE_PARM_DESC(iic_timeout, "Set timeout value in msecs (default 1000 ms)"); +MODULE_PARM_DESC(iic_read_delay, + "Delay between data read cycles (default 0 ms)"); diff --git a/drivers/i2c/busses/i2c-parport-light.c b/drivers/i2c/busses/i2c-parport-light.c index c6faf9bdad1..b2b8380f660 100644 --- a/drivers/i2c/busses/i2c-parport-light.c +++ b/drivers/i2c/busses/i2c-parport-light.c @@ -123,11 +123,6 @@ static struct i2c_adapter parport_adapter = { static int __devinit i2c_parport_probe(struct platform_device *pdev) { int err; - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start, res->end - res->start + 1, DRVNAME)) - return -EBUSY; /* Reset hardware to a sane state (SCL and SDA high) */ parport_setsda(NULL, 1); @@ -138,29 +133,19 @@ static int __devinit i2c_parport_probe(struct platform_device *pdev) parport_adapter.dev.parent = &pdev->dev; err = i2c_bit_add_bus(&parport_adapter); - if (err) { + if (err) dev_err(&pdev->dev, "Unable to register with I2C\n"); - goto exit_region; - } - return 0; - -exit_region: - release_region(res->start, res->end - res->start + 1); return err; } static int __devexit i2c_parport_remove(struct platform_device *pdev) { - struct resource *res; - i2c_del_adapter(&parport_adapter); /* Un-init if needed (power off...) */ if (adapter_parm[type].init.val) line_set(0, &adapter_parm[type].init); - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - release_region(res->start, res->end - res->start + 1); return 0; } @@ -175,12 +160,6 @@ static struct platform_driver i2c_parport_driver = { static int __init i2c_parport_device_add(u16 address) { - struct resource res = { - .start = address, - .end = address + 2, - .name = DRVNAME, - .flags = IORESOURCE_IO, - }; int err; pdev = platform_device_alloc(DRVNAME, -1); @@ -190,13 +169,6 @@ static int __init i2c_parport_device_add(u16 address) goto exit; } - err = platform_device_add_resources(pdev, &res, 1); - if (err) { - printk(KERN_ERR DRVNAME ": Device resource addition failed " - "(%d)\n", err); - goto exit_device_put; - } - err = platform_device_add(pdev); if (err) { printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", @@ -231,13 +203,16 @@ static int __init i2c_parport_init(void) base = DEFAULT_BASE; } + if (!request_region(base, 3, DRVNAME)) + return -EBUSY; + if (!adapter_parm[type].getscl.val) parport_algo_data.getscl = NULL; /* Sets global pdev as a side effect */ err = i2c_parport_device_add(base); if (err) - goto exit; + goto exit_release; err = platform_driver_register(&i2c_parport_driver); if (err) @@ -247,7 +222,8 @@ static int __init i2c_parport_init(void) exit_device: platform_device_unregister(pdev); -exit: +exit_release: + release_region(base, 3); return err; } @@ -255,6 +231,7 @@ static void __exit i2c_parport_exit(void) { platform_driver_unregister(&i2c_parport_driver); platform_device_unregister(pdev); + release_region(base, 3); } MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); diff --git a/drivers/i2c/busses/i2c-pca-isa.c b/drivers/i2c/busses/i2c-pca-isa.c index a119784bae1..f80df9ae505 100644 --- a/drivers/i2c/busses/i2c-pca-isa.c +++ b/drivers/i2c/busses/i2c-pca-isa.c @@ -36,8 +36,8 @@ #define DRIVER "i2c-pca-isa" #define IO_SIZE 4 -static unsigned long base = 0x330; -static int irq = 10; +static unsigned long base; +static int irq = -1; /* Data sheet recommends 59kHz for 100kHz operation due to variation * in the actual clock rate */ @@ -107,6 +107,19 @@ static struct i2c_adapter pca_isa_ops = { .timeout = 100, }; +static int __devinit pca_isa_match(struct device *dev, unsigned int id) +{ + int match = base != 0; + + if (match) { + if (irq <= -1) + dev_warn(dev, "Using polling mode (specify irq)\n"); + } else + dev_err(dev, "Please specify I/O base\n"); + + return match; +} + static int __devinit pca_isa_probe(struct device *dev, unsigned int id) { init_waitqueue_head(&pca_wait); @@ -153,7 +166,7 @@ static int __devexit pca_isa_remove(struct device *dev, unsigned int id) { i2c_del_adapter(&pca_isa_ops); - if (irq > 0) { + if (irq > -1) { disable_irq(irq); free_irq(irq, &pca_isa_ops); } @@ -163,6 +176,7 @@ static int __devexit pca_isa_remove(struct device *dev, unsigned int id) } static struct isa_driver pca_isa_driver = { + .match = pca_isa_match, .probe = pca_isa_probe, .remove = __devexit_p(pca_isa_remove), .driver = { diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 44d838410f1..906f9b9d715 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -38,7 +38,44 @@ #include <asm/irq.h> #include <asm/io.h> #include <mach/i2c.h> -#include <mach/pxa-regs.h> + +/* + * I2C registers and bit definitions + */ +#define IBMR (0x00) +#define IDBR (0x08) +#define ICR (0x10) +#define ISR (0x18) +#define ISAR (0x20) + +#define ICR_START (1 << 0) /* start bit */ +#define ICR_STOP (1 << 1) /* stop bit */ +#define ICR_ACKNAK (1 << 2) /* send ACK(0) or NAK(1) */ +#define ICR_TB (1 << 3) /* transfer byte bit */ +#define ICR_MA (1 << 4) /* master abort */ +#define ICR_SCLE (1 << 5) /* master clock enable */ +#define ICR_IUE (1 << 6) /* unit enable */ +#define ICR_GCD (1 << 7) /* general call disable */ +#define ICR_ITEIE (1 << 8) /* enable tx interrupts */ +#define ICR_IRFIE (1 << 9) /* enable rx interrupts */ +#define ICR_BEIE (1 << 10) /* enable bus error ints */ +#define ICR_SSDIE (1 << 11) /* slave STOP detected int enable */ +#define ICR_ALDIE (1 << 12) /* enable arbitration interrupt */ +#define ICR_SADIE (1 << 13) /* slave address detected int enable */ +#define ICR_UR (1 << 14) /* unit reset */ +#define ICR_FM (1 << 15) /* fast mode */ + +#define ISR_RWM (1 << 0) /* read/write mode */ +#define ISR_ACKNAK (1 << 1) /* ack/nak status */ +#define ISR_UB (1 << 2) /* unit busy */ +#define ISR_IBB (1 << 3) /* bus busy */ +#define ISR_SSD (1 << 4) /* slave stop detected */ +#define ISR_ALD (1 << 5) /* arbitration loss detected */ +#define ISR_ITE (1 << 6) /* tx buffer empty */ +#define ISR_IRF (1 << 7) /* rx buffer full */ +#define ISR_GCAD (1 << 8) /* general call address detected */ +#define ISR_SAD (1 << 9) /* slave address detected */ +#define ISR_BED (1 << 10) /* bus error no ACK/NAK */ struct pxa_i2c { spinlock_t lock; @@ -60,19 +97,21 @@ struct pxa_i2c { u32 icrlog[32]; void __iomem *reg_base; + unsigned int reg_shift; unsigned long iobase; unsigned long iosize; int irq; - int use_pio; + unsigned int use_pio :1; + unsigned int fast_mode :1; }; -#define _IBMR(i2c) ((i2c)->reg_base + 0) -#define _IDBR(i2c) ((i2c)->reg_base + 8) -#define _ICR(i2c) ((i2c)->reg_base + 0x10) -#define _ISR(i2c) ((i2c)->reg_base + 0x18) -#define _ISAR(i2c) ((i2c)->reg_base + 0x20) +#define _IBMR(i2c) ((i2c)->reg_base + (0x0 << (i2c)->reg_shift)) +#define _IDBR(i2c) ((i2c)->reg_base + (0x4 << (i2c)->reg_shift)) +#define _ICR(i2c) ((i2c)->reg_base + (0x8 << (i2c)->reg_shift)) +#define _ISR(i2c) ((i2c)->reg_base + (0xc << (i2c)->reg_shift)) +#define _ISAR(i2c) ((i2c)->reg_base + (0x10 << (i2c)->reg_shift)) /* * I2C Slave mode address @@ -188,14 +227,14 @@ static inline int i2c_pxa_is_slavemode(struct pxa_i2c *i2c) static void i2c_pxa_abort(struct pxa_i2c *i2c) { - unsigned long timeout = jiffies + HZ/4; + int i = 250; if (i2c_pxa_is_slavemode(i2c)) { dev_dbg(&i2c->adap.dev, "%s: called in slave mode\n", __func__); return; } - while (time_before(jiffies, timeout) && (readl(_IBMR(i2c)) & 0x1) == 0) { + while ((i > 0) && (readl(_IBMR(i2c)) & 0x1) == 0) { unsigned long icr = readl(_ICR(i2c)); icr &= ~ICR_START; @@ -205,7 +244,8 @@ static void i2c_pxa_abort(struct pxa_i2c *i2c) show_state(i2c); - msleep(1); + mdelay(1); + i --; } writel(readl(_ICR(i2c)) & ~(ICR_MA | ICR_START | ICR_STOP), @@ -364,7 +404,7 @@ static void i2c_pxa_reset(struct pxa_i2c *i2c) writel(i2c->slave_addr, _ISAR(i2c)); /* set control register values */ - writel(I2C_ICR_INIT, _ICR(i2c)); + writel(I2C_ICR_INIT | (i2c->fast_mode ? ICR_FM : 0), _ICR(i2c)); #ifdef CONFIG_I2C_PXA_SLAVE dev_info(&i2c->adap.dev, "Enabling slave mode\n"); @@ -907,12 +947,6 @@ static int i2c_pxa_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num struct pxa_i2c *i2c = adap->algo_data; int ret, i; - /* If the I2C controller is disabled we need to reset it (probably due - to a suspend/resume destroying state). We do this here as we can then - avoid worrying about resuming the controller before its users. */ - if (!(readl(_ICR(i2c)) & ICR_IUE)) - i2c_pxa_reset(i2c); - for (i = adap->retries; i >= 0; i--) { ret = i2c_pxa_do_xfer(i2c, msgs, num); if (ret != I2C_RETRY) @@ -993,6 +1027,7 @@ static int i2c_pxa_probe(struct platform_device *dev) ret = -EIO; goto eremap; } + i2c->reg_shift = (cpu_is_pxa3xx() && (dev->id == 1)) ? 0 : 1; i2c->iobase = res->start; i2c->iosize = res_len(res); @@ -1013,6 +1048,7 @@ static int i2c_pxa_probe(struct platform_device *dev) if (plat) { i2c->adap.class = plat->class; i2c->use_pio = plat->use_pio; + i2c->fast_mode = plat->fast_mode; } if (i2c->use_pio) { @@ -1082,9 +1118,33 @@ static int __exit i2c_pxa_remove(struct platform_device *dev) return 0; } +#ifdef CONFIG_PM +static int i2c_pxa_suspend_late(struct platform_device *dev, pm_message_t state) +{ + struct pxa_i2c *i2c = platform_get_drvdata(dev); + clk_disable(i2c->clk); + return 0; +} + +static int i2c_pxa_resume_early(struct platform_device *dev) +{ + struct pxa_i2c *i2c = platform_get_drvdata(dev); + + clk_enable(i2c->clk); + i2c_pxa_reset(i2c); + + return 0; +} +#else +#define i2c_pxa_suspend_late NULL +#define i2c_pxa_resume_early NULL +#endif + static struct platform_driver i2c_pxa_driver = { .probe = i2c_pxa_probe, .remove = __exit_p(i2c_pxa_remove), + .suspend_late = i2c_pxa_suspend_late, + .resume_early = i2c_pxa_resume_early, .driver = { .name = "pxa2xx-i2c", .owner = THIS_MODULE, diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c index 862eb352a2d..73dc52e114e 100644 --- a/drivers/i2c/busses/i2c-viapro.c +++ b/drivers/i2c/busses/i2c-viapro.c @@ -36,6 +36,7 @@ VT8237S 0x3372 yes VT8251 0x3287 yes CX700 0x8324 yes + VX800/VX820 0x8353 yes Note: we assume there can only be one device, with one SMBus interface. */ @@ -82,6 +83,7 @@ static unsigned short SMBHSTCFG = 0xD2; #define VT596_BYTE 0x04 #define VT596_BYTE_DATA 0x08 #define VT596_WORD_DATA 0x0C +#define VT596_PROC_CALL 0x10 #define VT596_BLOCK_DATA 0x14 #define VT596_I2C_BLOCK_DATA 0x34 @@ -232,6 +234,12 @@ static s32 vt596_access(struct i2c_adapter *adap, u16 addr, } size = VT596_WORD_DATA; break; + case I2C_SMBUS_PROC_CALL: + outb_p(command, SMBHSTCMD); + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + size = VT596_PROC_CALL; + break; case I2C_SMBUS_I2C_BLOCK_DATA: if (!(vt596_features & FEATURE_I2CBLOCK)) goto exit_unsupported; @@ -262,6 +270,9 @@ static s32 vt596_access(struct i2c_adapter *adap, u16 addr, if (status) return status; + if (size == VT596_PROC_CALL) + read_write = I2C_SMBUS_READ; + if ((read_write == I2C_SMBUS_WRITE) || (size == VT596_QUICK)) return 0; @@ -271,6 +282,7 @@ static s32 vt596_access(struct i2c_adapter *adap, u16 addr, data->byte = inb_p(SMBHSTDAT0); break; case VT596_WORD_DATA: + case VT596_PROC_CALL: data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); break; case VT596_I2C_BLOCK_DATA: @@ -295,7 +307,7 @@ static u32 vt596_func(struct i2c_adapter *adapter) { u32 func = I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | - I2C_FUNC_SMBUS_BLOCK_DATA; + I2C_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_DATA; if (vt596_features & FEATURE_I2CBLOCK) func |= I2C_FUNC_SMBUS_I2C_BLOCK; @@ -396,6 +408,7 @@ found: switch (pdev->device) { case PCI_DEVICE_ID_VIA_CX700: + case PCI_DEVICE_ID_VIA_VX800: case PCI_DEVICE_ID_VIA_8251: case PCI_DEVICE_ID_VIA_8237: case PCI_DEVICE_ID_VIA_8237A: @@ -459,6 +472,8 @@ static struct pci_device_id vt596_ids[] = { .driver_data = SMBBA3 }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_CX700), .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX800), + .driver_data = SMBBA3 }, { 0, } }; |