[i2c] [PATCH] Add polling transfer for i2c-pxa

eric miao eric.y.miao at gmail.com
Tue Dec 4 10:45:32 CET 2007


Yeah, and I verified this working on zylonite/pxa3xx, thanks!

-- 
Cheers
- eric

On Dec 4, 2007 5:24 PM, Mike Rapoport <mike at compulab.co.il> wrote:
> >>>>  #include "i2c-core.h"
> >>>> @@ -870,7 +871,24 @@ int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
> >>>>  {
> >>>>    int ret;
> >>>>
> >>>> -  if (adap->algo->master_xfer) {
> >>>> +  if (in_atomic() || irqs_disabled()) {
> >>> I wonder if it'd make more sense for this to be a per-adapter flag?
> >>> Eg, so you could switch the bus with the PMIC on to always use PIO
> >>> transfers.
> >> I'm not sure that making the bus with the PMIC on to always use PIO
> >> transfers is good idea. As Eric pointed there may be other devices
> >> on the same bus.
> >
> > That's exactly why I'm suggesting it - so that the bus is driven in the
> > same way all the time.
> >
> > Normal devices shouldn't care that it's being driven in PIO mode - they
> > don't know that it is.
> >
>
> I've added per-adapter 'use_pio' flag, and a method allowing platform code
> to enable this flag.
>
> Signed-off-by: Mike Rapoport <mike at compulab.co.il>
>
>  arch/arm/mach-pxa/pxa27x.c     |    6 ++
>  drivers/i2c/busses/i2c-pxa.c   |  123 ++++++++++++++++++++++++++++++++++++----
>  drivers/i2c/i2c-core.c         |   47 ++++++++++-----
>  include/asm-arm/arch-pxa/i2c.h |    6 ++
>  include/linux/i2c.h            |    5 ++
>  5 files changed, 161 insertions(+), 26 deletions(-)
>
> diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c
> index 8e126e6..57efebd 100644
> --- a/arch/arm/mach-pxa/pxa27x.c
> +++ b/arch/arm/mach-pxa/pxa27x.c
> @@ -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"
> @@ -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,
> diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
> index 6426a61..1ca9918 100644
> --- a/drivers/i2c/busses/i2c-pxa.c
> +++ b/drivers/i2c/busses/i2c-pxa.c
> @@ -163,6 +163,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)
>  {
> @@ -554,6 +555,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.
>   */
> @@ -610,6 +676,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.
>   */
> @@ -621,7 +716,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->adap.use_pio)
> +               wake_up(&i2c->wait);
>  }
>
>  static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr)
> @@ -837,6 +933,7 @@ static u32 i2c_pxa_functionality(struct i2c_adapter *adap)
>
>  static const struct i2c_algorithm i2c_pxa_algorithm = {
>         .master_xfer    = i2c_pxa_xfer,
> +       .pio_xfer       = i2c_pxa_pio_xfer,
>         .functionality  = i2c_pxa_functionality,
>  };
>
>
> @@ -912,21 +1009,23 @@ static int i2c_pxa_probe(struct platform_device *dev)
>         }
>  #endif
>
> -       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->adap.use_pio = plat->use_pio;
> +       }
>
> +       if (!i2c->adap.use_pio) {
> +               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
> @@ -952,7 +1051,8 @@ static int i2c_pxa_probe(struct platform_device *dev)
>         return 0;
>
>  eadapt:
> -       free_irq(irq, i2c);
> +       if (!i2c->adap.use_pio)
> +               free_irq(irq, i2c);
>  ereqirq:
>         clk_disable(i2c->clk);
>
> @@ -979,7 +1079,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->adap.use_pio)
> +               free_irq(i2c->irq, i2c);
>
>         clk_disable(i2c->clk);
>         clk_put(i2c->clk);
> diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
> index b5e13e4..5c21035 100644
> --- a/drivers/i2c/i2c-core.c
> +++ b/drivers/i2c/i2c-core.c
> @@ -864,30 +864,47 @@ module_exit(i2c_exit);
>   * the functional interface to the i2c busses.
>   * ----------------------------------------------------
>   */
> -
>  int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
>  {
>         int ret;
> +       int (*xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
>
> -       if (adap->algo->master_xfer) {
> -#ifdef DEBUG
> -               for (ret = 0; ret < num; ret++) {
> -                       dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
> -                               "len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
> -                               ? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
> -                               (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
> +       if (in_atomic() || irqs_disabled()) {
> +               if (adap->algo->pio_xfer == NULL) {
> +                       pr_debug("%s: algo has no poll xfer", __FUNCTION__);
> +                       return -EINVAL;
>                 }
> -#endif
> -
> -               mutex_lock_nested(&adap->bus_lock, adap->level);
> -               ret = adap->algo->master_xfer(adap,msgs,num);
> -               mutex_unlock(&adap->bus_lock);
> -
> -               return ret;
> +               ret = mutex_trylock(&adap->bus_lock);
> +               if (ret) { /* We get bus_lock, no ongoing I2C activity */
> +                       ret = adap->algo->pio_xfer(adap, msgs, num);
> +                       mutex_unlock(&adap->bus_lock);
> +                       return ret;
> +               } else {
> +                       /* I2C activity is ongoing. */
> +                       return -EBUSY;
> +               }
> +       } else if (adap->use_pio && adap->algo->pio_xfer != NULL) {
> +               xfer = adap->algo->pio_xfer;
> +       } else if (adap->algo->master_xfer) {
> +               xfer = adap->algo->master_xfer;
>         } else {
>                 dev_dbg(&adap->dev, "I2C level transfers not supported\n");
>                 return -ENOSYS;
>         }
> +
> +#ifdef DEBUG
> +       for (ret = 0; ret < num; ret++)
> +               dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
> +                       "len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
> +                       ? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
> +                       (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
> +#endif
> +
> +       mutex_lock_nested(&adap->bus_lock, adap->level);
> +       ret = xfer(adap, msgs, num);
> +       mutex_unlock(&adap->bus_lock);
> +
> +       return ret;
>  }
>  EXPORT_SYMBOL(i2c_transfer);
>
> diff --git a/include/asm-arm/arch-pxa/i2c.h b/include/asm-arm/arch-pxa/i2c.h
> index e404b23..80596b0 100644
> --- a/include/asm-arm/arch-pxa/i2c.h
> +++ b/include/asm-arm/arch-pxa/i2c.h
> @@ -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
> diff --git a/include/linux/i2c.h b/include/linux/i2c.h
> index a100c9f..2976c15 100644
> --- a/include/linux/i2c.h
> +++ b/include/linux/i2c.h
> @@ -290,6 +290,10 @@ struct i2c_algorithm {
>                            unsigned short flags, char read_write,
>                            u8 command, int size, union i2c_smbus_data * data);
>
> +       /* PIO xfer can be used in no-interrupts context */
> +       int (*pio_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
> +                       int num);
> +
>         /* To determine what the adapter supports */
>         u32 (*functionality) (struct i2c_adapter *);
>  };
> @@ -304,6 +308,7 @@ struct i2c_adapter {
>         unsigned int class;
>         const struct i2c_algorithm *algo; /* the algorithm to access the bus */
>         void *algo_data;
> +       int use_pio;
>
>         /* --- administration stuff. */
>         int (*client_register)(struct i2c_client *);
>
> --
> Sincerely yours,
> Mike.
>
>
>
> _______________________________________________
> i2c mailing list
> i2c at lm-sensors.org
> http://lists.lm-sensors.org/mailman/listinfo/i2c
>



More information about the i2c mailing list