652 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			652 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* drivers/i2c/busses/i2c-msm.c
 | |
|  *
 | |
|  * Copyright (C) 2007 Google, Inc.
 | |
|  *
 | |
|  * This software is licensed under the terms of the GNU General Public
 | |
|  * License version 2, as published by the Free Software Foundation, and
 | |
|  * may be copied, distributed, and modified under those terms.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  * GNU General Public License for more details.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include <linux/clk.h>
 | |
| #include <linux/err.h>
 | |
| #include <linux/gpio.h>
 | |
| #include <linux/init.h>
 | |
| #include <linux/i2c.h>
 | |
| #include <linux/i2c-msm.h>
 | |
| #include <linux/interrupt.h>
 | |
| #include <linux/platform_device.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/io.h>
 | |
| #include <linux/wakelock.h>
 | |
| #include <mach/system.h>
 | |
| 
 | |
| #define DEBUG 0
 | |
| 
 | |
| enum {
 | |
| 	I2C_WRITE_DATA          = 0x00,
 | |
| 	I2C_CLK_CTL             = 0x04,
 | |
| 	I2C_STATUS              = 0x08,
 | |
| 	I2C_READ_DATA           = 0x0c,
 | |
| 	I2C_INTERFACE_SELECT    = 0x10,
 | |
| 
 | |
| 	I2C_WRITE_DATA_DATA_BYTE            = 0xff,
 | |
| 	I2C_WRITE_DATA_ADDR_BYTE            = 1U << 8,
 | |
| 	I2C_WRITE_DATA_LAST_BYTE            = 1U << 9,
 | |
| 
 | |
| 	I2C_CLK_CTL_FS_DIVIDER_VALUE        = 0xff,
 | |
| 	I2C_CLK_CTL_HS_DIVIDER_VALUE        = 7U << 8,
 | |
| 
 | |
| 	I2C_STATUS_WR_BUFFER_FULL           = 1U << 0,
 | |
| 	I2C_STATUS_RD_BUFFER_FULL           = 1U << 1,
 | |
| 	I2C_STATUS_BUS_ERROR                = 1U << 2,
 | |
| 	I2C_STATUS_PACKET_NACKED            = 1U << 3,
 | |
| 	I2C_STATUS_ARB_LOST                 = 1U << 4,
 | |
| 	I2C_STATUS_INVALID_WRITE            = 1U << 5,
 | |
| 	I2C_STATUS_FAILED                   = 3U << 6,
 | |
| 	I2C_STATUS_BUS_ACTIVE               = 1U << 8,
 | |
| 	I2C_STATUS_BUS_MASTER               = 1U << 9,
 | |
| 	I2C_STATUS_ERROR_MASK               = 0xfc,
 | |
| 
 | |
| 	I2C_INTERFACE_SELECT_INTF_SELECT    = 1U << 0,
 | |
| 	I2C_INTERFACE_SELECT_SCL            = 1U << 8,
 | |
| 	I2C_INTERFACE_SELECT_SDA            = 1U << 9,
 | |
| };
 | |
| 
 | |
| struct msm_i2c_dev {
 | |
| 	struct device      *dev;
 | |
| 	void __iomem       *base;		/* virtual */
 | |
| 	int                 irq;
 | |
| 	struct clk         *clk;
 | |
| 	struct i2c_adapter  adapter;
 | |
| 
 | |
| 	spinlock_t          lock;
 | |
| 
 | |
| 	struct i2c_msg      *msg;
 | |
| 	int                 rem;
 | |
| 	int                 pos;
 | |
| 	int                 cnt;
 | |
| 	int                 ret;
 | |
| 	bool                need_flush;
 | |
| 	int                 flush_cnt;
 | |
| 	void                *complete;
 | |
| 	struct wake_lock    wakelock;
 | |
| 	bool                is_suspended;
 | |
| 	int                 clk_drv_str;
 | |
| 	int                 dat_drv_str;
 | |
| 	int                 skip_recover;
 | |
| };
 | |
| 
 | |
| #if DEBUG
 | |
| static void
 | |
| dump_status(uint32_t status)
 | |
| {
 | |
| 	printk("STATUS (0x%.8x): ", status);
 | |
| 	if (status & I2C_STATUS_BUS_MASTER)
 | |
| 		printk("MST ");
 | |
| 	if (status & I2C_STATUS_BUS_ACTIVE)
 | |
| 		printk("ACT ");
 | |
| 	if (status & I2C_STATUS_INVALID_WRITE)
 | |
| 		printk("INV_WR ");
 | |
| 	if (status & I2C_STATUS_ARB_LOST)
 | |
| 		printk("ARB_LST ");
 | |
| 	if (status & I2C_STATUS_PACKET_NACKED)
 | |
| 		printk("NAK ");
 | |
| 	if (status & I2C_STATUS_BUS_ERROR)
 | |
| 		printk("BUS_ERR ");
 | |
| 	if (status & I2C_STATUS_RD_BUFFER_FULL)
 | |
| 		printk("RD_FULL ");
 | |
| 	if (status & I2C_STATUS_WR_BUFFER_FULL)
 | |
| 		printk("WR_FULL ");
 | |
| 	if (status & I2C_STATUS_FAILED)
 | |
| 		printk("FAIL 0x%x", (status & I2C_STATUS_FAILED));
 | |
| 	printk("\n");
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static void msm_i2c_write_delay(struct msm_i2c_dev *dev)
 | |
| {
 | |
| 	/* If scl is still high we have >4us (for 100kbps) to write the data
 | |
| 	 * register before we risk hitting a bug where the controller releases
 | |
| 	 * scl to soon after driving sda low. Writing the data after the
 | |
| 	 * scheduled release time for scl also avoids the bug.
 | |
| 	 */
 | |
| 	if (readl(dev->base + I2C_INTERFACE_SELECT) & I2C_INTERFACE_SELECT_SCL)
 | |
| 		return;
 | |
| 	udelay(6);
 | |
| }
 | |
| 
 | |
| static bool msm_i2c_fill_write_buffer(struct msm_i2c_dev *dev)
 | |
| {
 | |
| 	uint16_t val;
 | |
| 	if (dev->pos < 0) {
 | |
| 		val = I2C_WRITE_DATA_ADDR_BYTE | dev->msg->addr << 1;
 | |
| 		if (dev->msg->flags & I2C_M_RD)
 | |
| 			val |= 1;
 | |
| 		if (dev->rem == 1 && dev->msg->len == 0)
 | |
| 			val |= I2C_WRITE_DATA_LAST_BYTE;
 | |
| 		msm_i2c_write_delay(dev);
 | |
| 		writel(val, dev->base + I2C_WRITE_DATA);
 | |
| 		dev->pos++;
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	if (dev->msg->flags & I2C_M_RD)
 | |
| 		return false;
 | |
| 
 | |
| 	if (!dev->cnt)
 | |
| 		return false;
 | |
| 
 | |
| 	/* Ready to take a byte */
 | |
| 	val = dev->msg->buf[dev->pos];
 | |
| 	if (dev->cnt == 1 && dev->rem == 1)
 | |
| 		val |= I2C_WRITE_DATA_LAST_BYTE;
 | |
| 
 | |
| 	msm_i2c_write_delay(dev);
 | |
| 	writel(val, dev->base + I2C_WRITE_DATA);
 | |
| 	dev->pos++;
 | |
| 	dev->cnt--;
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| static void msm_i2c_read_buffer(struct msm_i2c_dev *dev)
 | |
| {
 | |
| 	/*
 | |
| 	 * Theres something in the FIFO.
 | |
| 	 * Are we expecting data or flush crap?
 | |
| 	 */
 | |
| 	if ((dev->msg->flags & I2C_M_RD) && dev->pos >= 0 && dev->cnt) {
 | |
| 		switch (dev->cnt) {
 | |
| 		case 1:
 | |
| 			if (dev->pos != 0)
 | |
| 				break;
 | |
| 			dev->need_flush = true;
 | |
| 			/* fall-trough */
 | |
| 		case 2:
 | |
| 			writel(I2C_WRITE_DATA_LAST_BYTE,
 | |
| 			       dev->base + I2C_WRITE_DATA);
 | |
| 		}
 | |
| 		dev->msg->buf[dev->pos] = readl(dev->base + I2C_READ_DATA);
 | |
| 		dev->cnt--;
 | |
| 		dev->pos++;
 | |
| 	} else { /* FLUSH */
 | |
| 		if (dev->flush_cnt & 1) {
 | |
| 			/*
 | |
| 			* Stop requests are sometimes ignored, but writing
 | |
| 			* more than one request generates a write error.
 | |
| 			*/
 | |
| 			writel(I2C_WRITE_DATA_LAST_BYTE,
 | |
| 				dev->base + I2C_WRITE_DATA);
 | |
| 		}
 | |
| 		readl(dev->base + I2C_READ_DATA);
 | |
| 		if (dev->need_flush)
 | |
| 			dev->need_flush = false;
 | |
| 		else
 | |
| 			dev->flush_cnt++;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void msm_i2c_interrupt_locked(struct msm_i2c_dev *dev)
 | |
| {
 | |
| 	uint32_t status	= readl(dev->base + I2C_STATUS);
 | |
| 	bool not_done = true;
 | |
| 
 | |
| #if DEBUG
 | |
| 	dump_status(status);
 | |
| #endif
 | |
| 	if (!dev->msg) {
 | |
| 		dev_err(dev->dev,
 | |
| 			"IRQ but nothing to do!, status %x\n", status);
 | |
| 		return;
 | |
| 	}
 | |
| 	if (status & I2C_STATUS_ERROR_MASK)
 | |
| 		goto out_err;
 | |
| 
 | |
| 	if (!(status & I2C_STATUS_WR_BUFFER_FULL))
 | |
| 		not_done = msm_i2c_fill_write_buffer(dev);
 | |
| 	if (status & I2C_STATUS_RD_BUFFER_FULL)
 | |
| 		msm_i2c_read_buffer(dev);
 | |
| 
 | |
| 	if (dev->pos >= 0 && dev->cnt == 0) {
 | |
| 		if (dev->rem > 1) {
 | |
| 			dev->rem--;
 | |
| 			dev->msg++;
 | |
| 			dev->pos = -1;
 | |
| 			dev->cnt = dev->msg->len;
 | |
| 		} else if (!not_done && !dev->need_flush)
 | |
| 			goto out_complete;
 | |
| 	}
 | |
| 	return;
 | |
| 
 | |
| out_err:
 | |
| 	dev_err(dev->dev, "error, status %x (%02X)\n", status, dev->msg->addr);
 | |
| 	dev->ret = -EIO;
 | |
| out_complete:
 | |
| 	complete(dev->complete);
 | |
| }
 | |
| 
 | |
| static irqreturn_t
 | |
| msm_i2c_interrupt(int irq, void *devid)
 | |
| {
 | |
| 	struct msm_i2c_dev *dev = devid;
 | |
| 
 | |
| 	spin_lock(&dev->lock);
 | |
| 	msm_i2c_interrupt_locked(dev);
 | |
| 	spin_unlock(&dev->lock);
 | |
| 
 | |
| 	return IRQ_HANDLED;
 | |
| }
 | |
| #define MICROP_I2C_RCMD_GSENSOR_X_DATA		0x77
 | |
| #define MICROP_I2C_RCMD_GSENSOR_Y_DATA		0x78
 | |
| #define MICROP_I2C_RCMD_GSENSOR_Z_DATA		0x79
 | |
| #define MICROP_I2C_ADDR						0x66
 | |
| #define GSENSOR_TIMEDOUT					210
 | |
| 
 | |
| static int is_gsensor_msg(struct i2c_msg msgs[])
 | |
| {
 | |
| 	return msgs->addr==MICROP_I2C_ADDR && 
 | |
| 					(msgs->buf[0] == MICROP_I2C_RCMD_GSENSOR_X_DATA || 
 | |
| 					msgs->buf[0] == MICROP_I2C_RCMD_GSENSOR_Y_DATA|| 
 | |
| 					msgs->buf[0] == MICROP_I2C_RCMD_GSENSOR_Z_DATA);
 | |
| }
 | |
| 
 | |
| static int
 | |
| msm_i2c_poll_notbusy(struct msm_i2c_dev *dev, int warn, struct i2c_msg msgs[])
 | |
| {
 | |
| 	uint32_t retries = 0;
 | |
| 
 | |
| 	while (retries != (is_gsensor_msg(msgs) ? 100 : 200)) {
 | |
| 		uint32_t status = readl(dev->base + I2C_STATUS);
 | |
| 
 | |
| 		if (!(status & I2C_STATUS_BUS_ACTIVE)) {
 | |
| 			if (retries && warn)
 | |
| 				dev_warn(dev->dev,
 | |
| 					"Warning bus was busy (%d)\n", retries);
 | |
| 			return 0;
 | |
| 		}
 | |
| 		if (retries++ > 100)
 | |
| 			msleep(10);
 | |
| 	}
 | |
| 	dev_err(dev->dev, "Error waiting for notbusy (addr=%02x, msgs=%02x)\n", msgs->addr, msgs->buf[0]);
 | |
| 	return is_gsensor_msg(msgs) ? -GSENSOR_TIMEDOUT : -ETIMEDOUT;
 | |
| }
 | |
| 
 | |
| static int
 | |
| msm_i2c_recover_bus_busy(struct msm_i2c_dev *dev)
 | |
| {
 | |
| 	int i;
 | |
| 	uint32_t status = readl(dev->base + I2C_STATUS);
 | |
| 	int gpio_clk, gpio_dat;
 | |
| 	bool gpio_clk_status = false;
 | |
| 
 | |
| 	if (!(status & (I2C_STATUS_BUS_ACTIVE | I2C_STATUS_WR_BUFFER_FULL)))
 | |
| 		return 0;
 | |
| 
 | |
| 	msm_set_i2c_mux(true, &gpio_clk, &gpio_dat, 0, 0);
 | |
| 
 | |
| 	if (status & I2C_STATUS_RD_BUFFER_FULL) {
 | |
| 		dev_warn(dev->dev, "Read buffer full, status %x, intf %x\n",
 | |
| 			 status, readl(dev->base + I2C_INTERFACE_SELECT));
 | |
| 		writel(I2C_WRITE_DATA_LAST_BYTE, dev->base + I2C_WRITE_DATA);
 | |
| 		readl(dev->base + I2C_READ_DATA);
 | |
| 	}
 | |
| 	else if (status & I2C_STATUS_BUS_MASTER) {
 | |
| 		dev_warn(dev->dev, "Still the bus master, status %x, intf %x\n",
 | |
| 			 status, readl(dev->base + I2C_INTERFACE_SELECT));
 | |
| 		writel(I2C_WRITE_DATA_LAST_BYTE | 0xff,
 | |
| 		       dev->base + I2C_WRITE_DATA);
 | |
| 	}
 | |
| 
 | |
| 	dev_warn(dev->dev, "i2c_scl: %d, i2c_sda: %d\n",
 | |
| 		 gpio_get_value(gpio_clk), gpio_get_value(gpio_dat));
 | |
| 
 | |
| 	for (i = 0; i < 9; i++) {
 | |
| 		if (gpio_get_value(gpio_dat) && gpio_clk_status)
 | |
| 			break;
 | |
| 		gpio_direction_output(gpio_clk, 0);
 | |
| 		udelay(5);
 | |
| 		gpio_direction_output(gpio_dat, 0);
 | |
| 		udelay(5);
 | |
| 		gpio_direction_input(gpio_clk);
 | |
| 		udelay(5);
 | |
| 		if (!gpio_get_value(gpio_clk))
 | |
| 			udelay(20);
 | |
| 		if (!gpio_get_value(gpio_clk))
 | |
| 			msleep(10);
 | |
| 		gpio_clk_status = gpio_get_value(gpio_clk);
 | |
| 		gpio_direction_input(gpio_dat);
 | |
| 		udelay(5);
 | |
| 	}
 | |
| 	msm_set_i2c_mux(false, NULL, NULL,
 | |
| 		dev->clk_drv_str, dev->dat_drv_str);
 | |
| 
 | |
| 	udelay(10);
 | |
| 
 | |
| 	status = readl(dev->base + I2C_STATUS);
 | |
| 	if (!(status & I2C_STATUS_BUS_ACTIVE)) {
 | |
| 		dev_info(dev->dev, "Bus busy cleared after %d clock cycles, "
 | |
| 			 "status %x, intf %x\n",
 | |
| 			 i, status, readl(dev->base + I2C_INTERFACE_SELECT));
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	dev_warn(dev->dev, "Bus still busy, status %x, intf %x\n",
 | |
| 		 status, readl(dev->base + I2C_INTERFACE_SELECT));
 | |
| 	return -EBUSY;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| msm_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 | |
| {
 | |
| 	DECLARE_COMPLETION_ONSTACK(complete);
 | |
| 	struct msm_i2c_dev *dev = i2c_get_adapdata(adap);
 | |
| 	int ret, ret_wait;
 | |
| 	long timeout;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	/*
 | |
| 	 * If there is an i2c_xfer after driver has been suspended,
 | |
| 	 * grab wakelock to abort suspend.
 | |
| 	 */
 | |
| 	if (dev->is_suspended)
 | |
| 		wake_lock(&dev->wakelock);
 | |
| 	clk_enable(dev->clk);
 | |
| 	enable_irq(dev->irq);
 | |
| 
 | |
| 	ret = msm_i2c_poll_notbusy(dev, 1, msgs);
 | |
| 	if (ret) {
 | |
| 		dev_err(dev->dev, "Still busy in starting xfer(%02X)\n",
 | |
| 			msgs->addr);
 | |
| 		if (!dev->skip_recover) {
 | |
| 			ret = msm_i2c_recover_bus_busy(dev);
 | |
| 			if (ret)
 | |
| 				goto err;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	spin_lock_irqsave(&dev->lock, flags);
 | |
| 	if (dev->flush_cnt) {
 | |
| 		dev_warn(dev->dev, "%d unrequested bytes read\n",
 | |
| 			 dev->flush_cnt);
 | |
| 	}
 | |
| 	dev->msg = msgs;
 | |
| 	dev->rem = num;
 | |
| 	dev->pos = -1;
 | |
| 	dev->ret = num;
 | |
| 	dev->need_flush = false;
 | |
| 	dev->flush_cnt = 0;
 | |
| 	dev->cnt = msgs->len;
 | |
| 	dev->complete = &complete;
 | |
| 
 | |
| 	msm_i2c_interrupt_locked(dev);
 | |
| 	spin_unlock_irqrestore(&dev->lock, flags);
 | |
| 
 | |
| 	/*
 | |
| 	 * Now that we've setup the xfer, the ISR will transfer the data
 | |
| 	 * and wake us up with dev->err set if there was an error
 | |
| 	 */
 | |
| 
 | |
| 	timeout = wait_for_completion_timeout(&complete, HZ);
 | |
| 	ret_wait = msm_i2c_poll_notbusy(dev, 0, msgs);
 | |
| 	spin_lock_irqsave(&dev->lock, flags);
 | |
| 	if (dev->flush_cnt) {
 | |
| 		dev_warn(dev->dev, "%d unrequested bytes read\n",
 | |
| 			 dev->flush_cnt);
 | |
| 	}
 | |
| 	ret = dev->ret;
 | |
| 	dev->complete = NULL;
 | |
| 	dev->msg = NULL;
 | |
| 	dev->rem = 0;
 | |
| 	dev->pos = 0;
 | |
| 	dev->ret = 0;
 | |
| 	dev->flush_cnt = 0;
 | |
| 	dev->cnt = 0;
 | |
| 	spin_unlock_irqrestore(&dev->lock, flags);
 | |
| 	if (ret_wait) /* Read may not have stopped in time */
 | |
| 	{
 | |
| 		dev_err(dev->dev, "Still busy after xfer completion (%02X)\n", msgs->addr);
 | |
| 		if (ret_wait == -GSENSOR_TIMEDOUT)
 | |
| 			ret = 2; // in most situations the value of ret is 2 (dev->ret), we set it to 2 just to be sure that function i2c_read_block doesn't repeats the read
 | |
| 		if (!dev->skip_recover) {
 | |
| 			ret_wait = msm_i2c_recover_bus_busy(dev);
 | |
| 			if (ret_wait)
 | |
| 				goto err;
 | |
| 		}
 | |
| 	}
 | |
| 	if (!timeout) {
 | |
| 		dev_err(dev->dev, "Transaction timed out\n");
 | |
| 		ret = -ETIMEDOUT;
 | |
| 	}
 | |
| 
 | |
| 	if (ret < 0) {
 | |
| 		dev_err(dev->dev, "Error during data xfer (%d) (%02X)\n",
 | |
| 			ret, msgs->addr);
 | |
| 		if (!dev->skip_recover)
 | |
| 			msm_i2c_recover_bus_busy(dev);
 | |
| 	}
 | |
| err:
 | |
| 	disable_irq(dev->irq);
 | |
| 	clk_disable(dev->clk);
 | |
| 	if (dev->is_suspended)
 | |
| 		wake_unlock(&dev->wakelock);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static u32
 | |
| msm_i2c_func(struct i2c_adapter *adap)
 | |
| {
 | |
| 	return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
 | |
| }
 | |
| 
 | |
| static const struct i2c_algorithm msm_i2c_algo = {
 | |
| 	.master_xfer	= msm_i2c_xfer,
 | |
| 	.functionality	= msm_i2c_func,
 | |
| };
 | |
| 
 | |
| static int
 | |
| msm_i2c_probe(struct platform_device *pdev)
 | |
| {
 | |
| 	struct msm_i2c_dev	*dev;
 | |
| 	struct resource		*mem, *irq, *ioarea;
 | |
| 	struct msm_i2c_device_platform_data *pdata = pdev->dev.platform_data;
 | |
| 	int ret;
 | |
| 	int fs_div;
 | |
| 	int hs_div;
 | |
| 	int i2c_clk, i2c_clock;
 | |
| 	int clk_ctl;
 | |
| 	struct clk *clk;
 | |
| 
 | |
| 	printk(KERN_INFO "msm_i2c_probe\n");
 | |
| 
 | |
| 	/* NOTE: driver uses the static register mapping */
 | |
| 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | |
| 	if (!mem) {
 | |
| 		dev_err(&pdev->dev, "no mem resource?\n");
 | |
| 		return -ENODEV;
 | |
| 	}
 | |
| 	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 | |
| 	if (!irq) {
 | |
| 		dev_err(&pdev->dev, "no irq resource?\n");
 | |
| 		return -ENODEV;
 | |
| 	}
 | |
| 
 | |
| 	ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1,
 | |
| 			pdev->name);
 | |
| 	if (!ioarea) {
 | |
| 		dev_err(&pdev->dev, "I2C region already claimed\n");
 | |
| 		return -EBUSY;
 | |
| 	}
 | |
| 	clk = clk_get(&pdev->dev, "i2c_clk");
 | |
| 	if (IS_ERR(clk)) {
 | |
| 		dev_err(&pdev->dev, "Could not get clock\n");
 | |
| 		ret = PTR_ERR(clk);
 | |
| 		goto err_clk_get_failed;
 | |
| 	}
 | |
| 
 | |
| 	dev = kzalloc(sizeof(struct msm_i2c_dev), GFP_KERNEL);
 | |
| 	if (!dev) {
 | |
| 		ret = -ENOMEM;
 | |
| 		goto err_alloc_dev_failed;
 | |
| 	}
 | |
| 
 | |
| 	dev->dev = &pdev->dev;
 | |
| 	dev->irq = irq->start;
 | |
| 	dev->clk = clk;
 | |
| 	dev->base = ioremap(mem->start, (mem->end - mem->start) + 1);
 | |
| 	if (!dev->base) {
 | |
| 		ret = -ENOMEM;
 | |
| 		goto err_ioremap_failed;
 | |
| 	}
 | |
| 
 | |
| 	spin_lock_init(&dev->lock);
 | |
| 	wake_lock_init(&dev->wakelock, WAKE_LOCK_SUSPEND, "i2c");
 | |
| 	platform_set_drvdata(pdev, dev);
 | |
| 
 | |
| 	if (pdata) {
 | |
| 		dev->clk_drv_str = pdata->clock_strength;
 | |
| 		dev->dat_drv_str = pdata->data_strength;
 | |
| 		if (pdata->i2c_clock < 100000 || pdata->i2c_clock > 400000)
 | |
| 			i2c_clock = 100000;
 | |
| 		else
 | |
| 			i2c_clock = pdata->i2c_clock;
 | |
| 	} else {
 | |
| 		dev->clk_drv_str = 0;
 | |
| 		dev->dat_drv_str = 0;
 | |
| 		i2c_clock = 100000;
 | |
| 		dev->skip_recover = 1;
 | |
| 	}
 | |
| 
 | |
| 	if (!dev->skip_recover)
 | |
| 		msm_set_i2c_mux(false, NULL, NULL,
 | |
| 			dev->clk_drv_str, dev->dat_drv_str);
 | |
| 
 | |
| 	clk_enable(clk);
 | |
| 
 | |
| 	/* I2C_HS_CLK = I2C_CLK/(3*(HS_DIVIDER_VALUE+1) */
 | |
| 	/* I2C_FS_CLK = I2C_CLK/(2*(FS_DIVIDER_VALUE+3) */
 | |
| 	/* FS_DIVIDER_VALUE = ((I2C_CLK / I2C_FS_CLK) / 2) - 3 */
 | |
| 	i2c_clk = 19200000; /* input clock */
 | |
| 	fs_div = ((i2c_clk / i2c_clock) / 2) - 3;
 | |
| 	hs_div = 3;
 | |
| 	clk_ctl = ((hs_div & 0x7) << 8) | (fs_div & 0xff);
 | |
| 	writel(clk_ctl, dev->base + I2C_CLK_CTL);
 | |
| 	printk(KERN_INFO "msm_i2c_probe: clk_ctl %x, %d Hz\n",
 | |
| 	       clk_ctl, i2c_clk / (2 * ((clk_ctl & 0xff) + 3)));
 | |
| 	clk_disable(clk);
 | |
| 
 | |
| 	i2c_set_adapdata(&dev->adapter, dev);
 | |
| 	dev->adapter.algo = &msm_i2c_algo;
 | |
| 	strncpy(dev->adapter.name,
 | |
| 		"MSM I2C adapter",
 | |
| 		sizeof(dev->adapter.name));
 | |
| 
 | |
| 	dev->adapter.nr = pdev->id;
 | |
| 	ret = i2c_add_numbered_adapter(&dev->adapter);
 | |
| 	if (ret) {
 | |
| 		dev_err(&pdev->dev, "i2c_add_adapter failed\n");
 | |
| 		goto err_i2c_add_adapter_failed;
 | |
| 	}
 | |
| 
 | |
| 	ret = request_irq(dev->irq, msm_i2c_interrupt,
 | |
| 			IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TIMER,
 | |
| 			pdev->name, dev);
 | |
| 	if (ret) {
 | |
| 		dev_err(&pdev->dev, "request_irq failed\n");
 | |
| 		goto err_request_irq_failed;
 | |
| 	}
 | |
| 	disable_irq(dev->irq);
 | |
| 	return 0;
 | |
| 
 | |
| /*	free_irq(dev->irq, dev); */
 | |
| err_request_irq_failed:
 | |
| 	i2c_del_adapter(&dev->adapter);
 | |
| err_i2c_add_adapter_failed:
 | |
| 	iounmap(dev->base);
 | |
| err_ioremap_failed:
 | |
| 	kfree(dev);
 | |
| err_alloc_dev_failed:
 | |
| 	clk_put(clk);
 | |
| err_clk_get_failed:
 | |
| 	release_mem_region(mem->start, (mem->end - mem->start) + 1);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| msm_i2c_remove(struct platform_device *pdev)
 | |
| {
 | |
| 	struct msm_i2c_dev	*dev = platform_get_drvdata(pdev);
 | |
| 	struct resource		*mem;
 | |
| 
 | |
| 	platform_set_drvdata(pdev, NULL);
 | |
| 	enable_irq(dev->irq);
 | |
| 	free_irq(dev->irq, dev);
 | |
| 	i2c_del_adapter(&dev->adapter);
 | |
| 	wake_lock_destroy(&dev->wakelock);
 | |
| 	clk_put(dev->clk);
 | |
| 	iounmap(dev->base);
 | |
| 	kfree(dev);
 | |
| 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | |
| 	release_mem_region(mem->start, (mem->end - mem->start) + 1);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int msm_i2c_suspend_noirq(struct device *device)
 | |
| {
 | |
| 	struct platform_device *pdev = to_platform_device(device);
 | |
| 	struct msm_i2c_dev *dev = platform_get_drvdata(pdev);
 | |
| 
 | |
| 	/* Block to allow any i2c_xfers to finish */
 | |
| 	i2c_lock_adapter(&dev->adapter);
 | |
| 	dev->is_suspended = true;
 | |
| 	i2c_unlock_adapter(&dev->adapter);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int msm_i2c_resume_noirq(struct device *device) {
 | |
| 	struct platform_device *pdev = to_platform_device(device);
 | |
| 	struct msm_i2c_dev *dev = platform_get_drvdata(pdev);
 | |
| 
 | |
| 	/* Block to allow any i2c_xfers to finish */
 | |
| 	i2c_lock_adapter(&dev->adapter);
 | |
| 	dev->is_suspended = false;
 | |
| 	i2c_unlock_adapter(&dev->adapter);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static struct dev_pm_ops msm_i2c_pm_ops = {
 | |
| 	.suspend_noirq	= msm_i2c_suspend_noirq,
 | |
| 	.resume_noirq	= msm_i2c_resume_noirq,
 | |
| };
 | |
| 
 | |
| static struct platform_driver msm_i2c_driver = {
 | |
| 	.probe		= msm_i2c_probe,
 | |
| 	.remove		= msm_i2c_remove,
 | |
| 	.driver		= {
 | |
| 		.name	= "msm_i2c",
 | |
| 		.owner	= THIS_MODULE,
 | |
| 		.pm = &msm_i2c_pm_ops,
 | |
| 	},
 | |
| };
 | |
| 
 | |
| /* I2C may be needed to bring up other drivers */
 | |
| static int __init
 | |
| msm_i2c_init_driver(void)
 | |
| {
 | |
| 	return platform_driver_register(&msm_i2c_driver);
 | |
| }
 | |
| subsys_initcall(msm_i2c_init_driver);
 | |
| 
 | |
| static void __exit msm_i2c_exit_driver(void)
 | |
| {
 | |
| 	platform_driver_unregister(&msm_i2c_driver);
 | |
| }
 | |
| module_exit(msm_i2c_exit_driver);
 | |
| 
 |