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
|
/*
* SPDX-FileCopyrightText: Copyright (c) 2019 Ha Thach (tinyusb.org)
* SPDX-License-Identifier: MIT
*
* This file is part of the TinyUSB stack.
*/
#ifndef TUSB_OHCI_H_
#define TUSB_OHCI_H_
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// OHCI CONFIGURATION & CONSTANTS
//--------------------------------------------------------------------+
#define HOST_HCD_XFER_INTERRUPT // TODO interrupt is used widely, should always be enabled
#define OHCI_PERIODIC_LIST (defined HOST_HCD_XFER_INTERRUPT || defined HOST_HCD_XFER_ISOCHRONOUS)
// TODO merge OHCI with EHCI
enum {
OHCI_MAX_ITD = 4
};
#define ED_MAX (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX)
#define GTD_MAX ED_MAX
// tinyUSB's OHCI implementation caps number of EDs to 8 bits
TU_VERIFY_STATIC (ED_MAX <= 256, "Reduce CFG_TUH_DEVICE_MAX or CFG_TUH_ENDPOINT_MAX");
#define GTD_ALIGN_SIZE TU_MAX(CFG_TUH_MEM_DCACHE_LINE_SIZE, 16)
#define ED_ALIGN_SIZE TU_MAX(CFG_TUH_MEM_DCACHE_LINE_SIZE, 16)
#define ITD_ALIGN_SIZE TU_MAX(CFG_TUH_MEM_DCACHE_LINE_SIZE, 32)
//--------------------------------------------------------------------+
// OHCI Data Structure
//--------------------------------------------------------------------+
typedef struct {
uint32_t interrupt_table[32];
volatile uint16_t frame_number;
volatile uint16_t frame_pad;
volatile uint32_t done_head;
uint8_t reserved[116+4]; // TODO try to make use of this area if possible, extra 4 byte to make the whole struct size = 256
}ohci_hcca_t; // TU_ATTR_ALIGNED(256)
TU_VERIFY_STATIC( sizeof(ohci_hcca_t) == 256, "size is not correct" );
// An OHCI host controller is controlled using data structures placed in memory (RAM).
// It needs to both read and write these data structures (as defined by the OHCI specification),
// and this can be mentally conceptualized similar to two software threads running on
// two different CPUs. In order to prevent a _data race_ where data gets corrupted,
// the CPU and the OHCI host controller need to agree on how the memory should be accessed.
// In this driver, we do this by transferring logical ownership of transfer descriptors (TDs)
// between the CPU and the OHCI host controller. Only the device which holds the logical ownership
// is allowed to read or write the TD. This ownership is not visible anywhere in the code,
// but it instead must be inferred based on the logical state of the transfer.
//
// If dcache-supporting mode is enabled, we need to do additional manual cache operations
// in order to correctly transfer this logical ownership and prevent data corruption.
// In order to do this, we also choose to align each OHCI TD so that it doesn't
// share CPU cache lines with other TDs. This is because manual cache operations
// can only be performed on cache line granularity. In other words, one cache line is
// the _smallest_ amount that can be read/written at a time. If there were to be multiple TDs
// in the same cache line, they would be required to always have the same logical ownership.
// This ends up being impossible to guarantee, so we choose a design which avoids the situation entirely.
// common link item for gtd and itd for list travel
typedef struct TU_ATTR_ALIGNED(16) {
uint32_t reserved[2];
volatile uint32_t next;
uint32_t reserved2;
}ohci_td_item_t;
typedef struct TU_ATTR_ALIGNED(GTD_ALIGN_SIZE) {
// Word 0
uint32_t used : 1;
uint32_t index : 8; // endpoint index the gtd belongs to, or device address in case of control xfer
uint32_t : 9; // can be used
uint32_t buffer_rounding : 1;
uint32_t pid : 2;
uint32_t delay_interrupt : 3;
volatile uint32_t data_toggle : 2;
volatile uint32_t error_count : 2;
volatile uint32_t condition_code : 4;
// Word 1
uint8_t* volatile current_buffer_pointer;
// Word 2 : next TD
volatile uint32_t next;
// Word 3
uint8_t* buffer_end;
} ohci_gtd_t;
TU_VERIFY_STATIC(sizeof(ohci_gtd_t) == GTD_ALIGN_SIZE, "size is not correct" );
typedef union {
struct {
uint32_t dev_addr : 7;
uint32_t ep_number : 4;
uint32_t pid : 2;
uint32_t speed : 1;
uint32_t skip : 1;
uint32_t is_iso : 1;
uint32_t max_packet_size : 11;
// HCD: make use of 5 reserved bits
uint32_t used : 1;
uint32_t is_interrupt_xfer : 1;
uint32_t : 3;
};
uint32_t value;
} ohci_ed_word0_t;
TU_VERIFY_STATIC(sizeof(ohci_ed_word0_t) == 4, "size is not correct" );
typedef union {
uint32_t address;
struct {
uint32_t halted : 1;
uint32_t toggle : 1;
uint32_t : 30;
};
} ohci_ed_word2_t;
TU_VERIFY_STATIC(sizeof(ohci_ed_word2_t) == 4, "size is not correct" );
typedef struct TU_ATTR_ALIGNED(ED_ALIGN_SIZE) {
ohci_ed_word0_t w0; // Word 0
uint32_t td_tail; // Word 1
volatile ohci_ed_word2_t td_head; // Word 2
uint32_t next; // Word 3
} ohci_ed_t;
TU_VERIFY_STATIC(sizeof(ohci_ed_t) == ED_ALIGN_SIZE, "size is not correct" );
typedef struct TU_ATTR_ALIGNED(ITD_ALIGN_SIZE) {
/*---------- Word 1 ----------*/
uint32_t starting_frame : 16;
uint32_t : 5; // can be used
uint32_t delay_interrupt : 3;
uint32_t frame_count : 3;
uint32_t : 1; // can be used
volatile uint32_t condition_code : 4;
/*---------- Word 2 ----------*/
uint32_t buffer_page0; // 12 lsb bits can be used
/*---------- Word 3 ----------*/
volatile uint32_t next;
/*---------- Word 4 ----------*/
uint32_t buffer_end;
/*---------- Word 5-8 ----------*/
volatile uint16_t offset_packetstatus[8];
} ohci_itd_t;
TU_VERIFY_STATIC(sizeof(ohci_itd_t) == ITD_ALIGN_SIZE, "size is not correct" );
typedef struct {
uint16_t expected_bytes; // up to 8192 bytes so max is 13 bits
} gtd_extra_data_t;
TU_VERIFY_STATIC(sizeof(gtd_extra_data_t) == 2, "size is not correct" );
// structure with member alignment required from large to small
typedef struct TU_ATTR_ALIGNED(256) {
ohci_hcca_t hcca;
ohci_ed_t bulk_head_ed; // static bulk head (dummy)
ohci_ed_t period_head_ed; // static periodic list head (dummy)
// control endpoints has reserved resources
struct {
ohci_ed_t ed;
ohci_gtd_t gtd;
} control[CFG_TUH_DEVICE_MAX + CFG_TUH_HUB + 1];
// ochi_itd_t itd[OHCI_MAX_ITD]; // itd requires alignment of 32
ohci_ed_t ed_pool[ED_MAX];
ohci_gtd_t gtd_pool[GTD_MAX];
// extra data needed by TDs that can't fit in the TD struct
gtd_extra_data_t gtd_extra_control[CFG_TUH_DEVICE_MAX + CFG_TUH_HUB + 1];
gtd_extra_data_t gtd_extra[GTD_MAX];
volatile uint16_t frame_number_hi;
} ohci_data_t;
//--------------------------------------------------------------------+
// OHCI Operational Register
//--------------------------------------------------------------------+
//--------------------------------------------------------------------+
// OHCI Data Organization
//--------------------------------------------------------------------+
typedef volatile struct
{
uint32_t revision; // 0x00
union {
uint32_t control; // 0x04
struct {
uint32_t control_bulk_service_ratio : 2;
uint32_t periodic_list_enable : 1;
uint32_t isochronous_enable : 1;
uint32_t control_list_enable : 1;
uint32_t bulk_list_enable : 1;
uint32_t hc_functional_state : 2;
uint32_t interrupt_routing : 1;
uint32_t remote_wakeup_connected : 1;
uint32_t remote_wakeup_enale : 1;
uint32_t TU_RESERVED : 21;
}control_bit;
};
union {
uint32_t command_status; // 0x08
struct {
uint32_t controller_reset : 1;
uint32_t control_list_filled : 1;
uint32_t bulk_list_filled : 1;
uint32_t ownership_change_request : 1;
uint32_t : 12;
uint32_t scheduling_overrun_count : 2;
}command_status_bit;
};
uint32_t interrupt_status; // 0x0C
uint32_t interrupt_enable; // 0x10
uint32_t interrupt_disable; // 0x14
uint32_t hcca; // 0x18
uint32_t period_current_ed; // 0x1C
uint32_t control_head_ed; // 0x20
uint32_t control_current_ed; // 0x24
uint32_t bulk_head_ed; // 0x28
uint32_t bulk_current_ed; // 0x2C
uint32_t done_head; // 0x30
uint32_t frame_interval; // 0x34
uint32_t frame_remaining; // 0x38
uint32_t frame_number; // 0x3C
uint32_t periodic_start; // 0x40
uint32_t lowspeed_threshold; // 0x44
union {
uint32_t rh_descriptorA; // 0x48
struct {
uint32_t number_downstream_ports : 8;
uint32_t power_switching_mode : 1;
uint32_t no_power_switching : 1;
uint32_t device_type : 1;
uint32_t overcurrent_protection_mode : 1;
uint32_t no_over_current_protection : 1;
uint32_t reserved : 11;
uint32_t power_on_to_good_time : 8;
} rh_descriptorA_bit;
};
union {
uint32_t rh_descriptorB; // 0x4C
struct {
uint32_t device_removable : 16;
uint32_t port_power_control_mask : 16;
} rh_descriptorB_bit;
};
union {
uint32_t rh_status; // 0x50
struct {
uint32_t local_power_status : 1; // read Local Power Status; write: Clear Global Power
uint32_t over_current_indicator : 1;
uint32_t : 13;
uint32_t device_remote_wakeup_enable : 1;
uint32_t local_power_status_change : 1;
uint32_t over_current_indicator_change : 1;
uint32_t : 13;
uint32_t clear_remote_wakeup_enable : 1;
}rh_status_bit;
};
union {
uint32_t rhport_status[TUP_OHCI_RHPORTS]; // 0x54
struct {
uint32_t current_connect_status : 1;
uint32_t port_enable_status : 1;
uint32_t port_suspend_status : 1;
uint32_t port_over_current_indicator : 1;
uint32_t port_reset_status : 1;
uint32_t : 3;
uint32_t port_power_status : 1;
uint32_t low_speed_device_attached : 1;
uint32_t : 6;
uint32_t connect_status_change : 1;
uint32_t port_enable_status_change : 1;
uint32_t port_suspend_status_change : 1;
uint32_t port_over_current_indicator_change : 1;
uint32_t port_reset_status_change : 1;
uint32_t TU_RESERVED : 11;
}rhport_status_bit[TUP_OHCI_RHPORTS];
};
}ohci_registers_t;
TU_VERIFY_STATIC( sizeof(ohci_registers_t) == (0x54 + (4 * TUP_OHCI_RHPORTS)), "size is not correct");
#ifdef __cplusplus
}
#endif
#endif /* TUSB_OHCI_H_ */
|