Setting tag linux-2.6-22-50
[linux-2.6.git] / linux-2.6-150-cciss-allow-kexec-to-work.patch
1 commit 82eb03cfd862a65363fa2826de0dbd5474cfe5e2
2 Author: Chip Coldwell <coldwell@redhat.com>
3 Date:   Mon Feb 16 13:11:56 2009 +0100
4
5     cciss: PCI power management reset for kexec
6     
7     The kexec kernel resets the CCISS hardware in three steps:
8     
9     1. Use PCI power management states to reset the controller in the
10        kexec kernel.
11     
12     2. Clear the MSI/MSI-X bits in PCI configuration space so that MSI
13        initialization in the kexec kernel doesn't fail.
14     
15     3. Use the CCISS "No-op" message to determine when the controller
16        firmware has recovered from the PCI PM reset.
17     
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>
23
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)
29         kfree(p);
30  }
31  
32 +/* Send a message CDB to the firmware. */
33 +static __devinit int cciss_message(struct pci_dev *pdev, unsigned char opcode, unsigned char type)
34 +{
35 +       typedef struct {
36 +               CommandListHeader_struct CommandHeader;
37 +               RequestBlock_struct Request;
38 +               ErrDescriptor_struct ErrorDescriptor;
39 +       } Command;
40 +       static const size_t cmd_sz = sizeof(Command) + sizeof(ErrorInfo_struct);
41 +       Command *cmd;
42 +       dma_addr_t paddr64;
43 +       uint32_t paddr32, tag;
44 +       void __iomem *vaddr;
45 +       int i, err;
46 +
47 +       vaddr = ioremap_nocache(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
48 +       if (vaddr == NULL)
49 +               return -ENOMEM;
50 +
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
53 +          memory. */
54 +       err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
55 +       if (err) {
56 +               iounmap(vaddr);
57 +               return -ENOMEM;
58 +       }
59 +
60 +       cmd = pci_alloc_consistent(pdev, cmd_sz, &paddr64);
61 +       if (cmd == NULL) {
62 +               iounmap(vaddr);
63 +               return -ENOMEM;
64 +       }
65 +
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). */
69 +       paddr32 = paddr64;
70 +
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);
77 +
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 */
86 +
87 +       cmd->ErrorDescriptor.Addr.lower = paddr32 + sizeof(Command);
88 +       cmd->ErrorDescriptor.Addr.upper = 0;
89 +       cmd->ErrorDescriptor.Len = sizeof(ErrorInfo_struct);
90 +
91 +       writel(paddr32, vaddr + SA5_REQUEST_PORT_OFFSET);
92 +
93 +       for (i = 0; i < 10; i++) {
94 +               tag = readl(vaddr + SA5_REPLY_PORT_OFFSET);
95 +               if ((tag & ~3) == paddr32)
96 +                       break;
97 +               schedule_timeout_uninterruptible(HZ);
98 +       }
99 +
100 +       iounmap(vaddr);
101 +
102 +       /* we leak the DMA buffer here ... no choice since the controller could
103 +          still complete the command. */
104 +       if (i == 10) {
105 +               printk(KERN_ERR "cciss: controller message %02x:%02x timed out\n",
106 +                       opcode, type);
107 +               return -ETIMEDOUT;
108 +       }
109 +
110 +       pci_free_consistent(pdev, cmd_sz, cmd, paddr64);
111 +
112 +       if (tag & 2) {
113 +               printk(KERN_ERR "cciss: controller message %02x:%02x failed\n",
114 +                       opcode, type);
115 +               return -EIO;
116 +       }
117 +
118 +       printk(KERN_INFO "cciss: controller message %02x:%02x succeeded\n",
119 +               opcode, type);
120 +       return 0;
121 +}
122 +
123 +#define cciss_soft_reset_controller(p) cciss_message(p, 1, 0)
124 +#define cciss_noop(p) cciss_message(p, 3, 0)
125 +
126 +static __devinit int cciss_reset_msi(struct pci_dev *pdev)
127 +{
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)
131 +
132 +       int pos;
133 +       u16 control = 0;
134 +
135 +       pos = pci_find_capability(pdev, PCI_CAP_ID_MSI);
136 +       if (pos) {
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);
141 +               }
142 +       }
143 +
144 +       pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX);
145 +       if (pos) {
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);
150 +               }
151 +       }
152 +
153 +       return 0;
154 +}
155 +
156 +/* This does a hard reset of the controller using PCI power management
157 + * states. */
158 +static __devinit int cciss_hard_reset_controller(struct pci_dev *pdev)
159 +{
160 +       u16 pmcsr, saved_config_space[32];
161 +       int i, pos;
162 +
163 +       printk(KERN_INFO "cciss: using PCI PM to reset controller\n");
164 +
165 +       /* This is very nearly the same thing as
166 +
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);
171 +
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 .... */
179 +
180 +       for (i = 0; i < 32; i++)
181 +               pci_read_config_word(pdev, 2*i, &saved_config_space[i]);
182 +
183 +       pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
184 +       if (pos == 0) {
185 +               printk(KERN_ERR "cciss_reset_controller: PCI PM not supported\n");
186 +               return -ENODEV;
187 +       }
188 +
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
195 +        * controller." */
196 +
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);
202 +
203 +       set_current_state(TASK_UNINTERRUPTIBLE);
204 +       schedule_timeout(HZ >> 1);
205 +
206 +       /* enter the D0 power management state */
207 +       pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
208 +       pmcsr |= PCI_D0;
209 +       pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
210 +
211 +       set_current_state(TASK_UNINTERRUPTIBLE);
212 +       schedule_timeout(HZ >> 1);
213 +
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)
222 +                       continue;
223 +               pci_write_config_word(pdev, 2*i, saved_config_space[i]);
224 +       }
225 +       wmb();
226 +       pci_write_config_word(pdev, 4, saved_config_space[2]);
227 +
228 +       return 0;
229 +}
230 +
231  /*
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,
235         int rc;
236         int dac;
237  
238 +       if (reset_devices) {
239 +               /* Reset the controller with a PCI power-cycle */
240 +               if (cciss_hard_reset_controller(pdev) || cciss_reset_msi(pdev))
241 +                       return -ENODEV;
242 +
243 +               /* Some devices (notably the HP Smart Array 5i Controller)
244 +                  need a little pause here */
245 +               schedule_timeout_uninterruptible(30*HZ);
246 +
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)
250 +                               break;
251 +                       else
252 +                               printk("cciss: no-op failed%s\n", (i < 11 ? "; re-trying" : ""));
253 +               }
254 +       }
255 +
256         i = alloc_cciss_hba();
257         if (i < 0)
258                 return -1;