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  * Copyright 2012 Garrett D'Amore <garrett@damore.org>.  All rights reserved.
  27  */
  28 
  29 
  30 /*
  31  * Floppy Disk Controller Driver
  32  *
  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.
  36  *
  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.
  41  */
  42 
  43 /*
  44  * hack for bugid 1160621:
  45  * workaround compiler optimization bug by turning on DEBUG
  46  */
  47 #ifndef DEBUG
  48 #define DEBUG   1
  49 #endif
  50 
  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>
  63 
  64 #include <sys/autoconf.h>
  65 #include <sys/dkio.h>
  66 #include <sys/vtoc.h>
  67 #include <sys/kstat.h>
  68 
  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>
  76 
  77 /*
  78  * bss (uninitialized data)
  79  */
  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};
  84 
  85 /*
  86  * Local static data
  87  */
  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;
  94 
  95 /*
  96  * error handling
  97  *
  98  * for debugging, set rwretry and skretry = 1
  99  *              set fcerrlevel to 1
 100  *              set fcerrmask  to 224  or 644
 101  *
 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
 105  */
 106 static uint_t fcerrmask = FDEM_ALL;
 107 static int fcerrlevel = 6;
 108 
 109 #define KIOIP   KSTAT_INTR_PTR(fcp->c_intrstat)
 110 
 111 
 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}
 119 };
 120 
 121 static xlate_tbl_t sector_size[] = {
 122         {  256, 1},
 123         {  512, 2},
 124         { 1024, 3},
 125         {    0, 2}
 126 };
 127 
 128 static xlate_tbl_t motor_onbits[] = {
 129         {  0, 0x10},
 130         {  1, 0x20},
 131         {  2, 0x40},
 132         {  3, 0x80},
 133         {  0, 0x80}
 134 };
 135 
 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}
 154 };
 155 
 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}
 175 };
 176 #endif
 177 
 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 */
 205 };
 206 
 207 
 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);
 212 
 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,
 220         0,
 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,
 230 };
 231 
 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);
 238 
 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         (struct cb_ops *)0,     /* driver operations */
 249         &fdc_bus_ops,               /* bus operations */
 250         NULL,                   /* power */
 251         fdc_quiesce,            /* quiesce */
 252 };
 253 
 254 /*
 255  * This is the loadable module wrapper.
 256  */
 257 #include <sys/modctl.h>
 258 
 259 extern struct mod_ops mod_driverops;
 260 
 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 */
 265 };
 266 
 267 static struct modlinkage modlinkage = {
 268         MODREV_1, (void *)&modldrv, NULL
 269 };
 270 
 271 int
 272 _init(void)
 273 {
 274         int retval;
 275 
 276         if ((retval = ddi_soft_state_init(&fdc_state_head,
 277             sizeof (struct fdcntlr) + NFDUN * sizeof (struct fcu_obj), 0)) != 0)
 278                 return (retval);
 279 
 280         if ((retval = mod_install(&modlinkage)) != 0)
 281                 ddi_soft_state_fini(&fdc_state_head);
 282         return (retval);
 283 }
 284 
 285 int
 286 _fini(void)
 287 {
 288         int retval;
 289 
 290         if ((retval = mod_remove(&modlinkage)) != 0)
 291                 return (retval);
 292         ddi_soft_state_fini(&fdc_state_head);
 293         return (retval);
 294 }
 295 
 296 int
 297 _info(struct modinfo *modinfop)
 298 {
 299         return (mod_info(&modlinkage, modinfop));
 300 }
 301 
 302 
 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);
 312 
 313 static struct fcobjops fdc_iops = {
 314                 fdc_abort,      /* controller abort */
 315                 fdc_dkinfo,     /* get disk controller info */
 316 
 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 */
 325 };
 326 
 327 
 328 /*
 329  * Function prototypes
 330  */
 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);
 351 
 352 
 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)
 356 {
 357         struct  fdcntlr *fcp;
 358         struct  fcu_obj *fjp;
 359 
 360         _NOTE(ARGUNUSED(result));
 361 
 362         FCERRPRINT(FDEP_L0, FDEM_ATTA,
 363             (CE_CONT, "fdc_bus_ctl: cmd= %x\n", ctlop));
 364 
 365         if ((fcp = ddi_get_driver_private(dip)) == NULL)
 366                 return (DDI_FAILURE);
 367 
 368         switch (ctlop) {
 369 
 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);
 379 
 380         case DDI_CTLOPS_INITCHILD:
 381         {
 382                 dev_info_t *udip = (dev_info_t *)arg;
 383                 int cntlr;
 384                 int len;
 385                 int unit;
 386                 char name[MAXNAMELEN];
 387 
 388                 FCERRPRINT(FDEP_L3, FDEM_ATTA,
 389                     (CE_WARN, "fdc_bus_ctl: init child 0x%p", (void*)udip));
 390                 cntlr = fcp->c_number;
 391 
 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);
 399 
 400                 (void) sprintf(name, "%d,%d", cntlr, FDUNIT(unit));
 401                 ddi_set_name_addr(udip, name);
 402 
 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;
 409 
 410                 ddi_set_driver_private(udip, fjp);
 411 
 412                 return (DDI_SUCCESS);
 413         }
 414         case DDI_CTLOPS_UNINITCHILD:
 415         {
 416                 dev_info_t *udip = (dev_info_t *)arg;
 417 
 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);
 425         }
 426         default:
 427                 return (DDI_FAILURE);
 428         }
 429 }
 430 
 431 static int
 432 fdc_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
 433 {
 434         struct fdcntlr *fcp;
 435         int rval;
 436 
 437         _NOTE(ARGUNUSED(dip));
 438 
 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;
 448                 }
 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;
 455         }
 456         return (rval);
 457 }
 458 
 459 static int
 460 fdc_probe(dev_info_t *dip)
 461 {
 462         int     debug[2];
 463         int ioaddr;
 464         int     len;
 465         uchar_t stat;
 466 
 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];
 473         }
 474 
 475         FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_probe: dip %p",
 476             (void*)dip));
 477 
 478         if (get_ioaddr(dip, &ioaddr) != DDI_SUCCESS)
 479                 return (DDI_PROBE_FAILURE);
 480 
 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);
 485 
 486         return (DDI_PROBE_SUCCESS);
 487 }
 488 
 489 static int
 490 fdc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 491 {
 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];
 498 
 499         FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_attach: dip %p",
 500             (void*)dip));
 501 
 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);
 513                         }
 514                 } else {
 515                         if (get_unit(dip, &cntlr_num) != DDI_SUCCESS)
 516                                 return (DDI_FAILURE);
 517                 }
 518 
 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);
 523 
 524                 for (unit = 0, fjp = (struct fcu_obj *)(fcp+1);
 525                     unit < NFDUN; unit++) {
 526                         fcp->c_unit[unit] = fjp++;
 527                 }
 528                 fcp->c_dip = dip;
 529 
 530                 if (fdc_propinit1(fcp, cntlr_num) != DDI_SUCCESS)
 531                         goto no_attach;
 532 
 533                 /* get iblock cookie to initialize mutex used in the ISR */
 534                 if (ddi_get_iblock_cookie(dip, (uint_t)0, &fcp->c_iblock) !=
 535                     DDI_SUCCESS) {
 536                         cmn_err(CE_WARN,
 537                             "fdc_attach: cannot get iblock cookie");
 538                         goto no_attach;
 539                 }
 540                 mutex_init(&fcp->c_lock, NULL, MUTEX_DRIVER, fcp->c_iblock);
 541                 intr_set = 1;
 542 
 543                 /* setup interrupt handler */
 544                 if (ddi_add_intr(dip, (uint_t)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;
 549                 }
 550                 intr_set++;
 551 
 552                 /*
 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
 556                  */
 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;
 562                 }
 563                 (void) ddi_dmae_getattr(dip, &fdc_dma_attr);
 564                 fdc_dma_attr.dma_attr_align = MMU_PAGESIZE;
 565 
 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);
 569 
 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);
 575                 }
 576 
 577                 ddi_set_driver_private(dip, fcp);
 578 
 579                 /*
 580                  * reset the controller
 581                  */
 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);
 587 
 588                 /* first test for mode == Model 30 */
 589                 fcp->c_mode = (inb(fcp->c_regbase + FCR_SRB) & 0x1c) ?
 590                     FDCMODE_AT : FDCMODE_30;
 591 
 592                 while (fcp->c_flags & FCFLG_WAITING) {
 593                         cv_wait(&fcp->c_iocv, &fcp->c_lock);
 594                 }
 595                 mutex_exit(&fcp->c_lock);
 596                 sema_v(&fcp->c_selsem);
 597 
 598                 fdc_propinit2(fcp);
 599 
 600                 ddi_report_dev(dip);
 601                 return (DDI_SUCCESS);
 602 
 603         case DDI_RESUME:
 604 
 605                 fcp = ddi_get_driver_private(dip);
 606 
 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);
 612 
 613                 while (fcp->c_flags & FCFLG_WAITING) {
 614                         cv_wait(&fcp->c_iocv, &fcp->c_lock);
 615                 }
 616                 mutex_exit(&fcp->c_lock);
 617 
 618                 /* should be good to go now */
 619                 sema_v(&fcp->c_selsem);
 620 
 621                 return (DDI_SUCCESS);
 622                 /* break; */
 623 
 624         default:
 625                 return (DDI_FAILURE);
 626         }
 627 
 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);
 633         }
 634         ddi_soft_state_free(fdc_state_head, cntlr_num);
 635         return (DDI_FAILURE);
 636 }
 637 
 638 static int
 639 fdc_propinit1(struct fdcntlr *fcp, int cntlr)
 640 {
 641         dev_info_t *dip;
 642         int len;
 643         int value;
 644 
 645         dip = fcp->c_dip;
 646         len = sizeof (value);
 647 
 648         if (get_ioaddr(dip, &value) != DDI_SUCCESS)
 649                 return (DDI_FAILURE);
 650 
 651         fcp->c_regbase = (ushort_t)value;
 652 
 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);
 659         }
 660         fcp->c_dmachan = (ushort_t)value;
 661         fcp->c_number = cntlr;
 662         return (DDI_SUCCESS);
 663 }
 664 
 665 static void
 666 fdc_propinit2(struct fdcntlr *fcp)
 667 {
 668         dev_info_t *dip;
 669         int ccr;
 670         int len;
 671         int value;
 672 
 673         dip = fcp->c_dip;
 674         len = sizeof (value);
 675 
 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;
 684 
 685                 fcp->c_chip = i8272A;
 686                 (void) fdc_docmd(fcp, &versioncmd, 1);
 687                 /*
 688                  * Ignored return. If failed, warning was issued by fdc_docmd.
 689                  * fdc_results retrieves the controller/drive status
 690                  */
 691                 if (!fdc_result(fcp, &result, 1) && result == 0x90) {
 692                         /*
 693                          * try a perpendicular_mode cmd to ensure
 694                          * that we really have an enhanced controller
 695                          */
 696                         if (fdc_docmd(fcp, perpindcmd, 2) ||
 697                             fdc_docmd(fcp, configurecmd, 4))
 698                                 /*
 699                                  * perpindicular_mode will be rejected by
 700                                  * older controllers; make sure we don't hang.
 701                                  */
 702                                 (void) fdc_result(fcp, &result, 1);
 703                                 /*
 704                                  * Ignored return. If failed, warning was
 705                                  * issued by fdc_result.
 706                                  */
 707                         else
 708                                 /* enhanced type controller */
 709 
 710                                 if ((fcp->c_chip = fdc_enhance_probe(fcp)) == 0)
 711                                         /* default enhanced cntlr */
 712                                         fcp->c_chip = i82077;
 713                 }
 714                 (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip,
 715                     "chip", fcp->c_chip);
 716                 /*
 717                  * Ignoring return value because, for passed arguments, only
 718                  * DDI_SUCCESS is returned.
 719                  */
 720         }
 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++) {
 724                         /*
 725                          * run through all the combinations of NOPREC and
 726                          * datarate selection, and see if they show up in the
 727                          * Model 30 DIR
 728                          */
 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;
 735                         }
 736                 }
 737         else
 738                 fcp->c_mode = FDCMODE_AT;
 739         outb(fcp->c_regbase + FCR_CCR, 0);
 740 }
 741 
 742 static int
 743 fdc_enhance_probe(struct fdcntlr *fcp)
 744 {
 745         static uchar_t nsccmd = FO_NSC;
 746         uint_t  ddic;
 747         int     retcode = 0;
 748         uchar_t result;
 749         uchar_t save;
 750 
 751         /*
 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.
 757          */
 758 
 759         (void) fdc_docmd(fcp, &nsccmd, 1);
 760         /*
 761          * Ignored return. If failed, warning was issued by fdc_docmd.
 762          * fdc_results retrieves the controller/drive status
 763          */
 764         if (!fdc_result(fcp, &result, 1) && result != S0_IVCMD) {
 765                 /*
 766                  * only enhanced National Semi PC8477 core
 767                  * should respond to this command
 768                  */
 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);
 778 
 779                 do {
 780                         /* probe for motherboard version of SMC cntlr */
 781 
 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);
 787 
 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;       */
 803                         }
 804                         fcp->c_flags |= FCFLG_3DMODE;
 805                         retcode = FDC37C665;
 806                 } while (retcode == 0);
 807                 outb(fcp->c_regbase + FCR_SRA, FSA_DISB);
 808 
 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);
 815 
 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;       */
 831                         }
 832                         fcp->c_flags |= FCFLG_3DMODE;
 833                         retcode = FDC37C666;
 834                 }
 835                 outb(fcp->c_regbase + FCR_SRA, FSA_DISB);
 836 
 837                 drv_usecwait(10);
 838                 outb(fcp->c_regbase + FCR_SRA, save);
 839         }
 840         return (retcode);
 841 }
 842 
 843 static int
 844 fdc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 845 {
 846         struct fdcntlr *fcp;
 847         int unit;
 848         int rval = 0;
 849 
 850         FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_detach: dip %p",
 851             (void*)dip));
 852 
 853         fcp = ddi_get_driver_private(dip);
 854 
 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;
 861                         }
 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);
 872 
 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;
 879 
 880         case DDI_SUSPEND:
 881                 /*
 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.
 885                  *
 886                  * On resume, we'll reinit the hardware and release the
 887                  * semaphore.
 888                  */
 889                 sema_p(&fcp->c_selsem);
 890 
 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);
 899                 }
 900 
 901                 mutex_enter(&fcp->c_lock);
 902                 fcp->c_suspended = B_TRUE;
 903                 mutex_exit(&fcp->c_lock);
 904 
 905                 rval = DDI_SUCCESS;
 906                 break;
 907 
 908         default:
 909                 rval = EINVAL;
 910                 break;
 911         }
 912         return (rval);
 913 }
 914 
 915 
 916 int
 917 fdc_abort(struct fcu_obj *fjp)
 918 {
 919         struct fdcntlr *fcp = fjp->fj_fdc;
 920         int unit = fjp->fj_unit & 3;
 921 
 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) {
 926                         /*
 927                          * this can cause data corruption !
 928                          */
 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);
 938                 }
 939                 mutex_exit(&fcp->c_lock);
 940                 drv_usecwait(500);
 941                 return (DDI_SUCCESS);
 942         }
 943         return (DDI_FAILURE);
 944 }
 945 
 946 int
 947 fdc_dkinfo(struct fcu_obj *fjp, struct dk_cinfo *dcp)
 948 {
 949         struct fdcntlr *fcp = fjp->fj_fdc;
 950 
 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);
 964 }
 965 
 966 /*
 967  * on=> non-zero = select, 0 = de-select
 968  */
 969 int
 970 fdc_select(struct fcu_obj *fjp, int funit, int on)
 971 {
 972         struct fdcntlr *fcp = fjp->fj_fdc;
 973         int unit = funit & 3;
 974 
 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));
 980 
 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);
 993                 }
 994 
 995                 mutex_enter(&fcp->c_dorlock);
 996 
 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);
1001 
1002                 (void) fdc_motorsm(fjp, FMI_STARTCMD,
1003                     fjp->fj_drive->fdd_motoron);
1004                 /*
1005                  * Return value ignored - fdcmotort deals with failure.
1006                  */
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);
1010                         /*
1011                          * Return value ignored - fdcmotort deals with failure.
1012                          */
1013                 }
1014 
1015                 fcp->c_digout = (fcp->c_digout & ~FD_DRSEL) | (unit & FD_DRSEL);
1016                 outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
1017 
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));
1023 
1024                 mutex_enter(&fcp->c_dorlock);
1025 
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);
1030                 /*
1031                  * Return value ignored - fdcmotort deals with failure.
1032                  */
1033 
1034                 mutex_exit(&fcp->c_dorlock);
1035 
1036                 /* give up controller */
1037                 sema_v(&fcp->c_selsem);
1038         }
1039         return (0);
1040 }
1041 
1042 
1043 int
1044 fdgetchng(struct fcu_obj *fjp, int funit)
1045 {
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));
1049 }
1050 
1051 
1052 int
1053 fdresetchng(struct fcu_obj *fjp, int funit)
1054 {
1055         struct fdcntlr *fcp = fjp->fj_fdc;
1056         int unit = funit & 3;
1057         int newcyl;                     /* where to seek for reset of DSKCHG */
1058 
1059         FCERRPRINT(FDEP_L2, FDEM_CHEK, (CE_NOTE, "fdmediachng unit %d", funit));
1060 
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));
1066 }
1067 
1068 
1069 /*
1070  * fdrecalseek
1071  */
1072 int
1073 fdrecalseek(struct fcu_obj *fjp, int funit, int arg, int execflg)
1074 {
1075         struct fdcntlr *fcp = fjp->fj_fdc;
1076         struct fdcsb *csb;
1077         int unit = funit & 3;
1078         int rval;
1079 
1080         FCERRPRINT(FDEP_L2, FDEM_RECA, (CE_NOTE, "fdrecalseek unit %d to %d",
1081             funit, arg));
1082 
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;
1094         }
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;
1104 
1105         /* send cmd off to fdc_exec */
1106         if (rval = fdc_exec(fcp, 1, execflg))
1107                 goto out;
1108 
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;
1114 
1115         if (fdcsense_drv(fcp, unit))
1116                 cmn_err(CE_WARN, "fdgetchng: write protect check failed");
1117 out:
1118         return (rval);
1119 }
1120 
1121 
1122 /*
1123  * fdrw- used only for read/writing sectors into/from kernel buffers.
1124  */
1125 int
1126 fdrw(struct fcu_obj *fjp, int funit, int rw, int cyl, int head,
1127     int sector, caddr_t bufp, uint_t len)
1128 {
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;
1137 
1138         FCERRPRINT(FDEP_L1, FDEM_RW, (CE_CONT, "fdrw unit %d\n", funit));
1139 
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;
1149         }
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;
1159 
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;
1167 
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);
1175 
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;
1180         }
1181 
1182         /*
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.
1186          */
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;
1193         }
1194 
1195         if (dmar_flags & DDI_DMA_WRITE) {
1196                 bcopy(bufp, aligned_buf, len);
1197         }
1198 
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);
1202 
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;
1213                 }
1214         } else {
1215                 cmn_err(CE_WARN,
1216                     "fdrw: dma addr bind handle failed, rval = %d", rval);
1217                 rval = EINVAL;
1218                 goto out;
1219         }
1220         rval = fdc_exec(fcp, 1, 1);
1221 
1222         if (dmar_flags & DDI_DMA_READ) {
1223                 bcopy(aligned_buf, bufp, len);
1224         }
1225 
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;
1234                 }
1235                 if (mem_handle != NULL) {
1236                         ddi_dma_mem_free(&mem_handle);
1237                 }
1238                 ddi_dma_free_handle(&csb->csb_dmahandle);
1239                 csb->csb_dmahandle = NULL;
1240         }
1241         return (rval);
1242 }
1243 
1244 
1245 int
1246 fdtrkformat(struct fcu_obj *fjp, int funit, int cyl, int head, int filldata)
1247 {
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;
1258 
1259         FCERRPRINT(FDEP_L2, FDEM_FORM,
1260             (CE_NOTE, "fdformattrk unit %d cyl=%d, hd=%d", funit, cyl, head));
1261 
1262         csb = &fcp->c_csb;
1263 
1264         csb->csb_opflags = CSB_OFDMAWT | CSB_OFINRPT;
1265 
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;
1273 
1274         csb->csb_npcyl = (uchar_t)(cyl * fjp->fj_chars->fdc_steps);
1275 
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;
1286 
1287         /*
1288          * alloc space for format track cmd
1289          */
1290         /*
1291          * NOTE: have to add size of fifo also - for dummy format action
1292          */
1293         fmdatlen = 4 * numsctr;
1294 
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;
1299         }
1300 
1301         /*
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.
1305          */
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;
1312         }
1313         dp = (uchar_t *)aligned_buf;
1314 
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];
1324                 }
1325         }
1326 
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);
1330 
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;
1341                 }
1342         } else {
1343                 cmn_err(CE_WARN,
1344                     "fdtrkformat: dma buf bind handle failed, rval = %d",
1345                     rval);
1346                 rval = EINVAL;
1347                 goto out;
1348         }
1349 
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;
1359                 }
1360                 if (mem_handle != NULL) {
1361                         ddi_dma_mem_free(&mem_handle);
1362                 }
1363                 ddi_dma_free_handle(&csb->csb_dmahandle);
1364                 csb->csb_dmahandle = NULL;
1365         }
1366         return (rval);
1367 }
1368 
1369 int
1370 fdrawioctl(struct fcu_obj *fjp, int funit, caddr_t arg)
1371 {
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;
1384 
1385         _NOTE(ARGUNUSED(funit));
1386 
1387         FCERRPRINT(FDEP_L2, FDEM_RAWI,
1388             (CE_NOTE, "fdrawioctl: cmd[0]=0x%x", fdrp->fdr_cmd[0]));
1389 
1390         csb = &fcp->c_csb;
1391 
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;
1396 
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;
1401 
1402         switch (fdrp->fdr_cmd[0] & 0x0f) {
1403 
1404         case FO_SEEK:
1405                 change = 0;
1406                 /* FALLTHROUGH */
1407         case FO_RECAL:
1408                 csb->csb_opflags |= CSB_OFINRPT;
1409                 break;
1410 
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;
1423 
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;
1431 
1432         case FO_RDID:
1433                 csb->csb_opflags |= CSB_OFRESLT | CSB_OFINRPT;
1434                 csb->csb_nrslts = 7;
1435                 break;
1436 
1437         case FO_SDRV:
1438                 sleep = 0;
1439                 csb->csb_nrslts = 1;
1440                 break;
1441 
1442         case FO_SINT:
1443                 sleep = 0;
1444                 change = 0;
1445                 csb->csb_nrslts = 2;
1446                 break;
1447 
1448         case FO_SPEC:
1449                 sleep = 0;
1450                 change = 0;
1451                 break;
1452 
1453         default:
1454                 return (EINVAL);
1455         }
1456 
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;
1463 
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;
1469                 }
1470 
1471                 /*
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.
1475                  */
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;
1482                 }
1483 
1484                 if (dmar_flags & DDI_DMA_WRITE) {
1485                         bcopy(fdrp->fdr_addr, aligned_buf,
1486                             (uint_t)fdrp->fdr_nbytes);
1487                 }
1488 
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);
1494 
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;
1506                         }
1507                 } else {
1508                         cmn_err(CE_WARN, "fdrawioctl: "
1509                             "dma buf bind handle failed, rval = %d", rval);
1510                         rval = EINVAL;
1511                         goto out;
1512                 }
1513         }
1514 
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));
1524 
1525         /*
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.
1530          */
1531 
1532         /*
1533          * VP/ix sense drive ioctl call checks for the error return.
1534          */
1535 
1536         rval_exec = fdc_exec(fcp, sleep, change);
1537 
1538         if (dmar_flags & DDI_DMA_READ) {
1539                 bcopy(aligned_buf, fdrp->fdr_addr, (uint_t)fdrp->fdr_nbytes);
1540         }
1541 
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]));
1547 
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 */
1552 
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;
1561                 }
1562                 if (mem_handle != NULL) {
1563                         ddi_dma_mem_free(&mem_handle);
1564                 }
1565                 ddi_dma_free_handle(&csb->csb_dmahandle);
1566                 csb->csb_dmahandle = NULL;
1567         }
1568         if ((fdrp->fdr_cmd[0] & 0x0f) == FO_SDRV) {
1569                 return (rval_exec);
1570         }
1571         return (rval);
1572 }
1573 
1574 void
1575 encode(xlate_tbl_t *tablep, int val, uchar_t *rcode)
1576 {
1577         do {
1578                 if (tablep->value >= val) {
1579                         *rcode = tablep->code;
1580                         return;
1581                 }
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);
1586 }
1587 
1588 int
1589 decode(xlate_tbl_t *tablep, int kode, int *rvalue)
1590 {
1591         do  {
1592                 if (tablep->code == kode) {
1593                         *rvalue = tablep->value;
1594                         return (0);
1595                 }
1596         } while ((++tablep)->value);
1597         return (-1);
1598 }
1599 
1600 /*
1601  * quiesce(9E) entry point.
1602  *
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.
1606  *
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.
1609  */
1610 int
1611 fdc_quiesce(dev_info_t *dip)
1612 {
1613         struct fdcntlr *fcp;
1614         int ctlr = ddi_get_instance(dip);
1615         int unit;
1616 
1617         fcp = ddi_get_soft_state(fdc_state_head, ctlr);
1618 
1619         if (fcp == NULL)
1620                 return (DDI_FAILURE);
1621 
1622         /*
1623          * If no FD units are attached, there is no need to quiesce.
1624          */
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;
1629                 }
1630         }
1631 
1632         if (unit == NFDUN)
1633                 return (DDI_SUCCESS);
1634 
1635         (void) ddi_dmae_disable(fcp->c_dip, fcp->c_dmachan);
1636 
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);
1642 
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;
1657                         }
1658                         outb(fcp->c_regbase + FCR_DATA, *oplistp++);
1659                         drv_usecwait(16); /* See comment in fdc_result() */
1660                 } while (--count);
1661         }
1662 
1663         return (DDI_SUCCESS);
1664 }
1665 
1666 void
1667 fdcquiesce(struct fdcntlr *fcp)
1668 {
1669         int unit;
1670 
1671         FCERRPRINT(FDEP_L2, FDEM_RESE, (CE_NOTE, "fdcquiesce fcp %p",
1672             (void*)fcp));
1673 
1674         ASSERT(MUTEX_HELD(&fcp->c_lock));
1675         mutex_enter(&fcp->c_dorlock);
1676 
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);
1681 
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);
1687 
1688         mutex_exit(&fcp->c_dorlock);
1689 
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;
1695 
1696         if (fcp->c_chip >= i82077) {
1697                 (void) fdc_docmd(fcp, configurecmd, 4);
1698                 /*
1699                  * Ignored return. If failed, warning was issued by fdc_docmd.
1700                  */
1701         }
1702 }
1703 
1704 void
1705 fdcreadid(struct fdcntlr *fcp, struct fdcsb *csb)
1706 {
1707         static uchar_t readidcmd[2] = {FO_RDID | FO_MFM, 0};
1708 
1709         readidcmd[1] = csb->csb_cmd[1];
1710         (void) fdc_docmd(fcp, readidcmd, 2);
1711 }
1712 
1713 int
1714 fdcseek(struct fdcntlr *fcp, int unit, int cyl)
1715 {
1716         static uchar_t seekabscmd[3] = {FO_SEEK, 0, 0};
1717 
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));
1723 }
1724 
1725 /*
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
1729  */
1730 int
1731 fdcsense_chng(struct fdcntlr *fcp, int unit)
1732 {
1733         int digital_input;
1734 
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);
1741 }
1742 
1743 int
1744 fdcsense_drv(struct fdcntlr *fcp, int unit)
1745 {
1746         static uchar_t sensedrvcmd[2] = {FO_SDRV, 0};
1747         uchar_t senser;
1748         int rval;
1749 
1750         sensedrvcmd[1] = (uchar_t)unit;
1751         (void) fdc_docmd(fcp, sensedrvcmd, 2);
1752         /*
1753          * Ignored return. If failed, warning was issued by fdc_docmd.
1754          * fdc_results retrieves the controller/drive status
1755          */
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);
1764 }
1765 
1766 int
1767 fdcsense_int(struct fdcntlr *fcp, int *unitp, int *cylp)
1768 {
1769         uchar_t senser[2];
1770         int rval;
1771 
1772         (void) fdc_docmd(fcp, &senseintcmd, 1);
1773         /*
1774          * Ignored return. If failed, warning was issued by fdc_docmd.
1775          * fdc_results retrieves the controller/drive status
1776          */
1777 
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];
1785         }
1786         return (rval);
1787 }
1788 
1789 int
1790 fdcspecify(struct fdcntlr *fcp, int xferrate, int steprate, int hlt)
1791 {
1792         static uchar_t perpindcmd[2] = {FO_PERP, 0};
1793         static uchar_t specifycmd[3] = {FO_SPEC, 0, 0};
1794 
1795         encode(drate_mfm, xferrate, &fcp->c_config);
1796         outb(fcp->c_regbase + FCR_CCR, fcp->c_config);
1797 
1798         if (fcp->c_chip >= i82077) {
1799                 /*
1800                  * Use old style perpendicular mode command of 82077.
1801                  */
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);
1811                 /*
1812                  * Ignored return. If failed, warning was issued by fdc_docmd.
1813                  */
1814         }
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));
1820 }
1821 
1822 int
1823 fdcspdchange(struct fdcntlr *fcp, struct fcu_obj *fjp, int rpm)
1824 {
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;
1831 
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);
1836         }
1837 
1838         FCERRPRINT(FDEP_L1, FDEM_SCHG,
1839             (CE_CONT, "fdcspdchange: %d rpm\n", rpm));
1840         ASSERT(MUTEX_HELD(&fcp->c_dorlock));
1841 
1842         switch (fcp->c_chip) {
1843         default:
1844                 break;
1845         case i82077:
1846                 break;
1847 
1848         case PC87322:
1849                 {
1850                 uchar_t nscmodecmd[5] = {FO_MODE, 0x02, 0x00, 0xC8, 0x00};
1851 
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;
1863                 }
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);
1868                 }
1869 
1870                 (void) fdc_docmd(fcp, nscmodecmd, 5);
1871                 /*
1872                  * Ignored return. If failed, warning was issued by fdc_docmd.
1873                  */
1874                 break;
1875                 }
1876 
1877         case FDC37C665:
1878                 enable_code = FSA_ENA5;
1879                 goto SMC_config;
1880 
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;
1897                 }
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);
1902                 }
1903                 save = inb(fcp->c_regbase + FCR_SRA);
1904 
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);
1910 
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);
1915 
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;
1921         }
1922         if (deselect)
1923                 /* reselect drive */
1924                 outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
1925         return (retcode);
1926 }
1927 
1928 static int
1929 fdc_motorsm(struct fcu_obj *fjp, int input, int timeval)
1930 {
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;
1936 
1937         ASSERT(MUTEX_HELD(&fcp->c_dorlock));
1938         old_mstate = fcp->c_mtrstate[unit];
1939         encode(motor_onbits, unit, &motorbit);
1940 
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;
1967                 }
1968                 break;
1969 
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);
1983 
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;
1994                 }
1995                 break;
1996 
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);
2003                 }
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;
2009 
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;
2016 
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;
2036                 }
2037                 break;
2038 
2039         default:
2040                 rval = -3;
2041         }
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;
2056                 }
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);
2064 }
2065 
2066 /*
2067  * fdmotort
2068  *      is called from timeout() when a motor timer has expired.
2069  */
2070 static void
2071 fdmotort(void *arg)
2072 {
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;
2079 
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;
2085 
2086         mutex_enter(&fcp->c_lock);
2087 
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;
2099                 }
2100                 if (newxstate == -1 || newxstate == FXS_END) {
2101                         fcp->c_flags ^= FCFLG_WAITING;
2102                         cv_signal(&fcp->c_iocv);
2103                 }
2104         mutex_exit(&fcp->c_lock);
2105 }
2106 
2107 /*
2108  * DMA interrupt service routine
2109  *
2110  *      Called by EISA dma interrupt service routine when buffer chaining
2111  *      is required.
2112  */
2113 
2114 ddi_dma_cookie_t *
2115 fdc_dmae_isr(struct fdcntlr *fcp)
2116 {
2117         struct fdcsb *csb = &fcp->c_csb;
2118         off_t off;
2119         size_t len;
2120 
2121         if (csb->csb_dmahandle && !csb->csb_cmdstat) {
2122                 if (++csb->csb_dmacurrcookie < csb->csb_dmacookiecnt) {
2123                         ddi_dma_nextcookie(csb->csb_dmahandle,
2124                             &csb->csb_dmacookie);
2125                         return (&csb->csb_dmacookie);
2126                 } else if (++csb->csb_dmacurrwin < csb->csb_dmawincnt) {
2127                         if (ddi_dma_getwin(csb->csb_dmahandle,
2128                             csb->csb_dmacurrwin, &off, &len,
2129                             &csb->csb_dmacookie,
2130                             &csb->csb_dmacookiecnt) != DDI_SUCCESS) {
2131                                 return (NULL);
2132                         }
2133                         csb->csb_dmacurrcookie = 0;
2134                         return (&csb->csb_dmacookie);
2135                 }
2136         } else
2137                 cmn_err(CE_WARN, "fdc: unsolicited DMA interrupt");
2138         return (NULL);
2139 }
2140 
2141 
2142 /*
2143  * returns:
2144  *      0 if all ok,
2145  *      ENXIO - diskette not in drive
2146  *      ETIMEDOUT - for immediate operations that timed out
2147  *      EBUSY - if stupid chip is locked busy???
2148  *      ENOEXEC - for timeout during sending cmds to chip
2149  *
2150  * to sleep: set sleep
2151  * to check for disk changed: set change
2152  */
2153 static int
2154 fdc_exec(struct fdcntlr *fcp, int sleep, int change)
2155 {
2156         struct ddi_dmae_req dmaereq;
2157         struct fcu_obj *fjp;
2158         struct fdcsb *csb;
2159         off_t off;
2160         size_t len;
2161         int unit;
2162 
2163         mutex_enter(&fcp->c_lock);
2164         FCERRPRINT(FDEP_L0, FDEM_EXEC,
2165             (CE_CONT, "fdc_exec: sleep %x change %x\n", sleep, change));
2166         csb = &fcp->c_csb;
2167         unit = csb->csb_drive;
2168         fjp = fcp->c_unit[unit];
2169 
2170         if (csb->csb_opflags & CSB_OFINRPT) {
2171                 if (*csb->csb_cmd == FO_RECAL)
2172                         csb->csb_npcyl = 0;
2173                 else if ((*csb->csb_cmd & ~FO_MFM) != FO_FRMT)
2174                         csb->csb_npcyl =
2175                             csb->csb_cmd[2] * fjp->fj_chars->fdc_steps;
2176                 csb->csb_xstate = FXS_START;
2177         } else
2178                 csb->csb_xstate = FXS_DOIT;
2179         csb->csb_retrys = 0;
2180         csb->csb_ourtrys = 0;
2181 
2182         if (csb->csb_dmahandle) {
2183                 /* ensure that entire format xfer is in one cookie */
2184                 /*
2185                  * The change from  ddi_dma_buf/addr_setup() to
2186                  * ddi_dma_buf/addr_bind_handle() has already loaded
2187                  * the first DMA window and cookie.
2188                  */
2189                 if ((*csb->csb_cmd & ~FO_MFM) == FO_FRMT &&
2190                     (4 * csb->csb_cmd[3]) != csb->csb_dmacookie.dmac_size) {
2191                         mutex_exit(&fcp->c_lock);
2192                         return (EINVAL);
2193                 }
2194         }
2195 
2196 retry:
2197         if (fcp->c_curunit != unit || !(fjp->fj_flags & FUNIT_CHAROK)) {
2198                 fcp->c_curunit = unit;
2199                 fjp->fj_flags |= FUNIT_CHAROK;
2200                 if (fjp->fj_chars->fdc_transfer_rate == 417) {
2201                         /* XXX hack for fdformat */
2202                         /* fjp->fj_chars->fdc_transfer_rate == 500;       */
2203                         fjp->fj_attr->fda_rotatespd = 360;
2204                 }
2205                 if (fdcspecify(fcp, fjp->fj_chars->fdc_transfer_rate,
2206                     fjp->fj_drive->fdd_steprate, 40))
2207                         cmn_err(CE_WARN,
2208                             "fdc_select: controller setup rejected "
2209                             "fdcntrl %p transfer rate %x step rate %x "
2210                             "head load time 40", (void*)fcp,
2211                             fjp->fj_chars->fdc_transfer_rate,
2212                             fjp->fj_drive->fdd_steprate);
2213 
2214                 mutex_enter(&fcp->c_dorlock);
2215                 if (fdcspdchange(fcp, fjp, fjp->fj_attr->fda_rotatespd)) {
2216                         /* 3D drive requires 500 ms for speed change */
2217                         (void) fdc_motorsm(fjp, FMI_RSTARTCMD, 5);
2218                         /*
2219                          * Return value ignored - fdcmotort deals with failure.
2220                          */
2221                 }
2222                 mutex_exit(&fcp->c_dorlock);
2223         }
2224 
2225         /*
2226          * If checking for disk_change is enabled
2227          * (i.e. not seeking in fdresetchng),
2228          * we sample the DSKCHG line to see if the diskette has wandered away.
2229          */
2230         if (change && fdcsense_chng(fcp, unit)) {
2231                 FCERRPRINT(FDEP_L3, FDEM_EXEC,
2232                     (CE_WARN, "diskette %d changed!!!", csb->csb_drive));
2233                 fcp->c_unit[unit]->fj_flags |= FUNIT_CHANGED;
2234                 /*
2235                  * If the diskette is still gone... so are we, adios!
2236                  */
2237                 if (fdcheckdisk(fcp, unit)) {
2238                         mutex_exit(&fcp->c_lock);
2239 
2240                         /* VP/ix expects an EBUSY return here */
2241                         if (*csb->csb_cmd == FO_SDRV) {
2242                                 return (EBUSY);
2243                         }
2244                         return (ENXIO);
2245                 }
2246                 /*
2247                  * delay to ensure that new diskette is up to speed
2248                  */
2249                 mutex_enter(&fcp->c_dorlock);
2250                 (void) fdc_motorsm(fjp, FMI_RSTARTCMD,
2251                     fjp->fj_drive->fdd_motoron);
2252                 /*
2253                  * Return value ignored - fdcmotort deals with failure.
2254                  */
2255                 mutex_exit(&fcp->c_dorlock);
2256         }
2257 
2258         /*
2259          * gather some statistics
2260          */
2261         switch (csb->csb_cmd[0] & 0x1f) {
2262         case FO_RDDAT:
2263                 fcp->fdstats.rd++;
2264                 break;
2265         case FO_WRDAT:
2266                 fcp->fdstats.wr++;
2267                 break;
2268         case FO_RECAL:
2269                 fcp->fdstats.recal++;
2270                 break;
2271         case FO_FRMT:
2272                 fcp->fdstats.form++;
2273                 break;
2274         default:
2275                 fcp->fdstats.other++;
2276                 break;
2277         }
2278 
2279         bzero(csb->csb_rslt, 10);
2280         csb->csb_cmdstat = 0;
2281 
2282         if (csb->csb_dmahandle) {
2283                 bzero(&dmaereq, sizeof (struct ddi_dmae_req));
2284                 dmaereq.der_command = (csb->csb_opflags & CSB_OFDMAWT) ?
2285                     DMAE_CMD_WRITE : DMAE_CMD_READ;
2286                 /*
2287                  * setup for dma buffer chaining regardless of bus capability
2288                  */
2289                 dmaereq.der_bufprocess = DMAE_BUF_CHAIN;
2290                 dmaereq.proc = fdc_dmae_isr;
2291                 dmaereq.procparms = (void *)fcp;
2292                 if (ddi_dmae_prog(fcp->c_dip, &dmaereq, &csb->csb_dmacookie,
2293                     fcp->c_dmachan) != DDI_SUCCESS)
2294                         cmn_err(CE_WARN, "fdc_exec: dmae prog failed, "
2295                             "dip %p, dmachan %x",
2296                             (void*)fcp->c_dip, fcp->c_dmachan);
2297         }
2298 
2299         if ((fdc_statemach(fcp) == FXS_DOWT) && !sleep) {
2300                 /*
2301                  * If the operation has no results - then just return
2302                  */
2303                 if (!csb->csb_nrslts) {
2304                         mutex_exit(&fcp->c_lock);
2305                         return (0);
2306                 }
2307                 /*
2308                  * this operation has no interrupt and an immediate result
2309                  * so wait for the results and stuff them into the csb
2310                  */
2311                 if (fdc_statemach(fcp) == -1) {
2312                         mutex_exit(&fcp->c_lock);
2313                         return (EIO);
2314                 }
2315         } else {
2316                 fcp->c_flags |= FCFLG_WAITING;
2317                 /*
2318                  * wait for completion interrupt
2319                  */
2320                 while (fcp->c_flags & FCFLG_WAITING) {
2321                         cv_wait(&fcp->c_iocv, &fcp->c_lock);
2322                 }
2323         }
2324 
2325         /*
2326          * See if there was an error detected, if so, fdrecover()
2327          * will check it out and say what to do.
2328          *
2329          * Don't do this, though, if this was the Sense Drive Status
2330          * or the Dump Registers command.
2331          */
2332         if (csb->csb_cmdstat && *csb->csb_cmd != FO_SDRV) {
2333                 /* if it can restarted OK, then do so, else return error */
2334                 if (fdrecover(fcp)) {
2335                         mutex_exit(&fcp->c_lock);
2336                         return (EIO);
2337                 }
2338                 /* ASSUMES that cmd is still intact in csb */
2339                 if (csb->csb_xstate == FXS_END)
2340                         csb->csb_xstate = FXS_START;
2341                 if (fdc_dma_attr.dma_attr_sgllen > 1 && csb->csb_dmahandle) {
2342                         /*
2343                          * restarted read/write operation requires
2344                          * first DMA cookie of current window
2345                          */
2346                         if (ddi_dma_getwin(csb->csb_dmahandle,
2347                             csb->csb_dmacurrwin, &off, &len,
2348                             &csb->csb_dmacookie,
2349                             &csb->csb_dmacookiecnt) != DDI_SUCCESS) {
2350 
2351                                 mutex_exit(&fcp->c_lock);
2352                                 return (EIO);
2353                         }
2354                         csb->csb_dmacurrcookie = 0;
2355                 }
2356                 goto retry;
2357         }
2358         /* things went ok */
2359         mutex_exit(&fcp->c_lock);
2360         return (0);
2361 }
2362 
2363 /*
2364  * fdcheckdisk
2365  *      called by fdc_exec to check if the disk is still there - do a seek
2366  *      then see if DSKCHG line went away; if so, diskette is in; else
2367  *      it's (still) out.
2368  */
2369 int
2370 fdcheckdisk(struct fdcntlr *fcp, int unit)
2371 {
2372         struct fdcsb *csb = &fcp->c_csb;
2373         int newcyl;                     /* where to seek for reset of DSKCHG */
2374         int rval;
2375         enum fxstate save_xstate;
2376         uchar_t save_cmd, save_cd1, save_npcyl;
2377 
2378         ASSERT(MUTEX_HELD(&fcp->c_lock));
2379         FCERRPRINT(FDEP_L1, FDEM_CHEK,
2380             (CE_CONT, "fdcheckdisk unit %d\n", unit));
2381 
2382         if (fcp->c_curpcyl[unit])
2383                 newcyl = fcp->c_curpcyl[unit] - 1;
2384         else
2385                 newcyl = 1;
2386 
2387         save_cmd = *csb->csb_cmd;
2388         save_cd1 = csb->csb_cmd[1];
2389         save_npcyl = csb->csb_npcyl;
2390         save_xstate = csb->csb_xstate;
2391 
2392         *csb->csb_cmd = FO_SEEK;
2393         csb->csb_cmd[1] = (uchar_t)unit;
2394         csb->csb_npcyl = (uchar_t)newcyl;
2395         fcp->c_flags |= FCFLG_WAITING;
2396 
2397         if (fcp->c_mtrstate[unit] != FMS_ON && fcp->c_motort[unit] != 0)
2398                 /*
2399                  * wait for motor to get up to speed,
2400                  * and let motor_timer issue seek cmd
2401                  */
2402                 csb->csb_xstate = FXS_DKCHGX;
2403         else {
2404                 /*
2405                  * motor is up to speed; issue seek cmd now
2406                  */
2407                 csb->csb_xstate = FXS_SEEK;
2408                 if (rval = fdcseek(fcp, unit, newcyl)) {
2409                         /*
2410                          * any recal/seek errors are too serious to attend to
2411                          */
2412                         FCERRPRINT(FDEP_L3, FDEM_CHEK,
2413                             (CE_WARN, "fdcheckdisk err %d", rval));
2414                         fcp->c_flags ^= FCFLG_WAITING;
2415                 }
2416         }
2417         /*
2418          * wait for completion interrupt
2419          * XXX This should be backed up with a watchdog timer!
2420          */
2421         while (fcp->c_flags & FCFLG_WAITING) {
2422                 cv_wait(&fcp->c_iocv, &fcp->c_lock);
2423         }
2424 
2425         /*
2426          * if disk change still asserted, no diskette in drive!
2427          */
2428         if (rval = fdcsense_chng(fcp, unit)) {
2429                 FCERRPRINT(FDEP_L3, FDEM_CHEK,
2430                     (CE_WARN, "fdcheckdisk no disk %d", unit));
2431         }
2432 
2433         *csb->csb_cmd = save_cmd;
2434         csb->csb_cmd[1] = save_cd1;
2435         csb->csb_npcyl = save_npcyl;
2436         csb->csb_xstate = save_xstate;
2437         return (rval);
2438 }
2439 
2440 static int
2441 fdrecover(struct fdcntlr *fcp)
2442 {
2443         struct fcu_obj *fjp;
2444         struct fdcsb *csb = &fcp->c_csb;
2445         int residual;
2446         int unit;
2447         char *failure;
2448 
2449         FCERRPRINT(FDEP_L2, FDEM_RECO,
2450             (CE_NOTE, "fdrecover unit %d", csb->csb_drive));
2451 
2452         unit = csb->csb_drive;
2453         fjp = fcp->c_unit[unit];
2454         if (fcp->c_flags & FCFLG_TIMEOUT) {
2455                 fcp->c_flags ^= FCFLG_TIMEOUT;
2456                 csb->csb_rslt[1] |= 0x08;
2457                 FCERRPRINT(FDEP_L3, FDEM_RECO,
2458                     (CE_WARN, "fd unit %d: %s timed out", csb->csb_drive,
2459                     fdcmds[*csb->csb_cmd & 0x1f].cmdname));
2460         }
2461 
2462         if (csb->csb_status & S0_SEKEND)
2463                 fcp->c_curpcyl[unit] = -1;
2464 
2465         switch (csb->csb_oldxs) {
2466         case FXS_RCAL:          /* recalibrate */
2467         case FXS_SEEK:          /* seek */
2468         case FXS_RESET:         /* cntlr reset */
2469                 FCERRPRINT(FDEP_L4, FDEM_RECO, (CE_WARN,
2470                     "fd unit %d: %s error: st0=0x%x pcn=%d", csb->csb_drive,
2471                     fdcmds[*csb->csb_cmd & 0x1f].cmdname,
2472                     *csb->csb_rslt, csb->csb_rslt[1]));
2473                 if (csb->csb_retrys++ < skretry &&
2474                     !(csb->csb_opflags & CSB_OFRAWIOCTL))
2475                         return (0);
2476                 break;
2477 
2478         case FXS_RDID:          /* read ID */
2479                 if (!(csb->csb_status & S0_SEKEND))
2480                         csb->csb_xstate = FXS_HDST;
2481                 /* FALLTHROUGH */
2482         case FXS_DOIT:          /* original operation */
2483         case FXS_DOWT:          /* waiting on operation */
2484                 if (csb->csb_opflags & (CSB_OFDMARD | CSB_OFDMAWT)) {
2485                         if (ddi_dmae_getcnt(fcp->c_dip, fcp->c_dmachan,
2486                             &residual) != DDI_SUCCESS)
2487                                 cmn_err(CE_WARN,
2488                                     "fdc_recover: dmae getcnt failed, "
2489                                     "dip %p dmachan %x residual %x",
2490                                     (void*)fcp->c_dip, fcp->c_dmachan,
2491                                     residual);
2492                         FCERRPRINT(FDEP_L2, FDEM_RECO,
2493                             (CE_NOTE,
2494                             "fd unit %d: %s error: "
2495                             "dma count=0x%lx residual=0x%x",
2496                             csb->csb_drive,
2497                             fdcmds[*csb->csb_cmd & 0x1f].cmdname,
2498                             csb->csb_dmacookie.dmac_size, residual));
2499                 }
2500                 if (csb->csb_rslt[1] == S1_OVRUN)
2501                         /*
2502                          * handle retries of over/underrun
2503                          * with a secondary retry counter
2504                          */
2505                         if (++csb->csb_ourtrys <= OURUN_TRIES) {
2506                                 FCERRPRINT(FDEP_L2, FDEM_RECO,
2507                                     (CE_NOTE,
2508                                     "fd unit %d: %s error: over/under-run",
2509                                     csb->csb_drive,
2510                                     fdcmds[*csb->csb_cmd & 0x1f].cmdname));
2511                                 return (0);
2512                         } else
2513                                 /*
2514                                  * count 1 set of over/underruns
2515                                  * as 1 primary retry effort
2516                                  */
2517                                 csb->csb_ourtrys = 0;
2518 
2519                 if ((fjp->fj_flags & (FUNIT_UNLABELED | FUNIT_LABELOK)) &&
2520                     !(csb->csb_opflags & CSB_OFRAWIOCTL)) {
2521                         /*
2522                          * device is open so keep trying and
2523                          * gather statistics on errors
2524                          */
2525                         if (csb->csb_rslt[1] & S1_CRCER)
2526                                 fcp->fdstats.de++;
2527                         if (csb->csb_rslt[1] & S1_OVRUN)
2528                                 fcp->fdstats.run++;
2529                         if (csb->csb_rslt[1] & (S1_NODATA | S1_MADMK))
2530                                 fcp->fdstats.bfmt++;
2531                         if (csb->csb_rslt[1] & 0x08)
2532                                 fcp->fdstats.to++;
2533 
2534                         /*
2535                          * if we have not run out of retries, return 0
2536                          */
2537                         if (csb->csb_retrys++ < csb->csb_maxretry &&
2538                             (*csb->csb_cmd & ~FO_MFM) != FO_FRMT) {
2539                                 if (csb->csb_opflags &
2540                                     (CSB_OFDMARD | CSB_OFDMAWT)) {
2541                                         FCERRPRINT(FDEP_L4, FDEM_RECO,
2542                                             (CE_WARN,
2543                                             "fd unit %d: %s error: "
2544                                             "st0=0x%x st1=0x%x st2=0x%x",
2545                                             csb->csb_drive,
2546                                             fdcmds[*csb->csb_cmd &
2547                                             0x1f].cmdname,
2548                                             *csb->csb_rslt, csb->csb_rslt[1],
2549                                             csb->csb_rslt[2]));
2550                                 }
2551                                 if ((csb->csb_retrys & 1) &&
2552                                     csb->csb_xstate == FXS_END)
2553                                         csb->csb_xstate = FXS_DOIT;
2554                                 else if (csb->csb_retrys == 3)
2555                                         csb->csb_xstate = FXS_RESTART;
2556                                 return (0);
2557                         }
2558                         if (csb->csb_rslt[1] & S1_CRCER)
2559                                 failure = "crc error";
2560                         else if (csb->csb_rslt[1] & S1_OVRUN)
2561                                 failure = "over/under-run";
2562                         else if (csb->csb_rslt[1] & (S1_NODATA | S1_MADMK))
2563                                 failure = "bad format";
2564                         else if (csb->csb_rslt[1] & 0x08)
2565                                 failure = "timeout";
2566                         else
2567                                 failure = "failed";
2568                         cmn_err(CE_NOTE, "!fd unit %d: %s %s (%x %x %x)",
2569                             csb->csb_drive,
2570                             fdcmds[*csb->csb_cmd & 0x1f].cmdname, failure,
2571                             *csb->csb_rslt, csb->csb_rslt[1], csb->csb_rslt[2]);
2572                 } else {
2573                         FCERRPRINT(FDEP_L2, FDEM_RECO,
2574                             (CE_NOTE, "fd unit %d: %s failed (%x %x %x)",
2575                             csb->csb_drive,
2576                             fdcmds[*csb->csb_cmd & 0x1f].cmdname,
2577                             *csb->csb_rslt, csb->csb_rslt[1],
2578                             csb->csb_rslt[2]));
2579                 }
2580                 break;
2581 
2582         default:
2583                 FCERRPRINT(FDEP_L4, FDEM_RECO, (CE_WARN,
2584                     "fd unit %d: %s failed: st0=0x%x st1=0x%x st2=0x%x",
2585                     csb->csb_drive, fdcmds[*csb->csb_cmd & 0x1f].cmdname,
2586                     *csb->csb_rslt, csb->csb_rslt[1], csb->csb_rslt[2]));
2587                 break;
2588         }
2589         return (1);
2590 }
2591 
2592 
2593 /*      Autovector Interrupt Entry Point        */
2594 static uint_t
2595 fdc_intr(caddr_t arg)
2596 {
2597         struct fdcntlr *fcp = (struct fdcntlr *)arg;
2598         struct fdcsb *csb;
2599         off_t off;
2600         size_t blklen;
2601         int drive;
2602         int newstate;
2603         int pendstate;
2604         int rval = DDI_DMA_DONE;
2605         int state;
2606         int maxspin = 10;
2607 
2608         csb = &fcp->c_csb;
2609 
2610         mutex_enter(&fcp->c_lock);
2611         if (fcp->c_suspended) {
2612                 mutex_exit(&fcp->c_lock);
2613                 return (DDI_INTR_UNCLAIMED);
2614         }
2615 
2616         /*
2617          * Wait for the RQM bit to be set, or until we've tested it
2618          * a bunch of times (which may imply this isn't our interrupt).
2619          */
2620         state = inb(fcp->c_regbase + FCR_MSR);
2621         pendstate = state & (MS_RQM | MS_DIO | MS_CB);
2622         while (((pendstate & MS_RQM) == 0) && (maxspin-- > 0)) {
2623                 /* Small pause in between reading the status port */
2624                 drv_usecwait(10);
2625                 /* Reread the status port */
2626                 state = inb(fcp->c_regbase + FCR_MSR);
2627                 pendstate = state & (MS_RQM | MS_DIO | MS_CB);
2628         }
2629         FCERRPRINT(FDEP_L0, FDEM_INTR,
2630             (CE_CONT, "fdc_intr unit %d: xstate=%d MSR=0x%x\n",
2631             csb->csb_drive, csb->csb_xstate, state));
2632 
2633         /*
2634          * If there is an operation outstanding AND the controller is ready
2635          * to receive a command or send us the result of a command (OR if the
2636          * controller is ready to accept a new command), AND if
2637          * someone has been waiting for a command to finish AND (if no unit
2638          * is BUSY OR if the unit that we're waiting for is BUSY (i.e. it's in
2639          * the middle of a seek/recalibrate)) then this interrupt is for us.
2640          */
2641         if ((pendstate == (MS_RQM | MS_DIO | MS_CB) || pendstate == MS_RQM) &&
2642             (fcp->c_flags & FCFLG_WAITING) &&
2643             (!(state & 0x0f) || ((1 << csb->csb_drive) & state))) {
2644                 /*
2645                  * Remove one of the conditions for entering this code.
2646                  * The state_machine will release the c_lock if it
2647                  * calls untimeout()
2648                  */
2649                 fcp->c_flags ^= FCFLG_WAITING;
2650 
2651                 if ((newstate = fdc_statemach(fcp)) == -1) {
2652                         /* restore waiting flag */
2653                         fcp->c_flags |= FCFLG_WAITING;
2654                         mutex_exit(&fcp->c_lock);
2655                         return (DDI_INTR_CLAIMED);
2656                 }
2657 
2658                 if (fcp->c_intrstat)
2659                         KIOIP->intrs[KSTAT_INTR_HARD]++;
2660                 if (newstate == FXS_END) {
2661 
2662                         if (csb->csb_dmahandle && !csb->csb_cmdstat &&
2663                                 /*
2664                                  * read/write operation may have multiple DMA
2665                                  * cookies: process next one
2666                                  */
2667                             ((csb->csb_dmacurrcookie <
2668                             (csb->csb_dmacookiecnt - 1)) ||
2669                             (csb->csb_dmacurrwin) < (csb->csb_dmawincnt - 1))) {
2670                                 /*
2671                                  * read/write operation requires another
2672                                  * DMA cookie: process next one
2673                                  */
2674 
2675                                 if (++csb->csb_dmacurrcookie <
2676                                     csb->csb_dmacookiecnt) {
2677                                         ddi_dma_nextcookie(csb->csb_dmahandle,
2678                                             &csb->csb_dmacookie);
2679                                 } else if (++csb->csb_dmacurrwin <
2680                                     csb->csb_dmawincnt) {
2681                                         if (ddi_dma_getwin(csb->csb_dmahandle,
2682                                             csb->csb_dmacurrwin, &off, &blklen,
2683                                             &csb->csb_dmacookie,
2684                                             &csb->csb_dmacookiecnt) !=
2685                                             DDI_SUCCESS) {
2686                                                 cmn_err(CE_WARN,
2687                                                     "fdc_intr: "
2688                                                     "dma getwin failed");
2689                                         }
2690                                         csb->csb_dmacurrcookie = 0;
2691                                 }
2692 
2693                                 if (ddi_dmae_prog(fcp->c_dip, NULL,
2694                                     &csb->csb_dmacookie, fcp->c_dmachan) !=
2695                                     DDI_SUCCESS)
2696                                         cmn_err(CE_WARN,
2697                                             "fdc_intr: dmae prog failed, "
2698                                             "dip %p dmachannel %x",
2699                                             (void*)fcp->c_dip,
2700                                             fcp->c_dmachan);
2701 
2702                                 /*
2703                                  * status of last operation has disk
2704                                  * address for continuation
2705                                  */
2706                                 csb->csb_cmd[2] = csb->csb_rslt[3];
2707                                 csb->csb_cmd[3] = csb->csb_rslt[4];
2708                                 csb->csb_cmd[4] = csb->csb_rslt[5];
2709                                 csb->csb_cmd[1] = (csb->csb_cmd[1] & ~0x04) |
2710                                     (csb->csb_cmd[3] << 2);
2711 
2712                                 csb->csb_xstate = FXS_START;
2713                                 (void) fdc_statemach(fcp);
2714                                 /*
2715                                  * Ignored return.  If failed, warning already
2716                                  * posted.  Returned state irrelevant.
2717                                  */
2718                                 /* restore waiting flag */
2719                                 fcp->c_flags |= FCFLG_WAITING;
2720                                 goto fi_exit;
2721                         }
2722                         if (rval != DDI_DMA_DONE)
2723                                 csb->csb_cmdstat = EIO;
2724                         /*
2725                          * somebody's waiting for completion of fdcntlr/csb,
2726                          * wake them
2727                          */
2728                         cv_signal(&fcp->c_iocv);
2729                 }
2730                 else
2731                         /* restore waiting flag */
2732                         fcp->c_flags |= FCFLG_WAITING;
2733 fi_exit:
2734                 mutex_exit(&fcp->c_lock);
2735                 return (DDI_INTR_CLAIMED);
2736         }
2737 
2738         if (state & MS_RQM) {
2739                 (void) fdcsense_int(fcp, &drive, NULL);
2740                 /*
2741                  * Ignored return - senser state already saved
2742                  */
2743                 FCERRPRINT(FDEP_L4, FDEM_INTR,
2744                     (CE_WARN, "fdc_intr unit %d: nobody sleeping 0x%x",
2745                     drive, state));
2746         } else {
2747                 FCERRPRINT(FDEP_L4, FDEM_INTR,
2748                     (CE_WARN, "fdc_intr: nobody sleeping on %d 0x%x",
2749                     csb->csb_drive, state));
2750         }
2751         /*
2752          * This should probably be protected, but, what the
2753          * heck...the cost isn't worth the accuracy for this
2754          * statistic.
2755          */
2756         if (fcp->c_intrstat)
2757                 KIOIP->intrs[KSTAT_INTR_SPURIOUS]++;
2758         mutex_exit(&fcp->c_lock);
2759         return (DDI_INTR_UNCLAIMED);
2760 }
2761 
2762 /*
2763  * fdwatch
2764  *      is called from timeout() when a floppy operation timer has expired.
2765  */
2766 static void
2767 fdwatch(void *arg)
2768 {
2769         struct fdcntlr *fcp = (struct fdcntlr *)arg;
2770         struct fdcsb *csb;
2771 
2772         mutex_enter(&fcp->c_lock);
2773 
2774         if (fcp->c_timeid == 0) {
2775                 /*
2776                  * fdc_intr got here first, ergo, no timeout condition..
2777                  */
2778                 mutex_exit(&fcp->c_lock);
2779                 return;
2780         }
2781 
2782         if (fcp->c_flags & FCFLG_WAITING) {
2783                 if (ddi_dmae_stop(fcp->c_dip, fcp->c_dmachan) != DDI_SUCCESS)
2784                         cmn_err(CE_WARN, "fdwatch: dmae stop failed, "
2785                             "dip %p, dmachan %x",
2786                             (void*)fcp->c_dip, fcp->c_dmachan);
2787                 csb = &fcp->c_csb;
2788                 FCERRPRINT(FDEP_L3, FDEM_WATC,
2789                     (CE_WARN, "fdcwatch unit %d: xstate = %d",
2790                     csb->csb_drive, csb->csb_xstate));
2791                 drv_usecwait(50);
2792 
2793                 if (inb(fcp->c_regbase + FCR_MSR) != MS_RQM) {
2794                         /*
2795                          * cntlr is still busy, so reset it
2796                          */
2797                         csb->csb_xstate = FXS_KILL;
2798                         (void) fdc_statemach(fcp);
2799                         /*
2800                          * Ignored return.  If failed, warning already
2801                          * posted.  Returned state irrelevant.
2802                          */
2803                 } else {
2804                         csb->csb_xstate = FXS_END;
2805                         fcp->c_timeid = 0;
2806                         fcp->c_flags ^= FCFLG_WAITING;
2807                         cv_signal(&fcp->c_iocv);
2808                 }
2809                 csb->csb_cmdstat = EIO;
2810                 fcp->c_flags |= FCFLG_TIMEOUT;
2811         } else {
2812                 FCERRPRINT(FDEP_L4, FDEM_INTR,
2813                     (CE_WARN, "fdcwatch: not sleeping for unit %d",
2814                     fcp->c_csb.csb_drive));
2815         }
2816         if (fcp->c_intrstat)
2817                 KIOIP->intrs[KSTAT_INTR_WATCHDOG]++;
2818         mutex_exit(&fcp->c_lock);
2819 }
2820 
2821 
2822 static int
2823 fdc_statemach(struct fdcntlr *fcp)
2824 {
2825         struct fcu_obj *fjp;
2826         struct fdcsb *csb = &fcp->c_csb;
2827         int backoff;
2828         clock_t time;
2829         int unit;
2830 
2831         ASSERT(MUTEX_HELD(&fcp->c_lock));
2832 
2833         unit = csb->csb_drive;
2834         fjp = fcp->c_unit[unit];
2835 
2836         csb->csb_oldxs = csb->csb_xstate;
2837         switch (csb->csb_xstate) {
2838 
2839         case FXS_START:         /* start of operation */
2840                 ASSERT(fcp->c_timeid == 0);
2841                 time = drv_usectohz(100000 * (unsigned int)csb->csb_timer);
2842                 if (time == 0)
2843                         time = drv_usectohz(2000000);
2844                 fcp->c_timeid = timeout(fdwatch, (void *)fcp, time);
2845 
2846                 if (fcp->c_mtrstate[unit] == FMS_START) {
2847                         /*
2848                          * wait for motor to get up to speed
2849                          */
2850                         csb->csb_xstate = FXS_MTRON;
2851                         break;
2852                 }
2853                 /* FALLTHROUGH */
2854 
2855         case FXS_MTRON:         /* motor is at speed */
2856                 if (fcp->c_mtrstate[unit] != FMS_ON) {
2857                         /* how did we get here ?? */
2858                         cmn_err(CE_WARN, "fdc: selected but motor off");
2859                         return (-1);
2860                 }
2861                 if (fcp->c_curpcyl[unit] != -1 && *csb->csb_cmd != FO_RECAL)
2862                         goto nxs_seek;
2863                 recalcmd[1] = (uchar_t)unit;
2864                 if (fdc_docmd(fcp, recalcmd, 2) == -1) {
2865                         /* cntlr did not accept command bytes */
2866                         fdcquiesce(fcp);
2867                         csb->csb_cmdstat = EIO;
2868                         csb->csb_xstate = FXS_RESET;
2869                         break;
2870                 }
2871                 fcp->c_sekdir[unit] = 0;
2872                 csb->csb_xstate = FXS_RCAL;
2873                 break;
2874 
2875         case FXS_RCAL:          /* forced recalibrate is complete */
2876 #if 0   /* #ifdef _VPIX */
2877         /* WARNING: this code breaks SPARC compatibility */
2878                 if (csb->csb_opflags & CSB_OFRAWIOCTL &&
2879                     *csb->csb_cmd == FO_RECAL) {
2880                         fcp->c_curpcyl[unit] = 0;
2881                         csb->csb_status = 0;
2882                         goto nxs_cmpl;
2883                 }
2884 #endif
2885                 (void) fdc_docmd(fcp, &senseintcmd, 1);
2886                 /*
2887                  * Ignored return. If failed, warning was issued by fdc_docmd.
2888                  * fdc_results retrieves the controller/drive status
2889                  */
2890                 (void) fdc_result(fcp, csb->csb_rslt, 2);
2891                 /*
2892                  * Ignored return. If failed, warning was issued by fdc_result.
2893                  * Actual results checked below
2894                  */
2895                 if ((csb->csb_status = ((*csb->csb_rslt ^ S0_SEKEND) &
2896                     (S0_ICMASK | S0_SEKEND | S0_ECHK | S0_NOTRDY))) != 0) {
2897                         FCERRPRINT(FDEP_L3, FDEM_EXEC,
2898                             (CE_WARN, "fdc_statemach unit %d: recal result %x",
2899                             csb->csb_drive, *csb->csb_rslt));
2900                         fdcquiesce(fcp);
2901                         csb->csb_cmdstat = EIO;
2902                         csb->csb_xstate = FXS_RESET;
2903                         break;
2904                 }
2905                 if (unit != (*csb->csb_rslt & 3) || csb->csb_rslt[1]) {
2906                         csb->csb_status = S0_SEKEND;
2907                         goto nxs_cmpl;
2908                 }
2909                 fcp->c_curpcyl[unit] = csb->csb_rslt[1];
2910                 if (*csb->csb_cmd == FO_RECAL)
2911                         goto nxs_cmpl;
2912 nxs_seek:
2913                 if (*csb->csb_cmd != FO_SEEK &&
2914                     csb->csb_npcyl == fcp->c_curpcyl[unit])
2915                         goto nxs_doit;
2916                 fcp->c_sekdir[unit] = csb->csb_npcyl - fcp->c_curpcyl[unit];
2917                 /* FALLTHROUGH */
2918 
2919         case FXS_DKCHGX:        /* reset Disk-Change latch */
2920                 (void) fdcseek(fcp, csb->csb_cmd[1], csb->csb_npcyl);
2921                 /*
2922                  * Ignored return.  If command rejected, warnig already posted
2923                  * by fdc_docmd().
2924                  */
2925                 csb->csb_xstate = FXS_SEEK;
2926                 break;
2927 
2928         case FXS_RESTART:       /* special restart of read/write operation */
2929                 ASSERT(fcp->c_timeid == 0);
2930                 time = drv_usectohz(100000 * csb->csb_timer);
2931                 if (time == 0)
2932                         time = drv_usectohz(2000000);
2933                 fcp->c_timeid = timeout(fdwatch, (void *)fcp, time);
2934 
2935                 if (fcp->c_mtrstate[unit] != FMS_ON) {
2936                         cmn_err(CE_WARN, "fdc: selected but motor off");
2937                         return (-1);
2938                 }
2939                 if ((csb->csb_npcyl == 0 || fcp->c_sekdir[unit] >= 0) &&
2940                     (int)csb->csb_cmd[2] < (fjp->fj_chars->fdc_ncyl - 1))
2941                         backoff = csb->csb_npcyl + 1;
2942                 else
2943                         backoff = csb->csb_npcyl - 1;
2944                 (void) fdcseek(fcp, csb->csb_cmd[1], backoff);
2945                 /*
2946                  * Ignored return.  If command rejected, warnig already posted
2947                  * by fdc_docmd().
2948                  */
2949                 csb->csb_xstate = FXS_RESEEK;
2950                 break;
2951 
2952         case FXS_RESEEK:        /* seek to backoff-cyl complete */
2953                 (void) fdc_docmd(fcp, &senseintcmd, 1);
2954                 /*
2955                  * Ignored return. If failed, warning was issued by fdc_docmd.
2956                  * fdc_results retrieves the controller/drive status
2957                  */
2958                 (void) fdc_result(fcp, csb->csb_rslt, 2);
2959                 /*
2960                  * Ignored return. If failed, warning was issued by fdc_result.
2961                  * Actual results checked below
2962                  */
2963                 if ((csb->csb_status = ((*csb->csb_rslt ^ S0_SEKEND) &
2964                     (S0_ICMASK | S0_SEKEND | S0_ECHK | S0_NOTRDY))) != 0)
2965                         goto nxs_cmpl;
2966                 (void) fdcseek(fcp, csb->csb_cmd[1], csb->csb_npcyl);
2967                 /*
2968                  * Ignored return.  If command rejected, warnig already posted
2969                  * by fdc_docmd().
2970                  */
2971                 csb->csb_xstate = FXS_SEEK;
2972                 break;
2973 
2974         case FXS_SEEK:          /* seek complete */
2975 #if 0   /* #ifdef _VPIX */
2976         /* WARNING: this code breaks SPARC compatibility and */
2977         /* rawioctls in fdformat */
2978                 if (csb->csb_opflags & CSB_OFRAWIOCTL) {
2979                         fcp->c_curpcyl[unit] = csb->csb_npcyl;
2980                         csb->csb_status = 0;
2981                         goto nxs_cmpl;
2982                 }
2983 #endif
2984                 (void) fdc_docmd(fcp, &senseintcmd, 1);
2985                 /*
2986                  * Ignored return. If failed, warning was issued by fdc_docmd.
2987                  * fdc_results retrieves the controller/drive status
2988                  */
2989                 (void) fdc_result(fcp, csb->csb_rslt, 2);
2990                 /*
2991                  * Ignored return. If failed, warning was issued by fdc_result.
2992                  * Actual results checked below
2993                  */
2994                 if ((csb->csb_status = ((*csb->csb_rslt ^ S0_SEKEND) &
2995                     (S0_ICMASK | S0_SEKEND | S0_ECHK | S0_NOTRDY))) != 0)
2996                         goto nxs_cmpl;
2997                 if (unit != (*csb->csb_rslt & 3) ||
2998                     csb->csb_rslt[1] != csb->csb_npcyl) {
2999                         csb->csb_status = S0_SEKEND;
3000                         goto nxs_cmpl;
3001                 };
3002                 fcp->c_curpcyl[unit] = csb->csb_rslt[1];
3003                 /* use motor_timer to delay for head settle */
3004                 mutex_enter(&fcp->c_dorlock);
3005                 (void) fdc_motorsm(fjp, FMI_DELAYCMD,
3006                     fjp->fj_drive->fdd_headsettle / 1000);
3007                 /*
3008                  * Return value ignored - fdcmotort deals with failure.
3009                  */
3010                 mutex_exit(&fcp->c_dorlock);
3011                 csb->csb_xstate = FXS_HDST;
3012                 break;
3013 
3014         case FXS_HDST:          /* head settle */
3015                 if (*csb->csb_cmd == FO_SEEK)
3016                         goto nxs_cmpl;
3017                 if ((*csb->csb_cmd & ~FO_MFM) == FO_FRMT)
3018                         goto nxs_doit;
3019                 fdcreadid(fcp, csb);
3020                 csb->csb_xstate = FXS_RDID;
3021                 break;
3022 
3023         case FXS_RDID:          /* read ID complete */
3024                 (void) fdc_result(fcp, csb->csb_rslt, 7);
3025                 /*
3026                  * Ignored return. If failed, warning was issued by fdc_result.
3027                  * Actual results checked below
3028                  */
3029                 if ((csb->csb_status = (*csb->csb_rslt &
3030                     (S0_ICMASK | S0_ECHK | S0_NOTRDY))) != 0)
3031                         goto nxs_cmpl;
3032                 if (csb->csb_cmd[2] != csb->csb_rslt[3]) {
3033                         /* at wrong logical cylinder */
3034                         csb->csb_status = S0_SEKEND;
3035                         goto nxs_cmpl;
3036                 };
3037                 goto nxs_doit;
3038 
3039         case FXS_DOIT:          /* do original operation */
3040                 ASSERT(fcp->c_timeid == 0);
3041                 time = drv_usectohz(100000 * csb->csb_timer);
3042                 if (time == 0)
3043                         time = drv_usectohz(2000000);
3044                 fcp->c_timeid = timeout(fdwatch, (void *)fcp, time);
3045 nxs_doit:
3046                 if (fdc_docmd(fcp, csb->csb_cmd, csb->csb_ncmds) == -1) {
3047                         /* cntlr did not accept command bytes */
3048                         fdcquiesce(fcp);
3049                         csb->csb_xstate = FXS_RESET;
3050                         csb->csb_cmdstat = EIO;
3051                         break;
3052                 }
3053                 csb->csb_xstate = FXS_DOWT;
3054                 break;
3055 
3056         case FXS_DOWT:          /* operation complete */
3057                 (void) fdc_result(fcp, csb->csb_rslt, csb->csb_nrslts);
3058                 /*
3059                  * Ignored return. If failed, warning was issued by fdc_result.
3060                  * Actual results checked below.
3061                  */
3062                 if (*csb->csb_cmd == FO_SDRV) {
3063                         csb->csb_status =
3064                             (*csb->csb_rslt ^ (S3_DRRDY | S3_2SIDE)) &
3065                             ~(S3_HEAD | S3_UNIT);
3066                 } else {
3067                         csb->csb_status = *csb->csb_rslt &
3068                             (S0_ICMASK | S0_ECHK | S0_NOTRDY);
3069                 }
3070 nxs_cmpl:
3071                 if (csb->csb_status)
3072                         csb->csb_cmdstat = EIO;
3073                 csb->csb_xstate = FXS_END;
3074 
3075                 /*  remove watchdog timer if armed and not already triggered */
3076                 if (fcp->c_timeid != 0) {
3077                         timeout_id_t timeid;
3078                         timeid = fcp->c_timeid;
3079                         fcp->c_timeid = 0;
3080                         mutex_exit(&fcp->c_lock);
3081                         (void) untimeout(timeid);
3082                         mutex_enter(&fcp->c_lock);
3083                 }
3084                 break;
3085 
3086         case FXS_KILL:          /* quiesce cntlr by reset */
3087                 fdcquiesce(fcp);
3088                 fcp->c_timeid = timeout(fdwatch, (void *)fcp,
3089                     drv_usectohz(2000000));
3090                 csb->csb_xstate = FXS_RESET;
3091                 break;
3092 
3093         case FXS_RESET:         /* int from reset */
3094                 for (unit = 0; unit < NFDUN; unit++) {
3095                         (void) fdcsense_int(fcp, NULL, NULL);
3096                         fcp->c_curpcyl[unit] = -1;
3097                 }
3098                 if (fcp->c_timeid != 0) {
3099                         timeout_id_t timeid;
3100                         timeid = fcp->c_timeid;
3101                         fcp->c_timeid = 0;
3102                         mutex_exit(&fcp->c_lock);
3103                         (void) untimeout(timeid);
3104                         mutex_enter(&fcp->c_lock);
3105                 }
3106                 csb->csb_xstate = FXS_END;
3107                 break;
3108 
3109         default:
3110                 cmn_err(CE_WARN, "fdc: statemach, unknown state");
3111                 return (-1);
3112         }
3113         FCERRPRINT(FDEP_L1, FDEM_EXEC,
3114             (CE_CONT, "fdc_statemach unit %d: %d -> %d\n",
3115             csb->csb_drive, csb->csb_oldxs, csb->csb_xstate));
3116         return (csb->csb_xstate);
3117 }
3118 
3119 
3120 /*
3121  * routine to program a command into the floppy disk controller.
3122  */
3123 int
3124 fdc_docmd(struct fdcntlr *fcp, uchar_t *oplistp, uchar_t count)
3125 {
3126         int ntries;
3127 
3128         ASSERT(count >= 1);
3129         FCERRPRINT(FDEP_L0, FDEM_EXEC,
3130             (CE_CONT, "fdc_docmd: %x %x %x %x %x %x %x %x %x\n",
3131             oplistp[0], oplistp[1], oplistp[2], oplistp[3], oplistp[4],
3132             oplistp[5], oplistp[6], oplistp[7], oplistp[8]));
3133 
3134         do {
3135                 ntries = FDC_RQM_RETRY;
3136                 do {
3137                         if ((inb(fcp->c_regbase + FCR_MSR) & (MS_RQM|MS_DIO))
3138                             == MS_RQM)
3139                                 break;
3140                         else
3141                                 drv_usecwait(1);
3142                 } while (--ntries);
3143                 if (ntries == 0) {
3144                         FCERRPRINT(FDEP_L3, FDEM_EXEC,
3145                             (CE_WARN, "fdc_docmd: ctlr not ready"));
3146                         return (-1);
3147                 }
3148                 outb(fcp->c_regbase + FCR_DATA, *oplistp++);
3149                 drv_usecwait(16);       /* See comment in fdc_result() */
3150         } while (--count);
3151         return (0);
3152 }
3153 
3154 
3155 /*
3156  * Routine to return controller/drive status information.
3157  * The diskette-controller data-register is read the
3158  * requested number of times and the results are placed in
3159  * consecutive memory locations starting at the passed
3160  * address.
3161  */
3162 int
3163 fdc_result(struct fdcntlr *fcp, uchar_t *rsltp, uchar_t rcount)
3164 {
3165         int ntries;
3166         uchar_t *abresultp = rsltp;
3167         uchar_t stat;
3168         int laxative = 7;
3169 
3170         ntries = 10 * FDC_RQM_RETRY;
3171         do {
3172                 do {
3173                         if ((inb(fcp->c_regbase + FCR_MSR) &
3174                             (MS_RQM | MS_DIO)) == (MS_RQM | MS_DIO))
3175                                 break;
3176                         else
3177                                 drv_usecwait(10);
3178                 } while (--ntries);
3179                 if (!ntries) {
3180                         FCERRPRINT(FDEP_L3, FDEM_EXEC,
3181                             (CE_WARN, "fdc_result: ctlr not ready"));
3182                         return (-2);
3183                 }
3184                 *rsltp++ = inb(fcp->c_regbase + FCR_DATA);
3185 
3186                 /*
3187                  * The PRM suggests waiting for 14.5 us.
3188                  * Adding a bit more to cover the case of bad calibration
3189                  * of drv_usecwait().
3190                  */
3191                 drv_usecwait(16);
3192                 ntries = FDC_RQM_RETRY;
3193         } while (--rcount);
3194         while ((inb(fcp->c_regbase + FCR_MSR) & MS_CB) && laxative--) {
3195                 FCERRPRINT(FDEP_L3, FDEM_EXEC,
3196                     (CE_WARN, "fdc_result: ctlr still busy"));
3197                 /*
3198                  * try to complete Result phase by purging
3199                  * result bytes queued for reading
3200                  */
3201                 *abresultp = S0_IVCMD;
3202                 do {
3203                         stat = inb(fcp->c_regbase + FCR_MSR) &
3204                             (MS_RQM | MS_DIO);
3205                         if (stat == MS_RQM) {
3206                                 /*
3207                                  * Result phase is complete
3208                                  * but did we get the results corresponding to
3209                                  * the command we think we executed?
3210                                  */
3211                                 return (-1);
3212                         }
3213                         if (stat == (MS_RQM | MS_DIO))
3214                                 break;
3215                         else
3216                                 drv_usecwait(10);
3217                 } while (--ntries);
3218                 if (!ntries || !laxative) {
3219                         FCERRPRINT(FDEP_L3, FDEM_EXEC,
3220                             (CE_WARN,
3221                             "fdc_result: ctlr still busy and not ready"));
3222                         return (-3);
3223                 }
3224                 (void) inb(fcp->c_regbase + FCR_DATA);
3225 
3226                 drv_usecwait(16);       /* See comment above */
3227                 ntries = FDC_RQM_RETRY;
3228         }
3229         return (0);
3230 }
3231 
3232 /*
3233  *  Function: get_unit()
3234  *
3235  *  Assumptions:  ioaddr is either 0x3f0 or 0x370
3236  */
3237 static int
3238 get_unit(dev_info_t *dip, int *cntrl_num)
3239 {
3240         int ioaddr;
3241 
3242         if (get_ioaddr(dip, &ioaddr) != DDI_SUCCESS)
3243                 return (DDI_FAILURE);
3244 
3245         switch (ioaddr) {
3246         case 0x3f0:
3247                 *cntrl_num = 0;
3248                 break;
3249 
3250         case 0x370:
3251                 *cntrl_num = 1;
3252                 break;
3253 
3254         default:
3255                 return (DDI_FAILURE);
3256         }
3257         return (DDI_SUCCESS);
3258 }
3259 
3260 static int
3261 get_ioaddr(dev_info_t *dip, int *ioaddr)
3262 {
3263         int reglen, nregs, i;
3264         int status = DDI_FAILURE;
3265         struct {
3266                 int bustype;
3267                 int base;
3268                 int size;
3269         } *reglist;
3270 
3271         if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
3272             "reg", (caddr_t)&reglist, &reglen) != DDI_PROP_SUCCESS) {
3273                 cmn_err(CE_WARN, "fdc: reg property not found");
3274                 return (DDI_FAILURE);
3275         }
3276 
3277         nregs = reglen / sizeof (*reglist);
3278         for (i = 0; i < nregs; i++) {
3279                 if (reglist[i].bustype == 1) {
3280                         *ioaddr = reglist[i].base;
3281                         status = DDI_SUCCESS;
3282                         break;
3283                 }
3284         }
3285         kmem_free(reglist, reglen);
3286 
3287         if (status == DDI_SUCCESS) {
3288                 if (*ioaddr == 0x3f2 || *ioaddr == 0x372) {
3289                         /*
3290                          * Some BIOS's (ASUS is one) don't include first
3291                          * two IO ports in the floppy controller resources.
3292                          */
3293 
3294                         *ioaddr -= 2; /* step back to 0x3f0 or 0x370 */
3295 
3296                         /*
3297                          * It would be nice to update the regs property as well
3298                          * so device pathname contains 3f0 instead of 3f2, but
3299                          * updating the regs now won't have this effect as that
3300                          * component of the device pathname has already been
3301                          * constructed by the ISA nexus driver.
3302                          *
3303                          * reglist[i].base -= 2;
3304                          * reglist[i].size += 2;
3305                          * dev = makedevice(ddi_driver_major(dip), 0);
3306                          * ddi_prop_update_int_array(dev, dip, "reg",
3307                          *    (int *)reglist, reglen / sizeof (int));
3308                          */
3309                 }
3310         }
3311 
3312         return (status);
3313 }