631 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			631 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;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
msm_i2c_poll_notbusy(struct msm_i2c_dev *dev, int warn)
 | 
						|
{
 | 
						|
	uint32_t retries = 0;
 | 
						|
 | 
						|
	while (retries != 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\n");
 | 
						|
	return -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;
 | 
						|
	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);
 | 
						|
	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);
 | 
						|
	if (msm_i2c_poll_notbusy(dev, 0))  /* Read may not have stopped in time */
 | 
						|
		dev_err(dev->dev, "Still busy after xfer completion (%02X)\n",
 | 
						|
			msgs->addr);
 | 
						|
	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 (!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);
 | 
						|
 |