All of lore.kernel.org
 help / color / mirror / Atom feed
* "I2C_RDWR" ioctl returning EINVAL on Freescale mxc
@ 2009-08-14 21:09 alfred steele
       [not found] ` <528f13590908141409g4b2556e5m10aec0dc15429457-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  0 siblings, 1 reply; 8+ messages in thread
From: alfred steele @ 2009-08-14 21:09 UTC (permalink / raw)
  To: linux-i2c-u79uwXL29TY76Z2rM5mHXA

Hi all,

In my userspace driver for a temp sensor on a i2c bus, i am using the
/dev interface.  When i use plain read/write syscalls , they seem to
work just fine. But when i use a combined read/write using the IOCTL
"I2C_RDWR", i get a return of invalid argument i.e EINVAL from the
kernel space i2c dev/bus driver.

int rest  = ioctl(_fd, I2C_RDWR,  trans );

So basically res gets a value<1 on execution. I am putting in the
correct slave address. i have double checked on it. What could be the
other obvious reasons? Could endian ness be an issue as well although
the userspace is really invisible and if that had been the case i
would not have got acks from the slave for a i2c scan on the bus.
i checked the kernel bus driver code for the reason, but cannot figure
out just by reading the code at this point  as i have a broken h/w.
iam using the freescale  mxc i2c bus driver on the kernel side.

Any clues?
Thanks in advance.
-Alfred.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: "I2C_RDWR" ioctl returning EINVAL on Freescale mxc
       [not found] ` <528f13590908141409g4b2556e5m10aec0dc15429457-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2009-08-17  6:53   ` Robert Schwebel
       [not found]     ` <20090817065313.GJ31727-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
  0 siblings, 1 reply; 8+ messages in thread
From: Robert Schwebel @ 2009-08-17  6:53 UTC (permalink / raw)
  To: alfred steele; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA

Alfred,

On Fri, Aug 14, 2009 at 04:09:21PM -0500, alfred steele wrote:
> In my userspace driver for a temp sensor on a i2c bus, i am using the
> /dev interface.  When i use plain read/write syscalls , they seem to
> work just fine. But when i use a combined read/write using the IOCTL
> "I2C_RDWR", i get a return of invalid argument i.e EINVAL from the
> kernel space i2c dev/bus driver.
> 
> int rest  = ioctl(_fd, I2C_RDWR,  trans );
> 
> So basically res gets a value<1 on execution. I am putting in the
> correct slave address. i have double checked on it. What could be the
> other obvious reasons? Could endian ness be an issue as well although
> the userspace is really invisible and if that had been the case i
> would not have got acks from the slave for a i2c scan on the bus.
> i checked the kernel bus driver code for the reason, but cannot figure
> out just by reading the code at this point  as i have a broken h/w.
> iam using the freescale  mxc i2c bus driver on the kernel side.

Can you elaborate:

- which kernel version is that
- which i.MX derivate are you using?

rsc
-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: "I2C_RDWR" ioctl returning EINVAL on Freescale mxc
       [not found]     ` <20090817065313.GJ31727-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
@ 2009-08-17 14:51       ` alfred steele
       [not found]         ` <528f13590908170751u4dcf6d8ftb865103b888225e0-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  0 siblings, 1 reply; 8+ messages in thread
From: alfred steele @ 2009-08-17 14:51 UTC (permalink / raw)
  To: Robert Schwebel; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA

> - which kernel version is that
> - which i.MX derivate are you using?
>
2.6.24 , I am using mx31.

Thanks,
Alfred.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: "I2C_RDWR" ioctl returning EINVAL on Freescale mxc
       [not found]         ` <528f13590908170751u4dcf6d8ftb865103b888225e0-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2009-08-17 15:01           ` Robert Schwebel
       [not found]             ` <20090817150159.GT31727-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
  0 siblings, 1 reply; 8+ messages in thread
From: Robert Schwebel @ 2009-08-17 15:01 UTC (permalink / raw)
  To: alfred steele; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA

Alfred,

On Mon, Aug 17, 2009 at 09:51:11AM -0500, alfred steele wrote:
> > - which kernel version is that
> > - which i.MX derivate are you using?
>
> 2.6.24 , I am using mx31.

I assume you are using Freescale's patches ontop of 2.6.24.

Could you try the latest mxc-master from here:
http://git.pengutronix.de/?p=imx/linux-2.6.git

The i2c driver has seen a lot of modification recently.

rsc
-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: "I2C_RDWR" ioctl returning EINVAL on Freescale mxc
       [not found]             ` <20090817150159.GT31727-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
@ 2009-08-17 21:04               ` alfred steele
       [not found]                 ` <528f13590908171404q2aa4f9aaub98a191c91bd2448-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  0 siblings, 1 reply; 8+ messages in thread
From: alfred steele @ 2009-08-17 21:04 UTC (permalink / raw)
  To: Robert Schwebel; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA

On Mon, Aug 17, 2009 at 10:01 AM, Robert
Schwebel<r.schwebel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> wrote:
>
> Could you try the latest mxc-master from here:
> http://git.pengutronix.de/?p=imx/linux-2.6.git
I do not see any mx31/mxc specific i2c bus driver in the snapshot. Can
you point where its is?
Thanks & Regards,
Alfred

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: "I2C_RDWR" ioctl returning EINVAL on Freescale mxc
       [not found]                 ` <528f13590908171404q2aa4f9aaub98a191c91bd2448-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2009-08-17 21:21                   ` Robert Schwebel
       [not found]                     ` <20090817212127.GB31727-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
  0 siblings, 1 reply; 8+ messages in thread
From: Robert Schwebel @ 2009-08-17 21:21 UTC (permalink / raw)
  To: alfred steele; +Cc: Robert Schwebel, linux-i2c-u79uwXL29TY76Z2rM5mHXA

On Mon, Aug 17, 2009 at 04:04:00PM -0500, alfred steele wrote:
> > Could you try the latest mxc-master from here:
> > http://git.pengutronix.de/?p=imx/linux-2.6.git
>
> I do not see any mx31/mxc specific i2c bus driver in the snapshot. Can
> you point where its is?

drivers/i2c/busses/i2c-imx.{c,ko}

rsc
-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: "I2C_RDWR" ioctl returning EINVAL on Freescale mxc
       [not found]                     ` <20090817212127.GB31727-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
@ 2009-08-18  3:25                       ` alfred steele
       [not found]                         ` <528f13590908172025i276dc634qd0829cfad8c8c608-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  0 siblings, 1 reply; 8+ messages in thread
From: alfred steele @ 2009-08-18  3:25 UTC (permalink / raw)
  To: Robert Schwebel; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA

Hi Robert,
> drivers/i2c/busses/i2c-imx.{c,ko}
Thanks for your prompt responses and help!
I skimmed through the imx-i2c.c and ompared it to the  driver i have
mxc-i2c.  I do not understand how the changes might affect the IOCTL
"I2C_RDWR".Can you please elaborate? At the bottom is the mxc-i2c
driver i have.
Isn't  i2c-dev "i2c-dev.c" driver responsible for returning EINVAL
based on some validation checks. Has that also changed in the latest
GIT.
Please suggest.


===============================================================================================================================
/*
 * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

/*!
 * @file mxc_i2c.c
 *
 * @brief Driver for the Freescale Semiconductor MXC I2C buses.
 *
 * Based on i2c driver algorithm for PCF8584 adapters
 *
 * @ingroup MXCI2C
 */

/*
 * Include Files
 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/clk.h>
#include <asm/irq.h>
#include <asm/io.h>
#include "mxc_i2c_reg.h"

/*!
 * In case the MXC device has multiple I2C modules, this structure is used to
 * store information specific to each I2C module.
 */
typedef struct {
	/*!
	 * This structure is used to identify the physical i2c bus along with
	 * the access algorithms necessary to access it.
	 */
	struct i2c_adapter adap;

	/*!
	 * This waitqueue is used to wait for the data transfer to complete.
	 */
	wait_queue_head_t wq;

	/*!
	 * The base address of the I2C device.
	 */
	unsigned long membase;

	/*!
	 * The interrupt number used by the I2C device.
	 */
	int irq;

	/*!
	 * The default clock divider value to be used.
	 */
	unsigned int clkdiv;

	/*!
	 * The clock source for the device.
	 */
	struct clk *clk;

	/*!
	 * The current power state of the device
	 */
	bool low_power;
} mxc_i2c_device;

/*!
 * Boolean to indicate if data was transferred
 */
static bool transfer_done = false;

/*!
 * Boolean to indicate if we received an ACK for the data transmitted
 */
static bool tx_success = false;

struct clk_div_table {
	int reg_value;
	int div;
};

static const struct clk_div_table i2c_clk_table[] = {
	{0x20, 22}, {0x21, 24}, {0x22, 26}, {0x23, 28},
	{0, 30}, {1, 32}, {0x24, 32}, {2, 36},
	{0x25, 36}, {0x26, 40}, {3, 42}, {0x27, 44},
	{4, 48}, {0x28, 48}, {5, 52}, {0x29, 56},
	{6, 60}, {0x2A, 64}, {7, 72}, {0x2B, 72},
	{8, 80}, {0x2C, 80}, {9, 88}, {0x2D, 96},
	{0xA, 104}, {0x2E, 112}, {0xB, 128}, {0x2F, 128},
	{0xC, 144}, {0xD, 160}, {0x30, 160}, {0xE, 192},
	{0x31, 192}, {0x32, 224}, {0xF, 240}, {0x33, 256},
	{0x10, 288}, {0x11, 320}, {0x34, 320}, {0x12, 384},
	{0x35, 384}, {0x36, 448}, {0x13, 480}, {0x37, 512},
	{0x14, 576}, {0x15, 640}, {0x38, 640}, {0x16, 768},
	{0x39, 768}, {0x3A, 896}, {0x17, 960}, {0x3B, 1024},
	{0x18, 1152}, {0x19, 1280}, {0x3C, 1280}, {0x1A, 1536},
	{0x3D, 1536}, {0x3E, 1792}, {0x1B, 1920}, {0x3F, 2048},
	{0x1C, 2304}, {0x1D, 2560}, {0x1E, 3072}, {0x1F, 3840},
	{0, 0}
};

extern void gpio_i2c_active(int i2c_num);
extern void gpio_i2c_inactive(int i2c_num);

/*!
 * Transmit a \b STOP signal to the slave device.
 *
 * @param   dev   the mxc i2c structure used to get to the right i2c device
 */
static void mxc_i2c_stop(mxc_i2c_device * dev)
{
	unsigned int cr, sr;
	int retry = 16;
	spinlock_t lock;

	spin_lock(&lock);
	cr = readw(dev->membase + MXC_I2CR);
	cr &= ~(MXC_I2CR_MSTA | MXC_I2CR_MTX);
	writew(cr, dev->membase + MXC_I2CR);

	/* Wait till the Bus Busy bit is reset */
	sr = readw(dev->membase + MXC_I2SR);
	while (retry-- && ((sr & MXC_I2SR_IBB))) {
		udelay(3);
		sr = readw(dev->membase + MXC_I2SR);
	}
	spin_unlock(&lock);
	if (retry <= 0)
		printk(KERN_DEBUG "Could not set I2C Bus Busy bit to zero.\n");

}

/*!
 * Wait for the transmission of the data byte to complete. This function waits
 * till we get a signal from the interrupt service routine indicating completion
 * of the address cycle or we time out.
 *
 * @param   dev         the mxc i2c structure used to get to the right
i2c device
 * @param   trans_flag  transfer flag
 *
 *
 * @return  The function returns 0 on success or -1 if an ack was not received
 */

static int mxc_i2c_wait_for_tc(mxc_i2c_device * dev, int trans_flag)
{
	int retry = 16;

	while (retry-- && !transfer_done) {
		wait_event_interruptible_timeout(dev->wq,
						 transfer_done,
						 dev->adap.timeout);
	}
	transfer_done = false;

	if (retry <= 0) {
		/* Unable to send data */
		printk(KERN_DEBUG "Data not transmitted\n");
		return -1;
	}

	if (!tx_success) {
		/* An ACK was not received for transmitted byte */
		printk(KERN_DEBUG "ACK not received \n");
		return -1;
	}

	return 0;
}

/*!
 * Transmit a \b START signal to the slave device.
 *
 * @param   dev   the mxc i2c structure used to get to the right i2c device
 * @param   *msg  pointer to a message structure that contains the slave
 *                address
 *
 * @return  The function returns EBUSY on failure, 0 on success.
 */
static int mxc_i2c_start(mxc_i2c_device *dev, struct i2c_msg *msg)
{
	volatile unsigned int cr, sr;
	unsigned int addr_trans;
	int retry = 16;

	/*
	 * Set the slave address and the requested transfer mode
	 * in the data register
	 */
	addr_trans = msg->addr << 1;
	if (msg->flags & I2C_M_RD) {
		addr_trans |= 0x01;
	}

	/* Set the Master bit */
	cr = readw(dev->membase + MXC_I2CR);
	cr |= MXC_I2CR_MSTA;
	writew(cr, dev->membase + MXC_I2CR);

	/* Wait till the Bus Busy bit is set */
	sr = readw(dev->membase + MXC_I2SR);
	while (retry-- && (!(sr & MXC_I2SR_IBB))) {
		udelay(3);
		sr = readw(dev->membase + MXC_I2SR);
	}
	if (retry <= 0) {
		printk(KERN_DEBUG "Could not grab Bus ownership\n");
		return -EBUSY;
	}

	/* Set the Transmit bit */
	cr = readw(dev->membase + MXC_I2CR);
	cr |= MXC_I2CR_MTX;
	writew(cr, dev->membase + MXC_I2CR);

	writew(addr_trans, dev->membase + MXC_I2DR);
	return 0;
}

/*!
 * Transmit a \b REPEAT START to the slave device
 *
 * @param   dev   the mxc i2c structure used to get to the right i2c device
 * @param   *msg  pointer to a message structure that contains the slave
 *                address
 */
static void mxc_i2c_repstart(mxc_i2c_device * dev, struct i2c_msg *msg)
{
	volatile unsigned int cr;
	unsigned int addr_trans;

	/*
	 * Set the slave address and the requested transfer mode
	 * in the data register
	 */
	addr_trans = msg->addr << 1;
	if (msg->flags & I2C_M_RD) {
		addr_trans |= 0x01;
	}
	cr = readw(dev->membase + MXC_I2CR);
	cr |= MXC_I2CR_RSTA;
	writew(cr, dev->membase + MXC_I2CR);
	udelay(3);
	writew(addr_trans, dev->membase + MXC_I2DR);
}

/*!
 * Read the received data. The function waits till data is available or times
 * out. Generates a stop signal if this is the last message to be received.
 * Sends an ack for all the bytes received except the last byte.
 *
 * @param  dev       the mxc i2c structure used to get to the right i2c device
 * @param  *msg      pointer to a message structure that contains the slave
 *                   address and a pointer to the receive buffer
 * @param  last      indicates that this is the last message to be received
 * @param  addr_comp flag indicates that we just finished the address cycle
 *
 * @return  The function returns the number of bytes read or -1 on time out.
 */
static int mxc_i2c_readbytes(mxc_i2c_device * dev, struct i2c_msg *msg,
			     int last, int addr_comp)
{
	int i;
	char *buf = msg->buf;
	int len = msg->len;
	volatile unsigned int cr;

	cr = readw(dev->membase + MXC_I2CR);
	/*
	 * Clear MTX to switch to receive mode.
	 */
	cr &= ~MXC_I2CR_MTX;
	/*
	 * Clear the TXAK bit to gen an ack when receiving only one byte.
	 */
	if (len == 1) {
		cr |= MXC_I2CR_TXAK;
	} else {
		cr &= ~MXC_I2CR_TXAK;
	}
	writew(cr, dev->membase + MXC_I2CR);
	/*
	 * Dummy read only at the end of an address cycle
	 */
	if (addr_comp > 0) {
		readw(dev->membase + MXC_I2DR);
	}

	for (i = 0; i < len; i++) {
		/*
		 * Wait for data transmission to complete
		 */
		if (mxc_i2c_wait_for_tc(dev, msg->flags)) {
			mxc_i2c_stop(dev);
			return -1;
		}
		/*
		 * Do not generate an ACK for the last byte
		 */
		if (i == (len - 2)) {
			cr = readw(dev->membase + MXC_I2CR);
			cr |= MXC_I2CR_TXAK;
			writew(cr, dev->membase + MXC_I2CR);
		} else if (i == (len - 1)) {
			if (last) {
				mxc_i2c_stop(dev);
			}
		}
		/* Read the data */
		*buf++ = readw(dev->membase + MXC_I2DR);
	}

	return i;
}

/*!
 * Write the data to the data register. Generates a stop signal if this is
 * the last message to be sent or if no ack was received for the data sent.
 *
 * @param   dev   the mxc i2c structure used to get to the right i2c device
 * @param   *msg  pointer to a message structure that contains the slave
 *                address and data to be sent
 * @param   last  indicates that this is the last message to be received
 *
 * @return  The function returns the number of bytes written or -1 on time out
 *          or if no ack was received for the data that was sent.
 */
static int mxc_i2c_writebytes(mxc_i2c_device * dev, struct i2c_msg *msg,
			      int last)
{
	int i;
	char *buf = msg->buf;
	int len = msg->len;
	volatile unsigned int cr;

	cr = readw(dev->membase + MXC_I2CR);
	/* Set MTX to switch to transmit mode */
	cr |= MXC_I2CR_MTX;
	writew(cr, dev->membase + MXC_I2CR);

	for (i = 0; i < len; i++) {
		/*
		 * Write the data
		 */
		writew(*buf++, dev->membase + MXC_I2DR);
		if (mxc_i2c_wait_for_tc(dev, msg->flags)) {
			mxc_i2c_stop(dev);
			return -1;
		}
	}
	if (last > 0) {
		mxc_i2c_stop(dev);
	}

	return i;
}

/*!
 * Function enables the I2C module and initializes the registers.
 *
 * @param   dev   the mxc i2c structure used to get to the right i2c device
 * @param   trans_flag  transfer flag
 */
static void mxc_i2c_module_en(mxc_i2c_device * dev, int trans_flag)
{
	clk_enable(dev->clk);
	/* Set the frequency divider */
	writew(dev->clkdiv, dev->membase + MXC_IFDR);
	/* Clear the status register */
	writew(0x0, dev->membase + MXC_I2SR);
	/* Enable I2C and its interrupts */
	writew(MXC_I2CR_IEN, dev->membase + MXC_I2CR);
	writew(MXC_I2CR_IEN | MXC_I2CR_IIEN, dev->membase + MXC_I2CR);
}

/*!
 * Disables the I2C module.
 *
 * @param   dev   the mxc i2c structure used to get to the right i2c device
 */
static void mxc_i2c_module_dis(mxc_i2c_device * dev)
{
	writew(0x0, dev->membase + MXC_I2CR);
	clk_disable(dev->clk);
}

/*!
 * The function is registered in the adapter structure. It is called when an MXC
 * driver wishes to transfer data to a device connected to the I2C device.
 *
 * @param   adap   adapter structure for the MXC i2c device
 * @param   msgs[] array of messages to be transferred to the device
 * @param   num    number of messages to be transferred to the device
 *
 * @return  The function returns the number of messages transferred,
 *          \b -EREMOTEIO on I2C failure and a 0 if the num argument is
 *          less than 0.
 */
static int mxc_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
			int num)
{
	mxc_i2c_device *dev = (mxc_i2c_device *) (i2c_get_adapdata(adap));
	int i, ret = 0, addr_comp = 0;
	volatile unsigned int sr;

	if (dev->low_power) {
		printk(KERN_ERR "I2C Device in low power mode\n");
		return -EREMOTEIO;
	}

	if (num < 1) {
		return 0;
	}

	mxc_i2c_module_en(dev, msgs[0].flags);
	sr = readw(dev->membase + MXC_I2SR);
	/*
	 * Check bus state
	 */
	if (sr & MXC_I2SR_IBB) {
		mxc_i2c_module_dis(dev);
		printk(KERN_DEBUG "Bus busy\n");
		return -EREMOTEIO;
	}
	//gpio_i2c_active(dev->adap.id);
	transfer_done = false;
	tx_success = false;
	for (i = 0; i < num && ret >= 0; i++) {
		addr_comp = 0;
		/*
		 * Send the slave address and transfer direction in the
		 * address cycle
		 */
		if (i == 0) {
			/*
			 * Send a start or repeat start signal
			 */
			if (mxc_i2c_start(dev, &msgs[0]))
				return -EREMOTEIO;
			/* Wait for the address cycle to complete */
			if (mxc_i2c_wait_for_tc(dev, msgs[0].flags)) {
				mxc_i2c_stop(dev);
				//gpio_i2c_inactive(dev->adap.id);
				mxc_i2c_module_dis(dev);
				return -EREMOTEIO;
			}
			addr_comp = 1;
		} else {
			/*
			 * Generate repeat start only if required i.e the address
			 * changed or the transfer direction changed
			 */
			if ((msgs[i].addr != msgs[i - 1].addr) ||
			    ((msgs[i].flags & I2C_M_RD) !=
			     (msgs[i - 1].flags & I2C_M_RD))) {
				mxc_i2c_repstart(dev, &msgs[i]);
				/* Wait for the address cycle to complete */
				if (mxc_i2c_wait_for_tc(dev, msgs[i].flags)) {
					mxc_i2c_stop(dev);
					//gpio_i2c_inactive(dev->adap.id);
					mxc_i2c_module_dis(dev);
					return -EREMOTEIO;
				}
				addr_comp = 1;
			}
		}

		/* Transfer the data */
		if (msgs[i].flags & I2C_M_RD) {
			/* Read the data */
			ret = mxc_i2c_readbytes(dev, &msgs[i], (i + 1 == num),
						addr_comp);
			if (ret < 0) {
				printk(KERN_ERR "mxc_i2c_readbytes: fail.\n");
				break;
			}
		} else {
			/* Write the data */
			ret = mxc_i2c_writebytes(dev, &msgs[i], (i + 1 == num));
			if (ret < 0) {
				printk(KERN_ERR "mxc_i2c_writebytes: fail.\n");
				break;
			}
		}
	}

	//gpio_i2c_inactive(dev->adap.id);
	mxc_i2c_module_dis(dev);
	/*
	 * Decrease by 1 as we do not want Start message to be included in
	 * the count
	 */
	return (i - 1);
}

/*!
 * Returns the i2c functionality supported by this driver.
 *
 * @param   adap adapter structure for this i2c device
 *
 * @return Returns the functionality that is supported.
 */
static u32 mxc_i2c_func(struct i2c_adapter *adap)
{
	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}

/*!
 * Stores the pointers for the i2c algorithm functions. The algorithm functions
 * is used by the i2c bus driver to talk to the i2c bus
 */
static struct i2c_algorithm mxc_i2c_algorithm = {
	.master_xfer = mxc_i2c_xfer,
	.functionality = mxc_i2c_func
};

/*!
 * Interrupt Service Routine. It signals to the process about the data transfer
 * completion. Also sets a flag if bus arbitration is lost.
 * @param   irq    the interrupt number
 * @param   dev_id driver private data
 *
 * @return  The function returns \b IRQ_HANDLED.
 */
static irqreturn_t mxc_i2c_handler(int irq, void *dev_id)
{
	mxc_i2c_device *dev = dev_id;
	volatile unsigned int sr, cr;

	sr = readw(dev->membase + MXC_I2SR);
	cr = readw(dev->membase + MXC_I2CR);

	/*
	 * Clear the interrupt bit
	 */
	writew(0x0, dev->membase + MXC_I2SR);

	if (sr & MXC_I2SR_IAL) {
		printk(KERN_DEBUG "Bus Arbitration lost\n");
	} else {
		/* Interrupt due byte transfer completion */
		tx_success = true;
		/* Check if RXAK is received in Transmit mode */
		if ((cr & MXC_I2CR_MTX) && (sr & MXC_I2SR_RXAK)) {
			tx_success = false;
		}
		transfer_done = true;
		wake_up_interruptible(&dev->wq);
	}

	return IRQ_HANDLED;
}

/*!
 * This function is called to put the I2C adapter in a low power
state. Refer to the
 * document driver-model/driver.txt in the kernel source tree for more
 * information.
 *
 * @param   pdev  the device structure used to give information on which I2C
 *                to suspend
 * @param   state the power state the device is entering
 *
 * @return  The function returns 0 on success and -1 on failure.
 */
static int mxci2c_suspend(struct platform_device *pdev, pm_message_t state)
{
	mxc_i2c_device *mxcdev = platform_get_drvdata(pdev);
	volatile unsigned int sr = 0;

	if (mxcdev == NULL) {
		return -1;
	}

	/* Prevent further calls to be processed */
	mxcdev->low_power = true;
	/* Wait till we finish the current transfer */
	sr = readw(mxcdev->membase + MXC_I2SR);
	while (sr & MXC_I2SR_IBB) {
		msleep(10);
		sr = readw(mxcdev->membase + MXC_I2SR);
	}
	gpio_i2c_inactive(mxcdev->adap.id);

	return 0;
}

/*!
 * This function is called to bring the I2C adapter back from a low
power state. Refer
 * to the document driver-model/driver.txt in the kernel source tree for more
 * information.
 *
 * @param   pdev  the device structure used to give information on which I2C
 *                to resume
 *
 * @return  The function returns 0 on success and -1 on failure
 */
static int mxci2c_resume(struct platform_device *pdev)
{
	mxc_i2c_device *mxcdev = platform_get_drvdata(pdev);

	if (mxcdev == NULL)
		return -1;

	mxcdev->low_power = false;
	gpio_i2c_active(mxcdev->adap.id);

	return 0;
}

/*!
 * This function is called during the driver binding process.
 *
 * @param   pdev  the device structure used to store device specific
 *                information that is used by the suspend, resume and remove
 *                functions
 *
 * @return  The function always returns 0.
 */
static int mxci2c_probe(struct platform_device *pdev)
{
	mxc_i2c_device *mxc_i2c;
	struct mxc_i2c_platform_data *i2c_plat_data = pdev->dev.platform_data;
	struct resource *res;
	int id = pdev->id;
	u32 clk_freq;
	int ret = 0;
	int i;

	mxc_i2c = kzalloc(sizeof(mxc_i2c_device), GFP_KERNEL);
	if (!mxc_i2c) {
		return -ENOMEM;
	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (res == NULL) {
		ret = -ENODEV;
		goto err1;
	}
	mxc_i2c->membase = IO_ADDRESS(res->start);

	/*
	 * Request the I2C interrupt
	 */
	mxc_i2c->irq = platform_get_irq(pdev, 0);
	if (mxc_i2c->irq < 0) {
		ret = mxc_i2c->irq;
		goto err1;
	}

	ret = request_irq(mxc_i2c->irq, mxc_i2c_handler,
			  0, pdev->name, mxc_i2c);
	if (ret < 0) {
		goto err1;
	}

	init_waitqueue_head(&mxc_i2c->wq);

	mxc_i2c->low_power = false;

	gpio_i2c_active(id);

	mxc_i2c->clk = clk_get(&pdev->dev, "i2c_clk");
	clk_freq = clk_get_rate(mxc_i2c->clk);
	mxc_i2c->clkdiv = -1;
	if (i2c_plat_data->i2c_clk) {
		/* Calculate divider and round up any fractional part */
		int div = (clk_freq + i2c_plat_data->i2c_clk - 1) /
		    i2c_plat_data->i2c_clk;
		for (i = 0; i2c_clk_table[i].div != 0; i++) {
			if (i2c_clk_table[i].div >= div) {
				mxc_i2c->clkdiv = i2c_clk_table[i].reg_value;
				break;
			}
		}
	}
	if (mxc_i2c->clkdiv == -1) {
		i--;
		mxc_i2c->clkdiv = 0x1F;	/* Use max divider */
	}
	dev_dbg(&pdev->dev, "i2c speed is %d/%d = %d bps, reg val = 0x%02X\n",
		clk_freq, i2c_clk_table[i].div,
		clk_freq / i2c_clk_table[i].div, mxc_i2c->clkdiv);

	/*
	 * Set the adapter information
	 */
	strcpy(mxc_i2c->adap.name, pdev->name);
	mxc_i2c->adap.id = mxc_i2c->adap.nr = id;
	mxc_i2c->adap.algo = &mxc_i2c_algorithm;
	mxc_i2c->adap.timeout = 1;
	platform_set_drvdata(pdev, mxc_i2c);
	i2c_set_adapdata(&mxc_i2c->adap, mxc_i2c);
	if ((ret = i2c_add_numbered_adapter(&mxc_i2c->adap)) < 0) {
		goto err2;
	}

	printk(KERN_INFO "MXC I2C driver\n");
	return 0;

      err2:
	free_irq(mxc_i2c->irq, mxc_i2c);
	gpio_i2c_inactive(id);
      err1:
	dev_err(&pdev->dev, "failed to probe i2c adapter\n");
	kfree(mxc_i2c);
	return ret;
}

/*!
 * Dissociates the driver from the I2C device.
 *
 * @param   pdev   the device structure used to give information on which I2C
 *                to remove
 *
 * @return  The function always returns 0.
 */
static int mxci2c_remove(struct platform_device *pdev)
{
	mxc_i2c_device *mxc_i2c = platform_get_drvdata(pdev);
	int id = pdev->id;

	free_irq(mxc_i2c->irq, mxc_i2c);
	i2c_del_adapter(&mxc_i2c->adap);
	gpio_i2c_inactive(id);
	clk_put(mxc_i2c->clk);
	platform_set_drvdata(pdev, NULL);
	return 0;
}

/*!
 * This structure contains pointers to the power management callback functions.
 */
static struct platform_driver mxci2c_driver = {
	.driver = {
		   .name = "mxc_i2c",
		   .owner = THIS_MODULE,
		   },
	.probe = mxci2c_probe,
	.remove = mxci2c_remove,
	.suspend = mxci2c_suspend,
	.resume = mxci2c_resume,
};

/*!
 * Function requests the interrupts and registers the i2c adapter structures.
 *
 * @return The function returns 0 on success and a non-zero value on failure.
 */
static int __init mxc_i2c_init(void)
{
	/* Register the device driver structure. */
	return platform_driver_register(&mxci2c_driver);
}

/*!
 * This function is used to cleanup all resources before the driver exits.
 */
static void __exit mxc_i2c_exit(void)
{
	platform_driver_unregister(&mxci2c_driver);
}

subsys_initcall(mxc_i2c_init);
module_exit(mxc_i2c_exit);

MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("MXC I2C driver");
MODULE_LICENSE("GPL");
===============================================================================================================================

Thanks & Regards,
ALfred.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: "I2C_RDWR" ioctl returning EINVAL on Freescale mxc
       [not found]                         ` <528f13590908172025i276dc634qd0829cfad8c8c608-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2009-08-18  6:13                           ` Robert Schwebel
  0 siblings, 0 replies; 8+ messages in thread
From: Robert Schwebel @ 2009-08-18  6:13 UTC (permalink / raw)
  To: alfred steele; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA

Alfred,

On Mon, Aug 17, 2009 at 10:25:34PM -0500, alfred steele wrote:
> I skimmed through the imx-i2c.c and ompared it to the  driver i have
> mxc-i2c.  I do not understand how the changes might affect the IOCTL
> "I2C_RDWR".Can you please elaborate? At the bottom is the mxc-i2c
> driver i have.
> Isn't  i2c-dev "i2c-dev.c" driver responsible for returning EINVAL
> based on some validation checks. Has that also changed in the latest
> GIT.

Between 2.6.24 and 2.6.31-dirty are about 156849 patches, not to mention
what Freescale may have patched in your kernel. It might effect your
problem or it might not.

Could you test against mxc-master? If you do, others in the community
can reproduce your problem quickly and might find a fix.

rsc
-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2009-08-18  6:13 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-08-14 21:09 "I2C_RDWR" ioctl returning EINVAL on Freescale mxc alfred steele
     [not found] ` <528f13590908141409g4b2556e5m10aec0dc15429457-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2009-08-17  6:53   ` Robert Schwebel
     [not found]     ` <20090817065313.GJ31727-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2009-08-17 14:51       ` alfred steele
     [not found]         ` <528f13590908170751u4dcf6d8ftb865103b888225e0-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2009-08-17 15:01           ` Robert Schwebel
     [not found]             ` <20090817150159.GT31727-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2009-08-17 21:04               ` alfred steele
     [not found]                 ` <528f13590908171404q2aa4f9aaub98a191c91bd2448-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2009-08-17 21:21                   ` Robert Schwebel
     [not found]                     ` <20090817212127.GB31727-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2009-08-18  3:25                       ` alfred steele
     [not found]                         ` <528f13590908172025i276dc634qd0829cfad8c8c608-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2009-08-18  6:13                           ` Robert Schwebel

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.