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