diff options
author | Robert Jarzmik <robert.jarzmik@free.fr> | 2014-12-06 16:05:15 -0500 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2015-01-12 13:13:29 -0500 |
commit | 7acc9973e3c42de9926b28eec8ae3434dfdde3be (patch) | |
tree | 48cc00abfbbb8608577bd186aec7b0b5094cc797 /drivers/usb/phy/phy-generic.c | |
parent | 7bdea87a871f217a8a463f9203e6d0c702b7fda9 (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.c | 91 |
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 | |||
42 | struct platform_device *usb_phy_generic_register(void) | 46 | struct 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 */ | ||
74 | static 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 | |||
104 | static 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 | |||
69 | int usb_gen_phy_init(struct usb_phy *phy) | 140 | int 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; |