Add qemu 2.4.0
[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_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
161 {
162     struct megasas_lun_s *mlun_gf =
163         container_of(op->drive_gf, struct megasas_lun_s, drive);
164     u8 *cdb = cdbcmd;
165     struct megasas_cmd_frame *frame = GET_GLOBALFLAT(mlun_gf->frame);
166     u16 pci_id = GET_GLOBALFLAT(mlun_gf->pci_id);
167     int i;
168
169     if (!CONFIG_MEGASAS)
170         return DISK_RET_EBADTRACK;
171
172     memset_fl(frame, 0, sizeof(*frame));
173     SET_LOWFLAT(frame->cmd, MFI_CMD_LD_SCSI_IO);
174     SET_LOWFLAT(frame->cmd_status, 0xFF);
175     SET_LOWFLAT(frame->target_id, GET_GLOBALFLAT(mlun_gf->target));
176     SET_LOWFLAT(frame->lun, GET_GLOBALFLAT(mlun_gf->lun));
177     SET_LOWFLAT(frame->flags, 0x0001);
178     SET_LOWFLAT(frame->data_xfer_len, op->count * blocksize);
179     SET_LOWFLAT(frame->cdb_len, 16);
180
181     for (i = 0; i < 16; i++) {
182         SET_LOWFLAT(frame->pthru.cdb[i], cdb[i]);
183     }
184     dprintf(2, "pthru cmd 0x%x count %d bs %d\n",
185             cdb[0], op->count, blocksize);
186
187     if (op->count) {
188         SET_LOWFLAT(frame->pthru.sgl_addr, (u32)op->buf_fl);
189         SET_LOWFLAT(frame->pthru.sgl_len, op->count * blocksize);
190         SET_LOWFLAT(frame->sge_count, 1);
191     }
192     SET_LOWFLAT(frame->context, (u32)frame);
193
194     if (megasas_fire_cmd(pci_id, GET_GLOBALFLAT(mlun_gf->iobase), frame) == 0)
195         return DISK_RET_SUCCESS;
196
197     dprintf(2, "pthru cmd 0x%x failed\n", cdb[0]);
198     return DISK_RET_EBADTRACK;
199 }
200
201 static int
202 megasas_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun)
203 {
204     struct megasas_lun_s *mlun = malloc_fseg(sizeof(*mlun));
205     char *name;
206     int prio, ret = 0;
207
208     if (!mlun) {
209         warn_noalloc();
210         return -1;
211     }
212     memset(mlun, 0, sizeof(*mlun));
213     mlun->drive.type = DTYPE_MEGASAS;
214     mlun->drive.cntl_id = pci->bdf;
215     mlun->pci_id = pci->device;
216     mlun->target = target;
217     mlun->lun = lun;
218     mlun->iobase = iobase;
219     mlun->frame = memalign_low(256, sizeof(struct megasas_cmd_frame));
220     if (!mlun->frame) {
221         warn_noalloc();
222         free(mlun);
223         return -1;
224     }
225     name = znprintf(36, "MegaRAID SAS (PCI %02x:%02x.%x) LD %d:%d",
226                     pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf),
227                     pci_bdf_to_fn(pci->bdf), target, lun);
228     prio = bootprio_find_scsi_device(pci, target, lun);
229     ret = scsi_drive_setup(&mlun->drive, name, prio);
230     free(name);
231     if (ret) {
232         free(mlun->frame);
233         free(mlun);
234         ret = -1;
235     }
236
237     return ret;
238 }
239
240 static void megasas_scan_target(struct pci_device *pci, u32 iobase)
241 {
242     struct mfi_ld_list_s ld_list;
243     struct megasas_cmd_frame *frame = memalign_tmp(256, sizeof(*frame));
244     int i;
245
246     memset(&ld_list, 0, sizeof(ld_list));
247     memset_fl(frame, 0, sizeof(*frame));
248
249     frame->cmd = MFI_CMD_DCMD;
250     frame->cmd_status = 0xFF;
251     frame->sge_count = 1;
252     frame->flags = 0x0011;
253     frame->data_xfer_len = sizeof(ld_list);
254     frame->dcmd.opcode = 0x03010000;
255     frame->dcmd.sgl_addr = (u32)MAKE_FLATPTR(GET_SEG(SS), &ld_list);
256     frame->dcmd.sgl_len = sizeof(ld_list);
257     frame->context = (u32)frame;
258
259     if (megasas_fire_cmd(pci->device, iobase, frame) == 0) {
260         dprintf(2, "%d LD found\n", ld_list.count);
261         for (i = 0; i < ld_list.count; i++) {
262             dprintf(2, "LD %d:%d state 0x%x\n",
263                     ld_list.lds[i].target, ld_list.lds[i].lun,
264                     ld_list.lds[i].state);
265             if (ld_list.lds[i].state != 0) {
266                 megasas_add_lun(pci, iobase,
267                                 ld_list.lds[i].target, ld_list.lds[i].lun);
268             }
269         }
270     }
271 }
272
273 static int megasas_transition_to_ready(struct pci_device *pci, u32 ioaddr)
274 {
275     u32 fw_state = 0, new_state, mfi_flags = 0;
276
277     if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R ||
278         pci->device == PCI_DEVICE_ID_DELL_PERC5)
279         new_state = inl(ioaddr + MFI_OMSG0) & MFI_STATE_MASK;
280     else
281         new_state = inl(ioaddr + MFI_OSP0) & MFI_STATE_MASK;
282
283     while (fw_state != new_state) {
284         switch (new_state) {
285         case MFI_STATE_FAULT:
286             dprintf(1, "ERROR: fw in fault state\n");
287             return -1;
288             break;
289         case MFI_STATE_WAIT_HANDSHAKE:
290             mfi_flags = 0x08;
291             /* fallthrough */
292         case MFI_STATE_BOOT_MESSAGE_PENDING:
293             mfi_flags |= 0x10;
294             if (pci->device == PCI_DEVICE_ID_LSI_SAS2004 ||
295                 pci->device == PCI_DEVICE_ID_LSI_SAS2008 ||
296                 pci->device == PCI_DEVICE_ID_LSI_SAS2208 ||
297                 pci->device == PCI_DEVICE_ID_LSI_SAS3108) {
298                 outl(ioaddr + MFI_DB, mfi_flags);
299             } else {
300                 outl(ioaddr + MFI_IDB, mfi_flags);
301             }
302             break;
303         case MFI_STATE_OPERATIONAL:
304             mfi_flags = 0x07;
305             if (pci->device == PCI_DEVICE_ID_LSI_SAS2004 ||
306                 pci->device == PCI_DEVICE_ID_LSI_SAS2008 ||
307                 pci->device == PCI_DEVICE_ID_LSI_SAS2208 ||
308                 pci->device == PCI_DEVICE_ID_LSI_SAS3108) {
309                 outl(ioaddr + MFI_DB, mfi_flags);
310                 if (pci->device == PCI_DEVICE_ID_LSI_SAS2208 ||
311                     pci->device == PCI_DEVICE_ID_LSI_SAS3108) {
312                     int j = 0;
313                     u32 doorbell;
314
315                     while (j < MEGASAS_POLL_TIMEOUT) {
316                         doorbell = inl(ioaddr + MFI_DB) & 1;
317                         if (!doorbell)
318                             break;
319                         msleep(20);
320                         j++;
321                     }
322                 }
323             } else {
324                 outw(ioaddr + MFI_IDB, mfi_flags);
325             }
326             break;
327         case MFI_STATE_READY:
328             dprintf(2, "MegaRAID SAS fw ready\n");
329             return 0;
330         }
331         // The current state should not last longer than poll timeout
332         u32 end = timer_calc(MEGASAS_POLL_TIMEOUT);
333         for (;;) {
334             if (timer_check(end)) {
335                 break;
336             }
337             yield();
338             fw_state = new_state;
339             if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R ||
340                 pci->device == PCI_DEVICE_ID_DELL_PERC5)
341                 new_state = inl(ioaddr + MFI_OMSG0) & MFI_STATE_MASK;
342             else
343                 new_state = inl(ioaddr + MFI_OSP0) & MFI_STATE_MASK;
344             if (new_state != fw_state) {
345                 break;
346             }
347         }
348     }
349     dprintf(1, "ERROR: fw in state %x\n", new_state & MFI_STATE_MASK);
350     return -1;
351 }
352
353 static void
354 init_megasas(struct pci_device *pci)
355 {
356     u16 bdf = pci->bdf;
357     u32 iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_2)
358         & PCI_BASE_ADDRESS_IO_MASK;
359
360     if (!iobase)
361         iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0)
362             & PCI_BASE_ADDRESS_IO_MASK;
363
364     dprintf(1, "found MegaRAID SAS at %02x:%02x.%x, io @ %x\n",
365             pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf),
366             pci_bdf_to_fn(bdf), iobase);
367
368     pci_config_maskw(pci->bdf, PCI_COMMAND, 0,
369                      PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
370     // reset
371     if (megasas_transition_to_ready(pci, iobase) == 0)
372         megasas_scan_target(pci, iobase);
373
374     return;
375 }
376
377 void
378 megasas_setup(void)
379 {
380     ASSERT32FLAT();
381     if (!CONFIG_MEGASAS)
382         return;
383
384     dprintf(3, "init megasas\n");
385
386     struct pci_device *pci;
387     foreachpci(pci) {
388         if (pci->vendor != PCI_VENDOR_ID_LSI_LOGIC &&
389             pci->vendor != PCI_VENDOR_ID_DELL)
390             continue;
391         if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R ||
392             pci->device == PCI_DEVICE_ID_LSI_SAS1078 ||
393             pci->device == PCI_DEVICE_ID_LSI_SAS1078DE ||
394             pci->device == PCI_DEVICE_ID_LSI_SAS2108 ||
395             pci->device == PCI_DEVICE_ID_LSI_SAS2108E ||
396             pci->device == PCI_DEVICE_ID_LSI_SAS2004 ||
397             pci->device == PCI_DEVICE_ID_LSI_SAS2008 ||
398             pci->device == PCI_DEVICE_ID_LSI_VERDE_ZCR ||
399             pci->device == PCI_DEVICE_ID_DELL_PERC5 ||
400             pci->device == PCI_DEVICE_ID_LSI_SAS2208 ||
401             pci->device == PCI_DEVICE_ID_LSI_SAS3108)
402             init_megasas(pci);
403     }
404 }