root/sys/arch/mac68k/mac68k/pramasm.s
/*      $NetBSD: pramasm.s,v 1.10 2022/05/23 19:52:34 andvar Exp $      */

/*
 * RTC toolkit version 1.08b, copyright 1995, erik vogan
 *
 * All rights and privledges to this code hereby donated
 * to the ALICE group for use in NetBSD.  see the copyright
 * below for more info...
 */
/*
 * Copyright (c) 1995 Erik Vogan
 * All rights reserved.
 *
 * This code is derived from software contributed to the Alice Group
 * by Erik Vogan.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the Alice Group.
 * 4. The names of the Alice Group or any of its members may not be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE ALICE GROUP BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 *  The following are the C interface functions to RTC access functions
 * that are defined later in this file.
 */

#include "opt_adb.h"

#include <m68k/asm.h>

#ifdef MRG_ADB
/*
 * These functions are NOT defined at all if we are not using
 * the MRG method of accessing the ADB/PRAM/RTC.
 */

        .text

        .even
ENTRY(readPram)
        link    %a6,#-4         |  create a little home for ourselves
        .word   0xa03f          |  _InitUtil to read PRam
        moveml  %d1/%a1,%sp@-
        moveq   #0,%d0          |  zero out length register
        moveb   %a6@(19),%d0    |  move the length byte in
        moveq   #0,%d1          |  zero out location 
        moveb   %a6@(15),%d1    |  now get out PRam location
        lea     _C_LABEL(SysParam),%a1  |  start of PRam data
        movel   %a6@(8),%a0     |  get our data address
_readPramAgain:
        subql   #1,%d0
        bcs     _readPramDone   |  see if we are through
        moveb   %a1@(%d1),%a0@+ |  transfer byte
        addql   #1,%d1          |  next byte
        jmp     _readPramAgain  |  do it again 
_readPramDone:
        clrw    %d0
        moveml  %sp@+,%d1/%a1
        unlk    %a6             |  clean up after ourselves
        rts                     |  and return to caller


ENTRY(writePram)
        link    %a6,#-4         |  create a little home for ourselves
        .word   0xa03f          |  _InitUtil to read PRam in the case it hasn't been read yet
        moveml  %d1/%a1,%sp@-
        moveq   #0,%d0          |  zero out length register
        moveb   %a6@(19),%d0    |  move the length byte in
        moveq   #0,%d1          |  zero out location 
        moveb   %a6@(15),%d1    |  now get out PRam location
        lea     _C_LABEL(SysParam),%a1  |  start of PRam data
        movel   %a6@(8),%a0     |  get our data address
_writePramAgain:
        subql   #1,%d0
        bcs     _writePramDone  |  see if we are through
        cmpil   #0x14,%d1       |  check for end of _SysParam
        bcc     _writePramDone  |  do not write if beyond end
        moveb   %a0@+,%a1@(%d1) |  transfer byte
        addql   #1,%d1          |  next byte
        jmp     _writePramAgain |  do it again 
_writePramDone:
        .word   0xa038          |  writeParam
        moveml  %sp@+,%d1/%a1
        unlk    %a6             |  clean up after ourselves
        rts                     |  and return to caller


ENTRY(readExtPram)
        link    %a6,#-4         |  create a little home for ourselves
        moveq   #0,%d0          |  zero out our future command register
        moveb   %a6@(19),%d0    |  move the length byte in
        swap    %d0             |  and make that the MSW
        moveb   %a6@(15),%d0    |  now get out PRAM location
        movel   %a6@(8),%a0     |  get our data address
        .word   0xa051          |  and go read the data
        unlk    %a6             |  clean up after ourselves
        rts                     |  and return to caller

ENTRY(writeExtPram)
        link    %a6,#-4         |  create a little home for ourselves
        moveq   #0,%d0          |  zero out our future command register
        moveb   %a6@(19),%d0    |  move the length byte in
        swap    %d0             |  and make that the MSW
        moveb   %a6@(15),%d0    |  now get out PRAM location
        movel   %a6@(8),%a0     |  get our data address
        .word   0xa052          |  and go write the data
        unlk    %a6             |  clean up after ourselves
        rts                     |  and return to caller

ENTRY(getPramTime)
        link    %a6,#-4         |  create a little home for ourselves
        .word   0xa03f          |  call the routine to read the time (InitUtil)
        movel   _C_LABEL(Time),%d0
        unlk    %a6             |  clean up after ourselves
        rts                     |  and return to caller

ENTRY(setPramTime)
        link    %a6,#-4         |  create a little home for ourselves
        movel   %a6@(8),%d0     |  get the passed in long (seconds since 1904)
        .word   0xa03a          |  call the routine to write the time
        unlk    %a6             |  clean up after ourselves
        rts                     |  and return to caller

#else                   
/* The following routines are the hardware specific routines for the
 * machines that use the II-like method to access the PRAM, and are only
 * defined when the MRG method is not used to access the PRAM.
 */

/*
 *  The following are the C interface functions to RTC access functions
 * that are defined later in this file.
 */

        .text

        .even
ENTRY(readPramII)
        link    %a6,#-4         |  create a little home for ourselves
        moveq   #0,%d0          |  zero out our future command register
        moveb   %a6@(19),%d0    |  move the length byte in
        swap    %d0             |  and make that the MSW
        moveb   %a6@(15),%d0    |  now get out PRAM location
        oriw    #0x0100,%d0     |  and set up for non-extended read
        movel   %a6@(8),%a0     |  get our data address
        jbsr    _C_LABEL(PRAMacc)       |  and go read the data
        unlk    %a6             |  clean up after ourselves
        rts                     |  and return to caller

ENTRY(writePramII)
        link    %a6,#-4         |  create a little home for ourselves
        moveq   #0,%d0          |  zero out our future command register
        moveb   %a6@(19),%d0    |  move the length byte in
        swap    %d0             |  and make that the MSW
        moveb   %a6@(15),%d0    |  now get out PRAM location
        nop                     |  and set up for non-extended write 
        movel   %a6@(8),%a0     |  get our data address
        jbsr    _C_LABEL(PRAMacc)       |  and go write the data
        unlk    %a6             |  clean up after ourselves
        rts                     |  and return to caller

ENTRY(readExtPramII)
        link    %a6,#-4         |  create a little home for ourselves
        moveq   #0,%d0          |  zero out our future command register
        moveb   %a6@(19),%d0    |  move the length byte in
        swap    %d0             |  and make that the MSW
        moveb   %a6@(15),%d0    |  now get out PRAM location
        oriw    #0x0300,%d0     |  and set up for extended read
        movel   %a6@(8),%a0     |  get our data address
        jbsr    _C_LABEL(PRAMacc)       |  and go read the data
        unlk    %a6             |  clean up after ourselves
        rts                     |  and return to caller

ENTRY(writeExtPramII)
        link    %a6,#-4         |  create a little home for ourselves
        moveq   #0,%d0          |  zero out our future command register
        moveb   %a6@(19),%d0    |  move the length byte in
        swap    %d0             |  and make that the MSW
        moveb   %a6@(15),%d0    |  now get out PRAM location
        oriw    #0x0200,%d0     |  and set up for extended write
        movel   %a6@(8),%a0     |  get our data address
        jbsr    _C_LABEL(PRAMacc)       |  and go write the data
        unlk    %a6             |  clean up after ourselves
        rts                     |  and return to caller

ENTRY(getPramTimeII)
        link    %a6,#-4         |  create a little home for ourselves
        jbsr    _C_LABEL(readClock)     |  call the routine to read the time
        unlk    %a6             |  clean up after ourselves
        rts                     |  and return to caller

ENTRY(setPramTimeII)
        link    %a6,#-4         |  create a little home for ourselves
        movel   %a6@(8),%d0     |  get the passed in long (seconds since 1904)
        jbsr    _C_LABEL(writeClock)    |  call the routine to write the time
        unlk    %a6             |  clean up after ourselves
        rts                     |  and return to caller

/*
 *  The following are the RTC access functions used by the interface
 * routines, above.
 */

ENTRY_NOPROFILE(readClock)
        moveml  #0x7cc0,%sp@-   | store off the regs we need
        moveq   #00,%d0         | zero out our result reg
readagan:
        moveq   #00,%d5         | and our temp result reg
        moveq   #03,%d4         | set our count down reg to 4
        movel   #0x00000081,%d1 | read sec byte 0 first
getSecb:
        bsr     _C_LABEL(Transfer)      | get that byte
        rorl    #8,%d5          | shift our time to the right
        swap    %d1             | we want to access our new data
        moveb   %d1,%d5         | move that byte to the spot we vacated
        swap    %d1             | return our PRAM command to orig. config
        addqb   #4,%d1          | increment to the next sec byte
        dbf     %d4,getSecb     | any more bytes to get ?
        cmpl    %d5,%d0         | same secs value we as we just got ?
        beq     gotTime         | we got a good time value
        movel   %d5,%d0         | copy our current time to the compare reg
        bra     readagan        | read the time again
gotTime:
        rorl    #8,%d0          | make that last shift to correctly order
                                |  time bytes!!!
        movel   #0x00d50035,%d1 | we have to set the write protect bit
                                | so the clock doesn't run down !
        bsr     _C_LABEL(Transfer)      | (so sezs Apple...)
        moveml  %sp@+,#0x033e   | restore our regs
        rts                     | and return to caller

ENTRY_NOPROFILE(writeClock)
        moveml  #0x78c0,%sp@-   | store off the regs we need
        moveq   #03,%d4         | set our count down reg to 4
        movel   #0x00550035,%d1 | de-write-protect the PRAM
        bsr     _C_LABEL(Transfer)      | so we can set our value
        moveq   #1,%d1          | write sec byte 0 first
putSecb:
        swap    %d1             | we want access to data byte of command
        moveb   %d0,%d1         | set our first secs byte
        swap    %d1             | and return command to orig. config
        bsr     _C_LABEL(Transfer)      | write that byte
        rorl    #8,%d0          | shift our time to the right 
        addqb   #4,%d1          | increment to the next sec byte
        dbf     %d4,putSecb     | any more bytes to put ?
        movel   #0x00d50035,%d1 | we have to set the write protect bit
                                | so the clock doesn't run down !
        bsr     _C_LABEL(Transfer)      | (so sezs Apple...)
        moveml  %sp@+,#0x031e   | restore our regs
        rts                     | and return to caller

ENTRY_NOPROFILE(PRAMacc)
        moveml  #0xf8c0,%sp@-   | store off the regs we'll use
        moveq   #00,%d3         | zero out our command reg
        moveq   #00,%d4         | zero out our count reg too
        swap    %d0             | we want the length byte
        movew   %d0,%d4         | copy length byte to our counter reg
        swap    %d0             | and return command reg to prior state
        subqb   #1,%d4          | predecrement counter for use w/ DBF
        movew   %d0,%d2         | copy command to %d2
        rorw    #8,%d2          | rotate copy to examine flags
        roxrw   #1,%d2          | read/write bit out of param.
        roxlb   #1,%d3          | and into command reg
        tstb    %d3             | was it read (1) or write (0) ?
        bne     NoWrit          | go around de-write protect logic
        movel   #0x00550035,%d1 | clear write protect bit of PRAM
                                | (we really only need to zero the high
                                |  bit, but other patterns don't work! )
        moveml  #0x3000,%sp@-   | store off the regs that'll change
        bsr     _C_LABEL(Transfer)      | and go de-write protect RTC
        moveml  %sp@+,#0x000c   | reclaim our reg values
NoWrit:
        andib   #1,%d2          | isolate the extended command bit
        beq     oldPRAM         | it's zero, so do old PRAM style access
NuPRAM:
        moveb   %d0,%d2         | reget our PRAM location
        lslw    #4,%d3          | insert our template blanks
        moveq   #2,%d1          | set bit counter for 3 cycles
threebit:
        roxlb   #1,%d2          | rotate address bit from %d2
        roxlw   #1,%d3          | and into command in %d3
        dbf     %d1,threebit    | until we've done bits 7-5
        lslw    #1,%d3          | and add a bit spacer
        moveq   #4,%d1          | ok, 5 bits to go...
fivebit:
        roxlb   #1,%d2          | another addr bit out of %d2
        roxlw   #1,%d3          | and into command template in %d3
        dbf     %d1,fivebit     | til we've done bit 4-0
        lslw    #2,%d3          | more bit magic
        oriw    #0x3880,%d3     | set extended command bits
        bra     Loaddata        | go load the rest of command for xfer rtn
oldPRAM:
        moveb   %d0,%d2         | reget our PRAM location
        lslb    #1,%d3          | add a template blank (bit)
        rolb    #4,%d2          | get low nibble of PRAM loc ready
        moveq   #3,%d1          | set our bit counter for 4 cycles
fourbit:
        roxlb   #1,%d2          | bit out of PRAM loc
        roxlb   #1,%d3          | and bit into PRAM command
        dbf     %d1,fourbit     | until we've done the low nibble
        lslb    #2,%d3          | bump bits to type of command byte
        orib    #0x41,%d3       | set command bits (for access to $0-F!)
        btst    #4,%d2          | change to access $10-13 ?
        beq     Loaddata        | nope, should stay the way it is
        andib   #0x8F,%d3       | clear bits 4-6 of current command
        orib    #0x20,%d3       | and set bit 5 (now accesses $10-13)
Loaddata:
        moveb   %a0@,%d1        | get our (data/dummy) byte into %d1
        swap    %d1             | move (data/dummy) byte to MSW
        movew   %d3,%d1         | now move command into %d1
tagain: 
        bsr     _C_LABEL(Transfer)      | now execute that command
        swap    %d1             | we want access to (data/dummy) byte
        moveb   %d1,%a0@+       | move (data/dummy) byte back to %a0,
        moveb   %a0@,%d1        | NEXT VICTIM!!
        swap    %d1             | now we want to tweak the command
        addqw   #4,%d1          | increment our memory addr by 1 (this even 
                                | works if we want to dump across 32 byte
                                | boundaries for an extended command!!!
                                | thanks to the oriw #$3880 above !!!)
        dbf     %d4,tagain      | repeat until we've got all we want
        movel   #0x00d50035,%d1 | remember that command to write the wp byte ?
                                | set the high bit in the wp reg (Apple sezs
                                | this way the battery won't wear down !! )
        bsr     _C_LABEL(Transfer)      | so we'll play by the rules
        moveml  %sp@+,#0x031f   | restore all our registers
        rts                     | and return to our gracious caller

ENTRY_NOPROFILE(Transfer)
        movew   %sr,%sp@-       | store the SR (we'll change it!)
        oriw    #0x0700,%sr     | disable all interrupts
        moveal  _C_LABEL(Via1Base),%a1  | move VIA1 addr in reference reg
        moveq   #0,%d2          | zero out %d2 (it'll hold VIA1 reg B contents)
        moveb   %a1@,%d2        | and get VIA1 reg B contents
        andib   #0xF8,%d2       | don't touch any but RTC bits
                                | (and zero all those)
        movew   %d1,%d3         | we want to manipulate our command
        andiw   #0xFF00,%d3     | zero the LSB
        beq     oldPRAMc        | do an old PRAM style command
xPRAMc:
        rorw    #8,%d1          | swap the command bytes (1st byte of 2)
        bsr     writebyte       | and write the command byte
        rorw    #8,%d1          | swap the command bytes again (2nd byte of 2)
        bsr     writebyte       | write that byte to RTC too
        moveq   #0x1F,%d3       | r/w bit is $F for an extended command
                                | (but command is swapped to MSW!! so add $10)
        bra     Rwbrnch         | go figure out if it's a read or a write cmd
oldPRAMc:
        bsr     writebyte       | only one byte for an old PRAM command
        moveq   #0x17,%d3       | r/w bit is $7 for and old PRAM command
                                | ( command is swapped to MSW, add $10)
Rwbrnch:
        swap    %d1             | better get that (data/dummy) byte ready
        btst    %d3,%d1         | test bit no. %d3 of reg %d1 (read or write ?)
        beq     Wtrue           | 0 = write, 1 = read (branch on write)
Rtrue:
        bsr     readbyte        | read a byte from the RTC
        bra     Cleanup         | and call mom to clean up after us
Wtrue:
        bsr     writebyte       | write the data to the RTC
Cleanup:
        swap    %d1             | move command to LSW again
        bset    #2,%a1@         | bring the RTC enable line high (end of xfer)
        movew   %sp@+,%sr       | restore prior interrupt status
        rts                     | and return to caller

writebyte:
        moveq   #7,%d3          | set our bit counter to 8
wagain: 
        lsrb    #1,%d2          | ditch the old data channel value
        roxlb   #1,%d1          | and move a new value to X
        roxlb   #1,%d2          | now move value from X to data channel
        moveb   %d2,%a1@        | set our VIA1 reg B contents to match
        bset    #1,%a1@         | and finish strobing the clock line
        dbf     %d3,wagain      | do this until we've sent a whole byte
        lsrb    #1,%d2          | ditch the data channel value one last time
        roxlb   #1,%d1          | get rid of the extra X bit we've carried
        lslb    #1,%d2          | and restore %d2 to prior status
        rts                     | return to caller

readbyte:
        moveq   #7,%d3          | set our bit counter to 8
        bclr    #0,%a1@(0x0400) | set VIA1 reg B data line to input
ragain:
        bclr    #1,%a1@         | strobe the clock line to make
        bset    #1,%a1@         | the data valid
        moveb   %a1@,%d2        | and get out data byte 
        lsrb    #1,%d2          | get the data channel value to X
        roxlb   #1,%d1          | and move X to data byte
        dbf     %d3,ragain      | do this until we've received a whole byte
        bset    #0,%a1@(0x0400) | and return RTC data line to output
        rts                     | return to caller

#endif /* MRG_ADB */