1116 lines
53 KiB
C#
1116 lines
53 KiB
C#
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
}
|