/////////////////////////////////////////////////////////////////////////////// // // 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); } } /// /// Class RTClock 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 TimerOmap3430 /// programmable timer, time can be read with a resolution /// of xxx microseconds. /// [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); } } } }