diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/i2c/busses/Kconfig | 25 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-diolan-u2c.c | 535 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-eg20t.c | 161 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-mxs.c | 4 |
5 files changed, 656 insertions, 70 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index b6e53c27727..2a44a2ff2c5 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -670,15 +670,28 @@ config I2C_XILINX will be called xilinx_i2c. config I2C_EG20T - tristate "PCH I2C of Intel EG20T" - depends on PCI - help - This driver is for PCH(Platform controller Hub) I2C of EG20T which - is an IOH(Input/Output Hub) for x86 embedded processor. - This driver can access PCH I2C bus device. + tristate "Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH" + depends on PCI + help + This driver is for PCH(Platform controller Hub) I2C of EG20T which + is an IOH(Input/Output Hub) for x86 embedded processor. + This driver can access PCH I2C bus device. + + This driver also supports the ML7213, a companion chip for the + Atom E6xx series and compatible with the Intel EG20T PCH. comment "External I2C/SMBus adapter drivers" +config I2C_DIOLAN_U2C + tristate "Diolan U2C-12 USB adapter" + depends on USB + help + If you say yes to this option, support will be included for Diolan + U2C-12, a USB to I2C interface. + + This driver can also be built as a module. If so, the module + will be called i2c-diolan-u2c. + config I2C_PARPORT tristate "Parallel port adapter" depends on PARPORT diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 1dd554e5144..e6cf294d372 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o # External I2C/SMBus adapter drivers +obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o diff --git a/drivers/i2c/busses/i2c-diolan-u2c.c b/drivers/i2c/busses/i2c-diolan-u2c.c new file mode 100644 index 00000000000..76366716a85 --- /dev/null +++ b/drivers/i2c/busses/i2c-diolan-u2c.c @@ -0,0 +1,535 @@ +/* + * Driver for the Diolan u2c-12 USB-I2C adapter + * + * Copyright (c) 2010-2011 Ericsson AB + * + * Derived from: + * i2c-tiny-usb.c + * Copyright (C) 2006-2007 Till Harbaum (Till@Harbaum.org) + * + * 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 the Free Software Foundation, version 2. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/i2c.h> + +#define DRIVER_NAME "i2c-diolan-u2c" + +#define USB_VENDOR_ID_DIOLAN 0x0abf +#define USB_DEVICE_ID_DIOLAN_U2C 0x3370 + +#define DIOLAN_OUT_EP 0x02 +#define DIOLAN_IN_EP 0x84 + +/* commands via USB, must match command ids in the firmware */ +#define CMD_I2C_READ 0x01 +#define CMD_I2C_WRITE 0x02 +#define CMD_I2C_SCAN 0x03 /* Returns list of detected devices */ +#define CMD_I2C_RELEASE_SDA 0x04 +#define CMD_I2C_RELEASE_SCL 0x05 +#define CMD_I2C_DROP_SDA 0x06 +#define CMD_I2C_DROP_SCL 0x07 +#define CMD_I2C_READ_SDA 0x08 +#define CMD_I2C_READ_SCL 0x09 +#define CMD_GET_FW_VERSION 0x0a +#define CMD_GET_SERIAL 0x0b +#define CMD_I2C_START 0x0c +#define CMD_I2C_STOP 0x0d +#define CMD_I2C_REPEATED_START 0x0e +#define CMD_I2C_PUT_BYTE 0x0f +#define CMD_I2C_GET_BYTE 0x10 +#define CMD_I2C_PUT_ACK 0x11 +#define CMD_I2C_GET_ACK 0x12 +#define CMD_I2C_PUT_BYTE_ACK 0x13 +#define CMD_I2C_GET_BYTE_ACK 0x14 +#define CMD_I2C_SET_SPEED 0x1b +#define CMD_I2C_GET_SPEED 0x1c +#define CMD_I2C_SET_CLK_SYNC 0x24 +#define CMD_I2C_GET_CLK_SYNC 0x25 +#define CMD_I2C_SET_CLK_SYNC_TO 0x26 +#define CMD_I2C_GET_CLK_SYNC_TO 0x27 + +#define RESP_OK 0x00 +#define RESP_FAILED 0x01 +#define RESP_BAD_MEMADDR 0x04 +#define RESP_DATA_ERR 0x05 +#define RESP_NOT_IMPLEMENTED 0x06 +#define RESP_NACK 0x07 +#define RESP_TIMEOUT 0x09 + +#define U2C_I2C_SPEED_FAST 0 /* 400 kHz */ +#define U2C_I2C_SPEED_STD 1 /* 100 kHz */ +#define U2C_I2C_SPEED_2KHZ 242 /* 2 kHz, minimum speed */ +#define U2C_I2C_SPEED(f) ((DIV_ROUND_UP(1000000, (f)) - 10) / 2 + 1) + +#define U2C_I2C_FREQ_FAST 400000 +#define U2C_I2C_FREQ_STD 100000 +#define U2C_I2C_FREQ(s) (1000000 / (2 * (s - 1) + 10)) + +#define DIOLAN_USB_TIMEOUT 100 /* in ms */ +#define DIOLAN_SYNC_TIMEOUT 20 /* in ms */ + +#define DIOLAN_OUTBUF_LEN 128 +#define DIOLAN_FLUSH_LEN (DIOLAN_OUTBUF_LEN - 4) +#define DIOLAN_INBUF_LEN 256 /* Maximum supported receive length */ + +/* Structure to hold all of our device specific stuff */ +struct i2c_diolan_u2c { + u8 obuffer[DIOLAN_OUTBUF_LEN]; /* output buffer */ + u8 ibuffer[DIOLAN_INBUF_LEN]; /* input buffer */ + struct usb_device *usb_dev; /* the usb device for this device */ + struct usb_interface *interface;/* the interface for this device */ + struct i2c_adapter adapter; /* i2c related things */ + int olen; /* Output buffer length */ + int ocount; /* Number of enqueued messages */ +}; + +static uint frequency = U2C_I2C_FREQ_STD; /* I2C clock frequency in Hz */ + +module_param(frequency, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(frequency, "I2C clock frequency in hertz"); + +/* usb layer */ + +/* Send command to device, and get response. */ +static int diolan_usb_transfer(struct i2c_diolan_u2c *dev) +{ + int ret = 0; + int actual; + int i; + + if (!dev->olen || !dev->ocount) + return -EINVAL; + + ret = usb_bulk_msg(dev->usb_dev, + usb_sndbulkpipe(dev->usb_dev, DIOLAN_OUT_EP), + dev->obuffer, dev->olen, &actual, + DIOLAN_USB_TIMEOUT); + if (!ret) { + for (i = 0; i < dev->ocount; i++) { + int tmpret; + + tmpret = usb_bulk_msg(dev->usb_dev, + usb_rcvbulkpipe(dev->usb_dev, + DIOLAN_IN_EP), + dev->ibuffer, + sizeof(dev->ibuffer), &actual, + DIOLAN_USB_TIMEOUT); + /* + * Stop command processing if a previous command + * returned an error. + * Note that we still need to retrieve all messages. + */ + if (ret < 0) + continue; + ret = tmpret; + if (ret == 0 && actual > 0) { + switch (dev->ibuffer[actual - 1]) { + case RESP_NACK: + /* + * Return ENXIO if NACK was received as + * response to the address phase, + * EIO otherwise + */ + ret = i == 1 ? -ENXIO : -EIO; + break; + case RESP_TIMEOUT: + ret = -ETIMEDOUT; + break; + case RESP_OK: + /* strip off return code */ + ret = actual - 1; + break; + default: + ret = -EIO; + break; + } + } + } + } + dev->olen = 0; + dev->ocount = 0; + return ret; +} + +static int diolan_write_cmd(struct i2c_diolan_u2c *dev, bool flush) +{ + if (flush || dev->olen >= DIOLAN_FLUSH_LEN) + return diolan_usb_transfer(dev); + return 0; +} + +/* Send command (no data) */ +static int diolan_usb_cmd(struct i2c_diolan_u2c *dev, u8 command, bool flush) +{ + dev->obuffer[dev->olen++] = command; + dev->ocount++; + return diolan_write_cmd(dev, flush); +} + +/* Send command with one byte of data */ +static int diolan_usb_cmd_data(struct i2c_diolan_u2c *dev, u8 command, u8 data, + bool flush) +{ + dev->obuffer[dev->olen++] = command; + dev->obuffer[dev->olen++] = data; + dev->ocount++; + return diolan_write_cmd(dev, flush); +} + +/* Send command with two bytes of data */ +static int diolan_usb_cmd_data2(struct i2c_diolan_u2c *dev, u8 command, u8 d1, + u8 d2, bool flush) +{ + dev->obuffer[dev->olen++] = command; + dev->obuffer[dev->olen++] = d1; + dev->obuffer[dev->olen++] = d2; + dev->ocount++; + return diolan_write_cmd(dev, flush); +} + +/* + * Flush input queue. + * If we don't do this at startup and the controller has queued up + * messages which were not retrieved, it will stop responding + * at some point. + */ +static void diolan_flush_input(struct i2c_diolan_u2c *dev) +{ + int i; + + for (i = 0; i < 10; i++) { + int actual = 0; + int ret; + + ret = usb_bulk_msg(dev->usb_dev, + usb_rcvbulkpipe(dev->usb_dev, DIOLAN_IN_EP), + dev->ibuffer, sizeof(dev->ibuffer), &actual, + DIOLAN_USB_TIMEOUT); + if (ret < 0 || actual == 0) + break; + } + if (i == 10) + dev_err(&dev->interface->dev, "Failed to flush input buffer\n"); +} + +static int diolan_i2c_start(struct i2c_diolan_u2c *dev) +{ + return diolan_usb_cmd(dev, CMD_I2C_START, false); +} + +static int diolan_i2c_repeated_start(struct i2c_diolan_u2c *dev) +{ + return diolan_usb_cmd(dev, CMD_I2C_REPEATED_START, false); +} + +static int diolan_i2c_stop(struct i2c_diolan_u2c *dev) +{ + return diolan_usb_cmd(dev, CMD_I2C_STOP, true); +} + +static int diolan_i2c_get_byte_ack(struct i2c_diolan_u2c *dev, bool ack, + u8 *byte) +{ + int ret; + + ret = diolan_usb_cmd_data(dev, CMD_I2C_GET_BYTE_ACK, ack, true); + if (ret > 0) + *byte = dev->ibuffer[0]; + else if (ret == 0) + ret = -EIO; + + return ret; +} + +static int diolan_i2c_put_byte_ack(struct i2c_diolan_u2c *dev, u8 byte) +{ + return diolan_usb_cmd_data(dev, CMD_I2C_PUT_BYTE_ACK, byte, false); +} + +static int diolan_set_speed(struct i2c_diolan_u2c *dev, u8 speed) +{ + return diolan_usb_cmd_data(dev, CMD_I2C_SET_SPEED, speed, true); +} + +/* Enable or disable clock synchronization (stretching) */ +static int diolan_set_clock_synch(struct i2c_diolan_u2c *dev, bool enable) +{ + return diolan_usb_cmd_data(dev, CMD_I2C_SET_CLK_SYNC, enable, true); +} + +/* Set clock synchronization timeout in ms */ +static int diolan_set_clock_synch_timeout(struct i2c_diolan_u2c *dev, int ms) +{ + int to_val = ms * 10; + + return diolan_usb_cmd_data2(dev, CMD_I2C_SET_CLK_SYNC_TO, + to_val & 0xff, (to_val >> 8) & 0xff, true); +} + +static void diolan_fw_version(struct i2c_diolan_u2c *dev) +{ + int ret; + + ret = diolan_usb_cmd(dev, CMD_GET_FW_VERSION, true); + if (ret >= 2) + dev_info(&dev->interface->dev, + "Diolan U2C firmware version %u.%u\n", + (unsigned int)dev->ibuffer[0], + (unsigned int)dev->ibuffer[1]); +} + +static void diolan_get_serial(struct i2c_diolan_u2c *dev) +{ + int ret; + u32 serial; + + ret = diolan_usb_cmd(dev, CMD_GET_SERIAL, true); + if (ret >= 4) { + serial = le32_to_cpu(*(u32 *)dev->ibuffer); + dev_info(&dev->interface->dev, + "Diolan U2C serial number %u\n", serial); + } +} + +static int diolan_init(struct i2c_diolan_u2c *dev) +{ + int speed, ret; + + if (frequency >= 200000) { + speed = U2C_I2C_SPEED_FAST; + frequency = U2C_I2C_FREQ_FAST; + } else if (frequency >= 100000 || frequency == 0) { + speed = U2C_I2C_SPEED_STD; + frequency = U2C_I2C_FREQ_STD; + } else { + speed = U2C_I2C_SPEED(frequency); + if (speed > U2C_I2C_SPEED_2KHZ) + speed = U2C_I2C_SPEED_2KHZ; + frequency = U2C_I2C_FREQ(speed); + } + + dev_info(&dev->interface->dev, + "Diolan U2C at USB bus %03d address %03d speed %d Hz\n", + dev->usb_dev->bus->busnum, dev->usb_dev->devnum, frequency); + + diolan_flush_input(dev); + diolan_fw_version(dev); + diolan_get_serial(dev); + + /* Set I2C speed */ + ret = diolan_set_speed(dev, speed); + if (ret < 0) + return ret; + + /* Configure I2C clock synchronization */ + ret = diolan_set_clock_synch(dev, speed != U2C_I2C_SPEED_FAST); + if (ret < 0) + return ret; + + if (speed != U2C_I2C_SPEED_FAST) + ret = diolan_set_clock_synch_timeout(dev, DIOLAN_SYNC_TIMEOUT); + + return ret; +} + +/* i2c layer */ + +static int diolan_usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, + int num) +{ + struct i2c_diolan_u2c *dev = i2c_get_adapdata(adapter); + struct i2c_msg *pmsg; + int i, j; + int ret, sret; + + ret = diolan_i2c_start(dev); + if (ret < 0) + return ret; + + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + if (i) { + ret = diolan_i2c_repeated_start(dev); + if (ret < 0) + goto abort; + } + if (pmsg->flags & I2C_M_RD) { + ret = + diolan_i2c_put_byte_ack(dev, (pmsg->addr << 1) | 1); + if (ret < 0) + goto abort; + for (j = 0; j < pmsg->len; j++) { + u8 byte; + bool ack = j < pmsg->len - 1; + + /* + * Don't send NACK if this is the first byte + * of a SMBUS_BLOCK message. + */ + if (j == 0 && (pmsg->flags & I2C_M_RECV_LEN)) + ack = true; + + ret = diolan_i2c_get_byte_ack(dev, ack, &byte); + if (ret < 0) + goto abort; + /* + * Adjust count if first received byte is length + */ + if (j == 0 && (pmsg->flags & I2C_M_RECV_LEN)) { + if (byte == 0 + || byte > I2C_SMBUS_BLOCK_MAX) { + ret = -EPROTO; + goto abort; + } + pmsg->len += byte; + } + pmsg->buf[j] = byte; + } + } else { + ret = diolan_i2c_put_byte_ack(dev, pmsg->addr << 1); + if (ret < 0) + goto abort; + for (j = 0; j < pmsg->len; j++) { + ret = diolan_i2c_put_byte_ack(dev, + pmsg->buf[j]); + if (ret < 0) + goto abort; + } + } + } +abort: + sret = diolan_i2c_stop(dev); + if (sret < 0 && ret >= 0) + ret = sret; + return ret; +} + +/* + * Return list of supported functionality. + */ +static u32 diolan_usb_func(struct i2c_adapter *a) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL; +} + +static const struct i2c_algorithm diolan_usb_algorithm = { + .master_xfer = diolan_usb_xfer, + .functionality = diolan_usb_func, +}; + +/* device layer */ + +static const struct usb_device_id diolan_u2c_table[] = { + { USB_DEVICE(USB_VENDOR_ID_DIOLAN, USB_DEVICE_ID_DIOLAN_U2C) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, diolan_u2c_table); + +static void diolan_u2c_free(struct i2c_diolan_u2c *dev) +{ + usb_put_dev(dev->usb_dev); + kfree(dev); +} + +static int diolan_u2c_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct i2c_diolan_u2c *dev; + int ret; + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + dev_err(&interface->dev, "no memory for device state\n"); + ret = -ENOMEM; + goto error; + } + + dev->usb_dev = usb_get_dev(interface_to_usbdev(interface)); + dev->interface = interface; + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + /* setup i2c adapter description */ + dev->adapter.owner = THIS_MODULE; + dev->adapter.class = I2C_CLASS_HWMON; + dev->adapter.algo = &diolan_usb_algorithm; + i2c_set_adapdata(&dev->adapter, dev); + snprintf(dev->adapter.name, sizeof(dev->adapter.name), + DRIVER_NAME " at bus %03d device %03d", + dev->usb_dev->bus->busnum, dev->usb_dev->devnum); + + dev->adapter.dev.parent = &dev->interface->dev; + + /* initialize diolan i2c interface */ + ret = diolan_init(dev); + if (ret < 0) { + dev_err(&interface->dev, "failed to initialize adapter\n"); + goto error_free; + } + + /* and finally attach to i2c layer */ + ret = i2c_add_adapter(&dev->adapter); + if (ret < 0) { + dev_err(&interface->dev, "failed to add I2C adapter\n"); + goto error_free; + } + + dev_dbg(&interface->dev, "connected " DRIVER_NAME "\n"); + + return 0; + +error_free: + usb_set_intfdata(interface, NULL); + diolan_u2c_free(dev); +error: + return ret; +} + +static void diolan_u2c_disconnect(struct usb_interface *interface) +{ + struct i2c_diolan_u2c *dev = usb_get_intfdata(interface); + + i2c_del_adapter(&dev->adapter); + usb_set_intfdata(interface, NULL); + diolan_u2c_free(dev); + + dev_dbg(&interface->dev, "disconnected\n"); +} + +static struct usb_driver diolan_u2c_driver = { + .name = DRIVER_NAME, + .probe = diolan_u2c_probe, + .disconnect = diolan_u2c_disconnect, + .id_table = diolan_u2c_table, +}; + +static int __init diolan_u2c_init(void) +{ + /* register this driver with the USB subsystem */ + return usb_register(&diolan_u2c_driver); +} + +static void __exit diolan_u2c_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&diolan_u2c_driver); +} + +module_init(diolan_u2c_init); +module_exit(diolan_u2c_exit); + +MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>"); +MODULE_DESCRIPTION(DRIVER_NAME " driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index 50ea1f43bdc..878a12026af 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -132,6 +132,13 @@ #define pch_pci_dbg(pdev, fmt, arg...) \ dev_dbg(&pdev->dev, "%s :" fmt, __func__, ##arg) +/* +Set the number of I2C instance max +Intel EG20T PCH : 1ch +OKI SEMICONDUCTOR ML7213 IOH : 2ch +*/ +#define PCH_I2C_MAX_DEV 2 + /** * struct i2c_algo_pch_data - for I2C driver functionalities * @pch_adapter: stores the reference to i2c_adapter structure @@ -156,12 +163,14 @@ struct i2c_algo_pch_data { * @pch_data: stores a list of i2c_algo_pch_data * @pch_i2c_suspended: specifies whether the system is suspended or not * perhaps with more lines and words. + * @ch_num: specifies the number of i2c instance * * pch_data has as many elements as maximum I2C channels */ struct adapter_info { - struct i2c_algo_pch_data pch_data; + struct i2c_algo_pch_data pch_data[PCH_I2C_MAX_DEV]; bool pch_i2c_suspended; + int ch_num; }; @@ -170,8 +179,13 @@ 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 */ +#define PCI_VENDOR_ID_ROHM 0x10DB +#define PCI_DEVICE_ID_ML7213_I2C 0x802D + static struct pci_device_id __devinitdata pch_pcidev_id[] = { - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH_I2C)}, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_I2C), 1, }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_I2C), 2, }, {0,} }; @@ -212,8 +226,7 @@ static void pch_i2c_init(struct i2c_algo_pch_data *adap) /* Initialize I2C registers */ iowrite32(0x21, p + PCH_I2CNF); - pch_setbit(adap->pch_base_address, PCH_I2CCTL, - PCH_I2CCTL_I2CMEN); + pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_I2CCTL_I2CMEN); if (pch_i2c_speed != 400) pch_i2c_speed = 100; @@ -255,7 +268,7 @@ static inline bool ktime_lt(const ktime_t cmp1, const ktime_t cmp2) * @timeout: waiting time counter (us). */ static s32 pch_i2c_wait_for_bus_idle(struct i2c_algo_pch_data *adap, - s32 timeout) + s32 timeout) { void __iomem *p = adap->pch_base_address; @@ -475,8 +488,8 @@ static void pch_i2c_sendnack(struct i2c_algo_pch_data *adap) * @last: specifies whether last message or not. * @first: specifies whether first message or not. */ -s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, - u32 last, u32 first) +static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, + u32 last, u32 first) { struct i2c_algo_pch_data *adap = i2c_adap->algo_data; @@ -569,10 +582,10 @@ s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, } /** - * pch_i2c_cb_ch0() - Interrupt handler Call back function + * pch_i2c_cb() - Interrupt handler Call back function * @adap: Pointer to struct i2c_algo_pch_data. */ -static void pch_i2c_cb_ch0(struct i2c_algo_pch_data *adap) +static void pch_i2c_cb(struct i2c_algo_pch_data *adap) { u32 sts; void __iomem *p = adap->pch_base_address; @@ -600,24 +613,30 @@ static void pch_i2c_cb_ch0(struct i2c_algo_pch_data *adap) */ static irqreturn_t pch_i2c_handler(int irq, void *pData) { - s32 reg_val; - - struct i2c_algo_pch_data *adap_data = (struct i2c_algo_pch_data *)pData; - void __iomem *p = adap_data->pch_base_address; - u32 mode = ioread32(p + PCH_I2CMOD) & (BUFFER_MODE | EEPROM_SR_MODE); - - if (mode != NORMAL_MODE) { - pch_err(adap_data, "I2C mode is not supported\n"); - return IRQ_NONE; + u32 reg_val; + int flag; + int i; + struct adapter_info *adap_info = pData; + void __iomem *p; + u32 mode; + + for (i = 0, flag = 0; i < adap_info->ch_num; i++) { + p = adap_info->pch_data[i].pch_base_address; + mode = ioread32(p + PCH_I2CMOD); + mode &= BUFFER_MODE | EEPROM_SR_MODE; + if (mode != NORMAL_MODE) { + pch_err(adap_info->pch_data, + "I2C-%d mode(%d) is not supported\n", mode, i); + continue; + } + reg_val = ioread32(p + PCH_I2CSR); + if (reg_val & (I2CMAL_BIT | I2CMCF_BIT | I2CMIF_BIT)) { + pch_i2c_cb(&adap_info->pch_data[i]); + flag = 1; + } } - reg_val = ioread32(p + PCH_I2CSR); - if (reg_val & (I2CMAL_BIT | I2CMCF_BIT | I2CMIF_BIT)) - pch_i2c_cb_ch0(adap_data); - else - return IRQ_NONE; - - return IRQ_HANDLED; + return flag ? IRQ_HANDLED : IRQ_NONE; } /** @@ -627,7 +646,7 @@ static irqreturn_t pch_i2c_handler(int irq, void *pData) * @num: number of messages. */ static s32 pch_i2c_xfer(struct i2c_adapter *i2c_adap, - struct i2c_msg *msgs, s32 num) + struct i2c_msg *msgs, s32 num) { struct i2c_msg *pmsg; u32 i = 0; @@ -710,11 +729,13 @@ static void pch_i2c_disbl_int(struct i2c_algo_pch_data *adap) } static int __devinit pch_i2c_probe(struct pci_dev *pdev, - const struct pci_device_id *id) + const struct pci_device_id *id) { void __iomem *base_addr; - s32 ret; + int ret; + int i, j; struct adapter_info *adap_info; + struct i2c_adapter *pch_adap; pch_pci_dbg(pdev, "Entered.\n"); @@ -744,44 +765,48 @@ static int __devinit pch_i2c_probe(struct pci_dev *pdev, goto err_pci_iomap; } - adap_info->pch_i2c_suspended = false; + /* Set the number of I2C channel instance */ + adap_info->ch_num = id->driver_data; - adap_info->pch_data.p_adapter_info = adap_info; + for (i = 0; i < adap_info->ch_num; i++) { + pch_adap = &adap_info->pch_data[i].pch_adapter; + adap_info->pch_i2c_suspended = false; - adap_info->pch_data.pch_adapter.owner = THIS_MODULE; - adap_info->pch_data.pch_adapter.class = I2C_CLASS_HWMON; - strcpy(adap_info->pch_data.pch_adapter.name, KBUILD_MODNAME); - adap_info->pch_data.pch_adapter.algo = &pch_algorithm; - adap_info->pch_data.pch_adapter.algo_data = - &adap_info->pch_data; + adap_info->pch_data[i].p_adapter_info = adap_info; - /* (i * 0x80) + base_addr; */ - adap_info->pch_data.pch_base_address = base_addr; + pch_adap->owner = THIS_MODULE; + pch_adap->class = I2C_CLASS_HWMON; + strcpy(pch_adap->name, KBUILD_MODNAME); + pch_adap->algo = &pch_algorithm; + pch_adap->algo_data = &adap_info->pch_data[i]; - adap_info->pch_data.pch_adapter.dev.parent = &pdev->dev; + /* base_addr + offset; */ + adap_info->pch_data[i].pch_base_address = base_addr + 0x100 * i; - ret = i2c_add_adapter(&(adap_info->pch_data.pch_adapter)); + pch_adap->dev.parent = &pdev->dev; - if (ret) { - pch_pci_err(pdev, "i2c_add_adapter FAILED\n"); - goto err_i2c_add_adapter; - } + ret = i2c_add_adapter(pch_adap); + if (ret) { + pch_pci_err(pdev, "i2c_add_adapter[ch:%d] FAILED\n", i); + goto err_i2c_add_adapter; + } - pch_i2c_init(&adap_info->pch_data); + pch_i2c_init(&adap_info->pch_data[i]); + } ret = request_irq(pdev->irq, pch_i2c_handler, IRQF_SHARED, - KBUILD_MODNAME, &adap_info->pch_data); + KBUILD_MODNAME, adap_info); if (ret) { pch_pci_err(pdev, "request_irq FAILED\n"); - goto err_request_irq; + goto err_i2c_add_adapter; } pci_set_drvdata(pdev, adap_info); pch_pci_dbg(pdev, "returns %d.\n", ret); return 0; -err_request_irq: - i2c_del_adapter(&(adap_info->pch_data.pch_adapter)); err_i2c_add_adapter: + for (j = 0; j < i; j++) + i2c_del_adapter(&adap_info->pch_data[j].pch_adapter); pci_iounmap(pdev, base_addr); err_pci_iomap: pci_release_regions(pdev); @@ -794,17 +819,22 @@ err_pci_enable: static void __devexit pch_i2c_remove(struct pci_dev *pdev) { + int i; struct adapter_info *adap_info = pci_get_drvdata(pdev); - pch_i2c_disbl_int(&adap_info->pch_data); - free_irq(pdev->irq, &adap_info->pch_data); - i2c_del_adapter(&(adap_info->pch_data.pch_adapter)); + free_irq(pdev->irq, adap_info); - if (adap_info->pch_data.pch_base_address) { - pci_iounmap(pdev, adap_info->pch_data.pch_base_address); - adap_info->pch_data.pch_base_address = 0; + for (i = 0; i < adap_info->ch_num; i++) { + pch_i2c_disbl_int(&adap_info->pch_data[i]); + i2c_del_adapter(&adap_info->pch_data[i].pch_adapter); } + if (adap_info->pch_data[0].pch_base_address) + 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); pci_release_regions(pdev); @@ -817,17 +847,22 @@ static void __devexit pch_i2c_remove(struct pci_dev *pdev) static int pch_i2c_suspend(struct pci_dev *pdev, pm_message_t state) { int ret; + int i; struct adapter_info *adap_info = pci_get_drvdata(pdev); - void __iomem *p = adap_info->pch_data.pch_base_address; + void __iomem *p = adap_info->pch_data[0].pch_base_address; adap_info->pch_i2c_suspended = true; - while ((adap_info->pch_data.pch_i2c_xfer_in_progress)) { - /* Wait until all channel transfers are completed */ - msleep(20); + for (i = 0; i < adap_info->ch_num; i++) { + while ((adap_info->pch_data[i].pch_i2c_xfer_in_progress)) { + /* Wait until all channel transfers are completed */ + msleep(20); + } } + /* Disable the i2c interrupts */ - pch_i2c_disbl_int(&adap_info->pch_data); + for (i = 0; i < adap_info->ch_num; i++) + pch_i2c_disbl_int(&adap_info->pch_data[i]); pch_pci_dbg(pdev, "I2CSR = %x I2CBUFSTA = %x I2CESRSTA = %x " "invoked function pch_i2c_disbl_int successfully\n", @@ -850,6 +885,7 @@ static int pch_i2c_suspend(struct pci_dev *pdev, pm_message_t state) static int pch_i2c_resume(struct pci_dev *pdev) { + int i; struct adapter_info *adap_info = pci_get_drvdata(pdev); pci_set_power_state(pdev, PCI_D0); @@ -862,7 +898,8 @@ static int pch_i2c_resume(struct pci_dev *pdev) pci_enable_wake(pdev, PCI_D3hot, 0); - pch_i2c_init(&adap_info->pch_data); + for (i = 0; i < adap_info->ch_num; i++) + pch_i2c_init(&adap_info->pch_data[i]); adap_info->pch_i2c_suspended = false; @@ -894,7 +931,7 @@ static void __exit pch_pci_exit(void) } module_exit(pch_pci_exit); -MODULE_DESCRIPTION("PCH I2C PCI Driver"); +MODULE_DESCRIPTION("Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH I2C Driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Tomoya MORINAGA. <tomoya-linux@dsn.okisemi.com>"); module_param(pch_i2c_speed, int, (S_IRUSR | S_IWUSR)); diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 8022e2390a5..caf96dc8ca1 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -118,6 +118,8 @@ static void mxs_i2c_reset(struct mxs_i2c_dev *i2c) { mxs_reset_block(i2c->regs); writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_SET); + writel(MXS_I2C_QUEUECTRL_PIO_QUEUE_MODE, + i2c->regs + MXS_I2C_QUEUECTRL_SET); } static void mxs_i2c_pioq_setup_read(struct mxs_i2c_dev *i2c, u8 addr, int len, @@ -347,8 +349,6 @@ static int __devinit mxs_i2c_probe(struct platform_device *pdev) /* Do reset to enforce correct startup after pinmuxing */ mxs_i2c_reset(i2c); - writel(MXS_I2C_QUEUECTRL_PIO_QUEUE_MODE, - i2c->regs + MXS_I2C_QUEUECTRL_SET); adap = &i2c->adapter; strlcpy(adap->name, "MXS I2C adapter", sizeof(adap->name)); |