/*
 * Prepare controller for a transaction and call omap_i2c_xfer_msg
 * to do the work during IRQ processing.
 */
static int
omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
	struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
	int i;
	int r;
	struct platform_device *pdev;
	struct omap_i2c_bus_platform_data *pdata;

	pdev = container_of(dev->dev, struct platform_device, dev);
	pdata = pdev->dev.platform_data;

	/*
	 * hwspinlock is used to time share the I2C module between A9 and Ducati
	 * on OMAP4. To avoid spurious IRQ due to I2C transaction initiated on
	 * Ducati sub system I2C IRQ is enabled and disabled on i2c transfers.
	 */

	omap_i2c_hwspinlock_lock(dev);

	spin_lock(&dev->dpll_lock);
	if (dev->dpll_entry == 1) {
		dev->dpll_entry = 0;
		/*
		 * FIXME: Speed > 1000 can not be supported
		 * in DPLL cascading mode.
		 */
		if (dev->speed > 1000)
			return -1;
		omap_i2c_dpll_configure(dev, pdata, OMAP_I2C_DPLL_CLOCK / 1000);

	} else if (dev->dpll_exit == 1) {
		dev->dpll_exit = 0;
		omap_i2c_dpll_configure(dev, pdata, dev->i2c_fclk_rate);
	}
	spin_unlock(&dev->dpll_lock);

	omap_i2c_unidle(dev);
	enable_irq(dev->irq);

	r = omap_i2c_wait_for_bb(dev);
	if (r < 0)
		goto out;

	for (i = 0; i < num; i++) {
		r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
		if (r != 0)
			break;
	}

	if (r == 0)
		r = num;
out:
	disable_irq_nosync(dev->irq);
	omap_i2c_idle(dev);
	omap_i2c_hwspinlock_unlock(dev);
	return r;
}
示例#2
0
/*
 * Prepare controller for a transaction and call omap_i2c_xfer_msg
 * to do the work during IRQ processing.
 */
static int
omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
	struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
	int i;
	int r;
#ifdef CONFIG_OMAP4_DPLL_CASCADING
	struct platform_device *pdev;
	struct omap_i2c_bus_platform_data *pdata;
#endif

	if (dev == NULL)
		return -EINVAL;

#ifdef CONFIG_OMAP4_DPLL_CASCADING
	pdev = container_of(dev->dev, struct platform_device, dev);
	pdata = pdev->dev.platform_data;
#endif

	if (dev->shutdown)
		return -EPERM;

	r = omap_i2c_hwspinlock_lock(dev);
	/* To-Do: if we are unable to acquire the lock, we must
	try to recover somehow */
	if (r != 0)
		return r;
        dev->got_lock = true;

	r = omap_i2c_unidle(dev);
	if ((r < 0) && (r != -ETIMEDOUT))
		goto out_unlock;

        /* We have the bus, enable IRQ */
        enable_irq(dev->irq);

	r = omap_i2c_wait_for_bb(dev);
	if (r < 0)
		r = omap_i2c_bus_clear(dev);
	if (r < 0)
		goto out;

	/*
	 * When waiting for completion of a i2c transfer, we need to
	 * set a wake up latency constraint for the MPU. This is to
	 * ensure quick enough wakeup from idle, when transfer
	 * completes.
	 */
	if (dev->pm_qos)
		pm_qos_update_request(dev->pm_qos, dev->latency);

#ifdef CONFIG_OMAP4_DPLL_CASCADING
	spin_lock(&dev->dpll_lock);
	if (dev->dpll_entry == 1) {
		dev->dpll_entry = 0;
		/*
		 * FIXME: Speed > 1000 can not be supported
		 * in DPLL cascading mode.
		 */
		if (dev->speed > 1000) {
			spin_unlock(&dev->dpll_lock);
			return -1;
		}
		omap_i2c_dpll_configure(dev, pdata, OMAP_I2C_DPLL_CLOCK / 1000);

	} else if (dev->dpll_exit == 1) {
		dev->dpll_exit = 0;
		omap_i2c_dpll_configure(dev, pdata, dev->i2c_fclk_rate);
	}
	spin_unlock(&dev->dpll_lock);
#endif
	for (i = 0; i < num; i++) {
		r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
		if (r != 0)
			break;
	}

	if (dev->pm_qos)
		pm_qos_update_request(dev->pm_qos, PM_QOS_DEFAULT_VALUE);

	if (r == 0)
		r = num;

	omap_i2c_wait_for_bb(dev);
out:
	disable_irq(dev->irq);
	omap_i2c_idle(dev);
out_unlock:
	omap_i2c_hwspinlock_unlock(dev);
	return r;
}