1 // MegaRAID SAS boot support.
2 //
3 // Copyright (C) 2012 Hannes Reinecke, SUSE Linux Products GmbH
4 //
5 // Authors:
6 // Hannes Reinecke <hare@suse.de>
7 //
8 // based on virtio-scsi.c which is written by:
9 // Paolo Bonzini <pbonzini@redhat.com>
10 //
11 // This file may be distributed under the terms of the GNU LGPLv3 license.
12
13 #include "biosvar.h" // GET_GLOBALFLAT
14 #include "block.h" // struct drive_s
15 #include "blockcmd.h" // scsi_drive_setup
16 #include "config.h" // CONFIG_*
17 #include "malloc.h" // free
18 #include "output.h" // dprintf
19 #include "pci.h" // pci_config_readl
20 #include "pcidevice.h" // foreachpci
21 #include "pci_ids.h" // PCI_DEVICE_ID_XXX
22 #include "pci_regs.h" // PCI_VENDOR_ID
23 #include "stacks.h" // yield
24 #include "std/disk.h" // DISK_RET_SUCCESS
25 #include "string.h" // memset
26 #include "util.h" // timer_calc
27
28 #define MFI_DB 0x0 // Doorbell
29 #define MFI_OMSG0 0x18 // Outbound message 0
30 #define MFI_IDB 0x20 // Inbound doorbell
31 #define MFI_ODB 0x2c // Outbound doorbell
32 #define MFI_IQP 0x40 // Inbound queue port
33 #define MFI_OSP0 0xb0 // Outbound scratch pad0
34 #define MFI_IQPL 0xc0 // Inbound queue port (low bytes)
35 #define MFI_IQPH 0xc4 // Inbound queue port (high bytes)
36
37 #define MFI_STATE_MASK 0xf0000000
38 #define MFI_STATE_WAIT_HANDSHAKE 0x60000000
39 #define MFI_STATE_BOOT_MESSAGE_PENDING 0x90000000
40 #define MFI_STATE_READY 0xb0000000
41 #define MFI_STATE_OPERATIONAL 0xc0000000
42 #define MFI_STATE_FAULT 0xf0000000
43
44 /* MFI Commands */
45 typedef enum {
46 MFI_CMD_INIT = 0x00,
47 MFI_CMD_LD_READ,
48 MFI_CMD_LD_WRITE,
49 MFI_CMD_LD_SCSI_IO,
50 MFI_CMD_PD_SCSI_IO,
51 MFI_CMD_DCMD,
52 MFI_CMD_ABORT,
53 MFI_CMD_SMP,
54 MFI_CMD_STP
55 } mfi_cmd_t;
56
57 struct megasas_cmd_frame {
58 u8 cmd; /*00h */
59 u8 sense_len; /*01h */
60 u8 cmd_status; /*02h */
61 u8 scsi_status; /*03h */
62
63 u8 target_id; /*04h */
64 u8 lun; /*05h */
65 u8 cdb_len; /*06h */
66 u8 sge_count; /*07h */
67
68 u32 context; /*08h */
69 u32 context_64; /*0Ch */
70
71 u16 flags; /*10h */
72 u16 timeout; /*12h */
73 u32 data_xfer_len; /*14h */
74
75 union {
76 struct {
77 u32 opcode; /*18h */
78 u8 mbox[12]; /*1Ch */
79 u32 sgl_addr; /*28h */
80 u32 sgl_len; /*32h */
81 u32 pad; /*34h */
82 } dcmd;
83 struct {
84 u32 sense_buf_lo; /*18h */
85 u32 sense_buf_hi; /*1Ch */
86 u8 cdb[16]; /*20h */
87 u32 sgl_addr; /*30h */
88 u32 sgl_len; /*34h */
89 } pthru;
90 struct {
91 u8 pad[22]; /*18h */
92 } gen;
93 };
94 } __attribute__ ((packed));
95
96 struct mfi_ld_list_s {
97 u32 count;
98 u32 reserved_0;
99 struct {
100 u8 target;
101 u8 lun;
102 u16 seq;
103 u8 state;
104 u8 reserved_1[3];
105 u64 size;
106 } lds[64];
107 } __attribute__ ((packed));
108
109 #define MEGASAS_POLL_TIMEOUT 60000 // 60 seconds polling timeout
110
111 struct megasas_lun_s {
112 struct drive_s drive;
113 struct megasas_cmd_frame *frame;
114 u32 iobase;
115 u16 pci_id;
116 u8 target;
117 u8 lun;
118 };
119
megasas_fire_cmd(u16 pci_id,u32 ioaddr,struct megasas_cmd_frame * frame)120 static int megasas_fire_cmd(u16 pci_id, u32 ioaddr,
121 struct megasas_cmd_frame *frame)
122 {
123 u32 frame_addr = (u32)frame;
124 int frame_count = 1;
125 u8 cmd_state;
126
127 dprintf(2, "Frame 0x%x\n", frame_addr);
128 if (pci_id == PCI_DEVICE_ID_LSI_SAS2004 ||
129 pci_id == PCI_DEVICE_ID_LSI_SAS2008) {
130 outl(0, ioaddr + MFI_IQPH);
131 outl(frame_addr | frame_count << 1 | 1, ioaddr + MFI_IQPL);
132 } else if (pci_id == PCI_DEVICE_ID_DELL_PERC5 ||
133 pci_id == PCI_DEVICE_ID_LSI_SAS1064R ||
134 pci_id == PCI_DEVICE_ID_LSI_VERDE_ZCR) {
135 outl(frame_addr >> 3 | frame_count, ioaddr + MFI_IQP);
136 } else {
137 outl(frame_addr | frame_count << 1 | 1, ioaddr + MFI_IQP);
138 }
139
140 u32 end = timer_calc(MEGASAS_POLL_TIMEOUT);
141 do {
142 for (;;) {
143 cmd_state = GET_LOWFLAT(frame->cmd_status);
144 if (cmd_state != 0xff)
145 break;
146 if (timer_check(end)) {
147 warn_timeout();
148 return -1;
149 }
150 yield();
151 }
152 } while (cmd_state == 0xff);
153
154 if (cmd_state == 0 || cmd_state == 0x2d)
155 return 0;
156 dprintf(1, "ERROR: Frame 0x%x, status 0x%x\n", frame_addr, cmd_state);
157 return -1;
158 }
159
160 int
megasas_process_op(struct disk_op_s * op)161 megasas_process_op(struct disk_op_s *op)
162 {
163 if (!CONFIG_MEGASAS)
164 return DISK_RET_EBADTRACK;
165 u8 cdb[16];
166 int blocksize = scsi_fill_cmd(op, cdb, sizeof(cdb));
167 if (blocksize < 0)
168 return default_process_op(op);
169 struct megasas_lun_s *mlun_gf =
170 container_of(op->drive_fl, struct megasas_lun_s, drive);
171 struct megasas_cmd_frame *frame = GET_GLOBALFLAT(mlun_gf->frame);
172 u16 pci_id = GET_GLOBALFLAT(mlun_gf->pci_id);
173 int i;
174
175 memset_fl(frame, 0, sizeof(*frame));
176 SET_LOWFLAT(frame->cmd, MFI_CMD_LD_SCSI_IO);
177 SET_LOWFLAT(frame->cmd_status, 0xFF);
178 SET_LOWFLAT(frame->target_id, GET_GLOBALFLAT(mlun_gf->target));
179 SET_LOWFLAT(frame->lun, GET_GLOBALFLAT(mlun_gf->lun));
180 SET_LOWFLAT(frame->flags, 0x0001);
181 SET_LOWFLAT(frame->data_xfer_len, op->count * blocksize);
182 SET_LOWFLAT(frame->cdb_len, 16);
183
184 for (i = 0; i < 16; i++) {
185 SET_LOWFLAT(frame->pthru.cdb[i], cdb[i]);
186 }
187 dprintf(2, "pthru cmd 0x%x count %d bs %d\n",
188 cdb[0], op->count, blocksize);
189
190 if (op->count) {
191 SET_LOWFLAT(frame->pthru.sgl_addr, (u32)op->buf_fl);
192 SET_LOWFLAT(frame->pthru.sgl_len, op->count * blocksize);
193 SET_LOWFLAT(frame->sge_count, 1);
194 }
195 SET_LOWFLAT(frame->context, (u32)frame);
196
197 if (megasas_fire_cmd(pci_id, GET_GLOBALFLAT(mlun_gf->iobase), frame) == 0)
198 return DISK_RET_SUCCESS;
199
200 dprintf(2, "pthru cmd 0x%x failed\n", cdb[0]);
201 return DISK_RET_EBADTRACK;
202 }
203
204 static int
megasas_add_lun(struct pci_device * pci,u32 iobase,u8 target,u8 lun)205 megasas_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun)
206 {
207 struct megasas_lun_s *mlun = malloc_fseg(sizeof(*mlun));
208 char *name;
209 int prio, ret = 0;
210
211 if (!mlun) {
212 warn_noalloc();
213 return -1;
214 }
215 memset(mlun, 0, sizeof(*mlun));
216 mlun->drive.type = DTYPE_MEGASAS;
217 mlun->drive.cntl_id = pci->bdf;
218 mlun->pci_id = pci->device;
219 mlun->target = target;
220 mlun->lun = lun;
221 mlun->iobase = iobase;
222 mlun->frame = memalign_low(256, sizeof(struct megasas_cmd_frame));
223 if (!mlun->frame) {
224 warn_noalloc();
225 free(mlun);
226 return -1;
227 }
228 boot_lchs_find_scsi_device(pci, target, lun, &(mlun->drive.lchs));
229 name = znprintf(MAXDESCSIZE, "MegaRAID SAS (PCI %pP) LD %d:%d"
230 , pci, target, lun);
231 prio = bootprio_find_scsi_device(pci, target, lun);
232 ret = scsi_drive_setup(&mlun->drive, name, prio);
233 free(name);
234 if (ret) {
235 free(mlun->frame);
236 free(mlun);
237 ret = -1;
238 }
239
240 return ret;
241 }
242
megasas_scan_target(struct pci_device * pci,u32 iobase)243 static void megasas_scan_target(struct pci_device *pci, u32 iobase)
244 {
245 struct mfi_ld_list_s ld_list;
246 struct megasas_cmd_frame *frame = memalign_tmp(256, sizeof(*frame));
247 if (!frame) {
248 warn_noalloc();
249 return;
250 }
251
252 memset(&ld_list, 0, sizeof(ld_list));
253 memset_fl(frame, 0, sizeof(*frame));
254
255 frame->cmd = MFI_CMD_DCMD;
256 frame->cmd_status = 0xFF;
257 frame->sge_count = 1;
258 frame->flags = 0x0011;
259 frame->data_xfer_len = sizeof(ld_list);
260 frame->dcmd.opcode = 0x03010000;
261 frame->dcmd.sgl_addr = (u32)MAKE_FLATPTR(GET_SEG(SS), &ld_list);
262 frame->dcmd.sgl_len = sizeof(ld_list);
263 frame->context = (u32)frame;
264
265 if (megasas_fire_cmd(pci->device, iobase, frame) == 0) {
266 dprintf(2, "%d LD found\n", ld_list.count);
267 int i;
268 for (i = 0; i < ld_list.count; i++) {
269 dprintf(2, "LD %d:%d state 0x%x\n",
270 ld_list.lds[i].target, ld_list.lds[i].lun,
271 ld_list.lds[i].state);
272 if (ld_list.lds[i].state != 0) {
273 megasas_add_lun(pci, iobase,
274 ld_list.lds[i].target, ld_list.lds[i].lun);
275 }
276 }
277 }
278 }
279
megasas_transition_to_ready(struct pci_device * pci,u32 ioaddr)280 static int megasas_transition_to_ready(struct pci_device *pci, u32 ioaddr)
281 {
282 u32 fw_state = 0, new_state, mfi_flags = 0;
283
284 if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R ||
285 pci->device == PCI_DEVICE_ID_DELL_PERC5)
286 new_state = inl(ioaddr + MFI_OMSG0) & MFI_STATE_MASK;
287 else
288 new_state = inl(ioaddr + MFI_OSP0) & MFI_STATE_MASK;
289
290 while (fw_state != new_state) {
291 switch (new_state) {
292 case MFI_STATE_FAULT:
293 dprintf(1, "ERROR: fw in fault state\n");
294 return -1;
295 break;
296 case MFI_STATE_WAIT_HANDSHAKE:
297 mfi_flags = 0x08;
298 /* fallthrough */
299 case MFI_STATE_BOOT_MESSAGE_PENDING:
300 mfi_flags |= 0x10;
301 if (pci->device == PCI_DEVICE_ID_LSI_SAS2004 ||
302 pci->device == PCI_DEVICE_ID_LSI_SAS2008 ||
303 pci->device == PCI_DEVICE_ID_LSI_SAS2208 ||
304 pci->device == PCI_DEVICE_ID_LSI_SAS3108) {
305 outl(mfi_flags, ioaddr + MFI_DB);
306 } else {
307 outl(mfi_flags, ioaddr + MFI_IDB);
308 }
309 break;
310 case MFI_STATE_OPERATIONAL:
311 mfi_flags = 0x07;
312 if (pci->device == PCI_DEVICE_ID_LSI_SAS2004 ||
313 pci->device == PCI_DEVICE_ID_LSI_SAS2008 ||
314 pci->device == PCI_DEVICE_ID_LSI_SAS2208 ||
315 pci->device == PCI_DEVICE_ID_LSI_SAS3108) {
316 outl(mfi_flags, ioaddr + MFI_DB);
317 if (pci->device == PCI_DEVICE_ID_LSI_SAS2208 ||
318 pci->device == PCI_DEVICE_ID_LSI_SAS3108) {
319 int j = 0;
320 u32 doorbell;
321
322 while (j < MEGASAS_POLL_TIMEOUT) {
323 doorbell = inl(ioaddr + MFI_DB) & 1;
324 if (!doorbell)
325 break;
326 msleep(20);
327 j++;
328 }
329 }
330 } else {
331 outl(mfi_flags, ioaddr + MFI_IDB);
332 }
333 break;
334 case MFI_STATE_READY:
335 dprintf(2, "MegaRAID SAS fw ready\n");
336 return 0;
337 }
338 // The current state should not last longer than poll timeout
339 u32 end = timer_calc(MEGASAS_POLL_TIMEOUT);
340 for (;;) {
341 if (timer_check(end)) {
342 break;
343 }
344 yield();
345 fw_state = new_state;
346 if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R ||
347 pci->device == PCI_DEVICE_ID_DELL_PERC5)
348 new_state = inl(ioaddr + MFI_OMSG0) & MFI_STATE_MASK;
349 else
350 new_state = inl(ioaddr + MFI_OSP0) & MFI_STATE_MASK;
351 if (new_state != fw_state) {
352 break;
353 }
354 }
355 }
356 dprintf(1, "ERROR: fw in state %x\n", new_state & MFI_STATE_MASK);
357 return -1;
358 }
359
360 static void
init_megasas(void * data)361 init_megasas(void *data)
362 {
363 struct pci_device *pci = data;
364 u32 bar = PCI_BASE_ADDRESS_2;
365 if (!(pci_config_readl(pci->bdf, bar) & PCI_BASE_ADDRESS_IO_MASK))
366 bar = PCI_BASE_ADDRESS_0;
367 u32 iobase = pci_enable_iobar(pci, bar);
368 if (!iobase)
369 return;
370 pci_enable_busmaster(pci);
371
372 dprintf(1, "found MegaRAID SAS at %pP, io @ %x\n", pci, iobase);
373
374 // reset
375 if (megasas_transition_to_ready(pci, iobase) == 0)
376 megasas_scan_target(pci, iobase);
377 }
378
379 void
megasas_setup(void)380 megasas_setup(void)
381 {
382 ASSERT32FLAT();
383 if (!CONFIG_MEGASAS)
384 return;
385
386 dprintf(3, "init megasas\n");
387
388 struct pci_device *pci;
389 foreachpci(pci) {
390 if (pci->vendor != PCI_VENDOR_ID_LSI_LOGIC &&
391 pci->vendor != PCI_VENDOR_ID_DELL)
392 continue;
393 if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R ||
394 pci->device == PCI_DEVICE_ID_LSI_SAS1078 ||
395 pci->device == PCI_DEVICE_ID_LSI_SAS1078DE ||
396 pci->device == PCI_DEVICE_ID_LSI_SAS2108 ||
397 pci->device == PCI_DEVICE_ID_LSI_SAS2108E ||
398 pci->device == PCI_DEVICE_ID_LSI_SAS2004 ||
399 pci->device == PCI_DEVICE_ID_LSI_SAS2008 ||
400 pci->device == PCI_DEVICE_ID_LSI_VERDE_ZCR ||
401 pci->device == PCI_DEVICE_ID_DELL_PERC5 ||
402 pci->device == PCI_DEVICE_ID_LSI_SAS2208 ||
403 pci->device == PCI_DEVICE_ID_LSI_SAS3108)
404 run_thread(init_megasas, pci);
405 }
406 }
407