hp.c revision 1.8 1 /* $NetBSD: hp.c,v 1.8 1996/04/08 18:38:58 ragge Exp $ */
2 /*
3 * Copyright (c) 1996 Ludd, University of Lule}, Sweden.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed at Ludd, University of
17 * Lule}, Sweden and its contributors.
18 * 4. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 /*
34 * Simple device driver routine for massbuss disks.
35 * TODO:
36 * Fix support for Standard DEC BAD144 bad block forwarding.
37 * Be able to to handle soft/hard transfer errors.
38 * Handle non-data transfer interrupts.
39 * Autoconfiguration of disk drives 'on the fly'.
40 * Handle disk media changes.
41 * Dual-port operations should be supported.
42 */
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/device.h>
46 #include <sys/disklabel.h>
47 #include <sys/disk.h>
48 #include <sys/dkio.h>
49 #include <sys/buf.h>
50 #include <sys/stat.h>
51 #include <sys/ioccom.h>
52 #include <sys/fcntl.h>
53 #include <sys/syslog.h>
54 #include <sys/cpu.h>
55
56 #include <machine/trap.h>
57 #include <machine/pte.h>
58 #include <machine/mtpr.h>
59 #include <machine/cpu.h>
60
61 #include <vax/mba/mbavar.h>
62 #include <vax/mba/mbareg.h>
63 #include <vax/mba/hpreg.h>
64
65 #define HPMASK 0xffff
66
67 struct hp_softc {
68 struct device sc_dev;
69 struct disk sc_disk;
70 struct mba_device sc_md; /* Common struct used by mbaqueue. */
71 int sc_wlabel; /* Disklabel area is writable */
72 int sc_physnr; /* Physical disk number */
73 };
74
75 int hpmatch __P((struct device *, void *, void *));
76 void hpattach __P((struct device *, struct device *, void *));
77 void hpstrategy __P((struct buf *));
78 void hpstart __P((struct mba_device *));
79 int hpattn __P((struct mba_device *));
80 enum xfer_action hpfinish __P((struct mba_device *, int, int *));
81 int hpopen __P((dev_t, int, int));
82 int hpclose __P((dev_t, int, int));
83 int hpioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
84 int hpdump __P((dev_t, caddr_t, caddr_t, size_t));
85 int hpread __P((dev_t, struct uio *));
86 int hpwrite __P((dev_t, struct uio *));
87 int hpsize __P((dev_t));
88
89 struct cfdriver hp_cd = {
90 NULL, "hp", DV_DISK
91 };
92
93 struct cfattach hp_ca = {
94 sizeof(struct hp_softc), hpmatch, hpattach
95 };
96
97 /*
98 * Check if this is a disk drive; done by checking type from mbaattach.
99 */
100 int
101 hpmatch(parent, match, aux)
102 struct device *parent;
103 void *match, *aux;
104 {
105 struct cfdata *cf = match;
106 struct mba_attach_args *ma = aux;
107
108 if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != ma->unit)
109 return 0;
110
111 if (ma->devtyp != MB_RP)
112 return 0;
113
114 return 1;
115 }
116
117 /*
118 * Disk drive found; fake a disklabel and try to read the real one.
119 * If the on-disk label can't be read; we lose.
120 */
121 void
122 hpattach(parent, self, aux)
123 struct device *parent, *self;
124 void *aux;
125 {
126 struct hp_softc *sc = (void *)self;
127 struct mba_softc *ms = (void *)parent;
128 struct disklabel *dl;
129 struct mba_attach_args *ma = aux;
130 char *msg;
131
132 /*
133 * Init the common struct for both the adapter and its slaves.
134 */
135 sc->sc_md.md_softc = (void *)sc; /* Pointer to this softc */
136 sc->sc_md.md_mba = (void *)parent; /* Pointer to parent softc */
137 sc->sc_md.md_start = hpstart; /* Disk start routine */
138 sc->sc_md.md_attn = hpattn; /* Disk attention routine */
139 sc->sc_md.md_finish = hpfinish; /* Disk xfer finish routine */
140
141 ms->sc_md[ma->unit] = &sc->sc_md; /* Per-unit backpointer */
142
143 sc->sc_physnr = ma->unit;
144 /*
145 * Init and attach the disk structure.
146 */
147 sc->sc_disk.dk_name = sc->sc_dev.dv_xname;
148 disk_attach(&sc->sc_disk);
149
150 /*
151 * Fake a disklabel to be able to read in the real label.
152 */
153 dl = sc->sc_disk.dk_label;
154
155 dl->d_secsize = DEV_BSIZE;
156 dl->d_ntracks = 1;
157 dl->d_nsectors = 32;
158 dl->d_secpercyl = 32;
159
160 /*
161 * Read in label.
162 */
163 if ((msg = readdisklabel(makedev(0, self->dv_unit * 8), hpstrategy,
164 dl, NULL)) != NULL)
165 printf(": %s", msg);
166 printf(": %s, size = %d sectors\n", dl->d_typename, dl->d_secperunit);
167 }
168
169
170 void
171 hpstrategy(bp)
172 struct buf *bp;
173 {
174 struct hp_softc *sc;
175 struct buf *gp;
176 int unit, s;
177
178 unit = DISKUNIT(bp->b_dev);
179 sc = hp_cd.cd_devs[unit];
180
181 if (bounds_check_with_label(bp, sc->sc_disk.dk_label, sc->sc_wlabel)
182 <= 0)
183 goto done;
184 s = splbio();
185
186 gp = sc->sc_md.md_q.b_actf;
187 disksort(&sc->sc_md.md_q, bp);
188 if (gp == 0)
189 mbaqueue(&sc->sc_md);
190
191 splx(s);
192 return;
193
194 done:
195 bp->b_resid = bp->b_bcount;
196 biodone(bp);
197 }
198
199 /*
200 * Start transfer on given disk. Called from mbastart().
201 */
202 void
203 hpstart(md)
204 struct mba_device *md;
205 {
206 struct hp_softc *sc = md->md_softc;
207 struct mba_regs *mr = md->md_mba->sc_mbareg;
208 volatile struct hp_regs *hr;
209 struct disklabel *lp = sc->sc_disk.dk_label;
210 struct buf *bp = md->md_q.b_actf;
211 unsigned bn, cn, sn, tn;
212 int part = DISKPART(bp->b_dev);
213
214 /*
215 * Collect statistics.
216 */
217 disk_busy(&sc->sc_disk);
218 sc->sc_disk.dk_seek++;
219
220 hr = (void *)&mr->mba_md[DISKUNIT(bp->b_dev)];
221
222 bn = bp->b_blkno + lp->d_partitions[part].p_offset;
223 if (bn) {
224 cn = bn / lp->d_secpercyl;
225 sn = bn % lp->d_secpercyl;
226 tn = sn / lp->d_nsectors;
227 sn = sn % lp->d_nsectors;
228 } else
229 cn = sn = tn = 0;
230
231 hr->hp_dc = cn;
232 hr->hp_da = (tn << 8) | sn;
233 if (bp->b_flags & B_READ)
234 hr->hp_cs1 = HPCS_READ; /* GO */
235 else
236 hr->hp_cs1 = HPCS_WRITE;
237 }
238
239 int
240 hpopen(dev, flag, fmt)
241 dev_t dev;
242 int flag, fmt;
243 {
244 struct hp_softc *sc;
245 int unit, part;
246
247 unit = DISKUNIT(dev);
248 if (unit >= hp_cd.cd_ndevs)
249 return ENXIO;
250 sc = hp_cd.cd_devs[unit];
251 if (sc == 0)
252 return ENXIO;
253
254 part = DISKPART(dev);
255
256 if (part >= sc->sc_disk.dk_label->d_npartitions)
257 return ENXIO;
258
259 switch (fmt) {
260 case S_IFCHR:
261 sc->sc_disk.dk_copenmask |= (1 << part);
262 break;
263
264 case S_IFBLK:
265 sc->sc_disk.dk_bopenmask |= (1 << part);
266 break;
267 }
268 sc->sc_disk.dk_openmask =
269 sc->sc_disk.dk_copenmask | sc->sc_disk.dk_bopenmask;
270
271 return 0;
272 }
273
274 int
275 hpclose(dev, flag, fmt)
276 dev_t dev;
277 int flag, fmt;
278 {
279 struct hp_softc *sc;
280 int unit, part;
281
282 unit = DISKUNIT(dev);
283 sc = hp_cd.cd_devs[unit];
284
285 part = DISKPART(dev);
286
287 switch (fmt) {
288 case S_IFCHR:
289 sc->sc_disk.dk_copenmask &= ~(1 << part);
290 break;
291
292 case S_IFBLK:
293 sc->sc_disk.dk_bopenmask &= ~(1 << part);
294 break;
295 }
296 sc->sc_disk.dk_openmask =
297 sc->sc_disk.dk_copenmask | sc->sc_disk.dk_bopenmask;
298
299 return 0;
300 }
301
302 int
303 hpioctl(dev, cmd, addr, flag, p)
304 dev_t dev;
305 u_long cmd;
306 caddr_t addr;
307 int flag;
308 struct proc *p;
309 {
310 struct hp_softc *sc = hp_cd.cd_devs[DISKUNIT(dev)];
311 struct disklabel *lp = sc->sc_disk.dk_label;
312 int error;
313
314 switch (cmd) {
315 case DIOCGDINFO:
316 bcopy(lp, addr, sizeof (struct disklabel));
317 return 0;
318
319 case DIOCGPART:
320 ((struct partinfo *)addr)->disklab = lp;
321 ((struct partinfo *)addr)->part =
322 &lp->d_partitions[DISKPART(dev)];
323 break;
324
325 case DIOCSDINFO:
326 if ((flag & FWRITE) == 0)
327 return EBADF;
328
329 return setdisklabel(lp, (struct disklabel *)addr, 0, 0);
330
331 case DIOCWDINFO:
332 if ((flag & FWRITE) == 0)
333 error = EBADF;
334 else {
335 sc->sc_wlabel = 1;
336 error = writedisklabel(dev, hpstrategy, lp, 0);
337 sc->sc_wlabel = 0;
338 }
339 return error;
340 case DIOCWLABEL:
341 if ((flag & FWRITE) == 0)
342 return EBADF;
343 sc->sc_wlabel = 1;
344 break;
345
346 default:
347 printf("hpioctl: command %x\n", (unsigned int)cmd);
348 return ENOTTY;
349 }
350 return 0;
351 }
352
353 /*
354 * Called when a transfer is finished. Check if transfer went OK,
355 * Return info about what-to-do-now.
356 */
357 enum xfer_action
358 hpfinish(md, mbasr, attn)
359 struct mba_device *md;
360 int mbasr, *attn;
361 {
362 struct hp_softc *sc = md->md_softc;
363 struct buf *bp = md->md_q.b_actf;
364 volatile struct mba_regs *mr = md->md_mba->sc_mbareg;
365 volatile struct hp_regs *hr = (void *)&mr->mba_md[DISKUNIT(bp->b_dev)];
366 int er1, er2;
367 volatile int bc; /* to get GCC read whole longword */
368 unsigned byte;
369
370 er1 = hr->hp_er1 & HPMASK;
371 er2 = hr->hp_er2 & HPMASK;
372 hr->hp_er1 = hr->hp_er2 = 0;
373 hper1:
374 switch (ffs(er1) - 1) {
375 case -1:
376 hr->hp_er1 = 0;
377 goto hper2;
378
379 case HPER1_DCK: /* Corrected? data read. Just notice. */
380 bc = mr->mba_bc;
381 byte = ~(bc >> 16);
382 diskerr(buf, hp_cd.cd_name, "soft ecc", LOG_PRINTF,
383 btodb(bp->b_bcount - byte), sc->sc_disk.dk_label);
384 er1 &= ~(1<<HPER1_DCK);
385 er1 &= HPMASK;
386 break;
387
388 default:
389 printf("drive error :%s er1 %x er2 %x\n",
390 sc->sc_dev.dv_xname, er1, er2);
391 hr->hp_er1 = hr->hp_er2 = 0;
392 goto hper2;
393 }
394 goto hper1;
395
396 hper2:
397 mbasr &= ~(MBASR_DTBUSY|MBASR_DTCMP|MBASR_ATTN);
398 if (mbasr)
399 printf("massbuss error :%s %x\n",
400 sc->sc_dev.dv_xname, mbasr);
401
402 md->md_q.b_actf->b_resid = 0;
403 disk_unbusy(&sc->sc_disk, md->md_q.b_actf->b_bcount);
404 return XFER_FINISH;
405 }
406
407 /*
408 * Non-data transfer interrupt; like volume change.
409 */
410 int
411 hpattn(md)
412 struct mba_device *md;
413 {
414 struct hp_softc *sc = md->md_softc;
415 struct mba_softc *ms = (void *)sc->sc_dev.dv_parent;
416 struct mba_regs *mr = ms->sc_mbareg;
417 struct hp_regs *hr = (void *)&mr->mba_md[sc->sc_dev.dv_unit];
418 int er1, er2;
419
420 er1 = hr->hp_er1 & HPMASK;
421 er2 = hr->hp_er2 & HPMASK;
422
423 printf("%s: Attention! er1 %x er2 %x\n",
424 sc->sc_dev.dv_xname, er1, er2);
425 return 0;
426 }
427
428
429 int
430 hpsize(dev)
431 dev_t dev;
432 {
433 int size, unit = DISKUNIT(dev);
434 struct hp_softc *sc;
435
436 if (unit >= hp_cd.cd_ndevs || hp_cd.cd_devs[unit] == 0)
437 return -1;
438
439 sc = hp_cd.cd_devs[unit];
440 size = sc->sc_disk.dk_label->d_partitions[DISKPART(dev)].p_size;
441
442 return size;
443 }
444
445 int
446 hpdump(dev, a1, a2, size)
447 dev_t dev;
448 caddr_t a1, a2;
449 size_t size;
450 {
451 printf("hpdump: Not implemented yet.\n");
452 return 0;
453 }
454
455 int
456 hpread(dev, uio)
457 dev_t dev;
458 struct uio *uio;
459 {
460 return (physio(hpstrategy, NULL, dev, B_READ, minphys, uio));
461 }
462
463 int
464 hpwrite(dev, uio)
465 dev_t dev;
466 struct uio *uio;
467 {
468 return (physio(hpstrategy, NULL, dev, B_WRITE, minphys, uio));
469 }
470
471 /*
472 * Convert physical adapternr and unit to the unit number used by kernel.
473 */
474 int
475 hp_getdev(mbanr, unit)
476 int mbanr, unit;
477 {
478 struct mba_softc *ms;
479 struct hp_softc *sc;
480 int i;
481
482 for (i = 0; i < hp_cd.cd_ndevs; i++) {
483 if (hp_cd.cd_devs[i] == 0)
484 continue;
485
486 sc = hp_cd.cd_devs[i];
487 ms = (void *)sc->sc_dev.dv_parent;
488 if (ms->sc_physnr == mbanr && sc->sc_physnr == unit)
489 return i;
490 }
491 return -1;
492 }
493
494