From 335d7c58fcc1b71387a2c38b610b08bb9d3a6fcc Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 26 Jan 2011 11:45:49 -0800 Subject: i2c/busses: Add support for Diolan U2C-12 USB-I2C adapter This patch adds support for the Diolan U2C-12 USB-I2C adapter. It also updates MAINTAINERS to list the author as maintainer. Signed-off-by: Guenter Roeck Signed-off-by: Ben Dooks --- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-diolan-u2c.c | 535 ++++++++++++++++++++++++++++++++++++ 3 files changed, 546 insertions(+) create mode 100644 drivers/i2c/busses/i2c-diolan-u2c.c (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 113505a6434e..30f8dbd7e03d 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -648,6 +648,16 @@ config I2C_EG20T 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 9d2d0ec7fb23..3c630b7c5663 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -64,6 +64,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 000000000000..76366716a854 --- /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 +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION(DRIVER_NAME " driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 173442f2787c88e1ed1bb62aaeb6fd9127720559 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Tue, 1 Mar 2011 14:16:23 +0900 Subject: i2c-eg20t: support new device OKI SEMICONDUCTOR ML7213 IOH Support new device OKI SEMICONDUCTOR ML7213 IOH. The ML7213 which is for IVI(In-Vehicle Infotainment) is a companion chip for the Atom E6xx series and compatible with the Intel EG20T PCH. Signed-off-by: Tomoya MORINAGA Signed-off-by: Ben Dooks --- drivers/i2c/busses/Kconfig | 15 ++-- drivers/i2c/busses/i2c-eg20t.c | 161 +++++++++++++++++++++++++---------------- 2 files changed, 108 insertions(+), 68 deletions(-) (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 113505a6434e..5b592dfcbd78 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -639,12 +639,15 @@ 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" diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index 2e067dd2ee51..c57c83734692 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -131,6 +131,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 @@ -155,12 +162,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; }; @@ -169,8 +178,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,} }; @@ -211,8 +225,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; @@ -254,7 +267,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; @@ -474,8 +487,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; @@ -568,10 +581,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; @@ -599,24 +612,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; } /** @@ -626,7 +645,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; @@ -709,11 +728,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"); @@ -743,44 +764,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); @@ -793,17 +818,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); @@ -816,17 +846,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", @@ -849,6 +884,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); @@ -861,7 +897,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; @@ -893,7 +930,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. "); module_param(pch_i2c_speed, int, (S_IRUSR | S_IWUSR)); -- cgit v1.2.3 From 6b7d815c3f37f22c355b00362bc7c7d0bac9baea Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 25 Feb 2011 18:54:51 +0100 Subject: i2c-mxs: set controller to pio queue mode after reset mxs_reset_block() clears the PIO_QUEUE_MODE bit. So we have to set it again after a controller reset. Signed-off-by: Sascha Hauer Signed-off-by: Wolfram Sang Cc: Ben Dooks Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-mxs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 8022e2390a5a..caf96dc8ca1b 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)); -- cgit v1.2.3 From d6668c7cd4defdab1a74c8dd271b5ca23d99b627 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 23 Feb 2011 12:38:15 +0100 Subject: i2c-pxa2xx: use dynamic register layout This will prepare the driver to handle register layouts where certain registers are not available at all. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-pxa.c | 70 +++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 20 deletions(-) (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index f4c19a97e0b3..a011455650e9 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -38,29 +38,49 @@ #include #include +struct pxa_reg_layout { + u32 ibmr; + u32 idbr; + u32 icr; + u32 isr; + u32 isar; +}; + +enum pxa_i2c_types { + REGS_PXA2XX, + REGS_PXA3XX, +}; + /* - * I2C register offsets will be shifted 0 or 1 bit left, depending on - * different SoCs + * I2C registers definitions */ -#define REG_SHIFT_0 (0 << 0) -#define REG_SHIFT_1 (1 << 0) -#define REG_SHIFT(d) ((d) & 0x1) +static struct pxa_reg_layout pxa_reg_layout[] = { + [REGS_PXA2XX] = { + .ibmr = 0x00, + .idbr = 0x10, + .icr = 0x20, + .isr = 0x30, + .isar = 0x40, + }, + [REGS_PXA3XX] = { + .ibmr = 0x00, + .idbr = 0x08, + .icr = 0x10, + .isr = 0x18, + .isar = 0x20, + }, +}; static const struct platform_device_id i2c_pxa_id_table[] = { - { "pxa2xx-i2c", REG_SHIFT_1 }, - { "pxa3xx-pwri2c", REG_SHIFT_0 }, + { "pxa2xx-i2c", REGS_PXA2XX }, + { "pxa3xx-pwri2c", REGS_PXA3XX }, { }, }; MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table); /* - * I2C registers and bit definitions + * I2C 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 */ @@ -111,7 +131,11 @@ struct pxa_i2c { u32 icrlog[32]; void __iomem *reg_base; - unsigned int reg_shift; + void __iomem *reg_ibmr; + void __iomem *reg_idbr; + void __iomem *reg_icr; + void __iomem *reg_isr; + void __iomem *reg_isar; unsigned long iobase; unsigned long iosize; @@ -121,11 +145,11 @@ struct pxa_i2c { unsigned int fast_mode :1; }; -#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)) +#define _IBMR(i2c) ((i2c)->reg_ibmr) +#define _IDBR(i2c) ((i2c)->reg_idbr) +#define _ICR(i2c) ((i2c)->reg_icr) +#define _ISR(i2c) ((i2c)->reg_isr) +#define _ISAR(i2c) ((i2c)->reg_isar) /* * I2C Slave mode address @@ -1001,6 +1025,7 @@ static int i2c_pxa_probe(struct platform_device *dev) struct resource *res; struct i2c_pxa_platform_data *plat = dev->dev.platform_data; const struct platform_device_id *id = platform_get_device_id(dev); + enum pxa_i2c_types i2c_type = id->driver_data; int ret; int irq; @@ -1044,7 +1069,12 @@ static int i2c_pxa_probe(struct platform_device *dev) ret = -EIO; goto eremap; } - i2c->reg_shift = REG_SHIFT(id->driver_data); + + i2c->reg_ibmr = i2c->reg_base + pxa_reg_layout[i2c_type].ibmr; + i2c->reg_idbr = i2c->reg_base + pxa_reg_layout[i2c_type].idbr; + i2c->reg_icr = i2c->reg_base + pxa_reg_layout[i2c_type].icr; + i2c->reg_isr = i2c->reg_base + pxa_reg_layout[i2c_type].isr; + i2c->reg_isar = i2c->reg_base + pxa_reg_layout[i2c_type].isar; i2c->iobase = res->start; i2c->iosize = resource_size(res); -- cgit v1.2.3 From b459396ee9398bdf61e3118ca730394f58e90c9c Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 23 Feb 2011 12:38:16 +0100 Subject: ARM: pxa2xx: reorganize I2C files This patch moves the platform data definition from arch/arm/plat-pxa/include/plat/i2c.h to include/linux/i2c/pxa-i2c.h so it can be accessed from x86 the same way as on ARM. This change should make no functional change to the PXA code. The move is verified by building the following defconfigs: cm_x2xx_defconfig corgi_defconfig em_x270_defconfig ezx_defconfig imote2_defconfig pxa3xx_defconfig spitz_defconfig zeus_defconfig raumfeld_defconfig magician_defconfig mmp2_defconfig pxa168_defconfig pxa910_defconfig Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dirk Brandewie Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-pxa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index a011455650e9..fc2a90ed14cc 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -34,9 +34,9 @@ #include #include #include +#include #include -#include struct pxa_reg_layout { u32 ibmr; -- cgit v1.2.3 From 7e94dd154e934aa2137c427c3b1c8e9a6e465fcd Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 2 Mar 2011 11:26:53 +0100 Subject: i2c-pxa2xx: Add PCI support for PXA I2C controller The Sodaville I2C controller is almost the same as found on PXA2xx. The difference: - the register are at a different offset - no slave support The PCI probe code adds three platform devices which are probed then by the platform code. The X86 part also adds dummy clock defines because we don't have HW clock support. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dirk Brandewie Signed-off-by: Ben Dooks --- drivers/i2c/busses/Kconfig | 7 +- drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-pxa-pci.c | 176 +++++++++++++++++++++++++++++++++++++++ drivers/i2c/busses/i2c-pxa.c | 27 +++++- 4 files changed, 206 insertions(+), 5 deletions(-) create mode 100644 drivers/i2c/busses/i2c-pxa-pci.c (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 230601e8853f..b6e53c277276 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -546,15 +546,18 @@ config I2C_PUV3 config I2C_PXA tristate "Intel PXA2XX I2C adapter" - depends on ARCH_PXA || ARCH_MMP + depends on ARCH_PXA || ARCH_MMP || (X86_32 && PCI && OF) help If you have devices in the PXA I2C bus, say yes to this option. This driver can also be built as a module. If so, the module will be called i2c-pxa. +config I2C_PXA_PCI + def_bool I2C_PXA && X86_32 && PCI && OF + config I2C_PXA_SLAVE bool "Intel PXA2XX I2C Slave comms support" - depends on I2C_PXA + depends on I2C_PXA && !X86_32 help Support I2C slave mode communications on the PXA I2C bus. This is necessary for systems where the PXA may be a target on the diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 3878c959d4fa..1dd554e51440 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_I2C_PMCMSP) += i2c-pmcmsp.o obj-$(CONFIG_I2C_PNX) += i2c-pnx.o obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o obj-$(CONFIG_I2C_PXA) += i2c-pxa.o +obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o obj-$(CONFIG_I2C_S6000) += i2c-s6000.o obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o diff --git a/drivers/i2c/busses/i2c-pxa-pci.c b/drivers/i2c/busses/i2c-pxa-pci.c new file mode 100644 index 000000000000..6659d269b841 --- /dev/null +++ b/drivers/i2c/busses/i2c-pxa-pci.c @@ -0,0 +1,176 @@ +/* + * The CE4100's I2C device is more or less the same one as found on PXA. + * It does not support slave mode, the register slightly moved. This PCI + * device provides three bars, every contains a single I2C controller. + */ +#include +#include +#include +#include +#include +#include + +#define CE4100_PCI_I2C_DEVS 3 + +struct ce4100_devices { + struct platform_device *pdev[CE4100_PCI_I2C_DEVS]; +}; + +static struct platform_device *add_i2c_device(struct pci_dev *dev, int bar) +{ + struct platform_device *pdev; + struct i2c_pxa_platform_data pdata; + struct resource res[2]; + struct device_node *child; + static int devnum; + int ret; + + memset(&pdata, 0, sizeof(struct i2c_pxa_platform_data)); + memset(&res, 0, sizeof(res)); + + res[0].flags = IORESOURCE_MEM; + res[0].start = pci_resource_start(dev, bar); + res[0].end = pci_resource_end(dev, bar); + + res[1].flags = IORESOURCE_IRQ; + res[1].start = dev->irq; + res[1].end = dev->irq; + + for_each_child_of_node(dev->dev.of_node, child) { + const void *prop; + struct resource r; + int ret; + + ret = of_address_to_resource(child, 0, &r); + if (ret < 0) + continue; + if (r.start != res[0].start) + continue; + if (r.end != res[0].end) + continue; + if (r.flags != res[0].flags) + continue; + + prop = of_get_property(child, "fast-mode", NULL); + if (prop) + pdata.fast_mode = 1; + + break; + } + + if (!child) { + dev_err(&dev->dev, "failed to match a DT node for bar %d.\n", + bar); + ret = -EINVAL; + goto out; + } + + pdev = platform_device_alloc("ce4100-i2c", devnum); + if (!pdev) { + of_node_put(child); + ret = -ENOMEM; + goto out; + } + pdev->dev.parent = &dev->dev; + pdev->dev.of_node = child; + + ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res)); + if (ret) + goto err; + + ret = platform_device_add_data(pdev, &pdata, sizeof(pdata)); + if (ret) + goto err; + + ret = platform_device_add(pdev); + if (ret) + goto err; + devnum++; + return pdev; +err: + platform_device_put(pdev); +out: + return ERR_PTR(ret); +} + +static int __devinit ce4100_i2c_probe(struct pci_dev *dev, + const struct pci_device_id *ent) +{ + int ret; + int i; + struct ce4100_devices *sds; + + ret = pci_enable_device_mem(dev); + if (ret) + return ret; + + if (!dev->dev.of_node) { + dev_err(&dev->dev, "Missing device tree node.\n"); + return -EINVAL; + } + sds = kzalloc(sizeof(*sds), GFP_KERNEL); + if (!sds) + goto err_mem; + + for (i = 0; i < ARRAY_SIZE(sds->pdev); i++) { + sds->pdev[i] = add_i2c_device(dev, i); + if (IS_ERR(sds->pdev[i])) { + while (--i >= 0) + platform_device_unregister(sds->pdev[i]); + goto err_dev_add; + } + } + pci_set_drvdata(dev, sds); + return 0; + +err_dev_add: + pci_set_drvdata(dev, NULL); + kfree(sds); +err_mem: + pci_disable_device(dev); + return ret; +} + +static void __devexit ce4100_i2c_remove(struct pci_dev *dev) +{ + struct ce4100_devices *sds; + unsigned int i; + + sds = pci_get_drvdata(dev); + pci_set_drvdata(dev, NULL); + + for (i = 0; i < ARRAY_SIZE(sds->pdev); i++) + platform_device_unregister(sds->pdev[i]); + + pci_disable_device(dev); + kfree(sds); +} + +static struct pci_device_id ce4100_i2c_devices[] __devinitdata = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e68)}, + { }, +}; +MODULE_DEVICE_TABLE(pci, ce4100_i2c_devices); + +static struct pci_driver ce4100_i2c_driver = { + .name = "ce4100_i2c", + .id_table = ce4100_i2c_devices, + .probe = ce4100_i2c_probe, + .remove = __devexit_p(ce4100_i2c_remove), +}; + +static int __init ce4100_i2c_init(void) +{ + return pci_register_driver(&ce4100_i2c_driver); +} +module_init(ce4100_i2c_init); + +static void __exit ce4100_i2c_exit(void) +{ + pci_unregister_driver(&ce4100_i2c_driver); +} +module_exit(ce4100_i2c_exit); + +MODULE_DESCRIPTION("CE4100 PCI-I2C glue code for PXA's driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sebastian Andrzej Siewior "); diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index fc2a90ed14cc..ab59b7e492ed 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -38,6 +38,13 @@ #include +#ifndef CONFIG_HAVE_CLK +#define clk_get(dev, id) NULL +#define clk_put(clk) do { } while (0) +#define clk_disable(clk) do { } while (0) +#define clk_enable(clk) do { } while (0) +#endif + struct pxa_reg_layout { u32 ibmr; u32 idbr; @@ -49,6 +56,7 @@ struct pxa_reg_layout { enum pxa_i2c_types { REGS_PXA2XX, REGS_PXA3XX, + REGS_CE4100, }; /* @@ -69,11 +77,19 @@ static struct pxa_reg_layout pxa_reg_layout[] = { .isr = 0x18, .isar = 0x20, }, + [REGS_CE4100] = { + .ibmr = 0x14, + .idbr = 0x0c, + .icr = 0x00, + .isr = 0x04, + /* no isar register */ + }, }; static const struct platform_device_id i2c_pxa_id_table[] = { { "pxa2xx-i2c", REGS_PXA2XX }, { "pxa3xx-pwri2c", REGS_PXA3XX }, + { "ce4100-i2c", REGS_CE4100 }, { }, }; MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table); @@ -442,7 +458,8 @@ static void i2c_pxa_reset(struct pxa_i2c *i2c) writel(I2C_ISR_INIT, _ISR(i2c)); writel(readl(_ICR(i2c)) & ~ICR_UR, _ICR(i2c)); - writel(i2c->slave_addr, _ISAR(i2c)); + if (i2c->reg_isar) + writel(i2c->slave_addr, _ISAR(i2c)); /* set control register values */ writel(I2C_ICR_INIT | (i2c->fast_mode ? ICR_FM : 0), _ICR(i2c)); @@ -1074,7 +1091,8 @@ static int i2c_pxa_probe(struct platform_device *dev) i2c->reg_idbr = i2c->reg_base + pxa_reg_layout[i2c_type].idbr; i2c->reg_icr = i2c->reg_base + pxa_reg_layout[i2c_type].icr; i2c->reg_isr = i2c->reg_base + pxa_reg_layout[i2c_type].isr; - i2c->reg_isar = i2c->reg_base + pxa_reg_layout[i2c_type].isar; + if (i2c_type != REGS_CE4100) + i2c->reg_isar = i2c->reg_base + pxa_reg_layout[i2c_type].isar; i2c->iobase = res->start; i2c->iosize = resource_size(res); @@ -1113,7 +1131,10 @@ static int i2c_pxa_probe(struct platform_device *dev) i2c->adap.algo_data = i2c; i2c->adap.dev.parent = &dev->dev; - ret = i2c_add_numbered_adapter(&i2c->adap); + if (i2c_type == REGS_CE4100) + ret = i2c_add_adapter(&i2c->adap); + else + ret = i2c_add_numbered_adapter(&i2c->adap); if (ret < 0) { printk(KERN_INFO "I2C: Failed to add bus\n"); goto eadapt; -- cgit v1.2.3 From c66dc529194be374556d166ee7ddb84a7d1d302b Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 23 Feb 2011 12:38:18 +0100 Subject: i2c-pxa2xx: add support for shared IRQ handler Sodaville has three of them on a single IRQ. IRQF_DISABLED is removed because it is a NOP allready and scheduled for removal. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dirk Brandewie Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-pxa.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index ab59b7e492ed..fa8dd2c634d9 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -956,11 +956,17 @@ static void i2c_pxa_irq_rxfull(struct pxa_i2c *i2c, u32 isr) writel(icr, _ICR(i2c)); } +#define VALID_INT_SOURCE (ISR_SSD | ISR_ALD | ISR_ITE | ISR_IRF | \ + ISR_SAD | ISR_BED) static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id) { struct pxa_i2c *i2c = dev_id; u32 isr = readl(_ISR(i2c)); + isr &= VALID_INT_SOURCE; + if (!isr) + return IRQ_NONE; + if (i2c_debug > 2 && 0) { dev_dbg(&i2c->adap.dev, "%s: ISR=%08x, ICR=%08x, IBMR=%02x\n", __func__, isr, readl(_ICR(i2c)), readl(_IBMR(i2c))); @@ -975,7 +981,7 @@ static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id) /* * Always clear all pending IRQs. */ - writel(isr & (ISR_SSD|ISR_ALD|ISR_ITE|ISR_IRF|ISR_SAD|ISR_BED), _ISR(i2c)); + writel(isr, _ISR(i2c)); if (isr & ISR_SAD) i2c_pxa_slave_start(i2c, isr); @@ -1120,7 +1126,7 @@ static int i2c_pxa_probe(struct platform_device *dev) i2c->adap.algo = &i2c_pxa_pio_algorithm; } else { i2c->adap.algo = &i2c_pxa_algorithm; - ret = request_irq(irq, i2c_pxa_handler, IRQF_DISABLED, + ret = request_irq(irq, i2c_pxa_handler, IRQF_SHARED, i2c->adap.name, i2c); if (ret) goto ereqirq; -- cgit v1.2.3 From 93c92cfdec8c3f8764894b37606c174f68fd9338 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 23 Feb 2011 12:38:19 +0100 Subject: i2c-pxa2xx: check timeout correctly timeout here maybe 0 if the event occured and a task with a higher priority stole the cpu and we were sleeping longer than the timeout value we specified. In case of a real timeout I changed the error code to I2C_RETRY so we retry the transfer. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dirk Brandewie Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-pxa.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index fa8dd2c634d9..86128363a57f 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -770,8 +770,10 @@ static int i2c_pxa_do_xfer(struct pxa_i2c *i2c, struct i2c_msg *msg, int num) */ ret = i2c->msg_idx; - if (timeout == 0) + if (!timeout && i2c->msg_num) { i2c_pxa_scream_blue_murder(i2c, "timeout"); + ret = I2C_RETRY; + } out: return ret; -- cgit v1.2.3 From baa8cab012ff07d6835580b19c2fd93a20627823 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 23 Feb 2011 12:38:20 +0100 Subject: i2c-pxa2xx: pass of_node from platform driver to adapter and publish the of_node will auto-publish devices which are added to the device tree. Commit 925bb9c6 aka ("of/i2c: Fix module load order issue caused by of_i2c.c) moved the of_i2c_register_devices() function from the i2c core back to the drivers. This patch does the same thing for the pxa driver. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dirk Brandewie Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-pxa.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 86128363a57f..27b28e78666e 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -1138,6 +1139,9 @@ static int i2c_pxa_probe(struct platform_device *dev) i2c->adap.algo_data = i2c; i2c->adap.dev.parent = &dev->dev; +#ifdef CONFIG_OF + i2c->adap.dev.of_node = dev->dev.of_node; +#endif if (i2c_type == REGS_CE4100) ret = i2c_add_adapter(&i2c->adap); @@ -1147,6 +1151,7 @@ static int i2c_pxa_probe(struct platform_device *dev) printk(KERN_INFO "I2C: Failed to add bus\n"); goto eadapt; } + of_i2c_register_devices(&i2c->adap); platform_set_drvdata(dev, i2c); -- cgit v1.2.3 From 23e74a86ab43e5cccbf90edb76fc396e36acc998 Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Sun, 13 Mar 2011 15:53:28 +0200 Subject: i2c-pxa2xx: Fix register offsets Fix regression that was introduced by dynamic register layout. Signed-off-by: Vasily Khoruzhick Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-pxa.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 27b28e78666e..b914184748c9 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -65,19 +65,19 @@ enum pxa_i2c_types { */ static struct pxa_reg_layout pxa_reg_layout[] = { [REGS_PXA2XX] = { - .ibmr = 0x00, - .idbr = 0x10, - .icr = 0x20, - .isr = 0x30, - .isar = 0x40, - }, - [REGS_PXA3XX] = { .ibmr = 0x00, .idbr = 0x08, .icr = 0x10, .isr = 0x18, .isar = 0x20, }, + [REGS_PXA3XX] = { + .ibmr = 0x00, + .idbr = 0x04, + .icr = 0x08, + .isr = 0x0c, + .isar = 0x10, + }, [REGS_CE4100] = { .ibmr = 0x14, .idbr = 0x0c, -- cgit v1.2.3 From 97491ba3f64c2137101efdfe7593305d692d7d63 Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Sun, 13 Mar 2011 15:53:29 +0200 Subject: i2c-pxa2xx: Don't clear isr bits too early isr is passed later into i2c_pxa_irq_txempty and i2c_pxa_irq_rxfull and they may use some other bits than irq sources. Signed-off-by: Vasily Khoruzhick Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-pxa.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index b914184748c9..f59224a5c761 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -966,8 +966,7 @@ static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id) struct pxa_i2c *i2c = dev_id; u32 isr = readl(_ISR(i2c)); - isr &= VALID_INT_SOURCE; - if (!isr) + if (!(isr & VALID_INT_SOURCE)) return IRQ_NONE; if (i2c_debug > 2 && 0) { @@ -984,7 +983,7 @@ static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id) /* * Always clear all pending IRQs. */ - writel(isr, _ISR(i2c)); + writel(isr & VALID_INT_SOURCE, _ISR(i2c)); if (isr & ISR_SAD) i2c_pxa_slave_start(i2c, isr); -- cgit v1.2.3