hp.c revision 1.24.4.1 1 /* $NetBSD: hp.c,v 1.24.4.1 2001/10/10 11:56:42 fvdl 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/reboot.h>
55 #include <sys/conf.h>
56 #include <sys/vnode.h>
57
58 #include <machine/bus.h>
59 #include <machine/trap.h>
60 #include <machine/pte.h>
61 #include <machine/mtpr.h>
62 #include <machine/cpu.h>
63
64 #include <vax/mba/mbavar.h>
65 #include <vax/mba/mbareg.h>
66 #include <vax/mba/hpreg.h>
67
68 #include "ioconf.h"
69 #include "locators.h"
70
71 struct hp_softc {
72 struct device sc_dev;
73 struct disk sc_disk;
74 bus_space_tag_t sc_iot;
75 bus_space_handle_t sc_ioh;
76 struct mba_device sc_md; /* Common struct used by mbaqueue. */
77 int sc_wlabel; /* Disklabel area is writable */
78 };
79
80 int hpmatch(struct device *, struct cfdata *, void *);
81 void hpattach(struct device *, struct device *, void *);
82 void hpstart(struct mba_device *);
83 int hpattn(struct mba_device *);
84 enum xfer_action hpfinish(struct mba_device *, int, int *);
85 bdev_decl(hp);
86 cdev_decl(hp);
87
88 struct cfattach hp_ca = {
89 sizeof(struct hp_softc), hpmatch, hpattach
90 };
91
92 #define HP_WCSR(reg, val) \
93 bus_space_write_4(sc->sc_iot, sc->sc_ioh, (reg), (val))
94 #define HP_RCSR(reg) \
95 bus_space_read_4(sc->sc_iot, sc->sc_ioh, (reg))
96
97
98 /*
99 * Check if this is a disk drive; done by checking type from mbaattach.
100 */
101 int
102 hpmatch(struct device *parent, struct cfdata *cf, void *aux)
103 {
104 struct mba_attach_args *ma = aux;
105
106 if (cf->cf_loc[MBACF_DRIVE] != MBACF_DRIVE_DEFAULT &&
107 cf->cf_loc[MBACF_DRIVE] != ma->ma_unit)
108 return 0;
109
110 if (ma->ma_devtyp != MB_RP)
111 return 0;
112
113 return 1;
114 }
115
116 /*
117 * Disk drive found; fake a disklabel and try to read the real one.
118 * If the on-disk label can't be read; we lose.
119 */
120 void
121 hpattach(struct device *parent, struct device *self, void *aux)
122 {
123 struct hp_softc *sc = (void *)self;
124 struct mba_softc *ms = (void *)parent;
125 struct disklabel *dl;
126 struct mba_attach_args *ma = aux;
127 char *msg;
128 struct vnode vn; /* XXX */
129 struct specinfo si; /* XXX */
130
131 sc->sc_iot = ma->ma_iot;
132 sc->sc_ioh = ma->ma_ioh;
133 /*
134 * Init the common struct for both the adapter and its slaves.
135 */
136 BUFQ_INIT(&sc->sc_md.md_q);
137 sc->sc_md.md_softc = (void *)sc; /* Pointer to this softc */
138 sc->sc_md.md_mba = (void *)parent; /* Pointer to parent softc */
139 sc->sc_md.md_start = hpstart; /* Disk start routine */
140 sc->sc_md.md_attn = hpattn; /* Disk attention routine */
141 sc->sc_md.md_finish = hpfinish; /* Disk xfer finish routine */
142
143 ms->sc_md[ma->ma_unit] = &sc->sc_md; /* Per-unit backpointer */
144
145 /*
146 * Init and attach the disk structure.
147 */
148 sc->sc_disk.dk_name = sc->sc_dev.dv_xname;
149 disk_attach(&sc->sc_disk);
150
151 /*
152 * Fake a disklabel to be able to read in the real label.
153 */
154 dl = sc->sc_disk.dk_label;
155
156 dl->d_secsize = DEV_BSIZE;
157 dl->d_ntracks = 1;
158 dl->d_nsectors = 32;
159 dl->d_secpercyl = 32;
160
161 /*
162 * Read in label.
163 */
164 vn.v_specinfo = &si;
165 vn.v_type = VBLK;
166 vn.v_rdev = makedev(0, self->dv_unit * 8);
167 vn.v_devcookie = sc;
168 if ((msg = readdisklabel(&vn, hpstrategy, dl, NULL)) != NULL)
169 printf(": %s", msg);
170 printf(": %s, size = %d sectors\n", dl->d_typename, dl->d_secperunit);
171 }
172
173
174 void
175 hpstrategy(struct buf *bp)
176 {
177 struct hp_softc *sc;
178 struct buf *gp;
179 int unit, s, err;
180 struct disklabel *lp;
181 dev_t dev;
182 int part;
183
184 dev = vdev_rdev(bp->b_devvp);
185 unit = DISKUNIT(dev);
186 sc = hp_cd.cd_devs[unit];
187 lp = sc->sc_disk.dk_label;
188 part = (bp->b_flags & B_DKLABEL) ? RAW_PART : DISKPART(dev);
189
190 err = bounds_check_with_label(bp, lp, sc->sc_wlabel);
191 if (err < 0)
192 goto done;
193
194 bp->b_rawblkno =
195 bp->b_blkno + lp->d_partitions[part].p_offset;
196 bp->b_cylinder = bp->b_rawblkno / lp->d_secpercyl;
197
198 s = splbio();
199
200 gp = BUFQ_FIRST(&sc->sc_md.md_q);
201 disksort_cylinder(&sc->sc_md.md_q, bp);
202 if (gp == 0)
203 mbaqueue(&sc->sc_md);
204
205 splx(s);
206 return;
207
208 done:
209 bp->b_resid = bp->b_bcount;
210 biodone(bp);
211 }
212
213 /*
214 * Start transfer on given disk. Called from mbastart().
215 */
216 void
217 hpstart(struct mba_device *md)
218 {
219 struct hp_softc *sc = md->md_softc;
220 struct disklabel *lp = sc->sc_disk.dk_label;
221 struct buf *bp = BUFQ_FIRST(&md->md_q);
222 unsigned bn, cn, sn, tn;
223
224 /*
225 * Collect statistics.
226 */
227 disk_busy(&sc->sc_disk);
228 sc->sc_disk.dk_seek++;
229
230 bn = bp->b_rawblkno;
231 if (bn) {
232 cn = bn / lp->d_secpercyl;
233 sn = bn % lp->d_secpercyl;
234 tn = sn / lp->d_nsectors;
235 sn = sn % lp->d_nsectors;
236 } else
237 cn = sn = tn = 0;
238
239 HP_WCSR(HP_DC, cn);
240 HP_WCSR(HP_DA, (tn << 8) | sn);
241 if (bp->b_flags & B_READ)
242 HP_WCSR(HP_CS1, HPCS_READ);
243 else
244 HP_WCSR(HP_CS1, HPCS_WRITE);
245 }
246
247 int
248 hpopen(struct vnode *devvp, int flag, int fmt, struct proc *p)
249 {
250 struct hp_softc *sc;
251 int unit, part;
252 dev_t dev;
253
254 dev = vdev_rdev(devvp);
255 unit = DISKUNIT(dev);
256 if (unit >= hp_cd.cd_ndevs)
257 return ENXIO;
258 sc = hp_cd.cd_devs[unit];
259 if (sc == 0)
260 return ENXIO;
261
262 part = DISKPART(dev);
263
264 if (part >= sc->sc_disk.dk_label->d_npartitions)
265 return ENXIO;
266
267 vdev_setprivdata(devvp, sc);
268
269 switch (fmt) {
270 case S_IFCHR:
271 sc->sc_disk.dk_copenmask |= (1 << part);
272 break;
273
274 case S_IFBLK:
275 sc->sc_disk.dk_bopenmask |= (1 << part);
276 break;
277 }
278 sc->sc_disk.dk_openmask =
279 sc->sc_disk.dk_copenmask | sc->sc_disk.dk_bopenmask;
280
281 return 0;
282 }
283
284 int
285 hpclose(struct vnode *devvp, int flag, int fmt, struct proc *p)
286 {
287 struct hp_softc *sc;
288 int part;
289
290 sc = vdev_privdata(devvp);
291
292 part = DISKPART(vdev_rdev(devvp));
293
294 switch (fmt) {
295 case S_IFCHR:
296 sc->sc_disk.dk_copenmask &= ~(1 << part);
297 break;
298
299 case S_IFBLK:
300 sc->sc_disk.dk_bopenmask &= ~(1 << part);
301 break;
302 }
303 sc->sc_disk.dk_openmask =
304 sc->sc_disk.dk_copenmask | sc->sc_disk.dk_bopenmask;
305
306 return 0;
307 }
308
309 int
310 hpioctl(struct vnode *devvp, u_long cmd, caddr_t addr, int flag, struct proc *p)
311 {
312 struct hp_softc *sc;
313 struct disklabel *lp;
314 int error;
315
316 sc = vdev_privdata(devvp);
317 lp = sc->sc_disk.dk_label;
318
319 switch (cmd) {
320 case DIOCGDINFO:
321 bcopy(lp, addr, sizeof (struct disklabel));
322 return 0;
323
324 case DIOCGPART:
325 ((struct partinfo *)addr)->disklab = lp;
326 ((struct partinfo *)addr)->part =
327 &lp->d_partitions[DISKPART(vdev_rdev(devvp))];
328 break;
329
330 case DIOCSDINFO:
331 if ((flag & FWRITE) == 0)
332 return EBADF;
333
334 return setdisklabel(lp, (struct disklabel *)addr, 0, 0);
335
336 case DIOCWDINFO:
337 if ((flag & FWRITE) == 0)
338 error = EBADF;
339 else {
340 sc->sc_wlabel = 1;
341 error = writedisklabel(devvp, hpstrategy, lp, 0);
342 sc->sc_wlabel = 0;
343 }
344 return error;
345 case DIOCWLABEL:
346 if ((flag & FWRITE) == 0)
347 return EBADF;
348 sc->sc_wlabel = 1;
349 break;
350
351 default:
352 return ENOTTY;
353 }
354 return 0;
355 }
356
357 /*
358 * Called when a transfer is finished. Check if transfer went OK,
359 * Return info about what-to-do-now.
360 */
361 enum xfer_action
362 hpfinish(struct mba_device *md, int mbasr, int *attn)
363 {
364 struct hp_softc *sc = md->md_softc;
365 struct buf *bp = BUFQ_FIRST(&md->md_q);
366 int er1, er2, bc;
367 unsigned byte;
368
369 er1 = HP_RCSR(HP_ER1);
370 er2 = HP_RCSR(HP_ER2);
371 HP_WCSR(HP_ER1, 0);
372 HP_WCSR(HP_ER2, 0);
373
374 hper1:
375 switch (ffs(er1) - 1) {
376 case -1:
377 HP_WCSR(HP_ER1, 0);
378 goto hper2;
379
380 case HPER1_DCK: /* Corrected? data read. Just notice. */
381 bc = bus_space_read_4(md->md_mba->sc_iot,
382 md->md_mba->sc_ioh, MBA_BC);
383 byte = ~(bc >> 16);
384 diskerr(buf, hp_cd.cd_name, "soft ecc", LOG_PRINTF,
385 btodb(bp->b_bcount - byte), sc->sc_disk.dk_label);
386 er1 &= ~(1<<HPER1_DCK);
387 break;
388
389 default:
390 printf("drive error :%s er1 %x er2 %x\n",
391 sc->sc_dev.dv_xname, er1, er2);
392 HP_WCSR(HP_ER1, 0);
393 HP_WCSR(HP_ER2, 0);
394 goto hper2;
395 }
396 goto hper1;
397
398 hper2:
399 mbasr &= ~(MBASR_DTBUSY|MBASR_DTCMP|MBASR_ATTN);
400 if (mbasr)
401 printf("massbuss error :%s %x\n",
402 sc->sc_dev.dv_xname, mbasr);
403
404 BUFQ_FIRST(&md->md_q)->b_resid = 0;
405 disk_unbusy(&sc->sc_disk, BUFQ_FIRST(&md->md_q)->b_bcount);
406 return XFER_FINISH;
407 }
408
409 /*
410 * Non-data transfer interrupt; like volume change.
411 */
412 int
413 hpattn(struct mba_device *md)
414 {
415 struct hp_softc *sc = md->md_softc;
416 int er1, er2;
417
418 er1 = HP_RCSR(HP_ER1);
419 er2 = HP_RCSR(HP_ER2);
420
421 printf("%s: Attention! er1 %x er2 %x\n",
422 sc->sc_dev.dv_xname, er1, er2);
423 return 0;
424 }
425
426
427 int
428 hpsize(dev_t dev)
429 {
430 int size, unit = DISKUNIT(dev);
431 struct hp_softc *sc;
432
433 if (unit >= hp_cd.cd_ndevs || hp_cd.cd_devs[unit] == 0)
434 return -1;
435
436 sc = hp_cd.cd_devs[unit];
437 size = sc->sc_disk.dk_label->d_partitions[DISKPART(dev)].p_size *
438 (sc->sc_disk.dk_label->d_secsize / DEV_BSIZE);
439
440 return size;
441 }
442
443 int
444 hpdump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
445 {
446 return 0;
447 }
448
449 int
450 hpread(struct vnode *devvp, struct uio *uio, int ioflag)
451 {
452 return (physio(hpstrategy, NULL, devvp, B_READ, minphys, uio));
453 }
454
455 int
456 hpwrite(struct vnode *devvp, struct uio *uio, int ioflag)
457 {
458 return (physio(hpstrategy, NULL, devvp, B_WRITE, minphys, uio));
459 }
460