1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
|
/* ==========================================================================
* $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_linux.c $
* $Revision: #6 $
* $Date: 2009/02/18 $
* $Change: 1190679 $
*
* Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
* "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
* otherwise expressly agreed to in writing between Synopsys and you.
*
* The Software IS NOT an item of Licensed Software or Licensed Product under
* any End User Software License Agreement or Agreement for Licensed Product
* with Synopsys or any supplement thereto. You are permitted to use and
* redistribute this Software in source and binary forms, with or without
* modification, provided that redistributions of source code must retain this
* notice. You may not view, use, disclose, copy or distribute this file or
* any information contained herein except pursuant to this license grant from
* Synopsys. If you do not agree with this notice, including the disclaimer
* below, then you are not authorized to use the Software.
*
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
* ========================================================================== */
#ifndef DWC_HOST_ONLY
/** @file
* This file implements the Peripheral Controller Driver.
*
* The Peripheral Controller Driver (PCD) is responsible for
* translating requests from the Function Driver into the appropriate
* actions on the DWC_otg controller. It isolates the Function Driver
* from the specifics of the controller by providing an API to the
* Function Driver.
*
* The Peripheral Controller Driver for Linux will implement the
* Gadget API, so that the existing Gadget drivers can be used.
* (Gadget Driver is the Linux terminology for a Function Driver.)
*
* The Linux Gadget API is defined in the header file
* <code><linux/usb_gadget.h></code>. The USB EP operations API is
* defined in the structure <code>usb_ep_ops</code> and the USB
* Controller API is defined in the structure
* <code>usb_gadget_ops</code>.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <asm/io.h>
# include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/wakelock.h>
#include <linux/switch.h>
#include <soc/sprd/usb.h>
#include "dwc_otg_driver.h"
#include "dwc_otg_pcd_if.h"
#include "dwc_otg_cil.h"
#include "dwc_otg_pcd.h"
#include "dwc_otg_dbg.h"
#include "usb_hw.h"
static struct gadget_wrapper {
dwc_otg_pcd_t *pcd;
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct usb_ep ep0;
struct usb_ep in_ep[16];
struct usb_ep out_ep[16];
/*
* this timer is used for checking cable type, usb or ac adapter.
*/
struct workqueue_struct *cable2pc_wq;
struct workqueue_struct *detect_wq;
struct workqueue_struct *reinit_wq;
struct work_struct detect_work;
struct work_struct reinit_work;
struct delayed_work cable2pc;
struct switch_dev sdev;
int udc_startup;
int enabled;
int vbus;
spinlock_t lock;
} *gadget_wrapper;
static struct wake_lock usb_wake_lock;
static u64 dwc_otg_pcd_dmamask = DMA_BIT_MASK(32);
static DEFINE_MUTEX(udc_lock);
#define CABLE_TIMEOUT (HZ*15)
/* Display the contents of the buffer */
extern void dump_msg(const u8 * buf, unsigned int length);
extern int in_calibration(void);
extern void dwc_otg_pcd_stop(dwc_otg_pcd_t *pcd);
static void dwc_otg_clear_all_int(dwc_otg_core_if_t *core_if);
static struct timer_list reinit_usb_timer;
static int factory_mode = false;
static int charge_mode = false; /* add by lgd, fix the problem that sometime poweroff charging is so slow */
static int __init factory_start(char *str)
{
if(str)
pr_info("factory mode!\n");
factory_mode = true;
return 1;
}
__setup("factory", factory_start);
int in_factory_mode(void)
{
return (factory_mode == true);
}
/* < add by lgd, fix the problem that sometime poweroff charging is so slow */
static int __init charge_start(char *str)
{
if(!strncmp(str,"charger",7))
{
DWC_DEBUG("charge mode!\n");
charge_mode = true;
return 1;
} else {
DWC_DEBUG("no charge mode!\n");
charge_mode = false;
return 0;
}
}
__setup("androidboot.mode=", charge_start);
int in_charge_mode(void)
{
DWC_DEBUG("%s() in\n", __func__);
return (charge_mode == true);
}
/* add by lgd, fix the problem that sometime poweroff charging is so slow > */
static void usb_reinit_works(void)
{
struct gadget_wrapper *d;
d = gadget_wrapper;
DWC_PRINTF("reinit start ...\n");
dwc_otg_disable_global_interrupts(GET_CORE_IF(d->pcd));
dwc_otg_clear_all_int(GET_CORE_IF(d->pcd));
dwc_otg_pcd_stop(d->pcd);
udc_disable();
mdelay(200);
udc_enable();
dwc_otg_core_init(GET_CORE_IF(d->pcd));
dwc_otg_enable_global_interrupts(GET_CORE_IF(d->pcd));
dwc_otg_core_dev_init(GET_CORE_IF(d->pcd));
}
static void reinit_usb_timer_fun(unsigned long para)
{
struct gadget_wrapper *d;
d = gadget_wrapper;
queue_work(d->reinit_wq, &d->reinit_work);
del_timer(&reinit_usb_timer);
}
void _reinit_usb_later(dwc_otg_pcd_t *pcd)
{
dctl_data_t dctl = {.d32=0};
dwc_otg_dev_if_t *dev_if;
if (pcd->reinit_flag == 0)
return;
dev_if = pcd->core_if->dev_if;
dctl.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dctl);
dctl.b.sftdiscon = 1;
DWC_WRITE_REG32(&dev_if->dev_global_regs->dctl, dctl.d32);
DWC_PRINTF("call reinit USB controller...\n");
mod_timer(&reinit_usb_timer, jiffies + 3*HZ);
}
static struct timer_list setup_transfer_timer;
static int setup_transfer_timer_start = 0;
static void setup_transfer_timer_fun(unsigned long para)
{
if (gadget_wrapper->pcd->reinit_flag) {
setup_transfer_timer_start = 0;
_reinit_usb_later(gadget_wrapper->pcd);
}
}
/* USB Endpoint Operations */
/*
* The following sections briefly describe the behavior of the Gadget
* API endpoint operations implemented in the DWC_otg driver
* software. Detailed descriptions of the generic behavior of each of
* these functions can be found in the Linux header file
* include/linux/usb_gadget.h.
*
* The Gadget API provides wrapper functions for each of the function
* pointers defined in usb_ep_ops. The Gadget Driver calls the wrapper
* function, which then calls the underlying PCD function. The
* following sections are named according to the wrapper
* functions. Within each section, the corresponding DWC_otg PCD
* function name is specified.
*
*/
/**
* This function is called by the Gadget Driver for each EP to be
* configured for the current configuration (SET_CONFIGURATION).
*
* This function initializes the dwc_otg_ep_t data structure, and then
* calls dwc_otg_ep_activate.
*/
static int ep_enable(struct usb_ep *usb_ep,
const struct usb_endpoint_descriptor *ep_desc)
{
int retval;
if (!usb_ep || !ep_desc || ep_desc->bDescriptorType != USB_DT_ENDPOINT) {
DWC_WARN("%s, bad ep or descriptor\n", __func__);
return -EINVAL;
}
if (usb_ep == &gadget_wrapper->ep0) {
DWC_WARN("%s, bad ep(0)\n", __func__);
return -EINVAL;
}
/* Check FIFO size? */
if (!ep_desc->wMaxPacketSize) {
DWC_WARN("%s, bad %s maxpacket\n", __func__, usb_ep->name);
return -ERANGE;
}
if (!gadget_wrapper->driver ||
gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) {
DWC_WARN("%s, bogus device state\n", __func__);
return -ESHUTDOWN;
}
retval = dwc_otg_pcd_ep_enable(gadget_wrapper->pcd,
(const uint8_t *)ep_desc,
(void *)usb_ep);
if (retval) {
DWC_WARN("dwc_otg_pcd_ep_enable failed\n");
return -EINVAL;
}
usb_ep->maxpacket = le16_to_cpu(ep_desc->wMaxPacketSize);
return 0;
}
/**
* This function is called when an EP is disabled due to disconnect or
* change in configuration. Any pending requests will terminate with a
* status of -ESHUTDOWN.
*
* This function modifies the dwc_otg_ep_t data structure for this EP,
* and then calls dwc_otg_ep_deactivate.
*/
static int ep_disable(struct usb_ep *usb_ep)
{
int retval;
DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, usb_ep);
if (!usb_ep) {
DWC_DEBUGPL(DBG_PCD, "%s, %s not enabled\n", __func__,
usb_ep ? usb_ep->name : NULL);
return -EINVAL;
}
retval = dwc_otg_pcd_ep_disable(gadget_wrapper->pcd, usb_ep);
if (retval) {
retval = -EINVAL;
}
return retval;
}
/**
* This function allocates a request object to use with the specified
* endpoint.
*
* @param ep The endpoint to be used with with the request
* @param gfp_flags the GFP_* flags to use.
*/
static struct usb_request *dwc_otg_pcd_alloc_request(struct usb_ep *ep,
gfp_t gfp_flags)
{
struct usb_request *usb_req;
DWC_DEBUGPL(DBG_PCDV, "%s(%p,%d)\n", __func__, ep, gfp_flags);
if (0 == ep) {
DWC_WARN("%s() %s\n", __func__, "Invalid EP!\n");
return 0;
}
usb_req = kmalloc(sizeof(*usb_req), gfp_flags);
if (0 == usb_req) {
DWC_WARN("%s() %s\n", __func__, "request allocation failed!\n");
return 0;
}
memset(usb_req, 0, sizeof(*usb_req));
usb_req->dma = DWC_DMA_ADDR_INVALID;
return usb_req;
}
/**
* This function frees a request object.
*
* @param ep The endpoint associated with the request
* @param req The request being freed
*/
static void dwc_otg_pcd_free_request(struct usb_ep *ep, struct usb_request *req)
{
DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, ep, req);
if (0 == ep || 0 == req) {
DWC_WARN("%s() %s\n", __func__,
"Invalid ep or req argument!\n");
return;
}
kfree(req);
}
/**
* This function is used to submit an I/O Request to an EP.
*
* - When the request completes the request's completion callback
* is called to return the request to the driver.
* - An EP, except control EPs, may have multiple requests
* pending.
* - Once submitted the request cannot be examined or modified.
* - Each request is turned into one or more packets.
* - A BULK EP can queue any amount of data; the transfer is
* packetized.
* - Zero length Packets are specified with the request 'zero'
* flag.
*/
static int ep_queue(struct usb_ep *usb_ep, struct usb_request *usb_req,
gfp_t gfp_flags)
{
dwc_otg_pcd_t *pcd;
int retval = 0;
//trace_printk("(%p,%p,%d)\n",
// usb_ep, usb_req, gfp_flags);
if (!usb_req || !usb_req->complete ||
(!gadget_wrapper->gadget.sg_supported &&
!usb_req->buf)) {
DWC_WARN("bad params\n");
return -EINVAL;
}
if (!usb_ep) {
DWC_WARN("bad ep\n");
return -EINVAL;
}
pcd = gadget_wrapper->pcd;
if (!gadget_wrapper->driver ||
gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) {
DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n",
gadget_wrapper->gadget.speed);
DWC_WARN("bogus device state\n");
return -ESHUTDOWN;
}
//trace_printk( "%s queue req %p, len %d buf %p\n",
// usb_ep->name, usb_req, usb_req->length, usb_req->buf);
usb_req->status = -EINPROGRESS;
usb_req->actual = 0;
retval = dwc_otg_pcd_ep_queue(pcd, usb_ep, usb_req->buf, usb_req->dma/*dma_addr*/,
usb_req->length, usb_req->zero, usb_req->num_sgs,
usb_req->sg, usb_req, gfp_flags == GFP_ATOMIC ? 1 : 0);
if (retval) {
pr_err("%s, cannot enqueue a renquest, err :%d\n", __func__,
retval);
pr_info( "%s queue req %p, len %d buf %p\n",
usb_ep->name, usb_req, usb_req->length, usb_req->buf);
return -EINVAL;
}
return 0;
}
/**
* This function cancels an I/O request from an EP.
*/
static int ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req)
{
if (!usb_ep || !usb_req) {
DWC_WARN("bad argument\n");
return -EINVAL;
}
if (!gadget_wrapper->driver ||
gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) {
DWC_WARN("bogus device state\n");
return -ESHUTDOWN;
}
if (dwc_otg_pcd_ep_dequeue(gadget_wrapper->pcd, usb_ep, usb_req)) {
return -EINVAL;
}
return 0;
}
/**
* usb_ep_set_halt stalls an endpoint.
*
* usb_ep_clear_halt clears an endpoint halt and resets its data
* toggle.
*
* Both of these functions are implemented with the same underlying
* function. The behavior depends on the value argument.
*
* @param[in] usb_ep the Endpoint to halt or clear halt.
* @param[in] value
* - 0 means clear_halt.
* - 1 means set_halt,
* - 2 means clear stall lock flag.
* - 3 means set stall lock flag.
*/
static int ep_halt(struct usb_ep *usb_ep, int value)
{
int retval = 0;
DWC_DEBUGPL(DBG_PCD, "HALT %s %d\n", usb_ep->name, value);
if (!usb_ep) {
DWC_WARN("bad ep\n");
return -EINVAL;
}
retval = dwc_otg_pcd_ep_halt(gadget_wrapper->pcd, usb_ep, value);
if (retval == -DWC_E_AGAIN) {
return -EAGAIN;
} else if (retval) {
retval = -EINVAL;
}
return retval;
}
#ifdef DWC_EN_ISOC
/**
* This function is used to submit an ISOC Transfer Request to an EP.
*
* - Every time a sync period completes the request's completion callback
* is called to provide data to the gadget driver.
* - Once submitted the request cannot be modified.
* - Each request is turned into periodic data packets untill ISO
* Transfer is stopped..
*/
static int iso_ep_start(struct usb_ep *usb_ep, struct usb_iso_request *req,
gfp_t gfp_flags)
{
int retval = 0;
if (!req || !req->process_buffer || !req->buf0 || !req->buf1) {
DWC_WARN("bad params\n");
return -EINVAL;
}
if (!usb_ep) {
DWC_PRINTF("bad params\n");
return -EINVAL;
}
req->status = -EINPROGRESS;
retval =
dwc_otg_pcd_iso_ep_start(gadget_wrapper->pcd, usb_ep, req->buf0,
req->buf1, req->dma0, req->dma1,
req->sync_frame, req->data_pattern_frame,
req->data_per_frame,
req->flags & USB_REQ_ISO_ASAP ? -1 : req->
start_frame, req->buf_proc_intrvl, req,
gfp_flags == GFP_ATOMIC ? 1 : 0);
if (retval) {
return -EINVAL;
}
return retval;
}
/**
* This function stops ISO EP Periodic Data Transfer.
*/
static int iso_ep_stop(struct usb_ep *usb_ep, struct usb_iso_request *req)
{
int retval = 0;
if (!usb_ep) {
DWC_WARN("bad ep\n");
}
if (!gadget_wrapper->driver ||
gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) {
DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n",
gadget_wrapper->gadget.speed);
DWC_WARN("bogus device state\n");
}
dwc_otg_pcd_iso_ep_stop(gadget_wrapper->pcd, usb_ep, req);
if (retval) {
retval = -EINVAL;
}
return retval;
}
static struct usb_iso_request *alloc_iso_request(struct usb_ep *ep,
int packets, gfp_t gfp_flags)
{
struct usb_iso_request *pReq = NULL;
uint32_t req_size;
req_size = sizeof(struct usb_iso_request);
req_size +=
(2 * packets * (sizeof(struct usb_gadget_iso_packet_descriptor)));
pReq = kmalloc(req_size, gfp_flags);
if (!pReq) {
DWC_WARN("Can't allocate Iso Request\n");
return 0;
}
pReq->iso_packet_desc0 = (void *)(pReq + 1);
pReq->iso_packet_desc1 = pReq->iso_packet_desc0 + packets;
return pReq;
}
static void free_iso_request(struct usb_ep *ep, struct usb_iso_request *req)
{
kfree(req);
}
static struct usb_isoc_ep_ops dwc_otg_pcd_ep_ops = {
.ep_ops = {
.enable = ep_enable,
.disable = ep_disable,
.alloc_request = dwc_otg_pcd_alloc_request,
.free_request = dwc_otg_pcd_free_request,
.alloc_buffer = dwc_otg_pcd_alloc_buffer,
.free_buffer = dwc_otg_pcd_free_buffer,
.queue = ep_queue,
.dequeue = ep_dequeue,
.set_halt = ep_halt,
.fifo_status = 0,
.fifo_flush = 0,
},
.iso_ep_start = iso_ep_start,
.iso_ep_stop = iso_ep_stop,
.alloc_iso_request = alloc_iso_request,
.free_iso_request = free_iso_request,
};
#else
static struct usb_ep_ops dwc_otg_pcd_ep_ops = {
.enable = ep_enable,
.disable = ep_disable,
.alloc_request = dwc_otg_pcd_alloc_request,
.free_request = dwc_otg_pcd_free_request,
.queue = ep_queue,
.dequeue = ep_dequeue,
.set_halt = ep_halt,
.fifo_status = 0,
.fifo_flush = 0,
};
#endif /* _EN_ISOC_ */
/* Gadget Operations */
/**
* The following gadget operations will be implemented in the DWC_otg
* PCD. Functions in the API that are not described below are not
* implemented.
*
* The Gadget API provides wrapper functions for each of the function
* pointers defined in usb_gadget_ops. The Gadget Driver calls the
* wrapper function, which then calls the underlying PCD function. The
* following sections are named according to the wrapper functions
* (except for ioctl, which doesn't have a wrapper function). Within
* each section, the corresponding DWC_otg PCD function name is
* specified.
*
*/
/**
*Gets the USB Frame number of the last SOF.
*/
static int get_frame_number(struct usb_gadget *gadget)
{
struct gadget_wrapper *d;
DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, gadget);
if (gadget == 0) {
return -ENODEV;
}
d = container_of(gadget, struct gadget_wrapper, gadget);
return dwc_otg_pcd_get_frame_number(d->pcd);
}
#ifdef CONFIG_USB_DWC_OTG_LPM
static int test_lpm_enabled(struct usb_gadget *gadget)
{
struct gadget_wrapper *d;
d = container_of(gadget, struct gadget_wrapper, gadget);
return dwc_otg_pcd_is_lpm_enabled(d->pcd);
}
#endif
/**
* Initiates Session Request Protocol (SRP) to wakeup the host if no
* session is in progress. If a session is already in progress, but
* the device is suspended, remote wakeup signaling is started.
*
*/
/*
* currently we don't need remote wakeup, for it will cause USBCV
* test fail;
*/
static int wakeup(struct usb_gadget *gadget)__attribute__((unused));
static int wakeup(struct usb_gadget *gadget)
{
struct gadget_wrapper *d;
DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, gadget);
if (gadget == 0) {
return -ENODEV;
} else {
d = container_of(gadget, struct gadget_wrapper, gadget);
}
dwc_otg_pcd_wakeup(d->pcd);
return 0;
}
extern void dwc_otg_pcd_stop(dwc_otg_pcd_t *pcd);
static void __udc_startup(void);
static void __udc_shutdown(void);
static int pullup(struct usb_gadget *gadget, int is_on)
{
struct gadget_wrapper *d;
int action = is_on;
#ifndef DWC_DEVICE_ONLY
if(!usb_get_id_state())
return 0;
#endif
if (gadget == 0)
return -ENODEV;
else
d = container_of(gadget, struct gadget_wrapper, gadget);
if (!d->enabled || !d->vbus)
action = 0;
mutex_lock(&udc_lock);
if (action) {
queue_delayed_work(d->cable2pc_wq, &d->cable2pc,CABLE_TIMEOUT);
__udc_startup();
} else {
/*
*this is soft disconnct, and maybe the timer started
*by plugin still work, need cancel this timer like plugout
*/
cancel_delayed_work_sync(&d->cable2pc);
__udc_shutdown();
}
mutex_unlock(&udc_lock);
return 0;
}
static int dwc_usb_gadget_start(struct usb_gadget *, struct usb_gadget_driver *);
static int dwc_usb_gadget_stop(struct usb_gadget *, struct usb_gadget_driver *);
static const struct usb_gadget_ops dwc_otg_pcd_ops = {
.get_frame = get_frame_number,
//.wakeup = wakeup,
#ifdef CONFIG_USB_DWC_OTG_LPM
.lpm_support = test_lpm_enabled,
#endif
.pullup = pullup,
// current versions must always be self-powered
.udc_start = dwc_usb_gadget_start,
.udc_stop = dwc_usb_gadget_stop,
};
static int _setup(dwc_otg_pcd_t * pcd, uint8_t * bytes)
{
int retval = -DWC_E_NOT_SUPPORTED;
if (pcd->reinit_flag == 1 && setup_transfer_timer_start == 0) {
setup_transfer_timer_start = 1;
mod_timer(&setup_transfer_timer, jiffies + HZ/2);
}
if (gadget_wrapper->driver && gadget_wrapper->driver->setup) {
retval = gadget_wrapper->driver->setup(&gadget_wrapper->gadget,
(struct usb_ctrlrequest
*)bytes);
}
trace_printk("setup res val: %d\n", retval);
//sword
//if (retval == -ENOTSUPP) {
if (retval == -EOPNOTSUPP) {
retval = -DWC_E_NOT_SUPPORTED;
} else if (retval < 0) {
retval = -DWC_E_INVALID;
}
return retval;
}
#ifdef DWC_EN_ISOC
static int _isoc_complete(dwc_otg_pcd_t * pcd, void *ep_handle,
void *req_handle, int proc_buf_num)
{
int i, packet_count;
struct usb_gadget_iso_packet_descriptor *iso_packet = 0;
struct usb_iso_request *iso_req = req_handle;
if (proc_buf_num) {
iso_packet = iso_req->iso_packet_desc1;
} else {
iso_packet = iso_req->iso_packet_desc0;
}
packet_count =
dwc_otg_pcd_get_iso_packet_count(pcd, ep_handle, req_handle);
for (i = 0; i < packet_count; ++i) {
int status;
int actual;
int offset;
dwc_otg_pcd_get_iso_packet_params(pcd, ep_handle, req_handle,
i, &status, &actual, &offset);
switch (status) {
case -DWC_E_NO_DATA:
status = -ENODATA;
break;
default:
if (status) {
DWC_PRINTF("unknown status in isoc packet\n");
}
}
iso_packet[i].status = status;
iso_packet[i].offset = offset;
iso_packet[i].actual_length = actual;
}
iso_req->status = 0;
iso_req->process_buffer(ep_handle, iso_req);
return 0;
}
#endif /* DWC_EN_ISOC */
static int _complete(dwc_otg_pcd_t * pcd, void *ep_handle,
void *req_handle, int32_t status, uint32_t actual)
{
struct usb_request *req = (struct usb_request *)req_handle;
if (req && req->complete) {
switch (status) {
case -DWC_E_SHUTDOWN:
req->status = -ESHUTDOWN;
break;
case -DWC_E_RESTART:
req->status = -ECONNRESET;
break;
case -DWC_E_INVALID:
req->status = -EINVAL;
break;
case -DWC_E_TIMEOUT:
req->status = -ETIMEDOUT;
break;
default:
req->status = status;
}
if (pcd->ep0.priv == ep_handle && pcd->reinit_flag == 1) {
pcd->reinit_flag = 3;
setup_transfer_timer_start = 0;
del_timer(&setup_transfer_timer);
}
req->actual = actual;
DWC_SPINUNLOCK(pcd->lock);
req->complete(ep_handle, req);
DWC_SPINLOCK(pcd->lock);
}
return 0;
}
static int _connect(dwc_otg_pcd_t * pcd, int speed)
{
gadget_wrapper->gadget.speed = speed;
return 0;
}
static int _disconnect(dwc_otg_pcd_t * pcd)
{
if (gadget_wrapper->driver && gadget_wrapper->driver->disconnect) {
gadget_wrapper->driver->disconnect(&gadget_wrapper->gadget);
}
return 0;
}
static int _resume(dwc_otg_pcd_t * pcd)
{
if (gadget_wrapper->driver && gadget_wrapper->driver->resume) {
gadget_wrapper->driver->resume(&gadget_wrapper->gadget);
}
return 0;
}
static int _suspend(dwc_otg_pcd_t * pcd)
{
if (gadget_wrapper->driver && gadget_wrapper->driver->suspend) {
gadget_wrapper->driver->suspend(&gadget_wrapper->gadget);
}
return 0;
}
/**
* This function updates the otg values in the gadget structure.
*/
static int _hnp_changed(dwc_otg_pcd_t * pcd)
{
if (!gadget_wrapper->gadget.is_otg)
return 0;
gadget_wrapper->gadget.b_hnp_enable = get_b_hnp_enable(pcd);
gadget_wrapper->gadget.a_hnp_support = get_a_hnp_support(pcd);
gadget_wrapper->gadget.a_alt_hnp_support = get_a_alt_hnp_support(pcd);
return 0;
}
static int _reset(dwc_otg_pcd_t * pcd)
{
return 0;
}
#ifdef DWC_UTE_CFI
static int _cfi_setup(dwc_otg_pcd_t * pcd, void *cfi_req)
{
int retval = -DWC_E_INVALID;
if (gadget_wrapper->driver->cfi_feature_setup) {
retval =
gadget_wrapper->driver->cfi_feature_setup(&gadget_wrapper->
gadget,
(struct
cfi_usb_ctrlrequest
*)cfi_req);
}
return retval;
}
#endif
static const struct dwc_otg_pcd_function_ops fops = {
.complete = _complete,
#ifdef DWC_EN_ISOC
.isoc_complete = _isoc_complete,
#endif
.setup = _setup,
.disconnect = _disconnect,
.connect = _connect,
.resume = _resume,
.suspend = _suspend,
.hnp_changed = _hnp_changed,
.reset = _reset,
#ifdef DWC_UTE_CFI
.cfi_setup = _cfi_setup,
#endif
};
/**
* This function is the top level PCD interrupt handler.
*/
static irqreturn_t dwc_otg_pcd_irq(int irq, void *dev)
{
dwc_otg_pcd_t *pcd = dev;
int32_t retval = IRQ_NONE;
retval = dwc_otg_pcd_handle_intr(pcd);
if (retval != 0) {
S3C2410X_CLEAR_EINTPEND();
}
return IRQ_RETVAL(retval);
}
/**
* This function initialized the usb_ep structures to there default
* state.
*
* @param d Pointer on gadget_wrapper.
*/
void gadget_add_eps(struct gadget_wrapper *d)
{
static const char *names[] = {
"ep0",
"ep1in",
"ep2in",
"ep3in",
"ep4in",
"ep5in",
"ep6in",
"ep7in",
"ep8in",
"ep9in",
"ep10in",
"ep11in",
"ep12in",
"ep13in",
"ep14in",
"ep15in",
"ep1out",
"ep2out",
"ep3out",
"ep4out",
"ep5out",
"ep6out",
"ep7out",
"ep8out",
"ep9out",
"ep10out",
"ep11out",
"ep12out",
"ep13out",
"ep14out",
"ep15out"
};
int i;
struct usb_ep *ep;
DWC_DEBUGPL(DBG_PCDV, "%s\n", __func__);
INIT_LIST_HEAD(&d->gadget.ep_list);
d->gadget.ep0 = &d->ep0;
d->gadget.speed = USB_SPEED_UNKNOWN;
INIT_LIST_HEAD(&d->gadget.ep0->ep_list);
/**
* Initialize the EP0 structure.
*/
ep = &d->ep0;
/* Init the usb_ep structure. */
ep->name = names[0];
ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops;
/**
* @todo NGS: What should the max packet size be set to
* here? Before EP type is set?
*/
ep->maxpacket = MAX_PACKET_SIZE;
dwc_otg_pcd_ep_enable(d->pcd, NULL, ep);
list_add_tail(&ep->ep_list, &d->gadget.ep_list);
/**
* Initialize the EP structures.
*/
for (i = 0; i < 15; i++) {
ep = &d->in_ep[i];
/* Init the usb_ep structure. */
ep->name = names[i + 1];
ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops;
/**
* @todo NGS: What should the max packet size be set to
* here? Before EP type is set?
*/
ep->maxpacket = MAX_PACKET_SIZE;
list_add_tail(&ep->ep_list, &d->gadget.ep_list);
}
for (i = 0; i < 15; i++) {
ep = &d->out_ep[i];
/* Init the usb_ep structure. */
ep->name = names[15 + i + 1];
ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops;
/**
* @todo NGS: What should the max packet size be set to
* here? Before EP type is set?
*/
ep->maxpacket = MAX_PACKET_SIZE;
list_add_tail(&ep->ep_list, &d->gadget.ep_list);
}
/* remove ep0 from the list. There is a ep0 pointer. */
list_del_init(&d->ep0.ep_list);
d->ep0.maxpacket = MAX_EP0_SIZE;
}
/**
* This function releases the Gadget device.
* required by device_unregister().
*
* @todo Should this do something? Should it free the PCD?
*/
static void dwc_otg_pcd_gadget_release(struct device *dev)
{
DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, dev);
}
static struct gadget_wrapper *alloc_wrapper(
struct platform_device *_dev
)
{
static char pcd_name[] = "dwc_otg";
dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev);
struct gadget_wrapper *d;
int retval;
d = dwc_alloc(sizeof(*d));
if (d == NULL) {
return NULL;
}
memset(d, 0, sizeof(*d));
d->gadget.name = pcd_name;
d->pcd = otg_dev->pcd;
//sword
/*
strcpy(d->gadget.dev.bus_id, "gadget");
*/
dev_set_name(&d->gadget.dev, "gadget");
d->gadget.dev.parent = &_dev->dev;
d->gadget.dev.release = dwc_otg_pcd_gadget_release;
d->gadget.ops = &dwc_otg_pcd_ops;
d->gadget.max_speed = USB_SPEED_HIGH;
d->gadget.is_otg = dwc_otg_pcd_is_otg(otg_dev->pcd);
if (d->pcd->core_if->dma_enable &&
d->pcd->core_if->dma_desc_enable)
d->gadget.sg_supported = 1;
d->driver = 0;
/* Register the gadget device */
#if 0
retval = device_register(&d->gadget.dev);
if (retval != 0) {
DWC_ERROR("device_register failed\n");
dwc_free(d);
return NULL;
}
#endif
return d;
}
static void free_wrapper(struct gadget_wrapper *d)
{
if (d->driver) {
/* should have been done already by driver model core */
DWC_WARN("driver '%s' is still registered\n",
d->driver->driver.name);
usb_gadget_unregister_driver(d->driver);
}
device_unregister(&d->gadget.dev);
dwc_free(d);
}
static void __udc_startup(void)
{
struct gadget_wrapper *d;
d = gadget_wrapper;
pr_info("USB:startup udc\n");
if (!d->udc_startup) {
/* < add by lgd, fix the problem that sometime poweroff charging is so slow */
//wake_lock(&usb_wake_lock);
if(!in_charge_mode()) {
wake_lock(&usb_wake_lock);
DWC_DEBUG("%s(): wake_lock", __func__);
}
/* add by lgd, fix the problem that sometime poweroff charging is so slow > */
udc_enable();
dwc_otg_core_init(GET_CORE_IF(d->pcd));
dwc_otg_enable_global_interrupts(GET_CORE_IF(d->pcd));
dwc_otg_core_dev_init(GET_CORE_IF(d->pcd));
d->udc_startup = 1;
}
}
static void dwc_otg_clear_all_int(dwc_otg_core_if_t *core_if)
{
uint32_t inep_intr;
uint32_t outep_intr;
uint32_t gintsts;
uint32_t epnum = 0;
dwc_otg_dev_if_t *dev_if = core_if->dev_if;
for(epnum=0;epnum<MAX_EPS_CHANNELS;epnum++){
inep_intr = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepint);
DWC_WRITE_REG32(&dev_if->in_ep_regs[epnum]->diepint, inep_intr);
}
for(epnum=0;epnum<MAX_EPS_CHANNELS;epnum++){
outep_intr = DWC_READ_REG32(&dev_if->out_ep_regs[epnum]->doepint);
DWC_WRITE_REG32(&dev_if->out_ep_regs[epnum]->doepint, outep_intr);
}
gintsts = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts);
}
static void __udc_shutdown(void)
{
struct gadget_wrapper *d;
d = gadget_wrapper;
pr_info("USB:shutdown udc\n");
if (d->udc_startup) {
dwc_otg_disable_global_interrupts(GET_CORE_IF(d->pcd));
dwc_otg_clear_all_int(GET_CORE_IF(d->pcd));
dwc_otg_pcd_stop(d->pcd);
udc_disable();
d->udc_startup = 0;
/* < add by lgd, fix the problem that sometime poweroff charging is so slow */
//wake_unlock(&usb_wake_lock);
if(!in_charge_mode()) {
DWC_DEBUG("%s(): wake_unlock", __func__);
wake_unlock(&usb_wake_lock);
}
/* add by lgd, fix the problem that sometime poweroff charging is so slow > */
}
}
static int cable_is_connected(void);
void dwc_udc_startup(void)
{
//if usb cable not connect, do nothing
if (!cable_is_connected()) {
pr_warning("usb cable is not connect\n");
return;
}
//udc not startup, startup udc and get a wacklock
mutex_lock(&udc_lock);
__udc_startup();
mutex_unlock(&udc_lock);
}
void dwc_udc_shutdown(void)
{
mutex_lock(&udc_lock);
__udc_shutdown();
mutex_unlock(&udc_lock);
}
int dwc_udc_state(void)
{
struct gadget_wrapper *d;
d = gadget_wrapper;
return d->udc_startup;
}
void udc_phy_down(void)
{
//struct gadget_wrapper *d = gadget_wrapper;
//dwc_otg_dev_soft_disconnect(GET_CORE_IF(d->pcd));
}
void udc_phy_up(void)
{
//struct gadget_wrapper *d = gadget_wrapper;
//dwc_otg_dev_soft_connect(GET_CORE_IF(d->pcd));
}
static struct usb_hotplug_callback *hotplug_cb;
static void hotplug_callback(int event, int usb_cable)
{
if (unlikely(!hotplug_cb || !hotplug_cb->plugin))
pr_warning("%s, hotplug call back is not registered\n",
__func__);
switch (event) {
case VBUS_PLUG_IN:
if (hotplug_cb && hotplug_cb->plugin)
hotplug_cb->plugin(usb_cable, hotplug_cb->data);
break;
case VBUS_PLUG_OUT:
if (hotplug_cb && hotplug_cb->plugout)
hotplug_cb->plugout(usb_cable, hotplug_cb->data);
break;
default:
pr_warning("hotplug envent error %d\n", event);
break;
}
return;
}
static int cable_is_usb(void)
{
int value;
struct gadget_wrapper *d = gadget_wrapper;
value = d->pcd->ep0state;
return value != EP0_DISCONNECT;
}
static int usb_first_enable_flag = 0;
void usb_first_enable_store_flag(void)
{
usb_first_enable_flag = 1;
}
int get_usb_first_enable_store_flag(void)
{
return usb_first_enable_flag;
}
EXPORT_SYMBOL(usb_first_enable_store_flag);
EXPORT_SYMBOL(get_usb_first_enable_store_flag);
static void usb_detect_works(struct work_struct *work)
{
struct gadget_wrapper *d;
unsigned long flags;
int plug_in;
d = gadget_wrapper;
spin_lock_irqsave(&d->lock, flags);
plug_in = d->vbus;
spin_unlock_irqrestore(&d->lock, flags);
mutex_lock(&udc_lock);
if (plug_in){
pr_info("usb detect plug in,vbus pull up\n");
hotplug_callback(VBUS_PLUG_IN, 0);
if(get_usb_first_enable_store_flag()){
queue_delayed_work(d->cable2pc_wq, &d->cable2pc,CABLE_TIMEOUT);
__udc_startup();
}
} else {
pr_info("usb detect plug out,vbus pull down\n");
cancel_delayed_work_sync(&d->cable2pc);
__udc_shutdown();
hotplug_callback(VBUS_PLUG_OUT, cable_is_usb());
}
mutex_unlock(&udc_lock);
switch_set_state(&d->sdev, !!plug_in);
}
static irqreturn_t usb_detect_handler(int irq, void *dev_id)
{
struct gadget_wrapper *d;
int value = 0;
#ifndef DWC_DEVICE_ONLY
/*
*if otg cable is connected , id state =0
*as host turn on vbus, in this case shouldn't call this handler
*/
if(!usb_get_id_state())
return IRQ_HANDLED;
#endif
d = gadget_wrapper;
if (d->driver == NULL) {
pr_info("too early, no gadget drive\n");
return IRQ_HANDLED;
}
value = usb_get_vbus_state();
if (value){
pr_debug("usb detect plug in\n");
usb_set_vbus_irq_type(irq, VBUS_PLUG_OUT);
} else {
pr_debug("usb detect plug out\n");
usb_set_vbus_irq_type(irq, VBUS_PLUG_IN);
}
d->vbus = value;
queue_work(d->detect_wq, &d->detect_work);
return IRQ_HANDLED;
}
int epmic_event_handler(int level)
{
struct gadget_wrapper *d;
#ifndef DWC_DEVICE_ONLY
/*
*if otg cable is connected , id state =0
*as host turn on vbus, in this case shouldn't call this handler
*/
if(!usb_get_id_state())
return 0;
#endif
d = gadget_wrapper;
d->vbus = level;
queue_work(d->detect_wq, &d->detect_work);
return 0;
}
EXPORT_SYMBOL(epmic_event_handler);
static int cable_is_connected(void)
{
if (usb_get_vbus_state())
return 1;
else
return 0;
}
/*
* this function will check the cable type, if it is
* ac adapter calbe, we disable udc for lowering the
* power.
*/
#define REENUM_CNT 1
static void cable2pc_detect_works(struct work_struct *work)
{
int usb_cable;
static int reenum_cnt = REENUM_CNT;
struct gadget_wrapper *d;
d = gadget_wrapper;
usb_cable = cable_is_usb();
/* in factory mode, we know the cable must be usb, so we enumerate
* again
*/
if (in_factory_mode() && !usb_cable && reenum_cnt){
pr_info("try usb enumertation again\n");
reenum_cnt--;
mutex_lock(&udc_lock);
__udc_shutdown();
queue_delayed_work(d->cable2pc_wq, &d->cable2pc,CABLE_TIMEOUT);
__udc_startup();
mutex_unlock(&udc_lock);
return;
}
mutex_lock(&udc_lock);
if (!usb_cable) {
pr_info("cable is ac adapter\n");
__udc_shutdown();
}
mutex_unlock(&udc_lock);
return;
}
static struct device *device_dwc_otg;
/**
* This function initialized the PCD portion of the driver.
*
*/
int pcd_init(
struct platform_device *_dev
)
{
dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev);
struct sprd_usb_platform_data *pdata= _dev->dev.platform_data;
int retval = 0;
int irq;
int plug_irq;
DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _dev);
device_dwc_otg = &(_dev->dev);
if (dwc_otg_is_dma_enable(otg_dev->core_if)) {
_dev->dev.dma_mask = &dwc_otg_pcd_dmamask;
_dev->dev.coherent_dma_mask = dwc_otg_pcd_dmamask;
} else {
_dev->dev.dma_mask = (void *)0;
_dev->dev.coherent_dma_mask = 0;
}
wake_lock_init(&usb_wake_lock, WAKE_LOCK_SUSPEND, "usb_work");
// wake_lock(&usb_wake_lock);
otg_dev->pcd = dwc_otg_pcd_init(otg_dev->core_if);
if (!otg_dev->pcd) {
DWC_ERROR("dwc_otg_pcd_init failed\n");
return -ENOMEM;
}
gadget_wrapper = alloc_wrapper(_dev);
/*
* Initialize EP structures
*/
gadget_add_eps(gadget_wrapper);
/*
* Setup interupt handler
*/
irq = platform_get_irq(_dev, 0);
DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", irq);
retval = request_irq(irq, dwc_otg_pcd_irq,
IRQF_SHARED, gadget_wrapper->gadget.name,
otg_dev->pcd);
//SA_SHIRQ, gadget_wrapper->gadget.name,
if (retval != 0) {
DWC_ERROR("request of irq%d failed\n", irq);
free_wrapper(gadget_wrapper);
return -EBUSY;
}
/*
* initialize a timer for checking cable type.
*/
setup_timer(&setup_transfer_timer,
setup_transfer_timer_fun,
(unsigned long)gadget_wrapper);
setup_transfer_timer_start = 0;
setup_timer(&reinit_usb_timer, reinit_usb_timer_fun,
(unsigned long) gadget_wrapper);
INIT_DELAYED_WORK(&gadget_wrapper->cable2pc, cable2pc_detect_works);
gadget_wrapper->cable2pc_wq =
create_singlethread_workqueue("USB2pcWq");
/*
* setup usb cable detect interupt
*/
#ifndef CONFIG_MFD_SM5504
{
plug_irq = usb_alloc_vbus_irq(pdata->gpio_chgdet);
if (plug_irq < 0) {
pr_warning("cannot alloc vbus irq\n");
return -EBUSY;
}
usb_set_vbus_irq_type(plug_irq, VBUS_PLUG_IN);
#ifdef CONFIG_SC_FPGA
gadget_wrapper->vbus = 1;
#else
gadget_wrapper->vbus = usb_get_vbus_state();
#endif
pr_info("now usb vbus is :%d\n", gadget_wrapper->vbus);
retval = request_irq(plug_irq, usb_detect_handler, IRQF_SHARED | IRQF_NO_SUSPEND,
"usb detect", otg_dev->pcd);
#ifndef CONFIG_MUIC_CABLE_DETECT
disable_irq(plug_irq);
#endif
}
//gadget_wrapper->vbus = 1;//used when debug in FPGA, which doesn't have vbus operation
#endif
spin_lock_init(&gadget_wrapper->lock);
#ifdef CONFIG_SC_FPGA
gadget_wrapper->vbus = 1;
#endif
INIT_WORK(&gadget_wrapper->detect_work, usb_detect_works);
gadget_wrapper->detect_wq =
create_singlethread_workqueue("USBDetectWq");
INIT_WORK(&gadget_wrapper->reinit_work, usb_reinit_works);
gadget_wrapper->reinit_wq =
create_singlethread_workqueue("USBReinitWq");
/*
* register a switch device for sending pnp message,
* for the user app need be notified immediately
* when plug in & plug out happen;
*/
gadget_wrapper->sdev.name = "charger_cable";
retval = switch_dev_register(&gadget_wrapper->sdev);
if (retval){
pr_warning("register switch dev error:%s\n", __func__);
}
dwc_otg_pcd_start(gadget_wrapper->pcd, &fops);
/*
* dwc driver is ok, check if the cable is insert, if no,
* shutdown udc for saving power.
*/
if (!gadget_wrapper->vbus){
pr_debug("vbus is not power now \n");
gadget_wrapper->udc_startup = 1;
__udc_shutdown();
}
gadget_wrapper->udc_startup = gadget_wrapper->vbus;
gadget_wrapper->enabled = 0;
retval = usb_add_gadget_udc(&_dev->dev, &gadget_wrapper->gadget);
if (!retval)
return retval;
return retval;
}
/**
* Cleanup the PCD.
*/
void pcd_remove(
struct platform_device *_dev
)
{
dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev);
struct sprd_usb_platform_data *pdata= _dev->dev.platform_data;
dwc_otg_pcd_t *pcd = otg_dev->pcd;
int plug_irq;
DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _dev);
usb_del_gadget_udc(&gadget_wrapper->gadget);
/*
* Free the IRQ
*/
free_irq(platform_get_irq(_dev, 0), pcd);
plug_irq = usb_get_vbus_irq();
usb_free_vbus_irq(plug_irq,pdata->gpio_chgdet);
dwc_otg_pcd_remove(pcd);
destroy_workqueue(gadget_wrapper->detect_wq);
destroy_workqueue(gadget_wrapper->cable2pc_wq);
destroy_workqueue(gadget_wrapper->reinit_wq);
wake_lock_destroy(&usb_wake_lock);
switch_dev_unregister(&gadget_wrapper->sdev);
free_wrapper(gadget_wrapper);
pcd = 0;
}
/**
* This function registers a gadget driver with the PCD.
*
* When a driver is successfully registered, it will receive control
* requests including set_configuration(), which enables non-control
* requests. then usb traffic follows until a disconnect is reported.
* then a host may connect again, or the driver might get unbound.
*
* @param driver The driver being registered
*/
static int dwc_usb_gadget_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver)
{
int retval;
DWC_DEBUGPL(DBG_PCD, "registering gadget driver '%s'\n",
driver->driver.name);
pr_info("%s\n", __func__);
if (gadget_wrapper == 0) {
DWC_DEBUGPL(DBG_PCDV, "ENODEV\n");
return -ENODEV;
}
if (gadget_wrapper->driver != 0) {
DWC_DEBUGPL(DBG_PCDV, "EBUSY (%p)\n", gadget_wrapper->driver);
return -EBUSY;
}
/* hook up the driver */
gadget_wrapper->driver = driver;
gadget_wrapper->gadget.dev.driver = &driver->driver;
gadget_wrapper->enabled = 1;
DWC_DEBUGPL(DBG_ANY, "registered gadget driver '%s'\n",
driver->driver.name);
return 0;
}
/**
* This function unregisters a gadget driver
*
* @param driver The driver being unregistered
*/
static int dwc_usb_gadget_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver)
{
//DWC_DEBUGPL(DBG_PCDV,"%s(%p)\n", __func__, _driver);
if (gadget_wrapper == 0) {
DWC_DEBUGPL(DBG_ANY, "%s Return(%d): s_pcd==0\n", __func__,
-ENODEV);
return -ENODEV;
}
if (driver == 0 || driver != gadget_wrapper->driver) {
DWC_DEBUGPL(DBG_ANY, "%s Return(%d): driver?\n", __func__,
-EINVAL);
return -EINVAL;
}
gadget_wrapper->driver = 0;
gadget_wrapper->enabled = 0;
DWC_DEBUGPL(DBG_ANY, "unregistered driver '%s'\n", driver->driver.name);
return 0;
}
int usb_register_hotplug_callback(struct usb_hotplug_callback *cb)
{
int ret = 0;
int plug_irq = usb_get_vbus_irq();
if (cb){
hotplug_cb = cb;
enable_irq(plug_irq);
} else {
pr_warning("%s, error\n", __func__);
ret = -EINVAL;
}
return ret;
}
EXPORT_SYMBOL(usb_register_hotplug_callback);
struct device *get_gadget_wrapper_device()
{
#ifdef CONFIG_ARM64
if(NULL != device_dwc_otg)
{
return device_dwc_otg;
}
else
{
gadget_wrapper->gadget.dev.dma_mask = &dwc_otg_pcd_dmamask;
gadget_wrapper->gadget.dev.coherent_dma_mask = dwc_otg_pcd_dmamask;
return &(gadget_wrapper->gadget.dev);
}
#else
return NULL;
#endif
}
EXPORT_SYMBOL(get_gadget_wrapper_device);
#endif /* DWC_HOST_ONLY */
|