1 | /* |
2 | * Product specific probe and attach routines for: |
3 | * 27/284X and aic7770 motherboard SCSI controllers |
4 | * |
5 | * Copyright (c) 1994-1998, 2000, 2001 Justin T. Gibbs. |
6 | * All rights reserved. |
7 | * |
8 | * Redistribution and use in source and binary forms, with or without |
9 | * modification, are permitted provided that the following conditions |
10 | * are met: |
11 | * 1. Redistributions of source code must retain the above copyright |
12 | * notice, this list of conditions, and the following disclaimer, |
13 | * without modification. |
14 | * 2. Redistributions in binary form must reproduce at minimum a disclaimer |
15 | * substantially similar to the "NO WARRANTY" disclaimer below |
16 | * ("Disclaimer") and any redistribution must be conditioned upon |
17 | * including a substantially similar Disclaimer requirement for further |
18 | * binary redistribution. |
19 | * 3. Neither the names of the above-listed copyright holders nor the names |
20 | * of any contributors may be used to endorse or promote products derived |
21 | * from this software without specific prior written permission. |
22 | * |
23 | * Alternatively, this software may be distributed under the terms of the |
24 | * GNU General Public License ("GPL") version 2 as published by the Free |
25 | * Software Foundation. |
26 | * |
27 | * NO WARRANTY |
28 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
29 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
30 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR |
31 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
32 | * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
33 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
34 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
35 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
36 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
37 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
38 | * POSSIBILITY OF SUCH DAMAGES. |
39 | * |
40 | * $Id: //depot/aic7xxx/aic7xxx/aic7770.c#32 $ |
41 | * |
42 | * $FreeBSD$ |
43 | */ |
44 | |
45 | #include "aic7xxx_osm.h" |
46 | #include "aic7xxx_inline.h" |
47 | #include "aic7xxx_93cx6.h" |
48 | |
49 | #define ID_AIC7770 0x04907770 |
50 | #define ID_AHA_274x 0x04907771 |
51 | #define ID_AHA_284xB 0x04907756 /* BIOS enabled */ |
52 | #define ID_AHA_284x 0x04907757 /* BIOS disabled*/ |
53 | #define ID_OLV_274x 0x04907782 /* Olivetti OEM */ |
54 | #define ID_OLV_274xD 0x04907783 /* Olivetti OEM (Differential) */ |
55 | |
56 | static int aic7770_chip_init(struct ahc_softc *ahc); |
57 | static int aha2840_load_seeprom(struct ahc_softc *ahc); |
58 | static ahc_device_setup_t ahc_aic7770_VL_setup; |
59 | static ahc_device_setup_t ahc_aic7770_EISA_setup; |
60 | static ahc_device_setup_t ahc_aic7770_setup; |
61 | |
62 | struct aic7770_identity aic7770_ident_table[] = |
63 | { |
64 | { |
65 | ID_AHA_274x, |
66 | 0xFFFFFFFF, |
67 | "Adaptec 274X SCSI adapter" , |
68 | ahc_aic7770_EISA_setup |
69 | }, |
70 | { |
71 | ID_AHA_284xB, |
72 | 0xFFFFFFFE, |
73 | "Adaptec 284X SCSI adapter" , |
74 | ahc_aic7770_VL_setup |
75 | }, |
76 | { |
77 | ID_AHA_284x, |
78 | 0xFFFFFFFE, |
79 | "Adaptec 284X SCSI adapter (BIOS Disabled)" , |
80 | ahc_aic7770_VL_setup |
81 | }, |
82 | { |
83 | ID_OLV_274x, |
84 | 0xFFFFFFFF, |
85 | "Adaptec (Olivetti OEM) 274X SCSI adapter" , |
86 | ahc_aic7770_EISA_setup |
87 | }, |
88 | { |
89 | ID_OLV_274xD, |
90 | 0xFFFFFFFF, |
91 | "Adaptec (Olivetti OEM) 274X Differential SCSI adapter" , |
92 | ahc_aic7770_EISA_setup |
93 | }, |
94 | /* Generic chip probes for devices we don't know 'exactly' */ |
95 | { |
96 | ID_AIC7770, |
97 | 0xFFFFFFFF, |
98 | "Adaptec aic7770 SCSI adapter" , |
99 | ahc_aic7770_EISA_setup |
100 | } |
101 | }; |
102 | const int ahc_num_aic7770_devs = ARRAY_SIZE(aic7770_ident_table); |
103 | |
104 | struct aic7770_identity * |
105 | aic7770_find_device(uint32_t id) |
106 | { |
107 | struct aic7770_identity *entry; |
108 | int i; |
109 | |
110 | for (i = 0; i < ahc_num_aic7770_devs; i++) { |
111 | entry = &aic7770_ident_table[i]; |
112 | if (entry->full_id == (id & entry->id_mask)) |
113 | return (entry); |
114 | } |
115 | return (NULL); |
116 | } |
117 | |
118 | int |
119 | aic7770_config(struct ahc_softc *ahc, struct aic7770_identity *entry, u_int io) |
120 | { |
121 | int error; |
122 | int have_seeprom; |
123 | u_int hostconf; |
124 | u_int irq; |
125 | u_int intdef; |
126 | |
127 | error = entry->setup(ahc); |
128 | have_seeprom = 0; |
129 | if (error != 0) |
130 | return (error); |
131 | |
132 | error = aic7770_map_registers(ahc, port: io); |
133 | if (error != 0) |
134 | return (error); |
135 | |
136 | /* |
137 | * Before we continue probing the card, ensure that |
138 | * its interrupts are *disabled*. We don't want |
139 | * a misstep to hang the machine in an interrupt |
140 | * storm. |
141 | */ |
142 | ahc_intr_enable(ahc, FALSE); |
143 | |
144 | ahc->description = entry->name; |
145 | error = ahc_softc_init(ahc); |
146 | if (error != 0) |
147 | return (error); |
148 | |
149 | ahc->bus_chip_init = aic7770_chip_init; |
150 | |
151 | error = ahc_reset(ahc, /*reinit*/FALSE); |
152 | if (error != 0) |
153 | return (error); |
154 | |
155 | /* Make sure we have a valid interrupt vector */ |
156 | intdef = ahc_inb(ahc, port: INTDEF); |
157 | irq = intdef & VECTOR; |
158 | switch (irq) { |
159 | case 9: |
160 | case 10: |
161 | case 11: |
162 | case 12: |
163 | case 14: |
164 | case 15: |
165 | break; |
166 | default: |
167 | printk("aic7770_config: invalid irq setting %d\n" , intdef); |
168 | return (ENXIO); |
169 | } |
170 | |
171 | if ((intdef & EDGE_TRIG) != 0) |
172 | ahc->flags |= AHC_EDGE_INTERRUPT; |
173 | |
174 | switch (ahc->chip & (AHC_EISA|AHC_VL)) { |
175 | case AHC_EISA: |
176 | { |
177 | u_int biosctrl; |
178 | u_int scsiconf; |
179 | u_int scsiconf1; |
180 | |
181 | biosctrl = ahc_inb(ahc, port: HA_274_BIOSCTRL); |
182 | scsiconf = ahc_inb(ahc, port: SCSICONF); |
183 | scsiconf1 = ahc_inb(ahc, port: SCSICONF + 1); |
184 | |
185 | /* Get the primary channel information */ |
186 | if ((biosctrl & CHANNEL_B_PRIMARY) != 0) |
187 | ahc->flags |= 1; |
188 | |
189 | if ((biosctrl & BIOSMODE) == BIOSDISABLED) { |
190 | ahc->flags |= AHC_USEDEFAULTS; |
191 | } else { |
192 | if ((ahc->features & AHC_WIDE) != 0) { |
193 | ahc->our_id = scsiconf1 & HWSCSIID; |
194 | if (scsiconf & TERM_ENB) |
195 | ahc->flags |= AHC_TERM_ENB_A; |
196 | } else { |
197 | ahc->our_id = scsiconf & HSCSIID; |
198 | ahc->our_id_b = scsiconf1 & HSCSIID; |
199 | if (scsiconf & TERM_ENB) |
200 | ahc->flags |= AHC_TERM_ENB_A; |
201 | if (scsiconf1 & TERM_ENB) |
202 | ahc->flags |= AHC_TERM_ENB_B; |
203 | } |
204 | } |
205 | if ((ahc_inb(ahc, HA_274_BIOSGLOBAL) & HA_274_EXTENDED_TRANS)) |
206 | ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B; |
207 | break; |
208 | } |
209 | case AHC_VL: |
210 | { |
211 | have_seeprom = aha2840_load_seeprom(ahc); |
212 | break; |
213 | } |
214 | default: |
215 | break; |
216 | } |
217 | if (have_seeprom == 0) { |
218 | kfree(objp: ahc->seep_config); |
219 | ahc->seep_config = NULL; |
220 | } |
221 | |
222 | /* |
223 | * Ensure autoflush is enabled |
224 | */ |
225 | ahc_outb(ahc, port: SBLKCTL, val: ahc_inb(ahc, port: SBLKCTL) & ~AUTOFLUSHDIS); |
226 | |
227 | /* Setup the FIFO threshold and the bus off time */ |
228 | hostconf = ahc_inb(ahc, port: HOSTCONF); |
229 | ahc_outb(ahc, port: BUSSPD, val: hostconf & DFTHRSH); |
230 | ahc_outb(ahc, port: BUSTIME, val: (hostconf << 2) & BOFF); |
231 | |
232 | ahc->bus_softc.aic7770_softc.busspd = hostconf & DFTHRSH; |
233 | ahc->bus_softc.aic7770_softc.bustime = (hostconf << 2) & BOFF; |
234 | |
235 | /* |
236 | * Generic aic7xxx initialization. |
237 | */ |
238 | error = ahc_init(ahc); |
239 | if (error != 0) |
240 | return (error); |
241 | |
242 | error = aic7770_map_int(ahc, irq); |
243 | if (error != 0) |
244 | return (error); |
245 | |
246 | ahc->init_level++; |
247 | |
248 | /* |
249 | * Enable the board's BUS drivers |
250 | */ |
251 | ahc_outb(ahc, port: BCTL, val: ENABLE); |
252 | return (0); |
253 | } |
254 | |
255 | static int |
256 | aic7770_chip_init(struct ahc_softc *ahc) |
257 | { |
258 | ahc_outb(ahc, port: BUSSPD, val: ahc->bus_softc.aic7770_softc.busspd); |
259 | ahc_outb(ahc, port: BUSTIME, val: ahc->bus_softc.aic7770_softc.bustime); |
260 | ahc_outb(ahc, port: SBLKCTL, val: ahc_inb(ahc, port: SBLKCTL) & ~AUTOFLUSHDIS); |
261 | ahc_outb(ahc, port: BCTL, val: ENABLE); |
262 | return (ahc_chip_init(ahc)); |
263 | } |
264 | |
265 | /* |
266 | * Read the 284x SEEPROM. |
267 | */ |
268 | static int |
269 | aha2840_load_seeprom(struct ahc_softc *ahc) |
270 | { |
271 | struct seeprom_descriptor sd; |
272 | struct seeprom_config *sc; |
273 | int have_seeprom; |
274 | uint8_t scsi_conf; |
275 | |
276 | sd.sd_ahc = ahc; |
277 | sd.sd_control_offset = SEECTL_2840; |
278 | sd.sd_status_offset = STATUS_2840; |
279 | sd.sd_dataout_offset = STATUS_2840; |
280 | sd.sd_chip = C46; |
281 | sd.sd_MS = 0; |
282 | sd.sd_RDY = EEPROM_TF; |
283 | sd.sd_CS = CS_2840; |
284 | sd.sd_CK = CK_2840; |
285 | sd.sd_DO = DO_2840; |
286 | sd.sd_DI = DI_2840; |
287 | sc = ahc->seep_config; |
288 | |
289 | if (bootverbose) |
290 | printk("%s: Reading SEEPROM..." , ahc_name(ahc)); |
291 | have_seeprom = ahc_read_seeprom(sd: &sd, buf: (uint16_t *)sc, |
292 | /*start_addr*/0, count: sizeof(*sc)/2); |
293 | |
294 | if (have_seeprom) { |
295 | |
296 | if (ahc_verify_cksum(sc) == 0) { |
297 | if(bootverbose) |
298 | printk ("checksum error\n" ); |
299 | have_seeprom = 0; |
300 | } else if (bootverbose) { |
301 | printk("done.\n" ); |
302 | } |
303 | } |
304 | |
305 | if (!have_seeprom) { |
306 | if (bootverbose) |
307 | printk("%s: No SEEPROM available\n" , ahc_name(ahc)); |
308 | ahc->flags |= AHC_USEDEFAULTS; |
309 | } else { |
310 | /* |
311 | * Put the data we've collected down into SRAM |
312 | * where ahc_init will find it. |
313 | */ |
314 | int i; |
315 | int max_targ; |
316 | uint16_t discenable; |
317 | |
318 | max_targ = (ahc->features & AHC_WIDE) != 0 ? 16 : 8; |
319 | discenable = 0; |
320 | for (i = 0; i < max_targ; i++){ |
321 | uint8_t target_settings; |
322 | |
323 | target_settings = (sc->device_flags[i] & CFXFER) << 4; |
324 | if (sc->device_flags[i] & CFSYNCH) |
325 | target_settings |= SOFS; |
326 | if (sc->device_flags[i] & CFWIDEB) |
327 | target_settings |= WIDEXFER; |
328 | if (sc->device_flags[i] & CFDISC) |
329 | discenable |= (0x01 << i); |
330 | ahc_outb(ahc, TARG_SCSIRATE + i, target_settings); |
331 | } |
332 | ahc_outb(ahc, DISC_DSB, ~(discenable & 0xff)); |
333 | ahc_outb(ahc, DISC_DSB + 1, ~((discenable >> 8) & 0xff)); |
334 | |
335 | ahc->our_id = sc->brtime_id & CFSCSIID; |
336 | |
337 | scsi_conf = (ahc->our_id & 0x7); |
338 | if (sc->adapter_control & CFSPARITY) |
339 | scsi_conf |= ENSPCHK; |
340 | if (sc->adapter_control & CFRESETB) |
341 | scsi_conf |= RESET_SCSI; |
342 | |
343 | if (sc->bios_control & CF284XEXTEND) |
344 | ahc->flags |= AHC_EXTENDED_TRANS_A; |
345 | /* Set SCSICONF info */ |
346 | ahc_outb(ahc, SCSICONF, scsi_conf); |
347 | |
348 | if (sc->adapter_control & CF284XSTERM) |
349 | ahc->flags |= AHC_TERM_ENB_A; |
350 | } |
351 | return (have_seeprom); |
352 | } |
353 | |
354 | static int |
355 | ahc_aic7770_VL_setup(struct ahc_softc *ahc) |
356 | { |
357 | int error; |
358 | |
359 | error = ahc_aic7770_setup(ahc); |
360 | ahc->chip |= AHC_VL; |
361 | return (error); |
362 | } |
363 | |
364 | static int |
365 | ahc_aic7770_EISA_setup(struct ahc_softc *ahc) |
366 | { |
367 | int error; |
368 | |
369 | error = ahc_aic7770_setup(ahc); |
370 | ahc->chip |= AHC_EISA; |
371 | return (error); |
372 | } |
373 | |
374 | static int |
375 | ahc_aic7770_setup(struct ahc_softc *ahc) |
376 | { |
377 | ahc->channel = 'A'; |
378 | ahc->channel_b = 'B'; |
379 | ahc->chip = AHC_AIC7770; |
380 | ahc->features = AHC_AIC7770_FE; |
381 | ahc->bugs |= AHC_TMODE_WIDEODD_BUG; |
382 | ahc->flags |= AHC_PAGESCBS; |
383 | ahc->instruction_ram_size = 448; |
384 | return (0); |
385 | } |
386 | |