1 commit 82eb03cfd862a65363fa2826de0dbd5474cfe5e2
2 Author: Chip Coldwell <coldwell@redhat.com>
3 Date: Mon Feb 16 13:11:56 2009 +0100
5 cciss: PCI power management reset for kexec
7 The kexec kernel resets the CCISS hardware in three steps:
9 1. Use PCI power management states to reset the controller in the
12 2. Clear the MSI/MSI-X bits in PCI configuration space so that MSI
13 initialization in the kexec kernel doesn't fail.
15 3. Use the CCISS "No-op" message to determine when the controller
16 firmware has recovered from the PCI PM reset.
18 [akpm@linux-foundation.org: cleanups]
19 Signed-off-by: Mike Miller <mike.miller@hp.com>
20 Cc: Randy Dunlap <randy.dunlap@oracle.com>
21 Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
22 Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
24 diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
25 index 132f76b..fecf524 100644
26 --- a/drivers/block/cciss.c
27 +++ b/drivers/block/cciss.c
28 @@ -3257,6 +3257,205 @@ static void free_hba(int i)
32 +/* Send a message CDB to the firmware. */
33 +static __devinit int cciss_message(struct pci_dev *pdev, unsigned char opcode, unsigned char type)
36 + CommandListHeader_struct CommandHeader;
37 + RequestBlock_struct Request;
38 + ErrDescriptor_struct ErrorDescriptor;
40 + static const size_t cmd_sz = sizeof(Command) + sizeof(ErrorInfo_struct);
43 + uint32_t paddr32, tag;
44 + void __iomem *vaddr;
47 + vaddr = ioremap_nocache(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
51 + /* The Inbound Post Queue only accepts 32-bit physical addresses for the
52 + CCISS commands, so they must be allocated from the lower 4GiB of
54 + err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
60 + cmd = pci_alloc_consistent(pdev, cmd_sz, &paddr64);
66 + /* This must fit, because of the 32-bit consistent DMA mask. Also,
67 + although there's no guarantee, we assume that the address is at
68 + least 4-byte aligned (most likely, it's page-aligned). */
71 + cmd->CommandHeader.ReplyQueue = 0;
72 + cmd->CommandHeader.SGList = 0;
73 + cmd->CommandHeader.SGTotal = 0;
74 + cmd->CommandHeader.Tag.lower = paddr32;
75 + cmd->CommandHeader.Tag.upper = 0;
76 + memset(&cmd->CommandHeader.LUN.LunAddrBytes, 0, 8);
78 + cmd->Request.CDBLen = 16;
79 + cmd->Request.Type.Type = TYPE_MSG;
80 + cmd->Request.Type.Attribute = ATTR_HEADOFQUEUE;
81 + cmd->Request.Type.Direction = XFER_NONE;
82 + cmd->Request.Timeout = 0; /* Don't time out */
83 + cmd->Request.CDB[0] = opcode;
84 + cmd->Request.CDB[1] = type;
85 + memset(&cmd->Request.CDB[2], 0, 14); /* the rest of the CDB is reserved */
87 + cmd->ErrorDescriptor.Addr.lower = paddr32 + sizeof(Command);
88 + cmd->ErrorDescriptor.Addr.upper = 0;
89 + cmd->ErrorDescriptor.Len = sizeof(ErrorInfo_struct);
91 + writel(paddr32, vaddr + SA5_REQUEST_PORT_OFFSET);
93 + for (i = 0; i < 10; i++) {
94 + tag = readl(vaddr + SA5_REPLY_PORT_OFFSET);
95 + if ((tag & ~3) == paddr32)
97 + schedule_timeout_uninterruptible(HZ);
102 + /* we leak the DMA buffer here ... no choice since the controller could
103 + still complete the command. */
105 + printk(KERN_ERR "cciss: controller message %02x:%02x timed out\n",
110 + pci_free_consistent(pdev, cmd_sz, cmd, paddr64);
113 + printk(KERN_ERR "cciss: controller message %02x:%02x failed\n",
118 + printk(KERN_INFO "cciss: controller message %02x:%02x succeeded\n",
123 +#define cciss_soft_reset_controller(p) cciss_message(p, 1, 0)
124 +#define cciss_noop(p) cciss_message(p, 3, 0)
126 +static __devinit int cciss_reset_msi(struct pci_dev *pdev)
128 +/* the #defines are stolen from drivers/pci/msi.h. */
129 +#define msi_control_reg(base) (base + PCI_MSI_FLAGS)
130 +#define PCI_MSIX_FLAGS_ENABLE (1 << 15)
135 + pos = pci_find_capability(pdev, PCI_CAP_ID_MSI);
137 + pci_read_config_word(pdev, msi_control_reg(pos), &control);
138 + if (control & PCI_MSI_FLAGS_ENABLE) {
139 + printk(KERN_INFO "cciss: resetting MSI\n");
140 + pci_write_config_word(pdev, msi_control_reg(pos), control & ~PCI_MSI_FLAGS_ENABLE);
144 + pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX);
146 + pci_read_config_word(pdev, msi_control_reg(pos), &control);
147 + if (control & PCI_MSIX_FLAGS_ENABLE) {
148 + printk(KERN_INFO "cciss: resetting MSI-X\n");
149 + pci_write_config_word(pdev, msi_control_reg(pos), control & ~PCI_MSIX_FLAGS_ENABLE);
156 +/* This does a hard reset of the controller using PCI power management
158 +static __devinit int cciss_hard_reset_controller(struct pci_dev *pdev)
160 + u16 pmcsr, saved_config_space[32];
163 + printk(KERN_INFO "cciss: using PCI PM to reset controller\n");
165 + /* This is very nearly the same thing as
167 + pci_save_state(pci_dev);
168 + pci_set_power_state(pci_dev, PCI_D3hot);
169 + pci_set_power_state(pci_dev, PCI_D0);
170 + pci_restore_state(pci_dev);
172 + but we can't use these nice canned kernel routines on
173 + kexec, because they also check the MSI/MSI-X state in PCI
174 + configuration space and do the wrong thing when it is
175 + set/cleared. Also, the pci_save/restore_state functions
176 + violate the ordering requirements for restoring the
177 + configuration space from the CCISS document (see the
178 + comment below). So we roll our own .... */
180 + for (i = 0; i < 32; i++)
181 + pci_read_config_word(pdev, 2*i, &saved_config_space[i]);
183 + pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
185 + printk(KERN_ERR "cciss_reset_controller: PCI PM not supported\n");
189 + /* Quoting from the Open CISS Specification: "The Power
190 + * Management Control/Status Register (CSR) controls the power
191 + * state of the device. The normal operating state is D0,
192 + * CSR=00h. The software off state is D3, CSR=03h. To reset
193 + * the controller, place the interface device in D3 then to
194 + * D0, this causes a secondary PCI reset which will reset the
197 + /* enter the D3hot power management state */
198 + pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr);
199 + pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
200 + pmcsr |= PCI_D3hot;
201 + pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
203 + set_current_state(TASK_UNINTERRUPTIBLE);
204 + schedule_timeout(HZ >> 1);
206 + /* enter the D0 power management state */
207 + pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
209 + pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
211 + set_current_state(TASK_UNINTERRUPTIBLE);
212 + schedule_timeout(HZ >> 1);
214 + /* Restore the PCI configuration space. The Open CISS
215 + * Specification says, "Restore the PCI Configuration
216 + * Registers, offsets 00h through 60h. It is important to
217 + * restore the command register, 16-bits at offset 04h,
218 + * last. Do not restore the configuration status register,
219 + * 16-bits at offset 06h." Note that the offset is 2*i. */
220 + for (i = 0; i < 32; i++) {
221 + if (i == 2 || i == 3)
223 + pci_write_config_word(pdev, 2*i, saved_config_space[i]);
226 + pci_write_config_word(pdev, 4, saved_config_space[2]);
232 * This is it. Find all the controllers and register them. I really hate
233 * stealing all these major device numbers.
234 @@ -3270,6 +3469,24 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
238 + if (reset_devices) {
239 + /* Reset the controller with a PCI power-cycle */
240 + if (cciss_hard_reset_controller(pdev) || cciss_reset_msi(pdev))
243 + /* Some devices (notably the HP Smart Array 5i Controller)
244 + need a little pause here */
245 + schedule_timeout_uninterruptible(30*HZ);
247 + /* Now try to get the controller to respond to a no-op */
248 + for (i=0; i<12; i++) {
249 + if (cciss_noop(pdev) == 0)
252 + printk("cciss: no-op failed%s\n", (i < 11 ? "; re-trying" : ""));
256 i = alloc_cciss_hba();