aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/phy/phy.c
diff options
context:
space:
mode:
authorBaolin Wang <baolin.wang@linaro.org>2017-08-15 07:07:54 -0400
committerFelipe Balbi <felipe.balbi@linux.intel.com>2017-08-15 08:05:01 -0400
commita9081a008f84819ab2f3da596bf89afa16beea94 (patch)
tree8ab66396b7c73b1a75275a3b86d30f67a0fcbfba /drivers/usb/phy/phy.c
parent44dd8a989c787e9077745417140aa132bfe45bf5 (diff)
usb: phy: Add USB charger support
This patch introduces the usb charger support based on usb phy that makes an enhancement to a power driver. The basic conception of the usb charger is that, when one usb charger is added or removed by reporting from the extcon device state change, the usb charger will report to power user to set the current limitation. Power user can register a notifiee on the usb phy by issuing usb_register_notifier() to get notified by charger status changes or charger current changes. we can notify what current to be drawn to power user according to different charger type, and now we have 2 methods to get charger type. One is get charger type from extcon subsystem, which also means the charger state changes. Another is we can get the charger type from USB controller detecting or PMIC detecting, and the charger state changes should be told by issuing usb_phy_set_charger_state(). Signed-off-by: Baolin Wang <baolin.wang@linaro.org> Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
Diffstat (limited to 'drivers/usb/phy/phy.c')
-rw-r--r--drivers/usb/phy/phy.c272
1 files changed, 272 insertions, 0 deletions
diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c
index 032f5afaad4b..2dc48bb2553c 100644
--- a/drivers/usb/phy/phy.c
+++ b/drivers/usb/phy/phy.c
@@ -18,6 +18,18 @@
18 18
19#include <linux/usb/phy.h> 19#include <linux/usb/phy.h>
20 20
21/* Default current range by charger type. */
22#define DEFAULT_SDP_CUR_MIN 2
23#define DEFAULT_SDP_CUR_MAX 500
24#define DEFAULT_SDP_CUR_MIN_SS 150
25#define DEFAULT_SDP_CUR_MAX_SS 900
26#define DEFAULT_DCP_CUR_MIN 500
27#define DEFAULT_DCP_CUR_MAX 5000
28#define DEFAULT_CDP_CUR_MIN 1500
29#define DEFAULT_CDP_CUR_MAX 5000
30#define DEFAULT_ACA_CUR_MIN 1500
31#define DEFAULT_ACA_CUR_MAX 5000
32
21static LIST_HEAD(phy_list); 33static LIST_HEAD(phy_list);
22static LIST_HEAD(phy_bind_list); 34static LIST_HEAD(phy_bind_list);
23static DEFINE_SPINLOCK(phy_lock); 35static DEFINE_SPINLOCK(phy_lock);
@@ -77,6 +89,221 @@ static struct usb_phy *__of_usb_find_phy(struct device_node *node)
77 return ERR_PTR(-EPROBE_DEFER); 89 return ERR_PTR(-EPROBE_DEFER);
78} 90}
79 91
92static void usb_phy_set_default_current(struct usb_phy *usb_phy)
93{
94 usb_phy->chg_cur.sdp_min = DEFAULT_SDP_CUR_MIN;
95 usb_phy->chg_cur.sdp_max = DEFAULT_SDP_CUR_MAX;
96 usb_phy->chg_cur.dcp_min = DEFAULT_DCP_CUR_MIN;
97 usb_phy->chg_cur.dcp_max = DEFAULT_DCP_CUR_MAX;
98 usb_phy->chg_cur.cdp_min = DEFAULT_CDP_CUR_MIN;
99 usb_phy->chg_cur.cdp_max = DEFAULT_CDP_CUR_MAX;
100 usb_phy->chg_cur.aca_min = DEFAULT_ACA_CUR_MIN;
101 usb_phy->chg_cur.aca_max = DEFAULT_ACA_CUR_MAX;
102}
103
104/**
105 * usb_phy_notify_charger_work - notify the USB charger state
106 * @work - the charger work to notify the USB charger state
107 *
108 * This work can be issued when USB charger state has been changed or
109 * USB charger current has been changed, then we can notify the current
110 * what can be drawn to power user and the charger state to userspace.
111 *
112 * If we get the charger type from extcon subsystem, we can notify the
113 * charger state to power user automatically by usb_phy_get_charger_type()
114 * issuing from extcon subsystem.
115 *
116 * If we get the charger type from ->charger_detect() instead of extcon
117 * subsystem, the usb phy driver should issue usb_phy_set_charger_state()
118 * to set charger state when the charger state has been changed.
119 */
120static void usb_phy_notify_charger_work(struct work_struct *work)
121{
122 struct usb_phy *usb_phy = container_of(work, struct usb_phy, chg_work);
123 char uchger_state[50] = { 0 };
124 char *envp[] = { uchger_state, NULL };
125 unsigned int min, max;
126
127 switch (usb_phy->chg_state) {
128 case USB_CHARGER_PRESENT:
129 usb_phy_get_charger_current(usb_phy, &min, &max);
130
131 atomic_notifier_call_chain(&usb_phy->notifier, max, usb_phy);
132 snprintf(uchger_state, ARRAY_SIZE(uchger_state),
133 "USB_CHARGER_STATE=%s", "USB_CHARGER_PRESENT");
134 break;
135 case USB_CHARGER_ABSENT:
136 usb_phy_set_default_current(usb_phy);
137
138 atomic_notifier_call_chain(&usb_phy->notifier, 0, usb_phy);
139 snprintf(uchger_state, ARRAY_SIZE(uchger_state),
140 "USB_CHARGER_STATE=%s", "USB_CHARGER_ABSENT");
141 break;
142 default:
143 dev_warn(usb_phy->dev, "Unknown USB charger state: %d\n",
144 usb_phy->chg_state);
145 return;
146 }
147
148 kobject_uevent_env(&usb_phy->dev->kobj, KOBJ_CHANGE, envp);
149}
150
151static void __usb_phy_get_charger_type(struct usb_phy *usb_phy)
152{
153 if (extcon_get_state(usb_phy->edev, EXTCON_CHG_USB_SDP) > 0) {
154 usb_phy->chg_type = SDP_TYPE;
155 usb_phy->chg_state = USB_CHARGER_PRESENT;
156 } else if (extcon_get_state(usb_phy->edev, EXTCON_CHG_USB_CDP) > 0) {
157 usb_phy->chg_type = CDP_TYPE;
158 usb_phy->chg_state = USB_CHARGER_PRESENT;
159 } else if (extcon_get_state(usb_phy->edev, EXTCON_CHG_USB_DCP) > 0) {
160 usb_phy->chg_type = DCP_TYPE;
161 usb_phy->chg_state = USB_CHARGER_PRESENT;
162 } else if (extcon_get_state(usb_phy->edev, EXTCON_CHG_USB_ACA) > 0) {
163 usb_phy->chg_type = ACA_TYPE;
164 usb_phy->chg_state = USB_CHARGER_PRESENT;
165 } else {
166 usb_phy->chg_type = UNKNOWN_TYPE;
167 usb_phy->chg_state = USB_CHARGER_ABSENT;
168 }
169
170 schedule_work(&usb_phy->chg_work);
171}
172
173/**
174 * usb_phy_get_charger_type - get charger type from extcon subsystem
175 * @nb -the notifier block to determine charger type
176 * @state - the cable state
177 * @data - private data
178 *
179 * Determin the charger type from extcon subsystem which also means the
180 * charger state has been chaned, then we should notify this event.
181 */
182static int usb_phy_get_charger_type(struct notifier_block *nb,
183 unsigned long state, void *data)
184{
185 struct usb_phy *usb_phy = container_of(nb, struct usb_phy, type_nb);
186
187 __usb_phy_get_charger_type(usb_phy);
188 return NOTIFY_OK;
189}
190
191/**
192 * usb_phy_set_charger_current - set the USB charger current
193 * @usb_phy - the USB phy to be used
194 * @mA - the current need to be set
195 *
196 * Usually we only change the charger default current when USB finished the
197 * enumeration as one SDP charger. As one SDP charger, usb_phy_set_power()
198 * will issue this function to change charger current when after setting USB
199 * configuration, or suspend/resume USB. For other type charger, we should
200 * use the default charger current and we do not suggest to issue this function
201 * to change the charger current.
202 *
203 * When USB charger current has been changed, we need to notify the power users.
204 */
205void usb_phy_set_charger_current(struct usb_phy *usb_phy, unsigned int mA)
206{
207 switch (usb_phy->chg_type) {
208 case SDP_TYPE:
209 if (usb_phy->chg_cur.sdp_max == mA)
210 return;
211
212 usb_phy->chg_cur.sdp_max = (mA > DEFAULT_SDP_CUR_MAX_SS) ?
213 DEFAULT_SDP_CUR_MAX_SS : mA;
214 break;
215 case DCP_TYPE:
216 if (usb_phy->chg_cur.dcp_max == mA)
217 return;
218
219 usb_phy->chg_cur.dcp_max = (mA > DEFAULT_DCP_CUR_MAX) ?
220 DEFAULT_DCP_CUR_MAX : mA;
221 break;
222 case CDP_TYPE:
223 if (usb_phy->chg_cur.cdp_max == mA)
224 return;
225
226 usb_phy->chg_cur.cdp_max = (mA > DEFAULT_CDP_CUR_MAX) ?
227 DEFAULT_CDP_CUR_MAX : mA;
228 break;
229 case ACA_TYPE:
230 if (usb_phy->chg_cur.aca_max == mA)
231 return;
232
233 usb_phy->chg_cur.aca_max = (mA > DEFAULT_ACA_CUR_MAX) ?
234 DEFAULT_ACA_CUR_MAX : mA;
235 break;
236 default:
237 return;
238 }
239
240 schedule_work(&usb_phy->chg_work);
241}
242EXPORT_SYMBOL_GPL(usb_phy_set_charger_current);
243
244/**
245 * usb_phy_get_charger_current - get the USB charger current
246 * @usb_phy - the USB phy to be used
247 * @min - the minimum current
248 * @max - the maximum current
249 *
250 * Usually we will notify the maximum current to power user, but for some
251 * special case, power user also need the minimum current value. Then the
252 * power user can issue this function to get the suitable current.
253 */
254void usb_phy_get_charger_current(struct usb_phy *usb_phy,
255 unsigned int *min, unsigned int *max)
256{
257 switch (usb_phy->chg_type) {
258 case SDP_TYPE:
259 *min = usb_phy->chg_cur.sdp_min;
260 *max = usb_phy->chg_cur.sdp_max;
261 break;
262 case DCP_TYPE:
263 *min = usb_phy->chg_cur.dcp_min;
264 *max = usb_phy->chg_cur.dcp_max;
265 break;
266 case CDP_TYPE:
267 *min = usb_phy->chg_cur.cdp_min;
268 *max = usb_phy->chg_cur.cdp_max;
269 break;
270 case ACA_TYPE:
271 *min = usb_phy->chg_cur.aca_min;
272 *max = usb_phy->chg_cur.aca_max;
273 break;
274 default:
275 *min = 0;
276 *max = 0;
277 break;
278 }
279}
280EXPORT_SYMBOL_GPL(usb_phy_get_charger_current);
281
282/**
283 * usb_phy_set_charger_state - set the USB charger state
284 * @usb_phy - the USB phy to be used
285 * @state - the new state need to be set for charger
286 *
287 * The usb phy driver can issue this function when the usb phy driver
288 * detected the charger state has been changed, in this case the charger
289 * type should be get from ->charger_detect().
290 */
291void usb_phy_set_charger_state(struct usb_phy *usb_phy,
292 enum usb_charger_state state)
293{
294 if (usb_phy->chg_state == state || !usb_phy->charger_detect)
295 return;
296
297 usb_phy->chg_state = state;
298 if (usb_phy->chg_state == USB_CHARGER_PRESENT)
299 usb_phy->chg_type = usb_phy->charger_detect(usb_phy);
300 else
301 usb_phy->chg_type = UNKNOWN_TYPE;
302
303 schedule_work(&usb_phy->chg_work);
304}
305EXPORT_SYMBOL_GPL(usb_phy_set_charger_state);
306
80static void devm_usb_phy_release(struct device *dev, void *res) 307static void devm_usb_phy_release(struct device *dev, void *res)
81{ 308{
82 struct usb_phy *phy = *(struct usb_phy **)res; 309 struct usb_phy *phy = *(struct usb_phy **)res;
@@ -124,6 +351,44 @@ static int usb_add_extcon(struct usb_phy *x)
124 "register VBUS notifier failed\n"); 351 "register VBUS notifier failed\n");
125 return ret; 352 return ret;
126 } 353 }
354 } else {
355 x->type_nb.notifier_call = usb_phy_get_charger_type;
356
357 ret = devm_extcon_register_notifier(x->dev, x->edev,
358 EXTCON_CHG_USB_SDP,
359 &x->type_nb);
360 if (ret) {
361 dev_err(x->dev,
362 "register extcon USB SDP failed.\n");
363 return ret;
364 }
365
366 ret = devm_extcon_register_notifier(x->dev, x->edev,
367 EXTCON_CHG_USB_CDP,
368 &x->type_nb);
369 if (ret) {
370 dev_err(x->dev,
371 "register extcon USB CDP failed.\n");
372 return ret;
373 }
374
375 ret = devm_extcon_register_notifier(x->dev, x->edev,
376 EXTCON_CHG_USB_DCP,
377 &x->type_nb);
378 if (ret) {
379 dev_err(x->dev,
380 "register extcon USB DCP failed.\n");
381 return ret;
382 }
383
384 ret = devm_extcon_register_notifier(x->dev, x->edev,
385 EXTCON_CHG_USB_ACA,
386 &x->type_nb);
387 if (ret) {
388 dev_err(x->dev,
389 "register extcon USB ACA failed.\n");
390 return ret;
391 }
127 } 392 }
128 393
129 if (x->id_nb.notifier_call) { 394 if (x->id_nb.notifier_call) {
@@ -145,6 +410,13 @@ static int usb_add_extcon(struct usb_phy *x)
145 } 410 }
146 } 411 }
147 412
413 usb_phy_set_default_current(x);
414 INIT_WORK(&x->chg_work, usb_phy_notify_charger_work);
415 x->chg_type = UNKNOWN_TYPE;
416 x->chg_state = USB_CHARGER_DEFAULT;
417 if (x->type_nb.notifier_call)
418 __usb_phy_get_charger_type(x);
419
148 return 0; 420 return 0;
149} 421}
150 422