* David Brownell <[EMAIL PROTECTED]> [081006 11:20]:
> I can't test the pwrbutton stuff, but rtc and musb behave...
> 
> With one issue:  the usb transceiver glue misbehaves on
> startup if the device is connected.  (Again.)  I looked at
> this a bit, and I think the issue is probably caused by
> not actually using key USB registers ... IRQ trigger
> mode state transitions are at best a fragile proxy for the
> real state.
> 
> (This is similar to the GPIO patch I just sent, but simpler
> except for the impact on the few drivers thinking oddly
> about IRQs.  Those patches cover the key SIH modules, and
> a similar one affects the PIH in twl4030-core.)

Pushing this, looks like we should have the remaining I2C
issues sorted out soon.

Tony

> 
> - Dave
> 
> 
> ================================
> Streamline the "power irq" code, and some of the mechanisms
> it uses:
> 
>  - Support IRQ masking, not just unmasking; simpler code.
>  - Use the standard handle_edge_irq() handler for these edge IRQs.
>  - Use generic_handle_irq() instead of a manual expansion thereof.
>  - Provide a missing spinlock for the shared data.
> 
> In short, use more of the standard genirq code ... more correctly.
> 
> Also, update the drivers using the "power IRQ" accordingly:
> 
>  - Don't request IRQF_DISABLED if the handler makes I2C calls;
>    and defend against lockdep forcing it on us.
> 
>  - Let the irq_chip handle IRQ mask/unmask; it handles mutual
>    exclusion between drivers, among other things.
> 
>  - (Unrelated) remove useless MODULE_ALIAS in pwrbutton.
> 
> The USB transceiver driver still places some dodgey games with IRQ
> enable/disable, and IRQ trigger flags.  At least some of them seem
> like they'd be simplified by using USB transceiver registers ...
> notably, startup code, which doesn't seem to check state before
> it enters an irq-driven mode.
> 
> For the moment, these drivers still (wrongly) try to configure IRQ
> trigger modes themselves ... again, that's something that an irq_chip
> handles (but not yet, for this one).
> 
> NOTE:  tested (MUSB and RTC only) along with the init/retry hack
> to make twl4030-pwrirq work around the i2c-omap timeout problems.
> 
> ---
>  drivers/i2c/chips/twl4030-pwrbutton.c |   33 ++++------
>  drivers/i2c/chips/twl4030-pwrirq.c    |   98 ++++++++++----------------------
>  drivers/i2c/chips/twl4030-usb.c       |   57 ++++++++----------
>  drivers/rtc/rtc-twl4030.c             |    8 ++
>  4 files changed, 80 insertions(+), 116 deletions(-)
> 
> --- a/drivers/i2c/chips/twl4030-pwrbutton.c
> +++ b/drivers/i2c/chips/twl4030-pwrbutton.c
> @@ -49,6 +49,14 @@ static irqreturn_t powerbutton_irq(int i
>       int err;
>       u8 value;
>  
> +#ifdef CONFIG_LOCKDEP
> +     /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
> +      * we don't want and can't tolerate.  Although it might be
> +      * friendlier not to borrow this thread context...
> +      */
> +     local_irq_enable();
> +#endif
> +
>       err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &value,
>                                 STS_HW_CONDITIONS);
>       if (!err)  {
> @@ -67,6 +75,7 @@ static int __init twl4030_pwrbutton_init
>       int err = 0;
>       u8 value;
>  
> +     /* PWRBTN == PWRON */
>       if (request_irq(TWL4030_PWRIRQ_PWRBTN, powerbutton_irq, 0,
>                       "PwrButton", NULL) < 0) {
>               printk(KERN_ERR "Unable to allocate IRQ for power button\n");
> @@ -92,22 +101,9 @@ static int __init twl4030_pwrbutton_init
>               goto free_irq_and_out;
>       }
>  
> -     err = twl4030_i2c_read_u8(TWL4030_MODULE_INT, &value, PWR_IMR1);
> -     if (err) {
> -             printk(KERN_WARNING "I2C error %d while reading TWL4030"
> -                                     " INT PWR_IMR1 register\n", err);
> -
> -             goto free_input_dev;
> -     }
> -
> -     err = twl4030_i2c_write_u8(TWL4030_MODULE_INT,
> -                                value & (~PWR_PWRON_IRQ), PWR_IMR1);
> -     if (err) {
> -             printk(KERN_WARNING "I2C error %d while writing TWL4030"
> -                                 " INT PWR_IMR1 register\n", err);
> -             goto free_input_dev;
> -     }
> -
> +     /* FIXME just pass IRQF_EDGE_FALLING | IRQF_EDGE_RISING
> +      * to request_irq(), once MODULE_INT supports them...
> +      */
>       err = twl4030_i2c_read_u8(TWL4030_MODULE_INT, &value, PWR_EDR1);
>       if (err) {
>               printk(KERN_WARNING "I2C error %d while reading TWL4030"
> @@ -136,19 +132,16 @@ free_irq_and_out:
>  out:
>       return err;
>  }
> +module_init(twl4030_pwrbutton_init);
>  
>  static void __exit twl4030_pwrbutton_exit(void)
>  {
>       free_irq(TWL4030_PWRIRQ_PWRBTN, NULL);
>       input_unregister_device(powerbutton_dev);
>       input_free_device(powerbutton_dev);
> -
>  }
> -
> -module_init(twl4030_pwrbutton_init);
>  module_exit(twl4030_pwrbutton_exit);
>  
> -MODULE_ALIAS("i2c:twl4030-pwrbutton");
>  MODULE_DESCRIPTION("Triton2 Power Button");
>  MODULE_LICENSE("GPL");
>  MODULE_AUTHOR("Peter De Schrijver");
> --- a/drivers/i2c/chips/twl4030-pwrirq.c
> +++ b/drivers/i2c/chips/twl4030-pwrirq.c
> @@ -29,21 +29,35 @@
>  #include <linux/i2c/twl4030.h>
>  
>  
> +static DEFINE_SPINLOCK(pwr_lock);
>  static u8 twl4030_pwrirq_mask;
> -static u8 twl4030_pwrirq_pending_unmask;
>  
>  static struct task_struct *twl4030_pwrirq_unmask_thread;
>  
>  static void twl4030_pwrirq_ack(unsigned int irq) {}
>  
> -static void twl4030_pwrirq_disableint(unsigned int irq) {}
> +static void twl4030_pwrirq_disableint(unsigned int irq)
> +{
> +     unsigned long flags;
> +
> +     spin_lock_irqsave(&pwr_lock, flags);
> +     twl4030_pwrirq_mask |= 1 << (irq - TWL4030_PWR_IRQ_BASE);
> +     if (twl4030_pwrirq_unmask_thread
> +                     && twl4030_pwrirq_unmask_thread->state != TASK_RUNNING)
> +             wake_up_process(twl4030_pwrirq_unmask_thread);
> +     spin_unlock_irqrestore(&pwr_lock, flags);
> +}
>  
>  static void twl4030_pwrirq_enableint(unsigned int irq)
>  {
> -     twl4030_pwrirq_pending_unmask |= 1 << (irq - TWL4030_PWR_IRQ_BASE);
> -     if (twl4030_pwrirq_unmask_thread &&
> -             twl4030_pwrirq_unmask_thread->state != TASK_RUNNING)
> +     unsigned long flags;
> +
> +     spin_lock_irqsave(&pwr_lock, flags);
> +     twl4030_pwrirq_mask &= ~(1 << (irq - TWL4030_PWR_IRQ_BASE));
> +     if (twl4030_pwrirq_unmask_thread
> +                     && twl4030_pwrirq_unmask_thread->state != TASK_RUNNING)
>               wake_up_process(twl4030_pwrirq_unmask_thread);
> +     spin_unlock_irqrestore(&pwr_lock, flags);
>  }
>  
>  static struct irq_chip twl4030_pwrirq_chip = {
> @@ -53,48 +67,6 @@ static struct irq_chip twl4030_pwrirq_ch
>       .unmask = twl4030_pwrirq_enableint,
>  };
>  
> -static void do_twl4030_pwrmodule_irq(unsigned int irq, irq_desc_t *desc)
> -{
> -     struct irqaction *action;
> -     const unsigned int cpu = smp_processor_id();
> -
> -     desc->status |= IRQ_LEVEL;
> -
> -     if (!desc->depth) {
> -             kstat_cpu(cpu).irqs[irq]++;
> -
> -             action = desc->action;
> -             if (action) {
> -                     int ret;
> -                     int status = 0;
> -                     int retval = 0;
> -
> -                     do {
> -                             ret = action->handler(irq, action->dev_id);
> -                             if (ret == IRQ_HANDLED)
> -                                     status |= action->flags;
> -                             retval |= ret;
> -                             action = action->next;
> -                     } while (action);
> -
> -                     if (status & IRQF_SAMPLE_RANDOM)
> -                             add_interrupt_randomness(irq);
> -
> -                     if (retval != IRQ_HANDLED)
> -                             printk(KERN_ERR "ISR for TWL4030 power module"
> -                                     " irq %d can't handle interrupt\n",
> -                                     irq);
> -             } else {
> -                     local_irq_disable();
> -                     twl4030_pwrirq_mask |= 1 << (irq - 
> TWL4030_PWR_IRQ_BASE);
> -                     local_irq_enable();
> -                     twl4030_i2c_write_u8(TWL4030_MODULE_INT,
> -                                          twl4030_pwrirq_mask,
> -                                          TWL4030_INT_PWR_IMR1);
> -             }
> -     }
> -}
> -
>  static void do_twl4030_pwrirq(unsigned int irq, irq_desc_t *desc)
>  {
>       const unsigned int cpu = smp_processor_id();
> @@ -113,6 +85,7 @@ static void do_twl4030_pwrirq(unsigned i
>               local_irq_enable();
>               ret = twl4030_i2c_read_u8(TWL4030_MODULE_INT, &pwr_isr,
>                                         TWL4030_INT_PWR_ISR1);
> +             local_irq_disable();
>               if (ret) {
>                       printk(KERN_WARNING
>                               "I2C error %d while reading TWL4030"
> @@ -122,13 +95,10 @@ static void do_twl4030_pwrirq(unsigned i
>  
>               for (module_irq = TWL4030_PWR_IRQ_BASE; pwr_isr != 0;
>                       module_irq++, pwr_isr >>= 1) {
> -                     if (pwr_isr & 1) {
> -                             irq_desc_t *d = irq_desc + module_irq;
> -                             d->handle_irq(module_irq, d);
> -                     }
> +                     if (pwr_isr & 1)
> +                             generic_handle_irq(module_irq);
>               }
>  
> -             local_irq_disable();
>               desc->chip->unmask(irq);
>       }
>  }
> @@ -138,22 +108,19 @@ static int twl4030_pwrirq_thread(void *d
>       current->flags |= PF_NOFREEZE;
>  
>       while (!kthread_should_stop()) {
> -             u8 local_unmask;
> -
> -             local_irq_disable();
> -             local_unmask = twl4030_pwrirq_pending_unmask;
> -             twl4030_pwrirq_pending_unmask = 0;
> -             local_irq_enable();
> +             u8 local_mask;
>  
> -             twl4030_pwrirq_mask &= ~local_unmask;
> +             spin_lock_irq(&pwr_lock);
> +             local_mask = twl4030_pwrirq_mask;
> +             spin_unlock_irq(&pwr_lock);
>  
> -             twl4030_i2c_write_u8(TWL4030_MODULE_INT, twl4030_pwrirq_mask,
> +             twl4030_i2c_write_u8(TWL4030_MODULE_INT, local_mask,
>                                    TWL4030_INT_PWR_IMR1);
>  
> -             local_irq_disable();
> -             if (!twl4030_pwrirq_pending_unmask)
> +             spin_lock_irq(&pwr_lock);
> +             if (local_mask == twl4030_pwrirq_mask)
>                       set_current_state(TASK_INTERRUPTIBLE);
> -             local_irq_enable();
> +             spin_unlock_irq(&pwr_lock);
>  
>               schedule();
>       }
> @@ -168,7 +135,6 @@ static int __init twl4030_pwrirq_init(vo
>       int i, err;
>  
>       twl4030_pwrirq_mask = 0xff;
> -     twl4030_pwrirq_pending_unmask = 0;
>  
>  /* HEY:  core already did this.
>   * But that's surely not why we
> @@ -201,8 +167,8 @@ msleep(10);
>       }
>  
>       for (i = TWL4030_PWR_IRQ_BASE; i < TWL4030_PWR_IRQ_END; i++) {
> -             set_irq_chip(i, &twl4030_pwrirq_chip);
> -             set_irq_handler(i, do_twl4030_pwrmodule_irq);
> +             set_irq_chip_and_handler(i, &twl4030_pwrirq_chip,
> +                             handle_edge_irq);
>               set_irq_flags(i, IRQF_VALID);
>       }
>  
> --- a/drivers/i2c/chips/twl4030-usb.c
> +++ b/drivers/i2c/chips/twl4030-usb.c
> @@ -237,14 +237,9 @@
>  #define GPIO_USB_4PIN_ULPI_2430C     (3 << 0)
>  
>  /* In module TWL4030_MODULE_INT */
> -#define REG_PWR_ISR1                 0x00
> -#define REG_PWR_IMR1                 0x01
> -#define USB_PRES                     (1 << 2)
>  #define REG_PWR_EDR1                 0x05
>  #define USB_PRES_FALLING             (1 << 4)
>  #define USB_PRES_RISING                      (1 << 5)
> -#define REG_PWR_SIH_CTRL             0x07
> -#define COR                          (1 << 2)
>  
>  /* bits in OTG_CTRL */
>  #define      OTG_XCEIV_OUTPUTS \
> @@ -274,6 +269,7 @@ struct twl4030_usb {
>       unsigned                vbus:1;
>       int                     irq;
>       u8                      asleep;
> +     bool                    irq_enabled;
>  };
>  
>  /* internal define on top of container_of */
> @@ -417,6 +413,8 @@ static void usb_irq_enable(struct twl403
>  {
>       u8 val;
>  
> +     /* FIXME use set_irq_type(...) when that (soon) works */
> +
>       /* edge setup */
>       WARN_ON(twl4030_i2c_read_u8(TWL4030_MODULE_INT,
>                               &val, REG_PWR_EDR1) < 0);
> @@ -429,34 +427,18 @@ static void usb_irq_enable(struct twl403
>       WARN_ON(twl4030_i2c_write_u8_verify(twl, TWL4030_MODULE_INT,
>                               val, REG_PWR_EDR1) < 0);
>  
> -     /* un-mask interrupt */
> -     WARN_ON(twl4030_i2c_read_u8(TWL4030_MODULE_INT,
> -                             &val, REG_PWR_IMR1) < 0);
> -
> -     val &= ~USB_PRES;
> -
> -     WARN_ON(twl4030_i2c_write_u8_verify(twl, TWL4030_MODULE_INT,
> -                             val, REG_PWR_IMR1) < 0);
> +     if (!twl->irq_enabled) {
> +             enable_irq(twl->irq);
> +             twl->irq_enabled = true;
> +     }
>  }
>  
>  static void usb_irq_disable(struct twl4030_usb *twl)
>  {
> -     u8 val;
> -
> -     /* undo edge setup */
> -     WARN_ON(twl4030_i2c_read_u8(TWL4030_MODULE_INT,
> -                             &val, REG_PWR_EDR1) < 0);
> -     val &= ~(USB_PRES_RISING | USB_PRES_FALLING);
> -     WARN_ON(twl4030_i2c_write_u8_verify(twl, TWL4030_MODULE_INT,
> -                             val, REG_PWR_EDR1) < 0);
> -
> -     /* mask interrupt */
> -     WARN_ON(twl4030_i2c_read_u8(TWL4030_MODULE_INT,
> -                             &val, REG_PWR_IMR1) < 0);
> -     val |= USB_PRES;
> -
> -     WARN_ON(twl4030_i2c_write_u8_verify(twl, TWL4030_MODULE_INT,
> -                             val, REG_PWR_IMR1) < 0);
> +     if (twl->irq_enabled) {
> +             disable_irq(twl->irq);
> +             twl->irq_enabled = false;
> +     }
>  }
>  
>  static void twl4030_phy_power(struct twl4030_usb *twl, int on)
> @@ -565,6 +547,21 @@ static irqreturn_t twl4030_usb_irq(int i
>       struct twl4030_usb *twl = _twl;
>       u8 val;
>  
> +#ifdef CONFIG_LOCKDEP
> +     /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
> +      * we don't want and can't tolerate.  Although it might be
> +      * friendlier not to borrow this thread context...
> +      */
> +     local_irq_enable();
> +#endif
> +
> +     /* FIXME stop accessing PWR_EDR1 ... if nothing else, we
> +      * know which edges we told the IRQ to trigger on.  And
> +      * there seem to be OTG_specific registers and irqs that
> +      * provide the right info without guessing like this:
> +      * USB_INT_STS, ID_STATUS, STS_HW_CONDITIONS, etc.
> +      */
> +
>       /* action based on cable attach or detach */
>       WARN_ON(twl4030_i2c_read_u8(TWL4030_MODULE_INT,
>                               &val, REG_PWR_EDR1) < 0);
> @@ -697,7 +694,7 @@ static int __init twl4030_usb_probe(stru
>       /* init irq workqueue before request_irq */
>       INIT_WORK(&twl->irq_work, twl4030_usb_irq_work);
>  
> -     usb_irq_disable(twl);
> +     twl->irq_enabled = true;
>       status = request_irq(twl->irq, twl4030_usb_irq, 0, "twl4030_usb", twl);
>       if (status < 0) {
>               dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n",
> --- a/drivers/rtc/rtc-twl4030.c
> +++ b/drivers/rtc/rtc-twl4030.c
> @@ -347,6 +347,14 @@ static irqreturn_t twl4030_rtc_interrupt
>       int res;
>       u8 rd_reg;
>  
> +#ifdef CONFIG_LOCKDEP
> +     /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
> +      * we don't want and can't tolerate.  Although it might be
> +      * friendlier not to borrow this thread context...
> +      */
> +     local_irq_enable();
> +#endif
> +
>       res = twl4030_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
>       if (res)
>               goto out;
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to [EMAIL PROTECTED]
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to