edc_mca.c revision 1.1 1 1.1 jdolecek /* $NetBSD: edc_mca.c,v 1.1 2001/04/19 17:17:29 jdolecek Exp $ */
2 1.1 jdolecek
3 1.1 jdolecek /*
4 1.1 jdolecek * Copyright (c) 2001 The NetBSD Foundation, Inc.
5 1.1 jdolecek *
6 1.1 jdolecek * This code is derived from software contributed to The NetBSD Foundation
7 1.1 jdolecek * by Jaromir Dolecek.
8 1.1 jdolecek *
9 1.1 jdolecek * Redistribution and use in source and binary forms, with or without
10 1.1 jdolecek * modification, are permitted provided that the following conditions
11 1.1 jdolecek * are met:
12 1.1 jdolecek * 1. Redistributions of source code must retain the above copyright
13 1.1 jdolecek * notice, this list of conditions and the following disclaimer.
14 1.1 jdolecek * 2. Redistributions in binary form must reproduce the above copyright
15 1.1 jdolecek * notice, this list of conditions and the following disclaimer in the
16 1.1 jdolecek * documentation and/or other materials provided with the distribution.
17 1.1 jdolecek * 3. All advertising materials mentioning features or use of this software
18 1.1 jdolecek * must display the following acknowledgement:
19 1.1 jdolecek * This product includes software developed by the NetBSD
20 1.1 jdolecek * Foundation, Inc. and its contributors.
21 1.1 jdolecek * 4. The name of the author may not be used to endorse or promote products
22 1.1 jdolecek * derived from this software without specific prior written permission.
23 1.1 jdolecek *
24 1.1 jdolecek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25 1.1 jdolecek * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 1.1 jdolecek * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 1.1 jdolecek * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 1.1 jdolecek * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 1.1 jdolecek * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 1.1 jdolecek * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 1.1 jdolecek * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 1.1 jdolecek * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 1.1 jdolecek * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 1.1 jdolecek */
35 1.1 jdolecek
36 1.1 jdolecek /*
37 1.1 jdolecek * Driver for MCA ESDI controllers and disks conforming to IBM DASD
38 1.1 jdolecek * spec.
39 1.1 jdolecek *
40 1.1 jdolecek * The driver was written with DASD Storage Interface Specification
41 1.1 jdolecek * for MCA rev. 2.2 in hands, thanks to Scott Telford <st (at) epcc.ed.ac.uk>.
42 1.1 jdolecek *
43 1.1 jdolecek * TODO:
44 1.1 jdolecek * - move the MCA DMA controller (edc_setup_dma()) goo to device driver
45 1.1 jdolecek * independant location
46 1.1 jdolecek * - improve error recovery
47 1.1 jdolecek * add any soft resets when anything gets stuck?
48 1.1 jdolecek * - test with > 1 disk (this is supported by some controllers), eliminate
49 1.1 jdolecek * any remaining devno=0 assumptions if there are any still
50 1.1 jdolecek * - test with > 1 ESDI controller in machine; shared interrupts
51 1.1 jdolecek * necessary for this to work should be supported - edc_intr() specifically
52 1.1 jdolecek * checks if the interrupt is for this controller
53 1.1 jdolecek */
54 1.1 jdolecek
55 1.1 jdolecek #include "rnd.h"
56 1.1 jdolecek
57 1.1 jdolecek #include <sys/param.h>
58 1.1 jdolecek #include <sys/systm.h>
59 1.1 jdolecek #include <sys/errno.h>
60 1.1 jdolecek #include <sys/device.h>
61 1.1 jdolecek #include <sys/malloc.h>
62 1.1 jdolecek #include <sys/endian.h>
63 1.1 jdolecek #include <sys/disklabel.h>
64 1.1 jdolecek #include <sys/disk.h>
65 1.1 jdolecek #include <sys/syslog.h>
66 1.1 jdolecek #include <sys/proc.h>
67 1.1 jdolecek #include <sys/vnode.h>
68 1.1 jdolecek #include <sys/kernel.h>
69 1.1 jdolecek #if NRND > 0
70 1.1 jdolecek #include <sys/rnd.h>
71 1.1 jdolecek #endif
72 1.1 jdolecek
73 1.1 jdolecek #include <machine/bus.h>
74 1.1 jdolecek #include <machine/intr.h>
75 1.1 jdolecek
76 1.1 jdolecek #include <dev/mca/mcareg.h>
77 1.1 jdolecek #include <dev/mca/mcavar.h>
78 1.1 jdolecek #include <dev/mca/mcadevs.h>
79 1.1 jdolecek
80 1.1 jdolecek #include <dev/mca/edcreg.h>
81 1.1 jdolecek #include <dev/mca/edvar.h>
82 1.1 jdolecek #include <dev/mca/edcvar.h>
83 1.1 jdolecek
84 1.1 jdolecek #define DASD_MAXDEVS 7
85 1.1 jdolecek struct edc_mca_softc {
86 1.1 jdolecek struct device sc_dev;
87 1.1 jdolecek
88 1.1 jdolecek bus_space_tag_t sc_iot;
89 1.1 jdolecek bus_space_handle_t sc_ioh;
90 1.1 jdolecek
91 1.1 jdolecek bus_dma_tag_t sc_dmat; /* DMA tag as passed by parent */
92 1.1 jdolecek bus_space_handle_t sc_dmaextcmdh;
93 1.1 jdolecek bus_space_handle_t sc_dmaexech;
94 1.1 jdolecek
95 1.1 jdolecek void *sc_ih; /* interrupt handle */
96 1.1 jdolecek int sc_drq; /* DRQ number */
97 1.1 jdolecek int sc_cmd_async; /* asynchronous cmd pending */
98 1.1 jdolecek
99 1.1 jdolecek int sc_flags;
100 1.1 jdolecek #define DASD_QUIET 0x01 /* don't dump cmd error info */
101 1.1 jdolecek struct ed_softc *sc_ed[DASD_MAXDEVS];
102 1.1 jdolecek
103 1.1 jdolecek int sc_maxdevs; /* maximum # of devs supported by
104 1.1 jdolecek * controller */
105 1.1 jdolecek };
106 1.1 jdolecek
107 1.1 jdolecek int edc_mca_probe __P((struct device *, struct cfdata *, void *));
108 1.1 jdolecek void edc_mca_attach __P((struct device *, struct device *, void *));
109 1.1 jdolecek
110 1.1 jdolecek struct cfattach edc_mca_ca = {
111 1.1 jdolecek sizeof(struct edc_mca_softc), edc_mca_probe, edc_mca_attach
112 1.1 jdolecek };
113 1.1 jdolecek
114 1.1 jdolecek #define DMA_EXTCMD 0x18
115 1.1 jdolecek #define DMA_EXEC 0x1A
116 1.1 jdolecek
117 1.1 jdolecek static int edc_intr __P((void *));
118 1.1 jdolecek static void edc_attach_ed __P((struct device *));
119 1.1 jdolecek static void edc_dump_status_block __P((struct edc_mca_softc *, int, int,
120 1.1 jdolecek int));
121 1.1 jdolecek static int edc_setup_dma __P((struct edc_mca_softc *, struct buf *,
122 1.1 jdolecek bus_addr_t, bus_size_t));
123 1.1 jdolecek static int edc_do_attn __P((struct edc_mca_softc *, int, int, int));
124 1.1 jdolecek static int edc_cmd_wait __P((struct edc_mca_softc *, int, int, int));
125 1.1 jdolecek
126 1.1 jdolecek int
127 1.1 jdolecek edc_mca_probe(parent, match, aux)
128 1.1 jdolecek struct device *parent;
129 1.1 jdolecek struct cfdata *match;
130 1.1 jdolecek void *aux;
131 1.1 jdolecek {
132 1.1 jdolecek struct mca_attach_args *ma = aux;
133 1.1 jdolecek
134 1.1 jdolecek switch (ma->ma_id) {
135 1.1 jdolecek case MCA_PRODUCT_IBM_ESDIC:
136 1.1 jdolecek case MCA_PRODUCT_IBM_ESDIC_IG:
137 1.1 jdolecek return (1);
138 1.1 jdolecek default:
139 1.1 jdolecek return (0);
140 1.1 jdolecek }
141 1.1 jdolecek }
142 1.1 jdolecek
143 1.1 jdolecek void
144 1.1 jdolecek edc_mca_attach(parent, self, aux)
145 1.1 jdolecek struct device *parent, *self;
146 1.1 jdolecek void *aux;
147 1.1 jdolecek {
148 1.1 jdolecek struct edc_mca_softc *sc = (void *) self;
149 1.1 jdolecek struct mca_attach_args *ma = aux;
150 1.1 jdolecek int pos2, pos3, pos4;
151 1.1 jdolecek int irq, drq, iobase;
152 1.1 jdolecek const char *typestr;
153 1.1 jdolecek
154 1.1 jdolecek pos2 = mca_conf_read(ma->ma_mc, ma->ma_slot, 2);
155 1.1 jdolecek pos3 = mca_conf_read(ma->ma_mc, ma->ma_slot, 3);
156 1.1 jdolecek pos4 = mca_conf_read(ma->ma_mc, ma->ma_slot, 4);
157 1.1 jdolecek
158 1.1 jdolecek /*
159 1.1 jdolecek * POS register 2: (adf pos0)
160 1.1 jdolecek *
161 1.1 jdolecek * 7 6 5 4 3 2 1 0
162 1.1 jdolecek * \ \____/ \ \__ enable: 0=adapter disabled, 1=adapter enabled
163 1.1 jdolecek * \ \ \___ Primary/Alternate Port Adresses:
164 1.1 jdolecek * \ \ 0=0x3510-3517 1=0x3518-0x351f
165 1.1 jdolecek * \ \_____ DMA Arbitration Level: 0101=5 0110=6 0111=7
166 1.1 jdolecek * \ 0000=0 0001=1 0011=3 0100=4
167 1.1 jdolecek * \_________ Fairness On/Off: 1=On 0=Off
168 1.1 jdolecek *
169 1.1 jdolecek * POS register 3: (adf pos1)
170 1.1 jdolecek *
171 1.1 jdolecek * 7 6 5 4 3 2 1 0
172 1.1 jdolecek * 0 0 \_/
173 1.1 jdolecek * \__________ DMA Burst Pacing Interval: 10=24ms 11=31ms
174 1.1 jdolecek * 01=16ms 00=Burst Disabled
175 1.1 jdolecek *
176 1.1 jdolecek * POS register 4: (adf pos2)
177 1.1 jdolecek *
178 1.1 jdolecek * 7 6 5 4 3 2 1 0
179 1.1 jdolecek * \_/ \__ DMA Pacing Control: 1=Disabled 0=Enabled
180 1.1 jdolecek * \____ Time to Release: 1X=6ms 01=3ms 00=Immediate
181 1.1 jdolecek *
182 1.1 jdolecek * IRQ is fixed to 14 (0x0e).
183 1.1 jdolecek */
184 1.1 jdolecek
185 1.1 jdolecek switch (ma->ma_id) {
186 1.1 jdolecek case MCA_PRODUCT_IBM_ESDIC:
187 1.1 jdolecek typestr = "IBM ESDI Fixed Disk Controller";
188 1.1 jdolecek break;
189 1.1 jdolecek case MCA_PRODUCT_IBM_ESDIC_IG:
190 1.1 jdolecek typestr = "IBM Integ. ESDI Fixed Disk & Controller";
191 1.1 jdolecek break;
192 1.1 jdolecek default:
193 1.1 jdolecek /* never reached */
194 1.1 jdolecek }
195 1.1 jdolecek
196 1.1 jdolecek printf(" slot %d: %s\n", ma->ma_slot+1, typestr);
197 1.1 jdolecek
198 1.1 jdolecek irq = ESDIC_IRQ;
199 1.1 jdolecek iobase = (pos2 & IO_IS_ALT) ? ESDIC_IOALT : ESDIC_IOPRM;
200 1.1 jdolecek drq = (pos2 & DRQ_MASK) >> 2;
201 1.1 jdolecek
202 1.1 jdolecek #ifdef DIAGNOSTIC
203 1.1 jdolecek /*
204 1.1 jdolecek * It's not strictly necessary to check this, machine configuration
205 1.1 jdolecek * utility uses only valid adresses.
206 1.1 jdolecek */
207 1.1 jdolecek if (drq == 2 || drq >= 8) {
208 1.1 jdolecek printf("%s: invalid DMA Arbitration Level %d\n",
209 1.1 jdolecek sc->sc_dev.dv_xname, drq);
210 1.1 jdolecek return;
211 1.1 jdolecek }
212 1.1 jdolecek #endif
213 1.1 jdolecek
214 1.1 jdolecek printf("%s: Fairness %s, Release %s, DRQ %d, ",
215 1.1 jdolecek sc->sc_dev.dv_xname,
216 1.1 jdolecek (pos2 & FAIRNESS_ENABLE) ? "On" : "Off",
217 1.1 jdolecek (pos4 & RELEASE_1) ? "6ms"
218 1.1 jdolecek : ((pos4 & RELEASE_2) ? "3ms" : "Immediate"),
219 1.1 jdolecek drq);
220 1.1 jdolecek if ((pos4 & PACING_CTRL_DISABLE) == 0) {
221 1.1 jdolecek static const char * const pacint[] =
222 1.1 jdolecek { "disabled", "16ms", "24ms", "31ms"};
223 1.1 jdolecek printf("DMA burst pacing interval %s\n",
224 1.1 jdolecek pacint[(pos3 & PACING_INT_MASK) >> 4]);
225 1.1 jdolecek } else
226 1.1 jdolecek printf("DMA pacing control disabled\n");
227 1.1 jdolecek
228 1.1 jdolecek sc->sc_iot = ma->ma_iot;
229 1.1 jdolecek sc->sc_drq = drq;
230 1.1 jdolecek
231 1.1 jdolecek if (bus_space_map(sc->sc_iot, iobase,
232 1.1 jdolecek ESDIC_REG_NPORTS, 0, &sc->sc_ioh)) {
233 1.1 jdolecek printf("%s: couldn't map registers\n",
234 1.1 jdolecek sc->sc_dev.dv_xname);
235 1.1 jdolecek return;
236 1.1 jdolecek }
237 1.1 jdolecek
238 1.1 jdolecek if (bus_space_map(sc->sc_iot, DMA_EXTCMD, 1, 0, &sc->sc_dmaextcmdh)) {
239 1.1 jdolecek printf("%s: couldn't map registers\n",
240 1.1 jdolecek sc->sc_dev.dv_xname);
241 1.1 jdolecek return;
242 1.1 jdolecek }
243 1.1 jdolecek if (bus_space_map(sc->sc_iot, DMA_EXEC, 1, 0, &sc->sc_dmaexech)) {
244 1.1 jdolecek printf("%s: couldn't map registers\n",
245 1.1 jdolecek sc->sc_dev.dv_xname);
246 1.1 jdolecek return;
247 1.1 jdolecek }
248 1.1 jdolecek
249 1.1 jdolecek sc->sc_dmat = ma->ma_dmat;
250 1.1 jdolecek
251 1.1 jdolecek sc->sc_ih = mca_intr_establish(ma->ma_mc, irq, IPL_BIO, edc_intr, sc);
252 1.1 jdolecek if (sc->sc_ih == NULL) {
253 1.1 jdolecek printf("%s: couldn't establish interrupt handler\n",
254 1.1 jdolecek sc->sc_dev.dv_xname);
255 1.1 jdolecek return;
256 1.1 jdolecek }
257 1.1 jdolecek printf("%s: interrupting at irq %d\n", sc->sc_dev.dv_xname, irq);
258 1.1 jdolecek
259 1.1 jdolecek /*
260 1.1 jdolecek * Integrated ESDI controller supports only one disk, other
261 1.1 jdolecek * controllers support two disks.
262 1.1 jdolecek */
263 1.1 jdolecek if (ma->ma_id == MCA_PRODUCT_IBM_ESDIC_IG)
264 1.1 jdolecek sc->sc_maxdevs = 1;
265 1.1 jdolecek else
266 1.1 jdolecek sc->sc_maxdevs = 2;
267 1.1 jdolecek
268 1.1 jdolecek /* Defer probe for individual disks until interrupts are enabled. */
269 1.1 jdolecek config_interrupts(self, edc_attach_ed);
270 1.1 jdolecek }
271 1.1 jdolecek
272 1.1 jdolecek /*
273 1.1 jdolecek * Try to attach ed* at edc? if any configured and installed now
274 1.1 jdolecek * that interrupts are enabled.
275 1.1 jdolecek */
276 1.1 jdolecek static void
277 1.1 jdolecek edc_attach_ed(self)
278 1.1 jdolecek struct device *self;
279 1.1 jdolecek {
280 1.1 jdolecek struct edc_mca_softc *sc = (void *) self;
281 1.1 jdolecek struct ed_softc *ed;
282 1.1 jdolecek struct ed_attach_args eda;
283 1.1 jdolecek int devno;
284 1.1 jdolecek
285 1.1 jdolecek /* Do a reset to ensure sane state after warm boot. */
286 1.1 jdolecek if (bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) & BSR_BUSY) {
287 1.1 jdolecek /* hard reset */
288 1.1 jdolecek printf("%s: controller busy, performing hardware reset ...\n",
289 1.1 jdolecek sc->sc_dev.dv_xname);
290 1.1 jdolecek bus_space_write_1(sc->sc_iot, sc->sc_ioh, BCR,
291 1.1 jdolecek BCR_INT_ENABLE|BCR_RESET);
292 1.1 jdolecek } else {
293 1.1 jdolecek /* "SOFT" reset */
294 1.1 jdolecek edc_do_attn(sc, ATN_RESET_ATTACHMENT, DASD_DEVNO_CONTROLLER,0);
295 1.1 jdolecek }
296 1.1 jdolecek
297 1.1 jdolecek /* Wait until the reset completes. */
298 1.1 jdolecek while(bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) & BSR_BUSY)
299 1.1 jdolecek delay(1);
300 1.1 jdolecek
301 1.1 jdolecek /*
302 1.1 jdolecek * Get dummy ed_softc to be used during probe. Once a disk is
303 1.1 jdolecek * found, ed_mca_attach() calls edc_add_disk() to insert the
304 1.1 jdolecek * right pointer into sc->sc_ed[] array.
305 1.1 jdolecek */
306 1.1 jdolecek MALLOC(ed, struct ed_softc *, sizeof(struct ed_softc),
307 1.1 jdolecek M_TEMP, M_WAITOK);
308 1.1 jdolecek
309 1.1 jdolecek /* be quiet duting probes */
310 1.1 jdolecek sc->sc_flags |= DASD_QUIET;
311 1.1 jdolecek
312 1.1 jdolecek /* check for attached disks */
313 1.1 jdolecek for(devno=0; devno < sc->sc_maxdevs; devno++) {
314 1.1 jdolecek eda.sc_devno = devno;
315 1.1 jdolecek eda.sc_dmat = sc->sc_dmat;
316 1.1 jdolecek sc->sc_ed[devno] = ed;
317 1.1 jdolecek sc->sc_ed[devno] =
318 1.1 jdolecek (void *) config_found_sm(self, &eda, NULL, NULL);
319 1.1 jdolecek }
320 1.1 jdolecek
321 1.1 jdolecek /* enable full error dumps again */
322 1.1 jdolecek sc->sc_flags &= ~DASD_QUIET;
323 1.1 jdolecek
324 1.1 jdolecek /* cleanup */
325 1.1 jdolecek FREE(ed, M_TEMP);
326 1.1 jdolecek
327 1.1 jdolecek /* XXX disestablish interrupt if no disks found ? */
328 1.1 jdolecek }
329 1.1 jdolecek
330 1.1 jdolecek void
331 1.1 jdolecek edc_add_disk(sc, ed, devno)
332 1.1 jdolecek struct edc_mca_softc *sc;
333 1.1 jdolecek struct ed_softc *ed;
334 1.1 jdolecek int devno;
335 1.1 jdolecek {
336 1.1 jdolecek sc->sc_ed[devno] = ed;
337 1.1 jdolecek }
338 1.1 jdolecek
339 1.1 jdolecek static int
340 1.1 jdolecek edc_intr(arg)
341 1.1 jdolecek void *arg;
342 1.1 jdolecek {
343 1.1 jdolecek struct edc_mca_softc *sc = arg;
344 1.1 jdolecek u_int8_t isr, intr_id;
345 1.1 jdolecek u_int16_t sifr;
346 1.1 jdolecek int cmd, devno, bioerror;
347 1.1 jdolecek struct ed_softc *ed=NULL;
348 1.1 jdolecek
349 1.1 jdolecek /*
350 1.1 jdolecek * Check if the interrupt was for us.
351 1.1 jdolecek */
352 1.1 jdolecek if ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) & BSR_INTR) == 0)
353 1.1 jdolecek return (0);
354 1.1 jdolecek
355 1.1 jdolecek /*
356 1.1 jdolecek * Read ISR to find out interrupt type. This also clears the interrupt
357 1.1 jdolecek * condition and BSR_INTR flag. Accordings to docs interrupt ID of 0, 2
358 1.1 jdolecek * and 4 are reserved and not used.
359 1.1 jdolecek */
360 1.1 jdolecek isr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ISR);
361 1.1 jdolecek intr_id = isr & ISR_INTR_ID_MASK;
362 1.1 jdolecek
363 1.1 jdolecek #ifdef DEBUG
364 1.1 jdolecek if (intr_id == 0 || intr_id == 2 || intr_id == 4) {
365 1.1 jdolecek printf("%s: bogus interrupt id %d\n", sc->sc_dev.dv_xname,
366 1.1 jdolecek (int) intr_id);
367 1.1 jdolecek return (0);
368 1.1 jdolecek }
369 1.1 jdolecek #endif
370 1.1 jdolecek
371 1.1 jdolecek /* Get number of device whose intr this was */
372 1.1 jdolecek devno = (isr & 0xe0) >> 5;
373 1.1 jdolecek
374 1.1 jdolecek /*
375 1.1 jdolecek * Get Status block. Higher byte always says how long the status
376 1.1 jdolecek * block is, rest is device number and command code.
377 1.1 jdolecek * Check the status block length against our supported maximum length
378 1.1 jdolecek * and fetch the data.
379 1.1 jdolecek */
380 1.1 jdolecek if (bus_space_read_1(sc->sc_iot, sc->sc_ioh,BSR) & BSR_SIFR_FULL
381 1.1 jdolecek && intr_id != ISR_RESET_COMPLETED) {
382 1.1 jdolecek size_t len;
383 1.1 jdolecek int i;
384 1.1 jdolecek
385 1.1 jdolecek sifr = le16toh(bus_space_read_2(sc->sc_iot, sc->sc_ioh, SIFR));
386 1.1 jdolecek len = (sifr & 0xff00) >> 8;
387 1.1 jdolecek if (len > DASD_MAX_CMD_RES_LEN) {
388 1.1 jdolecek printf("%s: maximum Status Length exceeded: %d > %d\n",
389 1.1 jdolecek sc->sc_dev.dv_xname,
390 1.1 jdolecek len, DASD_MAX_CMD_RES_LEN);
391 1.1 jdolecek goto attn_eoi;
392 1.1 jdolecek }
393 1.1 jdolecek
394 1.1 jdolecek /* Get command code */
395 1.1 jdolecek cmd = sifr & SIFR_CMD_MASK;
396 1.1 jdolecek
397 1.1 jdolecek /* Read whole status block */
398 1.1 jdolecek ed = sc->sc_ed[devno];
399 1.1 jdolecek ed->sc_status_block[0] = sifr;
400 1.1 jdolecek for(i=1; i < len; i++) {
401 1.1 jdolecek while((bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR)
402 1.1 jdolecek & BSR_SIFR_FULL) == 0)
403 1.1 jdolecek delay(1);
404 1.1 jdolecek
405 1.1 jdolecek ed->sc_status_block[i] = le16toh(
406 1.1 jdolecek bus_space_read_2(sc->sc_iot, sc->sc_ioh, SIFR));
407 1.1 jdolecek }
408 1.1 jdolecek }
409 1.1 jdolecek
410 1.1 jdolecek switch (intr_id) {
411 1.1 jdolecek case ISR_DATA_TRANSFER_RDY:
412 1.1 jdolecek /*
413 1.1 jdolecek * Ready to do DMA, setup DMA controller and kick DASD
414 1.1 jdolecek * controller to do the transfer.
415 1.1 jdolecek */
416 1.1 jdolecek ed = sc->sc_ed[devno];
417 1.1 jdolecek if (!edc_setup_dma(sc, ed->sc_bp,
418 1.1 jdolecek ed->dmamap_xfer->dm_segs[0].ds_addr,
419 1.1 jdolecek ed->dmamap_xfer->dm_segs[0].ds_len)) {
420 1.1 jdolecek /* error XXX bail out? */
421 1.1 jdolecek bus_space_write_1(sc->sc_iot, sc->sc_ioh, BCR,
422 1.1 jdolecek BCR_INT_ENABLE);
423 1.1 jdolecek } else {
424 1.1 jdolecek /* OK, proceed with DMA */
425 1.1 jdolecek bus_space_write_1(sc->sc_iot, sc->sc_ioh, BCR,
426 1.1 jdolecek BCR_INT_ENABLE|BCR_DMA_ENABLE);
427 1.1 jdolecek }
428 1.1 jdolecek break;
429 1.1 jdolecek case ISR_COMPLETED:
430 1.1 jdolecek case ISR_COMPLETED_WITH_ECC:
431 1.1 jdolecek case ISR_COMPLETED_RETRIES:
432 1.1 jdolecek case ISR_COMPLETED_WARNING:
433 1.1 jdolecek bioerror = 0;
434 1.1 jdolecek break;
435 1.1 jdolecek case ISR_RESET_COMPLETED:
436 1.1 jdolecek case ISR_ABORT_COMPLETED:
437 1.1 jdolecek /* nothing to do */
438 1.1 jdolecek break;
439 1.1 jdolecek default:
440 1.1 jdolecek if ((sc->sc_flags & DASD_QUIET) == 0)
441 1.1 jdolecek edc_dump_status_block(sc, devno, cmd, intr_id);
442 1.1 jdolecek
443 1.1 jdolecek bioerror = EIO;
444 1.1 jdolecek break;
445 1.1 jdolecek }
446 1.1 jdolecek
447 1.1 jdolecek attn_eoi:
448 1.1 jdolecek /*
449 1.1 jdolecek * Unless the interrupt is for Data Transfer Ready or
450 1.1 jdolecek * Attention Error, finish by assertion EOI. This makes
451 1.1 jdolecek * attachment aware the interrupt is processed and system
452 1.1 jdolecek * is ready to accept another one.
453 1.1 jdolecek */
454 1.1 jdolecek if (intr_id != ISR_DATA_TRANSFER_RDY && intr_id != ISR_ATTN_ERROR)
455 1.1 jdolecek edc_do_attn(sc, ATN_END_INT, devno, intr_id);
456 1.1 jdolecek
457 1.1 jdolecek /* If Read or Write Data, wakeup worker thread to finish it */
458 1.1 jdolecek if (intr_id != ISR_DATA_TRANSFER_RDY
459 1.1 jdolecek && (cmd == CMD_READ_DATA || cmd == CMD_WRITE_DATA)) {
460 1.1 jdolecek sc->sc_ed[devno]->sc_error = bioerror;
461 1.1 jdolecek wakeup_one(&sc->sc_ed[devno]->edc_softc);
462 1.1 jdolecek }
463 1.1 jdolecek
464 1.1 jdolecek return (1);
465 1.1 jdolecek }
466 1.1 jdolecek
467 1.1 jdolecek /*
468 1.1 jdolecek * This follows the exact order for Attention Request as
469 1.1 jdolecek * written in DASD Storage Interface Specification MC (Rev 2.2).
470 1.1 jdolecek */
471 1.1 jdolecek static int
472 1.1 jdolecek edc_do_attn(sc, attn_type, devno, intr_id)
473 1.1 jdolecek struct edc_mca_softc *sc;
474 1.1 jdolecek int attn_type, devno, intr_id;
475 1.1 jdolecek {
476 1.1 jdolecek int tries;
477 1.1 jdolecek
478 1.1 jdolecek /* 1. Disable interrupts in BCR. */
479 1.1 jdolecek bus_space_write_1(sc->sc_iot, sc->sc_ioh, BCR, 0);
480 1.1 jdolecek
481 1.1 jdolecek /* 2. Assure NOT BUSY and NO INTERRUPT PENDING, unless acknowledging
482 1.1 jdolecek * a RESET COMPLETED interrupt. */
483 1.1 jdolecek if (intr_id != ISR_RESET_COMPLETED) {
484 1.1 jdolecek for(tries=0; tries < 1000; tries++) {
485 1.1 jdolecek if ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR)
486 1.1 jdolecek & (BSR_INT_PENDING|BSR_BUSY)) == 0)
487 1.1 jdolecek break;
488 1.1 jdolecek }
489 1.1 jdolecek
490 1.1 jdolecek if (tries == 1000) {
491 1.1 jdolecek printf("%s: edc_do_attn: timeout waiting for attachment to become available\n",
492 1.1 jdolecek (devno == DASD_DEVNO_CONTROLLER)
493 1.1 jdolecek ? sc->sc_dev.dv_xname
494 1.1 jdolecek : sc->sc_ed[devno]->sc_dev.dv_xname);
495 1.1 jdolecek return (EAGAIN);
496 1.1 jdolecek }
497 1.1 jdolecek }
498 1.1 jdolecek
499 1.1 jdolecek /*
500 1.1 jdolecek * 3. Write proper DEVICE NUMBER and Attention number to ATN.
501 1.1 jdolecek */
502 1.1 jdolecek bus_space_write_1(sc->sc_iot, sc->sc_ioh, ATN,
503 1.1 jdolecek attn_type | (devno << 5));
504 1.1 jdolecek
505 1.1 jdolecek /*
506 1.1 jdolecek * 4. Enable interrupts via BCR.
507 1.1 jdolecek */
508 1.1 jdolecek bus_space_write_1(sc->sc_iot, sc->sc_ioh, BCR, BCR_INT_ENABLE);
509 1.1 jdolecek
510 1.1 jdolecek return (0);
511 1.1 jdolecek }
512 1.1 jdolecek
513 1.1 jdolecek /*
514 1.1 jdolecek * Wait until command is processed, timeout after 'secs' seconds.
515 1.1 jdolecek * We use mono_time, since we don't need actual RTC, just time
516 1.1 jdolecek * interval.
517 1.1 jdolecek */
518 1.1 jdolecek static int
519 1.1 jdolecek edc_cmd_wait(sc, devno, cmd, secs)
520 1.1 jdolecek struct edc_mca_softc *sc;
521 1.1 jdolecek int devno, cmd, secs;
522 1.1 jdolecek {
523 1.1 jdolecek struct timeval start, now;
524 1.1 jdolecek int s;
525 1.1 jdolecek
526 1.1 jdolecek s = splclock();
527 1.1 jdolecek start = mono_time;
528 1.1 jdolecek splx(s);
529 1.1 jdolecek
530 1.1 jdolecek while((bus_space_read_1(sc->sc_iot,sc->sc_ioh,BSR)&BSR_CMD_INPROGRESS)){
531 1.1 jdolecek s = splclock();
532 1.1 jdolecek now = mono_time;
533 1.1 jdolecek splx(s);
534 1.1 jdolecek if (now.tv_sec - start.tv_sec > secs)
535 1.1 jdolecek break;
536 1.1 jdolecek
537 1.1 jdolecek delay(1);
538 1.1 jdolecek }
539 1.1 jdolecek
540 1.1 jdolecek if (now.tv_sec - start.tv_sec >= secs &&
541 1.1 jdolecek bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) & BSR_CMD_INPROGRESS){
542 1.1 jdolecek printf("%s: timed out waiting for previous cmd to finish, command %d not executed\n",
543 1.1 jdolecek sc->sc_ed[devno]->sc_dev.dv_xname, cmd);
544 1.1 jdolecek return (EAGAIN);
545 1.1 jdolecek }
546 1.1 jdolecek
547 1.1 jdolecek return (0);
548 1.1 jdolecek }
549 1.1 jdolecek
550 1.1 jdolecek int
551 1.1 jdolecek edc_run_cmd(sc, cmd, devno, cmd_args, cmd_len, async)
552 1.1 jdolecek struct edc_mca_softc *sc;
553 1.1 jdolecek int cmd;
554 1.1 jdolecek int devno;
555 1.1 jdolecek u_int16_t cmd_args[];
556 1.1 jdolecek int cmd_len;
557 1.1 jdolecek int async;
558 1.1 jdolecek {
559 1.1 jdolecek int i, error;
560 1.1 jdolecek u_int16_t cmd0;
561 1.1 jdolecek
562 1.1 jdolecek /*
563 1.1 jdolecek * If there has been an asynchronous command executed, first wait for it
564 1.1 jdolecek * to finish.
565 1.1 jdolecek */
566 1.1 jdolecek if (sc->sc_cmd_async) {
567 1.1 jdolecek /* Wait maximum 15s */
568 1.1 jdolecek if (edc_cmd_wait(sc, devno, cmd, 15))
569 1.1 jdolecek return (EAGAIN); /* Busy */
570 1.1 jdolecek
571 1.1 jdolecek sc->sc_cmd_async = 0;
572 1.1 jdolecek }
573 1.1 jdolecek
574 1.1 jdolecek /* Do Attention Request for Command Request. */
575 1.1 jdolecek if ((error = edc_do_attn(sc, ATN_CMD_REQ, devno, 0)))
576 1.1 jdolecek return (error);
577 1.1 jdolecek
578 1.1 jdolecek /*
579 1.1 jdolecek * Construct the command. The bits are like this:
580 1.1 jdolecek *
581 1.1 jdolecek * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
582 1.1 jdolecek * \_/ 0 0 1 0 \__/ \_____/
583 1.1 jdolecek * \ \__________/ \ \_ Command Code (see CMD_*)
584 1.1 jdolecek * \ \ \__ Device: 0 common, 7 controller
585 1.1 jdolecek * \ \__ Options: reserved, bit 10=cache bypass bit
586 1.1 jdolecek * \_ Type: 00=2B, 01=4B, 10 and 11 reserved
587 1.1 jdolecek *
588 1.1 jdolecek * We always use device 0 or 1, so difference is made only by Command
589 1.1 jdolecek * Code, Command Options and command length.
590 1.1 jdolecek */
591 1.1 jdolecek cmd0 = ((cmd_len == 4) ? (CIFR_LONG_CMD) : 0)
592 1.1 jdolecek | (devno << 5)
593 1.1 jdolecek | (cmd_args[0] << 8) | cmd;
594 1.1 jdolecek cmd_args[0] = cmd0;
595 1.1 jdolecek
596 1.1 jdolecek /*
597 1.1 jdolecek * Write word of CMD to the CIFR. This sets "Command
598 1.1 jdolecek * Interface Register Full (CMD IN)" in BSR. Once the attachment
599 1.1 jdolecek * detects it, it reads the word and clears CMD IN.
600 1.1 jdolecek */
601 1.1 jdolecek for(i=0; i < cmd_len; i++) {
602 1.1 jdolecek bus_space_write_2(sc->sc_iot, sc->sc_ioh, CIFR,
603 1.1 jdolecek htole16(cmd_args[i]));
604 1.1 jdolecek
605 1.1 jdolecek /* wait until CMD IN is cleared */
606 1.1 jdolecek while(bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) & BSR_CIFR_FULL)
607 1.1 jdolecek delay(1);
608 1.1 jdolecek }
609 1.1 jdolecek
610 1.1 jdolecek /*
611 1.1 jdolecek * Attachment is now executing the command. Unless we are executing
612 1.1 jdolecek * command asynchronously, wait until it finishes.
613 1.1 jdolecek */
614 1.1 jdolecek if (async) {
615 1.1 jdolecek sc->sc_cmd_async = 1;
616 1.1 jdolecek return (0);
617 1.1 jdolecek }
618 1.1 jdolecek
619 1.1 jdolecek /* Wait for command to complete, but maximum 15 seconds. */
620 1.1 jdolecek if (edc_cmd_wait(sc, devno, cmd, 15))
621 1.1 jdolecek return (EAGAIN);
622 1.1 jdolecek
623 1.1 jdolecek /* Check if the command completed successfully; if not, return error */
624 1.1 jdolecek switch(SB_GET_CMD_STATUS(sc->sc_ed[devno]->sc_status_block)) {
625 1.1 jdolecek case ISR_COMPLETED:
626 1.1 jdolecek case ISR_COMPLETED_WITH_ECC:
627 1.1 jdolecek case ISR_COMPLETED_RETRIES:
628 1.1 jdolecek case ISR_COMPLETED_WARNING:
629 1.1 jdolecek return (0);
630 1.1 jdolecek default:
631 1.1 jdolecek return (EIO);
632 1.1 jdolecek }
633 1.1 jdolecek }
634 1.1 jdolecek
635 1.1 jdolecek static int
636 1.1 jdolecek edc_setup_dma(sc, bp, phys, cnt)
637 1.1 jdolecek struct edc_mca_softc *sc;
638 1.1 jdolecek struct buf *bp;
639 1.1 jdolecek bus_addr_t phys;
640 1.1 jdolecek bus_size_t cnt;
641 1.1 jdolecek {
642 1.1 jdolecek /* XXX magic constants, should be moved to device-independant location*/
643 1.1 jdolecek /* The exact sequence to setup MCA DMA controller is taken from Minix */
644 1.1 jdolecek
645 1.1 jdolecek bus_space_write_1(sc->sc_iot, sc->sc_dmaextcmdh, 0,
646 1.1 jdolecek 0x90 + sc->sc_drq);
647 1.1 jdolecek /* Disable access to dma channel */
648 1.1 jdolecek bus_space_write_1(sc->sc_iot, sc->sc_dmaextcmdh, 0,
649 1.1 jdolecek 0x20 + sc->sc_drq);
650 1.1 jdolecek /* Clear the address byte pointer */
651 1.1 jdolecek bus_space_write_1(sc->sc_iot, sc->sc_dmaexech, 0,
652 1.1 jdolecek (phys >> 0) & 0xff); /* address bits 0..7 */
653 1.1 jdolecek bus_space_write_1(sc->sc_iot, sc->sc_dmaexech, 0,
654 1.1 jdolecek (phys >> 8) & 0xff); /* address bits 8..15 */
655 1.1 jdolecek bus_space_write_1(sc->sc_iot, sc->sc_dmaexech, 0,
656 1.1 jdolecek (phys >> 16) & 0xff); /* address bits 16..23 */
657 1.1 jdolecek bus_space_write_1(sc->sc_iot, sc->sc_dmaextcmdh, 0,
658 1.1 jdolecek 0x40 + sc->sc_drq);
659 1.1 jdolecek /* Clear the count byte pointer */
660 1.1 jdolecek bus_space_write_1(sc->sc_iot, sc->sc_dmaexech, 0,
661 1.1 jdolecek ((cnt - 1) >> 0) & 0xff); /* count bits 0..7 */
662 1.1 jdolecek bus_space_write_1(sc->sc_iot, sc->sc_dmaexech, 0,
663 1.1 jdolecek ((cnt - 1) >> 8) & 0xff); /* count bits 8..15 */
664 1.1 jdolecek bus_space_write_1(sc->sc_iot, sc->sc_dmaextcmdh, 0,
665 1.1 jdolecek 0x70 + sc->sc_drq);
666 1.1 jdolecek /* Set the transfer mode */
667 1.1 jdolecek bus_space_write_1(sc->sc_iot, sc->sc_dmaexech, 0,
668 1.1 jdolecek (bp->b_flags & B_READ) ? 0x4C : 0x44);
669 1.1 jdolecek bus_space_write_1(sc->sc_iot, sc->sc_dmaextcmdh, 0,
670 1.1 jdolecek 0xA0 + sc->sc_drq);
671 1.1 jdolecek /* Enable access to dma channel */
672 1.1 jdolecek
673 1.1 jdolecek return (1);
674 1.1 jdolecek }
675 1.1 jdolecek
676 1.1 jdolecek static const char * const edc_commands[] = {
677 1.1 jdolecek "Invalid Command",
678 1.1 jdolecek "Read Data",
679 1.1 jdolecek "Write Data",
680 1.1 jdolecek "Read Verify",
681 1.1 jdolecek "Write with Verify",
682 1.1 jdolecek "Seek",
683 1.1 jdolecek "Park Head",
684 1.1 jdolecek "Get Command Complete Status",
685 1.1 jdolecek "Get Device Status",
686 1.1 jdolecek "Get Device Configuration",
687 1.1 jdolecek "Get POS Information",
688 1.1 jdolecek "Translate RBA",
689 1.1 jdolecek "Write Attachment Buffer",
690 1.1 jdolecek "Read Attachment Buffer",
691 1.1 jdolecek "Run Diagnostic Test",
692 1.1 jdolecek "Get Diagnostic Status Block",
693 1.1 jdolecek "Get MFG Header",
694 1.1 jdolecek "Format Unit",
695 1.1 jdolecek "Format Prepare",
696 1.1 jdolecek "Set MAX RBA",
697 1.1 jdolecek "Set Power Saving Mode",
698 1.1 jdolecek "Power Conservation Command",
699 1.1 jdolecek };
700 1.1 jdolecek
701 1.1 jdolecek static const char * const edc_cmd_status[256] = {
702 1.1 jdolecek "Reserved",
703 1.1 jdolecek "Command completed successfully",
704 1.1 jdolecek "Reserved",
705 1.1 jdolecek "Command completed successfully with ECC applied",
706 1.1 jdolecek "Reserved",
707 1.1 jdolecek "Command completed successfully with retries",
708 1.1 jdolecek "Format Command partially completed", /* Status available */
709 1.1 jdolecek "Command completed successfully with ECC and retries",
710 1.1 jdolecek "Command completed with Warning", /* Command Error is available */
711 1.1 jdolecek "Aborted",
712 1.1 jdolecek "Reset completed",
713 1.1 jdolecek "Data Transfer Ready", /* No Status Block available */
714 1.1 jdolecek "Command terminated with failure", /* Device Error is available */
715 1.1 jdolecek "DMA Error", /* Retry entire command as recovery */
716 1.1 jdolecek "Command Block Error",
717 1.1 jdolecek "Attention Error (Illegal Attention Code)",
718 1.1 jdolecek /* 0x14 - 0xff reserved */
719 1.1 jdolecek };
720 1.1 jdolecek
721 1.1 jdolecek static const char * const edc_cmd_error[256] = {
722 1.1 jdolecek "No Error",
723 1.1 jdolecek "Invalid parameter in the command block",
724 1.1 jdolecek "Reserved",
725 1.1 jdolecek "Command not supported",
726 1.1 jdolecek "Command Aborted per request",
727 1.1 jdolecek "Reserved",
728 1.1 jdolecek "Command rejected", /* Attachment diagnostic failure */
729 1.1 jdolecek "Format Rejected", /* Prepare Format command is required */
730 1.1 jdolecek "Format Error (Primary Map is not readable)",
731 1.1 jdolecek "Format Error (Secondary map is not readable)",
732 1.1 jdolecek "Format Error (Diagnostic Failure)",
733 1.1 jdolecek "Format Warning (Secondary Map Overflow)",
734 1.1 jdolecek "Reserved"
735 1.1 jdolecek "Format Error (Host Checksum Error)",
736 1.1 jdolecek "Reserved",
737 1.1 jdolecek "Format Warning (Push table overflow)",
738 1.1 jdolecek "Format Warning (More pushes than allowed)",
739 1.1 jdolecek "Reserved",
740 1.1 jdolecek "Format Warning (Error during verifying)",
741 1.1 jdolecek "Invalid device number for the command",
742 1.1 jdolecek /* 0x14-0xff reserved */
743 1.1 jdolecek };
744 1.1 jdolecek
745 1.1 jdolecek static const char * const edc_dev_status[] = {
746 1.1 jdolecek "Seek or Command complete",
747 1.1 jdolecek "Track 0 Flag (emulated)",
748 1.1 jdolecek "Write Fault (emulated)",
749 1.1 jdolecek "Selected",
750 1.1 jdolecek "Ready",
751 1.1 jdolecek "Reserved (0)",
752 1.1 jdolecek "STANDBY",
753 1.1 jdolecek "Reserved (0)",
754 1.1 jdolecek };
755 1.1 jdolecek
756 1.1 jdolecek static const char * const edc_dev_errors[] = {
757 1.1 jdolecek "No Error",
758 1.1 jdolecek "Seek Fault", /* Device report */
759 1.1 jdolecek "Interface Fault (Parity, Attn, or Cmd Complete Error)",
760 1.1 jdolecek "Block not found (ID not found)",
761 1.1 jdolecek "Block not found (AM not found)",
762 1.1 jdolecek "Data ECC Error (hard error)",
763 1.1 jdolecek "ID CRC Error",
764 1.1 jdolecek "RBA Out of Range",
765 1.1 jdolecek "Reserved",
766 1.1 jdolecek "Defective Block",
767 1.1 jdolecek "Reserved",
768 1.1 jdolecek "Selection Error",
769 1.1 jdolecek "Reserved",
770 1.1 jdolecek "Write Fault",
771 1.1 jdolecek "No index or sector pulse",
772 1.1 jdolecek "Device Not Ready",
773 1.1 jdolecek "Seek Error", /* Attachment report */
774 1.1 jdolecek "Bad Format",
775 1.1 jdolecek "Volume Overflow",
776 1.1 jdolecek "No Data AM Found",
777 1.1 jdolecek "(Block not found) No ID AM or ID CRC error occured",
778 1.1 jdolecek "Reserved",
779 1.1 jdolecek "Reserved",
780 1.1 jdolecek "No ID found on track (ID search)",
781 1.1 jdolecek /* 0x19 - 0xff reserved */
782 1.1 jdolecek };
783 1.1 jdolecek
784 1.1 jdolecek static void
785 1.1 jdolecek edc_dump_status_block(sc, devno, cmd, intr_id)
786 1.1 jdolecek struct edc_mca_softc *sc;
787 1.1 jdolecek int devno, cmd, intr_id;
788 1.1 jdolecek {
789 1.1 jdolecek struct ed_softc *ed = sc->sc_ed[devno];
790 1.1 jdolecek printf("%s: Command: %s, Status: %s\n",
791 1.1 jdolecek ed->sc_dev.dv_xname,
792 1.1 jdolecek edc_commands[ed->sc_status_block[0] & 0x1f],
793 1.1 jdolecek edc_cmd_status[SB_GET_CMD_STATUS(ed->sc_status_block)]
794 1.1 jdolecek );
795 1.1 jdolecek printf("%s: IntrId: %s\n", ed->sc_dev.dv_xname,
796 1.1 jdolecek edc_cmd_status[intr_id]);
797 1.1 jdolecek
798 1.1 jdolecek if (cmd == ISR_COMPLETED_WARNING) {
799 1.1 jdolecek printf("%s: Command Error Code: %s\n",
800 1.1 jdolecek ed->sc_dev.dv_xname,
801 1.1 jdolecek edc_cmd_error[ed->sc_status_block[1] & 0xff]);
802 1.1 jdolecek }
803 1.1 jdolecek
804 1.1 jdolecek if (cmd == ISR_CMD_FAILED) {
805 1.1 jdolecek printf("%s: Device: Status: %s, Error Code: %s\n",
806 1.1 jdolecek ed->sc_dev.dv_xname,
807 1.1 jdolecek edc_dev_status[(ed->sc_status_block[2] & 0xff00) >> 8],
808 1.1 jdolecek edc_dev_errors[ed->sc_status_block[2] & 0xff]);
809 1.1 jdolecek }
810 1.1 jdolecek }
811