diff options
Diffstat (limited to 'drivers/scsi/aic94xx/aic94xx_init.c')
-rw-r--r-- | drivers/scsi/aic94xx/aic94xx_init.c | 190 |
1 files changed, 187 insertions, 3 deletions
diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index b70d6e7f96e9..5d761eb67442 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/kernel.h> | 29 | #include <linux/kernel.h> |
30 | #include <linux/pci.h> | 30 | #include <linux/pci.h> |
31 | #include <linux/delay.h> | 31 | #include <linux/delay.h> |
32 | #include <linux/firmware.h> | ||
32 | 33 | ||
33 | #include <scsi/scsi_host.h> | 34 | #include <scsi/scsi_host.h> |
34 | 35 | ||
@@ -36,6 +37,7 @@ | |||
36 | #include "aic94xx_reg.h" | 37 | #include "aic94xx_reg.h" |
37 | #include "aic94xx_hwi.h" | 38 | #include "aic94xx_hwi.h" |
38 | #include "aic94xx_seq.h" | 39 | #include "aic94xx_seq.h" |
40 | #include "aic94xx_sds.h" | ||
39 | 41 | ||
40 | /* The format is "version.release.patchlevel" */ | 42 | /* The format is "version.release.patchlevel" */ |
41 | #define ASD_DRIVER_VERSION "1.0.3" | 43 | #define ASD_DRIVER_VERSION "1.0.3" |
@@ -134,7 +136,7 @@ Err: | |||
134 | return err; | 136 | return err; |
135 | } | 137 | } |
136 | 138 | ||
137 | static void __devexit asd_unmap_memio(struct asd_ha_struct *asd_ha) | 139 | static void asd_unmap_memio(struct asd_ha_struct *asd_ha) |
138 | { | 140 | { |
139 | struct asd_ha_addrspace *io_handle; | 141 | struct asd_ha_addrspace *io_handle; |
140 | 142 | ||
@@ -171,7 +173,7 @@ static int __devinit asd_map_ioport(struct asd_ha_struct *asd_ha) | |||
171 | return err; | 173 | return err; |
172 | } | 174 | } |
173 | 175 | ||
174 | static void __devexit asd_unmap_ioport(struct asd_ha_struct *asd_ha) | 176 | static void asd_unmap_ioport(struct asd_ha_struct *asd_ha) |
175 | { | 177 | { |
176 | pci_release_region(asd_ha->pcidev, PCI_IOBAR_OFFSET); | 178 | pci_release_region(asd_ha->pcidev, PCI_IOBAR_OFFSET); |
177 | } | 179 | } |
@@ -208,7 +210,7 @@ Err: | |||
208 | return err; | 210 | return err; |
209 | } | 211 | } |
210 | 212 | ||
211 | static void __devexit asd_unmap_ha(struct asd_ha_struct *asd_ha) | 213 | static void asd_unmap_ha(struct asd_ha_struct *asd_ha) |
212 | { | 214 | { |
213 | if (asd_ha->iospace) | 215 | if (asd_ha->iospace) |
214 | asd_unmap_ioport(asd_ha); | 216 | asd_unmap_ioport(asd_ha); |
@@ -313,6 +315,181 @@ static ssize_t asd_show_dev_pcba_sn(struct device *dev, | |||
313 | } | 315 | } |
314 | static DEVICE_ATTR(pcba_sn, S_IRUGO, asd_show_dev_pcba_sn, NULL); | 316 | static DEVICE_ATTR(pcba_sn, S_IRUGO, asd_show_dev_pcba_sn, NULL); |
315 | 317 | ||
318 | #define FLASH_CMD_NONE 0x00 | ||
319 | #define FLASH_CMD_UPDATE 0x01 | ||
320 | #define FLASH_CMD_VERIFY 0x02 | ||
321 | |||
322 | struct flash_command { | ||
323 | u8 command[8]; | ||
324 | int code; | ||
325 | }; | ||
326 | |||
327 | static struct flash_command flash_command_table[] = | ||
328 | { | ||
329 | {"verify", FLASH_CMD_VERIFY}, | ||
330 | {"update", FLASH_CMD_UPDATE}, | ||
331 | {"", FLASH_CMD_NONE} /* Last entry should be NULL. */ | ||
332 | }; | ||
333 | |||
334 | struct error_bios { | ||
335 | char *reason; | ||
336 | int err_code; | ||
337 | }; | ||
338 | |||
339 | static struct error_bios flash_error_table[] = | ||
340 | { | ||
341 | {"Failed to open bios image file", FAIL_OPEN_BIOS_FILE}, | ||
342 | {"PCI ID mismatch", FAIL_CHECK_PCI_ID}, | ||
343 | {"Checksum mismatch", FAIL_CHECK_SUM}, | ||
344 | {"Unknown Error", FAIL_UNKNOWN}, | ||
345 | {"Failed to verify.", FAIL_VERIFY}, | ||
346 | {"Failed to reset flash chip.", FAIL_RESET_FLASH}, | ||
347 | {"Failed to find flash chip type.", FAIL_FIND_FLASH_ID}, | ||
348 | {"Failed to erash flash chip.", FAIL_ERASE_FLASH}, | ||
349 | {"Failed to program flash chip.", FAIL_WRITE_FLASH}, | ||
350 | {"Flash in progress", FLASH_IN_PROGRESS}, | ||
351 | {"Image file size Error", FAIL_FILE_SIZE}, | ||
352 | {"Input parameter error", FAIL_PARAMETERS}, | ||
353 | {"Out of memory", FAIL_OUT_MEMORY}, | ||
354 | {"OK", 0} /* Last entry err_code = 0. */ | ||
355 | }; | ||
356 | |||
357 | static ssize_t asd_store_update_bios(struct device *dev, | ||
358 | struct device_attribute *attr, | ||
359 | const char *buf, size_t count) | ||
360 | { | ||
361 | struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev); | ||
362 | char *cmd_ptr, *filename_ptr; | ||
363 | struct bios_file_header header, *hdr_ptr; | ||
364 | int res, i; | ||
365 | u32 csum = 0; | ||
366 | int flash_command = FLASH_CMD_NONE; | ||
367 | int err = 0; | ||
368 | |||
369 | cmd_ptr = kzalloc(count*2, GFP_KERNEL); | ||
370 | |||
371 | if (!cmd_ptr) { | ||
372 | err = FAIL_OUT_MEMORY; | ||
373 | goto out; | ||
374 | } | ||
375 | |||
376 | filename_ptr = cmd_ptr + count; | ||
377 | res = sscanf(buf, "%s %s", cmd_ptr, filename_ptr); | ||
378 | if (res != 2) { | ||
379 | err = FAIL_PARAMETERS; | ||
380 | goto out1; | ||
381 | } | ||
382 | |||
383 | for (i = 0; flash_command_table[i].code != FLASH_CMD_NONE; i++) { | ||
384 | if (!memcmp(flash_command_table[i].command, | ||
385 | cmd_ptr, strlen(cmd_ptr))) { | ||
386 | flash_command = flash_command_table[i].code; | ||
387 | break; | ||
388 | } | ||
389 | } | ||
390 | if (flash_command == FLASH_CMD_NONE) { | ||
391 | err = FAIL_PARAMETERS; | ||
392 | goto out1; | ||
393 | } | ||
394 | |||
395 | if (asd_ha->bios_status == FLASH_IN_PROGRESS) { | ||
396 | err = FLASH_IN_PROGRESS; | ||
397 | goto out1; | ||
398 | } | ||
399 | err = request_firmware(&asd_ha->bios_image, | ||
400 | filename_ptr, | ||
401 | &asd_ha->pcidev->dev); | ||
402 | if (err) { | ||
403 | asd_printk("Failed to load bios image file %s, error %d\n", | ||
404 | filename_ptr, err); | ||
405 | err = FAIL_OPEN_BIOS_FILE; | ||
406 | goto out1; | ||
407 | } | ||
408 | |||
409 | hdr_ptr = (struct bios_file_header *)asd_ha->bios_image->data; | ||
410 | |||
411 | if ((hdr_ptr->contrl_id.vendor != asd_ha->pcidev->vendor || | ||
412 | hdr_ptr->contrl_id.device != asd_ha->pcidev->device) && | ||
413 | (hdr_ptr->contrl_id.sub_vendor != asd_ha->pcidev->vendor || | ||
414 | hdr_ptr->contrl_id.sub_device != asd_ha->pcidev->device)) { | ||
415 | |||
416 | ASD_DPRINTK("The PCI vendor or device id does not match\n"); | ||
417 | ASD_DPRINTK("vendor=%x dev=%x sub_vendor=%x sub_dev=%x" | ||
418 | " pci vendor=%x pci dev=%x\n", | ||
419 | hdr_ptr->contrl_id.vendor, | ||
420 | hdr_ptr->contrl_id.device, | ||
421 | hdr_ptr->contrl_id.sub_vendor, | ||
422 | hdr_ptr->contrl_id.sub_device, | ||
423 | asd_ha->pcidev->vendor, | ||
424 | asd_ha->pcidev->device); | ||
425 | err = FAIL_CHECK_PCI_ID; | ||
426 | goto out2; | ||
427 | } | ||
428 | |||
429 | if (hdr_ptr->filelen != asd_ha->bios_image->size) { | ||
430 | err = FAIL_FILE_SIZE; | ||
431 | goto out2; | ||
432 | } | ||
433 | |||
434 | /* calculate checksum */ | ||
435 | for (i = 0; i < hdr_ptr->filelen; i++) | ||
436 | csum += asd_ha->bios_image->data[i]; | ||
437 | |||
438 | if ((csum & 0x0000ffff) != hdr_ptr->checksum) { | ||
439 | ASD_DPRINTK("BIOS file checksum mismatch\n"); | ||
440 | err = FAIL_CHECK_SUM; | ||
441 | goto out2; | ||
442 | } | ||
443 | if (flash_command == FLASH_CMD_UPDATE) { | ||
444 | asd_ha->bios_status = FLASH_IN_PROGRESS; | ||
445 | err = asd_write_flash_seg(asd_ha, | ||
446 | &asd_ha->bios_image->data[sizeof(*hdr_ptr)], | ||
447 | 0, hdr_ptr->filelen-sizeof(*hdr_ptr)); | ||
448 | if (!err) | ||
449 | err = asd_verify_flash_seg(asd_ha, | ||
450 | &asd_ha->bios_image->data[sizeof(*hdr_ptr)], | ||
451 | 0, hdr_ptr->filelen-sizeof(*hdr_ptr)); | ||
452 | } else { | ||
453 | asd_ha->bios_status = FLASH_IN_PROGRESS; | ||
454 | err = asd_verify_flash_seg(asd_ha, | ||
455 | &asd_ha->bios_image->data[sizeof(header)], | ||
456 | 0, hdr_ptr->filelen-sizeof(header)); | ||
457 | } | ||
458 | |||
459 | out2: | ||
460 | release_firmware(asd_ha->bios_image); | ||
461 | out1: | ||
462 | kfree(cmd_ptr); | ||
463 | out: | ||
464 | asd_ha->bios_status = err; | ||
465 | |||
466 | if (!err) | ||
467 | return count; | ||
468 | else | ||
469 | return -err; | ||
470 | } | ||
471 | |||
472 | static ssize_t asd_show_update_bios(struct device *dev, | ||
473 | struct device_attribute *attr, char *buf) | ||
474 | { | ||
475 | int i; | ||
476 | struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev); | ||
477 | |||
478 | for (i = 0; flash_error_table[i].err_code != 0; i++) { | ||
479 | if (flash_error_table[i].err_code == asd_ha->bios_status) | ||
480 | break; | ||
481 | } | ||
482 | if (asd_ha->bios_status != FLASH_IN_PROGRESS) | ||
483 | asd_ha->bios_status = FLASH_OK; | ||
484 | |||
485 | return snprintf(buf, PAGE_SIZE, "status=%x %s\n", | ||
486 | flash_error_table[i].err_code, | ||
487 | flash_error_table[i].reason); | ||
488 | } | ||
489 | |||
490 | static DEVICE_ATTR(update_bios, S_IRUGO|S_IWUGO, | ||
491 | asd_show_update_bios, asd_store_update_bios); | ||
492 | |||
316 | static int asd_create_dev_attrs(struct asd_ha_struct *asd_ha) | 493 | static int asd_create_dev_attrs(struct asd_ha_struct *asd_ha) |
317 | { | 494 | { |
318 | int err; | 495 | int err; |
@@ -328,9 +505,14 @@ static int asd_create_dev_attrs(struct asd_ha_struct *asd_ha) | |||
328 | err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn); | 505 | err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn); |
329 | if (err) | 506 | if (err) |
330 | goto err_biosb; | 507 | goto err_biosb; |
508 | err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_update_bios); | ||
509 | if (err) | ||
510 | goto err_update_bios; | ||
331 | 511 | ||
332 | return 0; | 512 | return 0; |
333 | 513 | ||
514 | err_update_bios: | ||
515 | device_remove_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn); | ||
334 | err_biosb: | 516 | err_biosb: |
335 | device_remove_file(&asd_ha->pcidev->dev, &dev_attr_bios_build); | 517 | device_remove_file(&asd_ha->pcidev->dev, &dev_attr_bios_build); |
336 | err_rev: | 518 | err_rev: |
@@ -343,6 +525,7 @@ static void asd_remove_dev_attrs(struct asd_ha_struct *asd_ha) | |||
343 | device_remove_file(&asd_ha->pcidev->dev, &dev_attr_revision); | 525 | device_remove_file(&asd_ha->pcidev->dev, &dev_attr_revision); |
344 | device_remove_file(&asd_ha->pcidev->dev, &dev_attr_bios_build); | 526 | device_remove_file(&asd_ha->pcidev->dev, &dev_attr_bios_build); |
345 | device_remove_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn); | 527 | device_remove_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn); |
528 | device_remove_file(&asd_ha->pcidev->dev, &dev_attr_update_bios); | ||
346 | } | 529 | } |
347 | 530 | ||
348 | /* The first entry, 0, is used for dynamic ids, the rest for devices | 531 | /* The first entry, 0, is used for dynamic ids, the rest for devices |
@@ -589,6 +772,7 @@ static int __devinit asd_pci_probe(struct pci_dev *dev, | |||
589 | asd_ha->sas_ha.dev = &asd_ha->pcidev->dev; | 772 | asd_ha->sas_ha.dev = &asd_ha->pcidev->dev; |
590 | asd_ha->sas_ha.lldd_ha = asd_ha; | 773 | asd_ha->sas_ha.lldd_ha = asd_ha; |
591 | 774 | ||
775 | asd_ha->bios_status = FLASH_OK; | ||
592 | asd_ha->name = asd_dev->name; | 776 | asd_ha->name = asd_dev->name; |
593 | asd_printk("found %s, device %s\n", asd_ha->name, pci_name(dev)); | 777 | asd_printk("found %s, device %s\n", asd_ha->name, pci_name(dev)); |
594 | 778 | ||