diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/block/cciss.c | 200 | ||||
-rw-r--r-- | drivers/block/cciss_cmd.h | 4 |
2 files changed, 152 insertions, 52 deletions
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index f49dcd734d1..b3060eced55 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c | |||
@@ -216,6 +216,12 @@ static void cciss_device_release(struct device *dev); | |||
216 | static void cciss_free_gendisk(ctlr_info_t *h, int drv_index); | 216 | static void cciss_free_gendisk(ctlr_info_t *h, int drv_index); |
217 | static void cciss_free_drive_info(ctlr_info_t *h, int drv_index); | 217 | static void cciss_free_drive_info(ctlr_info_t *h, int drv_index); |
218 | static inline u32 next_command(ctlr_info_t *h); | 218 | static inline u32 next_command(ctlr_info_t *h); |
219 | static int __devinit cciss_find_cfg_addrs(struct pci_dev *pdev, | ||
220 | void __iomem *vaddr, u32 *cfg_base_addr, u64 *cfg_base_addr_index, | ||
221 | u64 *cfg_offset); | ||
222 | static int __devinit cciss_pci_find_memory_BAR(struct pci_dev *pdev, | ||
223 | unsigned long *memory_bar); | ||
224 | |||
219 | 225 | ||
220 | /* performant mode helper functions */ | 226 | /* performant mode helper functions */ |
221 | static void calc_bucket_map(int *bucket, int num_buckets, int nsgs, | 227 | static void calc_bucket_map(int *bucket, int num_buckets, int nsgs, |
@@ -4413,68 +4419,130 @@ static __devinit int cciss_reset_msi(struct pci_dev *pdev) | |||
4413 | return 0; | 4419 | return 0; |
4414 | } | 4420 | } |
4415 | 4421 | ||
4416 | /* This does a hard reset of the controller using PCI power management | 4422 | static int cciss_controller_hard_reset(struct pci_dev *pdev, |
4417 | * states. */ | 4423 | void * __iomem vaddr, bool use_doorbell) |
4418 | static __devinit int cciss_hard_reset_controller(struct pci_dev *pdev) | ||
4419 | { | 4424 | { |
4420 | u16 pmcsr, saved_config_space[32]; | 4425 | u16 pmcsr; |
4421 | int i, pos; | 4426 | int pos; |
4422 | 4427 | ||
4423 | printk(KERN_INFO "cciss: using PCI PM to reset controller\n"); | 4428 | if (use_doorbell) { |
4429 | /* For everything after the P600, the PCI power state method | ||
4430 | * of resetting the controller doesn't work, so we have this | ||
4431 | * other way using the doorbell register. | ||
4432 | */ | ||
4433 | dev_info(&pdev->dev, "using doorbell to reset controller\n"); | ||
4434 | writel(DOORBELL_CTLR_RESET, vaddr + SA5_DOORBELL); | ||
4435 | msleep(1000); | ||
4436 | } else { /* Try to do it the PCI power state way */ | ||
4437 | |||
4438 | /* Quoting from the Open CISS Specification: "The Power | ||
4439 | * Management Control/Status Register (CSR) controls the power | ||
4440 | * state of the device. The normal operating state is D0, | ||
4441 | * CSR=00h. The software off state is D3, CSR=03h. To reset | ||
4442 | * the controller, place the interface device in D3 then to D0, | ||
4443 | * this causes a secondary PCI reset which will reset the | ||
4444 | * controller." */ | ||
4445 | |||
4446 | pos = pci_find_capability(pdev, PCI_CAP_ID_PM); | ||
4447 | if (pos == 0) { | ||
4448 | dev_err(&pdev->dev, | ||
4449 | "cciss_controller_hard_reset: " | ||
4450 | "PCI PM not supported\n"); | ||
4451 | return -ENODEV; | ||
4452 | } | ||
4453 | dev_info(&pdev->dev, "using PCI PM to reset controller\n"); | ||
4454 | /* enter the D3hot power management state */ | ||
4455 | pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr); | ||
4456 | pmcsr &= ~PCI_PM_CTRL_STATE_MASK; | ||
4457 | pmcsr |= PCI_D3hot; | ||
4458 | pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr); | ||
4424 | 4459 | ||
4425 | /* This is very nearly the same thing as | 4460 | msleep(500); |
4426 | 4461 | ||
4427 | pci_save_state(pci_dev); | 4462 | /* enter the D0 power management state */ |
4428 | pci_set_power_state(pci_dev, PCI_D3hot); | 4463 | pmcsr &= ~PCI_PM_CTRL_STATE_MASK; |
4429 | pci_set_power_state(pci_dev, PCI_D0); | 4464 | pmcsr |= PCI_D0; |
4430 | pci_restore_state(pci_dev); | 4465 | pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr); |
4431 | 4466 | ||
4432 | but we can't use these nice canned kernel routines on | 4467 | msleep(500); |
4433 | kexec, because they also check the MSI/MSI-X state in PCI | 4468 | } |
4434 | configuration space and do the wrong thing when it is | 4469 | return 0; |
4435 | set/cleared. Also, the pci_save/restore_state functions | 4470 | } |
4436 | violate the ordering requirements for restoring the | 4471 | |
4437 | configuration space from the CCISS document (see the | 4472 | /* This does a hard reset of the controller using PCI power management |
4438 | comment below). So we roll our own .... */ | 4473 | * states or using the doorbell register. */ |
4474 | static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev) | ||
4475 | { | ||
4476 | u16 saved_config_space[32]; | ||
4477 | u64 cfg_offset; | ||
4478 | u32 cfg_base_addr; | ||
4479 | u64 cfg_base_addr_index; | ||
4480 | void __iomem *vaddr; | ||
4481 | unsigned long paddr; | ||
4482 | u32 misc_fw_support, active_transport; | ||
4483 | int rc, i; | ||
4484 | CfgTable_struct __iomem *cfgtable; | ||
4485 | bool use_doorbell; | ||
4486 | |||
4487 | /* For controllers as old a the p600, this is very nearly | ||
4488 | * the same thing as | ||
4489 | * | ||
4490 | * pci_save_state(pci_dev); | ||
4491 | * pci_set_power_state(pci_dev, PCI_D3hot); | ||
4492 | * pci_set_power_state(pci_dev, PCI_D0); | ||
4493 | * pci_restore_state(pci_dev); | ||
4494 | * | ||
4495 | * but we can't use these nice canned kernel routines on | ||
4496 | * kexec, because they also check the MSI/MSI-X state in PCI | ||
4497 | * configuration space and do the wrong thing when it is | ||
4498 | * set/cleared. Also, the pci_save/restore_state functions | ||
4499 | * violate the ordering requirements for restoring the | ||
4500 | * configuration space from the CCISS document (see the | ||
4501 | * comment below). So we roll our own .... | ||
4502 | * | ||
4503 | * For controllers newer than the P600, the pci power state | ||
4504 | * method of resetting doesn't work so we have another way | ||
4505 | * using the doorbell register. | ||
4506 | */ | ||
4439 | 4507 | ||
4440 | for (i = 0; i < 32; i++) | 4508 | for (i = 0; i < 32; i++) |
4441 | pci_read_config_word(pdev, 2*i, &saved_config_space[i]); | 4509 | pci_read_config_word(pdev, 2*i, &saved_config_space[i]); |
4442 | 4510 | ||
4443 | pos = pci_find_capability(pdev, PCI_CAP_ID_PM); | 4511 | /* find the first memory BAR, so we can find the cfg table */ |
4444 | if (pos == 0) { | 4512 | rc = cciss_pci_find_memory_BAR(pdev, &paddr); |
4445 | printk(KERN_ERR "cciss_reset_controller: PCI PM not supported\n"); | 4513 | if (rc) |
4446 | return -ENODEV; | 4514 | return rc; |
4447 | } | 4515 | vaddr = remap_pci_mem(paddr, 0x250); |
4448 | 4516 | if (!vaddr) | |
4449 | /* Quoting from the Open CISS Specification: "The Power | 4517 | return -ENOMEM; |
4450 | * Management Control/Status Register (CSR) controls the power | ||
4451 | * state of the device. The normal operating state is D0, | ||
4452 | * CSR=00h. The software off state is D3, CSR=03h. To reset | ||
4453 | * the controller, place the interface device in D3 then to | ||
4454 | * D0, this causes a secondary PCI reset which will reset the | ||
4455 | * controller." */ | ||
4456 | |||
4457 | /* enter the D3hot power management state */ | ||
4458 | pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr); | ||
4459 | pmcsr &= ~PCI_PM_CTRL_STATE_MASK; | ||
4460 | pmcsr |= PCI_D3hot; | ||
4461 | pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr); | ||
4462 | 4518 | ||
4463 | schedule_timeout_uninterruptible(HZ >> 1); | 4519 | /* find cfgtable in order to check if reset via doorbell is supported */ |
4520 | rc = cciss_find_cfg_addrs(pdev, vaddr, &cfg_base_addr, | ||
4521 | &cfg_base_addr_index, &cfg_offset); | ||
4522 | if (rc) | ||
4523 | goto unmap_vaddr; | ||
4524 | cfgtable = remap_pci_mem(pci_resource_start(pdev, | ||
4525 | cfg_base_addr_index) + cfg_offset, sizeof(*cfgtable)); | ||
4526 | if (!cfgtable) { | ||
4527 | rc = -ENOMEM; | ||
4528 | goto unmap_vaddr; | ||
4529 | } | ||
4464 | 4530 | ||
4465 | /* enter the D0 power management state */ | 4531 | /* If reset via doorbell register is supported, use that. */ |
4466 | pmcsr &= ~PCI_PM_CTRL_STATE_MASK; | 4532 | misc_fw_support = readl(&cfgtable->misc_fw_support); |
4467 | pmcsr |= PCI_D0; | 4533 | use_doorbell = misc_fw_support & MISC_FW_DOORBELL_RESET; |
4468 | pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr); | ||
4469 | 4534 | ||
4470 | schedule_timeout_uninterruptible(HZ >> 1); | 4535 | rc = cciss_controller_hard_reset(pdev, vaddr, use_doorbell); |
4536 | if (rc) | ||
4537 | goto unmap_cfgtable; | ||
4471 | 4538 | ||
4472 | /* Restore the PCI configuration space. The Open CISS | 4539 | /* Restore the PCI configuration space. The Open CISS |
4473 | * Specification says, "Restore the PCI Configuration | 4540 | * Specification says, "Restore the PCI Configuration |
4474 | * Registers, offsets 00h through 60h. It is important to | 4541 | * Registers, offsets 00h through 60h. It is important to |
4475 | * restore the command register, 16-bits at offset 04h, | 4542 | * restore the command register, 16-bits at offset 04h, |
4476 | * last. Do not restore the configuration status register, | 4543 | * last. Do not restore the configuration status register, |
4477 | * 16-bits at offset 06h." Note that the offset is 2*i. */ | 4544 | * 16-bits at offset 06h." Note that the offset is 2*i. |
4545 | */ | ||
4478 | for (i = 0; i < 32; i++) { | 4546 | for (i = 0; i < 32; i++) { |
4479 | if (i == 2 || i == 3) | 4547 | if (i == 2 || i == 3) |
4480 | continue; | 4548 | continue; |
@@ -4483,23 +4551,51 @@ static __devinit int cciss_hard_reset_controller(struct pci_dev *pdev) | |||
4483 | wmb(); | 4551 | wmb(); |
4484 | pci_write_config_word(pdev, 4, saved_config_space[2]); | 4552 | pci_write_config_word(pdev, 4, saved_config_space[2]); |
4485 | 4553 | ||
4486 | return 0; | 4554 | /* Some devices (notably the HP Smart Array 5i Controller) |
4555 | need a little pause here */ | ||
4556 | msleep(CCISS_POST_RESET_PAUSE_MSECS); | ||
4557 | |||
4558 | /* Controller should be in simple mode at this point. If it's not, | ||
4559 | * It means we're on one of those controllers which doesn't support | ||
4560 | * the doorbell reset method and on which the PCI power management reset | ||
4561 | * method doesn't work (P800, for example.) | ||
4562 | * In those cases, don't try to proceed, as it generally doesn't work. | ||
4563 | */ | ||
4564 | active_transport = readl(&cfgtable->TransportActive); | ||
4565 | if (active_transport & PERFORMANT_MODE) { | ||
4566 | dev_warn(&pdev->dev, "Unable to successfully reset controller," | ||
4567 | " Ignoring controller.\n"); | ||
4568 | rc = -ENODEV; | ||
4569 | } | ||
4570 | |||
4571 | unmap_cfgtable: | ||
4572 | iounmap(cfgtable); | ||
4573 | |||
4574 | unmap_vaddr: | ||
4575 | iounmap(vaddr); | ||
4576 | return rc; | ||
4487 | } | 4577 | } |
4488 | 4578 | ||
4489 | static __devinit int cciss_init_reset_devices(struct pci_dev *pdev) | 4579 | static __devinit int cciss_init_reset_devices(struct pci_dev *pdev) |
4490 | { | 4580 | { |
4491 | int i; | 4581 | int rc, i; |
4492 | 4582 | ||
4493 | if (!reset_devices) | 4583 | if (!reset_devices) |
4494 | return 0; | 4584 | return 0; |
4495 | 4585 | ||
4496 | /* Reset the controller with a PCI power-cycle */ | 4586 | /* Reset the controller with a PCI power-cycle or via doorbell */ |
4497 | if (cciss_hard_reset_controller(pdev) || cciss_reset_msi(pdev)) | 4587 | rc = cciss_kdump_hard_reset_controller(pdev); |
4498 | return -ENODEV; | ||
4499 | 4588 | ||
4500 | /* Some devices (notably the HP Smart Array 5i Controller) | 4589 | /* -ENOTSUPP here means we cannot reset the controller |
4501 | need a little pause here */ | 4590 | * but it's already (and still) up and running in |
4502 | msleep(CCISS_POST_RESET_PAUSE_MSECS); | 4591 | * "performant mode". |
4592 | */ | ||
4593 | if (rc == -ENOTSUPP) | ||
4594 | return 0; /* just try to do the kdump anyhow. */ | ||
4595 | if (rc) | ||
4596 | return -ENODEV; | ||
4597 | if (cciss_reset_msi(pdev)) | ||
4598 | return -ENODEV; | ||
4503 | 4599 | ||
4504 | /* Now try to get the controller to respond to a no-op */ | 4600 | /* Now try to get the controller to respond to a no-op */ |
4505 | for (i = 0; i < CCISS_POST_RESET_NOOP_RETRIES; i++) { | 4601 | for (i = 0; i < CCISS_POST_RESET_NOOP_RETRIES; i++) { |
diff --git a/drivers/block/cciss_cmd.h b/drivers/block/cciss_cmd.h index 936b9666da6..eb060f1b00b 100644 --- a/drivers/block/cciss_cmd.h +++ b/drivers/block/cciss_cmd.h | |||
@@ -52,6 +52,7 @@ | |||
52 | /* Configuration Table */ | 52 | /* Configuration Table */ |
53 | #define CFGTBL_ChangeReq 0x00000001l | 53 | #define CFGTBL_ChangeReq 0x00000001l |
54 | #define CFGTBL_AccCmds 0x00000001l | 54 | #define CFGTBL_AccCmds 0x00000001l |
55 | #define DOORBELL_CTLR_RESET 0x00000004l | ||
55 | 56 | ||
56 | #define CFGTBL_Trans_Simple 0x00000002l | 57 | #define CFGTBL_Trans_Simple 0x00000002l |
57 | #define CFGTBL_Trans_Performant 0x00000004l | 58 | #define CFGTBL_Trans_Performant 0x00000004l |
@@ -230,6 +231,9 @@ typedef struct _CfgTable_struct { | |||
230 | DWORD MaxPhysicalDrives; | 231 | DWORD MaxPhysicalDrives; |
231 | DWORD MaxPhysicalDrivesPerLogicalUnit; | 232 | DWORD MaxPhysicalDrivesPerLogicalUnit; |
232 | DWORD MaxPerformantModeCommands; | 233 | DWORD MaxPerformantModeCommands; |
234 | u8 reserved[0x78 - 0x58]; | ||
235 | u32 misc_fw_support; /* offset 0x78 */ | ||
236 | #define MISC_FW_DOORBELL_RESET (0x02) | ||
233 | } CfgTable_struct; | 237 | } CfgTable_struct; |
234 | 238 | ||
235 | struct TransTable_struct { | 239 | struct TransTable_struct { |