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 name = znprintf(MAXDESCSIZE, "MegaRAID SAS (PCI %pP) LD %d:%d"
229 , pci, target, lun);
230 prio = bootprio_find_scsi_device(pci, target, lun);
231 ret = scsi_drive_setup(&mlun->drive, name, prio);
232 free(name);
233 if (ret) {
234 free(mlun->frame);
235 free(mlun);
236 ret = -1;
237 }
238
239 return ret;
240 }
241
megasas_scan_target(struct pci_device * pci,u32 iobase)242 static void megasas_scan_target(struct pci_device *pci, u32 iobase)
243 {
244 struct mfi_ld_list_s ld_list;
245 struct megasas_cmd_frame *frame = memalign_tmp(256, sizeof(*frame));
246 if (!frame) {
247 warn_noalloc();
248 return;
249 }
250
251 memset(&ld_list, 0, sizeof(ld_list));
252 memset_fl(frame, 0, sizeof(*frame));
253
254 frame->cmd = MFI_CMD_DCMD;
255 frame->cmd_status = 0xFF;
256 frame->sge_count = 1;
257 frame->flags = 0x0011;
258 frame->data_xfer_len = sizeof(ld_list);
259 frame->dcmd.opcode = 0x03010000;
260 frame->dcmd.sgl_addr = (u32)MAKE_FLATPTR(GET_SEG(SS), &ld_list);
261 frame->dcmd.sgl_len = sizeof(ld_list);
262 frame->context = (u32)frame;
263
264 if (megasas_fire_cmd(pci->device, iobase, frame) == 0) {
265 dprintf(2, "%d LD found\n", ld_list.count);
266 int i;
267 for (i = 0; i < ld_list.count; i++) {
268 dprintf(2, "LD %d:%d state 0x%x\n",
269 ld_list.lds[i].target, ld_list.lds[i].lun,
270 ld_list.lds[i].state);
271 if (ld_list.lds[i].state != 0) {
272 megasas_add_lun(pci, iobase,
273 ld_list.lds[i].target, ld_list.lds[i].lun);
274 }
275 }
276 }
277 }
278
megasas_transition_to_ready(struct pci_device * pci,u32 ioaddr)279 static int megasas_transition_to_ready(struct pci_device *pci, u32 ioaddr)
280 {
281 u32 fw_state = 0, new_state, mfi_flags = 0;
282
283 if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R ||
284 pci->device == PCI_DEVICE_ID_DELL_PERC5)
285 new_state = inl(ioaddr + MFI_OMSG0) & MFI_STATE_MASK;
286 else
287 new_state = inl(ioaddr + MFI_OSP0) & MFI_STATE_MASK;
288
289 while (fw_state != new_state) {
290 switch (new_state) {
291 case MFI_STATE_FAULT:
292 dprintf(1, "ERROR: fw in fault state\n");
293 return -1;
294 break;
295 case MFI_STATE_WAIT_HANDSHAKE:
296 mfi_flags = 0x08;
297 /* fallthrough */
298 case MFI_STATE_BOOT_MESSAGE_PENDING:
299 mfi_flags |= 0x10;
300 if (pci->device == PCI_DEVICE_ID_LSI_SAS2004 ||
301 pci->device == PCI_DEVICE_ID_LSI_SAS2008 ||
302 pci->device == PCI_DEVICE_ID_LSI_SAS2208 ||
303 pci->device == PCI_DEVICE_ID_LSI_SAS3108) {
304 outl(mfi_flags, ioaddr + MFI_DB);
305 } else {
306 outl(mfi_flags, ioaddr + MFI_IDB);
307 }
308 break;
309 case MFI_STATE_OPERATIONAL:
310 mfi_flags = 0x07;
311 if (pci->device == PCI_DEVICE_ID_LSI_SAS2004 ||
312 pci->device == PCI_DEVICE_ID_LSI_SAS2008 ||
313 pci->device == PCI_DEVICE_ID_LSI_SAS2208 ||
314 pci->device == PCI_DEVICE_ID_LSI_SAS3108) {
315 outl(mfi_flags, ioaddr + MFI_DB);
316 if (pci->device == PCI_DEVICE_ID_LSI_SAS2208 ||
317 pci->device == PCI_DEVICE_ID_LSI_SAS3108) {
318 int j = 0;
319 u32 doorbell;
320
321 while (j < MEGASAS_POLL_TIMEOUT) {
322 doorbell = inl(ioaddr + MFI_DB) & 1;
323 if (!doorbell)
324 break;
325 msleep(20);
326 j++;
327 }
328 }
329 } else {
330 outl(mfi_flags, ioaddr + MFI_IDB);
331 }
332 break;
333 case MFI_STATE_READY:
334 dprintf(2, "MegaRAID SAS fw ready\n");
335 return 0;
336 }
337 // The current state should not last longer than poll timeout
338 u32 end = timer_calc(MEGASAS_POLL_TIMEOUT);
339 for (;;) {
340 if (timer_check(end)) {
341 break;
342 }
343 yield();
344 fw_state = new_state;
345 if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R ||
346 pci->device == PCI_DEVICE_ID_DELL_PERC5)
347 new_state = inl(ioaddr + MFI_OMSG0) & MFI_STATE_MASK;
348 else
349 new_state = inl(ioaddr + MFI_OSP0) & MFI_STATE_MASK;
350 if (new_state != fw_state) {
351 break;
352 }
353 }
354 }
355 dprintf(1, "ERROR: fw in state %x\n", new_state & MFI_STATE_MASK);
356 return -1;
357 }
358
359 static void
init_megasas(void * data)360 init_megasas(void *data)
361 {
362 struct pci_device *pci = data;
363 u32 bar = PCI_BASE_ADDRESS_2;
364 if (!(pci_config_readl(pci->bdf, bar) & PCI_BASE_ADDRESS_IO_MASK))
365 bar = PCI_BASE_ADDRESS_0;
366 u32 iobase = pci_enable_iobar(pci, bar);
367 if (!iobase)
368 return;
369 pci_enable_busmaster(pci);
370
371 dprintf(1, "found MegaRAID SAS at %pP, io @ %x\n", pci, iobase);
372
373 // reset
374 if (megasas_transition_to_ready(pci, iobase) == 0)
375 megasas_scan_target(pci, iobase);
376 }
377
378 void
megasas_setup(void)379 megasas_setup(void)
380 {
381 ASSERT32FLAT();
382 if (!CONFIG_MEGASAS)
383 return;
384
385 dprintf(3, "init megasas\n");
386
387 struct pci_device *pci;
388 foreachpci(pci) {
389 if (pci->vendor != PCI_VENDOR_ID_LSI_LOGIC &&
390 pci->vendor != PCI_VENDOR_ID_DELL)
391 continue;
392 if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R ||
393 pci->device == PCI_DEVICE_ID_LSI_SAS1078 ||
394 pci->device == PCI_DEVICE_ID_LSI_SAS1078DE ||
395 pci->device == PCI_DEVICE_ID_LSI_SAS2108 ||
396 pci->device == PCI_DEVICE_ID_LSI_SAS2108E ||
397 pci->device == PCI_DEVICE_ID_LSI_SAS2004 ||
398 pci->device == PCI_DEVICE_ID_LSI_SAS2008 ||
399 pci->device == PCI_DEVICE_ID_LSI_VERDE_ZCR ||
400 pci->device == PCI_DEVICE_ID_DELL_PERC5 ||
401 pci->device == PCI_DEVICE_ID_LSI_SAS2208 ||
402 pci->device == PCI_DEVICE_ID_LSI_SAS3108)
403 run_thread(init_megasas, pci);
404 }
405 }
406