diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-eg20t.c')
| -rw-r--r-- | drivers/i2c/busses/i2c-eg20t.c | 381 |
1 files changed, 191 insertions, 190 deletions
diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index 878a12026af..a44ea13d143 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 OKI SEMICONDUCTOR CO., LTD. + * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,7 +18,6 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/delay.h> -#include <linux/init.h> #include <linux/errno.h> #include <linux/i2c.h> #include <linux/fs.h> @@ -64,6 +63,7 @@ #define TEN_BIT_ADDR_DEFAULT 0xF000 #define TEN_BIT_ADDR_MASK 0xF0 #define PCH_START 0x0020 +#define PCH_RESTART 0x0004 #define PCH_ESR_START 0x0001 #define PCH_BUFF_START 0x1 #define PCH_REPSTART 0x0004 @@ -135,7 +135,8 @@ /* Set the number of I2C instance max Intel EG20T PCH : 1ch -OKI SEMICONDUCTOR ML7213 IOH : 2ch +LAPIS Semiconductor ML7213 IOH : 2ch +LAPIS Semiconductor ML7831 IOH : 1ch */ #define PCH_I2C_MAX_DEV 2 @@ -179,13 +180,17 @@ static int pch_clk = 50000; /* specifies I2C clock speed in KHz */ static wait_queue_head_t pch_event; static DEFINE_MUTEX(pch_mutex); -/* Definition for ML7213 by OKI SEMICONDUCTOR */ +/* Definition for ML7213 by LAPIS Semiconductor */ #define PCI_VENDOR_ID_ROHM 0x10DB #define PCI_DEVICE_ID_ML7213_I2C 0x802D +#define PCI_DEVICE_ID_ML7223_I2C 0x8010 +#define PCI_DEVICE_ID_ML7831_I2C 0x8817 -static struct pci_device_id __devinitdata pch_pcidev_id[] = { +static const struct pci_device_id pch_pcidev_id[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_I2C), 1, }, { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_I2C), 2, }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_I2C), 1, }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_I2C), 1, }, {0,} }; @@ -240,7 +245,7 @@ static void pch_i2c_init(struct i2c_algo_pch_data *adap) if (pch_clk > PCH_MAX_CLK) pch_clk = 62500; - pch_i2cbc = (pch_clk + (pch_i2c_speed * 4)) / pch_i2c_speed * 8; + pch_i2cbc = (pch_clk + (pch_i2c_speed * 4)) / (pch_i2c_speed * 8); /* Set transfer speed in I2CBC */ iowrite32(pch_i2cbc, p + PCH_I2CBC); @@ -257,34 +262,36 @@ static void pch_i2c_init(struct i2c_algo_pch_data *adap) init_waitqueue_head(&pch_event); } -static inline bool ktime_lt(const ktime_t cmp1, const ktime_t cmp2) -{ - return cmp1.tv64 < cmp2.tv64; -} - /** * pch_i2c_wait_for_bus_idle() - check the status of bus. * @adap: Pointer to struct i2c_algo_pch_data. - * @timeout: waiting time counter (us). + * @timeout: waiting time counter (ms). */ static s32 pch_i2c_wait_for_bus_idle(struct i2c_algo_pch_data *adap, s32 timeout) { void __iomem *p = adap->pch_base_address; + int schedule = 0; + unsigned long end = jiffies + msecs_to_jiffies(timeout); + + while (ioread32(p + PCH_I2CSR) & I2CMBB_BIT) { + if (time_after(jiffies, end)) { + pch_dbg(adap, "I2CSR = %x\n", ioread32(p + PCH_I2CSR)); + pch_err(adap, "%s: Timeout Error.return%d\n", + __func__, -ETIME); + pch_i2c_init(adap); - /* MAX timeout value is timeout*1000*1000nsec */ - ktime_t ns_val = ktime_add_ns(ktime_get(), timeout*1000*1000); - do { - if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0) - break; - msleep(20); - } while (ktime_lt(ktime_get(), ns_val)); + return -ETIME; + } - pch_dbg(adap, "I2CSR = %x\n", ioread32(p + PCH_I2CSR)); + if (!schedule) + /* Retry after some usecs */ + udelay(5); + else + /* Wait a bit more without consuming CPU */ + usleep_range(20, 1000); - if (timeout == 0) { - pch_err(adap, "%s: Timeout Error.return%d\n", __func__, -ETIME); - return -ETIME; + schedule = 1; } return 0; @@ -304,65 +311,52 @@ static void pch_i2c_start(struct i2c_algo_pch_data *adap) } /** - * pch_i2c_wait_for_xfer_complete() - initiates a wait for the tx complete event + * pch_i2c_stop() - generate stop condition in normal mode. * @adap: Pointer to struct i2c_algo_pch_data. */ -static s32 pch_i2c_wait_for_xfer_complete(struct i2c_algo_pch_data *adap) +static void pch_i2c_stop(struct i2c_algo_pch_data *adap) { - s32 ret; - ret = wait_event_timeout(pch_event, - (adap->pch_event_flag != 0), msecs_to_jiffies(50)); - if (ret < 0) { - pch_err(adap, "timeout: %x\n", adap->pch_event_flag); - return ret; - } + void __iomem *p = adap->pch_base_address; + pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); + /* clear the start bit */ + pch_clrbit(adap->pch_base_address, PCH_I2CCTL, PCH_START); +} + +static int pch_i2c_wait_for_check_xfer(struct i2c_algo_pch_data *adap) +{ + long ret; + void __iomem *p = adap->pch_base_address; - if (ret == 0) { - pch_err(adap, "timeout: %x\n", adap->pch_event_flag); + ret = wait_event_timeout(pch_event, + (adap->pch_event_flag != 0), msecs_to_jiffies(1000)); + if (!ret) { + pch_err(adap, "%s:wait-event timeout\n", __func__); + adap->pch_event_flag = 0; + pch_i2c_stop(adap); + pch_i2c_init(adap); return -ETIMEDOUT; } if (adap->pch_event_flag & I2C_ERROR_MASK) { - pch_err(adap, "error bits set: %x\n", adap->pch_event_flag); - return -EIO; + pch_err(adap, "Lost Arbitration\n"); + adap->pch_event_flag = 0; + pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMAL_BIT); + pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMIF_BIT); + pch_i2c_init(adap); + return -EAGAIN; } adap->pch_event_flag = 0; - return 0; -} - -/** - * pch_i2c_getack() - to confirm ACK/NACK - * @adap: Pointer to struct i2c_algo_pch_data. - */ -static s32 pch_i2c_getack(struct i2c_algo_pch_data *adap) -{ - u32 reg_val; - void __iomem *p = adap->pch_base_address; - reg_val = ioread32(p + PCH_I2CSR) & PCH_GETACK; - - if (reg_val != 0) { - pch_err(adap, "return%d\n", -EPROTO); - return -EPROTO; + if (ioread32(p + PCH_I2CSR) & PCH_GETACK) { + pch_dbg(adap, "Receive NACK for slave address setting\n"); + return -ENXIO; } return 0; } /** - * pch_i2c_stop() - generate stop condition in normal mode. - * @adap: Pointer to struct i2c_algo_pch_data. - */ -static void pch_i2c_stop(struct i2c_algo_pch_data *adap) -{ - void __iomem *p = adap->pch_base_address; - pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); - /* clear the start bit */ - pch_clrbit(adap->pch_base_address, PCH_I2CCTL, PCH_START); -} - -/** * pch_i2c_repstart() - generate repeated start condition in normal mode * @adap: Pointer to struct i2c_algo_pch_data. */ @@ -392,6 +386,7 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap, u32 addr_2_msb; u32 addr_8_lsb; s32 wrcount; + s32 rtn; void __iomem *p = adap->pch_base_address; length = msgs->len; @@ -410,18 +405,17 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap, } if (msgs->flags & I2C_M_TEN) { - addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7); + addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7) & 0x06; iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR); if (first) pch_i2c_start(adap); - if (pch_i2c_wait_for_xfer_complete(adap) == 0 && - pch_i2c_getack(adap) == 0) { - addr_8_lsb = (addr & I2C_ADDR_MSK); - iowrite32(addr_8_lsb, p + PCH_I2CDR); - } else { - pch_i2c_stop(adap); - return -ETIME; - } + + rtn = pch_i2c_wait_for_check_xfer(adap); + if (rtn) + return rtn; + + addr_8_lsb = (addr & I2C_ADDR_MSK); + iowrite32(addr_8_lsb, p + PCH_I2CDR); } else { /* set 7 bit slave address and R/W bit as 0 */ iowrite32(addr << 1, p + PCH_I2CDR); @@ -429,31 +423,29 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap, pch_i2c_start(adap); } - if ((pch_i2c_wait_for_xfer_complete(adap) == 0) && - (pch_i2c_getack(adap) == 0)) { - for (wrcount = 0; wrcount < length; ++wrcount) { - /* write buffer value to I2C data register */ - iowrite32(buf[wrcount], p + PCH_I2CDR); - pch_dbg(adap, "writing %x to Data register\n", - buf[wrcount]); + rtn = pch_i2c_wait_for_check_xfer(adap); + if (rtn) + return rtn; - if (pch_i2c_wait_for_xfer_complete(adap) != 0) - return -ETIME; + for (wrcount = 0; wrcount < length; ++wrcount) { + /* write buffer value to I2C data register */ + iowrite32(buf[wrcount], p + PCH_I2CDR); + pch_dbg(adap, "writing %x to Data register\n", buf[wrcount]); - if (pch_i2c_getack(adap)) - return -EIO; - } + rtn = pch_i2c_wait_for_check_xfer(adap); + if (rtn) + return rtn; - /* check if this is the last message */ - if (last) - pch_i2c_stop(adap); - else - pch_i2c_repstart(adap); - } else { - pch_i2c_stop(adap); - return -EIO; + pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMCF_BIT); + pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMIF_BIT); } + /* check if this is the last message */ + if (last) + pch_i2c_stop(adap); + else + pch_i2c_repstart(adap); + pch_dbg(adap, "return=%d\n", wrcount); return wrcount; @@ -482,6 +474,19 @@ static void pch_i2c_sendnack(struct i2c_algo_pch_data *adap) } /** + * pch_i2c_restart() - Generate I2C restart condition in normal mode. + * @adap: Pointer to struct i2c_algo_pch_data. + * + * Generate I2C restart condition in normal mode by setting I2CCTL.I2CRSTA. + */ +static void pch_i2c_restart(struct i2c_algo_pch_data *adap) +{ + void __iomem *p = adap->pch_base_address; + pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); + pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_RESTART); +} + +/** * pch_i2c_readbytes() - read data from I2C bus in normal mode. * @i2c_adap: Pointer to the struct i2c_adapter. * @msgs: Pointer to i2c_msg structure. @@ -498,7 +503,9 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, u32 length; u32 addr; u32 addr_2_msb; + u32 addr_8_lsb; void __iomem *p = adap->pch_base_address; + s32 rtn; length = msgs->len; buf = msgs->buf; @@ -513,9 +520,26 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, } if (msgs->flags & I2C_M_TEN) { - addr_2_msb = (((addr & I2C_MSB_2B_MSK) >> 7) | (I2C_RD)); + addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7); iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR); + if (first) + pch_i2c_start(adap); + + rtn = pch_i2c_wait_for_check_xfer(adap); + if (rtn) + return rtn; + + addr_8_lsb = (addr & I2C_ADDR_MSK); + iowrite32(addr_8_lsb, p + PCH_I2CDR); + + pch_i2c_restart(adap); + rtn = pch_i2c_wait_for_check_xfer(adap); + if (rtn) + return rtn; + + addr_2_msb |= I2C_RD; + iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR); } else { /* 7 address bits + R/W bit */ addr = (((addr) << 1) | (I2C_RD)); @@ -526,56 +550,50 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, if (first) pch_i2c_start(adap); - if ((pch_i2c_wait_for_xfer_complete(adap) == 0) && - (pch_i2c_getack(adap) == 0)) { - pch_dbg(adap, "return %d\n", 0); + rtn = pch_i2c_wait_for_check_xfer(adap); + if (rtn) + return rtn; - if (length == 0) { - pch_i2c_stop(adap); - ioread32(p + PCH_I2CDR); /* Dummy read needs */ + if (length == 0) { + pch_i2c_stop(adap); + ioread32(p + PCH_I2CDR); /* Dummy read needs */ - count = length; - } else { - int read_index; - int loop; - pch_i2c_sendack(adap); + count = length; + } else { + int read_index; + int loop; + pch_i2c_sendack(adap); - /* Dummy read */ - for (loop = 1, read_index = 0; loop < length; loop++) { - buf[read_index] = ioread32(p + PCH_I2CDR); + /* Dummy read */ + for (loop = 1, read_index = 0; loop < length; loop++) { + buf[read_index] = ioread32(p + PCH_I2CDR); - if (loop != 1) - read_index++; + if (loop != 1) + read_index++; - if (pch_i2c_wait_for_xfer_complete(adap) != 0) { - pch_i2c_stop(adap); - return -ETIME; - } - } /* end for */ + rtn = pch_i2c_wait_for_check_xfer(adap); + if (rtn) + return rtn; + } /* end for */ - pch_i2c_sendnack(adap); + pch_i2c_sendnack(adap); - buf[read_index] = ioread32(p + PCH_I2CDR); + buf[read_index] = ioread32(p + PCH_I2CDR); /* Read final - 1 */ - if (length != 1) - read_index++; + if (length != 1) + read_index++; - if (pch_i2c_wait_for_xfer_complete(adap) == 0) { - if (last) - pch_i2c_stop(adap); - else - pch_i2c_repstart(adap); + rtn = pch_i2c_wait_for_check_xfer(adap); + if (rtn) + return rtn; - buf[read_index++] = ioread32(p + PCH_I2CDR); - count = read_index; - } else { - count = -ETIME; - } + if (last) + pch_i2c_stop(adap); + else + pch_i2c_repstart(adap); - } - } else { - count = -ETIME; - pch_i2c_stop(adap); + buf[read_index++] = ioread32(p + PCH_I2CDR); /* Read Final */ + count = read_index; } return count; @@ -651,15 +669,13 @@ static s32 pch_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *pmsg; u32 i = 0; u32 status; - u32 msglen; - u32 subaddrlen; s32 ret; struct i2c_algo_pch_data *adap = i2c_adap->algo_data; ret = mutex_lock_interruptible(&pch_mutex); if (ret) - return -ERESTARTSYS; + return ret; if (adap->p_adapter_info->pch_i2c_suspended) { mutex_unlock(&pch_mutex); @@ -671,32 +687,27 @@ static s32 pch_i2c_xfer(struct i2c_adapter *i2c_adap, /* transfer not completed */ adap->pch_i2c_xfer_in_progress = true; - pmsg = &msgs[0]; - pmsg->flags |= adap->pch_buff_mode_en; - status = pmsg->flags; - pch_dbg(adap, - "After invoking I2C_MODE_SEL :flag= 0x%x\n", status); - /* calculate sub address length and message length */ - /* these are applicable only for buffer mode */ - subaddrlen = pmsg->buf[0]; - /* calculate actual message length excluding - * the sub address fields */ - msglen = (pmsg->len) - (subaddrlen + 1); - if (status & (I2C_M_RD)) { - pch_dbg(adap, "invoking pch_i2c_readbytes\n"); - ret = pch_i2c_readbytes(i2c_adap, pmsg, (i + 1 == num), - (i == 0)); - } else { - pch_dbg(adap, "invoking pch_i2c_writebytes\n"); - ret = pch_i2c_writebytes(i2c_adap, pmsg, (i + 1 == num), - (i == 0)); + for (i = 0; i < num && ret >= 0; i++) { + pmsg = &msgs[i]; + pmsg->flags |= adap->pch_buff_mode_en; + status = pmsg->flags; + pch_dbg(adap, + "After invoking I2C_MODE_SEL :flag= 0x%x\n", status); + + if ((status & (I2C_M_RD)) != false) { + ret = pch_i2c_readbytes(i2c_adap, pmsg, (i + 1 == num), + (i == 0)); + } else { + ret = pch_i2c_writebytes(i2c_adap, pmsg, (i + 1 == num), + (i == 0)); + } } adap->pch_i2c_xfer_in_progress = false; /* transfer completed */ mutex_unlock(&pch_mutex); - return ret; + return (ret < 0) ? ret : num; } /** @@ -728,7 +739,7 @@ static void pch_i2c_disbl_int(struct i2c_algo_pch_data *adap) iowrite32(BUFFER_MODE_INTR_DISBL, p + PCH_I2CBUFMSK); } -static int __devinit pch_i2c_probe(struct pci_dev *pdev, +static int pch_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id) { void __iomem *base_addr; @@ -740,10 +751,8 @@ static int __devinit pch_i2c_probe(struct pci_dev *pdev, pch_pci_dbg(pdev, "Entered.\n"); adap_info = kzalloc((sizeof(struct adapter_info)), GFP_KERNEL); - if (adap_info == NULL) { - pch_pci_err(pdev, "Memory allocation FAILED\n"); + if (adap_info == NULL) return -ENOMEM; - } ret = pci_enable_device(pdev); if (ret) { @@ -768,6 +777,13 @@ static int __devinit pch_i2c_probe(struct pci_dev *pdev, /* Set the number of I2C channel instance */ adap_info->ch_num = id->driver_data; + ret = request_irq(pdev->irq, pch_i2c_handler, IRQF_SHARED, + KBUILD_MODNAME, adap_info); + if (ret) { + pch_pci_err(pdev, "request_irq FAILED\n"); + goto err_request_irq; + } + for (i = 0; i < adap_info->ch_num; i++) { pch_adap = &adap_info->pch_data[i].pch_adapter; adap_info->pch_i2c_suspended = false; @@ -776,7 +792,7 @@ static int __devinit pch_i2c_probe(struct pci_dev *pdev, pch_adap->owner = THIS_MODULE; pch_adap->class = I2C_CLASS_HWMON; - strcpy(pch_adap->name, KBUILD_MODNAME); + strlcpy(pch_adap->name, KBUILD_MODNAME, sizeof(pch_adap->name)); pch_adap->algo = &pch_algorithm; pch_adap->algo_data = &adap_info->pch_data[i]; @@ -785,28 +801,25 @@ static int __devinit pch_i2c_probe(struct pci_dev *pdev, pch_adap->dev.parent = &pdev->dev; - ret = i2c_add_adapter(pch_adap); + pch_i2c_init(&adap_info->pch_data[i]); + + pch_adap->nr = i; + ret = i2c_add_numbered_adapter(pch_adap); if (ret) { pch_pci_err(pdev, "i2c_add_adapter[ch:%d] FAILED\n", i); - goto err_i2c_add_adapter; + goto err_add_adapter; } - - pch_i2c_init(&adap_info->pch_data[i]); - } - ret = request_irq(pdev->irq, pch_i2c_handler, IRQF_SHARED, - KBUILD_MODNAME, adap_info); - if (ret) { - pch_pci_err(pdev, "request_irq FAILED\n"); - goto err_i2c_add_adapter; } pci_set_drvdata(pdev, adap_info); pch_pci_dbg(pdev, "returns %d.\n", ret); return 0; -err_i2c_add_adapter: +err_add_adapter: for (j = 0; j < i; j++) i2c_del_adapter(&adap_info->pch_data[j].pch_adapter); + free_irq(pdev->irq, adap_info); +err_request_irq: pci_iounmap(pdev, base_addr); err_pci_iomap: pci_release_regions(pdev); @@ -817,7 +830,7 @@ err_pci_enable: return ret; } -static void __devexit pch_i2c_remove(struct pci_dev *pdev) +static void pch_i2c_remove(struct pci_dev *pdev) { int i; struct adapter_info *adap_info = pci_get_drvdata(pdev); @@ -833,9 +846,7 @@ static void __devexit pch_i2c_remove(struct pci_dev *pdev) pci_iounmap(pdev, adap_info->pch_data[0].pch_base_address); for (i = 0; i < adap_info->ch_num; i++) - adap_info->pch_data[i].pch_base_address = 0; - - pci_set_drvdata(pdev, NULL); + adap_info->pch_data[i].pch_base_address = NULL; pci_release_regions(pdev); @@ -914,25 +925,15 @@ static struct pci_driver pch_pcidriver = { .name = KBUILD_MODNAME, .id_table = pch_pcidev_id, .probe = pch_i2c_probe, - .remove = __devexit_p(pch_i2c_remove), + .remove = pch_i2c_remove, .suspend = pch_i2c_suspend, .resume = pch_i2c_resume }; -static int __init pch_pci_init(void) -{ - return pci_register_driver(&pch_pcidriver); -} -module_init(pch_pci_init); - -static void __exit pch_pci_exit(void) -{ - pci_unregister_driver(&pch_pcidriver); -} -module_exit(pch_pci_exit); +module_pci_driver(pch_pcidriver); -MODULE_DESCRIPTION("Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH I2C Driver"); +MODULE_DESCRIPTION("Intel EG20T PCH/LAPIS Semico ML7213/ML7223/ML7831 IOH I2C"); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Tomoya MORINAGA. <tomoya-linux@dsn.okisemi.com>"); +MODULE_AUTHOR("Tomoya MORINAGA. <tomoya.rohm@gmail.com>"); module_param(pch_i2c_speed, int, (S_IRUSR | S_IWUSR)); module_param(pch_clk, int, (S_IRUSR | S_IWUSR)); |
