Commit 85dd45dd authored by Felipe Balbi's avatar Felipe Balbi Committed by Tony Lindgren

i2c: twl4030-usb: add 'vbus' sysfs file

vbus sysfs file will report the state of vbus irq coming from
twl4030-usb.
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@nokia.com>
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
parent fde13f8f
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
#include <linux/time.h> #include <linux/time.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/usb/ch9.h> #include <linux/usb/ch9.h>
...@@ -259,11 +261,17 @@ ...@@ -259,11 +261,17 @@
struct twl4030_usb { struct twl4030_usb {
struct work_struct irq_work;
struct otg_transceiver otg; struct otg_transceiver otg;
struct device *dev; struct device *dev;
/* for vbus reporting with irqs disabled */
spinlock_t lock;
/* pin configuration */ /* pin configuration */
enum twl4030_usb_mode usb_mode; enum twl4030_usb_mode usb_mode;
unsigned vbus:1;
int irq; int irq;
u8 asleep; u8 asleep;
}; };
...@@ -529,6 +537,29 @@ static void twl4030_usb_ldo_init(struct twl4030_usb *twl) ...@@ -529,6 +537,29 @@ static void twl4030_usb_ldo_init(struct twl4030_usb *twl)
twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, PROTECT_KEY); twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, PROTECT_KEY);
} }
static ssize_t twl4030_usb_vbus_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", twl->vbus ? "on" : "off");
spin_unlock_irqrestore(&twl->lock, flags);
return ret;
}
static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL);
static void twl4030_usb_irq_work(struct work_struct *work)
{
struct twl4030_usb *twl = container_of(work,
struct twl4030_usb, irq_work);
sysfs_notify(&twl->dev->kobj, NULL, "vbus");
}
static irqreturn_t twl4030_usb_irq(int irq, void *_twl) static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
{ {
struct twl4030_usb *twl = _twl; struct twl4030_usb *twl = _twl;
...@@ -541,10 +572,13 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) ...@@ -541,10 +572,13 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
if (val & USB_PRES_RISING) { if (val & USB_PRES_RISING) {
twl4030_phy_resume(twl); twl4030_phy_resume(twl);
twl4030charger_usb_en(1); twl4030charger_usb_en(1);
twl->vbus = 1;
} else { } else {
twl4030charger_usb_en(0); twl4030charger_usb_en(0);
twl->vbus = 0;
twl4030_phy_suspend(twl, 0); twl4030_phy_suspend(twl, 0);
} }
schedule_work(&twl->irq_work);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -634,6 +668,7 @@ static int __init twl4030_usb_probe(struct platform_device *pdev) ...@@ -634,6 +668,7 @@ static int __init twl4030_usb_probe(struct platform_device *pdev)
struct twl4030_usb_data *pdata = pdev->dev.platform_data; struct twl4030_usb_data *pdata = pdev->dev.platform_data;
struct twl4030_usb *twl; struct twl4030_usb *twl;
int status; int status;
u8 vbus;
twl = kzalloc(sizeof *twl, GFP_KERNEL); twl = kzalloc(sizeof *twl, GFP_KERNEL);
if (!twl) if (!twl)
...@@ -644,12 +679,23 @@ static int __init twl4030_usb_probe(struct platform_device *pdev) ...@@ -644,12 +679,23 @@ static int __init twl4030_usb_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
WARN_ON(twl4030_i2c_read_u8(TWL4030_MODULE_INT,
&vbus, REG_PWR_EDR1) < 0);
vbus &= USB_PRES_RISING;
twl->dev = &pdev->dev; twl->dev = &pdev->dev;
twl->irq = TWL4030_PWRIRQ_USB_PRES; twl->irq = TWL4030_PWRIRQ_USB_PRES;
twl->otg.set_host = twl4030_set_host; twl->otg.set_host = twl4030_set_host;
twl->otg.set_peripheral = twl4030_set_peripheral; twl->otg.set_peripheral = twl4030_set_peripheral;
twl->otg.set_suspend = twl4030_set_suspend; twl->otg.set_suspend = twl4030_set_suspend;
twl->usb_mode = pdata->usb_mode; twl->usb_mode = pdata->usb_mode;
twl->vbus = vbus ? 1 : 0;
/* init spinlock for workqueue */
spin_lock_init(&twl->lock);
/* init irq workqueue before request_irq */
INIT_WORK(&twl->irq_work, twl4030_usb_irq_work);
usb_irq_disable(twl); usb_irq_disable(twl);
status = request_irq(twl->irq, twl4030_usb_irq, 0, "twl4030_usb", twl); status = request_irq(twl->irq, twl4030_usb_irq, 0, "twl4030_usb", twl);
...@@ -660,7 +706,6 @@ static int __init twl4030_usb_probe(struct platform_device *pdev) ...@@ -660,7 +706,6 @@ static int __init twl4030_usb_probe(struct platform_device *pdev)
return status; return status;
} }
twl4030_usb_ldo_init(twl); twl4030_usb_ldo_init(twl);
twl4030_phy_power(twl, 1); twl4030_phy_power(twl, 1);
twl4030_i2c_access(twl, 1); twl4030_i2c_access(twl, 1);
...@@ -677,6 +722,9 @@ static int __init twl4030_usb_probe(struct platform_device *pdev) ...@@ -677,6 +722,9 @@ static int __init twl4030_usb_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, twl); platform_set_drvdata(pdev, twl);
dev_info(&pdev->dev, "Initialized TWL4030 USB module\n"); dev_info(&pdev->dev, "Initialized TWL4030 USB module\n");
if (device_create_file(&pdev->dev, &dev_attr_vbus))
dev_warn(&pdev->dev, "could not create sysfs file\n");
return 0; return 0;
} }
...@@ -687,6 +735,7 @@ static int __exit twl4030_usb_remove(struct platform_device *pdev) ...@@ -687,6 +735,7 @@ static int __exit twl4030_usb_remove(struct platform_device *pdev)
usb_irq_disable(twl); usb_irq_disable(twl);
free_irq(twl->irq, twl); free_irq(twl->irq, twl);
device_remove_file(twl->dev, &dev_attr_vbus);
/* set transceiver mode to power on defaults */ /* set transceiver mode to power on defaults */
twl4030_usb_set_mode(twl, -1); twl4030_usb_set_mode(twl, -1);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment