Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Disk Array driver for Compaq SMART2 Controllers | |
3 | * Copyright 1998 Compaq Computer Corporation | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | |
13 | * NON INFRINGEMENT. See the GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
18 | * | |
19 | * Questions/Comments/Bugfixes to iss_storagedev@hp.com | |
20 | * | |
21 | * If you want to make changes, improve or add functionality to this | |
22 | * driver, you'll probably need the Compaq Array Controller Interface | |
23 | * Specificiation (Document number ECG086/1198) | |
24 | */ | |
25 | ||
26 | /* | |
27 | * This file contains the controller communication implementation for | |
28 | * Compaq SMART-1 and SMART-2 controllers. To the best of my knowledge, | |
29 | * this should support: | |
30 | * | |
31 | * PCI: | |
32 | * SMART-2/P, SMART-2DH, SMART-2SL, SMART-221, SMART-3100ES, SMART-3200 | |
33 | * Integerated SMART Array Controller, SMART-4200, SMART-4250ES | |
34 | * | |
35 | * EISA: | |
36 | * SMART-2/E, SMART, IAES, IDA-2, IDA | |
37 | */ | |
38 | ||
39 | /* | |
40 | * Memory mapped FIFO interface (SMART 42xx cards) | |
41 | */ | |
42 | static void smart4_submit_command(ctlr_info_t *h, cmdlist_t *c) | |
43 | { | |
44 | writel(c->busaddr, h->vaddr + S42XX_REQUEST_PORT_OFFSET); | |
45 | } | |
46 | ||
47 | /* | |
48 | * This card is the opposite of the other cards. | |
49 | * 0 turns interrupts on... | |
50 | * 0x08 turns them off... | |
51 | */ | |
52 | static void smart4_intr_mask(ctlr_info_t *h, unsigned long val) | |
53 | { | |
54 | if (val) | |
55 | { /* Turn interrupts on */ | |
56 | writel(0, h->vaddr + S42XX_REPLY_INTR_MASK_OFFSET); | |
57 | } else /* Turn them off */ | |
58 | { | |
59 | writel( S42XX_INTR_OFF, | |
60 | h->vaddr + S42XX_REPLY_INTR_MASK_OFFSET); | |
61 | } | |
62 | } | |
63 | ||
64 | /* | |
65 | * For older cards FIFO Full = 0. | |
66 | * On this card 0 means there is room, anything else FIFO Full. | |
67 | * | |
68 | */ | |
69 | static unsigned long smart4_fifo_full(ctlr_info_t *h) | |
70 | { | |
71 | ||
72 | return (!readl(h->vaddr + S42XX_REQUEST_PORT_OFFSET)); | |
73 | } | |
74 | ||
75 | /* This type of controller returns -1 if the fifo is empty, | |
76 | * Not 0 like the others. | |
77 | * And we need to let it know we read a value out | |
78 | */ | |
79 | static unsigned long smart4_completed(ctlr_info_t *h) | |
80 | { | |
81 | long register_value | |
82 | = readl(h->vaddr + S42XX_REPLY_PORT_OFFSET); | |
83 | ||
84 | /* Fifo is empty */ | |
85 | if( register_value == 0xffffffff) | |
86 | return 0; | |
87 | ||
88 | /* Need to let it know we got the reply */ | |
89 | /* We do this by writing a 0 to the port we just read from */ | |
90 | writel(0, h->vaddr + S42XX_REPLY_PORT_OFFSET); | |
91 | ||
92 | return ((unsigned long) register_value); | |
93 | } | |
94 | ||
95 | /* | |
96 | * This hardware returns interrupt pending at a different place and | |
97 | * it does not tell us if the fifo is empty, we will have check | |
8e572bab | 98 | * that by getting a 0 back from the command_completed call. |
1da177e4 LT |
99 | */ |
100 | static unsigned long smart4_intr_pending(ctlr_info_t *h) | |
101 | { | |
102 | unsigned long register_value = | |
103 | readl(h->vaddr + S42XX_INTR_STATUS); | |
104 | ||
105 | if( register_value & S42XX_INTR_PENDING) | |
106 | return FIFO_NOT_EMPTY; | |
107 | return 0 ; | |
108 | } | |
109 | ||
110 | static struct access_method smart4_access = { | |
111 | smart4_submit_command, | |
112 | smart4_intr_mask, | |
113 | smart4_fifo_full, | |
114 | smart4_intr_pending, | |
115 | smart4_completed, | |
116 | }; | |
117 | ||
118 | /* | |
119 | * Memory mapped FIFO interface (PCI SMART2 and SMART 3xxx cards) | |
120 | */ | |
121 | static void smart2_submit_command(ctlr_info_t *h, cmdlist_t *c) | |
122 | { | |
123 | writel(c->busaddr, h->vaddr + COMMAND_FIFO); | |
124 | } | |
125 | ||
126 | static void smart2_intr_mask(ctlr_info_t *h, unsigned long val) | |
127 | { | |
128 | writel(val, h->vaddr + INTR_MASK); | |
129 | } | |
130 | ||
131 | static unsigned long smart2_fifo_full(ctlr_info_t *h) | |
132 | { | |
133 | return readl(h->vaddr + COMMAND_FIFO); | |
134 | } | |
135 | ||
136 | static unsigned long smart2_completed(ctlr_info_t *h) | |
137 | { | |
138 | return readl(h->vaddr + COMMAND_COMPLETE_FIFO); | |
139 | } | |
140 | ||
141 | static unsigned long smart2_intr_pending(ctlr_info_t *h) | |
142 | { | |
143 | return readl(h->vaddr + INTR_PENDING); | |
144 | } | |
145 | ||
146 | static struct access_method smart2_access = { | |
147 | smart2_submit_command, | |
148 | smart2_intr_mask, | |
149 | smart2_fifo_full, | |
150 | smart2_intr_pending, | |
151 | smart2_completed, | |
152 | }; | |
153 | ||
154 | /* | |
155 | * IO access for SMART-2/E cards | |
156 | */ | |
157 | static void smart2e_submit_command(ctlr_info_t *h, cmdlist_t *c) | |
158 | { | |
159 | outl(c->busaddr, h->io_mem_addr + COMMAND_FIFO); | |
160 | } | |
161 | ||
162 | static void smart2e_intr_mask(ctlr_info_t *h, unsigned long val) | |
163 | { | |
164 | outl(val, h->io_mem_addr + INTR_MASK); | |
165 | } | |
166 | ||
167 | static unsigned long smart2e_fifo_full(ctlr_info_t *h) | |
168 | { | |
169 | return inl(h->io_mem_addr + COMMAND_FIFO); | |
170 | } | |
171 | ||
172 | static unsigned long smart2e_completed(ctlr_info_t *h) | |
173 | { | |
174 | return inl(h->io_mem_addr + COMMAND_COMPLETE_FIFO); | |
175 | } | |
176 | ||
177 | static unsigned long smart2e_intr_pending(ctlr_info_t *h) | |
178 | { | |
179 | return inl(h->io_mem_addr + INTR_PENDING); | |
180 | } | |
181 | ||
182 | static struct access_method smart2e_access = { | |
183 | smart2e_submit_command, | |
184 | smart2e_intr_mask, | |
185 | smart2e_fifo_full, | |
186 | smart2e_intr_pending, | |
187 | smart2e_completed, | |
188 | }; | |
189 | ||
190 | /* | |
191 | * IO access for older SMART-1 type cards | |
192 | */ | |
193 | #define SMART1_SYSTEM_MASK 0xC8E | |
194 | #define SMART1_SYSTEM_DOORBELL 0xC8F | |
195 | #define SMART1_LOCAL_MASK 0xC8C | |
196 | #define SMART1_LOCAL_DOORBELL 0xC8D | |
197 | #define SMART1_INTR_MASK 0xC89 | |
198 | #define SMART1_LISTADDR 0xC90 | |
199 | #define SMART1_LISTLEN 0xC94 | |
200 | #define SMART1_TAG 0xC97 | |
201 | #define SMART1_COMPLETE_ADDR 0xC98 | |
202 | #define SMART1_LISTSTATUS 0xC9E | |
203 | ||
204 | #define CHANNEL_BUSY 0x01 | |
205 | #define CHANNEL_CLEAR 0x02 | |
206 | ||
207 | static void smart1_submit_command(ctlr_info_t *h, cmdlist_t *c) | |
208 | { | |
209 | /* | |
210 | * This __u16 is actually a bunch of control flags on SMART | |
211 | * and below. We want them all to be zero. | |
212 | */ | |
213 | c->hdr.size = 0; | |
214 | ||
215 | outb(CHANNEL_CLEAR, h->io_mem_addr + SMART1_SYSTEM_DOORBELL); | |
216 | ||
217 | outl(c->busaddr, h->io_mem_addr + SMART1_LISTADDR); | |
218 | outw(c->size, h->io_mem_addr + SMART1_LISTLEN); | |
219 | ||
220 | outb(CHANNEL_BUSY, h->io_mem_addr + SMART1_LOCAL_DOORBELL); | |
221 | } | |
222 | ||
223 | static void smart1_intr_mask(ctlr_info_t *h, unsigned long val) | |
224 | { | |
225 | if (val == 1) { | |
226 | outb(0xFD, h->io_mem_addr + SMART1_SYSTEM_DOORBELL); | |
227 | outb(CHANNEL_BUSY, h->io_mem_addr + SMART1_LOCAL_DOORBELL); | |
228 | outb(0x01, h->io_mem_addr + SMART1_INTR_MASK); | |
229 | outb(0x01, h->io_mem_addr + SMART1_SYSTEM_MASK); | |
230 | } else { | |
231 | outb(0, h->io_mem_addr + 0xC8E); | |
232 | } | |
233 | } | |
234 | ||
235 | static unsigned long smart1_fifo_full(ctlr_info_t *h) | |
236 | { | |
237 | unsigned char chan; | |
238 | chan = inb(h->io_mem_addr + SMART1_SYSTEM_DOORBELL) & CHANNEL_CLEAR; | |
239 | return chan; | |
240 | } | |
241 | ||
242 | static unsigned long smart1_completed(ctlr_info_t *h) | |
243 | { | |
244 | unsigned char status; | |
245 | unsigned long cmd; | |
246 | ||
247 | if (inb(h->io_mem_addr + SMART1_SYSTEM_DOORBELL) & CHANNEL_BUSY) { | |
248 | outb(CHANNEL_BUSY, h->io_mem_addr + SMART1_SYSTEM_DOORBELL); | |
249 | ||
250 | cmd = inl(h->io_mem_addr + SMART1_COMPLETE_ADDR); | |
251 | status = inb(h->io_mem_addr + SMART1_LISTSTATUS); | |
252 | ||
253 | outb(CHANNEL_CLEAR, h->io_mem_addr + SMART1_LOCAL_DOORBELL); | |
254 | ||
255 | /* | |
256 | * this is x86 (actually compaq x86) only, so it's ok | |
257 | */ | |
258 | if (cmd) ((cmdlist_t*)bus_to_virt(cmd))->req.hdr.rcode = status; | |
259 | } else { | |
260 | cmd = 0; | |
261 | } | |
262 | return cmd; | |
263 | } | |
264 | ||
265 | static unsigned long smart1_intr_pending(ctlr_info_t *h) | |
266 | { | |
267 | unsigned char chan; | |
268 | chan = inb(h->io_mem_addr + SMART1_SYSTEM_DOORBELL) & CHANNEL_BUSY; | |
269 | return chan; | |
270 | } | |
271 | ||
272 | static struct access_method smart1_access = { | |
273 | smart1_submit_command, | |
274 | smart1_intr_mask, | |
275 | smart1_fifo_full, | |
276 | smart1_intr_pending, | |
277 | smart1_completed, | |
278 | }; |