* David Brownell <david-b@xxxxxxxxxxx> [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 majordomo@xxxxxxxxxxxxxxx > 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html