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, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #ident  "%Z%%M% %I%     %E% SMI"
  28 
  29 /*
  30  * This file contains no entry points which can be called directly from
  31  * C and hence is of no interest to lint. However, we want to avoid the
  32  * dreaded "Empty translation unit"  warning.
  33  */
  34 
  35 #include <sys/asm_linkage.h>
  36 #include <sys/fdreg.h>
  37 #include <sys/fdvar.h>
  38 #include "fd_assym.h"
  39 
  40 /*
  41  * Since this is part of a Sparc "generic" module, it may be loaded during
  42  * reconfigure time on systems that do not support the fast interrupt
  43  * handler.  On these machines the symbol "impl_setintreg_on" will be
  44  * undefined but we don't want to cause error messages when we load.
  45  */
  46         .weak   impl_setintreg_on
  47         .type   impl_setintreg_on, #function
  48         .weak   fd_softintr_cookie
  49         .type   fd_softintr_cookie, #object
  50 
  51 #define Tmp2    %l4     /* temp register prior to dispatch to right opmode */
  52 #define Reg     %l4     /* pointer to the chip's registers */
  53 #define Fdc     %l3     /* pointer to fdctlr structure */
  54 #define Adr     %l5     /* data address pointer */
  55 #define Len     %l6     /* data length counter */
  56 #define Tmp     %l7     /* general scratch */
  57 #define TRIGGER 0x33
  58         ENTRY(fd_intr)          ! fd standard interrupt handler
  59         save    %sp, -SA(MINFRAME), %sp
  60         !
  61         ! Traverse the list of controllers until we find the first
  62         ! controller expecting an interrupt. Unfortunately, the
  63         ! 82072 floppy controller really doesn't have a way to tell
  64         ! you that it is interrupting.
  65         !
  66         set     fdctlrs, Fdc            ! load list of controllers
  67         ldn     [Fdc], Fdc              ! get the first in the list...
  68 1:      tst     Fdc                     ! do we have any more to check
  69         bz      .panic                  ! Nothing to service. Panic
  70         nop
  71 
  72 3:      ldub    [Fdc + FD_OPMODE], Tmp2 ! load opmode into Tmp2
  73         and     Tmp2, 0x3, Tmp2         ! opmode must be 1, 2, or 3
  74         tst     Tmp2                    ! non-zero?
  75         bnz     .mutex_enter            ! yes!
  76         nop
  77         ldn     [Fdc + FD_NEXT], Tmp    ! Try next ctlr...
  78         tst     Tmp
  79         bnz,a   1b
  80         mov     Tmp, Fdc
  81                                         ! no more controllers
  82         mov     0x2, Tmp2               ! must be spurious or "ready" int
  83 .mutex_enter:
  84         !
  85         ! grab high level mutex for this controller
  86         !
  87         sethi   %hi(asm_mutex_spin_enter), %l7
  88         jmpl    %l7 + %lo(asm_mutex_spin_enter), %l7
  89         add     Fdc, FD_HILOCK, %l6
  90         !
  91         ! dispatch to correct handler
  92         !
  93         cmp     Tmp2, 3                 !case 3: results ?
  94         be,a    .opmode3                ! yes...
  95         ldn     [Fdc + FD_REG], Reg     ! load pointer to h/w registers
  96         cmp     Tmp2, 2                 !case 2: seek/recalibrate ?
  97         be      .opmode2                ! yes..
  98         ldn     [Fdc + FD_REG], Reg     ! load pointer to h/w registers
  99         !
 100         ! opmode 1:
 101         ! read/write/format data-xfer case - they have a result phase
 102         !
 103 .opmode1:
 104         ld      [Fdc + FD_RLEN], Len
 105         !
 106         ! XXX- test for null raddr
 107         !
 108         ldn     [Fdc + FD_RADDR], Adr
 109 
 110         !
 111         ! while the fifo ready bit set, then data/status available
 112         !
 113 1:      ldub    [Reg], Tmp              ! get csr
 114         andcc   Tmp, RQM, %g0           !
 115         be      4f                      ! branch if bit clear
 116         andcc   Tmp, NDM, %g0           ! NDM set means data
 117         be      7f                      ! if not set, it is status time
 118         andcc   Tmp, DIO, %g0           ! check for input vs. output data
 119         be      2f                      !
 120         sub     Len, 0x1, Len           ! predecrement length...
 121         ldub    [Reg + 0x1], Tmp        ! DIO set, *addr = *fifo
 122         b       3f                      !
 123         stb     Tmp, [Adr]              !
 124 2:      ldsb    [Adr], Tmp              ! *fifo = *addr
 125         stb     Tmp, [Reg + 0x1]        !
 126 3:      tst     Len                     ! if (len == 0) send TC
 127         bne     1b                      ! branch if not....
 128         add     Adr, 0x1, Adr           !
 129         b       6f                      !
 130         .empty                          !
 131         !
 132         ! save updated len, addr
 133         !
 134 4:      st      Len, [Fdc + FD_RLEN]
 135         b       .out                    ! not done yet, return
 136         stn     Adr, [Fdc + FD_RADDR]
 137         !
 138         ! END OF TRANSFER - if read/write, toggle the TC
 139         ! bit in AUXIO_REG then save status and set state = 3.
 140         !
 141 5:
 142         !
 143         ! Stash len and addr before they get lost
 144         !
 145         st      Len, [Fdc + FD_RLEN]
 146 6:      stn     Adr, [Fdc + FD_RADDR]
 147         !
 148         ! Begin TC delay...
 149         ! Old comment:
 150         !       five nops provide 100ns of delay at 10MIPS to ensure
 151         !       TC is wide enough at slowest possible floppy clock
 152         !       (500ns @ 250Kbps).
 153         !
 154         ! I gather this mean that we have to give 100ns delay for TC.
 155         !
 156         ! At 100 Mips, that would be 1 * 10 (10) nops.
 157         !
 158 
 159         ldn     [Fdc + FD_AUXIOVA], Adr
 160         ldub    [Fdc + FD_AUXIODATA], Tmp2
 161         ldub    [Adr], Tmp
 162         or      Tmp, Tmp2, Tmp
 163         stb     Tmp, [Adr]
 164         nop; nop; nop; nop; nop; nop; nop; nop; nop; nop        ! 10 nops
 165         !
 166         ! End TC delay...now clear the TC bit
 167         !
 168         ldub    [Fdc + FD_AUXIODATA2], Tmp2
 169         andn    Tmp, Tmp2, Tmp
 170         stb     Tmp, [Adr]
 171         
 172         !
 173         ! set opmode to 3 to indicate going into status mode
 174         !
 175         mov     3, Tmp
 176         b       .out
 177         stb     Tmp, [Fdc + FD_OPMODE]
 178         !
 179         ! error status state: save old pointers, go direct to result snarfing
 180         !
 181 7:      st      Len, [Fdc + FD_RLEN]
 182         stn     Adr, [Fdc + FD_RADDR]
 183         mov     0x3, Tmp
 184         b       .opmode3
 185         stb     Tmp, [Fdc + FD_OPMODE]
 186         !
 187         ! opmode 2:
 188         ! recalibrate/seek - no result phase, must do sense interrupt status.
 189         !
 190 .opmode2:
 191         ldub    [Reg], Tmp                      ! Tmp = *csr
 192 1:      andcc   Tmp, CB, %g0                    ! is CB set?
 193         bne     1b                              ! yes, keep waiting
 194         ldub    [Reg], Tmp                      !! Tmp = *csr
 195         !
 196         ! wait!!! should we check rqm first???  ABSOLUTELY YES!!!!
 197         !
 198 1:      andcc   Tmp, RQM, %g0           !
 199         be,a    1b                      ! branch if bit clear
 200         ldub    [Reg], Tmp              ! busy wait until RQM set
 201         mov     SNSISTAT, Tmp           ! cmd for SENSE_INTERRUPT_STATUS
 202         stb     Tmp, [Reg + 0x1]
 203         !
 204         ! NOTE: we ignore DIO here, assume it is set before RQM!
 205         !
 206         ldub    [Reg], Tmp                      ! busy wait until RQM set
 207 1:      andcc   Tmp, RQM, Tmp
 208         be,a    1b                              ! branch if bit clear
 209         ldub    [Reg], Tmp                      ! busy wait until RQM set
 210         !
 211         ! fdc->c_csb.csb_rslt[0] = *fifo;
 212         !
 213         ldub    [Reg + 0x1], Tmp
 214         stb     Tmp, [Fdc + FD_RSLT]
 215         ldub    [Reg], Tmp                      ! busy wait until RQM set
 216 1:      andcc   Tmp, RQM, Tmp
 217         be,a    1b                              ! branch if bit clear
 218         ldub    [Reg], Tmp                      ! busy wait until RQM set
 219         !
 220         ! fdc->c_csb.csb_rslt[1] = *fifo;
 221         !
 222         ldub    [Reg + 0x1], Tmp
 223         b       .notify
 224         stb     Tmp, [Fdc + FD_RSLT + 1]
 225         !
 226         ! case 3: result mode
 227         ! We are in result mode make sure all status bytes are read out
 228         !
 229         ! We have to have *both* RQM and DIO set.
 230         !
 231 .opmode3:
 232         add     Fdc, FD_RSLT, Adr               ! load address of csb->csb_rslt
 233         add     Adr, 10, Len                    ! put an upper bound on it..
 234         ldub    [Reg], Tmp                      !
 235 1:      andcc   Tmp, CB, %g0                    ! is CB set?
 236         be      .notify                         ! no, jump around, must be done
 237         andcc   Tmp, RQM, %g0                   ! check for RQM in delay slot
 238         be,a    1b                              ! No RQM, go back
 239         ldub    [Reg], Tmp                      ! and load control reg in delay
 240         andcc   Tmp, DIO, %g0                   ! DIO set?
 241         be,a    1b                              ! No DIO, go back
 242         ldub    [Reg], Tmp                      ! and load control reg in delay
 243         !
 244         ! CB && DIO && RQM all true.
 245         ! Time to get a byte.
 246         !
 247         ldub    [Reg + 0x1], Tmp                ! *fifo into Tmp
 248         cmp     Adr, Len                        ! already at our limit?
 249         bge,a   1b                              ! Yes, go back..
 250         ldub    [Reg], Tmp                      ! and load control reg in delay
 251         stb     Tmp, [Adr]                      ! store new byte
 252         add     Adr, 1, Adr                     ! increment address
 253         b       1b                              ! and pop back to the top
 254         ldub    [Reg], Tmp                      ! and load control reg in delay
 255 
 256         !
 257         ! schedule 2nd stage interrupt
 258         !
 259 .notify:
 260         !
 261         ! if fast traps are enabled, use the platform dependent
 262         ! impl_setintreg_on function. 
 263         !
 264         ldub    [Fdc + FD_FASTTRAP], Tmp
 265         tst     Tmp
 266         bnz     .fast   
 267         nop
 268 
 269         !
 270         ! fast traps are not in use.  Do not schedule the soft interrupt
 271         ! at this time.  Wait to trigger it at the end of the handler
 272         ! when the mutexes have been released
 273         !
 274         mov     TRIGGER, Tmp2
 275         b       .out
 276         nop
 277 
 278         !
 279         ! fast traps are enabled.  Schedule the soft interrupt.
 280         ! impl_setintreg uses %l4-%l7
 281         !
 282 .fast:  sethi   %hi(fd_softintr_cookie), %l6
 283         sethi   %hi(impl_setintreg_on), %l7
 284         jmpl    %l7 + %lo(impl_setintreg_on), %l7
 285         ld      [%l6 + %lo(fd_softintr_cookie)], %l6
 286         !
 287         ! set new opmode to 4
 288         !
 289         mov     0x4, Tmp
 290         stb     Tmp, [Fdc + FD_OPMODE]
 291 
 292         !
 293         ! and fall through to exit
 294         !
 295 .out:
 296         !
 297         ! update high level interrupt counter...
 298         !
 299         ldn     [Fdc + FD_HIINTCT], Adr
 300         tst     Adr
 301         be,a    1f
 302         nop
 303         ld      [Adr], Tmp
 304         inc     Tmp
 305         st      Tmp, [Adr]
 306 1:
 307         !
 308         ! Release mutex
 309         !
 310         sethi   %hi(asm_mutex_spin_exit), %l7
 311         jmpl    %l7 + %lo(asm_mutex_spin_exit), %l7
 312         add     Fdc, FD_HILOCK, %l6
 313 
 314         !
 315         ! schedule the soft interrupt if needed 
 316         !
 317         cmp     Tmp2, TRIGGER 
 318         bne     .end
 319         nop
 320 
 321         !       
 322         ! set new opmode to 4
 323         !
 324         mov     0x4, Tmp
 325         stb     Tmp, [Fdc + FD_OPMODE]
 326          
 327         ! invoke ddi_trigger_softintr.  load
 328         ! softid parameter in the delay slot
 329         !
 330         call    ddi_trigger_softintr
 331         ldn     [Fdc + FD_SOFTID], %o0
 332 
 333 .end:   mov     1, %i0
 334         ret
 335         restore
 336         SET_SIZE(fd_intr)
 337 
 338 .panic:
 339         ! invoke a kernel panic
 340         sethi   %hi(panic_msg), %o1
 341         ldn    [%o1 + %lo(panic_msg)], %o1
 342         mov     3, %o0
 343         call    cmn_err
 344         nop
 345 
 346