///////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   RTClock.cs
//

// #define VERBOSE

using Microsoft.Singularity.Io;

using System;
using System.Threading;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Microsoft.Singularity.Configuration;

namespace Microsoft.Singularity.Hal
{
    //
    // NB: The RTC is on the Triton2 companion chip and we talk
    //     to it on the TI 3430 SDP using the 3430's I2C1 @ 0x4b
    //

    // declare resources for the kernel manifest
    [DriverCategory]
    [Signature("/arm/ti/3430/I2C")]
    public sealed class I2cResources : DriverCategoryDeclaration
    {
        // register block
        //  NB: we may need to share access to i2c1 with peripheral drivers
        [IoMemoryRange(0, Default = 0x48070000, Length = 0x80, Shared = true)]
        internal readonly IoMemoryRange i2cRegisters;

        // interrupt
        //  NB: we may need to share access to i2c1 with peripheral drivers

        // i2c1 - interrupt to drive the i2c controller
        [IoIrqRange(1, Default = 56, Shared = true)]
        internal readonly IoIrqRange i2cIrq;

        // sys_nirq - interrupts directly generated by Triton2
        [IoIrqRange(2, Default = 7, Shared = false)]
        internal readonly IoIrqRange tritonIrq;

        // CTR will create the rest of this class:
        public I2cResources(IoConfig config)
        {
            // dynamic resources
            i2cRegisters = (IoMemoryRange)config.DynamicRanges[0];
            i2cIrq = (IoIrqRange)config.DynamicRanges[1];
            tritonIrq = (IoIrqRange)config.DynamicRanges[2];
        }
    }

    //
    // Wrap up control of the I2C bus to allow us to easily access the RTC
    //
    [CLSCompliant(false)]
    internal sealed class I2cOmap3430
    {
        // size of registers in block
        const int Omap3430_I2C1_RegisterSize                = 0x00000058; // size of registers in block

        // clock frequency
        const uint Omap3430_TIMER1_Freq                     = 32768;

        // registers
        const uint Omap3430_I2C_REV                         = 0x00000000; // i2c block revision
        const uint Omap3430_I2C_IE                          = 0x00000004; // interrupt enable
        const uint Omap3430_I2C_STAT                        = 0x00000008; // [interrupt] status
        const uint Omap3430_I2C_WE                          = 0x0000000c; // wakeup enable
        const uint Omap3430_I2C_SYSS                        = 0x00000010; // [non-interrupt] status
        const uint Omap3430_I2C_BUF                         = 0x00000014; // FIFO control
        const uint Omap3430_I2C_CNT                         = 0x00000018; // FIFO data count
        const uint Omap3430_I2C_DATA                        = 0x0000001c; // FIFO data
        const uint Omap3430_I2C_SYSC                        = 0x00000020; // L4 control
        const uint Omap3430_I2C_CON                         = 0x00000024; // control
        const uint Omap3430_I2C_OA0                         = 0x00000028; // own address 0
        const uint Omap3430_I2C_SA                          = 0x0000002c; // slave address
        const uint Omap3430_I2C_PSC                         = 0x00000030; // prescale sampling clock
        const uint Omap3430_I2C_SCLL                        = 0x00000034; // SCL low time when master
        const uint Omap3430_I2C_SCLH                        = 0x00000038; // SCL high time when master
        const uint Omap3430_I2C_SYSTEST                     = 0x0000003c; // test-mode register
        const uint Omap3430_I2C_BUFSTAT                     = 0x00000040; // FIFO status
        const uint Omap3430_I2C_OA1                         = 0x00000044; // own address 1
        const uint Omap3430_I2C_OA2                         = 0x00000048; // own address 2
        const uint Omap3430_I2C_OA3                         = 0x0000004c; // own address 3
        const uint Omap3430_I2C_ACTOA                       = 0x00000050; // active own addresses
        const uint Omap3430_I2C_SBLOCK                      = 0x00000054; // slave i2c bus locking

        // revision register fields
        const uint Omap3430_I2C_REV_Mask                    = 0x000000ff; // i2c block revision
        const int  Omap3430_I2C_REV_Shift                   = 0;
        const uint Omap3430_I2C_REV_Major_Mask              = 0x000000f0; // i2c block major revision
        const int  Omap3430_I2C_REV_Major_Shift             = 4;
        const uint Omap3430_I2C_REV_Minor_Mask              = 0x0000000f; // i2c block minor revision
        const int  Omap3430_I2C_REV_Minor_Shift             = 0;

        // interrupt enable register fields
        const uint Omap3430_I2C_IE_XDR_IE                   = 0x00004000; // transmit draining
        const uint Omap3430_I2C_IE_RDR_IE                   = 0x00002000; // receive draining
        const uint Omap3430_I2C_IE_AAS_IE                   = 0x00000200; // addressed as slave
        const uint Omap3430_I2C_IE_BF_IE                    = 0x00000100; // bus free
        const uint Omap3430_I2C_IE_AERR_IE                  = 0x00000080; // access error
        const uint Omap3430_I2C_IE_STC_IE                   = 0x00000040; // start condition
        const uint Omap3430_I2C_IE_GC_IE                    = 0x00000020; // general call
        const uint Omap3430_I2C_IE_XRDY_IE                  = 0x00000010; // transmit data ready
        const uint Omap3430_I2C_IE_RRDY_IE                  = 0x00000008; // receive data ready
        const uint Omap3430_I2C_IE_ARDY_IE                  = 0x00000004; // register access ready
        const uint Omap3430_I2C_IE_NACK_IE                  = 0x00000002; // no acknowledgment
        const uint Omap3430_I2C_IE_AL_IE                    = 0x00000001; // arbitration lost

        // [interrupt] status register fields
        const uint Omap3430_I2C_STAT_XDR                    = 0x00004000; // transmit draining
        const uint Omap3430_I2C_STAT_RDR                    = 0x00002000; // receive draining
        const uint Omap3430_I2C_STAT_BB                     = 0x00001000; // bus busy
        const uint Omap3430_I2C_STAT_ROVR                   = 0x00000800; // read overrun
        const uint Omap3430_I2C_STAT_XUDF                   = 0x00000400; // transmit underflow
        const uint Omap3430_I2C_STAT_AAS                    = 0x00000200; // addressed as slave
        const uint Omap3430_I2C_STAT_BF                     = 0x00000100; // bus free
        const uint Omap3430_I2C_STAT_AERR                   = 0x00000080; // access error
        const uint Omap3430_I2C_STAT_STC                    = 0x00000040; // start condition
        const uint Omap3430_I2C_STAT_GC                     = 0x00000020; // general call
        const uint Omap3430_I2C_STAT_XRDY                   = 0x00000010; // transmit data ready
        const uint Omap3430_I2C_STAT_RRDY                   = 0x00000008; // receive data ready
        const uint Omap3430_I2C_STAT_ARDY                   = 0x00000004; // register access ready
        const uint Omap3430_I2C_STAT_NACK                   = 0x00000002; // no acknowledgment
        const uint Omap3430_I2C_STAT_AL                     = 0x00000001; // arbitration lost
        const uint Omap3430_I2C_STAT_Isr_Mask               = 0x000063ff; // ISR mask

        // wakeup enable register fields
        const uint Omap3430_I2C_WE_XDR_WE                   = 0x00004000; // transmit draining
        const uint Omap3430_I2C_WE_RDR_WE                   = 0x00002000; // receive draining
        const uint Omap3430_I2C_WE_AAS_WE                   = 0x00000200; // addressed as slave
        const uint Omap3430_I2C_WE_BF_WE                    = 0x00000100; // bus free
        const uint Omap3430_I2C_WE_STC_WE                   = 0x00000040; // start condition
        const uint Omap3430_I2C_WE_GC_WE                    = 0x00000020; // general call
        const uint Omap3430_I2C_WE_DRDY_WE                  = 0x00000008; // transmit/receive data ready
        const uint Omap3430_I2C_WE_ARDY_WE                  = 0x00000004; // register access ready
        const uint Omap3430_I2C_WE_NACK_WE                  = 0x00000002; // no acknowledgment
        const uint Omap3430_I2C_WE_AL_WE                    = 0x00000001; // arbitration lost

        // [non-interrupt] status register fields
        const uint Omap3430_I2C_SSYS_RDONE                  = 0x00000001; // reset done

        // FIFO control register fields
        const uint Omap3430_I2C_BUF_RDMA_EN                 = 0x00008000; // receive DMA enable
        const uint Omap3430_I2C_BUF_RXFIFO_CLR              = 0x00004000; // receive FIFO clear
        const uint Omap3430_I2C_BUF_RTRSH_Mask              = 0x00003f00; // receive threshold
        const int  Omap3430_I2C_BUF_RTRSH_Shift             = 8;
        const uint Omap3430_I2C_BUF_XDMA_EN                 = 0x00000080; // transmit DMA enable
        const uint Omap3430_I2C_BUF_TXFIFO_CLR              = 0x00000040; // transmit FIFO clear
        const uint Omap3430_I2C_BUF_XTRSH_Mask              = 0x0000003f; // transmit threshold
        const int  Omap3430_I2C_BUF_XTRSH_Shift             = 0;

        // FIFO data count register fields
        const uint Omap3430_I2C_CNT_Mask                    = 0x0000ffff; // FIFO count
        const int  Omap3430_I2C_CNT_Shift                   = 0;

        // FIFO data register fields
        const uint Omap3430_I2C_DATA_Mask                   = 0x000000ff; // FIFO data
        const int  Omap3430_I2C_DATA_Shift                  = 0;

        // L4 control register fields
        const uint Omap3430_I2C_SYSC_CLOCKACTIVIY_Function  = 0x00000200; // function clk on in idle
        const uint Omap3430_I2C_SYSC_CLOCKACTIVIY_Interface = 0x00000100; // interface clk on in idle
        const uint Omap3430_I2C_SYSC_IDLEMODE_Force         = 0x00000000; // force idle mode
        const uint Omap3430_I2C_SYSC_IDLEMODE_None          = 0x00000080; // never idle
        const uint Omap3430_I2C_SYSC_IDLEMODE_Smart         = 0x00000100; // smart idling
        const uint Omap3430_I2C_SYSC_IDLEMODE_Mask          = 0x00000180; // idle mode mask
        const uint Omap3430_I2C_SYSC_ENAWAKEUP              = 0x00000004; // wakeup enable
        const uint Omap3430_I2C_SYSC_SRST                   = 0x00000002; // software reset
        const uint Omap3430_I2C_SYSC_AUTOIDLE               = 0x00000001; // auto-idle enable

        // control register fields
        const uint Omap3430_I2C_CON_I2C_EN                  = 0x00008000; // i2c module enable
        const uint Omap3430_I2C_CON_OPMODE_Standard_Fast    = 0x00000000; // i2c standard/fast mode
        const uint Omap3430_I2C_CON_OPMODE_High_Speed       = 0x00001000; // i2c high speed mode
        const uint Omap3430_I2C_CON_OPMODE_SCCB             = 0x00002000; // SCCB mode
        const uint Omap3430_I2C_CON_OPMODE_Mask             = 0x00003000; // receive FIFO clear
        const uint Omap3430_I2C_CON_STB                     = 0x00000800; // start byte mode (master)
        const uint Omap3430_I2C_CON_MST                     = 0x00000400; // master/slave select
        const uint Omap3430_I2C_CON_TRX                     = 0x00000200; // transmit/receive select
        const uint Omap3430_I2C_CON_XSA                     = 0x00000100; // expanded slave addresses
        const uint Omap3430_I2C_CON_XOA0                    = 0x00000080; // expand own address 0
        const uint Omap3430_I2C_CON_XOA1                    = 0x00000040; // expand own address 1
        const uint Omap3430_I2C_CON_XOA2                    = 0x00000020; // expand own address 2
        const uint Omap3430_I2C_CON_XOA3                    = 0x00000010; // expand own address 3
        const uint Omap3430_I2C_CON_STP                     = 0x00000002; // stop condition (master)
        const uint Omap3430_I2C_CON_STT                     = 0x00000001; // start condition (master)

        // own address 0 register fields
        const uint Omap3430_I2C_OA0_MCODE_Mask              = 0x0000e000; // master code
        const int  Omap3430_I2C_OA0_MCODE_Shift             = 13;

        // own address 1 register fields
        // own address 2 register fields
        // own address 3 register fields
        const uint Omap3430_I2C_OAn_OA_Mask                 = 0x000003ff; // own address
        const int  Omap3430_I2C_OAn_OA_Shift                = 0;

        // slave address register fields
        const uint Omap3430_I2C_SA_SA_Mask                  = 0x000003ff; // slave address
        const int  Omap3430_I2C_SA_SA_Shift                 = 0;

        // prescale sampling clock register fields
        const uint Omap3430_I2C_PSC_PSC_Mask                = 0x0000000f; // prescale sampling clock
        const int  Omap3430_I2C_PSC_PSC_Shift               = 0;

        // SCL low time when master register fields
        const uint Omap3430_I2C_SCLL_HSSCLL_Mask            = 0x0000ff00; // SCL low time when master in h/s mode
        const int  Omap3430_I2C_SCLL_HSSCLL_Shift           = 8;
        const uint Omap3430_I2C_SCLL_SCLL_Mask              = 0x000000ff; // SCL low time when master in standard/fast mode
        const int  Omap3430_I2C_SCLL_SCLL_Shift             = 0;

        // SCL high time when master register fields
        const uint Omap3430_I2C_SCLH_HSSCLH_Mask            = 0x0000ff00; // SCL high time when master in h/s mode
        const int  Omap3430_I2C_SCLH_HSSCLH_Shift           = 8;
        const uint Omap3430_I2C_SCLH_SCLH_Mask              = 0x000000ff; // SCL high time when master in standard/fast mode
        const int  Omap3430_I2C_SCLH_SCLH_Shift             = 0;

        // test-mode register register fields
        const uint Omap3430_I2C_SYSTEST_ST_EN               = 0x00008000; // system test enable
        const uint Omap3430_I2C_SYSTEST_FREE                = 0x00004000; // free-running mode
        const uint Omap3430_I2C_SYSTEST_TMODE_Functional    = 0x00000000; // functional test
        const uint Omap3430_I2C_SYSTEST_TMODE_Counters      = 0x00002000; // counters test
        const uint Omap3430_I2C_SYSTEST_TMODE_Loopback_Io   = 0x00003000; // loopback / sda/scl io test
        const uint Omap3430_I2C_SYSTEST_TMODE_Mask          = 0x00003000; // test mode mask
        const uint Omap3430_I2C_SYSTEST_SSB                 = 0x00000800; // set status bits
        const uint Omap3430_I2C_SYSTEST_SCCBE_O             = 0x00000010; // SCCBE line drive output
        const uint Omap3430_I2C_SYSTEST_SCL_I               = 0x00000008; // sense SCL line input
        const uint Omap3430_I2C_SYSTEST_SCL_O               = 0x00000004; // drive SCL line output
        const uint Omap3430_I2C_SYSTEST_SDA_I               = 0x00000002; // sense SDA line input
        const uint Omap3430_I2C_SYSTEST_SDA_O               = 0x00000001; // drive SDA line output

        // FIFO status register fields
        const uint Omap3430_I2C_BUFSTAT_FIFODEPTH_Mask      = 0x0000c000; // lg2(FIFO depth) - 3
        const int  Omap3430_I2C_BUFSTAT_FIFODEPTH_Shift     = 14;
        const uint Omap3430_I2C_BUFSTAT_RXSTAT_Mask         = 0x00003f00; // receive count
        const int  Omap3430_I2C_BUFSTAT_RXSTAT_Shift        = 8;
        const uint Omap3430_I2C_BUFSTAT_TXSTAT_Mask         = 0x0000003f; // transmit count
        const int  Omap3430_I2C_BUFSTAT_TXSTAT_Shift        = 0;

        // active own addresses register fields
        const uint Omap3430_I2C_ACTOA_OA3_ACT               = 0x00000008; // own addresses 3 active
        const uint Omap3430_I2C_ACTOA_OA2_ACT               = 0x00000004; // own addresses 2 active
        const uint Omap3430_I2C_ACTOA_OA1_ACT               = 0x00000002; // own addresses 1 active
        const uint Omap3430_I2C_ACTOA_OA0_ACT               = 0x00000001; // own addresses 0 active

        // slave i2c bus locking register fields
        const uint Omap3430_I2C_SBLOCK_OA3_EN               = 0x00000008; // i2c clk blocked for own address 3
        const uint Omap3430_I2C_SBLOCK_OA2_EN               = 0x00000004; // i2c clk blocked for own address 2
        const uint Omap3430_I2C_SBLOCK_OA1_EN               = 0x00000002; // i2c clk blocked for own address 1
        const uint Omap3430_I2C_SBLOCK_OA0_EN               = 0x00000001; // i2c clk blocked for own address 0

        // resource allocation
        private PnpConfig config;
        private byte irq;
        private Pic pic;

        // registers
        private IoMappedPort i2c_rev;       // i2c block revision
        private IoMappedPort i2c_ie;        // interrupt enable
        private IoMappedPort i2c_stat;      // [interrupt] status
        private IoMappedPort i2c_we;        // wakeup enable
        private IoMappedPort i2c_syss;      // [non-interrupt] status
        private IoMappedPort i2c_buf;       // FIFO control
        private IoMappedPort i2c_cnt;       // FIFO data count
        private IoMappedPort i2c_data;      // FIFO data
        private IoMappedPort i2c_sysc;      // L4 control
        private IoMappedPort i2c_con;       // control
        private IoMappedPort i2c_oa0;       // own address 0
        private IoMappedPort i2c_sa;        // slave address
        private IoMappedPort i2c_psc;       // prescale sampling clock
        private IoMappedPort i2c_scll;      // SCL low time when master
        private IoMappedPort i2c_sclh;      // SCL high time when master
        private IoMappedPort i2c_systest;   // test-mode register
        private IoMappedPort i2c_bufstat;   // FIFO status
        private IoMappedPort i2c_oa1;       // own address 1
        private IoMappedPort i2c_oa2;       // own address 2
        private IoMappedPort i2c_oa3;       // own address 3
        private IoMappedPort i2c_actoa;     // active own addresses
        private IoMappedPort i2c_sblock;    // slave i2c bus locking

        // Constructor
        internal I2cOmap3430(I2cResources ir, Pic pic)
        {
            DebugStub.Print("I2cOmap3430: create\n");

            this.config = config;
            this.pic = pic;
            this.irq = ir.i2cIrq.Irq;

            IoMemory i2cRegisters = ir.i2cRegisters
                .MemoryAtOffset(0, Omap3430_I2C1_RegisterSize, Access.ReadWrite);

            i2c_rev     = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_REV, 4, Access.Read);
            i2c_ie      = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_IE, 4, Access.ReadWrite);
            i2c_stat    = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_STAT, 4, Access.ReadWrite);
            i2c_we      = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_WE, 4, Access.ReadWrite);
            i2c_syss    = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_SYSS, 4, Access.Read);
            i2c_buf     = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_BUF, 4, Access.ReadWrite);
            i2c_cnt     = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_CNT, 4, Access.ReadWrite);
            i2c_data    = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_DATA, 4, Access.ReadWrite);
            i2c_sysc    = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_SYSC, 4, Access.ReadWrite);
            i2c_con     = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_CON, 4, Access.ReadWrite);
            i2c_oa0     = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_OA0, 4, Access.ReadWrite);
            i2c_sa      = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_SA, 4, Access.ReadWrite);
            i2c_psc     = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_PSC, 4, Access.ReadWrite);
            i2c_scll    = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_SCLL, 4, Access.ReadWrite);
            i2c_sclh    = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_SCLH, 4, Access.ReadWrite);
            i2c_systest = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_SYSTEST, 4, Access.ReadWrite);
            i2c_bufstat = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_BUFSTAT, 4, Access.Read);
            i2c_oa1     = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_OA1, 4, Access.ReadWrite);
            i2c_oa2     = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_OA2, 4, Access.ReadWrite);
            i2c_oa3     = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_OA3, 4, Access.ReadWrite);
            i2c_actoa   = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_ACTOA, 4, Access.Read);
            i2c_sblock  = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_SBLOCK, 4, Access.ReadWrite);
        }

        public byte Initialize()
        {
#if VERBOSE
            Tracing.Log(Tracing.Debug, "I2COmap3430.Initialize()");
#endif

            // Take i2c controller into reset
            ushort val16 = i2c_con.Read16();
            val16 = (ushort) (val16 & ~((ushort)Omap3430_I2C_CON_I2C_EN));
            IoResult result = i2c_con.Write16NoThrow(val16);
            DebugStub.Assert(IoResult.Success == result);

            // Configure internal sampling frequency for 12Mhz.
            result = i2c_psc.Write8NoThrow((byte)((0x7 << Omap3430_I2C_PSC_PSC_Shift) & Omap3430_I2C_PSC_PSC_Mask));
            DebugStub.Assert(IoResult.Success == result);

            // Configure SCL high/low times for 100 kb/s
            result = i2c_scll.Write8NoThrow((byte)((0x36 << Omap3430_I2C_SCLL_SCLL_Shift) & Omap3430_I2C_SCLL_SCLL_Mask));
            DebugStub.Assert(IoResult.Success == result);

            result = i2c_sclh.Write8NoThrow((byte)((0x38 << Omap3430_I2C_SCLH_SCLH_Shift) & Omap3430_I2C_SCLH_SCLH_Mask));
            DebugStub.Assert(IoResult.Success == result);

            // Set own address address register
            result = i2c_oa0.Write16NoThrow((ushort)((0x21 << Omap3430_I2C_OAn_OA_Shift) & Omap3430_I2C_OAn_OA_Mask));
            DebugStub.Assert(IoResult.Success == result);

            // Take i2c controller out of reset
            result = i2c_con.Read16NoThrow(out val16);
            DebugStub.Assert(IoResult.Success == result);

            val16 |= (ushort)Omap3430_I2C_CON_I2C_EN;

            result = i2c_con.Write16NoThrow(val16);
            DebugStub.Assert(IoResult.Success == result);

            // Enable i2c interrupts
            pic.EnableIrq(irq);
            return pic.IrqToInterrupt(irq);
        }

        [NoHeapAllocation]
        public bool MasterSend(byte slaveAddress, byte[] data, bool genStart, bool genStop)
        {
            return MasterCommon(true, slaveAddress, data, genStart, genStop);
        }

        [NoHeapAllocation]
        public bool MasterReceive(byte slaveAddress, byte[] data, bool genStart, bool genStop)
        {
            return MasterCommon(false, slaveAddress, data, genStart, genStop);
        }

        [NoHeapAllocation]
        private bool MasterSetup(
            bool xmit,
            byte slaveAddress,
            ushort length,
            bool genStart,
            bool genStop
            )
        {
            ushort control;
            byte retry = 100;

            IoResult result = i2c_con.Write16NoThrow((ushort)Omap3430_I2C_CON_I2C_EN);
            DebugStub.Assert(IoResult.Success == result);

            // Set slave address
            result = i2c_sa.Write8NoThrow(slaveAddress);
            DebugStub.Assert(IoResult.Success == result);

            // Set transfer length
            result = i2c_cnt.Write16NoThrow(length);
            DebugStub.Assert(IoResult.Success == result);

            // Clear status
            result = i2c_stat.Write16NoThrow((ushort)(Omap3430_I2C_STAT_ARDY | Omap3430_I2C_STAT_NACK | Omap3430_I2C_STAT_AL));
            DebugStub.Assert(IoResult.Success == result);

            // Read bus status
            ushort status;
            result = i2c_stat.Read16NoThrow(out status);
            DebugStub.Assert(IoResult.Success == result);

            // Wait for free bus
            while ((status & Omap3430_I2C_STAT_BB) != 0) {
                if (--retry == 0) {
                    return false;
                }

                result = i2c_stat.Read16NoThrow(out status);
                DebugStub.Assert(IoResult.Success == result);
            }

            control = (ushort)(
                Omap3430_I2C_CON_I2C_EN |                   // enable i2c
                Omap3430_I2C_CON_MST |                      // i2c master
                (xmit ? Omap3430_I2C_CON_TRX : 0) |         // transmit/receive
                (genStart ? Omap3430_I2C_CON_STT : 0) |     // start
                (genStop ? Omap3430_I2C_CON_STP : 0)        // stop
            );

            result = i2c_con.Write16NoThrow(control);
            DebugStub.Assert(IoResult.Success == result);

            return true;
        }

        [NoHeapAllocation]
        private bool MasterTransfer(
            bool xmit,
            byte[] data
            )
        {
            ushort length = (ushort)data.Length;
            ushort index = 0;

            for (;;)
            {
                ushort status;
                ushort bytesAvailable;

                // Read bus status
                IoResult result = i2c_stat.Read16NoThrow(out status);
                DebugStub.Assert(IoResult.Success == result);

                if ((status & (Omap3430_I2C_STAT_NACK | Omap3430_I2C_STAT_AL)) != 0) {
                    return false;
                }

                if ((status & (Omap3430_I2C_STAT_ARDY)) != 0) {
                    DebugStub.Assert(length == 0);
                    return true;
                }

                if (xmit) {
                    if ((status & Omap3430_I2C_STAT_XDR) != 0) {
                        ushort bufferStatus;

                        // Read FIFO status
                        result = i2c_bufstat.Read16NoThrow(out bufferStatus);
                        DebugStub.Assert(IoResult.Success == result);

                        bytesAvailable = (ushort)((bufferStatus & Omap3430_I2C_BUFSTAT_TXSTAT_Mask) >> Omap3430_I2C_BUFSTAT_TXSTAT_Shift);

                        while (bytesAvailable-- != 0) {

                            result = i2c_data.Write8NoThrow(data[index]);
                            DebugStub.Assert(IoResult.Success == result);

                            index++;
                            length--;
                        }
                    }

                    if ((status & Omap3430_I2C_STAT_XRDY) != 0) {
                        ushort bufferControl;

                        // Read FIFO threshold
                        result = i2c_buf.Read16NoThrow(out bufferControl);
                        DebugStub.Assert(IoResult.Success == result);

                        bytesAvailable = (ushort)((bufferControl & Omap3430_I2C_BUF_XTRSH_Mask) >> Omap3430_I2C_BUF_XTRSH_Shift);
                        bytesAvailable += 1;

                        while (bytesAvailable-- != 0) {

                            result = i2c_data.Write8NoThrow(data[index]);
                            DebugStub.Assert(IoResult.Success == result);

                            index++;
                            length--;
                        }
                    }

                } else {

                    if ((status & Omap3430_I2C_STAT_RDR) != 0) {
                        ushort bufferStatus;

                        // Read FIFO status
                        result = i2c_bufstat.Read16NoThrow(out bufferStatus);
                        DebugStub.Assert(IoResult.Success == result);

                        bytesAvailable = (ushort)((bufferStatus & Omap3430_I2C_BUFSTAT_RXSTAT_Mask) >> Omap3430_I2C_BUFSTAT_RXSTAT_Shift);

                        while (bytesAvailable-- != 0) {

                            result = i2c_data.Read8NoThrow(out data[index]);
                            DebugStub.Assert(IoResult.Success == result);

                            index++;
                            length--;
                        }
                    }

                    if ((status & Omap3430_I2C_STAT_RRDY) != 0) {
                        ushort bufferControl;

                        // Read FIFO threshold
                        result = i2c_buf.Read16NoThrow(out bufferControl);
                        DebugStub.Assert(IoResult.Success == result);

                        bytesAvailable = (ushort)((bufferControl & Omap3430_I2C_BUF_RTRSH_Mask) >> Omap3430_I2C_BUF_RTRSH_Shift);
                        bytesAvailable += 1;

                        while (bytesAvailable-- != 0) {

                            result = i2c_data.Read8NoThrow(out data[index]);
                            DebugStub.Assert(IoResult.Success == result);

                            index++;
                            length--;
                        }
                    }
                }

                result = i2c_stat.Write16NoThrow(status);
                DebugStub.Assert(IoResult.Success == result);
            }
        }

        [NoHeapAllocation]
        private bool MasterCommon(
            bool xmit,
            byte slaveAddress,
            byte[] data,
            bool genStart,
            bool genStop
            )
        {
            int length = data.Length;
            DebugStub.Assert(length <= 0xffffu);

            int retry = 10;

            while (retry-- != 0) {

                MasterSetup(xmit, slaveAddress, (ushort)length, genStart, genStop);

                if (MasterTransfer(xmit, data)) {
                    return true;
                }
            }

            return false;
        }

        public void Finalize()
        {
            pic.DisableIrq(irq);
        }
    }

    /// <remarks>
    /// Class <c>RTClock</c> represents the system Real-Time Clock.
    ///
    /// RTC chips on ARM are provided by other sources, such as
    /// the TWL4030 companion chip. The TWL 4030 RTC provides a
    /// timing resolution of 1 second, and can only generate
    /// periodic interrupts that frequently.
    ///
    /// By combining information from the <c>TimerOmap3430</c>
    /// programmable timer, time can be read with a resolution
    /// of xxx microseconds.
    /// </remarks>
    [CLSCompliant(false)]
    internal sealed class RTClockOmap3430
    {
        private PnpConfig       config;
        private Pic             pic;
        private byte            irq;
        private TimerOmap3430   timer = null;
        private I2cOmap3430     i2c1;

        private byte[]          i2cReadBuffer = new byte[1];
        private byte[]          i2cWriteBuffer = new byte[2];

        const int InterruptGapTicks = 10000000; // units of 100ns

        const uint maxAttempts = 1000000;

        private RtcPitState rps   = null;
        private long        rtcBootTime;
        private volatile uint irqCount = 0;

        // Variables for fast GetKernelTicks implementation that uses
        // TSC when it is deemed safe.
        bool  noisyTimer;               // Running on VPC? Disable optimization
        long  pmccntrSnapshot;           // TSC value at sync point
        long  ticksSnapshot;            // Kernel ticks at sync point
        long  lastKernelTicks;          // Last kernel ticks reported

        bool  pmccntrSnapshotValid = false;
        bool  initedFastTime   = false;

        int   tickScale;
        const int tickRoll = 24;

        // Triton2 i2c bus addresses
        const byte TWL4030_ID1_I2c_Address      = 0x48; // i2c address for ID1
        const byte TWL4030_ID2_I2c_Address      = 0x49; // i2c address for ID2
        const byte TWL4030_ID3_I2c_Address      = 0x4a; // i2c address for ID3
        const byte TWL4030_ID4_I2c_Address      = 0x4b; // i2c address for ID4

        // Triton2 register addresses
        const byte TWL4030_SECONDS_REG                      = 0x1c; // BCD seconds, 0 -> 59
        const byte TWL4030_MINUTES_REG                      = 0x1d; // BCD minutes, 0 -> 59
        const byte TWL4030_HOURS_REG                        = 0x1e; // BCD hours, 0 -> 23 or 0 -> 11 (am/pm)
        const byte TWL4030_DAYS_REG                         = 0x1f; // BCD days, 0 -> 31
        const byte TWL4030_MONTHS_REG                       = 0x20; // BCD month, 1 -> 12
        const byte TWL4030_YEARS_REG                        = 0x21; // BCD year, [20]00 -> [20]99
        const byte TWL4030_WEEKS_REG                        = 0x22; // BCD day of week, 0 -> 6
        const byte TWL4030_ALARM_SECONDS_REG                = 0x23; // Alarm time, BCD seconds, 0 -> 59
        const byte TWL4030_ALARM_MINUTES_REG                = 0x24; // Alarm time, BCD minutes, 0 -> 59
        const byte TWL4030_ALARM_HOURS_REG                  = 0x25; // Alarm time, BCD hours, 0 -> 23 or 0 -> 11 (am/pm)
        const byte TWL4030_ALARM_DAYS_REG                   = 0x26; // Alarm time, BCD days, 0 -> 31
        const byte TWL4030_ALARM_MONTHS_REG                 = 0x27; // Alarm time, BCD month, 1 -> 12
        const byte TWL4030_ALARM_YEARS_REG                  = 0x28; // Alarm time, BCD year, [20]00 -> [20]99
        const byte TWL4030_RTC_CTRL_REG                     = 0x29; // control
        const byte TWL4030_RTC_STATUS_REG                   = 0x2a; // status
        const byte TWL4030_RTC_INTERRUPTS_REG               = 0x2b; // interrupt status
        const byte TWL4030_RTC_COMP_LSB_REG                 = 0x2c; // clock compensation, comp_reg[15:8], in (1/32768)s
        const byte TWL4030_RTC_COMP_MSB_REG                 = 0x2d; // clock compensation, comp_reg[7:0], in (1/32768)s

        const byte TWL4030_PIH_ISR_P1                       = 0x81; // programmable interrupt handler ISR for irq1
        const byte TWL4030_PIH_ISR_P2                       = 0x82; // programmable interrupt handler ISR for irq2
        const byte TWL4030_PIH_SIR                          = 0x83; // software interrupt ISR

        const byte TWL4030_PWR_ISR1                         = 0x2e; // power ISR
        const byte TWL4030_PWR_IMR1                         = 0x2f; // power interrupt mask
        const byte TWL4030_PWR_EDR1                         = 0x33; // power edge detection
        const byte TWL4030_PWR_SIH_CTRL                     = 0x35; // power secondary interrupt handler control

        // Triton2 register fields
        const byte TWL4030_SECONDS_REG_Mask                 = 0x7f; // BCD seconds, 0 -> 59
        const byte TWL4030_MINUTES_REG_Mask                 = 0x7f; // BCD minutes, 0 -> 59
        const byte TWL4030_HOURS_REG_Mask                   = 0x3f; // BCD hours, 0 -> 23 or 0 -> 11 (am/pm)
        const byte TWL4030_HOURS_REG_PM_NAM                 = 0x80; // pm/~am flag (am/pm mode)
        const byte TWL4030_DAYS_REG_Mask                    = 0x3f; // BCD days, 0 -> 31
        const byte TWL4030_MONTHS_REG_Mask                  = 0x1f; // BCD month, 1 -> 12
        const byte TWL4030_YEARS_REG_Mask                   = 0xff; // BCD year, [20]00 -> [20]99

        const byte TWL4030_WEEKS_REG_Mask                   = 0x07; // BCD day of week, 0 -> 6

        const byte TWL4030_ALARM_SECONDS_REG_Mask           = 0x7f; // Alarm time, BCD seconds, 0 -> 59
        const byte TWL4030_ALARM_MINUTES_REG_Mask           = 0x7f; // Alarm time, BCD minutes, 0 -> 59
        const byte TWL4030_ALARM_HOURS_REG_Mask             = 0x3f; // Alarm time, BCD hours, 0 -> 23 or 0 -> 11 (am/pm)
        const byte TWL4030_ALARM_HOURS_REG_PM_NAM           = 0x80; // Alarm time, pm/~am flag (am/pm mode)
        const byte TWL4030_ALARM_DAYS_REG_Mask              = 0x3f; // Alarm time, BCD days, 0 -> 31
        const byte TWL4030_ALARM_MONTHS_REG_Mask            = 0x1f; // Alarm time, BCD month, 1 -> 12
        const byte TWL4030_ALARM_YEARS_REG_Mask             = 0xff; // Alarm time, BCD year, [20]00 -> [20]99

        const byte TWL4030_RTC_CTRL_REG_GET_TIME            = 0x40; // latch the time into the alarm registers
        const byte TWL4030_RTC_CTRL_REG_SET_32_COUNTER      = 0x20; // set the 32kHz counter from comp_reg
        const byte TWL4030_RTC_CTRL_REG_TEST_MODE           = 0x10; // test mode
        const byte TWL4030_RTC_CTRL_REG_MODE_12_24          = 0x08; // enable am/pm mode (when set)
        const byte TWL4030_RTC_CTRL_REG_AUTO_COMP           = 0x04; // enable auto-compensation, as set in TWL4030_RTC_COMP_*_REG
        const byte TWL4030_RTC_CTRL_REG_ROUND_30S           = 0x02; // round time to closest minute on the next second
        const byte TWL4030_RTC_CTRL_REG_STOP_RTC            = 0x01; // ~RTC frozen (frozen when low)

        const byte TWL4030_RTC_STATUS_REG_POWER_UP          = 0x80; // RTC reset
        const byte TWL4030_RTC_STATUS_REG_ALARM             = 0x40; // alarm interrupt
        const byte TWL4030_RTC_STATUS_REG_1D_EVENT          = 0x20; // day interval
        const byte TWL4030_RTC_STATUS_REG_1H_EVENT          = 0x10; // hour interval
        const byte TWL4030_RTC_STATUS_REG_1M_EVENT          = 0x08; // minute interval
        const byte TWL4030_RTC_STATUS_REG_1S_EVENT          = 0x04; // second interval
        const byte TWL4030_RTC_STATUS_REG_RUN               = 0x02; // RTC running

        const byte TWL4030_RTC_INTERRUPTS_REG_IT_ALARM      = 0x08; // enable alarm interrupt
        const byte TWL4030_RTC_INTERRUPTS_REG_IT_TIMER      = 0x04; // enable periodic interrupt
        const byte TWL4030_RTC_INTERRUPTS_REG_EVERY_Mask    = 0x03; // interrupt period mask
        const byte TWL4030_RTC_INTERRUPTS_REG_EVERY_Day     = 0x03; // daily periodic interrupt
        const byte TWL4030_RTC_INTERRUPTS_REG_EVERY_Hour    = 0x02; // hourly periodic interrupt
        const byte TWL4030_RTC_INTERRUPTS_REG_EVERY_Minute  = 0x01; // periodic interrupt every minute
        const byte TWL4030_RTC_INTERRUPTS_REG_EVERY_Second  = 0x00; // periodic interrupt every second

        const byte TWL4030_PIH_ISR_P1_PIH_ISR5              = 0x81; // power management interrupt
        const byte TWL4030_PWR_ISR1_RTC_IT                  = 0x08; // RTC interrupt
        const byte TWL4030_PWR_IMR1_RTC_IT                  = 0x08; // RTC interrupt mask
        const byte TWL4030_PWR_EDR1_RTC_IT_RISING           = 0x80; // RTC interrupt rising edge detect
        const byte TWL4030_PWR_EDR1_RTC_IT_FALLING          = 0x40; // RTC interrupt falling edge detect
        const byte TWL4030_PWR_SIH_CTRL_COR                 = 0x04; // ~clear on read (clear on write when set)

        // Constructor
        internal RTClockOmap3430(PnpConfig config, Pic pic, TimerOmap3430 timer)
        {
            DebugStub.Print("RTClock: create\n");

            I2cResources ir = new I2cResources(config);

            this.config = config;
            this.pic    = pic;
            this.irq    = ir.tritonIrq.Irq; //sys_nirq
            this.timer  = timer;

            this.i2c1 = new I2cOmap3430(ir, pic);
        }

        [NoHeapAllocation]
        private bool ReadTritonRegister(byte i2c_addr, byte triton_addr, out byte value)
        {
            this.i2cReadBuffer[0] = triton_addr;

            bool success = i2c1.MasterSend(i2c_addr, this.i2cReadBuffer, true, false);

            if (success) {
                success = i2c1.MasterReceive(i2c_addr, this.i2cReadBuffer, false, true);
            }

            // NB: value is trash if we don't succeed. We can't raise an exception, etc.
            //     as we can't allocate on the heap!
            value = this.i2cReadBuffer[0];

            return success;
        }

        [NoHeapAllocation]
        private bool WriteTritonRegister(byte i2c_addr, byte triton_addr, byte value)
        {
            this.i2cWriteBuffer[0] = triton_addr;
            this.i2cWriteBuffer[1] = value;

            return i2c1.MasterSend(i2c_addr, this.i2cReadBuffer, true, true);
        }

        [NoHeapAllocation]
        private byte ReadRtc(byte triton_addr)
        {
            byte value;

            bool success = ReadTritonRegister(TWL4030_ID4_I2c_Address, triton_addr, out value);
            DebugStub.Assert(success);

            return value;
        }

        [NoHeapAllocation]
        private void WriteRtc(byte triton_addr, byte value)
        {
            bool success = WriteTritonRegister(TWL4030_ID4_I2c_Address, triton_addr, value);
            DebugStub.Assert(success);
        }

        [NoHeapAllocation]
        private byte ReadPih(byte triton_addr)
        {
            byte value;

            bool success = ReadTritonRegister(TWL4030_ID2_I2c_Address, triton_addr, out value);
            DebugStub.Assert(success);

            return value;
        }

        [NoHeapAllocation]
        private void WritePih(byte triton_addr, byte value)
        {
            bool success = WriteTritonRegister(TWL4030_ID2_I2c_Address, triton_addr, value);
            DebugStub.Assert(success);
        }

        [NoHeapAllocation]
        public void ClearInterrupt()
        {
            byte status = ReadRtc(TWL4030_RTC_STATUS_REG);

            if (((ReadPih(TWL4030_PIH_ISR_P1) & TWL4030_PIH_ISR_P1_PIH_ISR5) == 0) ||
                ((ReadPih(TWL4030_PWR_ISR1) & TWL4030_PWR_ISR1_RTC_IT) == 0) ||
                ((status & TWL4030_RTC_STATUS_REG_1S_EVENT) == 0)) {
                // Spurious Triton2 interrupt...
                pic.AckIrq(irq);
                return;
            }

            ClockLogger.AddEntry(4, rps, timer);
            rps.pitLastClock = 0; // timer.Timer2Read();
            rps.upTime += InterruptGapTicks;

            ClockLogger.AddEntry(5, rps, timer);
            irqCount++;

            if (timer.InterruptPending == false) {
                // This is to keep time progressing if the user has
                // not set an interrupt
                //timer.SetKeepAliveInterrupt();
            }
            WritePih(TWL4030_PWR_ISR1, TWL4030_PWR_ISR1_RTC_IT);
            pic.AckIrq(irq);

            // Invalidate TSC snapshot to force clock synchronization
            this.pmccntrSnapshotValid = false;
        }

        internal byte Initialize()
        {
            DebugStub.Print("RTClock: initialize\n");

            rps = timer.rps;

            // Start the RTC (if not already started)
            WriteRtc(TWL4030_RTC_CTRL_REG, TWL4030_RTC_CTRL_REG_STOP_RTC);

            // Disable interrupts for now
            WriteRtc(TWL4030_RTC_INTERRUPTS_REG, 0);

            WritePih(TWL4030_PWR_SIH_CTRL, TWL4030_PWR_SIH_CTRL_COR);
            WritePih(TWL4030_PWR_IMR1, 0);

            // Capture the boot time
            rtcBootTime = PullRtcTime().Ticks;

            // Enable and clear interrupts
            pic.EnableIrq(irq);
            return pic.IrqToInterrupt(irq);
        }

        internal void Start()
        {
            DebugStub.Print("RTClock::Start()\n");

            // Enable interrupt every second
            WriteRtc(TWL4030_RTC_INTERRUPTS_REG, (TWL4030_RTC_INTERRUPTS_REG_IT_TIMER | TWL4030_RTC_INTERRUPTS_REG_EVERY_Second));
            WritePih(TWL4030_PWR_IMR1, unchecked((byte)~TWL4030_PWR_IMR1_RTC_IT));
        }

        internal void Finalize()
        {
            pic.DisableIrq(irq);
        }

        [NoHeapAllocation]
        bool InitializeFastTime()
        {
            const ulong KernelTicksPerSecond = 10000000;
            ulong cps = Processor.CyclesPerSecond;
            if (cps >= KernelTicksPerSecond) {
                this.pmccntrSnapshot = 0;
                this.ticksSnapshot = 0;
                this.tickScale =
                    (int)((1 << tickRoll) * KernelTicksPerSecond / cps);
#if VERBOSE
                DebugStub.Print(
                    "tick scale = {0} (actual {1:d}, fast {2:d})",
                    __arglist(this.tickScale,
                              cps,
                              ((1 << tickRoll) * KernelTicksPerSecond /
                               (ulong)this.tickScale))
                    );
#endif // VERBOSE
                return true;
            }
            return false;
        }

        internal void SetNoisyTimer(bool noisy)
        {
            this.noisyTimer = noisy;
        }

        [NoHeapAllocation]
        private long GetKernelTicksFromHW()
        {
            bool iflag = Processor.DisableInterrupts();
            try {
                ClockLogger.AddEntry(100, rps, timer);
                long r = rps.upTime; //rps.GetKernelTicks(timer.Timer2Read());
                return r;
            }
            finally {
                Processor.RestoreInterrupts(iflag);
            }
        }

        [NoHeapAllocation]
        private long GetKernelTicksFromPmccntr()
        {
            long now = unchecked((long)Processor.CycleCount);
            long pmccntrDelta = now - this.pmccntrSnapshot;

            long tickDelta = (pmccntrDelta * tickScale) >> tickRoll;
            DebugStub.Assert(tickDelta >= 0);

            return ticksSnapshot + tickDelta;
        }

        [NoHeapAllocation]
        private long InternalGetKernelTicks()
        {
            if (!initedFastTime) {
                initedFastTime = InitializeFastTime();
            }

            if (this.noisyTimer || !initedFastTime) {
                // Pull time from the hardware as the PMSCCNTR
                // is not being used.
                return GetKernelTicksFromHW();
            }
            else {
                if (!pmccntrSnapshotValid) {
                    // When waking up from a processor halt
                    // event or from a clock interrupt, sync
                    // with the fixed h/w's concept of time.
                    this.ticksSnapshot = GetKernelTicksFromHW();

                    this.pmccntrSnapshot = unchecked((long)Processor.CycleCount);
                    this.pmccntrSnapshotValid = true;

                    return ticksSnapshot;
                }
                else {
                    return GetKernelTicksFromPmccntr();
                }
            }
        }

        [NoHeapAllocation]
        public long GetKernelTicks()
        {
#if DEBUG_TIMER
            {
                long cpuT = GetKernelTicksFromPmccntr();
                long hwT = GetKernelTicksFromHW();

                if (cpuT - hwT > 5000 || hwT - cpuT < -5000) {
                    DebugStub.Print("Delta = {0:d} (hw {1:d} cpu {2:d})\n",
                                    __arglist(cpuT - hwT, hwT, cpuT));
                }
            }
#endif // DEBUG_TIMER

            // We use the PMCCNTR to provide fast kernel ticks as
            // far as possible, and periodically sync against an
            // off-chip clock.  Because there are different time
            // sources and we don't ever want to report time
            // going backwards, we need to check that time would
            // reported to have advanced or remained the same,
            // and that the clock has not wrapped.
            long kernelTicks = InternalGetKernelTicks();

            if (kernelTicks >= lastKernelTicks) {
                lastKernelTicks = kernelTicks;
                return kernelTicks;
            }
            else if (kernelTicks < 0 && lastKernelTicks > 0) {
                    // clock wrap
                return kernelTicks;
            }
#if DEBUG_TIMER
            // Skew from switching between pmccntr and underlying clock
            DebugStub.Print("Backwards delta = {0} (HW {1} PMCCNTR {2})\n",
                            __arglist(kernelTicks - lastKernelTicks,
                                      GetKernelTicksFromHW(),
                                      GetKernelTicksFromPmccntr())
                            );
#endif // DEBUG_TIMER
            return lastKernelTicks;
        }

        [NoHeapAllocation]
        public void CpuResumeFromHaltEvent()
        {
            pmccntrSnapshotValid = false;
        }

        [NoHeapAllocation]
        public long GetRtcTime()
        {
            return rtcBootTime + GetKernelTicks();
        }

        public void SetRtcTime(long rtcTime)
        {
            long delta = rtcTime - GetRtcTime();
            rtcBootTime = rtcBootTime + delta;
            PushRtcTime(new DateTime(rtcTime));
        }

        [NoHeapAllocation]
        internal static byte BcdToHex(byte bcd)
        {
            return (byte) (10 * (bcd >> 4) + (bcd & 0x0f));
        }

        [NoHeapAllocation]
        internal static byte HexToBcd(int hex)
        {
            int h = hex / 10;
            return (byte) ((h << 4) | (hex % 10));
        }

        private DateTime PullRtcTime()
        {
            bool iflag = Processor.DisableInterrupts();

            // Read out captured time
            byte second = 0xff;
            byte minute = 0xff;
            byte hour = 0xff;
            byte day = 0xff;
            byte month = 0xff;
            byte year = 0xff;

            try {
                // Capture current time
                byte control = ReadRtc(TWL4030_RTC_CTRL_REG);
                control |= TWL4030_RTC_CTRL_REG_GET_TIME;
                WriteRtc(TWL4030_RTC_CTRL_REG, control);

                // Read out captured time
                second  = BcdToHex((byte)(ReadRtc(TWL4030_ALARM_SECONDS_REG) & TWL4030_ALARM_SECONDS_REG_Mask));
                minute  = BcdToHex((byte)(ReadRtc(TWL4030_ALARM_MINUTES_REG) & TWL4030_ALARM_MINUTES_REG_Mask));
                hour    = BcdToHex((byte)(ReadRtc(TWL4030_ALARM_HOURS_REG) & TWL4030_ALARM_HOURS_REG_Mask));
                day     = BcdToHex((byte)(ReadRtc(TWL4030_ALARM_DAYS_REG) & TWL4030_ALARM_DAYS_REG_Mask));
                month   = BcdToHex((byte)(ReadRtc(TWL4030_ALARM_MONTHS_REG) & TWL4030_ALARM_MONTHS_REG_Mask));
                year    = BcdToHex((byte)(ReadRtc(TWL4030_ALARM_YEARS_REG) & TWL4030_ALARM_YEARS_REG_Mask));

                DebugStub.Print("PullRtcTime: " +
                                "({0:d}:{1:d2}:{2:d2} {3:d}/{4:d}/20{5:d2}" +
                                ")\n",
                                __arglist(hour,
                                          minute,
                                          second,
                                          day,
                                          month,
                                          year));

                return new DateTime(2000 + year,
                                    month, day, hour, minute,
                                    second);
            }
            catch (ArgumentOutOfRangeException e) {
                DebugStub.Print("PullRtcTime failed: {0} " +
                                "({1:d}:{2:d2}:{3:d2} {4:d}/{5:d}/20{6:d2}" +
                                ")\n",
                                __arglist(e,
                                          hour,
                                          minute,
                                          second,
                                          day,
                                          month,
                                          year));
            }
            finally {
                Processor.RestoreInterrupts(iflag);
            }

            return new DateTime(2007, 7, 5, 16, 32, 45);
        }

        private void PushRtcTime(DateTime dt)
        {
            bool iflag = Processor.DisableInterrupts();
            byte control = ReadRtc(TWL4030_RTC_CTRL_REG);

            try {
                // Freeze RTC
                WriteRtc(TWL4030_RTC_CTRL_REG, (byte)(control & ~TWL4030_RTC_CTRL_REG_STOP_RTC));

                // Write new values
                WriteRtc(TWL4030_ALARM_SECONDS_REG, HexToBcd(dt.Second));
                WriteRtc(TWL4030_ALARM_MINUTES_REG, HexToBcd(dt.Minute));
                WriteRtc(TWL4030_ALARM_HOURS_REG,   HexToBcd(dt.Hour));
                WriteRtc(TWL4030_ALARM_DAYS_REG,    HexToBcd(dt.Day));
                WriteRtc(TWL4030_ALARM_MONTHS_REG,  HexToBcd(dt.Month));
                WriteRtc(TWL4030_ALARM_YEARS_REG,   HexToBcd(dt.Year % 100));

                // to mis-quote Ford:
                //  "you can have any century you like, as long as it's the 21st..."
                DebugStub.Print("PushRtcTime: " +
                                "({0:d}:{1:d2}:{2:d2} {3:d}/{4:d}/20{5:d2}" +
                                ")\n",
                                __arglist(dt.Hour,
                                          dt.Minute,
                                          dt.Second,
                                          dt.Day,
                                          dt.Month,
                                          dt.Year % 100));
                if ((dt.Year / 100) != 20) {
                    DebugStub.Print("PushRtcTime: truncating year {0:d4}->20{1:d2}\n",
                                    __arglist(dt.Year, dt.Year % 100));
                }
            }
            finally {
                // Run RTC again from new value
                WriteRtc(TWL4030_RTC_CTRL_REG, control);
                Processor.RestoreInterrupts(iflag);
            }
        }
    }
}
