aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/phy/phy-generic.c
diff options
context:
space:
mode:
authorRobert Jarzmik <robert.jarzmik@free.fr>2014-12-06 16:05:15 -0500
committerFelipe Balbi <balbi@ti.com>2015-01-12 13:13:29 -0500
commit7acc9973e3c42de9926b28eec8ae3434dfdde3be (patch)
tree48cc00abfbbb8608577bd186aec7b0b5094cc797 /drivers/usb/phy/phy-generic.c
parent7bdea87a871f217a8a463f9203e6d0c702b7fda9 (diff)
usb: phy: generic: add vbus support
Add support for vbus detection and power supply. This code is more or less stolen from phy-gpio-vbus-usb.c, and aims at providing a detection mechanism for VBus (ie. usb cable plug) based on a GPIO line, and a power supply activation which draws current from the VBus. [ balbi@ti.com : fix build break ] Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr> Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/phy/phy-generic.c')
-rw-r--r--drivers/usb/phy/phy-generic.c91
1 files changed, 90 insertions, 1 deletions
diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c
index d53928f3a6ea..dd05254241fb 100644
--- a/drivers/usb/phy/phy-generic.c
+++ b/drivers/usb/phy/phy-generic.c
@@ -27,6 +27,7 @@
27#include <linux/module.h> 27#include <linux/module.h>
28#include <linux/platform_device.h> 28#include <linux/platform_device.h>
29#include <linux/dma-mapping.h> 29#include <linux/dma-mapping.h>
30#include <linux/usb/gadget.h>
30#include <linux/usb/otg.h> 31#include <linux/usb/otg.h>
31#include <linux/usb/usb_phy_generic.h> 32#include <linux/usb/usb_phy_generic.h>
32#include <linux/slab.h> 33#include <linux/slab.h>
@@ -39,6 +40,9 @@
39 40
40#include "phy-generic.h" 41#include "phy-generic.h"
41 42
43#define VBUS_IRQ_FLAGS \
44 (IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
45
42struct platform_device *usb_phy_generic_register(void) 46struct platform_device *usb_phy_generic_register(void)
43{ 47{
44 return platform_device_register_simple("usb_phy_generic", 48 return platform_device_register_simple("usb_phy_generic",
@@ -66,6 +70,73 @@ static void nop_reset_set(struct usb_phy_generic *nop, int asserted)
66 usleep_range(10000, 20000); 70 usleep_range(10000, 20000);
67} 71}
68 72
73/* interface to regulator framework */
74static void nop_set_vbus_draw(struct usb_phy_generic *nop, unsigned mA)
75{
76 struct regulator *vbus_draw = nop->vbus_draw;
77 int enabled;
78 int ret;
79
80 if (!vbus_draw)
81 return;
82
83 enabled = nop->vbus_draw_enabled;
84 if (mA) {
85 regulator_set_current_limit(vbus_draw, 0, 1000 * mA);
86 if (!enabled) {
87 ret = regulator_enable(vbus_draw);
88 if (ret < 0)
89 return;
90 nop->vbus_draw_enabled = 1;
91 }
92 } else {
93 if (enabled) {
94 ret = regulator_disable(vbus_draw);
95 if (ret < 0)
96 return;
97 nop->vbus_draw_enabled = 0;
98 }
99 }
100 nop->mA = mA;
101}
102
103
104static irqreturn_t nop_gpio_vbus_thread(int irq, void *data)
105{
106 struct usb_phy_generic *nop = data;
107 struct usb_otg *otg = nop->phy.otg;
108 int vbus, status;
109
110 vbus = gpiod_get_value(nop->gpiod_vbus);
111 if ((vbus ^ nop->vbus) == 0)
112 return IRQ_HANDLED;
113 nop->vbus = vbus;
114
115 if (vbus) {
116 status = USB_EVENT_VBUS;
117 otg->state = OTG_STATE_B_PERIPHERAL;
118 nop->phy.last_event = status;
119 usb_gadget_vbus_connect(otg->gadget);
120
121 /* drawing a "unit load" is *always* OK, except for OTG */
122 nop_set_vbus_draw(nop, 100);
123
124 atomic_notifier_call_chain(&nop->phy.notifier, status,
125 otg->gadget);
126 } else {
127 nop_set_vbus_draw(nop, 0);
128
129 usb_gadget_vbus_disconnect(otg->gadget);
130 status = USB_EVENT_NONE;
131 otg->state = OTG_STATE_B_IDLE;
132 nop->phy.last_event = status;
133
134 atomic_notifier_call_chain(&nop->phy.notifier, status,
135 otg->gadget);
136 }
137 return IRQ_HANDLED;
138}
139
69int usb_gen_phy_init(struct usb_phy *phy) 140int usb_gen_phy_init(struct usb_phy *phy)
70{ 141{
71 struct usb_phy_generic *nop = dev_get_drvdata(phy->dev); 142 struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
@@ -149,17 +220,23 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop,
149 needs_vcc = of_property_read_bool(node, "vcc-supply"); 220 needs_vcc = of_property_read_bool(node, "vcc-supply");
150 nop->gpiod_reset = devm_gpiod_get(dev, "reset-gpios"); 221 nop->gpiod_reset = devm_gpiod_get(dev, "reset-gpios");
151 err = PTR_ERR(nop->gpiod_reset); 222 err = PTR_ERR(nop->gpiod_reset);
223 if (!err) {
224 nop->gpiod_vbus = devm_gpiod_get(dev,
225 "vbus-detect-gpio");
226 err = PTR_ERR(nop->gpiod_vbus);
227 }
152 } else if (pdata) { 228 } else if (pdata) {
153 type = pdata->type; 229 type = pdata->type;
154 clk_rate = pdata->clk_rate; 230 clk_rate = pdata->clk_rate;
155 needs_vcc = pdata->needs_vcc; 231 needs_vcc = pdata->needs_vcc;
156 if (gpio_is_valid(gpio->gpio_reset)) { 232 if (gpio_is_valid(pdata->gpio_reset)) {
157 err = devm_gpio_request_one(dev, pdata->gpio_reset, 0, 233 err = devm_gpio_request_one(dev, pdata->gpio_reset, 0,
158 dev_name(dev)); 234 dev_name(dev));
159 if (!err) 235 if (!err)
160 nop->gpiod_reset = 236 nop->gpiod_reset =
161 gpio_to_desc(pdata->gpio_reset); 237 gpio_to_desc(pdata->gpio_reset);
162 } 238 }
239 nop->gpiod_vbus = pdata->gpiod_vbus;
163 } 240 }
164 241
165 if (err == -EPROBE_DEFER) 242 if (err == -EPROBE_DEFER)
@@ -224,6 +301,18 @@ static int usb_phy_generic_probe(struct platform_device *pdev)
224 err = usb_phy_gen_create_phy(dev, nop, dev_get_platdata(&pdev->dev)); 301 err = usb_phy_gen_create_phy(dev, nop, dev_get_platdata(&pdev->dev));
225 if (err) 302 if (err)
226 return err; 303 return err;
304 if (nop->gpiod_vbus) {
305 err = devm_request_threaded_irq(&pdev->dev,
306 gpiod_to_irq(nop->gpiod_vbus),
307 NULL, nop_gpio_vbus_thread,
308 VBUS_IRQ_FLAGS, "vbus_detect",
309 nop);
310 if (err) {
311 dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
312 gpiod_to_irq(nop->gpiod_vbus), err);
313 return err;
314 }
315 }
227 316
228 nop->phy.init = usb_gen_phy_init; 317 nop->phy.init = usb_gen_phy_init;
229 nop->phy.shutdown = usb_gen_phy_shutdown; 318 nop->phy.shutdown = usb_gen_phy_shutdown;