forked from FrameworkComputer/EmbeddedController
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathusb_tc_drp_acc_trysrc_sm.c
4195 lines (3625 loc) · 112 KB
/
usb_tc_drp_acc_trysrc_sm.c
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
/* Copyright 2019 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "builtin/assert.h"
#include "charge_manager.h"
#include "charge_state.h"
#include "common.h"
#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "system.h"
#include "task.h"
#include "tcpm/tcpm.h"
#include "typec_control.h"
#include "usb_common.h"
#include "usb_mux.h"
#include "usb_pd.h"
#include "usb_pd_dpm_sm.h"
#include "usb_pd_tcpm.h"
#include "usb_pd_timer.h"
#include "usb_pe_sm.h"
#include "usb_prl_sm.h"
#include "usb_sm.h"
#include "usb_tc_sm.h"
#include "usbc_ocp.h"
#include "usbc_ppc.h"
#include "vboot.h"
/*
* USB Type-C DRP with Accessory and Try.SRC module
* See Figure 4-16 in Release 1.4 of USB Type-C Spec.
*/
#ifdef CONFIG_COMMON_RUNTIME
#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ##args)
#define CPRINTS(format, args...) cprints(CC_USBPD, format, ##args)
#else /* CONFIG_COMMON_RUNTIME */
#define CPRINTF(format, args...)
#define CPRINTS(format, args...)
#endif
#define CPRINTF_LX(x, format, args...) \
do { \
if (tc_debug_level >= x) \
CPRINTF(format, ##args); \
} while (0)
#define CPRINTF_L1(format, args...) CPRINTF_LX(1, format, ##args)
#define CPRINTF_L2(format, args...) CPRINTF_LX(2, format, ##args)
#define CPRINTF_L3(format, args...) CPRINTF_LX(3, format, ##args)
#define CPRINTS_LX(x, format, args...) \
do { \
if (tc_debug_level >= x) \
CPRINTS(format, ##args); \
} while (0)
#define CPRINTS_L1(format, args...) CPRINTS_LX(1, format, ##args)
#define CPRINTS_L2(format, args...) CPRINTS_LX(2, format, ##args)
#define CPRINTS_L3(format, args...) CPRINTS_LX(3, format, ##args)
/*
* Define DEBUG_PRINT_FLAG_AND_EVENT_NAMES to print flag names when set and
* cleared, and event names when handled by tc_event_check().
*/
#undef DEBUG_PRINT_FLAG_AND_EVENT_NAMES
#ifdef DEBUG_PRINT_FLAG_AND_EVENT_NAMES
void print_flag(int port, int set_or_clear, int flag);
#define TC_SET_FLAG(port, flag) \
do { \
print_flag(port, 1, flag); \
atomic_or(&tc[port].flags, (flag)); \
} while (0)
#define TC_CLR_FLAG(port, flag) \
do { \
print_flag(port, 0, flag); \
atomic_clear_bits(&tc[port].flags, (flag)); \
} while (0)
#else
#define TC_SET_FLAG(port, flag) atomic_or(&tc[port].flags, (flag))
#define TC_CLR_FLAG(port, flag) atomic_clear_bits(&tc[port].flags, (flag))
#endif
#define TC_CHK_FLAG(port, flag) (tc[port].flags & (flag))
/* Type-C Layer Flags */
/* Flag to note we are sourcing VCONN */
#define TC_FLAGS_VCONN_ON BIT(0)
/* Flag to note port partner has Rp/Rp or Rd/Rd */
#define TC_FLAGS_TS_DTS_PARTNER BIT(1)
/* Flag to note VBus input has never been low */
#define TC_FLAGS_VBUS_NEVER_LOW BIT(2)
/* Flag to note Low Power Mode transition is currently happening */
#define TC_FLAGS_LPM_TRANSITION BIT(3)
/* Flag to note Low Power Mode is currently on */
#define TC_FLAGS_LPM_ENGAGED BIT(4)
/* Flag to note CVTPD has been detected */
#define TC_FLAGS_CTVPD_DETECTED BIT(5)
/* Flag to note request to swap to VCONN on */
#define TC_FLAGS_REQUEST_VC_SWAP_ON BIT(6)
/* Flag to note request to swap to VCONN off */
#define TC_FLAGS_REQUEST_VC_SWAP_OFF BIT(7)
/* Flag to note request to swap VCONN is being rejected */
#define TC_FLAGS_REJECT_VCONN_SWAP BIT(8)
/* Flag to note request to power role swap */
#define TC_FLAGS_REQUEST_PR_SWAP BIT(9)
/* Flag to note request to data role swap */
#define TC_FLAGS_REQUEST_DR_SWAP BIT(10)
/* Flag to note request to power off sink */
#define TC_FLAGS_POWER_OFF_SNK BIT(11)
/* Flag to note port partner is Power Delivery capable */
#define TC_FLAGS_PARTNER_PD_CAPABLE BIT(12)
/* Flag to note hard reset has been requested */
#define TC_FLAGS_HARD_RESET_REQUESTED BIT(13)
/* Flag to note we are currently performing PR Swap */
#define TC_FLAGS_PR_SWAP_IN_PROGRESS BIT(14)
/* Flag to note we should check for connection */
#define TC_FLAGS_CHECK_CONNECTION BIT(15)
/* Flag to note request from pd_set_suspend to enter TC_DISABLED state */
#define TC_FLAGS_REQUEST_SUSPEND BIT(16)
/* Flag to note we are in TC_DISABLED state */
#define TC_FLAGS_SUSPENDED BIT(17)
/* Flag to indicate the port current limit has changed */
#define TC_FLAGS_UPDATE_CURRENT BIT(18)
/* Flag to indicate USB mux should be updated */
#define TC_FLAGS_UPDATE_USB_MUX BIT(19)
/* Flag for retimer firmware update */
#define TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN BIT(20)
#define TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN BIT(21)
/* Flag for asynchronous call to request Error Recovery */
#define TC_FLAGS_REQUEST_ERROR_RECOVERY BIT(22)
/* For checking flag_bit_names[] array */
#define TC_FLAGS_COUNT 23
/* On disconnect, clear most of the flags. */
#define CLR_FLAGS_ON_DISCONNECT(port) \
TC_CLR_FLAG(port, ~(TC_FLAGS_LPM_ENGAGED | TC_FLAGS_REQUEST_SUSPEND | \
TC_FLAGS_SUSPENDED))
/*
* 10 ms is enough time for any TCPC transaction to complete
*
* This value must be below ~39.7 ms to put ANX7447 into LPM due to bug in
* silicon (see b/77544959 and b/149761477 for more details).
*/
#define PD_LPM_DEBOUNCE_US (10 * MSEC)
/*
* This delay is not part of the USB Type-C specification or the USB port
* controller specification. Some TCPCs require extra time before the CC_STATUS
* register is updated when exiting low power mode.
*
* This delay can be possibly shortened or removed by checking VBUS state
* before trying to re-enter LPM.
*
* TODO(b/162347811): TCPMv2: Wait for debounce on Vbus and CC lines
*/
#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
#define PD_LPM_EXIT_DEBOUNCE_US CONFIG_USB_PD_TCPC_LPM_EXIT_DEBOUNCE
#else
/*
* Define this value regardless so it is not missing at compile time.
*/
#define PD_LPM_EXIT_DEBOUNCE_US 0
#endif
/*
* The TypeC state machine uses this bit to disable/enable PD
* This bit corresponds to bit-0 of pd_disabled_mask
*/
#define PD_DISABLED_NO_CONNECTION BIT(0)
/*
* Console and Host commands use this bit to override the
* PD_DISABLED_NO_CONNECTION bit that was set by the TypeC
* state machine.
* This bit corresponds to bit-1 of pd_disabled_mask
*/
#define PD_DISABLED_BY_POLICY BIT(1)
/* Unreachable time in future */
#define TIMER_DISABLED 0xffffffffffffffff
enum ps_reset_sequence {
PS_STATE0,
PS_STATE1,
PS_STATE2,
};
/* List of all TypeC-level states */
enum usb_tc_state {
/* Super States */
TC_CC_OPEN,
TC_CC_RD,
TC_CC_RP,
/* Normal States */
TC_DISABLED,
TC_ERROR_RECOVERY,
TC_UNATTACHED_SNK,
TC_ATTACH_WAIT_SNK,
TC_ATTACHED_SNK,
TC_UNATTACHED_SRC,
TC_ATTACH_WAIT_SRC,
TC_ATTACHED_SRC,
TC_TRY_SRC,
TC_TRY_WAIT_SNK,
TC_DRP_AUTO_TOGGLE,
TC_LOW_POWER_MODE,
TC_CT_UNATTACHED_SNK,
TC_CT_ATTACHED_SNK,
TC_STATE_COUNT,
};
/* Forward declare the full list of states. This is indexed by usb_tc_state */
static const struct usb_state tc_states[];
/*
* Remove all of the states that aren't support at link time. This allows
* IS_ENABLED to work.
*/
#ifndef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
GEN_NOT_SUPPORTED(TC_DRP_AUTO_TOGGLE);
#define TC_DRP_AUTO_TOGGLE TC_DRP_AUTO_TOGGLE_NOT_SUPPORTED
#endif /* CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE */
#ifndef CONFIG_USB_PD_TCPC_LOW_POWER
GEN_NOT_SUPPORTED(TC_LOW_POWER_MODE);
#define TC_LOW_POWER_MODE TC_LOW_POWER_MODE_NOT_SUPPORTED
#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */
#ifndef CONFIG_USB_PE_SM
GEN_NOT_SUPPORTED(TC_CT_UNATTACHED_SNK);
#define TC_CT_UNATTACHED_SNK TC_CT_UNATTACHED_SNK_NOT_SUPPORTED
GEN_NOT_SUPPORTED(TC_CT_ATTACHED_SNK);
#define TC_CT_ATTACHED_SNK TC_CT_ATTACHED_SNK_NOT_SUPPORTED
#endif /* CONFIG_USB_PE_SM */
/*
* If CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT is not defined then
* _GPIO_CCD_MODE_ODL is not needed. Declare as extern so IS_ENABLED will work.
*/
#ifndef CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT
extern int _GPIO_CCD_MODE_ODL;
#else
#define _GPIO_CCD_MODE_ODL GPIO_CCD_MODE_ODL
#endif /* CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT */
/*
* We will use DEBUG LABELS if we will be able to print (COMMON RUNTIME)
* and either CONFIG_USB_PD_DEBUG_LEVEL is not defined (no override) or
* we are overriding and the level is not DISABLED.
*
* If we can't print or the CONFIG_USB_PD_DEBUG_LEVEL is defined to be 0
* then the DEBUG LABELS will be removed from the build.
*/
#if defined(CONFIG_COMMON_RUNTIME) && (!defined(CONFIG_USB_PD_DEBUG_LEVEL) || \
(CONFIG_USB_PD_DEBUG_LEVEL > 0))
#define USB_PD_DEBUG_LABELS
#endif
/*
* Helper Macro to determine if the machine is in state
* TC_ATTACHED_SRC
*/
#define IS_ATTACHED_SRC(port) (get_state_tc(port) == TC_ATTACHED_SRC)
/*
* Helper Macro to determine if the machine is in state
* TC_ATTACHED_SNK
*/
#define IS_ATTACHED_SNK(port) (get_state_tc(port) == TC_ATTACHED_SNK)
/* List of human readable state names for console debugging */
__maybe_unused static __const_data const char *const tc_state_names[] = {
#ifdef USB_PD_DEBUG_LABELS
[TC_DISABLED] = "Disabled",
[TC_ERROR_RECOVERY] = "ErrorRecovery",
[TC_UNATTACHED_SNK] = "Unattached.SNK",
[TC_ATTACH_WAIT_SNK] = "AttachWait.SNK",
[TC_ATTACHED_SNK] = "Attached.SNK",
[TC_UNATTACHED_SRC] = "Unattached.SRC",
[TC_ATTACH_WAIT_SRC] = "AttachWait.SRC",
[TC_ATTACHED_SRC] = "Attached.SRC",
[TC_TRY_SRC] = "Try.SRC",
[TC_TRY_WAIT_SNK] = "TryWait.SNK",
#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
[TC_DRP_AUTO_TOGGLE] = "DRPAutoToggle",
#endif
#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
[TC_LOW_POWER_MODE] = "LowPowerMode",
#endif
#ifdef CONFIG_USB_PE_SM
[TC_CT_UNATTACHED_SNK] = "CTUnattached.SNK",
[TC_CT_ATTACHED_SNK] = "CTAttached.SNK",
#endif
/* Super States */
[TC_CC_OPEN] = "SS:CC_OPEN",
[TC_CC_RD] = "SS:CC_RD",
[TC_CC_RP] = "SS:CC_RP",
[TC_STATE_COUNT] = "",
#endif
};
/* Debug log level - higher number == more log */
#ifdef CONFIG_USB_PD_DEBUG_LEVEL
static const enum debug_level tc_debug_level = CONFIG_USB_PD_DEBUG_LEVEL;
#elif defined(CONFIG_USB_PD_INITIAL_DEBUG_LEVEL)
static enum debug_level tc_debug_level = CONFIG_USB_PD_INITIAL_DEBUG_LEVEL;
#else
static enum debug_level tc_debug_level = DEBUG_LEVEL_1;
#endif
#ifdef DEBUG_PRINT_FLAG_AND_EVENT_NAMES
struct bit_name {
int value;
const char *name;
};
static struct bit_name flag_bit_names[] = {
{ TC_FLAGS_VCONN_ON, "VCONN_ON" },
{ TC_FLAGS_TS_DTS_PARTNER, "TS_DTS_PARTNER" },
{ TC_FLAGS_VBUS_NEVER_LOW, "VBUS_NEVER_LOW" },
{ TC_FLAGS_LPM_TRANSITION, "LPM_TRANSITION" },
{ TC_FLAGS_LPM_ENGAGED, "LPM_ENGAGED" },
{ TC_FLAGS_CTVPD_DETECTED, "CTVPD_DETECTED" },
{ TC_FLAGS_REQUEST_VC_SWAP_ON, "REQUEST_VC_SWAP_ON" },
{ TC_FLAGS_REQUEST_VC_SWAP_OFF, "REQUEST_VC_SWAP_OFF" },
{ TC_FLAGS_REJECT_VCONN_SWAP, "REJECT_VCONN_SWAP" },
{ TC_FLAGS_REQUEST_PR_SWAP, "REQUEST_PR_SWAP" },
{ TC_FLAGS_REQUEST_DR_SWAP, "REQUEST_DR_SWAP" },
{ TC_FLAGS_POWER_OFF_SNK, "POWER_OFF_SNK" },
{ TC_FLAGS_PARTNER_PD_CAPABLE, "PARTNER_PD_CAPABLE" },
{ TC_FLAGS_HARD_RESET_REQUESTED, "HARD_RESET_REQUESTED" },
{ TC_FLAGS_PR_SWAP_IN_PROGRESS, "PR_SWAP_IN_PROGRESS" },
{ TC_FLAGS_CHECK_CONNECTION, "CHECK_CONNECTION" },
{ TC_FLAGS_REQUEST_SUSPEND, "REQUEST_SUSPEND" },
{ TC_FLAGS_SUSPENDED, "SUSPENDED" },
{ TC_FLAGS_UPDATE_CURRENT, "UPDATE_CURRENT" },
{ TC_FLAGS_UPDATE_USB_MUX, "UPDATE_USB_MUX" },
{ TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN, "USB_RETIMER_FW_UPDATE_RUN" },
{ TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN,
"USB_RETIMER_FW_UPDATE_LTD_RUN" },
{ TC_FLAGS_REQUEST_ERROR_RECOVERY, "REQUEST_ERROR_RECOCVERY" },
};
BUILD_ASSERT(ARRAY_SIZE(flag_bit_names) == TC_FLAGS_COUNT);
static struct bit_name event_bit_names[] = {
{ TASK_EVENT_SYSJUMP_READY, "SYSJUMP_READY" },
{ TASK_EVENT_IPC_READY, "IPC_READY" },
{ TASK_EVENT_PD_AWAKE, "PD_AWAKE" },
{ TASK_EVENT_PECI_DONE, "PECI_DONE" },
{ TASK_EVENT_I2C_IDLE, "I2C_IDLE" },
#ifdef TASK_EVENT_PS2_DONE
{ TASK_EVENT_PS2_DONE, "PS2_DONE" },
#endif
{ TASK_EVENT_DMA_TC, "DMA_TC" },
{ TASK_EVENT_ADC_DONE, "ADC_DONE" },
{ TASK_EVENT_RESET_DONE, "RESET_DONE" },
{ TASK_EVENT_WAKE, "WAKE" },
{ TASK_EVENT_MUTEX, "MUTEX" },
{ TASK_EVENT_TIMER, "TIMER" },
{ PD_EVENT_TX, "TX" },
{ PD_EVENT_CC, "CC" },
{ PD_EVENT_TCPC_RESET, "TCPC_RESET" },
{ PD_EVENT_UPDATE_DUAL_ROLE, "UPDATE_DUAL_ROLE" },
{ PD_EVENT_DEVICE_ACCESSED, "DEVICE_ACCESSED" },
{ PD_EVENT_POWER_STATE_CHANGE, "POWER_STATE_CHANGE" },
{ PD_EVENT_SEND_HARD_RESET, "SEND_HARD_RESET" },
{ PD_EVENT_SYSJUMP, "SYSJUMP" },
};
static void print_bits(int port, const char *desc, int value,
struct bit_name *names, int names_size)
{
int i;
CPRINTF("C%d: %s 0x%x : ", port, desc, value);
for (i = 0; i < names_size; i++) {
if (value & names[i].value)
CPRINTF("%s | ", names[i].name);
value &= ~names[i].value;
}
if (value != 0)
CPRINTF("0x%x", value);
CPRINTF("\n");
}
void print_flag(int port, int set_or_clear, int flag)
{
print_bits(port, set_or_clear ? "Set" : "Clr", flag, flag_bit_names,
ARRAY_SIZE(flag_bit_names));
}
#endif /* DEBUG_PRINT_FLAG_AND_EVENT_NAMES */
#ifndef CONFIG_USB_PD_TRY_SRC
extern int TC_TRY_SRC_UNDEFINED;
extern int TC_TRY_WAIT_SNK_UNDEFINED;
#define TC_TRY_SRC TC_TRY_SRC_UNDEFINED
#define TC_TRY_WAIT_SNK TC_TRY_WAIT_SNK_UNDEFINED
#endif
static struct type_c {
/* state machine context */
struct sm_ctx ctx;
/* current port power role (SOURCE or SINK) */
enum pd_power_role power_role;
/* current port data role (DFP or UFP) */
enum pd_data_role data_role;
/*
* Higher-level power deliver state machines are enabled if false,
* else they're disabled if bits PD_DISABLED_NO_CONNECTION or
* PD_DISABLED_BY_POLICY are set.
*/
atomic_t pd_disabled_mask;
/*
* Timer for handling TOGGLE_OFF/FORCE_SINK mode when auto-toggle
* enabled. See drp_auto_toggle_next_state() for details.
*/
uint64_t drp_sink_time;
#ifdef CONFIG_USB_PE_SM
/* Power supply reset sequence during a hard reset */
enum ps_reset_sequence ps_reset_state;
#endif
/* Port polarity */
enum tcpc_cc_polarity polarity;
/* port flags, see TC_FLAGS_* */
atomic_t flags;
/* The cc state */
enum pd_cc_states cc_state;
/* Tasks to notify after TCPC has been reset */
atomic_t tasks_waiting_on_reset;
/* Tasks preventing TCPC from entering low power mode */
atomic_t tasks_preventing_lpm;
/* Voltage on CC pin */
enum tcpc_cc_voltage_status cc_voltage;
/* Type-C current */
typec_current_t typec_curr;
/* Type-C current change */
typec_current_t typec_curr_change;
/* Selected TCPC CC/Rp values */
enum tcpc_cc_pull select_cc_pull;
enum tcpc_rp_value select_current_limit_rp;
enum tcpc_rp_value select_collision_rp;
} tc[CONFIG_USB_PD_PORT_MAX_COUNT];
/* Port dual-role state */
static volatile __maybe_unused enum pd_dual_role_states
drp_state[CONFIG_USB_PD_PORT_MAX_COUNT] = {
[0 ...(CONFIG_USB_PD_PORT_MAX_COUNT - 1)] =
CONFIG_USB_PD_INITIAL_DRP_STATE
};
static void set_vconn(int port, int enable);
/* Forward declare common, private functions */
static __maybe_unused int reset_device_and_notify(int port);
static __maybe_unused void check_drp_connection(const int port);
static void sink_power_sub_states(int port);
static void set_ccd_mode(int port, bool enable);
__maybe_unused static void handle_new_power_state(int port);
static void pd_update_dual_role_config(int port);
/* Forward declare common, private functions */
static void set_state_tc(const int port, const enum usb_tc_state new_state);
test_export_static enum usb_tc_state get_state_tc(const int port);
/* Enable variable for Try.SRC states */
static atomic_t pd_try_src;
static volatile enum try_src_override_t pd_try_src_override;
static void pd_update_try_source(void);
static void sink_stop_drawing_current(int port);
__maybe_unused static bool is_try_src_enabled(int port)
{
if (!IS_ENABLED(CONFIG_USB_PD_TRY_SRC))
assert(0);
return ((pd_try_src_override == TRY_SRC_OVERRIDE_ON) ||
(pd_try_src_override == TRY_SRC_NO_OVERRIDE && pd_try_src));
}
/*
* Public Functions
*
* NOTE: Functions prefixed with pd_ are defined in usb_pd.h
* Functions prefixed with tc_ are defined int usb_tc_sm.h
*/
#ifndef CONFIG_USB_PRL_SM
/*
* These pd_ functions are implemented in common/usb_prl_sm.c
*/
void pd_transmit_complete(int port, int status)
{
/* DO NOTHING */
}
void pd_execute_hard_reset(int port)
{
/* DO NOTHING */
}
__overridable void pd_set_vbus_discharge(int port, int enable)
{
/* DO NOTHING */
}
#endif /* !CONFIG_USB_PRL_SM */
#ifndef CONFIG_USB_PE_SM
/*
* These pd_ functions are implemented in the PE layer
*/
const uint32_t *const pd_get_src_caps(int port)
{
return NULL;
}
uint8_t pd_get_src_cap_cnt(int port)
{
return 0;
}
const uint32_t *const pd_get_snk_caps(int port)
{
return NULL;
}
uint8_t pd_get_snk_cap_cnt(int port)
{
return 0;
}
void pd_set_src_caps(int port, int cnt, uint32_t *src_caps)
{
}
int pd_get_rev(int port, enum tcpci_msg_type type)
{
return PD_REV30;
}
#endif /* !CONFIG_USB_PR_SM */
#ifndef CONFIG_AP_POWER_CONTROL
__overridable enum pd_dual_role_states board_tc_get_initial_drp_mode(int port)
{
/*
* DRP state is typically adjusted as the chipset state is changed. For
* projects which don't include an AP this function can be used for to
* specify what the starting DRP state should be.
*/
return PD_DRP_FORCE_SINK;
}
#endif
void pd_update_contract(int port)
{
if (IS_ENABLED(CONFIG_USB_PE_SM)) {
if (IS_ATTACHED_SRC(port))
pd_dpm_request(port, DPM_REQUEST_SRC_CAP_CHANGE);
}
}
void pd_request_source_voltage(int port, int mv)
{
if (IS_ENABLED(CONFIG_USB_PE_SM)) {
pd_set_max_voltage(mv);
if (IS_ATTACHED_SNK(port))
pd_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL);
else
pd_dpm_request(port, DPM_REQUEST_PR_SWAP);
task_wake(PD_PORT_TO_TASK_ID(port));
}
}
void pd_set_external_voltage_limit(int port, int mv)
{
if (IS_ENABLED(CONFIG_USB_PE_SM)) {
pd_set_max_voltage(mv);
/* Must be in Attached.SNK when this function is called */
if (get_state_tc(port) == TC_ATTACHED_SNK)
pd_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL);
task_wake(PD_PORT_TO_TASK_ID(port));
}
}
void pd_set_new_power_request(int port)
{
if (IS_ENABLED(CONFIG_USB_PE_SM)) {
/* Must be in Attached.SNK when this function is called */
if (get_state_tc(port) == TC_ATTACHED_SNK)
pd_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL);
}
}
void tc_request_power_swap(int port)
{
if (IS_ENABLED(CONFIG_USB_PE_SM)) {
/*
* Must be in Attached.SRC or Attached.SNK
*/
if (IS_ATTACHED_SRC(port) || IS_ATTACHED_SNK(port)) {
TC_SET_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS);
/* Let tc_pr_swap_complete start the Vbus debounce */
pd_timer_disable(port, TC_TIMER_VBUS_DEBOUNCE);
}
/*
* TCPCI Rev2 V1.1 4.4.5.4.4
* Disconnect Detection by the Sink TCPC during a Connection
*
* Upon reception of or prior to transmitting a PR_Swap
* message, the TCPM acting as a Sink shall disable the Sink
* disconnect detection to retain PD message delivery when
* Power Role Swap happens. Disable AutoDischargeDisconnect.
*/
if (IS_ATTACHED_SNK(port))
tcpm_enable_auto_discharge_disconnect(port, 0);
}
}
/* Flag to indicate PD comm is disabled on init */
static int pd_disabled_on_init;
static void pd_update_pd_comm(void)
{
int i;
/*
* Some batteries take much longer time to report its SOC.
* The init function disabled PD comm on startup. Need this
* hook to enable PD comm when the battery level is enough.
*/
if (pd_disabled_on_init && pd_is_battery_capable()) {
for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++)
pd_comm_enable(i, 1);
pd_disabled_on_init = 0;
}
}
DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, pd_update_pd_comm, HOOK_PRIO_DEFAULT);
static bool pd_comm_allowed_by_policy(void)
{
if (system_is_in_rw())
return true;
if (vboot_allow_usb_pd())
return true;
/*
* If enable PD in RO on a non-EFS2 device, a hard reset will be issued
* when sysjump to RW that makes the device brownout on the dead-battery
* case. Disable PD for this special case as a workaround.
*/
if (!system_is_locked()) {
if (IS_ENABLED(CONFIG_VBOOT_EFS2))
return true;
if (pd_is_battery_capable())
return true;
pd_disabled_on_init = 1;
}
return false;
}
static void tc_policy_pd_enable(int port, int en)
{
if (en)
atomic_clear_bits(&tc[port].pd_disabled_mask,
PD_DISABLED_BY_POLICY);
else
atomic_or(&tc[port].pd_disabled_mask, PD_DISABLED_BY_POLICY);
CPRINTS("C%d: PD comm policy %sabled", port, en ? "en" : "dis");
}
static void tc_enable_pd(int port, int en)
{
if (en)
atomic_clear_bits(&tc[port].pd_disabled_mask,
PD_DISABLED_NO_CONNECTION);
else
atomic_or(&tc[port].pd_disabled_mask,
PD_DISABLED_NO_CONNECTION);
}
__maybe_unused static void tc_enable_try_src(int en)
{
if (!IS_ENABLED(CONFIG_USB_PD_TRY_SRC))
assert(0);
if (en)
atomic_or(&pd_try_src, 1);
else
atomic_clear_bits(&pd_try_src, 1);
}
/*
* Exit all modes due to a detach event or hard reset
*
* Note: this skips the ExitMode VDM steps in the PE because it is assumed the
* partner is not present to receive them, and the PE will no longer be running,
* or we've forced an abrupt mode exit through a hard reset.
*/
static void tc_set_modes_exit(int port)
{
if (IS_ENABLED(CONFIG_USB_PE_SM) &&
IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) {
pd_dfp_exit_mode(port, TCPCI_MSG_SOP, 0, 0);
pd_dfp_exit_mode(port, TCPCI_MSG_SOP_PRIME, 0, 0);
pd_dfp_exit_mode(port, TCPCI_MSG_SOP_PRIME_PRIME, 0, 0);
}
}
static void tc_detached(int port)
{
TC_CLR_FLAG(port, TC_FLAGS_TS_DTS_PARTNER);
hook_notify(HOOK_USB_PD_DISCONNECT);
tc_enable_pd(port, 0);
tc_pd_connection(port, 0);
tcpm_debug_accessory(port, 0);
set_ccd_mode(port, 0);
tc_set_modes_exit(port);
if (IS_ENABLED(CONFIG_USB_PRL_SM))
prl_set_default_pd_revision(port);
/* Clear any mux connection on detach */
if (IS_ENABLED(CONFIG_USBC_SS_MUX))
usb_mux_set(port, USB_PD_MUX_NONE, USB_SWITCH_DISCONNECT,
tc[port].polarity);
}
static inline void pd_set_dual_role_and_event(int port,
enum pd_dual_role_states state,
uint32_t event)
{
drp_state[port] = state;
if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC))
pd_update_try_source();
if (event != 0)
task_set_event(PD_PORT_TO_TASK_ID(port), event);
}
void pd_set_dual_role(int port, enum pd_dual_role_states state)
{
pd_set_dual_role_and_event(port, state, PD_EVENT_UPDATE_DUAL_ROLE);
}
int pd_comm_is_enabled(int port)
{
return tc_get_pd_enabled(port);
}
void pd_request_data_swap(int port)
{
/*
* Must be in Attached.SRC, Attached.SNK, DebugAccessory.SNK,
* or UnorientedDebugAccessory.SRC when this function
* is called
*/
if (IS_ATTACHED_SRC(port) || IS_ATTACHED_SNK(port)) {
TC_SET_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP);
task_wake(PD_PORT_TO_TASK_ID(port));
}
}
/* Return true if partner port is known to be PD capable. */
bool pd_capable(int port)
{
return !!TC_CHK_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE);
}
/*
* Return true if we transition through Unattached.SNK, but we're still waiting
* to receive source caps from the partner. This indicates that the PD
* capabilities are not yet known.
*/
bool pd_waiting_on_partner_src_caps(int port)
{
return !pd_get_src_cap_cnt(port);
}
enum pd_dual_role_states pd_get_dual_role(int port)
{
return drp_state[port];
}
const char *tc_get_current_state(int port)
{
if (IS_ENABLED(USB_PD_DEBUG_LABELS))
return tc_state_names[get_state_tc(port)];
else
return "";
}
uint32_t tc_get_flags(int port)
{
return tc[port].flags;
}
int tc_is_attached_src(int port)
{
return IS_ATTACHED_SRC(port);
}
int tc_is_attached_snk(int port)
{
return IS_ATTACHED_SNK(port);
}
__overridable void tc_update_pd_sleep_mask(int port)
{
}
void tc_pd_connection(int port, int en)
{
if (en) {
bool new_pd_capable = false;
if (!TC_CHK_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE))
new_pd_capable = true;
TC_SET_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE);
/* If a PD device is attached then disable deep sleep */
if (IS_ENABLED(CONFIG_LOW_POWER_IDLE) &&
IS_ENABLED(CONFIG_USB_PD_TCPC_ON_CHIP))
tc_update_pd_sleep_mask(port);
else if (IS_ENABLED(CONFIG_LOW_POWER_IDLE))
disable_sleep(SLEEP_MASK_USB_PD);
/*
* Update the mux state, only when the PD capable flag
* transitions from 0 to 1. This ensures that PD charger
* devices, without data capability are not marked as having
* USB.
*/
if (new_pd_capable)
set_usb_mux_with_current_data_role(port);
} else {
TC_CLR_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE);
/* If a PD device isn't attached then enable deep sleep */
if (IS_ENABLED(CONFIG_LOW_POWER_IDLE) &&
IS_ENABLED(CONFIG_USB_PD_TCPC_ON_CHIP))
tc_update_pd_sleep_mask(port);
else if (IS_ENABLED(CONFIG_LOW_POWER_IDLE)) {
int i;
/* If all ports are not connected, allow the sleep */
for (i = 0; i < board_get_usb_pd_port_count(); i++) {
if (pd_capable(i))
break;
}
if (i == board_get_usb_pd_port_count())
enable_sleep(SLEEP_MASK_USB_PD);
}
}
}
void tc_ctvpd_detected(int port)
{
TC_SET_FLAG(port, TC_FLAGS_CTVPD_DETECTED);
}
void pd_try_vconn_src(int port)
{
set_vconn(port, 1);
}
int tc_check_vconn_swap(int port)
{
if (IS_ENABLED(CONFIG_USBC_VCONN)) {
if (TC_CHK_FLAG(port, TC_FLAGS_REJECT_VCONN_SWAP))
return 0;
return pd_check_vconn_swap(port);
} else
return 0;
}
void tc_pr_swap_complete(int port, bool success)
{
if (IS_ATTACHED_SNK(port)) {
/*
* Give the ADCs in the TCPC or PPC time to react following
* a PS_RDY message received during a SRC to SNK swap.
* Note: This is empirically determined, not strictly
* part of the USB PD spec.
* Note: Swap in progress should not be cleared until the
* debounce is completed.
*/
pd_timer_enable(port, TC_TIMER_VBUS_DEBOUNCE, PD_T_DEBOUNCE);
} else {
/* PR Swap is no longer in progress */
TC_CLR_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS);
/*
* AutoDischargeDisconnect was turned off near the SNK->SRC
* PR-Swap message. If the swap was a success, Vbus should be
* valid, so re-enable AutoDischargeDisconnect
*/
if (success)
tcpm_enable_auto_discharge_disconnect(port, 1);
}
}
void tc_prs_src_snk_assert_rd(int port)
{
/*
* Must be in Attached.SRC or UnorientedDebugAccessory.SRC
* when this function is called
*/
if (IS_ATTACHED_SRC(port)) {
/*
* Transition to Attached.SNK to
* DebugAccessory.SNK assert Rd
*/
TC_SET_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP);
task_wake(PD_PORT_TO_TASK_ID(port));
}
}
void tc_prs_snk_src_assert_rp(int port)
{
/*
* Must be in Attached.SNK or DebugAccessory.SNK
* when this function is called
*/
if (IS_ATTACHED_SNK(port)) {
/*
* Transition to Attached.SRC or
* UnorientedDebugAccessory.SRC to assert Rp
*/
TC_SET_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP);
task_wake(PD_PORT_TO_TASK_ID(port));
}
}
/*
* Hard Reset is being requested. This should not allow a TC connection
* to go to an unattached state until the connection is recovered from
* the hard reset. It is possible for a Hard Reset to cause a timeout
* in trying to recover and an additional Hard Reset would be issued.
* During this entire process it is important that the TC is not allowed
* to go to an unattached state.
*
* Type-C Spec Rev 2.0 section 4.5.2.2.5.2
* Exiting from Attached.SNK State
* A port that is not a V CONN-Powered USB Device and is not in the
* process of a USB PD PR_Swap or a USB PD Hard Reset or a USB PD
* FR_Swap shall transition to Unattached.SNK
*/
void tc_hard_reset_request(int port)
{
TC_SET_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED);
task_wake(PD_PORT_TO_TASK_ID(port));
}
void tc_try_src_override(enum try_src_override_t ov)
{
if (!IS_ENABLED(CONFIG_USB_PD_TRY_SRC))
assert(0);
if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) {
switch (ov) {
case TRY_SRC_OVERRIDE_OFF: /* 0 */
pd_try_src_override = TRY_SRC_OVERRIDE_OFF;
break;
case TRY_SRC_OVERRIDE_ON: /* 1 */
pd_try_src_override = TRY_SRC_OVERRIDE_ON;
break;
default:
pd_try_src_override = TRY_SRC_NO_OVERRIDE;
}
}
}
enum try_src_override_t tc_get_try_src_override(void)
{
if (!IS_ENABLED(CONFIG_USB_PD_TRY_SRC))
assert(0);
return pd_try_src_override;
}