1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Adaptec AAC series RAID controller driver |
4 | * (c) Copyright 2001 Red Hat Inc. |
5 | * |
6 | * based on the old aacraid driver that is.. |
7 | * Adaptec aacraid device driver for Linux. |
8 | * |
9 | * Copyright (c) 2000-2010 Adaptec, Inc. |
10 | * 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com) |
11 | * 2016-2017 Microsemi Corp. (aacraid@microsemi.com) |
12 | * |
13 | * Module Name: |
14 | * sa.c |
15 | * |
16 | * Abstract: Drawbridge specific support functions |
17 | */ |
18 | |
19 | #include <linux/kernel.h> |
20 | #include <linux/init.h> |
21 | #include <linux/types.h> |
22 | #include <linux/pci.h> |
23 | #include <linux/spinlock.h> |
24 | #include <linux/blkdev.h> |
25 | #include <linux/delay.h> |
26 | #include <linux/completion.h> |
27 | #include <linux/time.h> |
28 | #include <linux/interrupt.h> |
29 | |
30 | #include <scsi/scsi_host.h> |
31 | |
32 | #include "aacraid.h" |
33 | |
34 | static irqreturn_t aac_sa_intr(int irq, void *dev_id) |
35 | { |
36 | struct aac_dev *dev = dev_id; |
37 | unsigned short intstat, mask; |
38 | |
39 | intstat = sa_readw(dev, DoorbellReg_p); |
40 | /* |
41 | * Read mask and invert because drawbridge is reversed. |
42 | * This allows us to only service interrupts that have been enabled. |
43 | */ |
44 | mask = ~(sa_readw(dev, SaDbCSR.PRISETIRQMASK)); |
45 | |
46 | /* Check to see if this is our interrupt. If it isn't just return */ |
47 | |
48 | if (intstat & mask) { |
49 | if (intstat & PrintfReady) { |
50 | aac_printf(dev, sa_readl(dev, Mailbox5)); |
51 | sa_writew(dev, DoorbellClrReg_p, PrintfReady); /* clear PrintfReady */ |
52 | sa_writew(dev, DoorbellReg_s, PrintfDone); |
53 | } else if (intstat & DOORBELL_1) { // dev -> Host Normal Command Ready |
54 | sa_writew(dev, DoorbellClrReg_p, DOORBELL_1); |
55 | aac_command_normal(q: &dev->queues->queue[HostNormCmdQueue]); |
56 | } else if (intstat & DOORBELL_2) { // dev -> Host Normal Response Ready |
57 | sa_writew(dev, DoorbellClrReg_p, DOORBELL_2); |
58 | aac_response_normal(q: &dev->queues->queue[HostNormRespQueue]); |
59 | } else if (intstat & DOORBELL_3) { // dev -> Host Normal Command Not Full |
60 | sa_writew(dev, DoorbellClrReg_p, DOORBELL_3); |
61 | } else if (intstat & DOORBELL_4) { // dev -> Host Normal Response Not Full |
62 | sa_writew(dev, DoorbellClrReg_p, DOORBELL_4); |
63 | } |
64 | return IRQ_HANDLED; |
65 | } |
66 | return IRQ_NONE; |
67 | } |
68 | |
69 | /** |
70 | * aac_sa_disable_interrupt - disable interrupt |
71 | * @dev: Which adapter to enable. |
72 | */ |
73 | |
74 | static void aac_sa_disable_interrupt (struct aac_dev *dev) |
75 | { |
76 | sa_writew(dev, SaDbCSR.PRISETIRQMASK, 0xffff); |
77 | } |
78 | |
79 | /** |
80 | * aac_sa_enable_interrupt - enable interrupt |
81 | * @dev: Which adapter to enable. |
82 | */ |
83 | |
84 | static void aac_sa_enable_interrupt (struct aac_dev *dev) |
85 | { |
86 | sa_writew(dev, SaDbCSR.PRICLEARIRQMASK, (PrintfReady | DOORBELL_1 | |
87 | DOORBELL_2 | DOORBELL_3 | DOORBELL_4)); |
88 | } |
89 | |
90 | /** |
91 | * aac_sa_notify_adapter - handle adapter notification |
92 | * @dev: Adapter that notification is for |
93 | * @event: Event to notidy |
94 | * |
95 | * Notify the adapter of an event |
96 | */ |
97 | |
98 | static void aac_sa_notify_adapter(struct aac_dev *dev, u32 event) |
99 | { |
100 | switch (event) { |
101 | |
102 | case AdapNormCmdQue: |
103 | sa_writew(dev, DoorbellReg_s,DOORBELL_1); |
104 | break; |
105 | case HostNormRespNotFull: |
106 | sa_writew(dev, DoorbellReg_s,DOORBELL_4); |
107 | break; |
108 | case AdapNormRespQue: |
109 | sa_writew(dev, DoorbellReg_s,DOORBELL_2); |
110 | break; |
111 | case HostNormCmdNotFull: |
112 | sa_writew(dev, DoorbellReg_s,DOORBELL_3); |
113 | break; |
114 | case HostShutdown: |
115 | /* |
116 | sa_sync_cmd(dev, HOST_CRASHING, 0, 0, 0, 0, 0, 0, |
117 | NULL, NULL, NULL, NULL, NULL); |
118 | */ |
119 | break; |
120 | case FastIo: |
121 | sa_writew(dev, DoorbellReg_s,DOORBELL_6); |
122 | break; |
123 | case AdapPrintfDone: |
124 | sa_writew(dev, DoorbellReg_s,DOORBELL_5); |
125 | break; |
126 | default: |
127 | BUG(); |
128 | break; |
129 | } |
130 | } |
131 | |
132 | |
133 | /** |
134 | * sa_sync_cmd - send a command and wait |
135 | * @dev: Adapter |
136 | * @command: Command to execute |
137 | * @p1: first parameter |
138 | * @p2: second parameter |
139 | * @p3: third parameter |
140 | * @p4: forth parameter |
141 | * @p5: fifth parameter |
142 | * @p6: sixth parameter |
143 | * @ret: adapter status |
144 | * @r1: first return value |
145 | * @r2: second return value |
146 | * @r3: third return value |
147 | * @r4: forth return value |
148 | * |
149 | * This routine will send a synchronous command to the adapter and wait |
150 | * for its completion. |
151 | */ |
152 | static int sa_sync_cmd(struct aac_dev *dev, u32 command, |
153 | u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6, |
154 | u32 *ret, u32 *r1, u32 *r2, u32 *r3, u32 *r4) |
155 | { |
156 | unsigned long start; |
157 | int ok; |
158 | /* |
159 | * Write the Command into Mailbox 0 |
160 | */ |
161 | sa_writel(dev, Mailbox0, command); |
162 | /* |
163 | * Write the parameters into Mailboxes 1 - 4 |
164 | */ |
165 | sa_writel(dev, Mailbox1, p1); |
166 | sa_writel(dev, Mailbox2, p2); |
167 | sa_writel(dev, Mailbox3, p3); |
168 | sa_writel(dev, Mailbox4, p4); |
169 | |
170 | /* |
171 | * Clear the synch command doorbell to start on a clean slate. |
172 | */ |
173 | sa_writew(dev, DoorbellClrReg_p, DOORBELL_0); |
174 | /* |
175 | * Signal that there is a new synch command |
176 | */ |
177 | sa_writew(dev, DoorbellReg_s, DOORBELL_0); |
178 | |
179 | ok = 0; |
180 | start = jiffies; |
181 | |
182 | while(time_before(jiffies, start+30*HZ)) |
183 | { |
184 | /* |
185 | * Delay 5uS so that the monitor gets access |
186 | */ |
187 | udelay(5); |
188 | /* |
189 | * Mon110 will set doorbell0 bit when it has |
190 | * completed the command. |
191 | */ |
192 | if(sa_readw(dev, DoorbellReg_p) & DOORBELL_0) { |
193 | ok = 1; |
194 | break; |
195 | } |
196 | msleep(msecs: 1); |
197 | } |
198 | |
199 | if (ok != 1) |
200 | return -ETIMEDOUT; |
201 | /* |
202 | * Clear the synch command doorbell. |
203 | */ |
204 | sa_writew(dev, DoorbellClrReg_p, DOORBELL_0); |
205 | /* |
206 | * Pull the synch status from Mailbox 0. |
207 | */ |
208 | if (ret) |
209 | *ret = sa_readl(dev, Mailbox0); |
210 | if (r1) |
211 | *r1 = sa_readl(dev, Mailbox1); |
212 | if (r2) |
213 | *r2 = sa_readl(dev, Mailbox2); |
214 | if (r3) |
215 | *r3 = sa_readl(dev, Mailbox3); |
216 | if (r4) |
217 | *r4 = sa_readl(dev, Mailbox4); |
218 | return 0; |
219 | } |
220 | |
221 | /** |
222 | * aac_sa_interrupt_adapter - interrupt an adapter |
223 | * @dev: Which adapter to enable. |
224 | * |
225 | * Breakpoint an adapter. |
226 | */ |
227 | |
228 | static void aac_sa_interrupt_adapter (struct aac_dev *dev) |
229 | { |
230 | sa_sync_cmd(dev, BREAKPOINT_REQUEST, p1: 0, p2: 0, p3: 0, p4: 0, p5: 0, p6: 0, |
231 | NULL, NULL, NULL, NULL, NULL); |
232 | } |
233 | |
234 | /** |
235 | * aac_sa_start_adapter - activate adapter |
236 | * @dev: Adapter |
237 | * |
238 | * Start up processing on an ARM based AAC adapter |
239 | */ |
240 | |
241 | static void aac_sa_start_adapter(struct aac_dev *dev) |
242 | { |
243 | union aac_init *init; |
244 | /* |
245 | * Fill in the remaining pieces of the init. |
246 | */ |
247 | init = dev->init; |
248 | init->r7.host_elapsed_seconds = cpu_to_le32(ktime_get_real_seconds()); |
249 | /* We can only use a 32 bit address here */ |
250 | sa_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS, |
251 | p1: (u32)(ulong)dev->init_pa, p2: 0, p3: 0, p4: 0, p5: 0, p6: 0, |
252 | NULL, NULL, NULL, NULL, NULL); |
253 | } |
254 | |
255 | static int aac_sa_restart_adapter(struct aac_dev *dev, int bled, u8 reset_type) |
256 | { |
257 | return -EINVAL; |
258 | } |
259 | |
260 | /** |
261 | * aac_sa_check_health |
262 | * @dev: device to check if healthy |
263 | * |
264 | * Will attempt to determine if the specified adapter is alive and |
265 | * capable of handling requests, returning 0 if alive. |
266 | */ |
267 | static int aac_sa_check_health(struct aac_dev *dev) |
268 | { |
269 | long status = sa_readl(dev, Mailbox7); |
270 | |
271 | /* |
272 | * Check to see if the board failed any self tests. |
273 | */ |
274 | if (status & SELF_TEST_FAILED) |
275 | return -1; |
276 | /* |
277 | * Check to see if the board panic'd while booting. |
278 | */ |
279 | if (status & KERNEL_PANIC) |
280 | return -2; |
281 | /* |
282 | * Wait for the adapter to be up and running. Wait up to 3 minutes |
283 | */ |
284 | if (!(status & KERNEL_UP_AND_RUNNING)) |
285 | return -3; |
286 | /* |
287 | * Everything is OK |
288 | */ |
289 | return 0; |
290 | } |
291 | |
292 | /** |
293 | * aac_sa_ioremap |
294 | * @dev: device to ioremap |
295 | * @size: mapping resize request |
296 | * |
297 | */ |
298 | static int aac_sa_ioremap(struct aac_dev * dev, u32 size) |
299 | { |
300 | if (!size) { |
301 | iounmap(addr: dev->regs.sa); |
302 | return 0; |
303 | } |
304 | dev->base = dev->regs.sa = ioremap(offset: dev->base_start, size); |
305 | return (dev->base == NULL) ? -1 : 0; |
306 | } |
307 | |
308 | /** |
309 | * aac_sa_init - initialize an ARM based AAC card |
310 | * @dev: device to configure |
311 | * |
312 | * Allocate and set up resources for the ARM based AAC variants. The |
313 | * device_interface in the commregion will be allocated and linked |
314 | * to the comm region. |
315 | */ |
316 | |
317 | int aac_sa_init(struct aac_dev *dev) |
318 | { |
319 | unsigned long start; |
320 | unsigned long status; |
321 | int instance; |
322 | const char *name; |
323 | |
324 | instance = dev->id; |
325 | name = dev->name; |
326 | |
327 | /* |
328 | * Fill in the function dispatch table. |
329 | */ |
330 | |
331 | dev->a_ops.adapter_interrupt = aac_sa_interrupt_adapter; |
332 | dev->a_ops.adapter_disable_int = aac_sa_disable_interrupt; |
333 | dev->a_ops.adapter_enable_int = aac_sa_enable_interrupt; |
334 | dev->a_ops.adapter_notify = aac_sa_notify_adapter; |
335 | dev->a_ops.adapter_sync_cmd = sa_sync_cmd; |
336 | dev->a_ops.adapter_check_health = aac_sa_check_health; |
337 | dev->a_ops.adapter_restart = aac_sa_restart_adapter; |
338 | dev->a_ops.adapter_start = aac_sa_start_adapter; |
339 | dev->a_ops.adapter_intr = aac_sa_intr; |
340 | dev->a_ops.adapter_deliver = aac_rx_deliver_producer; |
341 | dev->a_ops.adapter_ioremap = aac_sa_ioremap; |
342 | |
343 | if (aac_sa_ioremap(dev, size: dev->base_size)) { |
344 | printk(KERN_WARNING "%s: unable to map adapter.\n" , name); |
345 | goto error_iounmap; |
346 | } |
347 | |
348 | /* |
349 | * Check to see if the board failed any self tests. |
350 | */ |
351 | if (sa_readl(dev, Mailbox7) & SELF_TEST_FAILED) { |
352 | printk(KERN_WARNING "%s%d: adapter self-test failed.\n" , name, instance); |
353 | goto error_iounmap; |
354 | } |
355 | /* |
356 | * Check to see if the board panic'd while booting. |
357 | */ |
358 | if (sa_readl(dev, Mailbox7) & KERNEL_PANIC) { |
359 | printk(KERN_WARNING "%s%d: adapter kernel panic'd.\n" , name, instance); |
360 | goto error_iounmap; |
361 | } |
362 | start = jiffies; |
363 | /* |
364 | * Wait for the adapter to be up and running. Wait up to 3 minutes. |
365 | */ |
366 | while (!(sa_readl(dev, Mailbox7) & KERNEL_UP_AND_RUNNING)) { |
367 | if (time_after(jiffies, start+startup_timeout*HZ)) { |
368 | status = sa_readl(dev, Mailbox7); |
369 | printk(KERN_WARNING "%s%d: adapter kernel failed to start, init status = %lx.\n" , |
370 | name, instance, status); |
371 | goto error_iounmap; |
372 | } |
373 | msleep(msecs: 1); |
374 | } |
375 | |
376 | /* |
377 | * First clear out all interrupts. Then enable the one's that |
378 | * we can handle. |
379 | */ |
380 | aac_adapter_disable_int(dev); |
381 | aac_adapter_enable_int(dev); |
382 | |
383 | if(aac_init_adapter(dev) == NULL) |
384 | goto error_irq; |
385 | dev->sync_mode = 0; /* sync. mode not supported */ |
386 | if (request_irq(irq: dev->pdev->irq, handler: dev->a_ops.adapter_intr, |
387 | IRQF_SHARED, name: "aacraid" , dev: (void *)dev) < 0) { |
388 | printk(KERN_WARNING "%s%d: Interrupt unavailable.\n" , |
389 | name, instance); |
390 | goto error_iounmap; |
391 | } |
392 | dev->dbg_base = dev->base_start; |
393 | dev->dbg_base_mapped = dev->base; |
394 | dev->dbg_size = dev->base_size; |
395 | |
396 | aac_adapter_enable_int(dev); |
397 | |
398 | /* |
399 | * Tell the adapter that all is configure, and it can start |
400 | * accepting requests |
401 | */ |
402 | aac_sa_start_adapter(dev); |
403 | return 0; |
404 | |
405 | error_irq: |
406 | aac_sa_disable_interrupt(dev); |
407 | free_irq(dev->pdev->irq, (void *)dev); |
408 | |
409 | error_iounmap: |
410 | |
411 | return -1; |
412 | } |
413 | |
414 | |