1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27 /*
28 * Floppy Disk Controller Driver
29 *
30 * for the standard PC architecture using the Intel 8272A fdc.
31 * Note that motor control and drive select use a latch external
32 * to the fdc.
33 *
34 * This driver is EISA capable, and uses DMA buffer chaining if available.
35 * If this driver is attached to the ISA bus nexus (or if the EISA bus driver
36 * does not support DMA buffer chaining), then the bus driver must ensure
37 * that dma mapping (breakup) and dma engine requests are properly degraded.
38 */
39
40 /*
41 * hack for bugid 1160621:
42 * workaround compiler optimization bug by turning on DEBUG
43 */
44 #ifndef DEBUG
45 #define DEBUG 1
46 #endif
47
48 #include <sys/param.h>
49 #include <sys/buf.h>
50 #include <sys/ioctl.h>
51 #include <sys/uio.h>
52 #include <sys/open.h>
53 #include <sys/conf.h>
54 #include <sys/file.h>
55 #include <sys/cmn_err.h>
56 #include <sys/note.h>
57 #include <sys/debug.h>
58 #include <sys/kmem.h>
59 #include <sys/stat.h>
60
61 #include <sys/autoconf.h>
62 #include <sys/dkio.h>
63 #include <sys/vtoc.h>
64 #include <sys/kstat.h>
65
66 #include <sys/fdio.h>
67 #include <sys/fdc.h>
68 #include <sys/i8272A.h>
69 #include <sys/fd_debug.h>
70 #include <sys/promif.h>
71 #include <sys/ddi.h>
72 #include <sys/sunddi.h>
73
74 /*
75 * bss (uninitialized data)
76 */
77 static void *fdc_state_head; /* opaque handle top of state structs */
78 static ddi_dma_attr_t fdc_dma_attr;
79 static ddi_device_acc_attr_t fdc_accattr = {DDI_DEVICE_ATTR_V0,
80 DDI_STRUCTURE_LE_ACC, DDI_STRICTORDER_ACC};
81
82 /*
83 * Local static data
84 */
85 #define OURUN_TRIES 12
86 static uchar_t rwretry = 4;
87 static uchar_t skretry = 3;
88 static uchar_t configurecmd[4] = {FO_CNFG, 0, 0x0F, 0};
89 static uchar_t recalcmd[2] = {FO_RECAL, 0};
90 static uchar_t senseintcmd = FO_SINT;
91
92 /*
93 * error handling
94 *
95 * for debugging, set rwretry and skretry = 1
96 * set fcerrlevel to 1
97 * set fcerrmask to 224 or 644
98 *
99 * after debug, set rwretry to 4, skretry to 3, and fcerrlevel to 5
100 * set fcerrmask to FDEM_ALL
101 * or remove the define DEBUG
102 */
103 static uint_t fcerrmask = FDEM_ALL;
104 static int fcerrlevel = 6;
105
106 #define KIOIP KSTAT_INTR_PTR(fcp->c_intrstat)
107
108
109 static xlate_tbl_t drate_mfm[] = {
110 { 250, 2},
111 { 300, 1},
112 { 417, 0},
113 { 500, 0},
114 { 1000, 3},
115 { 0, 0}
116 };
117
118 static xlate_tbl_t sector_size[] = {
119 { 256, 1},
120 { 512, 2},
121 { 1024, 3},
122 { 0, 2}
123 };
124
125 static xlate_tbl_t motor_onbits[] = {
126 { 0, 0x10},
127 { 1, 0x20},
128 { 2, 0x40},
129 { 3, 0x80},
130 { 0, 0x80}
131 };
132
133 static xlate_tbl_t step_rate[] = {
134 { 10, 0xF0}, /* for 500K data rate */
135 { 20, 0xE0},
136 { 30, 0xD0},
137 { 40, 0xC0},
138 { 50, 0xB0},
139 { 60, 0xA0},
140 { 70, 0x90},
141 { 80, 0x80},
142 { 90, 0x70},
143 { 100, 0x60},
144 { 110, 0x50},
145 { 120, 0x40},
146 { 130, 0x30},
147 { 140, 0x20},
148 { 150, 0x10},
149 { 160, 0x00},
150 { 0, 0x00}
151 };
152
153 #ifdef notdef
154 static xlate_tbl_t head_unld[] = {
155 { 16, 0x1}, /* for 500K data rate */
156 { 32, 0x2},
157 { 48, 0x3},
158 { 64, 0x4},
159 { 80, 0x5},
160 { 96, 0x6},
161 { 112, 0x7},
162 { 128, 0x8},
163 { 144, 0x9},
164 { 160, 0xA},
165 { 176, 0xB},
166 { 192, 0xC},
167 { 208, 0xD},
168 { 224, 0xE},
169 { 240, 0xF},
170 { 256, 0x0},
171 { 0, 0x0}
172 };
173 #endif
174
175 static struct fdcmdinfo {
176 char *cmdname; /* command name */
177 uchar_t ncmdbytes; /* number of bytes of command */
178 uchar_t nrsltbytes; /* number of bytes in result */
179 uchar_t cmdtype; /* characteristics */
180 } fdcmds[] = {
181 "", 0, 0, 0, /* - */
182 "", 0, 0, 0, /* - */
183 "read_track", 9, 7, 1, /* 2 */
184 "specify", 3, 0, 3, /* 3 */
185 "sense_drv_status", 2, 1, 3, /* 4 */
186 "write", 9, 7, 1, /* 5 */
187 "read", 9, 7, 1, /* 6 */
188 "recalibrate", 2, 0, 2, /* 7 */
189 "sense_int_status", 1, 2, 3, /* 8 */
190 "write_del", 9, 7, 1, /* 9 */
191 "read_id", 2, 7, 2, /* A */
192 "", 0, 0, 0, /* - */
193 "read_del", 9, 7, 1, /* C */
194 "format_track", 10, 7, 1, /* D */
195 "dump_reg", 1, 10, 4, /* E */
196 "seek", 3, 0, 2, /* F */
197 "version", 1, 1, 3, /* 10 */
198 "", 0, 0, 0, /* - */
199 "perp_mode", 2, 0, 3, /* 12 */
200 "configure", 4, 0, 4, /* 13 */
201 /* relative seek */
202 };
203
204
205 static int
206 fdc_bus_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
207 static int get_ioaddr(dev_info_t *dip, int *ioaddr);
208 static int get_unit(dev_info_t *dip, int *cntrl_num);
209
210 struct bus_ops fdc_bus_ops = {
211 BUSO_REV,
212 nullbusmap,
213 0, /* ddi_intrspec_t (*bus_get_intrspec)(); */
214 0, /* int (*bus_add_intrspec)(); */
215 0, /* void (*bus_remove_intrspec)(); */
216 i_ddi_map_fault,
217 ddi_dma_map,
218 ddi_dma_allochdl,
219 ddi_dma_freehdl,
220 ddi_dma_bindhdl,
221 ddi_dma_unbindhdl,
222 ddi_dma_flush,
223 ddi_dma_win,
224 ddi_dma_mctl,
225 fdc_bus_ctl,
226 ddi_bus_prop_op,
227 };
228
229 static int fdc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
230 static int fdc_probe(dev_info_t *);
231 static int fdc_attach(dev_info_t *, ddi_attach_cmd_t);
232 static int fdc_detach(dev_info_t *, ddi_detach_cmd_t);
233 static int fdc_quiesce(dev_info_t *);
234 static int fdc_enhance_probe(struct fdcntlr *fcp);
235
236 struct dev_ops fdc_ops = {
237 DEVO_REV, /* devo_rev, */
238 0, /* refcnt */
239 fdc_getinfo, /* getinfo */
240 nulldev, /* identify */
241 fdc_probe, /* probe */
242 fdc_attach, /* attach */
243 fdc_detach, /* detach */
244 nodev, /* reset */
245 (struct cb_ops *)0, /* driver operations */
246 &fdc_bus_ops, /* bus operations */
247 NULL, /* power */
248 fdc_quiesce, /* quiesce */
249 };
250
251 /*
252 * This is the loadable module wrapper.
253 */
254 #include <sys/modctl.h>
255
256 extern struct mod_ops mod_driverops;
257
258 static struct modldrv modldrv = {
259 &mod_driverops, /* Type of module. This one is a driver */
260 "Floppy Controller", /* Name of the module. */
261 &fdc_ops, /* Driver ops vector */
262 };
263
264 static struct modlinkage modlinkage = {
265 MODREV_1, (void *)&modldrv, NULL
266 };
267
268 int
_init(void)269 _init(void)
270 {
271 int retval;
272
273 if ((retval = ddi_soft_state_init(&fdc_state_head,
274 sizeof (struct fdcntlr) + NFDUN * sizeof (struct fcu_obj), 0)) != 0)
275 return (retval);
276
277 if ((retval = mod_install(&modlinkage)) != 0)
278 ddi_soft_state_fini(&fdc_state_head);
279 return (retval);
280 }
281
282 int
_fini(void)283 _fini(void)
284 {
285 int retval;
286
287 if ((retval = mod_remove(&modlinkage)) != 0)
288 return (retval);
289 ddi_soft_state_fini(&fdc_state_head);
290 return (retval);
291 }
292
293 int
_info(struct modinfo * modinfop)294 _info(struct modinfo *modinfop)
295 {
296 return (mod_info(&modlinkage, modinfop));
297 }
298
299
300 int fdc_abort(struct fcu_obj *);
301 int fdc_dkinfo(struct fcu_obj *, struct dk_cinfo *);
302 int fdc_select(struct fcu_obj *, int, int);
303 int fdgetchng(struct fcu_obj *, int);
304 int fdresetchng(struct fcu_obj *, int);
305 int fdrecalseek(struct fcu_obj *, int, int, int);
306 int fdrw(struct fcu_obj *, int, int, int, int, int, caddr_t, uint_t);
307 int fdtrkformat(struct fcu_obj *, int, int, int, int);
308 int fdrawioctl(struct fcu_obj *, int, caddr_t);
309
310 static struct fcobjops fdc_iops = {
311 fdc_abort, /* controller abort */
312 fdc_dkinfo, /* get disk controller info */
313
314 fdc_select, /* select / deselect unit */
315 fdgetchng, /* get media change */
316 fdresetchng, /* reset media change */
317 fdrecalseek, /* recal / seek */
318 NULL, /* read /write request (UNUSED) */
319 fdrw, /* read /write sector */
320 fdtrkformat, /* format track */
321 fdrawioctl /* raw ioctl */
322 };
323
324
325 /*
326 * Function prototypes
327 */
328 void encode(xlate_tbl_t *tablep, int val, uchar_t *rcode);
329 int decode(xlate_tbl_t *, int, int *);
330 static int fdc_propinit1(struct fdcntlr *, int);
331 static void fdc_propinit2(struct fdcntlr *);
332 void fdcquiesce(struct fdcntlr *);
333 int fdcsense_chng(struct fdcntlr *, int);
334 int fdcsense_drv(struct fdcntlr *, int);
335 int fdcsense_int(struct fdcntlr *, int *, int *);
336 int fdcspecify(struct fdcntlr *, int, int, int);
337 int fdcspdchange(struct fdcntlr *, struct fcu_obj *, int);
338 static int fdc_exec(struct fdcntlr *, int, int);
339 int fdcheckdisk(struct fdcntlr *, int);
340 static uint_t fdc_intr(caddr_t arg);
341 static void fdwatch(void *arg);
342 static void fdmotort(void *arg);
343 static int fdrecover(struct fdcntlr *);
344 static int fdc_motorsm(struct fcu_obj *, int, int);
345 static int fdc_statemach(struct fdcntlr *);
346 int fdc_docmd(struct fdcntlr *, uchar_t *, uchar_t);
347 int fdc_result(struct fdcntlr *, uchar_t *, uchar_t);
348
349
350 static int
fdc_bus_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)351 fdc_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
352 void *arg, void *result)
353 {
354 struct fdcntlr *fcp;
355 struct fcu_obj *fjp;
356
357 _NOTE(ARGUNUSED(result));
358
359 FCERRPRINT(FDEP_L0, FDEM_ATTA,
360 (CE_CONT, "fdc_bus_ctl: cmd= %x\n", ctlop));
361
362 if ((fcp = ddi_get_driver_private(dip)) == NULL)
363 return (DDI_FAILURE);
364
365 switch (ctlop) {
366
367 case DDI_CTLOPS_REPORTDEV:
368 cmn_err(CE_CONT, "?%s%d at %s%d\n",
369 ddi_get_name(rdip), ddi_get_instance(rdip),
370 ddi_get_name(dip), ddi_get_instance(dip));
371 FCERRPRINT(FDEP_L3, FDEM_ATTA,
372 (CE_WARN, "fdc_bus_ctl: report %s%d at %s%d",
373 ddi_get_name(rdip), ddi_get_instance(rdip),
374 ddi_get_name(dip), ddi_get_instance(dip)));
375 return (DDI_SUCCESS);
376
377 case DDI_CTLOPS_INITCHILD:
378 {
379 dev_info_t *udip = (dev_info_t *)arg;
380 int cntlr;
381 int len;
382 int unit;
383 char name[MAXNAMELEN];
384
385 FCERRPRINT(FDEP_L3, FDEM_ATTA,
386 (CE_WARN, "fdc_bus_ctl: init child 0x%p", (void*)udip));
387 cntlr = fcp->c_number;
388
389 len = sizeof (unit);
390 if (ddi_prop_op(DDI_DEV_T_ANY, udip, PROP_LEN_AND_VAL_BUF,
391 DDI_PROP_DONTPASS, "unit", (caddr_t)&unit, &len)
392 != DDI_PROP_SUCCESS ||
393 cntlr != FDCTLR(unit) ||
394 (fcp->c_unit[FDUNIT(unit)])->fj_dip)
395 return (DDI_NOT_WELL_FORMED);
396
397 (void) sprintf(name, "%d,%d", cntlr, FDUNIT(unit));
398 ddi_set_name_addr(udip, name);
399
400 fjp = fcp->c_unit[FDUNIT(unit)];
401 fjp->fj_unit = unit;
402 fjp->fj_dip = udip;
403 fjp->fj_ops = &fdc_iops;
404 fjp->fj_fdc = fcp;
405 fjp->fj_iblock = &fcp->c_iblock;
406
407 ddi_set_driver_private(udip, fjp);
408
409 return (DDI_SUCCESS);
410 }
411 case DDI_CTLOPS_UNINITCHILD:
412 {
413 dev_info_t *udip = (dev_info_t *)arg;
414
415 FCERRPRINT(FDEP_L3, FDEM_ATTA,
416 (CE_WARN, "fdc_bus_ctl: uninit child 0x%p", (void *)udip));
417 fjp = ddi_get_driver_private(udip);
418 ddi_set_driver_private(udip, NULL);
419 fjp->fj_dip = NULL;
420 ddi_set_name_addr(udip, NULL);
421 return (DDI_SUCCESS);
422 }
423 default:
424 return (DDI_FAILURE);
425 }
426 }
427
428 static int
fdc_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)429 fdc_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
430 {
431 struct fdcntlr *fcp;
432 int rval;
433
434 _NOTE(ARGUNUSED(dip));
435
436 switch (cmd) {
437 case DDI_INFO_DEVT2DEVINFO:
438 if (fcp = ddi_get_soft_state(fdc_state_head, (dev_t)arg)) {
439 *result = fcp->c_dip;
440 rval = DDI_SUCCESS;
441 break;
442 } else {
443 rval = DDI_FAILURE;
444 break;
445 }
446 case DDI_INFO_DEVT2INSTANCE:
447 *result = (void *)(uintptr_t)getminor((dev_t)arg);
448 rval = DDI_SUCCESS;
449 break;
450 default:
451 rval = DDI_FAILURE;
452 }
453 return (rval);
454 }
455
456 static int
fdc_probe(dev_info_t * dip)457 fdc_probe(dev_info_t *dip)
458 {
459 int debug[2];
460 int ioaddr;
461 int len;
462 uchar_t stat;
463
464 len = sizeof (debug);
465 if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
466 DDI_PROP_DONTPASS, "debug", (caddr_t)debug, &len) ==
467 DDI_PROP_SUCCESS) {
468 fcerrlevel = debug[0];
469 fcerrmask = (uint_t)debug[1];
470 }
471
472 FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_probe: dip %p",
473 (void*)dip));
474
475 if (get_ioaddr(dip, &ioaddr) != DDI_SUCCESS)
476 return (DDI_PROBE_FAILURE);
477
478 stat = inb(ioaddr + FCR_MSR);
479 if ((stat & (MS_RQM | MS_DIO | MS_CB)) != MS_RQM &&
480 (stat & ~MS_DIO) != MS_CB)
481 return (DDI_PROBE_FAILURE);
482
483 return (DDI_PROBE_SUCCESS);
484 }
485
486 static int
fdc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)487 fdc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
488 {
489 struct fdcntlr *fcp;
490 struct fcu_obj *fjp;
491 int cntlr_num, ctlr, unit;
492 int intr_set = 0;
493 int len;
494 char name[MAXNAMELEN];
495
496 FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_attach: dip %p",
497 (void*)dip));
498
499 switch (cmd) {
500 case DDI_ATTACH:
501 if (ddi_getprop
502 (DDI_DEV_T_ANY, dip, 0, "ignore-hardware-nodes", 0)) {
503 len = sizeof (cntlr_num);
504 if (ddi_prop_op(DDI_DEV_T_ANY, dip,
505 PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "unit",
506 (caddr_t)&cntlr_num, &len) != DDI_PROP_SUCCESS) {
507 FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN,
508 "fdc_attach failed: dip %p", (void*)dip));
509 return (DDI_FAILURE);
510 }
511 } else {
512 if (get_unit(dip, &cntlr_num) != DDI_SUCCESS)
513 return (DDI_FAILURE);
514 }
515
516 ctlr = ddi_get_instance(dip);
517 if (ddi_soft_state_zalloc(fdc_state_head, ctlr) != 0)
518 return (DDI_FAILURE);
519 fcp = ddi_get_soft_state(fdc_state_head, ctlr);
520
521 for (unit = 0, fjp = (struct fcu_obj *)(fcp+1);
522 unit < NFDUN; unit++) {
523 fcp->c_unit[unit] = fjp++;
524 }
525 fcp->c_dip = dip;
526
527 if (fdc_propinit1(fcp, cntlr_num) != DDI_SUCCESS)
528 goto no_attach;
529
530 /* get iblock cookie to initialize mutex used in the ISR */
531 if (ddi_get_iblock_cookie(dip, (uint_t)0, &fcp->c_iblock) !=
532 DDI_SUCCESS) {
533 cmn_err(CE_WARN,
534 "fdc_attach: cannot get iblock cookie");
535 goto no_attach;
536 }
537 mutex_init(&fcp->c_lock, NULL, MUTEX_DRIVER, fcp->c_iblock);
538 intr_set = 1;
539
540 /* setup interrupt handler */
541 if (ddi_add_intr(dip, (uint_t)0, NULL,
542 (ddi_idevice_cookie_t *)0, fdc_intr, (caddr_t)fcp) !=
543 DDI_SUCCESS) {
544 cmn_err(CE_WARN, "fdc: cannot add intr");
545 goto no_attach;
546 }
547 intr_set++;
548
549 /*
550 * acquire the DMA channel
551 * this assumes that the chnl is not shared; else allocate
552 * and free the chnl with each fdc request
553 */
554 if (ddi_dmae_alloc(dip, fcp->c_dmachan, DDI_DMA_DONTWAIT, NULL)
555 != DDI_SUCCESS) {
556 cmn_err(CE_WARN, "fdc: cannot acquire dma%d",
557 fcp->c_dmachan);
558 goto no_attach;
559 }
560 (void) ddi_dmae_getattr(dip, &fdc_dma_attr);
561 fdc_dma_attr.dma_attr_align = MMU_PAGESIZE;
562
563 mutex_init(&fcp->c_dorlock, NULL, MUTEX_DRIVER, fcp->c_iblock);
564 cv_init(&fcp->c_iocv, NULL, CV_DRIVER, fcp->c_iblock);
565 sema_init(&fcp->c_selsem, 1, NULL, SEMA_DRIVER, NULL);
566
567 (void) sprintf(name, "fdc%d", ctlr);
568 fcp->c_intrstat = kstat_create("fdc", ctlr, name,
569 "controller", KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT);
570 if (fcp->c_intrstat) {
571 kstat_install(fcp->c_intrstat);
572 }
573
574 ddi_set_driver_private(dip, fcp);
575
576 /*
577 * reset the controller
578 */
579 sema_p(&fcp->c_selsem);
580 mutex_enter(&fcp->c_lock);
581 fcp->c_csb.csb_xstate = FXS_RESET;
582 fcp->c_flags |= FCFLG_WAITING;
583 fdcquiesce(fcp);
584
585 /* first test for mode == Model 30 */
586 fcp->c_mode = (inb(fcp->c_regbase + FCR_SRB) & 0x1c) ?
587 FDCMODE_AT : FDCMODE_30;
588
589 while (fcp->c_flags & FCFLG_WAITING) {
590 cv_wait(&fcp->c_iocv, &fcp->c_lock);
591 }
592 mutex_exit(&fcp->c_lock);
593 sema_v(&fcp->c_selsem);
594
595 fdc_propinit2(fcp);
596
597 ddi_report_dev(dip);
598 return (DDI_SUCCESS);
599
600 case DDI_RESUME:
601
602 fcp = ddi_get_driver_private(dip);
603
604 mutex_enter(&fcp->c_lock);
605 fcp->c_suspended = B_FALSE;
606 fcp->c_csb.csb_xstate = FXS_RESET;
607 fcp->c_flags |= FCFLG_WAITING;
608 fdcquiesce(fcp);
609
610 while (fcp->c_flags & FCFLG_WAITING) {
611 cv_wait(&fcp->c_iocv, &fcp->c_lock);
612 }
613 mutex_exit(&fcp->c_lock);
614
615 /* should be good to go now */
616 sema_v(&fcp->c_selsem);
617
618 return (DDI_SUCCESS);
619 /* break; */
620
621 default:
622 return (DDI_FAILURE);
623 }
624
625 no_attach:
626 if (intr_set) {
627 if (intr_set > 1)
628 ddi_remove_intr(dip, 0, fcp->c_iblock);
629 mutex_destroy(&fcp->c_lock);
630 }
631 ddi_soft_state_free(fdc_state_head, cntlr_num);
632 return (DDI_FAILURE);
633 }
634
635 static int
fdc_propinit1(struct fdcntlr * fcp,int cntlr)636 fdc_propinit1(struct fdcntlr *fcp, int cntlr)
637 {
638 dev_info_t *dip;
639 int len;
640 int value;
641
642 dip = fcp->c_dip;
643 len = sizeof (value);
644
645 if (get_ioaddr(dip, &value) != DDI_SUCCESS)
646 return (DDI_FAILURE);
647
648 fcp->c_regbase = (ushort_t)value;
649
650 if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
651 DDI_PROP_DONTPASS, "dma-channels", (caddr_t)&value, &len)
652 != DDI_PROP_SUCCESS) {
653 cmn_err(CE_WARN,
654 "fdc_attach: Error, could not find a dma channel");
655 return (DDI_FAILURE);
656 }
657 fcp->c_dmachan = (ushort_t)value;
658 fcp->c_number = cntlr;
659 return (DDI_SUCCESS);
660 }
661
662 static void
fdc_propinit2(struct fdcntlr * fcp)663 fdc_propinit2(struct fdcntlr *fcp)
664 {
665 dev_info_t *dip;
666 int ccr;
667 int len;
668 int value;
669
670 dip = fcp->c_dip;
671 len = sizeof (value);
672
673 if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
674 DDI_PROP_DONTPASS, "chip", (caddr_t)&value, &len)
675 == DDI_PROP_SUCCESS)
676 fcp->c_chip = value;
677 else {
678 static uchar_t perpindcmd[2] = {FO_PERP, 0};
679 static uchar_t versioncmd = FO_VRSN;
680 uchar_t result;
681
682 fcp->c_chip = i8272A;
683 (void) fdc_docmd(fcp, &versioncmd, 1);
684 /*
685 * Ignored return. If failed, warning was issued by fdc_docmd.
686 * fdc_results retrieves the controller/drive status
687 */
688 if (!fdc_result(fcp, &result, 1) && result == 0x90) {
689 /*
690 * try a perpendicular_mode cmd to ensure
691 * that we really have an enhanced controller
692 */
693 if (fdc_docmd(fcp, perpindcmd, 2) ||
694 fdc_docmd(fcp, configurecmd, 4))
695 /*
696 * perpindicular_mode will be rejected by
697 * older controllers; make sure we don't hang.
698 */
699 (void) fdc_result(fcp, &result, 1);
700 /*
701 * Ignored return. If failed, warning was
702 * issued by fdc_result.
703 */
704 else
705 /* enhanced type controller */
706
707 if ((fcp->c_chip = fdc_enhance_probe(fcp)) == 0)
708 /* default enhanced cntlr */
709 fcp->c_chip = i82077;
710 }
711 (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip,
712 "chip", fcp->c_chip);
713 /*
714 * Ignoring return value because, for passed arguments, only
715 * DDI_SUCCESS is returned.
716 */
717 }
718 if (fcp->c_chip >= i82077 && fcp->c_mode == FDCMODE_30 &&
719 (inb(fcp->c_regbase + FCR_DIR) & 0x70) == 0)
720 for (ccr = 0; ccr <= (FCC_NOPREC | FCC_DRATE); ccr++) {
721 /*
722 * run through all the combinations of NOPREC and
723 * datarate selection, and see if they show up in the
724 * Model 30 DIR
725 */
726 outb(fcp->c_regbase + FCR_CCR, ccr);
727 drv_usecwait(5);
728 if ((inb(fcp->c_regbase + FCR_DIR) &
729 (FCC_NOPREC | FCC_DRATE)) != ccr) {
730 fcp->c_mode = FDCMODE_AT;
731 break;
732 }
733 }
734 else
735 fcp->c_mode = FDCMODE_AT;
736 outb(fcp->c_regbase + FCR_CCR, 0);
737 }
738
739 static int
fdc_enhance_probe(struct fdcntlr * fcp)740 fdc_enhance_probe(struct fdcntlr *fcp)
741 {
742 static uchar_t nsccmd = FO_NSC;
743 uint_t ddic;
744 int retcode = 0;
745 uchar_t result;
746 uchar_t save;
747
748 /*
749 * Try to identify the enhanced floppy controller.
750 * This is required so that we can program the DENSEL output to
751 * control 3D mode (1.0 MB, 1.6 MB and 2.0 MB unformatted capacity,
752 * 720 KB, 1.2 MB, and 1.44 MB formatted capacity) 3.5" dual-speed
753 * floppy drives. Refer to bugid 1195155.
754 */
755
756 (void) fdc_docmd(fcp, &nsccmd, 1);
757 /*
758 * Ignored return. If failed, warning was issued by fdc_docmd.
759 * fdc_results retrieves the controller/drive status
760 */
761 if (!fdc_result(fcp, &result, 1) && result != S0_IVCMD) {
762 /*
763 * only enhanced National Semi PC8477 core
764 * should respond to this command
765 */
766 if ((result & 0xf0) == 0x70) {
767 /* low 4 bits may change */
768 fcp->c_flags |= FCFLG_3DMODE;
769 retcode = PC87322;
770 } else
771 cmn_err(CE_CONT,
772 "?fdc: unidentified, enhanced, National Semiconductor cntlr %x\n", result);
773 } else {
774 save = inb(fcp->c_regbase + FCR_SRA);
775
776 do {
777 /* probe for motherboard version of SMC cntlr */
778
779 /* try to enable configuration mode */
780 ddic = ddi_enter_critical();
781 outb(fcp->c_regbase + FCR_SRA, FSA_ENA5);
782 outb(fcp->c_regbase + FCR_SRA, FSA_ENA5);
783 ddi_exit_critical(ddic);
784
785 outb(fcp->c_regbase + FCR_SRA, 0x0F);
786 if (inb(fcp->c_regbase + FCR_SRB) != 0x00)
787 /* always expect 0 from config reg F */
788 break;
789 outb(fcp->c_regbase + FCR_SRA, 0x0D);
790 if (inb(fcp->c_regbase + FCR_SRB) != 0x65)
791 /* expect 0x65 from config reg D */
792 break;
793 outb(fcp->c_regbase + FCR_SRA, 0x0E);
794 result = inb(fcp->c_regbase + FCR_SRB);
795 if (result != 0x02) {
796 /* expect revision level 2 from config reg E */
797 cmn_err(CE_CONT,
798 "?fdc: unidentified, enhanced, SMC cntlr revision %x\n", result);
799 /* break; */
800 }
801 fcp->c_flags |= FCFLG_3DMODE;
802 retcode = FDC37C665;
803 } while (retcode == 0);
804 outb(fcp->c_regbase + FCR_SRA, FSA_DISB);
805
806 while (retcode == 0) {
807 /* probe for adapter version of SMC cntlr */
808 ddic = ddi_enter_critical();
809 outb(fcp->c_regbase + FCR_SRA, FSA_ENA6);
810 outb(fcp->c_regbase + FCR_SRA, FSA_ENA6);
811 ddi_exit_critical(ddic);
812
813 outb(fcp->c_regbase + FCR_SRA, 0x0F);
814 if (inb(fcp->c_regbase + FCR_SRB) != 0x00)
815 /* always expect 0 from config reg F */
816 break;
817 outb(fcp->c_regbase + FCR_SRA, 0x0D);
818 if (inb(fcp->c_regbase + FCR_SRB) != 0x66)
819 /* expect 0x66 from config reg D */
820 break;
821 outb(fcp->c_regbase + FCR_SRA, 0x0E);
822 result = inb(fcp->c_regbase + FCR_SRB);
823 if (result != 0x02) {
824 /* expect revision level 2 from config reg E */
825 cmn_err(CE_CONT,
826 "?fdc: unidentified, enhanced, SMC cntlr revision %x\n", result);
827 /* break; */
828 }
829 fcp->c_flags |= FCFLG_3DMODE;
830 retcode = FDC37C666;
831 }
832 outb(fcp->c_regbase + FCR_SRA, FSA_DISB);
833
834 drv_usecwait(10);
835 outb(fcp->c_regbase + FCR_SRA, save);
836 }
837 return (retcode);
838 }
839
840 static int
fdc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)841 fdc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
842 {
843 struct fdcntlr *fcp;
844 int unit;
845 int rval = 0;
846
847 FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_detach: dip %p",
848 (void*)dip));
849
850 fcp = ddi_get_driver_private(dip);
851
852 switch (cmd) {
853 case DDI_DETACH:
854 for (unit = 0; unit < NFDUN; unit++)
855 if ((fcp->c_unit[unit])->fj_dip) {
856 rval = EBUSY;
857 break;
858 }
859 kstat_delete(fcp->c_intrstat);
860 fcp->c_intrstat = NULL;
861 ddi_remove_intr(fcp->c_dip, 0, fcp->c_iblock);
862 if (ddi_dmae_release(fcp->c_dip, fcp->c_dmachan) !=
863 DDI_SUCCESS)
864 cmn_err(CE_WARN, "fdc_detach: dma release failed, "
865 "dip %p, dmachan %x",
866 (void*)fcp->c_dip, fcp->c_dmachan);
867 ddi_prop_remove_all(fcp->c_dip);
868 ddi_set_driver_private(fcp->c_dip, NULL);
869
870 mutex_destroy(&fcp->c_lock);
871 mutex_destroy(&fcp->c_dorlock);
872 cv_destroy(&fcp->c_iocv);
873 sema_destroy(&fcp->c_selsem);
874 ddi_soft_state_free(fdc_state_head, ddi_get_instance(dip));
875 break;
876
877 case DDI_SUSPEND:
878 /*
879 * For suspend, we just use the semaphore to
880 * keep any child devices from accessing any of our
881 * hardware routines, and then shutdown the hardware.
882 *
883 * On resume, we'll reinit the hardware and release the
884 * semaphore.
885 */
886 sema_p(&fcp->c_selsem);
887
888 if (ddi_dmae_disable(fcp->c_dip, fcp->c_dmachan) !=
889 DDI_SUCCESS) {
890 cmn_err(CE_WARN, "fdc_suspend: dma disable failed, "
891 "dip %p, dmachan %x", (void *)fcp->c_dip,
892 fcp->c_dmachan);
893 /* give it back on failure */
894 sema_v(&fcp->c_selsem);
895 return (DDI_FAILURE);
896 }
897
898 mutex_enter(&fcp->c_lock);
899 fcp->c_suspended = B_TRUE;
900 mutex_exit(&fcp->c_lock);
901
902 rval = DDI_SUCCESS;
903 break;
904
905 default:
906 rval = EINVAL;
907 break;
908 }
909 return (rval);
910 }
911
912
913 int
fdc_abort(struct fcu_obj * fjp)914 fdc_abort(struct fcu_obj *fjp)
915 {
916 struct fdcntlr *fcp = fjp->fj_fdc;
917 int unit = fjp->fj_unit & 3;
918
919 FCERRPRINT(FDEP_L3, FDEM_RESE, (CE_WARN, "fdc_abort"));
920 if (fcp->c_curunit == unit) {
921 mutex_enter(&fcp->c_lock);
922 if (fcp->c_flags & FCFLG_WAITING) {
923 /*
924 * this can cause data corruption !
925 */
926 fdcquiesce(fcp);
927 fcp->c_csb.csb_xstate = FXS_RESET;
928 fcp->c_flags |= FCFLG_TIMEOUT;
929 if (ddi_dmae_stop(fcp->c_dip, fcp->c_dmachan) !=
930 DDI_SUCCESS)
931 cmn_err(CE_WARN,
932 "fdc_detach: dma release failed, "
933 "dip %p, dmachan %x",
934 (void*)fcp->c_dip, fcp->c_dmachan);
935 }
936 mutex_exit(&fcp->c_lock);
937 drv_usecwait(500);
938 return (DDI_SUCCESS);
939 }
940 return (DDI_FAILURE);
941 }
942
943 int
fdc_dkinfo(struct fcu_obj * fjp,struct dk_cinfo * dcp)944 fdc_dkinfo(struct fcu_obj *fjp, struct dk_cinfo *dcp)
945 {
946 struct fdcntlr *fcp = fjp->fj_fdc;
947
948 (void) strncpy((char *)&dcp->dki_cname, ddi_get_name(fcp->c_dip),
949 DK_DEVLEN);
950 dcp->dki_ctype = DKC_UNKNOWN; /* no code for generic PC/AT fdc */
951 dcp->dki_flags = DKI_FMTTRK;
952 dcp->dki_addr = fcp->c_regbase;
953 dcp->dki_space = 0;
954 dcp->dki_prio = fcp->c_intprio;
955 dcp->dki_vec = fcp->c_intvec;
956 (void) strncpy((char *)&dcp->dki_dname, ddi_driver_name(fjp->fj_dip),
957 DK_DEVLEN);
958 dcp->dki_slave = fjp->fj_unit & 3;
959 dcp->dki_maxtransfer = maxphys / DEV_BSIZE;
960 return (DDI_SUCCESS);
961 }
962
963 /*
964 * on=> non-zero = select, 0 = de-select
965 */
966 int
fdc_select(struct fcu_obj * fjp,int funit,int on)967 fdc_select(struct fcu_obj *fjp, int funit, int on)
968 {
969 struct fdcntlr *fcp = fjp->fj_fdc;
970 int unit = funit & 3;
971
972 if (on) {
973 /* possess controller */
974 sema_p(&fcp->c_selsem);
975 FCERRPRINT(FDEP_L2, FDEM_DSEL,
976 (CE_NOTE, "fdc_select unit %d: on", funit));
977
978 if (fcp->c_curunit != unit || !(fjp->fj_flags & FUNIT_CHAROK)) {
979 fcp->c_curunit = unit;
980 fjp->fj_flags |= FUNIT_CHAROK;
981 if (fdcspecify(fcp,
982 fjp->fj_chars->fdc_transfer_rate,
983 fjp->fj_drive->fdd_steprate, 40))
984 cmn_err(CE_WARN,
985 "fdc_select: controller setup rejected "
986 "fdcntrl %p transfer rate %x step rate %x"
987 " head load time 40", (void*)fcp,
988 fjp->fj_chars->fdc_transfer_rate,
989 fjp->fj_drive->fdd_steprate);
990 }
991
992 mutex_enter(&fcp->c_dorlock);
993
994 /* make sure drive is not selected in case we change speed */
995 fcp->c_digout = (fcp->c_digout & ~FD_DRSEL) |
996 (~unit & FD_DRSEL);
997 outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
998
999 (void) fdc_motorsm(fjp, FMI_STARTCMD,
1000 fjp->fj_drive->fdd_motoron);
1001 /*
1002 * Return value ignored - fdcmotort deals with failure.
1003 */
1004 if (fdcspdchange(fcp, fjp, fjp->fj_attr->fda_rotatespd)) {
1005 /* 3D drive requires 500 ms for speed change */
1006 (void) fdc_motorsm(fjp, FMI_RSTARTCMD, 5);
1007 /*
1008 * Return value ignored - fdcmotort deals with failure.
1009 */
1010 }
1011
1012 fcp->c_digout = (fcp->c_digout & ~FD_DRSEL) | (unit & FD_DRSEL);
1013 outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
1014
1015 mutex_exit(&fcp->c_dorlock);
1016 fcp->c_csb.csb_drive = (uchar_t)unit;
1017 } else {
1018 FCERRPRINT(FDEP_L2, FDEM_DSEL,
1019 (CE_NOTE, "fdc_select unit %d: off", funit));
1020
1021 mutex_enter(&fcp->c_dorlock);
1022
1023 fcp->c_digout |= FD_DRSEL;
1024 outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
1025 (void) fdc_motorsm(fjp, FMI_IDLECMD,
1026 fjp->fj_drive->fdd_motoroff);
1027 /*
1028 * Return value ignored - fdcmotort deals with failure.
1029 */
1030
1031 mutex_exit(&fcp->c_dorlock);
1032
1033 /* give up controller */
1034 sema_v(&fcp->c_selsem);
1035 }
1036 return (0);
1037 }
1038
1039
1040 int
fdgetchng(struct fcu_obj * fjp,int funit)1041 fdgetchng(struct fcu_obj *fjp, int funit)
1042 {
1043 if (fdcsense_drv(fjp->fj_fdc, funit & 3))
1044 cmn_err(CE_WARN, "fdgetchng: write protect check failed");
1045 return (fdcsense_chng(fjp->fj_fdc, funit & 3));
1046 }
1047
1048
1049 int
fdresetchng(struct fcu_obj * fjp,int funit)1050 fdresetchng(struct fcu_obj *fjp, int funit)
1051 {
1052 struct fdcntlr *fcp = fjp->fj_fdc;
1053 int unit = funit & 3;
1054 int newcyl; /* where to seek for reset of DSKCHG */
1055
1056 FCERRPRINT(FDEP_L2, FDEM_CHEK, (CE_NOTE, "fdmediachng unit %d", funit));
1057
1058 if (fcp->c_curpcyl[unit])
1059 newcyl = fcp->c_curpcyl[unit] - 1;
1060 else
1061 newcyl = 1;
1062 return (fdrecalseek(fjp, funit, newcyl, 0));
1063 }
1064
1065
1066 /*
1067 * fdrecalseek
1068 */
1069 int
fdrecalseek(struct fcu_obj * fjp,int funit,int arg,int execflg)1070 fdrecalseek(struct fcu_obj *fjp, int funit, int arg, int execflg)
1071 {
1072 struct fdcntlr *fcp = fjp->fj_fdc;
1073 struct fdcsb *csb;
1074 int unit = funit & 3;
1075 int rval;
1076
1077 FCERRPRINT(FDEP_L2, FDEM_RECA, (CE_NOTE, "fdrecalseek unit %d to %d",
1078 funit, arg));
1079
1080 csb = &fcp->c_csb;
1081 csb->csb_cmd[1] = (uchar_t)unit;
1082 if (arg < 0) { /* is recal... */
1083 *csb->csb_cmd = FO_RECAL;
1084 csb->csb_ncmds = 2;
1085 csb->csb_timer = 28;
1086 } else {
1087 *csb->csb_cmd = FO_SEEK;
1088 csb->csb_cmd[2] = (uchar_t)arg;
1089 csb->csb_ncmds = 3;
1090 csb->csb_timer = 10;
1091 }
1092 csb->csb_nrslts = 2; /* 2 for SENSE INTERRUPTS */
1093 csb->csb_opflags = CSB_OFINRPT;
1094 csb->csb_maxretry = skretry;
1095 csb->csb_dmahandle = NULL;
1096 csb->csb_handle_bound = 0;
1097 csb->csb_dmacookiecnt = 0;
1098 csb->csb_dmacurrcookie = 0;
1099 csb->csb_dmawincnt = 0;
1100 csb->csb_dmacurrwin = 0;
1101
1102 /* send cmd off to fdc_exec */
1103 if (rval = fdc_exec(fcp, 1, execflg))
1104 goto out;
1105
1106 if (!(*csb->csb_rslt & S0_SEKEND) ||
1107 (*csb->csb_rslt & S0_ICMASK) ||
1108 ((*csb->csb_rslt & S0_ECHK) && arg < 0) ||
1109 csb->csb_cmdstat)
1110 rval = ENODEV;
1111
1112 if (fdcsense_drv(fcp, unit))
1113 cmn_err(CE_WARN, "fdgetchng: write protect check failed");
1114 out:
1115 return (rval);
1116 }
1117
1118
1119 /*
1120 * fdrw- used only for read/writing sectors into/from kernel buffers.
1121 */
1122 int
fdrw(struct fcu_obj * fjp,int funit,int rw,int cyl,int head,int sector,caddr_t bufp,uint_t len)1123 fdrw(struct fcu_obj *fjp, int funit, int rw, int cyl, int head,
1124 int sector, caddr_t bufp, uint_t len)
1125 {
1126 struct fdcntlr *fcp = fjp->fj_fdc;
1127 struct fdcsb *csb;
1128 uint_t dmar_flags = 0;
1129 int unit = funit & 3;
1130 int rval;
1131 ddi_acc_handle_t mem_handle = NULL;
1132 caddr_t aligned_buf;
1133 size_t real_size;
1134
1135 FCERRPRINT(FDEP_L1, FDEM_RW, (CE_CONT, "fdrw unit %d\n", funit));
1136
1137 csb = &fcp->c_csb;
1138 if (rw) {
1139 dmar_flags = DDI_DMA_READ;
1140 csb->csb_opflags = CSB_OFDMARD | CSB_OFINRPT;
1141 *csb->csb_cmd = FO_MT | FO_MFM | FO_SK | FO_RDDAT;
1142 } else { /* write */
1143 dmar_flags = DDI_DMA_WRITE;
1144 csb->csb_opflags = CSB_OFDMAWT | CSB_OFINRPT;
1145 *csb->csb_cmd = FO_MT | FO_MFM | FO_WRDAT;
1146 }
1147 csb->csb_cmd[1] = (uchar_t)(unit | ((head & 0x1) << 2));
1148 csb->csb_cmd[2] = (uchar_t)cyl;
1149 csb->csb_cmd[3] = (uchar_t)head;
1150 csb->csb_cmd[4] = (uchar_t)sector;
1151 encode(sector_size, fjp->fj_chars->fdc_sec_size,
1152 &csb->csb_cmd[5]);
1153 csb->csb_cmd[6] = (uchar_t)max(fjp->fj_chars->fdc_secptrack, sector);
1154 csb->csb_cmd[7] = fjp->fj_attr->fda_gapl;
1155 csb->csb_cmd[8] = 0xFF;
1156
1157 csb->csb_ncmds = 9;
1158 csb->csb_nrslts = 7;
1159 csb->csb_timer = 36;
1160 if (rw == FDRDONE)
1161 csb->csb_maxretry = 1;
1162 else
1163 csb->csb_maxretry = rwretry;
1164
1165 csb->csb_dmahandle = NULL;
1166 csb->csb_handle_bound = 0;
1167 csb->csb_dmacookiecnt = 0;
1168 csb->csb_dmacurrcookie = 0;
1169 csb->csb_dmawincnt = 0;
1170 csb->csb_dmacurrwin = 0;
1171 dmar_flags |= (DDI_DMA_STREAMING | DDI_DMA_PARTIAL);
1172
1173 if (ddi_dma_alloc_handle(fcp->c_dip, &fdc_dma_attr, DDI_DMA_SLEEP,
1174 0, &csb->csb_dmahandle) != DDI_SUCCESS) {
1175 rval = EINVAL;
1176 goto out;
1177 }
1178
1179 /*
1180 * allocate a page aligned buffer to dma to/from. This way we can
1181 * ensure the cookie is a whole multiple of granularity and avoids
1182 * any alignment issues.
1183 */
1184 rval = ddi_dma_mem_alloc(csb->csb_dmahandle, len, &fdc_accattr,
1185 DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &aligned_buf,
1186 &real_size, &mem_handle);
1187 if (rval != DDI_SUCCESS) {
1188 rval = EINVAL;
1189 goto out;
1190 }
1191
1192 if (dmar_flags & DDI_DMA_WRITE) {
1193 bcopy(bufp, aligned_buf, len);
1194 }
1195
1196 rval = ddi_dma_addr_bind_handle(csb->csb_dmahandle, NULL, aligned_buf,
1197 len, dmar_flags, DDI_DMA_SLEEP, 0, &csb->csb_dmacookie,
1198 &csb->csb_dmacookiecnt);
1199
1200 if (rval == DDI_DMA_MAPPED) {
1201 csb->csb_dmawincnt = 1;
1202 csb->csb_handle_bound = 1;
1203 } else if (rval == DDI_DMA_PARTIAL_MAP) {
1204 csb->csb_handle_bound = 1;
1205 if (ddi_dma_numwin(csb->csb_dmahandle, &csb->csb_dmawincnt) !=
1206 DDI_SUCCESS) {
1207 cmn_err(CE_WARN, "fdrw: dma numwin failed");
1208 rval = EINVAL;
1209 goto out;
1210 }
1211 } else {
1212 cmn_err(CE_WARN,
1213 "fdrw: dma addr bind handle failed, rval = %d", rval);
1214 rval = EINVAL;
1215 goto out;
1216 }
1217 rval = fdc_exec(fcp, 1, 1);
1218
1219 if (dmar_flags & DDI_DMA_READ) {
1220 bcopy(aligned_buf, bufp, len);
1221 }
1222
1223 out:
1224 if (csb->csb_dmahandle) {
1225 if (csb->csb_handle_bound) {
1226 if (ddi_dma_unbind_handle(csb->csb_dmahandle) !=
1227 DDI_SUCCESS)
1228 cmn_err(CE_WARN, "fdrw: "
1229 "dma unbind handle failed");
1230 csb->csb_handle_bound = 0;
1231 }
1232 if (mem_handle != NULL) {
1233 ddi_dma_mem_free(&mem_handle);
1234 }
1235 ddi_dma_free_handle(&csb->csb_dmahandle);
1236 csb->csb_dmahandle = NULL;
1237 }
1238 return (rval);
1239 }
1240
1241
1242 int
fdtrkformat(struct fcu_obj * fjp,int funit,int cyl,int head,int filldata)1243 fdtrkformat(struct fcu_obj *fjp, int funit, int cyl, int head, int filldata)
1244 {
1245 struct fdcntlr *fcp = fjp->fj_fdc;
1246 struct fdcsb *csb;
1247 int unit = funit & 3;
1248 int fmdatlen, lsector, lstart;
1249 int interleave, numsctr, offset, psector;
1250 uchar_t *dp;
1251 int rval;
1252 ddi_acc_handle_t mem_handle = NULL;
1253 caddr_t aligned_buf;
1254 size_t real_size;
1255
1256 FCERRPRINT(FDEP_L2, FDEM_FORM,
1257 (CE_NOTE, "fdformattrk unit %d cyl=%d, hd=%d", funit, cyl, head));
1258
1259 csb = &fcp->c_csb;
1260
1261 csb->csb_opflags = CSB_OFDMAWT | CSB_OFINRPT;
1262
1263 *csb->csb_cmd = FO_FRMT | FO_MFM;
1264 csb->csb_cmd[1] = (head << 2) | unit;
1265 encode(sector_size, fjp->fj_chars->fdc_sec_size,
1266 &csb->csb_cmd[2]);
1267 csb->csb_cmd[3] = numsctr = fjp->fj_chars->fdc_secptrack;
1268 csb->csb_cmd[4] = fjp->fj_attr->fda_gapf;
1269 csb->csb_cmd[5] = (uchar_t)filldata;
1270
1271 csb->csb_npcyl = (uchar_t)(cyl * fjp->fj_chars->fdc_steps);
1272
1273 csb->csb_dmahandle = NULL;
1274 csb->csb_handle_bound = 0;
1275 csb->csb_dmacookiecnt = 0;
1276 csb->csb_dmacurrcookie = 0;
1277 csb->csb_dmawincnt = 0;
1278 csb->csb_dmacurrwin = 0;
1279 csb->csb_ncmds = 6;
1280 csb->csb_nrslts = 7;
1281 csb->csb_timer = 32;
1282 csb->csb_maxretry = rwretry;
1283
1284 /*
1285 * alloc space for format track cmd
1286 */
1287 /*
1288 * NOTE: have to add size of fifo also - for dummy format action
1289 */
1290 fmdatlen = 4 * numsctr;
1291
1292 if (ddi_dma_alloc_handle(fcp->c_dip, &fdc_dma_attr, DDI_DMA_SLEEP,
1293 0, &csb->csb_dmahandle) != DDI_SUCCESS) {
1294 rval = EINVAL;
1295 goto out;
1296 }
1297
1298 /*
1299 * allocate a page aligned buffer to dma to/from. This way we can
1300 * ensure the cookie is a whole multiple of granularity and avoids
1301 * any alignment issues.
1302 */
1303 rval = ddi_dma_mem_alloc(csb->csb_dmahandle, fmdatlen, &fdc_accattr,
1304 DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &aligned_buf,
1305 &real_size, &mem_handle);
1306 if (rval != DDI_SUCCESS) {
1307 rval = EINVAL;
1308 goto out;
1309 }
1310 dp = (uchar_t *)aligned_buf;
1311
1312 interleave = fjp->fj_attr->fda_intrlv;
1313 offset = (numsctr + interleave - 1) / interleave;
1314 for (psector = lstart = 1;
1315 psector <= numsctr; psector += interleave, lstart++) {
1316 for (lsector = lstart; lsector <= numsctr; lsector += offset) {
1317 *dp++ = (uchar_t)cyl;
1318 *dp++ = (uchar_t)head;
1319 *dp++ = (uchar_t)lsector;
1320 *dp++ = csb->csb_cmd[2];
1321 }
1322 }
1323
1324 rval = ddi_dma_addr_bind_handle(csb->csb_dmahandle, NULL, aligned_buf,
1325 fmdatlen, DDI_DMA_WRITE | DDI_DMA_STREAMING | DDI_DMA_PARTIAL,
1326 DDI_DMA_SLEEP, 0, &csb->csb_dmacookie, &csb->csb_dmacookiecnt);
1327
1328 if (rval == DDI_DMA_MAPPED) {
1329 csb->csb_dmawincnt = 1;
1330 csb->csb_handle_bound = 1;
1331 } else if (rval == DDI_DMA_PARTIAL_MAP) {
1332 csb->csb_handle_bound = 1;
1333 if (ddi_dma_numwin(csb->csb_dmahandle, &csb->csb_dmawincnt) !=
1334 DDI_SUCCESS) {
1335 cmn_err(CE_WARN, "fdtrkformat: dma numwin failed");
1336 rval = EINVAL;
1337 goto out;
1338 }
1339 } else {
1340 cmn_err(CE_WARN,
1341 "fdtrkformat: dma buf bind handle failed, rval = %d",
1342 rval);
1343 rval = EINVAL;
1344 goto out;
1345 }
1346
1347 rval = fdc_exec(fcp, 1, 1);
1348 out:
1349 if (csb->csb_dmahandle) {
1350 if (csb->csb_handle_bound) {
1351 if (ddi_dma_unbind_handle(csb->csb_dmahandle) !=
1352 DDI_SUCCESS)
1353 cmn_err(CE_WARN, "fdtrkformat: "
1354 "dma unbind handle failed");
1355 csb->csb_handle_bound = 0;
1356 }
1357 if (mem_handle != NULL) {
1358 ddi_dma_mem_free(&mem_handle);
1359 }
1360 ddi_dma_free_handle(&csb->csb_dmahandle);
1361 csb->csb_dmahandle = NULL;
1362 }
1363 return (rval);
1364 }
1365
1366 int
fdrawioctl(struct fcu_obj * fjp,int funit,caddr_t arg)1367 fdrawioctl(struct fcu_obj *fjp, int funit, caddr_t arg)
1368 {
1369 struct fdcntlr *fcp = fjp->fj_fdc;
1370 struct fd_raw *fdrp = (struct fd_raw *)arg;
1371 struct fdcsb *csb;
1372 uint_t dmar_flags = 0;
1373 int i;
1374 int change = 1;
1375 int sleep = 1;
1376 int rval = 0;
1377 int rval_exec = 0;
1378 ddi_acc_handle_t mem_handle = NULL;
1379 caddr_t aligned_buf;
1380 size_t real_size;
1381
1382 _NOTE(ARGUNUSED(funit));
1383
1384 FCERRPRINT(FDEP_L2, FDEM_RAWI,
1385 (CE_NOTE, "fdrawioctl: cmd[0]=0x%x", fdrp->fdr_cmd[0]));
1386
1387 csb = &fcp->c_csb;
1388
1389 /* copy cmd bytes into csb */
1390 for (i = 0; i <= fdrp->fdr_cnum; i++)
1391 csb->csb_cmd[i] = fdrp->fdr_cmd[i];
1392 csb->csb_ncmds = (uchar_t)fdrp->fdr_cnum;
1393
1394 csb->csb_maxretry = 0; /* let the application deal with errors */
1395 csb->csb_opflags = CSB_OFRAWIOCTL;
1396 csb->csb_nrslts = 0;
1397 csb->csb_timer = 50;
1398
1399 switch (fdrp->fdr_cmd[0] & 0x0f) {
1400
1401 case FO_SEEK:
1402 change = 0;
1403 /* FALLTHROUGH */
1404 case FO_RECAL:
1405 csb->csb_opflags |= CSB_OFINRPT;
1406 break;
1407
1408 case FO_FRMT:
1409 csb->csb_npcyl = *(uchar_t *)(fdrp->fdr_addr) *
1410 fjp->fj_chars->fdc_steps;
1411 /* FALLTHROUGH */
1412 case FO_WRDAT:
1413 case FO_WRDEL:
1414 csb->csb_opflags |= CSB_OFDMAWT | CSB_OFRESLT | CSB_OFINRPT;
1415 csb->csb_nrslts = 7;
1416 if (fdrp->fdr_nbytes == 0)
1417 return (EINVAL);
1418 dmar_flags = DDI_DMA_WRITE;
1419 break;
1420
1421 case FO_RDDAT:
1422 case FO_RDDEL:
1423 case FO_RDTRK:
1424 csb->csb_opflags |= CSB_OFDMARD | CSB_OFRESLT | CSB_OFINRPT;
1425 csb->csb_nrslts = 7;
1426 dmar_flags = DDI_DMA_READ;
1427 break;
1428
1429 case FO_RDID:
1430 csb->csb_opflags |= CSB_OFRESLT | CSB_OFINRPT;
1431 csb->csb_nrslts = 7;
1432 break;
1433
1434 case FO_SDRV:
1435 sleep = 0;
1436 csb->csb_nrslts = 1;
1437 break;
1438
1439 case FO_SINT:
1440 sleep = 0;
1441 change = 0;
1442 csb->csb_nrslts = 2;
1443 break;
1444
1445 case FO_SPEC:
1446 sleep = 0;
1447 change = 0;
1448 break;
1449
1450 default:
1451 return (EINVAL);
1452 }
1453
1454 csb->csb_dmahandle = NULL;
1455 csb->csb_handle_bound = 0;
1456 csb->csb_dmacookiecnt = 0;
1457 csb->csb_dmacurrcookie = 0;
1458 csb->csb_dmawincnt = 0;
1459 csb->csb_dmacurrwin = 0;
1460
1461 if (csb->csb_opflags & (CSB_OFDMARD | CSB_OFDMAWT)) {
1462 if (ddi_dma_alloc_handle(fcp->c_dip, &fdc_dma_attr,
1463 DDI_DMA_SLEEP, 0, &csb->csb_dmahandle) != DDI_SUCCESS) {
1464 rval = EINVAL;
1465 goto out;
1466 }
1467
1468 /*
1469 * allocate a page aligned buffer to dma to/from. This way we
1470 * can ensure the cookie is a whole multiple of granularity and
1471 * avoids any alignment issues.
1472 */
1473 rval = ddi_dma_mem_alloc(csb->csb_dmahandle,
1474 (uint_t)fdrp->fdr_nbytes, &fdc_accattr, DDI_DMA_CONSISTENT,
1475 DDI_DMA_SLEEP, NULL, &aligned_buf, &real_size, &mem_handle);
1476 if (rval != DDI_SUCCESS) {
1477 rval = EINVAL;
1478 goto out;
1479 }
1480
1481 if (dmar_flags & DDI_DMA_WRITE) {
1482 bcopy(fdrp->fdr_addr, aligned_buf,
1483 (uint_t)fdrp->fdr_nbytes);
1484 }
1485
1486 dmar_flags |= (DDI_DMA_STREAMING | DDI_DMA_PARTIAL);
1487 rval = ddi_dma_addr_bind_handle(csb->csb_dmahandle, NULL,
1488 aligned_buf, (uint_t)fdrp->fdr_nbytes, dmar_flags,
1489 DDI_DMA_SLEEP, 0, &csb->csb_dmacookie,
1490 &csb->csb_dmacookiecnt);
1491
1492 if (rval == DDI_DMA_MAPPED) {
1493 csb->csb_dmawincnt = 1;
1494 csb->csb_handle_bound = 1;
1495 } else if (rval == DDI_DMA_PARTIAL_MAP) {
1496 csb->csb_handle_bound = 1;
1497 if (ddi_dma_numwin(csb->csb_dmahandle,
1498 &csb->csb_dmawincnt) != DDI_SUCCESS) {
1499 cmn_err(CE_WARN,
1500 "fdrawioctl: dma numwin failed");
1501 rval = EINVAL;
1502 goto out;
1503 }
1504 } else {
1505 cmn_err(CE_WARN, "fdrawioctl: "
1506 "dma buf bind handle failed, rval = %d", rval);
1507 rval = EINVAL;
1508 goto out;
1509 }
1510 }
1511
1512 FCERRPRINT(FDEP_L1, FDEM_RAWI,
1513 (CE_CONT, "cmd: %x %x %x %x %x %x %x %x %x %x\n", csb->csb_cmd[0],
1514 csb->csb_cmd[1], csb->csb_cmd[2], csb->csb_cmd[3],
1515 csb->csb_cmd[4], csb->csb_cmd[5], csb->csb_cmd[6],
1516 csb->csb_cmd[7], csb->csb_cmd[8], csb->csb_cmd[9]));
1517 FCERRPRINT(FDEP_L1, FDEM_RAWI,
1518 (CE_CONT, "nbytes: %x, opflags: %x, addr: %p, len: %x\n",
1519 csb->csb_ncmds, csb->csb_opflags, (void *)fdrp->fdr_addr,
1520 fdrp->fdr_nbytes));
1521
1522 /*
1523 * Note that we ignore any error returns from fdexec.
1524 * This is the way the driver has been, and it may be
1525 * that the raw ioctl senders simply don't want to
1526 * see any errors returned in this fashion.
1527 */
1528
1529 /*
1530 * VP/ix sense drive ioctl call checks for the error return.
1531 */
1532
1533 rval_exec = fdc_exec(fcp, sleep, change);
1534
1535 if (dmar_flags & DDI_DMA_READ) {
1536 bcopy(aligned_buf, fdrp->fdr_addr, (uint_t)fdrp->fdr_nbytes);
1537 }
1538
1539 FCERRPRINT(FDEP_L1, FDEM_RAWI,
1540 (CE_CONT, "rslt: %x %x %x %x %x %x %x %x %x %x\n", csb->csb_rslt[0],
1541 csb->csb_rslt[1], csb->csb_rslt[2], csb->csb_rslt[3],
1542 csb->csb_rslt[4], csb->csb_rslt[5], csb->csb_rslt[6],
1543 csb->csb_rslt[7], csb->csb_rslt[8], csb->csb_rslt[9]));
1544
1545 /* copy results into fdr */
1546 for (i = 0; i <= (int)csb->csb_nrslts; i++)
1547 fdrp->fdr_result[i] = csb->csb_rslt[i];
1548 /* fdrp->fdr_nbytes = fdc->c_csb.csb_rlen; return resid */
1549
1550 out:
1551 if (csb->csb_dmahandle) {
1552 if (csb->csb_handle_bound) {
1553 if (ddi_dma_unbind_handle(csb->csb_dmahandle) !=
1554 DDI_SUCCESS)
1555 cmn_err(CE_WARN, "fdrawioctl: "
1556 "dma unbind handle failed");
1557 csb->csb_handle_bound = 0;
1558 }
1559 if (mem_handle != NULL) {
1560 ddi_dma_mem_free(&mem_handle);
1561 }
1562 ddi_dma_free_handle(&csb->csb_dmahandle);
1563 csb->csb_dmahandle = NULL;
1564 }
1565 if ((fdrp->fdr_cmd[0] & 0x0f) == FO_SDRV) {
1566 return (rval_exec);
1567 }
1568 return (rval);
1569 }
1570
1571 void
encode(xlate_tbl_t * tablep,int val,uchar_t * rcode)1572 encode(xlate_tbl_t *tablep, int val, uchar_t *rcode)
1573 {
1574 do {
1575 if (tablep->value >= val) {
1576 *rcode = tablep->code;
1577 return;
1578 }
1579 } while ((++tablep)->value);
1580 *rcode = tablep->code;
1581 cmn_err(CE_WARN, "fdc encode failed, table %p val %x code %x",
1582 (void *)tablep, val, (uint_t)*rcode);
1583 }
1584
1585 int
decode(xlate_tbl_t * tablep,int kode,int * rvalue)1586 decode(xlate_tbl_t *tablep, int kode, int *rvalue)
1587 {
1588 do {
1589 if (tablep->code == kode) {
1590 *rvalue = tablep->value;
1591 return (0);
1592 }
1593 } while ((++tablep)->value);
1594 return (-1);
1595 }
1596
1597 /*
1598 * quiesce(9E) entry point.
1599 *
1600 * This function is called when the system is single-threaded at high
1601 * PIL with preemption disabled. Therefore, this function must not be
1602 * blocked.
1603 *
1604 * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
1605 * DDI_FAILURE indicates an error condition and should almost never happen.
1606 */
1607 int
fdc_quiesce(dev_info_t * dip)1608 fdc_quiesce(dev_info_t *dip)
1609 {
1610 struct fdcntlr *fcp;
1611 int ctlr = ddi_get_instance(dip);
1612 int unit;
1613
1614 fcp = ddi_get_soft_state(fdc_state_head, ctlr);
1615
1616 if (fcp == NULL)
1617 return (DDI_FAILURE);
1618
1619 /*
1620 * If no FD units are attached, there is no need to quiesce.
1621 */
1622 for (unit = 0; unit < NFDUN; unit++) {
1623 struct fcu_obj *fjp = fcp->c_unit[unit];
1624 if (fjp->fj_flags & FUNIT_DRVATCH) {
1625 break;
1626 }
1627 }
1628
1629 if (unit == NFDUN)
1630 return (DDI_SUCCESS);
1631
1632 (void) ddi_dmae_disable(fcp->c_dip, fcp->c_dmachan);
1633
1634 fcp->c_digout = (fcp->c_digout & (FD_DMTREN | FD_DRSEL)) | FD_ENABLE;
1635 outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
1636 drv_usecwait(20);
1637 fcp->c_digout |= FD_RSETZ;
1638 outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
1639
1640 if (fcp->c_chip >= i82077) {
1641 int count = 4;
1642 uchar_t *oplistp = configurecmd;
1643 do {
1644 int ntries = FDC_RQM_RETRY;
1645 do {
1646 if ((inb(fcp->c_regbase + FCR_MSR) &
1647 (MS_RQM|MS_DIO)) == MS_RQM)
1648 break;
1649 else
1650 drv_usecwait(1);
1651 } while (--ntries);
1652 if (ntries == 0) {
1653 break;
1654 }
1655 outb(fcp->c_regbase + FCR_DATA, *oplistp++);
1656 drv_usecwait(16); /* See comment in fdc_result() */
1657 } while (--count);
1658 }
1659
1660 return (DDI_SUCCESS);
1661 }
1662
1663 void
fdcquiesce(struct fdcntlr * fcp)1664 fdcquiesce(struct fdcntlr *fcp)
1665 {
1666 int unit;
1667
1668 FCERRPRINT(FDEP_L2, FDEM_RESE, (CE_NOTE, "fdcquiesce fcp %p",
1669 (void*)fcp));
1670
1671 ASSERT(MUTEX_HELD(&fcp->c_lock));
1672 mutex_enter(&fcp->c_dorlock);
1673
1674 if (ddi_dmae_stop(fcp->c_dip, fcp->c_dmachan) != DDI_SUCCESS)
1675 cmn_err(CE_WARN, "fdcquiesce: dmae stop failed, "
1676 "dip %p, dmachan %x",
1677 (void*)fcp->c_dip, fcp->c_dmachan);
1678
1679 fcp->c_digout = (fcp->c_digout & (FD_DMTREN | FD_DRSEL)) | FD_ENABLE;
1680 outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
1681 drv_usecwait(20);
1682 fcp->c_digout |= FD_RSETZ;
1683 outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
1684
1685 mutex_exit(&fcp->c_dorlock);
1686
1687 /* count resets */
1688 fcp->fdstats.reset++;
1689 fcp->c_curunit = -1;
1690 for (unit = 0; unit < NFDUN; unit++)
1691 fcp->c_curpcyl[unit] = -1;
1692
1693 if (fcp->c_chip >= i82077) {
1694 (void) fdc_docmd(fcp, configurecmd, 4);
1695 /*
1696 * Ignored return. If failed, warning was issued by fdc_docmd.
1697 */
1698 }
1699 }
1700
1701 void
fdcreadid(struct fdcntlr * fcp,struct fdcsb * csb)1702 fdcreadid(struct fdcntlr *fcp, struct fdcsb *csb)
1703 {
1704 static uchar_t readidcmd[2] = {FO_RDID | FO_MFM, 0};
1705
1706 readidcmd[1] = csb->csb_cmd[1];
1707 (void) fdc_docmd(fcp, readidcmd, 2);
1708 }
1709
1710 int
fdcseek(struct fdcntlr * fcp,int unit,int cyl)1711 fdcseek(struct fdcntlr *fcp, int unit, int cyl)
1712 {
1713 static uchar_t seekabscmd[3] = {FO_SEEK, 0, 0};
1714
1715 FCERRPRINT(FDEP_L0, FDEM_RECA, (CE_CONT, "fdcseek unit %d to cyl %d\n",
1716 unit, cyl));
1717 seekabscmd[1] = (uchar_t)unit;
1718 seekabscmd[2] = (uchar_t)cyl;
1719 return (fdc_docmd(fcp, seekabscmd, 3));
1720 }
1721
1722 /*
1723 * Returns status of disk change line of selected drive.
1724 * = 0 means diskette is present
1725 * != 0 means diskette was removed and current state is unknown
1726 */
1727 int
fdcsense_chng(struct fdcntlr * fcp,int unit)1728 fdcsense_chng(struct fdcntlr *fcp, int unit)
1729 {
1730 int digital_input;
1731
1732 FCERRPRINT(FDEP_L0, FDEM_SCHG,
1733 (CE_CONT, "fdcsense_chng unit %d\n", unit));
1734 digital_input = inb(fcp->c_regbase + FCR_DIR);
1735 if (fcp->c_mode == FDCMODE_30)
1736 digital_input ^= FDI_DKCHG;
1737 return (digital_input & FDI_DKCHG);
1738 }
1739
1740 int
fdcsense_drv(struct fdcntlr * fcp,int unit)1741 fdcsense_drv(struct fdcntlr *fcp, int unit)
1742 {
1743 static uchar_t sensedrvcmd[2] = {FO_SDRV, 0};
1744 uchar_t senser;
1745 int rval;
1746
1747 sensedrvcmd[1] = (uchar_t)unit;
1748 (void) fdc_docmd(fcp, sensedrvcmd, 2);
1749 /*
1750 * Ignored return. If failed, warning was issued by fdc_docmd.
1751 * fdc_results retrieves the controller/drive status
1752 */
1753 if (rval = fdc_result(fcp, &senser, 1))
1754 goto done;
1755 if (senser & S3_WPROT)
1756 fcp->c_unit[unit]->fj_flags |= FUNIT_WPROT;
1757 else
1758 fcp->c_unit[unit]->fj_flags &= ~FUNIT_WPROT;
1759 done:
1760 return (rval);
1761 }
1762
1763 int
fdcsense_int(struct fdcntlr * fcp,int * unitp,int * cylp)1764 fdcsense_int(struct fdcntlr *fcp, int *unitp, int *cylp)
1765 {
1766 uchar_t senser[2];
1767 int rval;
1768
1769 (void) fdc_docmd(fcp, &senseintcmd, 1);
1770 /*
1771 * Ignored return. If failed, warning was issued by fdc_docmd.
1772 * fdc_results retrieves the controller/drive status
1773 */
1774
1775 if (!(rval = fdc_result(fcp, senser, 2))) {
1776 if ((*senser & (S0_IVCMD | S0_SEKEND | S0_ECHK)) != S0_SEKEND)
1777 rval = 1;
1778 if (unitp)
1779 *unitp = *senser & 3;
1780 if (cylp)
1781 *cylp = senser[1];
1782 }
1783 return (rval);
1784 }
1785
1786 int
fdcspecify(struct fdcntlr * fcp,int xferrate,int steprate,int hlt)1787 fdcspecify(struct fdcntlr *fcp, int xferrate, int steprate, int hlt)
1788 {
1789 static uchar_t perpindcmd[2] = {FO_PERP, 0};
1790 static uchar_t specifycmd[3] = {FO_SPEC, 0, 0};
1791
1792 encode(drate_mfm, xferrate, &fcp->c_config);
1793 outb(fcp->c_regbase + FCR_CCR, fcp->c_config);
1794
1795 if (fcp->c_chip >= i82077) {
1796 /*
1797 * Use old style perpendicular mode command of 82077.
1798 */
1799 if (xferrate == 1000) {
1800 /* Set GAP and WGATE */
1801 perpindcmd[1] = 3;
1802 /* double step rate because xlate table is for 500Kb */
1803 steprate <<= 1;
1804 hlt <<= 1;
1805 } else
1806 perpindcmd[1] = 0;
1807 (void) fdc_docmd(fcp, perpindcmd, 2);
1808 /*
1809 * Ignored return. If failed, warning was issued by fdc_docmd.
1810 */
1811 }
1812 encode(step_rate, steprate, &fcp->c_hutsrt);
1813 specifycmd[1] = fcp->c_hutsrt |= 0x0F; /* use max head unload time */
1814 hlt = (hlt >= 256) ? 0 : (hlt >> 1); /* encode head load time */
1815 specifycmd[2] = fcp->c_hlt = hlt << 1; /* make room for DMA bit */
1816 return (fdc_docmd(fcp, specifycmd, 3));
1817 }
1818
1819 int
fdcspdchange(struct fdcntlr * fcp,struct fcu_obj * fjp,int rpm)1820 fdcspdchange(struct fdcntlr *fcp, struct fcu_obj *fjp, int rpm)
1821 {
1822 int retcode = 0;
1823 uint_t ddic;
1824 uchar_t deselect = 0;
1825 uchar_t ds_code;
1826 uchar_t enable_code;
1827 uchar_t save;
1828
1829 if (((fcp->c_flags & FCFLG_DSOUT) == 0 && rpm <= fjp->fj_rotspd) ||
1830 ((fcp->c_flags & FCFLG_DSOUT) && (fjp->fj_flags & FUNIT_3DMODE) &&
1831 rpm > fjp->fj_rotspd)) {
1832 return (0);
1833 }
1834
1835 FCERRPRINT(FDEP_L1, FDEM_SCHG,
1836 (CE_CONT, "fdcspdchange: %d rpm\n", rpm));
1837 ASSERT(MUTEX_HELD(&fcp->c_dorlock));
1838
1839 switch (fcp->c_chip) {
1840 default:
1841 break;
1842 case i82077:
1843 break;
1844
1845 case PC87322:
1846 {
1847 uchar_t nscmodecmd[5] = {FO_MODE, 0x02, 0x00, 0xC8, 0x00};
1848
1849 if (rpm > fjp->fj_rotspd) {
1850 nscmodecmd[3] ^= 0xC0;
1851 retcode = (fcp->c_flags ^ FCFLG_DSOUT) ||
1852 (fjp->fj_flags ^ FUNIT_3DMODE);
1853 fcp->c_flags |= FCFLG_DSOUT;
1854 fjp->fj_flags |= FUNIT_3DMODE;
1855 } else {
1856 /* program DENSEL to default output */
1857 fcp->c_flags &= ~FCFLG_DSOUT;
1858 retcode = fjp->fj_flags & FUNIT_3DMODE;
1859 fjp->fj_flags &= ~FUNIT_3DMODE;
1860 }
1861 if (retcode && (fcp->c_digout & FD_DRSEL) == fcp->c_curunit) {
1862 /* de-select drive while changing speed */
1863 deselect = fcp->c_digout ^ FD_DRSEL;
1864 outb(fcp->c_regbase + FCR_DOR, deselect);
1865 }
1866
1867 (void) fdc_docmd(fcp, nscmodecmd, 5);
1868 /*
1869 * Ignored return. If failed, warning was issued by fdc_docmd.
1870 */
1871 break;
1872 }
1873
1874 case FDC37C665:
1875 enable_code = FSA_ENA5;
1876 goto SMC_config;
1877
1878 case FDC37C666:
1879 enable_code = FSA_ENA6;
1880 SMC_config:
1881 if (rpm > fjp->fj_rotspd) {
1882 /* force DENSEL output to active LOW */
1883 ds_code = FSB_DSHI;
1884 retcode = (fcp->c_flags ^ FCFLG_DSOUT) ||
1885 (fjp->fj_flags ^ FUNIT_3DMODE);
1886 fcp->c_flags |= FCFLG_DSOUT;
1887 fjp->fj_flags |= FUNIT_3DMODE;
1888 } else {
1889 /* program DENSEL to default output */
1890 ds_code = 0;
1891 fcp->c_flags &= ~FCFLG_DSOUT;
1892 retcode = fjp->fj_flags & FUNIT_3DMODE;
1893 fjp->fj_flags &= ~FUNIT_3DMODE;
1894 }
1895 if (retcode && (fcp->c_digout & FD_DRSEL) == fcp->c_curunit) {
1896 /* de-select drive while changing speed */
1897 deselect = fcp->c_digout ^ FD_DRSEL;
1898 outb(fcp->c_regbase + FCR_DOR, deselect);
1899 }
1900 save = inb(fcp->c_regbase + FCR_SRA);
1901
1902 /* enter configuration mode */
1903 ddic = ddi_enter_critical();
1904 outb(fcp->c_regbase + FCR_SRA, enable_code);
1905 outb(fcp->c_regbase + FCR_SRA, enable_code);
1906 ddi_exit_critical(ddic);
1907
1908 outb(fcp->c_regbase + FCR_SRA, FSA_CR5);
1909 enable_code = inb(fcp->c_regbase + FCR_SRB) & FSB_DSDEF;
1910 /* update DENSEL mode bits */
1911 outb(fcp->c_regbase + FCR_SRB, enable_code | ds_code);
1912
1913 /* exit configuration mode */
1914 outb(fcp->c_regbase + FCR_SRA, FSA_DISB);
1915 drv_usecwait(10);
1916 outb(fcp->c_regbase + FCR_SRA, save);
1917 break;
1918 }
1919 if (deselect)
1920 /* reselect drive */
1921 outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
1922 return (retcode);
1923 }
1924
1925 static int
fdc_motorsm(struct fcu_obj * fjp,int input,int timeval)1926 fdc_motorsm(struct fcu_obj *fjp, int input, int timeval)
1927 {
1928 struct fdcntlr *fcp = fjp->fj_fdc;
1929 int unit = fjp->fj_unit & 3;
1930 int old_mstate;
1931 int rval = 0;
1932 uchar_t motorbit;
1933
1934 ASSERT(MUTEX_HELD(&fcp->c_dorlock));
1935 old_mstate = fcp->c_mtrstate[unit];
1936 encode(motor_onbits, unit, &motorbit);
1937
1938 switch (input) {
1939 case FMI_TIMER: /* timer expired */
1940 fcp->c_motort[unit] = 0;
1941 switch (old_mstate) {
1942 case FMS_START:
1943 case FMS_DELAY:
1944 fcp->c_mtrstate[unit] = FMS_ON;
1945 break;
1946 case FMS_KILLST:
1947 fcp->c_motort[unit] = timeout(fdmotort, (void *)fjp,
1948 drv_usectohz(1000000));
1949 fcp->c_mtrstate[unit] = FMS_IDLE;
1950 break;
1951 case FMS_IDLE:
1952 fcp->c_digout &= ~motorbit;
1953 outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
1954 fcp->c_mtrstate[unit] = FMS_OFF;
1955 fjp->fj_flags &= ~FUNIT_3DMODE;
1956 break;
1957 case 86:
1958 rval = -1;
1959 break;
1960 case FMS_OFF:
1961 case FMS_ON:
1962 default:
1963 rval = -2;
1964 }
1965 break;
1966
1967 case FMI_STARTCMD: /* start command */
1968 switch (old_mstate) {
1969 case FMS_IDLE:
1970 fcp->c_mtrstate[unit] = 86;
1971 mutex_exit(&fcp->c_dorlock);
1972 (void) untimeout(fcp->c_motort[unit]);
1973 mutex_enter(&fcp->c_dorlock);
1974 fcp->c_motort[unit] = 0;
1975 fcp->c_mtrstate[unit] = FMS_ON;
1976 break;
1977 case FMS_OFF:
1978 fcp->c_digout |= motorbit;
1979 outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
1980
1981 /* start motor_spinup_timer */
1982 ASSERT(timeval > 0);
1983 fcp->c_motort[unit] = timeout(fdmotort, (void *)fjp,
1984 drv_usectohz(100000 * timeval));
1985 /* FALLTHROUGH */
1986 case FMS_KILLST:
1987 fcp->c_mtrstate[unit] = FMS_START;
1988 break;
1989 default:
1990 rval = -2;
1991 }
1992 break;
1993
1994 case FMI_RSTARTCMD: /* restart command */
1995 if (fcp->c_motort[unit] != 0) {
1996 fcp->c_mtrstate[unit] = 86;
1997 mutex_exit(&fcp->c_dorlock);
1998 (void) untimeout(fcp->c_motort[unit]);
1999 mutex_enter(&fcp->c_dorlock);
2000 }
2001 ASSERT(timeval > 0);
2002 fcp->c_motort[unit] = timeout(fdmotort, (void *)fjp,
2003 drv_usectohz(100000 * timeval));
2004 fcp->c_mtrstate[unit] = FMS_START;
2005 break;
2006
2007 case FMI_DELAYCMD: /* delay command */
2008 if (fcp->c_motort[unit] == 0)
2009 fcp->c_motort[unit] = timeout(fdmotort, (void *)fjp,
2010 drv_usectohz(15000));
2011 fcp->c_mtrstate[unit] = FMS_DELAY;
2012 break;
2013
2014 case FMI_IDLECMD: /* idle command */
2015 switch (old_mstate) {
2016 case FMS_DELAY:
2017 fcp->c_mtrstate[unit] = 86;
2018 mutex_exit(&fcp->c_dorlock);
2019 (void) untimeout(fcp->c_motort[unit]);
2020 mutex_enter(&fcp->c_dorlock);
2021 /* FALLTHROUGH */
2022 case FMS_ON:
2023 ASSERT(timeval > 0);
2024 fcp->c_motort[unit] = timeout(fdmotort, (void *)fjp,
2025 drv_usectohz(100000 * timeval));
2026 fcp->c_mtrstate[unit] = FMS_IDLE;
2027 break;
2028 case FMS_START:
2029 fcp->c_mtrstate[unit] = FMS_KILLST;
2030 break;
2031 default:
2032 rval = -2;
2033 }
2034 break;
2035
2036 default:
2037 rval = -3;
2038 }
2039 if (rval) {
2040 FCERRPRINT(FDEP_L4, FDEM_EXEC, (CE_WARN,
2041 "fdc_motorsm: unit %d bad input %d or bad state %d",
2042 (int)fjp->fj_unit, input, old_mstate));
2043 #if 0
2044 cmn_err(CE_WARN,
2045 "fdc_motorsm: unit %d bad input %d or bad state %d",
2046 (int)fjp->fj_unit, input, old_mstate);
2047 fcp->c_mtrstate[unit] = FMS_OFF;
2048 if (fcp->c_motort[unit] != 0) {
2049 mutex_exit(&fcp->c_dorlock);
2050 (void) untimeout(fcp->c_motort[unit]);
2051 mutex_enter(&fcp->c_dorlock);
2052 fcp->c_motort[unit] = 0;
2053 }
2054 #endif
2055 } else
2056 FCERRPRINT(FDEP_L0, FDEM_EXEC,
2057 (CE_CONT, "fdc_motorsm unit %d: input %d, %d -> %d\n",
2058 (int)fjp->fj_unit, input, old_mstate,
2059 fcp->c_mtrstate[unit]));
2060 return (rval);
2061 }
2062
2063 /*
2064 * fdmotort
2065 * is called from timeout() when a motor timer has expired.
2066 */
2067 static void
fdmotort(void * arg)2068 fdmotort(void *arg)
2069 {
2070 struct fcu_obj *fjp = (struct fcu_obj *)arg;
2071 struct fdcntlr *fcp = fjp->fj_fdc;
2072 struct fdcsb *csb = &fcp->c_csb;
2073 int unit = fjp->fj_unit & 3;
2074 int mval;
2075 int newxstate = 0;
2076
2077 mutex_enter(&fcp->c_dorlock);
2078 mval = fdc_motorsm(fjp, FMI_TIMER, 0);
2079 mutex_exit(&fcp->c_dorlock);
2080 if (mval < 0)
2081 return;
2082
2083 mutex_enter(&fcp->c_lock);
2084
2085 if ((fcp->c_flags & FCFLG_WAITING) &&
2086 fcp->c_mtrstate[unit] == FMS_ON &&
2087 (csb->csb_xstate == FXS_MTRON || csb->csb_xstate == FXS_HDST ||
2088 csb->csb_xstate == FXS_DKCHGX))
2089 newxstate = fdc_statemach(fcp);
2090 if (newxstate == -1) {
2091 FCERRPRINT(FDEP_L3, FDEM_EXEC,
2092 (CE_WARN,
2093 "fdc_motort unit %d: motor ready but bad xstate",
2094 (int)fjp->fj_unit));
2095 fcp->c_csb.csb_cmdstat = EIO;
2096 }
2097 if (newxstate == -1 || newxstate == FXS_END) {
2098 fcp->c_flags ^= FCFLG_WAITING;
2099 cv_signal(&fcp->c_iocv);
2100 }
2101 mutex_exit(&fcp->c_lock);
2102 }
2103
2104 /*
2105 * DMA interrupt service routine
2106 *
2107 * Called by EISA dma interrupt service routine when buffer chaining
2108 * is required.
2109 */
2110
2111 ddi_dma_cookie_t *
fdc_dmae_isr(struct fdcntlr * fcp)2112 fdc_dmae_isr(struct fdcntlr *fcp)
2113 {
2114 struct fdcsb *csb = &fcp->c_csb;
2115 off_t off;
2116 size_t len;
2117
2118 if (csb->csb_dmahandle && !csb->csb_cmdstat) {
2119 if (++csb->csb_dmacurrcookie < csb->csb_dmacookiecnt) {
2120 ddi_dma_nextcookie(csb->csb_dmahandle,
2121 &csb->csb_dmacookie);
2122 return (&csb->csb_dmacookie);
2123 } else if (++csb->csb_dmacurrwin < csb->csb_dmawincnt) {
2124 if (ddi_dma_getwin(csb->csb_dmahandle,
2125 csb->csb_dmacurrwin, &off, &len,
2126 &csb->csb_dmacookie,
2127 &csb->csb_dmacookiecnt) != DDI_SUCCESS) {
2128 return (NULL);
2129 }
2130 csb->csb_dmacurrcookie = 0;
2131 return (&csb->csb_dmacookie);
2132 }
2133 } else
2134 cmn_err(CE_WARN, "fdc: unsolicited DMA interrupt");
2135 return (NULL);
2136 }
2137
2138
2139 /*
2140 * returns:
2141 * 0 if all ok,
2142 * ENXIO - diskette not in drive
2143 * ETIMEDOUT - for immediate operations that timed out
2144 * EBUSY - if stupid chip is locked busy???
2145 * ENOEXEC - for timeout during sending cmds to chip
2146 *
2147 * to sleep: set sleep
2148 * to check for disk changed: set change
2149 */
2150 static int
fdc_exec(struct fdcntlr * fcp,int sleep,int change)2151 fdc_exec(struct fdcntlr *fcp, int sleep, int change)
2152 {
2153 struct ddi_dmae_req dmaereq;
2154 struct fcu_obj *fjp;
2155 struct fdcsb *csb;
2156 off_t off;
2157 size_t len;
2158 int unit;
2159
2160 mutex_enter(&fcp->c_lock);
2161 FCERRPRINT(FDEP_L0, FDEM_EXEC,
2162 (CE_CONT, "fdc_exec: sleep %x change %x\n", sleep, change));
2163 csb = &fcp->c_csb;
2164 unit = csb->csb_drive;
2165 fjp = fcp->c_unit[unit];
2166
2167 if (csb->csb_opflags & CSB_OFINRPT) {
2168 if (*csb->csb_cmd == FO_RECAL)
2169 csb->csb_npcyl = 0;
2170 else if ((*csb->csb_cmd & ~FO_MFM) != FO_FRMT)
2171 csb->csb_npcyl =
2172 csb->csb_cmd[2] * fjp->fj_chars->fdc_steps;
2173 csb->csb_xstate = FXS_START;
2174 } else
2175 csb->csb_xstate = FXS_DOIT;
2176 csb->csb_retrys = 0;
2177 csb->csb_ourtrys = 0;
2178
2179 if (csb->csb_dmahandle) {
2180 /* ensure that entire format xfer is in one cookie */
2181 /*
2182 * The change from ddi_dma_buf/addr_setup() to
2183 * ddi_dma_buf/addr_bind_handle() has already loaded
2184 * the first DMA window and cookie.
2185 */
2186 if ((*csb->csb_cmd & ~FO_MFM) == FO_FRMT &&
2187 (4 * csb->csb_cmd[3]) != csb->csb_dmacookie.dmac_size) {
2188 mutex_exit(&fcp->c_lock);
2189 return (EINVAL);
2190 }
2191 }
2192
2193 retry:
2194 if (fcp->c_curunit != unit || !(fjp->fj_flags & FUNIT_CHAROK)) {
2195 fcp->c_curunit = unit;
2196 fjp->fj_flags |= FUNIT_CHAROK;
2197 if (fjp->fj_chars->fdc_transfer_rate == 417) {
2198 /* XXX hack for fdformat */
2199 /* fjp->fj_chars->fdc_transfer_rate == 500; */
2200 fjp->fj_attr->fda_rotatespd = 360;
2201 }
2202 if (fdcspecify(fcp, fjp->fj_chars->fdc_transfer_rate,
2203 fjp->fj_drive->fdd_steprate, 40))
2204 cmn_err(CE_WARN,
2205 "fdc_select: controller setup rejected "
2206 "fdcntrl %p transfer rate %x step rate %x "
2207 "head load time 40", (void*)fcp,
2208 fjp->fj_chars->fdc_transfer_rate,
2209 fjp->fj_drive->fdd_steprate);
2210
2211 mutex_enter(&fcp->c_dorlock);
2212 if (fdcspdchange(fcp, fjp, fjp->fj_attr->fda_rotatespd)) {
2213 /* 3D drive requires 500 ms for speed change */
2214 (void) fdc_motorsm(fjp, FMI_RSTARTCMD, 5);
2215 /*
2216 * Return value ignored - fdcmotort deals with failure.
2217 */
2218 }
2219 mutex_exit(&fcp->c_dorlock);
2220 }
2221
2222 /*
2223 * If checking for disk_change is enabled
2224 * (i.e. not seeking in fdresetchng),
2225 * we sample the DSKCHG line to see if the diskette has wandered away.
2226 */
2227 if (change && fdcsense_chng(fcp, unit)) {
2228 FCERRPRINT(FDEP_L3, FDEM_EXEC,
2229 (CE_WARN, "diskette %d changed!!!", csb->csb_drive));
2230 fcp->c_unit[unit]->fj_flags |= FUNIT_CHANGED;
2231 /*
2232 * If the diskette is still gone... so are we, adios!
2233 */
2234 if (fdcheckdisk(fcp, unit)) {
2235 mutex_exit(&fcp->c_lock);
2236
2237 /* VP/ix expects an EBUSY return here */
2238 if (*csb->csb_cmd == FO_SDRV) {
2239 return (EBUSY);
2240 }
2241 return (ENXIO);
2242 }
2243 /*
2244 * delay to ensure that new diskette is up to speed
2245 */
2246 mutex_enter(&fcp->c_dorlock);
2247 (void) fdc_motorsm(fjp, FMI_RSTARTCMD,
2248 fjp->fj_drive->fdd_motoron);
2249 /*
2250 * Return value ignored - fdcmotort deals with failure.
2251 */
2252 mutex_exit(&fcp->c_dorlock);
2253 }
2254
2255 /*
2256 * gather some statistics
2257 */
2258 switch (csb->csb_cmd[0] & 0x1f) {
2259 case FO_RDDAT:
2260 fcp->fdstats.rd++;
2261 break;
2262 case FO_WRDAT:
2263 fcp->fdstats.wr++;
2264 break;
2265 case FO_RECAL:
2266 fcp->fdstats.recal++;
2267 break;
2268 case FO_FRMT:
2269 fcp->fdstats.form++;
2270 break;
2271 default:
2272 fcp->fdstats.other++;
2273 break;
2274 }
2275
2276 bzero(csb->csb_rslt, 10);
2277 csb->csb_cmdstat = 0;
2278
2279 if (csb->csb_dmahandle) {
2280 bzero(&dmaereq, sizeof (struct ddi_dmae_req));
2281 dmaereq.der_command = (csb->csb_opflags & CSB_OFDMAWT) ?
2282 DMAE_CMD_WRITE : DMAE_CMD_READ;
2283 /*
2284 * setup for dma buffer chaining regardless of bus capability
2285 */
2286 dmaereq.der_bufprocess = DMAE_BUF_CHAIN;
2287 dmaereq.proc = fdc_dmae_isr;
2288 dmaereq.procparms = (void *)fcp;
2289 if (ddi_dmae_prog(fcp->c_dip, &dmaereq, &csb->csb_dmacookie,
2290 fcp->c_dmachan) != DDI_SUCCESS)
2291 cmn_err(CE_WARN, "fdc_exec: dmae prog failed, "
2292 "dip %p, dmachan %x",
2293 (void*)fcp->c_dip, fcp->c_dmachan);
2294 }
2295
2296 if ((fdc_statemach(fcp) == FXS_DOWT) && !sleep) {
2297 /*
2298 * If the operation has no results - then just return
2299 */
2300 if (!csb->csb_nrslts) {
2301 mutex_exit(&fcp->c_lock);
2302 return (0);
2303 }
2304 /*
2305 * this operation has no interrupt and an immediate result
2306 * so wait for the results and stuff them into the csb
2307 */
2308 if (fdc_statemach(fcp) == -1) {
2309 mutex_exit(&fcp->c_lock);
2310 return (EIO);
2311 }
2312 } else {
2313 fcp->c_flags |= FCFLG_WAITING;
2314 /*
2315 * wait for completion interrupt
2316 */
2317 while (fcp->c_flags & FCFLG_WAITING) {
2318 cv_wait(&fcp->c_iocv, &fcp->c_lock);
2319 }
2320 }
2321
2322 /*
2323 * See if there was an error detected, if so, fdrecover()
2324 * will check it out and say what to do.
2325 *
2326 * Don't do this, though, if this was the Sense Drive Status
2327 * or the Dump Registers command.
2328 */
2329 if (csb->csb_cmdstat && *csb->csb_cmd != FO_SDRV) {
2330 /* if it can restarted OK, then do so, else return error */
2331 if (fdrecover(fcp)) {
2332 mutex_exit(&fcp->c_lock);
2333 return (EIO);
2334 }
2335 /* ASSUMES that cmd is still intact in csb */
2336 if (csb->csb_xstate == FXS_END)
2337 csb->csb_xstate = FXS_START;
2338 if (fdc_dma_attr.dma_attr_sgllen > 1 && csb->csb_dmahandle) {
2339 /*
2340 * restarted read/write operation requires
2341 * first DMA cookie of current window
2342 */
2343 if (ddi_dma_getwin(csb->csb_dmahandle,
2344 csb->csb_dmacurrwin, &off, &len,
2345 &csb->csb_dmacookie,
2346 &csb->csb_dmacookiecnt) != DDI_SUCCESS) {
2347
2348 mutex_exit(&fcp->c_lock);
2349 return (EIO);
2350 }
2351 csb->csb_dmacurrcookie = 0;
2352 }
2353 goto retry;
2354 }
2355 /* things went ok */
2356 mutex_exit(&fcp->c_lock);
2357 return (0);
2358 }
2359
2360 /*
2361 * fdcheckdisk
2362 * called by fdc_exec to check if the disk is still there - do a seek
2363 * then see if DSKCHG line went away; if so, diskette is in; else
2364 * it's (still) out.
2365 */
2366 int
fdcheckdisk(struct fdcntlr * fcp,int unit)2367 fdcheckdisk(struct fdcntlr *fcp, int unit)
2368 {
2369 struct fdcsb *csb = &fcp->c_csb;
2370 int newcyl; /* where to seek for reset of DSKCHG */
2371 int rval;
2372 enum fxstate save_xstate;
2373 uchar_t save_cmd, save_cd1, save_npcyl;
2374
2375 ASSERT(MUTEX_HELD(&fcp->c_lock));
2376 FCERRPRINT(FDEP_L1, FDEM_CHEK,
2377 (CE_CONT, "fdcheckdisk unit %d\n", unit));
2378
2379 if (fcp->c_curpcyl[unit])
2380 newcyl = fcp->c_curpcyl[unit] - 1;
2381 else
2382 newcyl = 1;
2383
2384 save_cmd = *csb->csb_cmd;
2385 save_cd1 = csb->csb_cmd[1];
2386 save_npcyl = csb->csb_npcyl;
2387 save_xstate = csb->csb_xstate;
2388
2389 *csb->csb_cmd = FO_SEEK;
2390 csb->csb_cmd[1] = (uchar_t)unit;
2391 csb->csb_npcyl = (uchar_t)newcyl;
2392 fcp->c_flags |= FCFLG_WAITING;
2393
2394 if (fcp->c_mtrstate[unit] != FMS_ON && fcp->c_motort[unit] != 0)
2395 /*
2396 * wait for motor to get up to speed,
2397 * and let motor_timer issue seek cmd
2398 */
2399 csb->csb_xstate = FXS_DKCHGX;
2400 else {
2401 /*
2402 * motor is up to speed; issue seek cmd now
2403 */
2404 csb->csb_xstate = FXS_SEEK;
2405 if (rval = fdcseek(fcp, unit, newcyl)) {
2406 /*
2407 * any recal/seek errors are too serious to attend to
2408 */
2409 FCERRPRINT(FDEP_L3, FDEM_CHEK,
2410 (CE_WARN, "fdcheckdisk err %d", rval));
2411 fcp->c_flags ^= FCFLG_WAITING;
2412 }
2413 }
2414 /*
2415 * wait for completion interrupt
2416 * XXX This should be backed up with a watchdog timer!
2417 */
2418 while (fcp->c_flags & FCFLG_WAITING) {
2419 cv_wait(&fcp->c_iocv, &fcp->c_lock);
2420 }
2421
2422 /*
2423 * if disk change still asserted, no diskette in drive!
2424 */
2425 if (rval = fdcsense_chng(fcp, unit)) {
2426 FCERRPRINT(FDEP_L3, FDEM_CHEK,
2427 (CE_WARN, "fdcheckdisk no disk %d", unit));
2428 }
2429
2430 *csb->csb_cmd = save_cmd;
2431 csb->csb_cmd[1] = save_cd1;
2432 csb->csb_npcyl = save_npcyl;
2433 csb->csb_xstate = save_xstate;
2434 return (rval);
2435 }
2436
2437 static int
fdrecover(struct fdcntlr * fcp)2438 fdrecover(struct fdcntlr *fcp)
2439 {
2440 struct fcu_obj *fjp;
2441 struct fdcsb *csb = &fcp->c_csb;
2442 int residual;
2443 int unit;
2444 char *failure;
2445
2446 FCERRPRINT(FDEP_L2, FDEM_RECO,
2447 (CE_NOTE, "fdrecover unit %d", csb->csb_drive));
2448
2449 unit = csb->csb_drive;
2450 fjp = fcp->c_unit[unit];
2451 if (fcp->c_flags & FCFLG_TIMEOUT) {
2452 fcp->c_flags ^= FCFLG_TIMEOUT;
2453 csb->csb_rslt[1] |= 0x08;
2454 FCERRPRINT(FDEP_L3, FDEM_RECO,
2455 (CE_WARN, "fd unit %d: %s timed out", csb->csb_drive,
2456 fdcmds[*csb->csb_cmd & 0x1f].cmdname));
2457 }
2458
2459 if (csb->csb_status & S0_SEKEND)
2460 fcp->c_curpcyl[unit] = -1;
2461
2462 switch (csb->csb_oldxs) {
2463 case FXS_RCAL: /* recalibrate */
2464 case FXS_SEEK: /* seek */
2465 case FXS_RESET: /* cntlr reset */
2466 FCERRPRINT(FDEP_L4, FDEM_RECO, (CE_WARN,
2467 "fd unit %d: %s error: st0=0x%x pcn=%d", csb->csb_drive,
2468 fdcmds[*csb->csb_cmd & 0x1f].cmdname,
2469 *csb->csb_rslt, csb->csb_rslt[1]));
2470 if (csb->csb_retrys++ < skretry &&
2471 !(csb->csb_opflags & CSB_OFRAWIOCTL))
2472 return (0);
2473 break;
2474
2475 case FXS_RDID: /* read ID */
2476 if (!(csb->csb_status & S0_SEKEND))
2477 csb->csb_xstate = FXS_HDST;
2478 /* FALLTHROUGH */
2479 case FXS_DOIT: /* original operation */
2480 case FXS_DOWT: /* waiting on operation */
2481 if (csb->csb_opflags & (CSB_OFDMARD | CSB_OFDMAWT)) {
2482 if (ddi_dmae_getcnt(fcp->c_dip, fcp->c_dmachan,
2483 &residual) != DDI_SUCCESS)
2484 cmn_err(CE_WARN,
2485 "fdc_recover: dmae getcnt failed, "
2486 "dip %p dmachan %x residual %x",
2487 (void*)fcp->c_dip, fcp->c_dmachan,
2488 residual);
2489 FCERRPRINT(FDEP_L2, FDEM_RECO,
2490 (CE_NOTE,
2491 "fd unit %d: %s error: "
2492 "dma count=0x%lx residual=0x%x",
2493 csb->csb_drive,
2494 fdcmds[*csb->csb_cmd & 0x1f].cmdname,
2495 csb->csb_dmacookie.dmac_size, residual));
2496 }
2497 if (csb->csb_rslt[1] == S1_OVRUN)
2498 /*
2499 * handle retries of over/underrun
2500 * with a secondary retry counter
2501 */
2502 if (++csb->csb_ourtrys <= OURUN_TRIES) {
2503 FCERRPRINT(FDEP_L2, FDEM_RECO,
2504 (CE_NOTE,
2505 "fd unit %d: %s error: over/under-run",
2506 csb->csb_drive,
2507 fdcmds[*csb->csb_cmd & 0x1f].cmdname));
2508 return (0);
2509 } else
2510 /*
2511 * count 1 set of over/underruns
2512 * as 1 primary retry effort
2513 */
2514 csb->csb_ourtrys = 0;
2515
2516 if ((fjp->fj_flags & (FUNIT_UNLABELED | FUNIT_LABELOK)) &&
2517 !(csb->csb_opflags & CSB_OFRAWIOCTL)) {
2518 /*
2519 * device is open so keep trying and
2520 * gather statistics on errors
2521 */
2522 if (csb->csb_rslt[1] & S1_CRCER)
2523 fcp->fdstats.de++;
2524 if (csb->csb_rslt[1] & S1_OVRUN)
2525 fcp->fdstats.run++;
2526 if (csb->csb_rslt[1] & (S1_NODATA | S1_MADMK))
2527 fcp->fdstats.bfmt++;
2528 if (csb->csb_rslt[1] & 0x08)
2529 fcp->fdstats.to++;
2530
2531 /*
2532 * if we have not run out of retries, return 0
2533 */
2534 if (csb->csb_retrys++ < csb->csb_maxretry &&
2535 (*csb->csb_cmd & ~FO_MFM) != FO_FRMT) {
2536 if (csb->csb_opflags &
2537 (CSB_OFDMARD | CSB_OFDMAWT)) {
2538 FCERRPRINT(FDEP_L4, FDEM_RECO,
2539 (CE_WARN,
2540 "fd unit %d: %s error: "
2541 "st0=0x%x st1=0x%x st2=0x%x",
2542 csb->csb_drive,
2543 fdcmds[*csb->csb_cmd &
2544 0x1f].cmdname,
2545 *csb->csb_rslt, csb->csb_rslt[1],
2546 csb->csb_rslt[2]));
2547 }
2548 if ((csb->csb_retrys & 1) &&
2549 csb->csb_xstate == FXS_END)
2550 csb->csb_xstate = FXS_DOIT;
2551 else if (csb->csb_retrys == 3)
2552 csb->csb_xstate = FXS_RESTART;
2553 return (0);
2554 }
2555 if (csb->csb_rslt[1] & S1_CRCER)
2556 failure = "crc error";
2557 else if (csb->csb_rslt[1] & S1_OVRUN)
2558 failure = "over/under-run";
2559 else if (csb->csb_rslt[1] & (S1_NODATA | S1_MADMK))
2560 failure = "bad format";
2561 else if (csb->csb_rslt[1] & 0x08)
2562 failure = "timeout";
2563 else
2564 failure = "failed";
2565 cmn_err(CE_NOTE, "!fd unit %d: %s %s (%x %x %x)",
2566 csb->csb_drive,
2567 fdcmds[*csb->csb_cmd & 0x1f].cmdname, failure,
2568 *csb->csb_rslt, csb->csb_rslt[1], csb->csb_rslt[2]);
2569 } else {
2570 FCERRPRINT(FDEP_L2, FDEM_RECO,
2571 (CE_NOTE, "fd unit %d: %s failed (%x %x %x)",
2572 csb->csb_drive,
2573 fdcmds[*csb->csb_cmd & 0x1f].cmdname,
2574 *csb->csb_rslt, csb->csb_rslt[1],
2575 csb->csb_rslt[2]));
2576 }
2577 break;
2578
2579 default:
2580 FCERRPRINT(FDEP_L4, FDEM_RECO, (CE_WARN,
2581 "fd unit %d: %s failed: st0=0x%x st1=0x%x st2=0x%x",
2582 csb->csb_drive, fdcmds[*csb->csb_cmd & 0x1f].cmdname,
2583 *csb->csb_rslt, csb->csb_rslt[1], csb->csb_rslt[2]));
2584 break;
2585 }
2586 return (1);
2587 }
2588
2589
2590 /* Autovector Interrupt Entry Point */
2591 static uint_t
fdc_intr(caddr_t arg)2592 fdc_intr(caddr_t arg)
2593 {
2594 struct fdcntlr *fcp = (struct fdcntlr *)arg;
2595 struct fdcsb *csb;
2596 off_t off;
2597 size_t blklen;
2598 int drive;
2599 int newstate;
2600 int pendstate;
2601 int rval = DDI_DMA_DONE;
2602 int state;
2603 int maxspin = 10;
2604
2605 csb = &fcp->c_csb;
2606
2607 mutex_enter(&fcp->c_lock);
2608 if (fcp->c_suspended) {
2609 mutex_exit(&fcp->c_lock);
2610 return (DDI_INTR_UNCLAIMED);
2611 }
2612
2613 /*
2614 * Wait for the RQM bit to be set, or until we've tested it
2615 * a bunch of times (which may imply this isn't our interrupt).
2616 */
2617 state = inb(fcp->c_regbase + FCR_MSR);
2618 pendstate = state & (MS_RQM | MS_DIO | MS_CB);
2619 while (((pendstate & MS_RQM) == 0) && (maxspin-- > 0)) {
2620 /* Small pause in between reading the status port */
2621 drv_usecwait(10);
2622 /* Reread the status port */
2623 state = inb(fcp->c_regbase + FCR_MSR);
2624 pendstate = state & (MS_RQM | MS_DIO | MS_CB);
2625 }
2626 FCERRPRINT(FDEP_L0, FDEM_INTR,
2627 (CE_CONT, "fdc_intr unit %d: xstate=%d MSR=0x%x\n",
2628 csb->csb_drive, csb->csb_xstate, state));
2629
2630 /*
2631 * If there is an operation outstanding AND the controller is ready
2632 * to receive a command or send us the result of a command (OR if the
2633 * controller is ready to accept a new command), AND if
2634 * someone has been waiting for a command to finish AND (if no unit
2635 * is BUSY OR if the unit that we're waiting for is BUSY (i.e. it's in
2636 * the middle of a seek/recalibrate)) then this interrupt is for us.
2637 */
2638 if ((pendstate == (MS_RQM | MS_DIO | MS_CB) || pendstate == MS_RQM) &&
2639 (fcp->c_flags & FCFLG_WAITING) &&
2640 (!(state & 0x0f) || ((1 << csb->csb_drive) & state))) {
2641 /*
2642 * Remove one of the conditions for entering this code.
2643 * The state_machine will release the c_lock if it
2644 * calls untimeout()
2645 */
2646 fcp->c_flags ^= FCFLG_WAITING;
2647
2648 if ((newstate = fdc_statemach(fcp)) == -1) {
2649 /* restore waiting flag */
2650 fcp->c_flags |= FCFLG_WAITING;
2651 mutex_exit(&fcp->c_lock);
2652 return (DDI_INTR_CLAIMED);
2653 }
2654
2655 if (fcp->c_intrstat)
2656 KIOIP->intrs[KSTAT_INTR_HARD]++;
2657 if (newstate == FXS_END) {
2658
2659 if (csb->csb_dmahandle && !csb->csb_cmdstat &&
2660 /*
2661 * read/write operation may have multiple DMA
2662 * cookies: process next one
2663 */
2664 ((csb->csb_dmacurrcookie <
2665 (csb->csb_dmacookiecnt - 1)) ||
2666 (csb->csb_dmacurrwin) < (csb->csb_dmawincnt - 1))) {
2667 /*
2668 * read/write operation requires another
2669 * DMA cookie: process next one
2670 */
2671
2672 if (++csb->csb_dmacurrcookie <
2673 csb->csb_dmacookiecnt) {
2674 ddi_dma_nextcookie(csb->csb_dmahandle,
2675 &csb->csb_dmacookie);
2676 } else if (++csb->csb_dmacurrwin <
2677 csb->csb_dmawincnt) {
2678 if (ddi_dma_getwin(csb->csb_dmahandle,
2679 csb->csb_dmacurrwin, &off, &blklen,
2680 &csb->csb_dmacookie,
2681 &csb->csb_dmacookiecnt) !=
2682 DDI_SUCCESS) {
2683 cmn_err(CE_WARN,
2684 "fdc_intr: "
2685 "dma getwin failed");
2686 }
2687 csb->csb_dmacurrcookie = 0;
2688 }
2689
2690 if (ddi_dmae_prog(fcp->c_dip, NULL,
2691 &csb->csb_dmacookie, fcp->c_dmachan) !=
2692 DDI_SUCCESS)
2693 cmn_err(CE_WARN,
2694 "fdc_intr: dmae prog failed, "
2695 "dip %p dmachannel %x",
2696 (void*)fcp->c_dip,
2697 fcp->c_dmachan);
2698
2699 /*
2700 * status of last operation has disk
2701 * address for continuation
2702 */
2703 csb->csb_cmd[2] = csb->csb_rslt[3];
2704 csb->csb_cmd[3] = csb->csb_rslt[4];
2705 csb->csb_cmd[4] = csb->csb_rslt[5];
2706 csb->csb_cmd[1] = (csb->csb_cmd[1] & ~0x04) |
2707 (csb->csb_cmd[3] << 2);
2708
2709 csb->csb_xstate = FXS_START;
2710 (void) fdc_statemach(fcp);
2711 /*
2712 * Ignored return. If failed, warning already
2713 * posted. Returned state irrelevant.
2714 */
2715 /* restore waiting flag */
2716 fcp->c_flags |= FCFLG_WAITING;
2717 goto fi_exit;
2718 }
2719 if (rval != DDI_DMA_DONE)
2720 csb->csb_cmdstat = EIO;
2721 /*
2722 * somebody's waiting for completion of fdcntlr/csb,
2723 * wake them
2724 */
2725 cv_signal(&fcp->c_iocv);
2726 }
2727 else
2728 /* restore waiting flag */
2729 fcp->c_flags |= FCFLG_WAITING;
2730 fi_exit:
2731 mutex_exit(&fcp->c_lock);
2732 return (DDI_INTR_CLAIMED);
2733 }
2734
2735 if (state & MS_RQM) {
2736 (void) fdcsense_int(fcp, &drive, NULL);
2737 /*
2738 * Ignored return - senser state already saved
2739 */
2740 FCERRPRINT(FDEP_L4, FDEM_INTR,
2741 (CE_WARN, "fdc_intr unit %d: nobody sleeping 0x%x",
2742 drive, state));
2743 } else {
2744 FCERRPRINT(FDEP_L4, FDEM_INTR,
2745 (CE_WARN, "fdc_intr: nobody sleeping on %d 0x%x",
2746 csb->csb_drive, state));
2747 }
2748 /*
2749 * This should probably be protected, but, what the
2750 * heck...the cost isn't worth the accuracy for this
2751 * statistic.
2752 */
2753 if (fcp->c_intrstat)
2754 KIOIP->intrs[KSTAT_INTR_SPURIOUS]++;
2755 mutex_exit(&fcp->c_lock);
2756 return (DDI_INTR_UNCLAIMED);
2757 }
2758
2759 /*
2760 * fdwatch
2761 * is called from timeout() when a floppy operation timer has expired.
2762 */
2763 static void
fdwatch(void * arg)2764 fdwatch(void *arg)
2765 {
2766 struct fdcntlr *fcp = (struct fdcntlr *)arg;
2767 struct fdcsb *csb;
2768
2769 mutex_enter(&fcp->c_lock);
2770
2771 if (fcp->c_timeid == 0) {
2772 /*
2773 * fdc_intr got here first, ergo, no timeout condition..
2774 */
2775 mutex_exit(&fcp->c_lock);
2776 return;
2777 }
2778
2779 if (fcp->c_flags & FCFLG_WAITING) {
2780 if (ddi_dmae_stop(fcp->c_dip, fcp->c_dmachan) != DDI_SUCCESS)
2781 cmn_err(CE_WARN, "fdwatch: dmae stop failed, "
2782 "dip %p, dmachan %x",
2783 (void*)fcp->c_dip, fcp->c_dmachan);
2784 csb = &fcp->c_csb;
2785 FCERRPRINT(FDEP_L3, FDEM_WATC,
2786 (CE_WARN, "fdcwatch unit %d: xstate = %d",
2787 csb->csb_drive, csb->csb_xstate));
2788 drv_usecwait(50);
2789
2790 if (inb(fcp->c_regbase + FCR_MSR) != MS_RQM) {
2791 /*
2792 * cntlr is still busy, so reset it
2793 */
2794 csb->csb_xstate = FXS_KILL;
2795 (void) fdc_statemach(fcp);
2796 /*
2797 * Ignored return. If failed, warning already
2798 * posted. Returned state irrelevant.
2799 */
2800 } else {
2801 csb->csb_xstate = FXS_END;
2802 fcp->c_timeid = 0;
2803 fcp->c_flags ^= FCFLG_WAITING;
2804 cv_signal(&fcp->c_iocv);
2805 }
2806 csb->csb_cmdstat = EIO;
2807 fcp->c_flags |= FCFLG_TIMEOUT;
2808 } else {
2809 FCERRPRINT(FDEP_L4, FDEM_INTR,
2810 (CE_WARN, "fdcwatch: not sleeping for unit %d",
2811 fcp->c_csb.csb_drive));
2812 }
2813 if (fcp->c_intrstat)
2814 KIOIP->intrs[KSTAT_INTR_WATCHDOG]++;
2815 mutex_exit(&fcp->c_lock);
2816 }
2817
2818
2819 static int
fdc_statemach(struct fdcntlr * fcp)2820 fdc_statemach(struct fdcntlr *fcp)
2821 {
2822 struct fcu_obj *fjp;
2823 struct fdcsb *csb = &fcp->c_csb;
2824 int backoff;
2825 clock_t time;
2826 int unit;
2827
2828 ASSERT(MUTEX_HELD(&fcp->c_lock));
2829
2830 unit = csb->csb_drive;
2831 fjp = fcp->c_unit[unit];
2832
2833 csb->csb_oldxs = csb->csb_xstate;
2834 switch (csb->csb_xstate) {
2835
2836 case FXS_START: /* start of operation */
2837 ASSERT(fcp->c_timeid == 0);
2838 time = drv_usectohz(100000 * (unsigned int)csb->csb_timer);
2839 if (time == 0)
2840 time = drv_usectohz(2000000);
2841 fcp->c_timeid = timeout(fdwatch, (void *)fcp, time);
2842
2843 if (fcp->c_mtrstate[unit] == FMS_START) {
2844 /*
2845 * wait for motor to get up to speed
2846 */
2847 csb->csb_xstate = FXS_MTRON;
2848 break;
2849 }
2850 /* FALLTHROUGH */
2851
2852 case FXS_MTRON: /* motor is at speed */
2853 if (fcp->c_mtrstate[unit] != FMS_ON) {
2854 /* how did we get here ?? */
2855 cmn_err(CE_WARN, "fdc: selected but motor off");
2856 return (-1);
2857 }
2858 if (fcp->c_curpcyl[unit] != -1 && *csb->csb_cmd != FO_RECAL)
2859 goto nxs_seek;
2860 recalcmd[1] = (uchar_t)unit;
2861 if (fdc_docmd(fcp, recalcmd, 2) == -1) {
2862 /* cntlr did not accept command bytes */
2863 fdcquiesce(fcp);
2864 csb->csb_cmdstat = EIO;
2865 csb->csb_xstate = FXS_RESET;
2866 break;
2867 }
2868 fcp->c_sekdir[unit] = 0;
2869 csb->csb_xstate = FXS_RCAL;
2870 break;
2871
2872 case FXS_RCAL: /* forced recalibrate is complete */
2873 #if 0 /* #ifdef _VPIX */
2874 /* WARNING: this code breaks SPARC compatibility */
2875 if (csb->csb_opflags & CSB_OFRAWIOCTL &&
2876 *csb->csb_cmd == FO_RECAL) {
2877 fcp->c_curpcyl[unit] = 0;
2878 csb->csb_status = 0;
2879 goto nxs_cmpl;
2880 }
2881 #endif
2882 (void) fdc_docmd(fcp, &senseintcmd, 1);
2883 /*
2884 * Ignored return. If failed, warning was issued by fdc_docmd.
2885 * fdc_results retrieves the controller/drive status
2886 */
2887 (void) fdc_result(fcp, csb->csb_rslt, 2);
2888 /*
2889 * Ignored return. If failed, warning was issued by fdc_result.
2890 * Actual results checked below
2891 */
2892 if ((csb->csb_status = ((*csb->csb_rslt ^ S0_SEKEND) &
2893 (S0_ICMASK | S0_SEKEND | S0_ECHK | S0_NOTRDY))) != 0) {
2894 FCERRPRINT(FDEP_L3, FDEM_EXEC,
2895 (CE_WARN, "fdc_statemach unit %d: recal result %x",
2896 csb->csb_drive, *csb->csb_rslt));
2897 fdcquiesce(fcp);
2898 csb->csb_cmdstat = EIO;
2899 csb->csb_xstate = FXS_RESET;
2900 break;
2901 }
2902 if (unit != (*csb->csb_rslt & 3) || csb->csb_rslt[1]) {
2903 csb->csb_status = S0_SEKEND;
2904 goto nxs_cmpl;
2905 }
2906 fcp->c_curpcyl[unit] = csb->csb_rslt[1];
2907 if (*csb->csb_cmd == FO_RECAL)
2908 goto nxs_cmpl;
2909 nxs_seek:
2910 if (*csb->csb_cmd != FO_SEEK &&
2911 csb->csb_npcyl == fcp->c_curpcyl[unit])
2912 goto nxs_doit;
2913 fcp->c_sekdir[unit] = csb->csb_npcyl - fcp->c_curpcyl[unit];
2914 /* FALLTHROUGH */
2915
2916 case FXS_DKCHGX: /* reset Disk-Change latch */
2917 (void) fdcseek(fcp, csb->csb_cmd[1], csb->csb_npcyl);
2918 /*
2919 * Ignored return. If command rejected, warnig already posted
2920 * by fdc_docmd().
2921 */
2922 csb->csb_xstate = FXS_SEEK;
2923 break;
2924
2925 case FXS_RESTART: /* special restart of read/write operation */
2926 ASSERT(fcp->c_timeid == 0);
2927 time = drv_usectohz(100000 * csb->csb_timer);
2928 if (time == 0)
2929 time = drv_usectohz(2000000);
2930 fcp->c_timeid = timeout(fdwatch, (void *)fcp, time);
2931
2932 if (fcp->c_mtrstate[unit] != FMS_ON) {
2933 cmn_err(CE_WARN, "fdc: selected but motor off");
2934 return (-1);
2935 }
2936 if ((csb->csb_npcyl == 0 || fcp->c_sekdir[unit] >= 0) &&
2937 (int)csb->csb_cmd[2] < (fjp->fj_chars->fdc_ncyl - 1))
2938 backoff = csb->csb_npcyl + 1;
2939 else
2940 backoff = csb->csb_npcyl - 1;
2941 (void) fdcseek(fcp, csb->csb_cmd[1], backoff);
2942 /*
2943 * Ignored return. If command rejected, warnig already posted
2944 * by fdc_docmd().
2945 */
2946 csb->csb_xstate = FXS_RESEEK;
2947 break;
2948
2949 case FXS_RESEEK: /* seek to backoff-cyl complete */
2950 (void) fdc_docmd(fcp, &senseintcmd, 1);
2951 /*
2952 * Ignored return. If failed, warning was issued by fdc_docmd.
2953 * fdc_results retrieves the controller/drive status
2954 */
2955 (void) fdc_result(fcp, csb->csb_rslt, 2);
2956 /*
2957 * Ignored return. If failed, warning was issued by fdc_result.
2958 * Actual results checked below
2959 */
2960 if ((csb->csb_status = ((*csb->csb_rslt ^ S0_SEKEND) &
2961 (S0_ICMASK | S0_SEKEND | S0_ECHK | S0_NOTRDY))) != 0)
2962 goto nxs_cmpl;
2963 (void) fdcseek(fcp, csb->csb_cmd[1], csb->csb_npcyl);
2964 /*
2965 * Ignored return. If command rejected, warnig already posted
2966 * by fdc_docmd().
2967 */
2968 csb->csb_xstate = FXS_SEEK;
2969 break;
2970
2971 case FXS_SEEK: /* seek complete */
2972 #if 0 /* #ifdef _VPIX */
2973 /* WARNING: this code breaks SPARC compatibility and */
2974 /* rawioctls in fdformat */
2975 if (csb->csb_opflags & CSB_OFRAWIOCTL) {
2976 fcp->c_curpcyl[unit] = csb->csb_npcyl;
2977 csb->csb_status = 0;
2978 goto nxs_cmpl;
2979 }
2980 #endif
2981 (void) fdc_docmd(fcp, &senseintcmd, 1);
2982 /*
2983 * Ignored return. If failed, warning was issued by fdc_docmd.
2984 * fdc_results retrieves the controller/drive status
2985 */
2986 (void) fdc_result(fcp, csb->csb_rslt, 2);
2987 /*
2988 * Ignored return. If failed, warning was issued by fdc_result.
2989 * Actual results checked below
2990 */
2991 if ((csb->csb_status = ((*csb->csb_rslt ^ S0_SEKEND) &
2992 (S0_ICMASK | S0_SEKEND | S0_ECHK | S0_NOTRDY))) != 0)
2993 goto nxs_cmpl;
2994 if (unit != (*csb->csb_rslt & 3) ||
2995 csb->csb_rslt[1] != csb->csb_npcyl) {
2996 csb->csb_status = S0_SEKEND;
2997 goto nxs_cmpl;
2998 };
2999 fcp->c_curpcyl[unit] = csb->csb_rslt[1];
3000 /* use motor_timer to delay for head settle */
3001 mutex_enter(&fcp->c_dorlock);
3002 (void) fdc_motorsm(fjp, FMI_DELAYCMD,
3003 fjp->fj_drive->fdd_headsettle / 1000);
3004 /*
3005 * Return value ignored - fdcmotort deals with failure.
3006 */
3007 mutex_exit(&fcp->c_dorlock);
3008 csb->csb_xstate = FXS_HDST;
3009 break;
3010
3011 case FXS_HDST: /* head settle */
3012 if (*csb->csb_cmd == FO_SEEK)
3013 goto nxs_cmpl;
3014 if ((*csb->csb_cmd & ~FO_MFM) == FO_FRMT)
3015 goto nxs_doit;
3016 fdcreadid(fcp, csb);
3017 csb->csb_xstate = FXS_RDID;
3018 break;
3019
3020 case FXS_RDID: /* read ID complete */
3021 (void) fdc_result(fcp, csb->csb_rslt, 7);
3022 /*
3023 * Ignored return. If failed, warning was issued by fdc_result.
3024 * Actual results checked below
3025 */
3026 if ((csb->csb_status = (*csb->csb_rslt &
3027 (S0_ICMASK | S0_ECHK | S0_NOTRDY))) != 0)
3028 goto nxs_cmpl;
3029 if (csb->csb_cmd[2] != csb->csb_rslt[3]) {
3030 /* at wrong logical cylinder */
3031 csb->csb_status = S0_SEKEND;
3032 goto nxs_cmpl;
3033 };
3034 goto nxs_doit;
3035
3036 case FXS_DOIT: /* do original operation */
3037 ASSERT(fcp->c_timeid == 0);
3038 time = drv_usectohz(100000 * csb->csb_timer);
3039 if (time == 0)
3040 time = drv_usectohz(2000000);
3041 fcp->c_timeid = timeout(fdwatch, (void *)fcp, time);
3042 nxs_doit:
3043 if (fdc_docmd(fcp, csb->csb_cmd, csb->csb_ncmds) == -1) {
3044 /* cntlr did not accept command bytes */
3045 fdcquiesce(fcp);
3046 csb->csb_xstate = FXS_RESET;
3047 csb->csb_cmdstat = EIO;
3048 break;
3049 }
3050 csb->csb_xstate = FXS_DOWT;
3051 break;
3052
3053 case FXS_DOWT: /* operation complete */
3054 (void) fdc_result(fcp, csb->csb_rslt, csb->csb_nrslts);
3055 /*
3056 * Ignored return. If failed, warning was issued by fdc_result.
3057 * Actual results checked below.
3058 */
3059 if (*csb->csb_cmd == FO_SDRV) {
3060 csb->csb_status =
3061 (*csb->csb_rslt ^ (S3_DRRDY | S3_2SIDE)) &
3062 ~(S3_HEAD | S3_UNIT);
3063 } else {
3064 csb->csb_status = *csb->csb_rslt &
3065 (S0_ICMASK | S0_ECHK | S0_NOTRDY);
3066 }
3067 nxs_cmpl:
3068 if (csb->csb_status)
3069 csb->csb_cmdstat = EIO;
3070 csb->csb_xstate = FXS_END;
3071
3072 /* remove watchdog timer if armed and not already triggered */
3073 if (fcp->c_timeid != 0) {
3074 timeout_id_t timeid;
3075 timeid = fcp->c_timeid;
3076 fcp->c_timeid = 0;
3077 mutex_exit(&fcp->c_lock);
3078 (void) untimeout(timeid);
3079 mutex_enter(&fcp->c_lock);
3080 }
3081 break;
3082
3083 case FXS_KILL: /* quiesce cntlr by reset */
3084 fdcquiesce(fcp);
3085 fcp->c_timeid = timeout(fdwatch, (void *)fcp,
3086 drv_usectohz(2000000));
3087 csb->csb_xstate = FXS_RESET;
3088 break;
3089
3090 case FXS_RESET: /* int from reset */
3091 for (unit = 0; unit < NFDUN; unit++) {
3092 (void) fdcsense_int(fcp, NULL, NULL);
3093 fcp->c_curpcyl[unit] = -1;
3094 }
3095 if (fcp->c_timeid != 0) {
3096 timeout_id_t timeid;
3097 timeid = fcp->c_timeid;
3098 fcp->c_timeid = 0;
3099 mutex_exit(&fcp->c_lock);
3100 (void) untimeout(timeid);
3101 mutex_enter(&fcp->c_lock);
3102 }
3103 csb->csb_xstate = FXS_END;
3104 break;
3105
3106 default:
3107 cmn_err(CE_WARN, "fdc: statemach, unknown state");
3108 return (-1);
3109 }
3110 FCERRPRINT(FDEP_L1, FDEM_EXEC,
3111 (CE_CONT, "fdc_statemach unit %d: %d -> %d\n",
3112 csb->csb_drive, csb->csb_oldxs, csb->csb_xstate));
3113 return (csb->csb_xstate);
3114 }
3115
3116
3117 /*
3118 * routine to program a command into the floppy disk controller.
3119 */
3120 int
fdc_docmd(struct fdcntlr * fcp,uchar_t * oplistp,uchar_t count)3121 fdc_docmd(struct fdcntlr *fcp, uchar_t *oplistp, uchar_t count)
3122 {
3123 int ntries;
3124
3125 ASSERT(count >= 1);
3126 FCERRPRINT(FDEP_L0, FDEM_EXEC,
3127 (CE_CONT, "fdc_docmd: %x %x %x %x %x %x %x %x %x\n",
3128 oplistp[0], oplistp[1], oplistp[2], oplistp[3], oplistp[4],
3129 oplistp[5], oplistp[6], oplistp[7], oplistp[8]));
3130
3131 do {
3132 ntries = FDC_RQM_RETRY;
3133 do {
3134 if ((inb(fcp->c_regbase + FCR_MSR) & (MS_RQM|MS_DIO))
3135 == MS_RQM)
3136 break;
3137 else
3138 drv_usecwait(1);
3139 } while (--ntries);
3140 if (ntries == 0) {
3141 FCERRPRINT(FDEP_L3, FDEM_EXEC,
3142 (CE_WARN, "fdc_docmd: ctlr not ready"));
3143 return (-1);
3144 }
3145 outb(fcp->c_regbase + FCR_DATA, *oplistp++);
3146 drv_usecwait(16); /* See comment in fdc_result() */
3147 } while (--count);
3148 return (0);
3149 }
3150
3151
3152 /*
3153 * Routine to return controller/drive status information.
3154 * The diskette-controller data-register is read the
3155 * requested number of times and the results are placed in
3156 * consecutive memory locations starting at the passed
3157 * address.
3158 */
3159 int
fdc_result(struct fdcntlr * fcp,uchar_t * rsltp,uchar_t rcount)3160 fdc_result(struct fdcntlr *fcp, uchar_t *rsltp, uchar_t rcount)
3161 {
3162 int ntries;
3163 uchar_t *abresultp = rsltp;
3164 uchar_t stat;
3165 int laxative = 7;
3166
3167 ntries = 10 * FDC_RQM_RETRY;
3168 do {
3169 do {
3170 if ((inb(fcp->c_regbase + FCR_MSR) &
3171 (MS_RQM | MS_DIO)) == (MS_RQM | MS_DIO))
3172 break;
3173 else
3174 drv_usecwait(10);
3175 } while (--ntries);
3176 if (!ntries) {
3177 FCERRPRINT(FDEP_L3, FDEM_EXEC,
3178 (CE_WARN, "fdc_result: ctlr not ready"));
3179 return (-2);
3180 }
3181 *rsltp++ = inb(fcp->c_regbase + FCR_DATA);
3182
3183 /*
3184 * The PRM suggests waiting for 14.5 us.
3185 * Adding a bit more to cover the case of bad calibration
3186 * of drv_usecwait().
3187 */
3188 drv_usecwait(16);
3189 ntries = FDC_RQM_RETRY;
3190 } while (--rcount);
3191 while ((inb(fcp->c_regbase + FCR_MSR) & MS_CB) && laxative--) {
3192 FCERRPRINT(FDEP_L3, FDEM_EXEC,
3193 (CE_WARN, "fdc_result: ctlr still busy"));
3194 /*
3195 * try to complete Result phase by purging
3196 * result bytes queued for reading
3197 */
3198 *abresultp = S0_IVCMD;
3199 do {
3200 stat = inb(fcp->c_regbase + FCR_MSR) &
3201 (MS_RQM | MS_DIO);
3202 if (stat == MS_RQM) {
3203 /*
3204 * Result phase is complete
3205 * but did we get the results corresponding to
3206 * the command we think we executed?
3207 */
3208 return (-1);
3209 }
3210 if (stat == (MS_RQM | MS_DIO))
3211 break;
3212 else
3213 drv_usecwait(10);
3214 } while (--ntries);
3215 if (!ntries || !laxative) {
3216 FCERRPRINT(FDEP_L3, FDEM_EXEC,
3217 (CE_WARN,
3218 "fdc_result: ctlr still busy and not ready"));
3219 return (-3);
3220 }
3221 (void) inb(fcp->c_regbase + FCR_DATA);
3222
3223 drv_usecwait(16); /* See comment above */
3224 ntries = FDC_RQM_RETRY;
3225 }
3226 return (0);
3227 }
3228
3229 /*
3230 * Function: get_unit()
3231 *
3232 * Assumptions: ioaddr is either 0x3f0 or 0x370
3233 */
3234 static int
get_unit(dev_info_t * dip,int * cntrl_num)3235 get_unit(dev_info_t *dip, int *cntrl_num)
3236 {
3237 int ioaddr;
3238
3239 if (get_ioaddr(dip, &ioaddr) != DDI_SUCCESS)
3240 return (DDI_FAILURE);
3241
3242 switch (ioaddr) {
3243 case 0x3f0:
3244 *cntrl_num = 0;
3245 break;
3246
3247 case 0x370:
3248 *cntrl_num = 1;
3249 break;
3250
3251 default:
3252 return (DDI_FAILURE);
3253 }
3254 return (DDI_SUCCESS);
3255 }
3256
3257 static int
get_ioaddr(dev_info_t * dip,int * ioaddr)3258 get_ioaddr(dev_info_t *dip, int *ioaddr)
3259 {
3260 int reglen, nregs, i;
3261 int status = DDI_FAILURE;
3262 struct {
3263 int bustype;
3264 int base;
3265 int size;
3266 } *reglist;
3267
3268 if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
3269 "reg", (caddr_t)®list, ®len) != DDI_PROP_SUCCESS) {
3270 cmn_err(CE_WARN, "fdc: reg property not found");
3271 return (DDI_FAILURE);
3272 }
3273
3274 nregs = reglen / sizeof (*reglist);
3275 for (i = 0; i < nregs; i++) {
3276 if (reglist[i].bustype == 1) {
3277 *ioaddr = reglist[i].base;
3278 status = DDI_SUCCESS;
3279 break;
3280 }
3281 }
3282 kmem_free(reglist, reglen);
3283
3284 if (status == DDI_SUCCESS) {
3285 if (*ioaddr == 0x3f2 || *ioaddr == 0x372) {
3286 /*
3287 * Some BIOS's (ASUS is one) don't include first
3288 * two IO ports in the floppy controller resources.
3289 */
3290
3291 *ioaddr -= 2; /* step back to 0x3f0 or 0x370 */
3292
3293 /*
3294 * It would be nice to update the regs property as well
3295 * so device pathname contains 3f0 instead of 3f2, but
3296 * updating the regs now won't have this effect as that
3297 * component of the device pathname has already been
3298 * constructed by the ISA nexus driver.
3299 *
3300 * reglist[i].base -= 2;
3301 * reglist[i].size += 2;
3302 * dev = makedevice(ddi_driver_major(dip), 0);
3303 * ddi_prop_update_int_array(dev, dip, "reg",
3304 * (int *)reglist, reglen / sizeof (int));
3305 */
3306 }
3307 }
3308
3309 return (status);
3310 }
3311