diff options
author | Stephen M. Cameron <scameron@beardog.cce.hp.com> | 2010-10-22 15:21:12 -0400 |
---|---|---|
committer | Jens Axboe <jaxboe@fusionio.com> | 2010-10-23 12:45:07 -0400 |
commit | f442e64b93e16dba6bf9ab7e8dc5a90f6bcd8a85 (patch) | |
tree | c8418e1e6c7291232bbb531817740a86e582920a /drivers/block | |
parent | afa842fa641e11a025725883b04d1e144e6bad39 (diff) |
cciss: Use kernel provided PCI state save and restore functions
and use the doorbell reset method if available (which doesn't
lock up the controller if you properly save and restore all
the PCI registers that you're supposed to.)
Signed-off-by: Stephen M. Cameron <scameron@beardog.cce.hp.com>
Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/cciss.c | 73 |
1 files changed, 15 insertions, 58 deletions
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index fd08644bf2a0..2e547bddc5a7 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c | |||
@@ -4361,36 +4361,6 @@ static __devinit int cciss_message(struct pci_dev *pdev, unsigned char opcode, u | |||
4361 | #define cciss_soft_reset_controller(p) cciss_message(p, 1, 0) | 4361 | #define cciss_soft_reset_controller(p) cciss_message(p, 1, 0) |
4362 | #define cciss_noop(p) cciss_message(p, 3, 0) | 4362 | #define cciss_noop(p) cciss_message(p, 3, 0) |
4363 | 4363 | ||
4364 | static __devinit int cciss_reset_msi(struct pci_dev *pdev) | ||
4365 | { | ||
4366 | /* the #defines are stolen from drivers/pci/msi.h. */ | ||
4367 | #define msi_control_reg(base) (base + PCI_MSI_FLAGS) | ||
4368 | #define PCI_MSIX_FLAGS_ENABLE (1 << 15) | ||
4369 | |||
4370 | int pos; | ||
4371 | u16 control = 0; | ||
4372 | |||
4373 | pos = pci_find_capability(pdev, PCI_CAP_ID_MSI); | ||
4374 | if (pos) { | ||
4375 | pci_read_config_word(pdev, msi_control_reg(pos), &control); | ||
4376 | if (control & PCI_MSI_FLAGS_ENABLE) { | ||
4377 | dev_info(&pdev->dev, "resetting MSI\n"); | ||
4378 | pci_write_config_word(pdev, msi_control_reg(pos), control & ~PCI_MSI_FLAGS_ENABLE); | ||
4379 | } | ||
4380 | } | ||
4381 | |||
4382 | pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX); | ||
4383 | if (pos) { | ||
4384 | pci_read_config_word(pdev, msi_control_reg(pos), &control); | ||
4385 | if (control & PCI_MSIX_FLAGS_ENABLE) { | ||
4386 | dev_info(&pdev->dev, "resetting MSI-X\n"); | ||
4387 | pci_write_config_word(pdev, msi_control_reg(pos), control & ~PCI_MSIX_FLAGS_ENABLE); | ||
4388 | } | ||
4389 | } | ||
4390 | |||
4391 | return 0; | ||
4392 | } | ||
4393 | |||
4394 | static int cciss_controller_hard_reset(struct pci_dev *pdev, | 4364 | static int cciss_controller_hard_reset(struct pci_dev *pdev, |
4395 | void * __iomem vaddr, bool use_doorbell) | 4365 | void * __iomem vaddr, bool use_doorbell) |
4396 | { | 4366 | { |
@@ -4445,17 +4415,17 @@ static int cciss_controller_hard_reset(struct pci_dev *pdev, | |||
4445 | * states or using the doorbell register. */ | 4415 | * states or using the doorbell register. */ |
4446 | static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev) | 4416 | static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev) |
4447 | { | 4417 | { |
4448 | u16 saved_config_space[32]; | ||
4449 | u64 cfg_offset; | 4418 | u64 cfg_offset; |
4450 | u32 cfg_base_addr; | 4419 | u32 cfg_base_addr; |
4451 | u64 cfg_base_addr_index; | 4420 | u64 cfg_base_addr_index; |
4452 | void __iomem *vaddr; | 4421 | void __iomem *vaddr; |
4453 | unsigned long paddr; | 4422 | unsigned long paddr; |
4454 | u32 misc_fw_support, active_transport; | 4423 | u32 misc_fw_support, active_transport; |
4455 | int rc, i; | 4424 | int rc; |
4456 | CfgTable_struct __iomem *cfgtable; | 4425 | CfgTable_struct __iomem *cfgtable; |
4457 | bool use_doorbell; | 4426 | bool use_doorbell; |
4458 | u32 board_id; | 4427 | u32 board_id; |
4428 | u16 command_register; | ||
4459 | 4429 | ||
4460 | /* For controllers as old a the p600, this is very nearly | 4430 | /* For controllers as old a the p600, this is very nearly |
4461 | * the same thing as | 4431 | * the same thing as |
@@ -4465,14 +4435,6 @@ static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev) | |||
4465 | * pci_set_power_state(pci_dev, PCI_D0); | 4435 | * pci_set_power_state(pci_dev, PCI_D0); |
4466 | * pci_restore_state(pci_dev); | 4436 | * pci_restore_state(pci_dev); |
4467 | * | 4437 | * |
4468 | * but we can't use these nice canned kernel routines on | ||
4469 | * kexec, because they also check the MSI/MSI-X state in PCI | ||
4470 | * configuration space and do the wrong thing when it is | ||
4471 | * set/cleared. Also, the pci_save/restore_state functions | ||
4472 | * violate the ordering requirements for restoring the | ||
4473 | * configuration space from the CCISS document (see the | ||
4474 | * comment below). So we roll our own .... | ||
4475 | * | ||
4476 | * For controllers newer than the P600, the pci power state | 4438 | * For controllers newer than the P600, the pci power state |
4477 | * method of resetting doesn't work so we have another way | 4439 | * method of resetting doesn't work so we have another way |
4478 | * using the doorbell register. | 4440 | * using the doorbell register. |
@@ -4491,8 +4453,13 @@ static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev) | |||
4491 | return -ENODEV; | 4453 | return -ENODEV; |
4492 | } | 4454 | } |
4493 | 4455 | ||
4494 | for (i = 0; i < 32; i++) | 4456 | /* Save the PCI command register */ |
4495 | pci_read_config_word(pdev, 2*i, &saved_config_space[i]); | 4457 | pci_read_config_word(pdev, 4, &command_register); |
4458 | /* Turn the board off. This is so that later pci_restore_state() | ||
4459 | * won't turn the board on before the rest of config space is ready. | ||
4460 | */ | ||
4461 | pci_disable_device(pdev); | ||
4462 | pci_save_state(pdev); | ||
4496 | 4463 | ||
4497 | /* find the first memory BAR, so we can find the cfg table */ | 4464 | /* find the first memory BAR, so we can find the cfg table */ |
4498 | rc = cciss_pci_find_memory_BAR(pdev, &paddr); | 4465 | rc = cciss_pci_find_memory_BAR(pdev, &paddr); |
@@ -4527,21 +4494,13 @@ static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev) | |||
4527 | rc = cciss_controller_hard_reset(pdev, vaddr, use_doorbell); | 4494 | rc = cciss_controller_hard_reset(pdev, vaddr, use_doorbell); |
4528 | if (rc) | 4495 | if (rc) |
4529 | goto unmap_cfgtable; | 4496 | goto unmap_cfgtable; |
4530 | 4497 | pci_restore_state(pdev); | |
4531 | /* Restore the PCI configuration space. The Open CISS | 4498 | rc = pci_enable_device(pdev); |
4532 | * Specification says, "Restore the PCI Configuration | 4499 | if (rc) { |
4533 | * Registers, offsets 00h through 60h. It is important to | 4500 | dev_warn(&pdev->dev, "failed to enable device.\n"); |
4534 | * restore the command register, 16-bits at offset 04h, | 4501 | goto unmap_cfgtable; |
4535 | * last. Do not restore the configuration status register, | ||
4536 | * 16-bits at offset 06h." Note that the offset is 2*i. | ||
4537 | */ | ||
4538 | for (i = 0; i < 32; i++) { | ||
4539 | if (i == 2 || i == 3) | ||
4540 | continue; | ||
4541 | pci_write_config_word(pdev, 2*i, saved_config_space[i]); | ||
4542 | } | 4502 | } |
4543 | wmb(); | 4503 | pci_write_config_word(pdev, 4, command_register); |
4544 | pci_write_config_word(pdev, 4, saved_config_space[2]); | ||
4545 | 4504 | ||
4546 | /* Some devices (notably the HP Smart Array 5i Controller) | 4505 | /* Some devices (notably the HP Smart Array 5i Controller) |
4547 | need a little pause here */ | 4506 | need a little pause here */ |
@@ -4601,8 +4560,6 @@ static __devinit int cciss_init_reset_devices(struct pci_dev *pdev) | |||
4601 | return 0; /* just try to do the kdump anyhow. */ | 4560 | return 0; /* just try to do the kdump anyhow. */ |
4602 | if (rc) | 4561 | if (rc) |
4603 | return -ENODEV; | 4562 | return -ENODEV; |
4604 | if (cciss_reset_msi(pdev)) | ||
4605 | return -ENODEV; | ||
4606 | 4563 | ||
4607 | /* Now try to get the controller to respond to a no-op */ | 4564 | /* Now try to get the controller to respond to a no-op */ |
4608 | for (i = 0; i < CCISS_POST_RESET_NOOP_RETRIES; i++) { | 4565 | for (i = 0; i < CCISS_POST_RESET_NOOP_RETRIES; i++) { |