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 | 64 +++++++++++++++++++++++++++++++++++++++- 1 files changed, 62 insertions(+), 2 deletions(-) diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 416e441..0ffe02b 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -256,6 +256,7 @@ struct twl4030_usb { u8 linkstat; u8 asleep; bool irq_enabled; + u8 irq_source; }; /* internal define on top of container_of */ @@ -339,6 +340,17 @@ 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; @@ -512,6 +524,36 @@ 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; @@ -525,6 +567,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) { @@ -548,6 +596,7 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); +vbus_event: return IRQ_HANDLED; } @@ -626,8 +675,18 @@ 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)) - dev_warn(&pdev->dev, "could not create sysfs file\n"); + err = device_create_file(&pdev->dev, &dev_attr_vbus); + if (err) { + dev_err(&pdev->dev, "could not create sysfs file \"vbus\"\n"); + kfree(twl); + return err; + } + err = device_create_file(&pdev->dev, &dev_attr_vbusevent); + if (err) { + dev_err(&pdev->dev, "could not create sysfs file \"vbusevent\"\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. @@ -669,6 +728,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.6.0.6 -- 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