Skip to content

Commit

Permalink
i2c-pxa: Add polling transfer
Browse files Browse the repository at this point in the history
Add polling I2C transfer implementation for PXA I2C. This is needed
for cases where I2C transactions have to occur at times interrups are
disabled.

Signed-off-by: Mike Rapoport <mike@compulab.co.il>
Acked-by: eric miao <eric.miao@marvell.com>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
  • Loading branch information
Mike Rapoport authored and Jean Delvare committed Jan 27, 2008
1 parent cea443a commit b7a3670
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 12 deletions.
6 changes: 6 additions & 0 deletions arch/arm/mach-pxa/pxa27x.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <asm/arch/ohci.h>
#include <asm/arch/pm.h>
#include <asm/arch/dma.h>
#include <asm/arch/i2c.h>

#include "generic.h"
#include "devices.h"
Expand Down Expand Up @@ -423,6 +424,11 @@ struct platform_device pxa27x_device_i2c_power = {
.num_resources = ARRAY_SIZE(i2c_power_resources),
};

void __init pxa_set_i2c_power_info(struct i2c_pxa_platform_data *info)
{
pxa27x_device_i2c_power.dev.platform_data = info;
}

static struct platform_device *devices[] __initdata = {
&pxa_device_mci,
&pxa_device_udc,
Expand Down
133 changes: 121 additions & 12 deletions drivers/i2c/busses/i2c-pxa.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ struct pxa_i2c {
unsigned long iosize;

int irq;
int use_pio;
};

#define _IBMR(i2c) ((i2c)->reg_base + 0)
Expand Down Expand Up @@ -163,6 +164,7 @@ static void i2c_pxa_show_state(struct pxa_i2c *i2c, int lno, const char *fname)
#define eedbg(lvl, x...) do { if ((lvl) < 1) { printk(KERN_DEBUG "" x); } } while(0)

static void i2c_pxa_master_complete(struct pxa_i2c *i2c, int ret);
static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id);

static void i2c_pxa_scream_blue_murder(struct pxa_i2c *i2c, const char *why)
{
Expand Down Expand Up @@ -554,6 +556,71 @@ static inline void i2c_pxa_stop_message(struct pxa_i2c *i2c)
writel(icr, _ICR(i2c));
}

static int i2c_pxa_pio_set_master(struct pxa_i2c *i2c)
{
/* make timeout the same as for interrupt based functions */
long timeout = 2 * DEF_TIMEOUT;

/*
* Wait for the bus to become free.
*/
while (timeout-- && readl(_ISR(i2c)) & (ISR_IBB | ISR_UB)) {
udelay(1000);
show_state(i2c);
}

if (timeout <= 0) {
show_state(i2c);
dev_err(&i2c->adap.dev,
"i2c_pxa: timeout waiting for bus free\n");
return I2C_RETRY;
}

/*
* Set master mode.
*/
writel(readl(_ICR(i2c)) | ICR_SCLE, _ICR(i2c));

return 0;
}

static int i2c_pxa_do_pio_xfer(struct pxa_i2c *i2c,
struct i2c_msg *msg, int num)
{
unsigned long timeout = 500000; /* 5 seconds */
int ret = 0;

ret = i2c_pxa_pio_set_master(i2c);
if (ret)
goto out;

i2c->msg = msg;
i2c->msg_num = num;
i2c->msg_idx = 0;
i2c->msg_ptr = 0;
i2c->irqlogidx = 0;

i2c_pxa_start_message(i2c);

while (timeout-- && i2c->msg_num > 0) {
i2c_pxa_handler(0, i2c);
udelay(10);
}

i2c_pxa_stop_message(i2c);

/*
* We place the return code in i2c->msg_idx.
*/
ret = i2c->msg_idx;

out:
if (timeout == 0)
i2c_pxa_scream_blue_murder(i2c, "timeout");

return ret;
}

/*
* We are protected by the adapter bus mutex.
*/
Expand Down Expand Up @@ -610,6 +677,35 @@ static int i2c_pxa_do_xfer(struct pxa_i2c *i2c, struct i2c_msg *msg, int num)
return ret;
}

static int i2c_pxa_pio_xfer(struct i2c_adapter *adap,
struct i2c_msg msgs[], int num)
{
struct pxa_i2c *i2c = adap->algo_data;
int ret, i;

/* If the I2C controller is disabled we need to reset it
(probably due to a suspend/resume destroying state). We do
this here as we can then avoid worrying about resuming the
controller before its users. */
if (!(readl(_ICR(i2c)) & ICR_IUE))
i2c_pxa_reset(i2c);

for (i = adap->retries; i >= 0; i--) {
ret = i2c_pxa_do_pio_xfer(i2c, msgs, num);
if (ret != I2C_RETRY)
goto out;

if (i2c_debug)
dev_dbg(&adap->dev, "Retrying transmission\n");
udelay(100);
}
i2c_pxa_scream_blue_murder(i2c, "exhausted retries");
ret = -EREMOTEIO;
out:
i2c_pxa_set_slave(i2c, ret);
return ret;
}

/*
* i2c_pxa_master_complete - complete the message and wake up.
*/
Expand All @@ -621,7 +717,8 @@ static void i2c_pxa_master_complete(struct pxa_i2c *i2c, int ret)
i2c->msg_num = 0;
if (ret)
i2c->msg_idx = ret;
wake_up(&i2c->wait);
if (!i2c->use_pio)
wake_up(&i2c->wait);
}

static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr)
Expand Down Expand Up @@ -840,6 +937,11 @@ static const struct i2c_algorithm i2c_pxa_algorithm = {
.functionality = i2c_pxa_functionality,
};

static const struct i2c_algorithm i2c_pxa_pio_algorithm = {
.master_xfer = i2c_pxa_pio_xfer,
.functionality = i2c_pxa_functionality,
};

static void i2c_pxa_enable(struct platform_device *dev)
{
if (cpu_is_pxa27x()) {
Expand Down Expand Up @@ -890,7 +992,6 @@ static int i2c_pxa_probe(struct platform_device *dev)
}

i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &i2c_pxa_algorithm;
i2c->adap.retries = 5;

spin_lock_init(&i2c->lock);
Expand Down Expand Up @@ -927,20 +1028,26 @@ static int i2c_pxa_probe(struct platform_device *dev)
clk_enable(i2c->clk);
i2c_pxa_enable(dev);

ret = request_irq(irq, i2c_pxa_handler, IRQF_DISABLED,
i2c->adap.name, i2c);
if (ret)
goto ereqirq;
if (plat) {
i2c->adap.class = plat->class;
i2c->use_pio = plat->use_pio;
}

if (i2c->use_pio) {
i2c->adap.algo = &i2c_pxa_pio_algorithm;
} else {
i2c->adap.algo = &i2c_pxa_algorithm;
ret = request_irq(irq, i2c_pxa_handler, IRQF_DISABLED,
i2c->adap.name, i2c);
if (ret)
goto ereqirq;
}

i2c_pxa_reset(i2c);

i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &dev->dev;

if (plat) {
i2c->adap.class = plat->class;
}

/*
* If "dev->id" is negative we consider it as zero.
* The reason to do so is to avoid sysfs names that only make
Expand All @@ -966,7 +1073,8 @@ static int i2c_pxa_probe(struct platform_device *dev)
return 0;

eadapt:
free_irq(irq, i2c);
if (!i2c->use_pio)
free_irq(irq, i2c);
ereqirq:
clk_disable(i2c->clk);
i2c_pxa_disable(dev);
Expand All @@ -986,7 +1094,8 @@ static int i2c_pxa_remove(struct platform_device *dev)
platform_set_drvdata(dev, NULL);

i2c_del_adapter(&i2c->adap);
free_irq(i2c->irq, i2c);
if (!i2c->use_pio)
free_irq(i2c->irq, i2c);

clk_disable(i2c->clk);
clk_put(i2c->clk);
Expand Down
6 changes: 6 additions & 0 deletions include/asm-arm/arch-pxa/i2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,13 @@ struct i2c_pxa_platform_data {
unsigned int slave_addr;
struct i2c_slave_client *slave;
unsigned int class;
int use_pio;
};

extern void pxa_set_i2c_info(struct i2c_pxa_platform_data *info);

#ifdef CONFIG_PXA27x
extern void pxa_set_i2c_power_info(struct i2c_pxa_platform_data *info);
#endif

#endif

0 comments on commit b7a3670

Please sign in to comment.