1 /* $NetBSD: hp.c,v 1.54 2017/05/22 17:13:09 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 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 /*
28 * Simple device driver routine for massbuss disks.
29 * TODO:
30 * Fix support for Standard DEC BAD144 bad block forwarding.
31 * Be able to to handle soft/hard transfer errors.
32 * Handle non-data transfer interrupts.
33 * Autoconfiguration of disk drives 'on the fly'.
34 * Handle disk media changes.
35 * Dual-port operations should be supported.
36 */
37
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: hp.c,v 1.54 2017/05/22 17:13:09 ragge Exp $");
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/bus.h>
44 #include <sys/cpu.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/bufq.h>
51 #include <sys/stat.h>
52 #include <sys/ioccom.h>
53 #include <sys/fcntl.h>
54 #include <sys/conf.h>
55 #include <sys/event.h>
56 #include <sys/syslog.h>
57
58 #include <vax/mba/mbavar.h>
59 #include <vax/mba/mbareg.h>
60 #include <vax/mba/hpreg.h>
61
62 #include "ioconf.h"
63 #include "locators.h"
64
65 struct hp_softc {
66 device_t sc_dev;
67 struct disk sc_disk;
68 bus_space_tag_t sc_iot;
69 bus_space_handle_t sc_ioh;
70 struct mba_device sc_md; /* Common struct used by mbaqueue. */
71 int sc_wlabel; /* Disklabel area is writable */
72 };
73
74 int hpmatch(device_t, cfdata_t, void *);
75 void hpattach(device_t, device_t, void *);
76 void hpstart(struct mba_device *);
77 int hpattn(struct mba_device *);
78 enum xfer_action hpfinish(struct mba_device *, int, int *);
79
80 CFATTACH_DECL_NEW(hp, sizeof(struct hp_softc),
81 hpmatch, hpattach, NULL, NULL);
82
83 static dev_type_open(hpopen);
84 static dev_type_close(hpclose);
85 static dev_type_read(hpread);
86 static dev_type_write(hpwrite);
87 static dev_type_ioctl(hpioctl);
88 static dev_type_strategy(hpstrategy);
89 static dev_type_size(hppsize);
90
91 const struct bdevsw hp_bdevsw = {
92 .d_open = hpopen,
93 .d_close = hpclose,
94 .d_strategy = hpstrategy,
95 .d_ioctl = hpioctl,
96 .d_dump = nulldump,
97 .d_psize = hppsize,
98 .d_discard = nodiscard,
99 .d_flag = D_DISK
100 };
101
102 const struct cdevsw hp_cdevsw = {
103 .d_open = hpopen,
104 .d_close = hpclose,
105 .d_read = hpread,
106 .d_write = hpwrite,
107 .d_ioctl = hpioctl,
108 .d_stop = nostop,
109 .d_tty = notty,
110 .d_poll = nopoll,
111 .d_mmap = nommap,
112 .d_kqfilter = nokqfilter,
113 .d_discard = nodiscard,
114 .d_flag = D_DISK
115 };
116
117 #define HP_WCSR(reg, val) \
118 bus_space_write_4(sc->sc_iot, sc->sc_ioh, (reg), (val))
119 #define HP_RCSR(reg) \
120 bus_space_read_4(sc->sc_iot, sc->sc_ioh, (reg))
121
122
123 /*
124 * Check if this is a disk drive; done by checking type from mbaattach.
125 */
126 int
hpmatch(device_t parent,cfdata_t cf,void * aux)127 hpmatch(device_t parent, cfdata_t cf, void *aux)
128 {
129 struct mba_attach_args * const ma = aux;
130
131 if (cf->cf_loc[MBACF_DRIVE] != MBACF_DRIVE_DEFAULT &&
132 cf->cf_loc[MBACF_DRIVE] != ma->ma_unit)
133 return 0;
134
135 if (ma->ma_devtyp != MB_RP)
136 return 0;
137
138 return 1;
139 }
140
141 /*
142 * Disk drive found; fake a disklabel and try to read the real one.
143 * If the on-disk label can't be read; we lose.
144 */
145 void
hpattach(device_t parent,device_t self,void * aux)146 hpattach(device_t parent, device_t self, void *aux)
147 {
148 struct hp_softc * const sc = device_private(self);
149 struct mba_softc * const ms = device_private(parent);
150 struct mba_attach_args * const ma = aux;
151 struct disklabel *dl;
152 const char *msg;
153
154 sc->sc_dev = self;
155 sc->sc_iot = ma->ma_iot;
156 sc->sc_ioh = ma->ma_ioh;
157
158 /*
159 * Init the common struct for both the adapter and its slaves.
160 */
161 bufq_alloc(&sc->sc_md.md_q, "disksort", BUFQ_SORT_CYLINDER);
162 sc->sc_md.md_softc = sc; /* Pointer to this softc */
163 sc->sc_md.md_mba = ms; /* Pointer to parent softc */
164 sc->sc_md.md_start = hpstart; /* Disk start routine */
165 sc->sc_md.md_attn = hpattn; /* Disk attention routine */
166 sc->sc_md.md_finish = hpfinish; /* Disk xfer finish routine */
167
168 ms->sc_md[ma->ma_unit] = &sc->sc_md; /* Per-unit backpointer */
169
170 /*
171 * Init and attach the disk structure.
172 */
173 disk_init(&sc->sc_disk, device_xname(sc->sc_dev), NULL);
174 disk_attach(&sc->sc_disk);
175
176 /*
177 * Fake a disklabel to be able to read in the real label.
178 */
179 dl = sc->sc_disk.dk_label;
180
181 dl->d_secsize = DEV_BSIZE;
182 dl->d_ntracks = 1;
183 dl->d_nsectors = 32;
184 dl->d_secpercyl = 32;
185
186 /*
187 * Read in label.
188 */
189 if ((msg = readdisklabel(makedev(0, device_unit(self) * 8), hpstrategy,
190 dl, NULL)) != NULL)
191 printf(": %s", msg);
192 printf(": %s, size = %d sectors\n", dl->d_typename, dl->d_secperunit);
193 }
194
195
196 void
hpstrategy(struct buf * bp)197 hpstrategy(struct buf *bp)
198 {
199 struct hp_softc *sc;
200 struct buf *gp;
201 struct disklabel *lp;
202 int unit, s, err;
203
204 unit = DISKUNIT(bp->b_dev);
205 sc = device_lookup_private(&hp_cd, unit);
206 lp = sc->sc_disk.dk_label;
207
208 err = bounds_check_with_label(&sc->sc_disk, bp, sc->sc_wlabel);
209 if (err <= 0)
210 goto done;
211
212 bp->b_rawblkno =
213 bp->b_blkno + lp->d_partitions[DISKPART(bp->b_dev)].p_offset;
214 bp->b_cylinder = bp->b_rawblkno / lp->d_secpercyl;
215
216 s = splbio();
217
218 gp = bufq_peek(sc->sc_md.md_q);
219 bufq_put(sc->sc_md.md_q, bp);
220 if (gp == 0)
221 mbaqueue(&sc->sc_md);
222
223 splx(s);
224 return;
225
226 done:
227 bp->b_resid = bp->b_bcount;
228 biodone(bp);
229 }
230
231 /*
232 * Start transfer on given disk. Called from mbastart().
233 */
234 void
hpstart(struct mba_device * md)235 hpstart(struct mba_device *md)
236 {
237 struct hp_softc * const sc = md->md_softc;
238 struct disklabel * const lp = sc->sc_disk.dk_label;
239 struct buf *bp = bufq_peek(md->md_q);
240 unsigned bn, cn, sn, tn;
241
242 /*
243 * Collect statistics.
244 */
245 disk_busy(&sc->sc_disk);
246 iostat_seek(sc->sc_disk.dk_stats);
247
248 bn = bp->b_rawblkno;
249 if (bn) {
250 cn = bn / lp->d_secpercyl;
251 sn = bn % lp->d_secpercyl;
252 tn = sn / lp->d_nsectors;
253 sn = sn % lp->d_nsectors;
254 } else
255 cn = sn = tn = 0;
256
257 HP_WCSR(HP_DC, cn);
258 HP_WCSR(HP_DA, (tn << 8) | sn);
259 if (bp->b_flags & B_READ)
260 HP_WCSR(HP_CS1, HPCS_READ);
261 else
262 HP_WCSR(HP_CS1, HPCS_WRITE);
263 }
264
265 int
hpopen(dev_t dev,int flag,int fmt,struct lwp * l)266 hpopen(dev_t dev, int flag, int fmt, struct lwp *l)
267 {
268 struct hp_softc *sc;
269 int part = DISKPART(dev);
270
271 sc = device_lookup_private(&hp_cd, DISKUNIT(dev));
272 if (sc == NULL)
273 return ENXIO;
274
275 if (part >= sc->sc_disk.dk_label->d_npartitions)
276 return ENXIO;
277
278 switch (fmt) {
279 case S_IFCHR:
280 sc->sc_disk.dk_copenmask |= (1 << part);
281 break;
282
283 case S_IFBLK:
284 sc->sc_disk.dk_bopenmask |= (1 << part);
285 break;
286 }
287 sc->sc_disk.dk_openmask =
288 sc->sc_disk.dk_copenmask | sc->sc_disk.dk_bopenmask;
289
290 return 0;
291 }
292
293 int
hpclose(dev_t dev,int flag,int fmt,struct lwp * l)294 hpclose(dev_t dev, int flag, int fmt, struct lwp *l)
295 {
296 struct hp_softc * const sc = device_lookup_private(&hp_cd, DISKUNIT(dev));
297 const int part = DISKPART(dev);
298
299 switch (fmt) {
300 case S_IFCHR:
301 sc->sc_disk.dk_copenmask &= ~(1 << part);
302 break;
303
304 case S_IFBLK:
305 sc->sc_disk.dk_bopenmask &= ~(1 << part);
306 break;
307 }
308 sc->sc_disk.dk_openmask =
309 sc->sc_disk.dk_copenmask | sc->sc_disk.dk_bopenmask;
310
311 return 0;
312 }
313
314 int
hpioctl(dev_t dev,u_long cmd,void * addr,int flag,struct lwp * l)315 hpioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
316 {
317 struct hp_softc * const sc = device_lookup_private(&hp_cd, DISKUNIT(dev));
318 struct disklabel * const lp = sc->sc_disk.dk_label;
319 int error;
320
321 error = disk_ioctl(&sc->sc_disk, dev, cmd, addr, flag, l);
322 if (error != EPASSTHROUGH)
323 return error;
324
325 switch (cmd) {
326 case DIOCSDINFO:
327 if ((flag & FWRITE) == 0)
328 return EBADF;
329
330 return setdisklabel(lp, (struct disklabel *)addr, 0, 0);
331
332 case DIOCWDINFO:
333 if ((flag & FWRITE) == 0)
334 error = EBADF;
335 else {
336 sc->sc_wlabel = 1;
337 error = writedisklabel(dev, hpstrategy, lp, 0);
338 sc->sc_wlabel = 0;
339 }
340 return error;
341 case DIOCWLABEL:
342 if ((flag & FWRITE) == 0)
343 return EBADF;
344 sc->sc_wlabel = 1;
345 break;
346
347 default:
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
hpfinish(struct mba_device * md,int mbasr,int * attn)358 hpfinish(struct mba_device *md, int mbasr, int *attn)
359 {
360 struct hp_softc * const sc = md->md_softc;
361 struct buf *bp = bufq_peek(md->md_q);
362 int er1, er2, bc;
363 unsigned byte;
364
365 er1 = HP_RCSR(HP_ER1);
366 er2 = HP_RCSR(HP_ER2);
367 HP_WCSR(HP_ER1, 0);
368 HP_WCSR(HP_ER2, 0);
369
370 hper1:
371 switch (ffs(er1) - 1) {
372 case -1:
373 HP_WCSR(HP_ER1, 0);
374 goto hper2;
375
376 case HPER1_DCK: /* Corrected? data read. Just notice. */
377 bc = bus_space_read_4(md->md_mba->sc_iot,
378 md->md_mba->sc_ioh, MBA_BC);
379 byte = ~(bc >> 16);
380 diskerr(bp, hp_cd.cd_name, "soft ecc", LOG_PRINTF,
381 btodb(bp->b_bcount - byte), sc->sc_disk.dk_label);
382 er1 &= ~(1<<HPER1_DCK);
383 break;
384
385 default:
386 aprint_error_dev(sc->sc_dev, "drive error: er1 %x er2 %x\n",
387 er1, er2);
388 HP_WCSR(HP_ER1, 0);
389 HP_WCSR(HP_ER2, 0);
390 goto hper2;
391 }
392 goto hper1;
393
394 hper2:
395 mbasr &= ~(MBASR_DTBUSY|MBASR_DTCMP|MBASR_ATTN);
396 if (mbasr)
397 aprint_error_dev(sc->sc_dev, "massbuss error: %x\n", mbasr);
398
399 bufq_peek(md->md_q)->b_resid = 0;
400 disk_unbusy(&sc->sc_disk, bufq_peek(md->md_q)->b_bcount,
401 (bp->b_flags & B_READ));
402 return XFER_FINISH;
403 }
404
405 /*
406 * Non-data transfer interrupt; like volume change.
407 */
408 int
hpattn(struct mba_device * md)409 hpattn(struct mba_device *md)
410 {
411 struct hp_softc * const sc = md->md_softc;
412 int er1, er2;
413
414 er1 = HP_RCSR(HP_ER1);
415 er2 = HP_RCSR(HP_ER2);
416
417 aprint_error_dev(sc->sc_dev, "Attention! er1 %x er2 %x\n", er1, er2);
418 return 0;
419 }
420
421
422 int
hppsize(dev_t dev)423 hppsize(dev_t dev)
424 {
425 struct hp_softc * const sc = device_lookup_private(&hp_cd, DISKUNIT(dev));
426 const int part = DISKPART(dev);
427
428 if (sc == NULL || part >= sc->sc_disk.dk_label->d_npartitions)
429 return -1;
430
431 return sc->sc_disk.dk_label->d_partitions[part].p_size *
432 (sc->sc_disk.dk_label->d_secsize / DEV_BSIZE);
433 }
434
435 int
hpread(dev_t dev,struct uio * uio,int ioflag)436 hpread(dev_t dev, struct uio *uio, int ioflag)
437 {
438 return (physio(hpstrategy, NULL, dev, B_READ, minphys, uio));
439 }
440
441 int
hpwrite(dev_t dev,struct uio * uio,int ioflag)442 hpwrite(dev_t dev, struct uio *uio, int ioflag)
443 {
444 return (physio(hpstrategy, NULL, dev, B_WRITE, minphys, uio));
445 }
446