[PATCH] Add Sysfs Interface to Control Vbus states

Felipe Balbi
2007-05-29T12:02:35Z

From: Felipe Balbi <felipe.lima@indt.org.br>

We need to control Vbus in order to save power and
let us test SRP features.
This sysfs interface provides an easy way to control
the Vbus states.

HowTo use it:
echo off > /sys/devices/platform/musb_hdrc/vbus
or
echo on > /sys/devices/platform/musb_hdrc/vbus

Later improvements:
* Check current Vbus state before setting new state.
* Implement MUSB PM features and shutdown Vbus whenever we're
not using the Bus.

Signed-off-by: Felipe Balbi <felipe.lima@indt.org.br>
---

Index: linux-omap-2.6/drivers/usb/musb/plat_uds.c
===================================================================
--- linux-omap-2.6.orig/drivers/usb/musb/plat_uds.c 2007-05-28 13:18:47.000000000 +0300
+++ linux-omap-2.6/drivers/usb/musb/plat_uds.c 2007-05-28 13:18:52.000000000 +0300
@@ -1579,6 +1579,78 @@
}
static DEVICE_ATTR(cable, S_IRUGO, musb_cable_show, NULL);

+static ssize_t
+musb_vbus_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct musb *musb = dev_to_musb(dev);
+ unsigned long flags;
+ u8 devctl;
+
+ /* REVISIT the correct way to do it would be checking if we're
+ * the Standard A device. B devices do not provide Vbus.
+ */
+ spin_lock_irqsave(&musb->Lock, flags);
+ devctl = musb_readb(musb->pRegs, MGC_O_HDRC_DEVCTL);
+ if (!strncmp(buf, "on", 2)) {
+ u8 power = musb_readb(musb->pRegs, MGC_O_HDRC_POWER);
+
+ musb->xceiv.default_a = 1;
+ devctl |= MGC_M_DEVCTL_SESSION;
+ musb_writeb(musb->pRegs, MGC_O_HDRC_DEVCTL, devctl);
+ musb_set_vbus(musb, 1);
+
+ /* After turning on Vbus we need to generate a reset signal
+ * on the bus. The following code treats this
+ */
+ power &= 0xf0;
+ musb_writeb(musb->pRegs, MGC_O_HDRC_POWER,
+ power | MGC_M_POWER_RESET);
+ musb->port1_status |= USB_PORT_STAT_RESET;
+ musb->port1_status &= ~USB_PORT_STAT_ENABLE;
+ musb->rh_timer = jiffies + msecs_to_jiffies(20);
+
+ /* REVISIT after sending the reset signal during 20msecs
+ * we need to clear the RESET bit in the power register
+ */
+ if (time_after(jiffies, musb->rh_timer))
+ musb_writeb(musb->pRegs, MGC_O_HDRC_POWER,
+ power & ~MGC_M_POWER_RESET);
+
+ }
+ if (!strncmp(buf, "off", 3)) {
+ devctl |= ~MGC_M_DEVCTL_SESSION;
+ musb_writeb(musb->pRegs, MGC_O_HDRC_DEVCTL, devctl);
+ musb_set_vbus(musb, 0);
+ }
+ /* REVIST when shutting down Vbus we should enter A_IDLE state and not
+ * B_IDLE. Check tusb_source_power() definition
+ */
+ spin_unlock_irqrestore(&musb->Lock, flags);
+
+ return n;
+}
+
+static ssize_t
+musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct musb *musb = dev_to_musb(dev);
+ unsigned long flags;
+ int vbus;
+ char *ret;
+
+ spin_lock_irqsave(&musb->Lock, flags);
+ vbus = musb_platform_get_vbus_status(musb);
+ if (vbus)
+ ret = "Vbus on";
+ else
+ ret = "Vbus off";
+ spin_unlock_irqrestore(&musb->Lock, flags);
+
+ return sprintf(buf, "%s\n", ret);
+}
+static DEVICE_ATTR(vbus, 0644, musb_vbus_show, musb_vbus_store);
+
#ifdef CONFIG_USB_MUSB_OTG
static ssize_t
musb_srp_store(struct device *dev, struct device_attribute *attr,
@@ -1674,6 +1746,7 @@
#ifdef CONFIG_SYSFS
device_remove_file(musb->controller, &dev_attr_mode);
device_remove_file(musb->controller, &dev_attr_cable);
+ device_remove_file(musb->controller, &dev_attr_vbus);
#ifdef CONFIG_USB_MUSB_OTG
device_remove_file(musb->controller, &dev_attr_srp);
#endif
@@ -1905,6 +1978,7 @@
#ifdef CONFIG_SYSFS
status = device_create_file(dev, &dev_attr_mode);
status = device_create_file(dev, &dev_attr_cable);
+ status = device_create_file(dev, &dev_attr_vbus);
#ifdef CONFIG_USB_MUSB_OTG
status = device_create_file(dev, &dev_attr_srp);
#endif /* CONFIG_USB_MUSB_OTG */