1 /**************************************************************************/
2 /* */
3 /* Copyright (c) Microsoft Corporation. All rights reserved. */
4 /* */
5 /* This software is licensed under the Microsoft Software License */
6 /* Terms for Microsoft Azure RTOS. Full text of the license can be */
7 /* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
8 /* and in the root directory of this software. */
9 /* */
10 /**************************************************************************/
11
12
13 /**************************************************************************/
14 /**************************************************************************/
15 /** */
16 /** USBX Component */
17 /** */
18 /** OHCI Controller Driver */
19 /** */
20 /**************************************************************************/
21 /**************************************************************************/
22
23
24 /* Include necessary system files. */
25
26 #define UX_SOURCE_CODE
27
28 #include "ux_api.h"
29 #include "ux_hcd_ohci.h"
30 #include "ux_host_stack.h"
31
32
33 /**************************************************************************/
34 /* */
35 /* FUNCTION RELEASE */
36 /* */
37 /* _ux_hcd_ohci_power_root_hubs PORTABLE C */
38 /* 6.1.2 */
39 /* AUTHOR */
40 /* */
41 /* Chaoqiong Xiao, Microsoft Corporation */
42 /* */
43 /* DESCRIPTION */
44 /* */
45 /* This function powers individually or in gang mode the root HUBs */
46 /* attached to the OHCI controller. */
47 /* */
48 /* INPUT */
49 /* */
50 /* hcd_ohci Pointer to OHCI controller */
51 /* */
52 /* OUTPUT */
53 /* */
54 /* None */
55 /* */
56 /* CALLS */
57 /* */
58 /* _ux_hcd_ohci_register_read OHCI register read */
59 /* _ux_hcd_ohci_register_write OHCI register write */
60 /* _ux_utility_delay_ms Delay */
61 /* */
62 /* CALLED BY */
63 /* */
64 /* OHCI Controller Driver */
65 /* */
66 /* RELEASE HISTORY */
67 /* */
68 /* DATE NAME DESCRIPTION */
69 /* */
70 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
71 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
72 /* resulting in version 6.1 */
73 /* 11-09-2020 Chaoqiong Xiao Modified comment(s), */
74 /* fixed compile warnings, */
75 /* resulting in version 6.1.2 */
76 /* */
77 /**************************************************************************/
_ux_hcd_ohci_power_root_hubs(UX_HCD_OHCI * hcd_ohci)78 VOID _ux_hcd_ohci_power_root_hubs(UX_HCD_OHCI *hcd_ohci)
79 {
80
81 ULONG ohci_register_a;
82 ULONG ohci_register_b;
83 ULONG ohci_register_port_status;
84 UINT port_index;
85
86
87 /* Read the RH descriptor A. This will tell us if ports are always powered or not. */
88 ohci_register_a = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_RH_DESCRIPTOR_A);
89 if (ohci_register_a & OHCI_HC_RH_NPS)
90 return;
91
92 /* Read the RH descriptor B. It will give us the characteristics of the root HUB. */
93 ohci_register_b = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_RH_DESCRIPTOR_B);
94
95 /* The ports must be power switched. There are 3 possibilities:
96
97 1) individual
98 2) gang mode
99 3) a combination of both
100
101 The logic is as follows:
102
103 If the PSM bit is not set, gang mode is forced and we use the global power (LPSC) command.
104 If PSM is set, each port is powered individually.
105
106 BUT we also need to look into the PPCM field to check if there is any ports
107 that may still want to be powered by the global power command. If the bit for a port in
108 the mask is set, the power is applied by the local port command in the RH port status (PPS). */
109 if (ohci_register_a & OHCI_HC_RH_PSM)
110 {
111
112 /* Check the PPCM field to see if some existing ports need to be powered by the LPSC command. */
113 for (port_index = 0; port_index < hcd_ohci -> ux_hcd_ohci_nb_root_hubs; port_index++)
114 {
115
116 if ((ohci_register_b & (0x20000u << port_index)) == 0)
117 {
118
119 _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_RH_STATUS, OHCI_HC_RS_LPSC);
120 break;
121 }
122 }
123
124 /* Ports have to be powered individually. This is done for each of the ports whose bit mask is
125 set in the PPCM field. */
126 for (port_index = 0; port_index < hcd_ohci -> ux_hcd_ohci_nb_root_hubs; port_index++)
127 {
128
129 if ((ohci_register_b & (0x20000u << port_index)) != 0)
130 {
131
132 ohci_register_port_status = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_RH_PORT_STATUS + port_index);
133
134 ohci_register_port_status |= OHCI_HC_PS_PPS;
135 _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_RH_PORT_STATUS + port_index, ohci_register_port_status);
136 }
137 }
138 }
139 else
140 {
141
142 /* Ports have to be powered all at the same time. */
143 _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_RH_STATUS, OHCI_HC_RS_LPSC);
144 }
145
146 /* Wait for the power to be stable. the RH descriptor contains the value POTPGT. We multiply this value by 2
147 and this is the number of milliseconds to wait for power to set. */
148 _ux_utility_delay_ms(ohci_register_a >> (OHCI_HC_RH_POTPGT - 1));
149
150 /* Return to caller. */
151 return;
152 }
153
154