diff options
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/Kconfig | 10 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-ali1563.c | 8 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-bfin-twi.c | 6 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-imx.c | 26 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-octeon.c | 651 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-omap.c | 13 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-pca-isa.c | 4 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-pca-platform.c | 4 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-piix4.c | 4 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-tiny-usb.c | 12 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-viapro.c | 4 | ||||
-rw-r--r-- | drivers/i2c/i2c-core.c | 7 |
13 files changed, 713 insertions, 37 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 5f318ce2977..737f05200b1 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -564,6 +564,16 @@ config I2C_VERSATILE This driver can also be built as a module. If so, the module will be called i2c-versatile. +config I2C_OCTEON + tristate "Cavium OCTEON I2C bus support" + depends on CPU_CAVIUM_OCTEON + help + Say yes if you want to support the I2C serial bus on Cavium + OCTEON SOC. + + This driver can also be built as a module. If so, the module + will be called i2c-octeon. + comment "External I2C/SMBus adapter drivers" config I2C_PARPORT diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 302c551977b..c2c4ea1908d 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o obj-$(CONFIG_I2C_STU300) += i2c-stu300.o obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o +obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o # External I2C/SMBus adapter drivers obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o diff --git a/drivers/i2c/busses/i2c-ali1563.c b/drivers/i2c/busses/i2c-ali1563.c index f70f46582c6..4687af40dd5 100644 --- a/drivers/i2c/busses/i2c-ali1563.c +++ b/drivers/i2c/busses/i2c-ali1563.c @@ -87,9 +87,9 @@ static int ali1563_transaction(struct i2c_adapter * a, int size) outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_START, SMB_HST_CNTL2); timeout = ALI1563_MAX_TIMEOUT; - do + do { msleep(1); - while (((data = inb_p(SMB_HST_STS)) & HST_STS_BUSY) && --timeout); + } while (((data = inb_p(SMB_HST_STS)) & HST_STS_BUSY) && --timeout); dev_dbg(&a->dev, "Transaction (post): STS=%02x, CNTL1=%02x, " "CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n", @@ -157,9 +157,9 @@ static int ali1563_block_start(struct i2c_adapter * a) outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_START, SMB_HST_CNTL2); timeout = ALI1563_MAX_TIMEOUT; - do + do { msleep(1); - while (!((data = inb_p(SMB_HST_STS)) & HST_STS_DONE) && --timeout); + } while (!((data = inb_p(SMB_HST_STS)) & HST_STS_DONE) && --timeout); dev_dbg(&a->dev, "Block (post): STS=%02x, CNTL1=%02x, " "CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n", diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c index b309ac2c3d5..fe3fb567317 100644 --- a/drivers/i2c/busses/i2c-bfin-twi.c +++ b/drivers/i2c/busses/i2c-bfin-twi.c @@ -693,13 +693,13 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev) } /* Set TWI internal clock as 10MHz */ - write_CONTROL(iface, ((get_sclk() / 1024 / 1024 + 5) / 10) & 0x7F); + write_CONTROL(iface, ((get_sclk() / 1000 / 1000 + 5) / 10) & 0x7F); /* * We will not end up with a CLKDIV=0 because no one will specify - * 20kHz SCL or less in Kconfig now. (5 * 1024 / 20 = 0x100) + * 20kHz SCL or less in Kconfig now. (5 * 1000 / 20 = 250) */ - clkhilow = 5 * 1024 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ; + clkhilow = ((10 * 1000 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ) + 1) / 2; /* Set Twi interface clock as specified */ write_CLKDIV(iface, (clkhilow << 8) | clkhilow); diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index e3654d683e1..75bf820e7cc 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -226,7 +226,6 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx) temp = readb(i2c_imx->base + IMX_I2C_I2CR); temp &= ~(I2CR_MSTA | I2CR_MTX); writeb(temp, i2c_imx->base + IMX_I2C_I2CR); - i2c_imx->stopped = 1; } if (cpu_is_mx1()) { /* @@ -236,8 +235,10 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx) udelay(i2c_imx->disable_delay); } - if (!i2c_imx->stopped) + if (!i2c_imx->stopped) { i2c_imx_bus_busy(i2c_imx, 0); + i2c_imx->stopped = 1; + } /* Disable I2C controller */ writeb(0, i2c_imx->base + IMX_I2C_I2CR); @@ -496,22 +497,23 @@ static int __init i2c_imx_probe(struct platform_device *pdev) } res_size = resource_size(res); + + if (!request_mem_region(res->start, res_size, DRIVER_NAME)) { + ret = -EBUSY; + goto fail0; + } + base = ioremap(res->start, res_size); if (!base) { dev_err(&pdev->dev, "ioremap failed\n"); ret = -EIO; - goto fail0; + goto fail1; } i2c_imx = kzalloc(sizeof(struct imx_i2c_struct), GFP_KERNEL); if (!i2c_imx) { dev_err(&pdev->dev, "can't allocate interface\n"); ret = -ENOMEM; - goto fail1; - } - - if (!request_mem_region(res->start, res_size, DRIVER_NAME)) { - ret = -EBUSY; goto fail2; } @@ -582,11 +584,11 @@ fail5: fail4: clk_put(i2c_imx->clk); fail3: - release_mem_region(i2c_imx->res->start, resource_size(res)); -fail2: kfree(i2c_imx); -fail1: +fail2: iounmap(base); +fail1: + release_mem_region(res->start, resource_size(res)); fail0: if (pdata && pdata->exit) pdata->exit(&pdev->dev); @@ -618,8 +620,8 @@ static int __exit i2c_imx_remove(struct platform_device *pdev) clk_put(i2c_imx->clk); - release_mem_region(i2c_imx->res->start, resource_size(i2c_imx->res)); iounmap(i2c_imx->base); + release_mem_region(i2c_imx->res->start, resource_size(i2c_imx->res)); kfree(i2c_imx); return 0; } diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c new file mode 100644 index 00000000000..60375504fa4 --- /dev/null +++ b/drivers/i2c/busses/i2c-octeon.c @@ -0,0 +1,651 @@ +/* + * (C) Copyright 2009-2010 + * Nokia Siemens Networks, michael.lawnick.ext@nsn.com + * + * Portions Copyright (C) 2010 Cavium Networks, Inc. + * + * This is a driver for the i2c adapter in Cavium Networks' OCTEON processors. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/init.h> + +#include <linux/io.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/platform_device.h> + +#include <asm/octeon/octeon.h> + +#define DRV_NAME "i2c-octeon" + +/* The previous out-of-tree version was implicitly version 1.0. */ +#define DRV_VERSION "2.0" + +/* register offsets */ +#define SW_TWSI 0x00 +#define TWSI_INT 0x10 + +/* Controller command patterns */ +#define SW_TWSI_V 0x8000000000000000ull +#define SW_TWSI_EOP_TWSI_DATA 0x0C00000100000000ull +#define SW_TWSI_EOP_TWSI_CTL 0x0C00000200000000ull +#define SW_TWSI_EOP_TWSI_CLKCTL 0x0C00000300000000ull +#define SW_TWSI_EOP_TWSI_STAT 0x0C00000300000000ull +#define SW_TWSI_EOP_TWSI_RST 0x0C00000700000000ull +#define SW_TWSI_OP_TWSI_CLK 0x0800000000000000ull +#define SW_TWSI_R 0x0100000000000000ull + +/* Controller command and status bits */ +#define TWSI_CTL_CE 0x80 +#define TWSI_CTL_ENAB 0x40 +#define TWSI_CTL_STA 0x20 +#define TWSI_CTL_STP 0x10 +#define TWSI_CTL_IFLG 0x08 +#define TWSI_CTL_AAK 0x04 + +/* Some status values */ +#define STAT_START 0x08 +#define STAT_RSTART 0x10 +#define STAT_TXADDR_ACK 0x18 +#define STAT_TXDATA_ACK 0x28 +#define STAT_RXADDR_ACK 0x40 +#define STAT_RXDATA_ACK 0x50 +#define STAT_IDLE 0xF8 + +struct octeon_i2c { + wait_queue_head_t queue; + struct i2c_adapter adap; + int irq; + int twsi_freq; + int sys_freq; + resource_size_t twsi_phys; + void __iomem *twsi_base; + resource_size_t regsize; + struct device *dev; +}; + +/** + * octeon_i2c_write_sw - write an I2C core register. + * @i2c: The struct octeon_i2c. + * @eop_reg: Register selector. + * @data: Value to be written. + * + * The I2C core registers are accessed indirectly via the SW_TWSI CSR. + */ +static void octeon_i2c_write_sw(struct octeon_i2c *i2c, + u64 eop_reg, + u8 data) +{ + u64 tmp; + + __raw_writeq(SW_TWSI_V | eop_reg | data, i2c->twsi_base + SW_TWSI); + do { + tmp = __raw_readq(i2c->twsi_base + SW_TWSI); + } while ((tmp & SW_TWSI_V) != 0); +} + +/** + * octeon_i2c_read_sw - write an I2C core register. + * @i2c: The struct octeon_i2c. + * @eop_reg: Register selector. + * + * Returns the data. + * + * The I2C core registers are accessed indirectly via the SW_TWSI CSR. + */ +static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg) +{ + u64 tmp; + + __raw_writeq(SW_TWSI_V | eop_reg | SW_TWSI_R, i2c->twsi_base + SW_TWSI); + do { + tmp = __raw_readq(i2c->twsi_base + SW_TWSI); + } while ((tmp & SW_TWSI_V) != 0); + + return tmp & 0xFF; +} + +/** + * octeon_i2c_write_int - write the TWSI_INT register + * @i2c: The struct octeon_i2c. + * @data: Value to be written. + */ +static void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data) +{ + u64 tmp; + + __raw_writeq(data, i2c->twsi_base + TWSI_INT); + tmp = __raw_readq(i2c->twsi_base + TWSI_INT); +} + +/** + * octeon_i2c_int_enable - enable the TS interrupt. + * @i2c: The struct octeon_i2c. + * + * The interrupt will be asserted when there is non-STAT_IDLE state in + * the SW_TWSI_EOP_TWSI_STAT register. + */ +static void octeon_i2c_int_enable(struct octeon_i2c *i2c) +{ + octeon_i2c_write_int(i2c, 0x40); +} + +/** + * octeon_i2c_int_disable - disable the TS interrupt. + * @i2c: The struct octeon_i2c. + */ +static void octeon_i2c_int_disable(struct octeon_i2c *i2c) +{ + octeon_i2c_write_int(i2c, 0); +} + +/** + * octeon_i2c_unblock - unblock the bus. + * @i2c: The struct octeon_i2c. + * + * If there was a reset while a device was driving 0 to bus, + * bus is blocked. We toggle it free manually by some clock + * cycles and send a stop. + */ +static void octeon_i2c_unblock(struct octeon_i2c *i2c) +{ + int i; + + dev_dbg(i2c->dev, "%s\n", __func__); + for (i = 0; i < 9; i++) { + octeon_i2c_write_int(i2c, 0x0); + udelay(5); + octeon_i2c_write_int(i2c, 0x200); + udelay(5); + } + octeon_i2c_write_int(i2c, 0x300); + udelay(5); + octeon_i2c_write_int(i2c, 0x100); + udelay(5); + octeon_i2c_write_int(i2c, 0x0); +} + +/** + * octeon_i2c_isr - the interrupt service routine. + * @int: The irq, unused. + * @dev_id: Our struct octeon_i2c. + */ +static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) +{ + struct octeon_i2c *i2c = dev_id; + + octeon_i2c_int_disable(i2c); + wake_up_interruptible(&i2c->queue); + + return IRQ_HANDLED; +} + + +static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) +{ + return (octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_CTL) & TWSI_CTL_IFLG) != 0; +} + +/** + * octeon_i2c_wait - wait for the IFLG to be set. + * @i2c: The struct octeon_i2c. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_wait(struct octeon_i2c *i2c) +{ + int result; + + octeon_i2c_int_enable(i2c); + + result = wait_event_interruptible_timeout(i2c->queue, + octeon_i2c_test_iflg(i2c), + i2c->adap.timeout); + + octeon_i2c_int_disable(i2c); + + if (result < 0) { + dev_dbg(i2c->dev, "%s: wait interrupted\n", __func__); + return result; + } else if (result == 0) { + dev_dbg(i2c->dev, "%s: timeout\n", __func__); + result = -ETIMEDOUT; + } + + return 0; +} + +/** + * octeon_i2c_start - send START to the bus. + * @i2c: The struct octeon_i2c. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_start(struct octeon_i2c *i2c) +{ + u8 data; + int result; + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB | TWSI_CTL_STA); + + result = octeon_i2c_wait(i2c); + if (result) { + if (octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT) == STAT_IDLE) { + /* + * Controller refused to send start flag May + * be a client is holding SDA low - let's try + * to free it. + */ + octeon_i2c_unblock(i2c); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB | TWSI_CTL_STA); + + result = octeon_i2c_wait(i2c); + } + if (result) + return result; + } + + data = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + if ((data != STAT_START) && (data != STAT_RSTART)) { + dev_err(i2c->dev, "%s: bad status (0x%x)\n", __func__, data); + return -EIO; + } + + return 0; +} + +/** + * octeon_i2c_stop - send STOP to the bus. + * @i2c: The struct octeon_i2c. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_stop(struct octeon_i2c *i2c) +{ + u8 data; + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB | TWSI_CTL_STP); + + data = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + + if (data != STAT_IDLE) { + dev_err(i2c->dev, "%s: bad status(0x%x)\n", __func__, data); + return -EIO; + } + return 0; +} + +/** + * octeon_i2c_write - send data to the bus. + * @i2c: The struct octeon_i2c. + * @target: Target address. + * @data: Pointer to the data to be sent. + * @length: Length of the data. + * + * The address is sent over the bus, then the data. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_write(struct octeon_i2c *i2c, int target, + const u8 *data, int length) +{ + int i, result; + u8 tmp; + + result = octeon_i2c_start(i2c); + if (result) + return result; + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, target << 1); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + + for (i = 0; i < length; i++) { + tmp = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + if ((tmp != STAT_TXADDR_ACK) && (tmp != STAT_TXDATA_ACK)) { + dev_err(i2c->dev, + "%s: bad status before write (0x%x)\n", + __func__, tmp); + return -EIO; + } + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, data[i]); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + } + + return 0; +} + +/** + * octeon_i2c_read - receive data from the bus. + * @i2c: The struct octeon_i2c. + * @target: Target address. + * @data: Pointer to the location to store the datae . + * @length: Length of the data. + * + * The address is sent over the bus, then the data is read. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_read(struct octeon_i2c *i2c, int target, + u8 *data, int length) +{ + int i, result; + u8 tmp; + + if (length < 1) + return -EINVAL; + + result = octeon_i2c_start(i2c); + if (result) + return result; + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, (target<<1) | 1); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + + for (i = 0; i < length; i++) { + tmp = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + if ((tmp != STAT_RXDATA_ACK) && (tmp != STAT_RXADDR_ACK)) { + dev_err(i2c->dev, + "%s: bad status before read (0x%x)\n", + __func__, tmp); + return -EIO; + } + + if (i+1 < length) + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB | TWSI_CTL_AAK); + else + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + + data[i] = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_DATA); + } + return 0; +} + +/** + * octeon_i2c_xfer - The driver's master_xfer function. + * @adap: Pointer to the i2c_adapter structure. + * @msgs: Pointer to the messages to be processed. + * @num: Length of the MSGS array. + * + * Returns the number of messages processed, or a negative errno on + * failure. + */ +static int octeon_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, + int num) +{ + struct i2c_msg *pmsg; + int i; + int ret = 0; + struct octeon_i2c *i2c = i2c_get_adapdata(adap); + + for (i = 0; ret == 0 && i < num; i++) { + pmsg = &msgs[i]; + dev_dbg(i2c->dev, + "Doing %s %d byte(s) to/from 0x%02x - %d of %d messages\n", + pmsg->flags & I2C_M_RD ? "read" : "write", + pmsg->len, pmsg->addr, i + 1, num); + if (pmsg->flags & I2C_M_RD) + ret = octeon_i2c_read(i2c, pmsg->addr, pmsg->buf, + pmsg->len); + else + ret = octeon_i2c_write(i2c, pmsg->addr, pmsg->buf, + pmsg->len); + } + octeon_i2c_stop(i2c); + + return (ret != 0) ? ret : num; +} + +static u32 octeon_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm octeon_i2c_algo = { + .master_xfer = octeon_i2c_xfer, + .functionality = octeon_i2c_functionality, +}; + +static struct i2c_adapter octeon_i2c_ops = { + .owner = THIS_MODULE, + .name = "OCTEON adapter", + .algo = &octeon_i2c_algo, + .timeout = 2, +}; + +/** + * octeon_i2c_setclock - Calculate and set clock divisors. + */ +static int __init octeon_i2c_setclock(struct octeon_i2c *i2c) +{ + int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff; + int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = 1000000; + + for (ndiv_idx = 0; ndiv_idx < 8 && delta_hz != 0; ndiv_idx++) { + /* + * An mdiv value of less than 2 seems to not work well + * with ds1337 RTCs, so we constrain it to larger + * values. + */ + for (mdiv_idx = 15; mdiv_idx >= 2 && delta_hz != 0; mdiv_idx--) { + /* + * For given ndiv and mdiv values check the + * two closest thp values. + */ + tclk = i2c->twsi_freq * (mdiv_idx + 1) * 10; + tclk *= (1 << ndiv_idx); + thp_base = (i2c->sys_freq / (tclk * 2)) - 1; + for (inc = 0; inc <= 1; inc++) { + thp_idx = thp_base + inc; + if (thp_idx < 5 || thp_idx > 0xff) + continue; + + foscl = i2c->sys_freq / (2 * (thp_idx + 1)); + foscl = foscl / (1 << ndiv_idx); + foscl = foscl / (mdiv_idx + 1) / 10; + diff = abs(foscl - i2c->twsi_freq); + if (diff < delta_hz) { + delta_hz = diff; + thp = thp_idx; + mdiv = mdiv_idx; + ndiv = ndiv_idx; + } + } + } + } + octeon_i2c_write_sw(i2c, SW_TWSI_OP_TWSI_CLK, thp); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CLKCTL, (mdiv << 3) | ndiv); + + return 0; +} + +static int __init octeon_i2c_initlowlevel(struct octeon_i2c *i2c) +{ + u8 status; + int tries; + + /* disable high level controller, enable bus access */ + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + /* reset controller */ + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_RST, 0); + + for (tries = 10; tries; tries--) { + udelay(1); + status = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + if (status == STAT_IDLE) + return 0; + } + dev_err(i2c->dev, "%s: TWSI_RST failed! (0x%x)\n", __func__, status); + return -EIO; +} + +static int __devinit octeon_i2c_probe(struct platform_device *pdev) +{ + int irq, result = 0; + struct octeon_i2c *i2c; + struct octeon_i2c_data *i2c_data; + struct resource *res_mem; + + /* All adaptors have an irq. */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); + if (!i2c) { + dev_err(&pdev->dev, "kzalloc failed\n"); + result = -ENOMEM; + goto out; + } + i2c->dev = &pdev->dev; + i2c_data = pdev->dev.platform_data; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (res_mem == NULL) { + dev_err(i2c->dev, "found no memory resource\n"); + result = -ENXIO; + goto fail_region; + } + + if (i2c_data == NULL) { + dev_err(i2c->dev, "no I2C frequency data\n"); + result = -ENXIO; + goto fail_region; + } + + i2c->twsi_phys = res_mem->start; + i2c->regsize = resource_size(res_mem); + i2c->twsi_freq = i2c_data->i2c_freq; + i2c->sys_freq = i2c_data->sys_freq; + + if (!request_mem_region(i2c->twsi_phys, i2c->regsize, res_mem->name)) { + dev_err(i2c->dev, "request_mem_region failed\n"); + goto fail_region; + } + i2c->twsi_base = ioremap(i2c->twsi_phys, i2c->regsize); + + init_waitqueue_head(&i2c->queue); + + i2c->irq = irq; + + result = request_irq(i2c->irq, octeon_i2c_isr, 0, DRV_NAME, i2c); + if (result < 0) { + dev_err(i2c->dev, "failed to attach interrupt\n"); + goto fail_irq; + } + + result = octeon_i2c_initlowlevel(i2c); + if (result) { + dev_err(i2c->dev, "init low level failed\n"); + goto fail_add; + } + + result = octeon_i2c_setclock(i2c); + if (result) { + dev_err(i2c->dev, "clock init failed\n"); + goto fail_add; + } + + i2c->adap = octeon_i2c_ops; + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.nr = pdev->id >= 0 ? pdev->id : 0; + i2c_set_adapdata(&i2c->adap, i2c); + platform_set_drvdata(pdev, i2c); + + result = i2c_add_numbered_adapter(&i2c->adap); + if (result < 0) { + dev_err(i2c->dev, "failed to add adapter\n"); + goto fail_add; + } + + dev_info(i2c->dev, "version %s\n", DRV_VERSION); + + return result; + +fail_add: + platform_set_drvdata(pdev, NULL); + free_irq(i2c->irq, i2c); +fail_irq: + iounmap(i2c->twsi_base); + release_mem_region(i2c->twsi_phys, i2c->regsize); +fail_region: + kfree(i2c); +out: + return result; +}; + +static int __devexit octeon_i2c_remove(struct platform_device *pdev) +{ + struct octeon_i2c *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adap); + platform_set_drvdata(pdev, NULL); + free_irq(i2c->irq, i2c); + iounmap(i2c->twsi_base); + release_mem_region(i2c->twsi_phys, i2c->regsize); + kfree(i2c); + return 0; +}; + +static struct platform_driver octeon_i2c_driver = { + .probe = octeon_i2c_probe, + .remove = __devexit_p(octeon_i2c_remove), + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + }, +}; + +static int __init octeon_i2c_init(void) +{ + int rv; + + rv = platform_driver_register(&octeon_i2c_driver); + return rv; +} + +static void __exit octeon_i2c_exit(void) +{ + platform_driver_unregister(&octeon_i2c_driver); +} + +MODULE_AUTHOR("Michael Lawnick <michael.lawnick.ext@nsn.com>"); +MODULE_DESCRIPTION("I2C-Bus adapter for Cavium OCTEON processors"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); +MODULE_ALIAS("platform:" DRV_NAME); + +module_init(octeon_i2c_init); +module_exit(octeon_i2c_exit); diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 75bf3ad1809..0037e31076b 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -247,7 +247,13 @@ static void omap_i2c_unidle(struct omap_i2c_dev *dev) omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); } dev->idle = 0; - omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate); + + /* + * Don't write to this register if the IE state is 0 as it can + * cause deadlock. + */ + if (dev->iestate) + omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate); } static void omap_i2c_idle(struct omap_i2c_dev *dev) @@ -280,6 +286,11 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) unsigned long internal_clk = 0; if (dev->rev >= OMAP_I2C_REV_2) { + /* Disable I2C controller before soft reset */ + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, + omap_i2c_read_reg(dev, OMAP_I2C_CON_REG) & + ~(OMAP_I2C_CON_EN)); + omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, SYSC_SOFTRESET_MASK); /* For some reason we need to set the EN bit before the * reset done bit gets set. */ diff --git a/drivers/i2c/busses/i2c-pca-isa.c b/drivers/i2c/busses/i2c-pca-isa.c index 0ed68e2ccd2..f7346a9bd95 100644 --- a/drivers/i2c/busses/i2c-pca-isa.c +++ b/drivers/i2c/busses/i2c-pca-isa.c @@ -75,7 +75,7 @@ static int pca_isa_waitforcompletion(void *pd) unsigned long timeout; if (irq > -1) { - ret = wait_event_interruptible_timeout(pca_wait, + ret = wait_event_timeout(pca_wait, pca_isa_readbyte(pd, I2C_PCA_CON) & I2C_PCA_CON_SI, pca_isa_ops.timeout); } else { @@ -96,7 +96,7 @@ static void pca_isa_resetchip(void *pd) } static irqreturn_t pca_handler(int this_irq, void *dev_id) { - wake_up_interruptible(&pca_wait); + wake_up(&pca_wait); return IRQ_HANDLED; } diff --git a/drivers/i2c/busses/i2c-pca-platform.c b/drivers/i2c/busses/i2c-pca-platform.c index c4df9d411cd..5b2213df5ed 100644 --- a/drivers/i2c/busses/i2c-pca-platform.c +++ b/drivers/i2c/busses/i2c-pca-platform.c @@ -84,7 +84,7 @@ static int i2c_pca_pf_waitforcompletion(void *pd) unsigned long timeout; if (i2c->irq) { - ret = wait_event_interruptible_timeout(i2c->wait, + ret = wait_event_timeout(i2c->wait, i2c->algo_data.read_byte(i2c, I2C_PCA_CON) & I2C_PCA_CON_SI, i2c->adap.timeout); } else { @@ -122,7 +122,7 @@ static irqreturn_t i2c_pca_pf_handler(int this_irq, void *dev_id) if ((i2c->algo_data.read_byte(i2c, I2C_PCA_CON) & I2C_PCA_CON_SI) == 0) return IRQ_NONE; - wake_up_interruptible(&i2c->wait); + wake_up(&i2c->wait); return IRQ_HANDLED; } diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 1e245e9cad3..e56e4b6823c 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -324,12 +324,12 @@ static int piix4_transaction(void) else msleep(1); - while ((timeout++ < MAX_TIMEOUT) && + while ((++timeout < MAX_TIMEOUT) && ((temp = inb_p(SMBHSTSTS)) & 0x01)) msleep(1); /* If the SMBus is still busy, we give up */ - if (timeout >= MAX_TIMEOUT) { + if (timeout == MAX_TIMEOUT) { dev_err(&piix4_adapter.dev, "SMBus Timeout!\n"); result = -ETIMEDOUT; } diff --git a/drivers/i2c/busses/i2c-tiny-usb.c b/drivers/i2c/busses/i2c-tiny-usb.c index b1c050ff311..e29b6d5ba8e 100644 --- a/drivers/i2c/busses/i2c-tiny-usb.c +++ b/drivers/i2c/busses/i2c-tiny-usb.c @@ -13,6 +13,7 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/module.h> +#include <linux/types.h> /* include interfaces to usb layer */ #include <linux/usb.h> @@ -31,8 +32,8 @@ #define CMD_I2C_IO_END (1<<1) /* i2c bit delay, default is 10us -> 100kHz */ -static int delay = 10; -module_param(delay, int, 0); +static unsigned short delay = 10; +module_param(delay, ushort, 0); MODULE_PARM_DESC(delay, "bit delay in microseconds, " "e.g. 10 for 100kHz (default is 100kHz)"); @@ -109,7 +110,7 @@ static int usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) static u32 usb_func(struct i2c_adapter *adapter) { - u32 func; + __le32 func; /* get functionality from adapter */ if (usb_read(adapter, CMD_GET_FUNC, 0, 0, &func, sizeof(func)) != @@ -118,7 +119,7 @@ static u32 usb_func(struct i2c_adapter *adapter) return 0; } - return func; + return le32_to_cpu(func); } /* This is the actual algorithm we define */ @@ -216,8 +217,7 @@ static int i2c_tiny_usb_probe(struct usb_interface *interface, "i2c-tiny-usb at bus %03d device %03d", dev->usb_dev->bus->busnum, dev->usb_dev->devnum); - if (usb_write(&dev->adapter, CMD_SET_DELAY, - cpu_to_le16(delay), 0, NULL, 0) != 0) { + if (usb_write(&dev->adapter, CMD_SET_DELAY, delay, 0, NULL, 0) != 0) { dev_err(&dev->adapter.dev, "failure setting delay to %dus\n", delay); retval = -EIO; diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c index e4b1543015a..a84a909e123 100644 --- a/drivers/i2c/busses/i2c-viapro.c +++ b/drivers/i2c/busses/i2c-viapro.c @@ -165,10 +165,10 @@ static int vt596_transaction(u8 size) do { msleep(1); temp = inb_p(SMBHSTSTS); - } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT)); + } while ((temp & 0x01) && (++timeout < MAX_TIMEOUT)); /* If the SMBus is still busy, we give up */ - if (timeout >= MAX_TIMEOUT) { + if (timeout == MAX_TIMEOUT) { result = -ETIMEDOUT; dev_err(&vt596_adapter.dev, "SMBus timeout!\n"); } diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 0ac2f90ab84..10be7b5fbe9 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -248,7 +248,7 @@ static const struct attribute_group *i2c_dev_attr_groups[] = { NULL }; -const static struct dev_pm_ops i2c_device_pm_ops = { +static const struct dev_pm_ops i2c_device_pm_ops = { .suspend = i2c_device_pm_suspend, .resume = i2c_device_pm_resume, }; @@ -843,6 +843,9 @@ int i2c_del_adapter(struct i2c_adapter *adap) adap->dev.parent); #endif + /* device name is gone after device_unregister */ + dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name); + /* clean up the sysfs representation */ init_completion(&adap->dev_released); device_unregister(&adap->dev); @@ -855,8 +858,6 @@ int i2c_del_adapter(struct i2c_adapter *adap) idr_remove(&i2c_adapter_idr, adap->nr); mutex_unlock(&core_lock); - dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name); - /* Clear the device structure in case this adapter is ever going to be added again */ memset(&adap->dev, 0, sizeof(adap->dev)); |