blob: 78666d78448beaf0723a07f8bd0a1a9da7f432cc [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* linux/arch/arm/mach-msm/rpc_hsusb.c
2 *
Duy Truong790f06d2013-02-13 16:38:12 -08003 * Copyright (c) 2008-2012, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07004 *
5 * All source code in this file is licensed under the following license except
6 * where indicated.
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License version 2 as published
10 * by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 *
16 * See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you can find it at http://www.fsf.org
19 */
20
21#include <linux/err.h>
22#include <linux/slab.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070023#include <linux/module.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070024#include <mach/rpc_hsusb.h>
25#include <asm/mach-types.h>
26
27static struct msm_rpc_endpoint *usb_ep;
28static struct msm_rpc_endpoint *chg_ep;
29
30#define MSM_RPC_CHG_PROG 0x3000001a
31
32struct msm_chg_rpc_ids {
33 unsigned long vers_comp;
34 unsigned chg_usb_charger_connected_proc;
35 unsigned chg_usb_charger_disconnected_proc;
36 unsigned chg_usb_i_is_available_proc;
37 unsigned chg_usb_i_is_not_available_proc;
38};
39
40struct msm_hsusb_rpc_ids {
41 unsigned long prog;
42 unsigned long vers_comp;
43 unsigned long init_phy;
44 unsigned long vbus_pwr_up;
45 unsigned long vbus_pwr_down;
46 unsigned long update_product_id;
47 unsigned long update_serial_num;
48 unsigned long update_is_serial_num_null;
49 unsigned long reset_rework_installed;
50 unsigned long enable_pmic_ulpi_data0;
51 unsigned long disable_pmic_ulpi_data0;
52};
53
54static struct msm_hsusb_rpc_ids usb_rpc_ids;
55static struct msm_chg_rpc_ids chg_rpc_ids;
56
57static int msm_hsusb_init_rpc_ids(unsigned long vers)
58{
59 if (vers == 0x00010001) {
60 usb_rpc_ids.prog = 0x30000064;
61 usb_rpc_ids.vers_comp = 0x00010001;
62 usb_rpc_ids.init_phy = 2;
63 usb_rpc_ids.vbus_pwr_up = 6;
64 usb_rpc_ids.vbus_pwr_down = 7;
65 usb_rpc_ids.update_product_id = 8;
66 usb_rpc_ids.update_serial_num = 9;
67 usb_rpc_ids.update_is_serial_num_null = 10;
68 usb_rpc_ids.reset_rework_installed = 17;
69 usb_rpc_ids.enable_pmic_ulpi_data0 = 18;
70 usb_rpc_ids.disable_pmic_ulpi_data0 = 19;
71 return 0;
72 } else if (vers == 0x00010002) {
73 usb_rpc_ids.prog = 0x30000064;
74 usb_rpc_ids.vers_comp = 0x00010002;
75 usb_rpc_ids.init_phy = 2;
76 usb_rpc_ids.vbus_pwr_up = 6;
77 usb_rpc_ids.vbus_pwr_down = 7;
78 usb_rpc_ids.update_product_id = 8;
79 usb_rpc_ids.update_serial_num = 9;
80 usb_rpc_ids.update_is_serial_num_null = 10;
81 usb_rpc_ids.reset_rework_installed = 17;
82 usb_rpc_ids.enable_pmic_ulpi_data0 = 18;
83 usb_rpc_ids.disable_pmic_ulpi_data0 = 19;
84 return 0;
85 } else {
86 pr_err("%s: no matches found for version\n",
87 __func__);
88 return -ENODATA;
89 }
90}
91
92static int msm_chg_init_rpc(unsigned long vers)
93{
94 if (((vers & RPC_VERSION_MAJOR_MASK) == 0x00010000) ||
95 ((vers & RPC_VERSION_MAJOR_MASK) == 0x00020000) ||
96 ((vers & RPC_VERSION_MAJOR_MASK) == 0x00030000) ||
97 ((vers & RPC_VERSION_MAJOR_MASK) == 0x00040000)) {
98 chg_ep = msm_rpc_connect_compatible(MSM_RPC_CHG_PROG, vers,
99 MSM_RPC_UNINTERRUPTIBLE);
100 if (IS_ERR(chg_ep))
101 return -ENODATA;
102 chg_rpc_ids.vers_comp = vers;
103 chg_rpc_ids.chg_usb_charger_connected_proc = 7;
104 chg_rpc_ids.chg_usb_charger_disconnected_proc = 8;
105 chg_rpc_ids.chg_usb_i_is_available_proc = 9;
106 chg_rpc_ids.chg_usb_i_is_not_available_proc = 10;
107 return 0;
108 } else
109 return -ENODATA;
110}
111
112/* rpc connect for hsusb */
113int msm_hsusb_rpc_connect(void)
114{
115
116 if (usb_ep && !IS_ERR(usb_ep)) {
117 pr_debug("%s: usb_ep already connected\n", __func__);
118 return 0;
119 }
120
121 /* Initialize rpc ids */
122 if (msm_hsusb_init_rpc_ids(0x00010001)) {
123 pr_err("%s: rpc ids initialization failed\n"
124 , __func__);
125 return -ENODATA;
126 }
127
128 usb_ep = msm_rpc_connect_compatible(usb_rpc_ids.prog,
129 usb_rpc_ids.vers_comp,
130 MSM_RPC_UNINTERRUPTIBLE);
131
132 if (IS_ERR(usb_ep)) {
133 pr_err("%s: connect compatible failed vers = %lx\n",
134 __func__, usb_rpc_ids.vers_comp);
135
136 /* Initialize rpc ids */
137 if (msm_hsusb_init_rpc_ids(0x00010002)) {
138 pr_err("%s: rpc ids initialization failed\n",
139 __func__);
140 return -ENODATA;
141 }
142 usb_ep = msm_rpc_connect_compatible(usb_rpc_ids.prog,
143 usb_rpc_ids.vers_comp,
144 MSM_RPC_UNINTERRUPTIBLE);
145 }
146
147 if (IS_ERR(usb_ep)) {
148 pr_err("%s: connect compatible failed vers = %lx\n",
149 __func__, usb_rpc_ids.vers_comp);
150 return -EAGAIN;
151 } else
152 pr_debug("%s: rpc connect success vers = %lx\n",
153 __func__, usb_rpc_ids.vers_comp);
154
155 return 0;
156}
157EXPORT_SYMBOL(msm_hsusb_rpc_connect);
158
159/* rpc connect for charging */
160int msm_chg_rpc_connect(void)
161{
162 uint32_t chg_vers;
163
164 if (machine_is_msm7x27_surf() || machine_is_qsd8x50_surf())
165 return -ENOTSUPP;
166
167 if (chg_ep && !IS_ERR(chg_ep)) {
168 pr_debug("%s: chg_ep already connected\n", __func__);
169 return 0;
170 }
171
172 chg_vers = 0x00040001;
173 if (!msm_chg_init_rpc(chg_vers))
174 goto chg_found;
175
176 chg_vers = 0x00030001;
177 if (!msm_chg_init_rpc(chg_vers))
178 goto chg_found;
179
180 chg_vers = 0x00020001;
181 if (!msm_chg_init_rpc(chg_vers))
182 goto chg_found;
183
184 chg_vers = 0x00010001;
185 if (!msm_chg_init_rpc(chg_vers))
186 goto chg_found;
187
188 pr_err("%s: connect compatible failed \n",
189 __func__);
190 return -EAGAIN;
191
192chg_found:
193 pr_debug("%s: connected to rpc vers = %x\n",
194 __func__, chg_vers);
195 return 0;
196}
197EXPORT_SYMBOL(msm_chg_rpc_connect);
198
199/* rpc call for phy_reset */
200int msm_hsusb_phy_reset(void)
201{
202 int rc = 0;
203 struct hsusb_phy_start_req {
204 struct rpc_request_hdr hdr;
205 } req;
206
207 if (!usb_ep || IS_ERR(usb_ep)) {
208 pr_err("%s: phy_reset rpc failed before call,"
209 "rc = %ld\n", __func__, PTR_ERR(usb_ep));
210 return -EAGAIN;
211 }
212
213 rc = msm_rpc_call(usb_ep, usb_rpc_ids.init_phy,
214 &req, sizeof(req), 5 * HZ);
215
216 if (rc < 0) {
217 pr_err("%s: phy_reset rpc failed! rc = %d\n",
218 __func__, rc);
219 } else
220 pr_debug("msm_hsusb_phy_reset\n");
221
222 return rc;
223}
224EXPORT_SYMBOL(msm_hsusb_phy_reset);
225
226/* rpc call for vbus powerup */
227int msm_hsusb_vbus_powerup(void)
228{
229 int rc = 0;
230 struct hsusb_phy_start_req {
231 struct rpc_request_hdr hdr;
232 } req;
233
234 if (!usb_ep || IS_ERR(usb_ep)) {
235 pr_err("%s: vbus_powerup rpc failed before call,"
236 "rc = %ld\n", __func__, PTR_ERR(usb_ep));
237 return -EAGAIN;
238 }
239
240 rc = msm_rpc_call(usb_ep, usb_rpc_ids.vbus_pwr_up,
241 &req, sizeof(req), 5 * HZ);
242
243 if (rc < 0) {
244 pr_err("%s: vbus_powerup failed! rc = %d\n",
245 __func__, rc);
246 } else
247 pr_debug("msm_hsusb_vbus_powerup\n");
248
249 return rc;
250}
251EXPORT_SYMBOL(msm_hsusb_vbus_powerup);
252
253/* rpc call for vbus shutdown */
254int msm_hsusb_vbus_shutdown(void)
255{
256 int rc = 0;
257 struct hsusb_phy_start_req {
258 struct rpc_request_hdr hdr;
259 } req;
260
261 if (!usb_ep || IS_ERR(usb_ep)) {
262 pr_err("%s: vbus_shutdown rpc failed before call,"
263 "rc = %ld\n", __func__, PTR_ERR(usb_ep));
264 return -EAGAIN;
265 }
266
267 rc = msm_rpc_call(usb_ep, usb_rpc_ids.vbus_pwr_down,
268 &req, sizeof(req), 5 * HZ);
269
270 if (rc < 0) {
271 pr_err("%s: vbus_shutdown failed! rc = %d\n",
272 __func__, rc);
273 } else
274 pr_debug("msm_hsusb_vbus_shutdown\n");
275
276 return rc;
277}
278EXPORT_SYMBOL(msm_hsusb_vbus_shutdown);
279
280int msm_hsusb_send_productID(uint32_t product_id)
281{
282 int rc = 0;
283 struct hsusb_phy_start_req {
284 struct rpc_request_hdr hdr;
285 uint32_t product_id;
286 } req;
287
288 if (!usb_ep || IS_ERR(usb_ep)) {
289 pr_err("%s: rpc connect failed: rc = %ld\n",
290 __func__, PTR_ERR(usb_ep));
291 return -EAGAIN;
292 }
293
294 req.product_id = cpu_to_be32(product_id);
295 rc = msm_rpc_call(usb_ep, usb_rpc_ids.update_product_id,
296 &req, sizeof(req),
297 5 * HZ);
298 if (rc < 0)
299 pr_err("%s: rpc call failed! error: %d\n",
300 __func__, rc);
301 else
302 pr_debug("%s: rpc call success\n" , __func__);
303
304 return rc;
305}
306EXPORT_SYMBOL(msm_hsusb_send_productID);
307
308int msm_hsusb_send_serial_number(const char *serial_number)
309{
310 int rc = 0, serial_len, rlen;
311 struct hsusb_send_sn_req {
312 struct rpc_request_hdr hdr;
313 uint32_t length;
314 char sn[0];
315 } *req;
316
317 if (!usb_ep || IS_ERR(usb_ep)) {
318 pr_err("%s: rpc connect failed: rc = %ld\n",
319 __func__, PTR_ERR(usb_ep));
320 return -EAGAIN;
321 }
322
323 /*
324 * USB driver passes null terminated string to us. Modem processor
325 * expects serial number to be 32 bit aligned.
326 */
327 serial_len = strlen(serial_number)+1;
328 rlen = sizeof(struct rpc_request_hdr) + sizeof(uint32_t) +
329 ((serial_len + 3) & ~3);
330
331 req = kmalloc(rlen, GFP_KERNEL);
332 if (!req)
333 return -ENOMEM;
334
335 req->length = cpu_to_be32(serial_len);
336 strncpy(req->sn , serial_number, serial_len);
337 rc = msm_rpc_call(usb_ep, usb_rpc_ids.update_serial_num,
338 req, rlen, 5 * HZ);
339 if (rc < 0)
340 pr_err("%s: rpc call failed! error: %d\n",
341 __func__, rc);
342 else
343 pr_debug("%s: rpc call success\n", __func__);
344
345 kfree(req);
346 return rc;
347}
348EXPORT_SYMBOL(msm_hsusb_send_serial_number);
349
350int msm_hsusb_is_serial_num_null(uint32_t val)
351{
352 int rc = 0;
353 struct hsusb_phy_start_req {
354 struct rpc_request_hdr hdr;
355 uint32_t value;
356 } req;
357
358 if (!usb_ep || IS_ERR(usb_ep)) {
359 pr_err("%s: rpc connect failed: rc = %ld\n",
360 __func__, PTR_ERR(usb_ep));
361 return -EAGAIN;
362 }
363 if (!usb_rpc_ids.update_is_serial_num_null) {
364 pr_err("%s: proc id not supported \n", __func__);
365 return -ENODATA;
366 }
367
368 req.value = cpu_to_be32(val);
369 rc = msm_rpc_call(usb_ep, usb_rpc_ids.update_is_serial_num_null,
370 &req, sizeof(req),
371 5 * HZ);
372 if (rc < 0)
373 pr_err("%s: rpc call failed! error: %d\n" ,
374 __func__, rc);
375 else
376 pr_debug("%s: rpc call success\n", __func__);
377
378 return rc;
379}
380EXPORT_SYMBOL(msm_hsusb_is_serial_num_null);
381
382int msm_chg_usb_charger_connected(uint32_t device)
383{
384 int rc = 0;
385 struct hsusb_start_req {
386 struct rpc_request_hdr hdr;
387 uint32_t otg_dev;
388 } req;
389
390 if (!chg_ep || IS_ERR(chg_ep))
391 return -EAGAIN;
392 req.otg_dev = cpu_to_be32(device);
393 rc = msm_rpc_call(chg_ep, chg_rpc_ids.chg_usb_charger_connected_proc,
394 &req, sizeof(req), 5 * HZ);
395
396 if (rc < 0) {
397 pr_err("%s: charger_connected failed! rc = %d\n",
398 __func__, rc);
399 } else
400 pr_debug("msm_chg_usb_charger_connected\n");
401
402 return rc;
403}
404EXPORT_SYMBOL(msm_chg_usb_charger_connected);
405
406int msm_chg_usb_i_is_available(uint32_t sample)
407{
408 int rc = 0;
409 struct hsusb_start_req {
410 struct rpc_request_hdr hdr;
411 uint32_t i_ma;
412 } req;
413
414 if (!chg_ep || IS_ERR(chg_ep))
415 return -EAGAIN;
416 req.i_ma = cpu_to_be32(sample);
417 rc = msm_rpc_call(chg_ep, chg_rpc_ids.chg_usb_i_is_available_proc,
418 &req, sizeof(req), 5 * HZ);
419
420 if (rc < 0) {
421 pr_err("%s: charger_i_available failed! rc = %d\n",
422 __func__, rc);
423 } else
424 pr_debug("msm_chg_usb_i_is_available(%u)\n", sample);
425
426 return rc;
427}
428EXPORT_SYMBOL(msm_chg_usb_i_is_available);
429
430int msm_chg_usb_i_is_not_available(void)
431{
432 int rc = 0;
433 struct hsusb_start_req {
434 struct rpc_request_hdr hdr;
435 } req;
436
437 if (!chg_ep || IS_ERR(chg_ep))
438 return -EAGAIN;
439 rc = msm_rpc_call(chg_ep, chg_rpc_ids.chg_usb_i_is_not_available_proc,
440 &req, sizeof(req), 5 * HZ);
441
442 if (rc < 0) {
443 pr_err("%s: charger_i_not_available failed! rc ="
444 "%d \n", __func__, rc);
445 } else
446 pr_debug("msm_chg_usb_i_is_not_available\n");
447
448 return rc;
449}
450EXPORT_SYMBOL(msm_chg_usb_i_is_not_available);
451
452int msm_chg_usb_charger_disconnected(void)
453{
454 int rc = 0;
455 struct hsusb_start_req {
456 struct rpc_request_hdr hdr;
457 } req;
458
459 if (!chg_ep || IS_ERR(chg_ep))
460 return -EAGAIN;
461 rc = msm_rpc_call(chg_ep, chg_rpc_ids.chg_usb_charger_disconnected_proc,
462 &req, sizeof(req), 5 * HZ);
463
464 if (rc < 0) {
465 pr_err("%s: charger_disconnected failed! rc = %d\n",
466 __func__, rc);
467 } else
468 pr_debug("msm_chg_usb_charger_disconnected\n");
469
470 return rc;
471}
472EXPORT_SYMBOL(msm_chg_usb_charger_disconnected);
473
474/* rpc call to close connection */
475int msm_hsusb_rpc_close(void)
476{
477 int rc = 0;
478
479 if (IS_ERR(usb_ep)) {
480 pr_err("%s: rpc_close failed before call, rc = %ld\n",
481 __func__, PTR_ERR(usb_ep));
482 return -EAGAIN;
483 }
484
485 rc = msm_rpc_close(usb_ep);
486 usb_ep = NULL;
487
488 if (rc < 0) {
489 pr_err("%s: close rpc failed! rc = %d\n",
490 __func__, rc);
491 return -EAGAIN;
492 } else
493 pr_debug("rpc close success\n");
494
495 return rc;
496}
497EXPORT_SYMBOL(msm_hsusb_rpc_close);
498
499/* rpc call to close charging connection */
500int msm_chg_rpc_close(void)
501{
502 int rc = 0;
503
504 if (IS_ERR(chg_ep)) {
505 pr_err("%s: rpc_close failed before call, rc = %ld\n",
506 __func__, PTR_ERR(chg_ep));
507 return -EAGAIN;
508 }
509
510 rc = msm_rpc_close(chg_ep);
511 chg_ep = NULL;
512
513 if (rc < 0) {
514 pr_err("%s: close rpc failed! rc = %d\n",
515 __func__, rc);
516 return -EAGAIN;
517 } else
518 pr_debug("rpc close success\n");
519
520 return rc;
521}
522EXPORT_SYMBOL(msm_chg_rpc_close);
523
524int msm_hsusb_reset_rework_installed(void)
525{
526 int rc = 0;
527 struct hsusb_start_req {
528 struct rpc_request_hdr hdr;
529 } req;
530 struct hsusb_rpc_rep {
531 struct rpc_reply_hdr hdr;
532 uint32_t rework;
533 } rep;
534
535 memset(&rep, 0, sizeof(rep));
536
537 if (!usb_ep || IS_ERR(usb_ep)) {
538 pr_err("%s: hsusb rpc connection not initialized, rc = %ld\n",
539 __func__, PTR_ERR(usb_ep));
540 return -EAGAIN;
541 }
542
543 rc = msm_rpc_call_reply(usb_ep, usb_rpc_ids.reset_rework_installed,
544 &req, sizeof(req),
545 &rep, sizeof(rep), 5 * HZ);
546
547 if (rc < 0) {
548 pr_err("%s: rpc call failed! error: (%d)"
549 "proc id: (%lx)\n",
550 __func__, rc,
551 usb_rpc_ids.reset_rework_installed);
552 return rc;
553 }
554
555 pr_info("%s: rework: (%d)\n", __func__, rep.rework);
556 return be32_to_cpu(rep.rework);
557}
558EXPORT_SYMBOL(msm_hsusb_reset_rework_installed);
559
560static int msm_hsusb_pmic_ulpidata0_config(int enable)
561{
562 int rc = 0;
563 struct hsusb_start_req {
564 struct rpc_request_hdr hdr;
565 } req;
566
567 if (!usb_ep || IS_ERR(usb_ep)) {
568 pr_err("%s: hsusb rpc connection not initialized, rc = %ld\n",
569 __func__, PTR_ERR(usb_ep));
570 return -EAGAIN;
571 }
572
573 if (enable)
574 rc = msm_rpc_call(usb_ep, usb_rpc_ids.enable_pmic_ulpi_data0,
575 &req, sizeof(req), 5 * HZ);
576 else
577 rc = msm_rpc_call(usb_ep, usb_rpc_ids.disable_pmic_ulpi_data0,
578 &req, sizeof(req), 5 * HZ);
579
580 if (rc < 0)
581 pr_err("%s: rpc call failed! error: %d\n",
582 __func__, rc);
583 return rc;
584}
585
586int msm_hsusb_enable_pmic_ulpidata0(void)
587{
588 return msm_hsusb_pmic_ulpidata0_config(1);
589}
590EXPORT_SYMBOL(msm_hsusb_enable_pmic_ulpidata0);
591
592int msm_hsusb_disable_pmic_ulpidata0(void)
593{
594 return msm_hsusb_pmic_ulpidata0_config(0);
595}
596EXPORT_SYMBOL(msm_hsusb_disable_pmic_ulpidata0);
597
598
599/* wrapper for sending pid and serial# info to bootloader */
600int usb_diag_update_pid_and_serial_num(uint32_t pid, const char *snum)
601{
602 int ret;
603
604 ret = msm_hsusb_send_productID(pid);
605 if (ret)
606 return ret;
607
608 if (!snum) {
609 ret = msm_hsusb_is_serial_num_null(1);
610 if (ret)
611 return ret;
612 }
613
614 ret = msm_hsusb_is_serial_num_null(0);
615 if (ret)
616 return ret;
617 ret = msm_hsusb_send_serial_number(snum);
618 if (ret)
619 return ret;
620
621 return 0;
622}
623
624
Lena Salman57d167e2012-03-21 19:46:38 +0200625#ifdef CONFIG_USB_MSM_72K
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700626/* charger api wrappers */
627int hsusb_chg_init(int connect)
628{
629 if (connect)
630 return msm_chg_rpc_connect();
631 else
632 return msm_chg_rpc_close();
633}
634EXPORT_SYMBOL(hsusb_chg_init);
635
636void hsusb_chg_vbus_draw(unsigned mA)
637{
638 msm_chg_usb_i_is_available(mA);
639}
640EXPORT_SYMBOL(hsusb_chg_vbus_draw);
641
642void hsusb_chg_connected(enum chg_type chgtype)
643{
644 char *chg_types[] = {"STD DOWNSTREAM PORT",
645 "CARKIT",
646 "DEDICATED CHARGER",
647 "INVALID"};
648
649 if (chgtype == USB_CHG_TYPE__INVALID) {
650 msm_chg_usb_i_is_not_available();
651 msm_chg_usb_charger_disconnected();
652 return;
653 }
654
655 pr_info("\nCharger Type: %s\n", chg_types[chgtype]);
656
657 msm_chg_usb_charger_connected(chgtype);
658}
659EXPORT_SYMBOL(hsusb_chg_connected);
660#endif