Handler for VBUS pin monitor events. Notify sysfs on vbus level change. Return error if creating sysfs files fails. Signed-off-by: Krogerus Heikki (EXT-Teleca/Helsinki) <ext-heikki.krogerus@xxxxxxxxx> --- drivers/usb/otg/twl4030-usb.c | 60 +++++++++++++++++++++++++++++++++++++++- 1 files changed, 58 insertions(+), 2 deletions(-) diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 32ceed0..af50497 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -263,6 +263,7 @@ struct twl4030_usb { u8 linkstat; u8 asleep; bool irq_enabled; + u8 irq_source; }; /* internal define on top of container_of */ @@ -346,6 +347,18 @@ twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) /*-------------------------------------------------------------------------*/ +const char *twl4030_vbusevent_string(struct twl4030_usb *twl) +{ + if (twl->irq_source & USB_INT_SESSEND) + return "sessend"; + else if (twl->irq_source & USB_INT_SESSVALID) + return "sessvalid"; + else if (twl->irq_source & USB_INT_VBUSVALID) + return "vbusvalid"; + return "noevent"; +} + + static enum linkstat twl4030_usb_linkstat(struct twl4030_usb *twl) { int status; @@ -559,10 +572,40 @@ static ssize_t twl4030_usb_vbus_show(struct device *dev, } static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); +static ssize_t twl4030_usb_vbusevent_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct twl4030_usb *twl = dev_get_drvdata(dev); + unsigned long flags; + int ret = -EINVAL; + + spin_lock_irqsave(&twl->lock, flags); + ret = sprintf(buf, "%s\n", twl4030_vbusevent_string(twl)); + spin_unlock_irqrestore(&twl->lock, flags); + + return ret; +} +static DEVICE_ATTR(vbusevent, 0444, twl4030_usb_vbusevent_show, NULL); + +static void twl4030_vbusevent(struct twl4030_usb *twl) +{ + /* Handle unmasked events. See the levels from the TRM. + * + * Condition USB_INT_STS/LATCH + * ---------------------------------------------------------- + * VBUS < VSESSEND USB_INT_SESSEND + * VSESSVALID <= VBUS < VBUSVALID USB_INT_SESSVALID + * VBUSVALID <= VBUS USB_INT_VBUSVALID + * + * REVISIT call power management as needed. + */ + sysfs_notify(&twl->dev->kobj, NULL, "vbusevent"); +} + static irqreturn_t twl4030_usb_irq(int irq, void *_twl) { struct twl4030_usb *twl = _twl; - int status; + int status, int_sts; #ifdef CONFIG_LOCKDEP /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which @@ -572,6 +615,12 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) local_irq_enable(); #endif + twl->irq_source = twl4030_usb_read(twl, USB_INT_STS); + if (twl->irq_source) { + twl4030_vbusevent(twl); + goto vbus_event; + } + status = twl4030_usb_linkstat(twl); if (status != USB_LINK_UNKNOWN) { @@ -595,6 +644,7 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); +vbus_event: return IRQ_HANDLED; } @@ -678,8 +728,13 @@ static int __init twl4030_usb_probe(struct platform_device *pdev) otg_set_transceiver(&twl->otg); platform_set_drvdata(pdev, twl); - if (device_create_file(&pdev->dev, &dev_attr_vbus)) + err = device_create_file(&pdev->dev, &dev_attr_vbus); + err = device_create_file(&pdev->dev, &dev_attr_vbusevent); + if (err) { dev_warn(&pdev->dev, "could not create sysfs file\n"); + kfree(twl); + return err; + } /* Our job is to use irqs and status from the power module * to keep the transceiver disabled when nothing's connected. @@ -721,6 +776,7 @@ static int __exit twl4030_usb_remove(struct platform_device *pdev) free_irq(twl->irq, twl); device_remove_file(twl->dev, &dev_attr_vbus); + device_remove_file(twl->dev, &dev_attr_vbusevent); /* set transceiver mode to power on defaults */ twl4030_usb_set_mode(twl, -1); -- 1.5.4.3 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html