Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | /* |
2 | * ========== Copyright Header Begin ========================================== | |
3 | * | |
4 | * OpenSPARC T2 Processor File: sas.h | |
5 | * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
6 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES. | |
7 | * | |
8 | * The above named program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public | |
10 | * License version 2 as published by the Free Software Foundation. | |
11 | * | |
12 | * The above named program is distributed in the hope that it will be | |
13 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public | |
18 | * License along with this work; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. | |
20 | * | |
21 | * ========== Copyright Header End ============================================ | |
22 | */ | |
23 | #ifndef __SAS_H__ | |
24 | #define __SAS_H__ | |
25 | ||
26 | #include <queue> | |
27 | #include "pcie.h" | |
28 | #include "pci_dev.h" | |
29 | #include "module.h" | |
30 | #include "mpi.h" | |
31 | #include "mpi_ioc.h" | |
32 | #include "mpi_init.h" | |
33 | #include "mpi_cnfg.h" | |
34 | #include "disk.h" | |
35 | #include "system.h" | |
36 | ||
37 | ||
38 | // ioc parameters | |
39 | #define IOC_REPLY_QUEUE_DEPTH 128 | |
40 | #define IOC_REQ_QUEUE_DEPTH 128 | |
41 | #define IOC_PRI_QUEUE_DEPTH 128 | |
42 | #define IOC_REQ_FRAME_SIZE 128 | |
43 | #define IOC_CHAIN_DEPTH 128 | |
44 | #define IOC_NUM_PORTS 4 | |
45 | #define IOC_BLOCK_SIZE 32 | |
46 | #define IOC_PRODUCTION_ID 101 | |
47 | #define IOC_CAPABILITIES 0xfc1 | |
48 | ||
49 | ||
50 | // port parameters | |
51 | #define PORT_TYPE 0x30 | |
52 | #define PORT_MAX_DEVICES 1 | |
53 | #define PORT_SCSI_ID 0 | |
54 | #define PORT_PROTOCOL_FLAGS 0xc | |
55 | #define PORT_MAX_POSTED_CMD_BUFFERS 32 | |
56 | #define PORT_MAX_PERSISTENT_IDS 1 | |
57 | #define PORT_MAX_LAN_BUCKETS 0 | |
58 | ||
59 | ||
60 | // message frame address mask | |
61 | #define FMA_ADDRESS_MASK 0xfffffff8 | |
62 | ||
63 | ||
64 | // maximal size of message (in byte) received/sent through doorbell handshake | |
65 | #define HDSHK_MSG_SIZE 256 | |
66 | ||
67 | ||
68 | #define swap_hword(value) \ | |
69 | ((uint32_t)(((value) & 0xff) << 8 | (value) >> 8)) | |
70 | ||
71 | #define swap_word(value) \ | |
72 | ((uint32_t)((Word)swap_hword((HWord)((value) & 0xffff)) << 16 | \ | |
73 | (Word)swap_hword((HWord)((value) >> 16)))) | |
74 | ||
75 | #define swap_lword(value) \ | |
76 | ((uint64_t)((((LWord)swap_word(LO_W(value))) << 32) | \ | |
77 | ((LWord)swap_word(HI_W(value))))) | |
78 | ||
79 | ||
80 | // system interface register set | |
81 | typedef struct { | |
82 | uint32_t doorbell; | |
83 | uint32_t write_seq; | |
84 | uint32_t diag; | |
85 | uint32_t test_base_addr; | |
86 | uint32_t diagrw_data; | |
87 | uint32_t diagrw_addr; | |
88 | ||
89 | uint32_t res1[6]; | |
90 | ||
91 | uint32_t intr_status; | |
92 | uint32_t intr_mask; | |
93 | ||
94 | uint32_t res2[2]; | |
95 | ||
96 | uint32_t req_q; | |
97 | uint32_t reply_q; | |
98 | uint32_t pri_req_q; | |
99 | } mpt_reg_t; | |
100 | ||
101 | ||
102 | // ioc configuration | |
103 | typedef struct { | |
104 | // assigned by ioc init | |
105 | uint8_t who_init; | |
106 | uint8_t max_devices; | |
107 | uint8_t max_buses; | |
108 | uint16_t reply_frame_size; | |
109 | uint32_t host_mfa_high_addr; | |
110 | uint32_t sense_buffer_high_addr; | |
111 | sge_simple64_t host_page_buffer_sge; | |
112 | } ioc_conf_t; | |
113 | ||
114 | ||
115 | class SAS; | |
116 | ||
117 | ||
118 | // event queue callback function | |
119 | extern void sas_io_callback(void *arg1, void *arg2); | |
120 | ||
121 | ||
122 | /* event table is used to support diskdelay. | |
123 | It is not a FIFO, because some later request may | |
124 | finish early than an early one. | |
125 | ||
126 | allocate an entry: when a request is received. | |
127 | free an entry: when a reply is read. | |
128 | */ | |
129 | #define EVENT_TABLE_SIZE 128 | |
130 | ||
131 | ||
132 | typedef struct { | |
133 | // valid flag | |
134 | uint8_t used; | |
135 | ||
136 | // for diskdelay, parameters used to by send_mpi_rely() in the callback function | |
137 | uint32_t reply; | |
138 | uint8_t is_cntx; | |
139 | uint8_t cntx_type; | |
140 | ||
141 | // callback function invoke time | |
142 | int64_t invoke_time; | |
143 | ||
144 | // done flag | |
145 | uint8_t done; | |
146 | } event_table_entry_t; | |
147 | ||
148 | ||
149 | typedef struct { | |
150 | event_table_entry_t events[EVENT_TABLE_SIZE]; | |
151 | int next; // next available entry | |
152 | ||
153 | void init() { | |
154 | next = 0; | |
155 | ||
156 | for (int i = 0; i < EVENT_TABLE_SIZE; i++) { | |
157 | events[i].used = 0; | |
158 | events[i].reply = 0; | |
159 | events[i].is_cntx = 0; | |
160 | events[i].cntx_type = 0; | |
161 | events[i].invoke_time = 0; | |
162 | events[i].done = 0; | |
163 | } | |
164 | } | |
165 | ||
166 | void clear() { | |
167 | init(); | |
168 | } | |
169 | ||
170 | int alloc() { | |
171 | int entry = next; | |
172 | ||
173 | for (int i = 0; i < EVENT_TABLE_SIZE; i++) { | |
174 | if (events[entry].used == 0) { | |
175 | events[entry].used = 1; | |
176 | events[entry].reply = 0; | |
177 | events[entry].is_cntx = 0; | |
178 | events[entry].cntx_type = 0; | |
179 | events[entry].invoke_time = 0; | |
180 | events[entry].done = 0; | |
181 | ||
182 | next = entry + 1; | |
183 | if (next == EVENT_TABLE_SIZE) | |
184 | next = 0; | |
185 | ||
186 | return(entry); | |
187 | } | |
188 | else { | |
189 | entry++; | |
190 | if (entry == EVENT_TABLE_SIZE) | |
191 | entry = 0; | |
192 | } | |
193 | } | |
194 | ||
195 | fprintf(stderr, "no free entry in the event table\n"); | |
196 | exit(1); | |
197 | } | |
198 | ||
199 | void free(int entry) { | |
200 | events[entry].used = 0; | |
201 | } | |
202 | ||
203 | void set_invoke_time(int entry, int64_t time) { | |
204 | events[entry].invoke_time = time; | |
205 | } | |
206 | ||
207 | int64_t get_invoke_time(int entry) { | |
208 | return(events[entry].invoke_time); | |
209 | } | |
210 | ||
211 | void done(int entry, uint32_t reply, uint8_t is_cntx, uint8_t cntx_type) { | |
212 | events[entry].reply = reply; | |
213 | events[entry].is_cntx = is_cntx; | |
214 | events[entry].cntx_type = cntx_type; | |
215 | events[entry].done = 1; | |
216 | } | |
217 | ||
218 | void dump(SAS *sasp, FILE *fp) { | |
219 | fprintf(fp, "next_entry %d\n", next); | |
220 | for (int i = 0; i < EVENT_TABLE_SIZE; i++) { | |
221 | fprintf(fp, "event_table[%d] %hhu %u %hhu %hhu %lld %hhu\n", i, | |
222 | events[i].used, | |
223 | events[i].reply, | |
224 | events[i].is_cntx, | |
225 | events[i].cntx_type, | |
226 | events[i].invoke_time, | |
227 | events[i].done); | |
228 | } | |
229 | } | |
230 | ||
231 | void restore(SAS *sasp, FILE *fp) { | |
232 | char name[64]; | |
233 | fscanf(fp, "%s %d\n", name, &next); | |
234 | for (int i = 0; i < EVENT_TABLE_SIZE; i++) { | |
235 | fscanf(fp, "%s %hhu %u %hhu %hhu %lld %hhu\n", name, | |
236 | &events[i].used, | |
237 | &events[i].reply, | |
238 | &events[i].is_cntx, | |
239 | &events[i].cntx_type, | |
240 | &events[i].invoke_time, | |
241 | &events[i].done); | |
242 | if (events[i].used == 1) { | |
243 | if (events[i].done != 1) { | |
244 | printf("io request for event %d has not been processed yet\n", i); | |
245 | exit(1); | |
246 | } | |
247 | mmi_register_event(events[i].invoke_time, (EventFunc_T*)&sas_io_callback, (void *)sasp, (void *)i); | |
248 | } | |
249 | } | |
250 | } | |
251 | } event_table_t; | |
252 | ||
253 | ||
254 | // SAS controller | |
255 | class SAS : public genericPcieDev, public Module { | |
256 | const char *fname; // configuration file | |
257 | ||
258 | uint32_t _io_base; // io base | |
259 | uint64_t _mem0_base; // mem0 base | |
260 | uint64_t _mem1_base; // mem1 base | |
261 | ||
262 | uint8_t _hdshk_msg_req_size; // the size of request msg received through doorbell handshake | |
263 | uint8_t _hdshk_msg_req_recv; // number of bytes received | |
264 | uchar_t *_hdshk_msg_req_buf; // buffer for doorbell handshake request msg | |
265 | ||
266 | uint8_t _hdshk_msg_reply_size; // the size of reply msg sent through doorbell handshake | |
267 | uint8_t _hdshk_msg_reply_send; // number of bytes sent | |
268 | uchar_t *_hdshk_msg_reply_buf; // buffer for doorbell handshake reply msg | |
269 | ||
270 | mpt_reg_t *_mpt_reg; // system interface register set | |
271 | ioc_conf_t *_ioc_conf; // ioc configuration | |
272 | ||
273 | uint8_t *_port_enabled; // port enabled | |
274 | ||
275 | int64_t *_earliest_serving_time; // the earliest time when the controller is ready for new service | |
276 | ||
277 | uint16_t _request_queue_size; // number of outstanding requests | |
278 | queue<uint32_t> _reply_post_queue; // reply post queue | |
279 | queue<uint32_t> _reply_free_queue; // reply free queue | |
280 | ||
281 | event_table_t _etable; // event table, for recording events | |
282 | ||
283 | SCSIDisk *_disk[IOC_NUM_PORTS]; // attached disks, one disk per port | |
284 | ||
285 | pthread_mutex_t _reply_queue_mutex; // mutex for reply queue | |
286 | ||
287 | void reset_ioc(); | |
288 | ||
289 | void send_handshake_msg(); | |
290 | void process_handshake_msg(); | |
291 | void send_mpi_msg(uint32_t reply, bool is_cntx, uint8_t cntx_type); | |
292 | void process_mpi_msg(int entry, uint32_t fma); | |
293 | ||
294 | void doorbell_function_handshake(uint8_t size); | |
295 | void doorbell_function_reply_frame_removal(); | |
296 | void doorbell_function_ioc_msg_unit_reset(); | |
297 | ||
298 | void compute_disk_delay(int entry, int target, bool wr); | |
299 | void scsi_io_done(int entry, uint32_t reply, uint8_t is_cntx, uint8_t cntx_type, int target, bool wr); | |
300 | void scsi_io_error(int entry, msg_scsi_io_request_t *req, uint16_t ioc_status); | |
301 | void process_scsi_cmd(int entry, msg_scsi_io_request_t *req, uint64_t sge_addr); | |
302 | ||
303 | void send_page_reply(msg_config_t *req, msg_config_reply_t *reply, config_page_header_t *page_header); | |
304 | void send_extended_page_reply(msg_config_t *req, msg_config_reply_t *reply, | |
305 | config_extended_page_header_t *extended_page_header, bool invalid); | |
306 | void access_page_manufacturing(msg_config_t *req, msg_config_reply_t *reply); | |
307 | void access_page_ioc(msg_config_t *req, msg_config_reply_t *reply); | |
308 | void access_page_sas_io_unit(msg_config_t *req, msg_config_reply_t *reply); | |
309 | void access_page_sas_device(msg_config_t *req, msg_config_reply_t *reply); | |
310 | void access_page_sas_phy(msg_config_t *req, msg_config_reply_t *reply); | |
311 | ||
312 | bool memory_space_access(int offset, bool wr, uint64_t *in_buf, uint8_t size); | |
313 | void access_door_bell(bool wr, uint64_t *in_buf, uint8_t size); | |
314 | void access_write_sequence(bool wr, uint64_t *in_buf, uint8_t size); | |
315 | void access_host_diagnostic(bool wr, uint64_t *in_buf, uint8_t size); | |
316 | void access_test_base_address(bool wr, uint64_t *in_buf, uint8_t size); | |
317 | void access_diag_rw_data(bool wr, uint64_t *in_buf, uint8_t size); | |
318 | void access_diag_rw_address(bool wr, uint64_t *in_buf, uint8_t size); | |
319 | void access_host_interrupt_status(bool wr, uint64_t *in_buf, uint8_t size); | |
320 | void access_host_interrupt_mask(bool wr, uint64_t *in_buf, uint8_t size); | |
321 | void access_host_request_queue(bool wr, uint64_t *in_buf, uint8_t size); | |
322 | void access_reply_queue(bool wr, uint64_t *in_buf, uint8_t size); | |
323 | void access_hi_pri_request_queue(bool wr, uint64_t *in_buf, uint8_t size); | |
324 | ||
325 | void memory_access(uint64_t va, addrMd_xactnType mode, void *data, uint32_t size, bool wr); | |
326 | void memory_transport(uint64_t sge_addr, addrMd_xactnType sge_addr_mode, void *data, uint32_t size, bool wr); | |
327 | void memory_transport(void *sge, void *data, uint32_t size, bool wr); | |
328 | ||
329 | void set_doorbell_interrupt(); | |
330 | void set_reply_msg_interrupt(); | |
331 | ||
332 | bool msi_enabled(); | |
333 | ||
334 | addrMd_xactnType mfa_addr_mode() { | |
335 | return(_ioc_conf->host_mfa_high_addr == 0 ? mem_addr32 : mem_addr64); | |
336 | } | |
337 | ||
338 | uint64_t mfa_addr(uint32_t addr) { | |
339 | return(addr | (_ioc_conf->host_mfa_high_addr << 32)); | |
340 | } | |
341 | ||
342 | public: | |
343 | SAS(const char *modname, const char *instance_name); | |
344 | ~SAS(); | |
345 | ||
346 | // callback function for disk io | |
347 | void io_callback(int entry); | |
348 | ||
349 | // override virtual functions from Module class | |
350 | const char *get_help(); | |
351 | const char *get_version(); | |
352 | bool parse_arg(const char *); | |
353 | bool check_args(); | |
354 | void init_done(); | |
355 | void module_added(mmi_instance_t, const char*); | |
356 | void module_deleted(mmi_instance_t, const char*); | |
357 | void modinfo(); | |
358 | void *get_interface(const char *name); | |
359 | bool dump(FILE *fp); | |
360 | bool restore(FILE *fp); | |
361 | ||
362 | // override virtual function from genericPcieDev | |
363 | pcieCompleter devif_memAccess(bool wr, uint64_t offset, addrMd_xactnType mode, void * data,\ | |
364 | uint16_t length, uint8_t be, uint16_t reqId, tlp_X args,SAM_DeviceId* samId); | |
365 | ||
366 | pcieCompleter devif_confAccess(bool wr, uint32_t offset, void * data, \ | |
367 | uint8_t be,uint16_t reqId, addrMd_xactnType tType, uint16_t length, tlp_X args,SAM_DeviceId* samId); | |
368 | ||
369 | pcieCompleter devif_ioAccess(bool wr, uint32_t offset, void * data, uint8_t be, \ | |
370 | uint16_t reqId, uint16_t length, tlp_X args,SAM_DeviceId* samId); | |
371 | ||
372 | pcieCompleter devif_msgAccess(uint8_t messageCode, msgRouting route, uint64_t tarIdOrAddr, uint16_t reqId, \ | |
373 | void * data, uint16_t length, uint16_t vendorId, uint32_t vendor_data, tlp_X args,SAM_DeviceId* samId); | |
374 | ||
375 | void devif_confAccessCb(bool wr, uint64_t offset, uint8_t be); | |
376 | ||
377 | mmi_instance_t devif_getInstance() {return instance;} | |
378 | ||
379 | const char *devif_getName() {return getName();} | |
380 | ||
381 | // other functions | |
382 | void handle_ui_cmd(int argc, char * argv[]); | |
383 | ||
384 | void initPci(); | |
385 | ||
386 | }; | |
387 | ||
388 | #endif // __SAS_H__ |