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