These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / seabios / src / hw / megasas.c
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" // foreachpci
20 #include "pci_ids.h" // PCI_DEVICE_ID_XXX
21 #include "pci_regs.h" // PCI_VENDOR_ID
22 #include "stacks.h" // yield
23 #include "std/disk.h" // DISK_RET_SUCCESS
24 #include "string.h" // memset
25 #include "util.h" // timer_calc
26
27 #define MFI_DB 0x0 // Doorbell
28 #define MFI_OMSG0 0x18 // Outbound message 0
29 #define MFI_IDB 0x20 // Inbound doorbell
30 #define MFI_ODB 0x2c // Outbound doorbell
31 #define MFI_IQP 0x40 // Inbound queue port
32 #define MFI_OSP0 0xb0 // Outbound scratch pad0
33 #define MFI_IQPL 0xc0 // Inbound queue port (low bytes)
34 #define MFI_IQPH 0xc4 // Inbound queue port (high bytes)
35
36 #define MFI_STATE_MASK                0xf0000000
37 #define MFI_STATE_WAIT_HANDSHAKE      0x60000000
38 #define MFI_STATE_BOOT_MESSAGE_PENDING 0x90000000
39 #define MFI_STATE_READY               0xb0000000
40 #define MFI_STATE_OPERATIONAL         0xc0000000
41 #define MFI_STATE_FAULT               0xf0000000
42
43 /* MFI Commands */
44 typedef enum {
45     MFI_CMD_INIT = 0x00,
46     MFI_CMD_LD_READ,
47     MFI_CMD_LD_WRITE,
48     MFI_CMD_LD_SCSI_IO,
49     MFI_CMD_PD_SCSI_IO,
50     MFI_CMD_DCMD,
51     MFI_CMD_ABORT,
52     MFI_CMD_SMP,
53     MFI_CMD_STP
54 } mfi_cmd_t;
55
56 struct megasas_cmd_frame {
57     u8 cmd;             /*00h */
58     u8 sense_len;       /*01h */
59     u8 cmd_status;      /*02h */
60     u8 scsi_status;     /*03h */
61
62     u8 target_id;       /*04h */
63     u8 lun;             /*05h */
64     u8 cdb_len;         /*06h */
65     u8 sge_count;       /*07h */
66
67     u32 context;        /*08h */
68     u32 context_64;     /*0Ch */
69
70     u16 flags;          /*10h */
71     u16 timeout;        /*12h */
72     u32 data_xfer_len;   /*14h */
73
74     union {
75         struct {
76             u32 opcode;       /*18h */
77             u8 mbox[12];      /*1Ch */
78             u32 sgl_addr;     /*28h */
79             u32 sgl_len;      /*32h */
80             u32 pad;          /*34h */
81         } dcmd;
82         struct {
83             u32 sense_buf_lo; /*18h */
84             u32 sense_buf_hi; /*1Ch */
85             u8 cdb[16];       /*20h */
86             u32 sgl_addr;     /*30h */
87             u32 sgl_len;      /*34h */
88         } pthru;
89         struct {
90             u8 pad[22];       /*18h */
91         } gen;
92     };
93 } __attribute__ ((packed));
94
95 struct mfi_ld_list_s {
96     u32     count;
97     u32     reserved_0;
98     struct {
99         u8          target;
100         u8          lun;
101         u16         seq;
102         u8          state;
103         u8          reserved_1[3];
104         u64         size;
105     } lds[64];
106 } __attribute__ ((packed));
107
108 #define MEGASAS_POLL_TIMEOUT 60000 // 60 seconds polling timeout
109
110 struct megasas_lun_s {
111     struct drive_s drive;
112     struct megasas_cmd_frame *frame;
113     u32 iobase;
114     u16 pci_id;
115     u8 target;
116     u8 lun;
117 };
118
119 static int megasas_fire_cmd(u16 pci_id, u32 ioaddr,
120                             struct megasas_cmd_frame *frame)
121 {
122     u32 frame_addr = (u32)frame;
123     int frame_count = 1;
124     u8 cmd_state;
125
126     dprintf(2, "Frame 0x%x\n", frame_addr);
127     if (pci_id == PCI_DEVICE_ID_LSI_SAS2004 ||
128         pci_id == PCI_DEVICE_ID_LSI_SAS2008) {
129         outl(0, ioaddr + MFI_IQPH);
130         outl(frame_addr | frame_count << 1 | 1, ioaddr + MFI_IQPL);
131     } else if (pci_id == PCI_DEVICE_ID_DELL_PERC5 ||
132                pci_id == PCI_DEVICE_ID_LSI_SAS1064R ||
133                pci_id == PCI_DEVICE_ID_LSI_VERDE_ZCR) {
134         outl(frame_addr >> 3 | frame_count, ioaddr + MFI_IQP);
135     } else {
136         outl(frame_addr | frame_count << 1 | 1, ioaddr + MFI_IQP);
137     }
138
139     u32 end = timer_calc(MEGASAS_POLL_TIMEOUT);
140     do {
141         for (;;) {
142             cmd_state = GET_LOWFLAT(frame->cmd_status);
143             if (cmd_state != 0xff)
144                 break;
145             if (timer_check(end)) {
146                 warn_timeout();
147                 return -1;
148             }
149             yield();
150         }
151     } while (cmd_state == 0xff);
152
153     if (cmd_state == 0 || cmd_state == 0x2d)
154         return 0;
155     dprintf(1, "ERROR: Frame 0x%x, status 0x%x\n", frame_addr, cmd_state);
156     return -1;
157 }
158
159 int
160 megasas_process_op(struct disk_op_s *op)
161 {
162     if (!CONFIG_MEGASAS)
163         return DISK_RET_EBADTRACK;
164     u8 cdb[16];
165     int blocksize = scsi_fill_cmd(op, cdb, sizeof(cdb));
166     if (blocksize < 0)
167         return default_process_op(op);
168     struct megasas_lun_s *mlun_gf =
169         container_of(op->drive_gf, struct megasas_lun_s, drive);
170     struct megasas_cmd_frame *frame = GET_GLOBALFLAT(mlun_gf->frame);
171     u16 pci_id = GET_GLOBALFLAT(mlun_gf->pci_id);
172     int i;
173
174     memset_fl(frame, 0, sizeof(*frame));
175     SET_LOWFLAT(frame->cmd, MFI_CMD_LD_SCSI_IO);
176     SET_LOWFLAT(frame->cmd_status, 0xFF);
177     SET_LOWFLAT(frame->target_id, GET_GLOBALFLAT(mlun_gf->target));
178     SET_LOWFLAT(frame->lun, GET_GLOBALFLAT(mlun_gf->lun));
179     SET_LOWFLAT(frame->flags, 0x0001);
180     SET_LOWFLAT(frame->data_xfer_len, op->count * blocksize);
181     SET_LOWFLAT(frame->cdb_len, 16);
182
183     for (i = 0; i < 16; i++) {
184         SET_LOWFLAT(frame->pthru.cdb[i], cdb[i]);
185     }
186     dprintf(2, "pthru cmd 0x%x count %d bs %d\n",
187             cdb[0], op->count, blocksize);
188
189     if (op->count) {
190         SET_LOWFLAT(frame->pthru.sgl_addr, (u32)op->buf_fl);
191         SET_LOWFLAT(frame->pthru.sgl_len, op->count * blocksize);
192         SET_LOWFLAT(frame->sge_count, 1);
193     }
194     SET_LOWFLAT(frame->context, (u32)frame);
195
196     if (megasas_fire_cmd(pci_id, GET_GLOBALFLAT(mlun_gf->iobase), frame) == 0)
197         return DISK_RET_SUCCESS;
198
199     dprintf(2, "pthru cmd 0x%x failed\n", cdb[0]);
200     return DISK_RET_EBADTRACK;
201 }
202
203 static int
204 megasas_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun)
205 {
206     struct megasas_lun_s *mlun = malloc_fseg(sizeof(*mlun));
207     char *name;
208     int prio, ret = 0;
209
210     if (!mlun) {
211         warn_noalloc();
212         return -1;
213     }
214     memset(mlun, 0, sizeof(*mlun));
215     mlun->drive.type = DTYPE_MEGASAS;
216     mlun->drive.cntl_id = pci->bdf;
217     mlun->pci_id = pci->device;
218     mlun->target = target;
219     mlun->lun = lun;
220     mlun->iobase = iobase;
221     mlun->frame = memalign_low(256, sizeof(struct megasas_cmd_frame));
222     if (!mlun->frame) {
223         warn_noalloc();
224         free(mlun);
225         return -1;
226     }
227     name = znprintf(36, "MegaRAID SAS (PCI %02x:%02x.%x) LD %d:%d",
228                     pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf),
229                     pci_bdf_to_fn(pci->bdf), 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
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
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
360 init_megasas(struct pci_device *pci)
361 {
362     u16 bdf = pci->bdf;
363     u32 iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_2)
364         & PCI_BASE_ADDRESS_IO_MASK;
365
366     if (!iobase)
367         iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0)
368             & PCI_BASE_ADDRESS_IO_MASK;
369
370     dprintf(1, "found MegaRAID SAS at %02x:%02x.%x, io @ %x\n",
371             pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf),
372             pci_bdf_to_fn(bdf), iobase);
373
374     pci_config_maskw(pci->bdf, PCI_COMMAND, 0,
375                      PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
376     // reset
377     if (megasas_transition_to_ready(pci, iobase) == 0)
378         megasas_scan_target(pci, iobase);
379
380     return;
381 }
382
383 void
384 megasas_setup(void)
385 {
386     ASSERT32FLAT();
387     if (!CONFIG_MEGASAS)
388         return;
389
390     dprintf(3, "init megasas\n");
391
392     struct pci_device *pci;
393     foreachpci(pci) {
394         if (pci->vendor != PCI_VENDOR_ID_LSI_LOGIC &&
395             pci->vendor != PCI_VENDOR_ID_DELL)
396             continue;
397         if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R ||
398             pci->device == PCI_DEVICE_ID_LSI_SAS1078 ||
399             pci->device == PCI_DEVICE_ID_LSI_SAS1078DE ||
400             pci->device == PCI_DEVICE_ID_LSI_SAS2108 ||
401             pci->device == PCI_DEVICE_ID_LSI_SAS2108E ||
402             pci->device == PCI_DEVICE_ID_LSI_SAS2004 ||
403             pci->device == PCI_DEVICE_ID_LSI_SAS2008 ||
404             pci->device == PCI_DEVICE_ID_LSI_VERDE_ZCR ||
405             pci->device == PCI_DEVICE_ID_DELL_PERC5 ||
406             pci->device == PCI_DEVICE_ID_LSI_SAS2208 ||
407             pci->device == PCI_DEVICE_ID_LSI_SAS3108)
408             init_megasas(pci);
409     }
410 }