(*
说明:全志A20的System Clock底层操作封装类。单例。
作者:tjCFeng
邮箱:tjCFeng@163.com
更新日期:2014.12.06
*)

unit Clock;

{$mode objfpc}{$H+}

interface

uses SysUtils, A20;

type
  TCCU = class
  private
    class var FInstance: TCCU;
    class function GetInstance: TCCU; static;
  public
    class procedure Release;
    class property Instance: TCCU read GetInstance;
	
  private
    FCCU_BASE: ^LongWord;
	
    FPLL1_CFG: ^LongWord;
    FPLL1_TUN: ^LongWord;
    FPLL2_CFG: ^LongWord;
    FPLL2_TUN: ^LongWord;
    FPLL3_CFG: ^LongWord;
    FPLL4_CFG: ^LongWord;
    FPLL5_CFG: ^LongWord;
    FPLL5_TUN: ^LongWord;
    FPLL6_CFG: ^LongWord;
    FPLL6_TUN: ^LongWord;
    FPLL7_CFG: ^LongWord;
    FPLL1_TUN2: ^LongWord;
    FPLL5_TUN2: ^LongWord;
    FPLL8_CFG: ^LongWord;

    FOSC24M_CFG: ^LongWord;
    FCPU_AHB_APB0_CFG: ^LongWord;
    FAPB1_CLK_DIV: ^LongWord;
    FAHB_GATING0: ^LongWord;
    FAHB_GATING1: ^LongWord;
    FAPB0_GATING: ^LongWord;
    FAPB1_GATING: ^LongWord;

    FNAND_SCLK_CFG: ^LongWord;
    FMS_SCLK_CFG: ^LongWord;

    FSD0_CLK: ^LongWord;
    FSD1_CLK: ^LongWord;
    FSD2_CLK: ^LongWord;
    FSD3_CLK: ^LongWord;

    FTS_CLK: ^LongWord;
    FSS_CLK: ^LongWord;

    FSPI0_CLK: ^LongWord;
    FSPI1_CLK: ^LongWord;
    FSPI2_CLK: ^LongWord;

    FIR0_CLK: ^LongWord;
    FIR1_CLK: ^LongWord;

    FIIS0_CLK: ^LongWord;
    FAC97_CLK: ^LongWord;
    FSPDIF_CLK: ^LongWord;

    FKEYPAD_CLK: ^LongWord;
    FSATA_CLK: ^LongWord;
    FUSB_CLK: ^LongWord;
    FSPI3_CLK: ^LongWord;
    FIIS1_CLK: ^LongWord;
    FIIS2_CLK: ^LongWord;
    FDRAM_CLK: ^LongWord;

    FBE0_SCLK_CFG: ^LongWord;
    FBE1_SCLK_CFG: ^LongWord;
    FFE0_CLK: ^LongWord;
    FFE1_CLK: ^LongWord;

    FMP_CLK: ^LongWord;
    FLCD0_CH0_CLK: ^LongWord;
    FLCD1_CH0_CLK: ^LongWord;
    FCSI_SCLK: ^LongWord;
    FTVD_CLK: ^LongWord;
    FLCD0_CH1_CLK: ^LongWord;
    FLCD1_CH1_CLK: ^LongWord;
    FCSI0_CLK: ^LongWord;
    FCSI1_CLK: ^LongWord;

    FVE_CLK: ^LongWord;
    FAUDIO_CODEC_CLK: ^LongWord;
    FAVS_CLK: ^LongWord;
    FACE_CLK: ^LongWord;
    FLVDS_CLK: ^LongWord;
    FHDMI_CLK: ^LongWord;
    FMALI400_CLK: ^LongWord;
    FMBUS_SCLK_CFG: ^LongWord;
    FGMAC_CLK: ^LongWord;
    FHDMI1_RST: ^LongWord;
    FHDMI1_CTRL: ^LongWord;
    FHDMI1_SLOW_CLK: ^LongWord;
    FHDMI1_REPEAT_CLK: ^LongWord;

    FCLK_OUTA: ^LongWord;
    FCLK_OUTB: ^LongWord;

    constructor Create;
    destructor Destroy; override;
  public
    function Get_CorePLL: LongWord;
    function Get_AXI: LongWord;
    function Get_AHB: LongWord;
    function Get_APB0: LongWord;
    function Get_APB1: LongWord;
  public
    procedure CLK_SetGPIO(ED: Boolean);
    function CLK_GetGPIO: Boolean;
	
    procedure CLK_SetTWI(IndexTWI: Byte; ED: Boolean);
    function CLK_GetTWI(IndexTWI: Byte): Boolean;

    procedure CLK_SetSPI(IndexSPI: Byte; ED: Boolean);
    function CLK_GetSPI(IndexSPI: Byte): Boolean;
  end;

implementation

const
  CCU_BASE = $01C20000;
  
class function TCCU.GetInstance: TCCU;
begin
  if FInstance = nil then FInstance:= TCCU.Create;
  Result:= FInstance;
end;

class procedure TCCU.Release;
begin
  FreeAndNil(FInstance);
end;


constructor TCCU.Create;
var Base: LongWord;
begin
  inherited Create;
  
  FCCU_BASE:= TA20.Instance.GetMMap(CCU_BASE);
  Base:= LongWord(FCCU_BASE) + TA20.Instance.BaseOffset(CCU_BASE);

  FPLL1_CFG:= Pointer(Base + $0000);
  FPLL1_TUN:= Pointer(Base + $0004);
  FPLL2_CFG:= Pointer(Base + $0008);
  FPLL2_TUN:= Pointer(Base + $000C);
  FPLL3_CFG:= Pointer(Base + $0010);
  FPLL4_CFG:= Pointer(Base + $0018);
  FPLL5_CFG:= Pointer(Base + $0020);
  FPLL5_TUN:= Pointer(Base + $0024);
  FPLL6_CFG:= Pointer(Base + $0028);
  FPLL6_TUN:= Pointer(Base + $002C);
  FPLL7_CFG:= Pointer(Base + $0030);
  FPLL1_TUN2:= Pointer(Base + $0038);
  FPLL5_TUN2:= Pointer(Base + $003C);
  FPLL8_CFG:= Pointer(Base + $0040);

  FOSC24M_CFG:= Pointer(Base + $0050);
  FCPU_AHB_APB0_CFG:= Pointer(Base + $0054);
  FAPB1_CLK_DIV:= Pointer(Base + $0058);
  FAHB_GATING0:= Pointer(Base + $0060);
  FAHB_GATING1:= Pointer(Base + $0064);
  FAPB0_GATING:= Pointer(Base + $0068);
  FAPB1_GATING:= Pointer(Base + $006C);

  FNAND_SCLK_CFG:= Pointer(Base + $0080);
  FMS_SCLK_CFG:= Pointer(Base + $0084);

  FSD0_CLK:= Pointer(Base + $0088);
  FSD1_CLK:= Pointer(Base + $008C);
  FSD2_CLK:= Pointer(Base + $0090);
  FSD3_CLK:= Pointer(Base + $0094);

  FTS_CLK:= Pointer(Base + $0098);
  FSS_CLK:= Pointer(Base + $009C);

  FSPI0_CLK:= Pointer(Base + $00A0);
  FSPI1_CLK:= Pointer(Base + $00A4);
  FSPI2_CLK:= Pointer(Base + $00A8);

  FIR0_CLK:= Pointer(Base + $00B0);
  FIR1_CLK:= Pointer(Base + $00B4);

  FIIS0_CLK:= Pointer(Base + $00B8);
  FAC97_CLK:= Pointer(Base + $00BC);
  FSPDIF_CLK:= Pointer(Base + $00C0);

  FKEYPAD_CLK:= Pointer(Base + $00C4);
  FSATA_CLK:= Pointer(Base + $00C8);
  FUSB_CLK:= Pointer(Base + $00CC);
  FSPI3_CLK:= Pointer(Base + $00D4);
  FIIS1_CLK:= Pointer(Base + $00D8);
  FIIS2_CLK:= Pointer(Base + $00DC);
  FDRAM_CLK:= Pointer(Base + $0100);

  FBE0_SCLK_CFG:= Pointer(Base + $0104);
  FBE1_SCLK_CFG:= Pointer(Base + $0108);
  FFE0_CLK:= Pointer(Base + $010C);
  FFE1_CLK:= Pointer(Base + $0110);

  FMP_CLK:= Pointer(Base + $0114);
  FLCD0_CH0_CLK:= Pointer(Base + $0118);
  FLCD1_CH0_CLK:= Pointer(Base + $011C);
  FCSI_SCLK:= Pointer(Base + $0120);
  FTVD_CLK:= Pointer(Base + $0128);
  FLCD0_CH1_CLK:= Pointer(Base + $012C);
  FLCD1_CH1_CLK:= Pointer(Base + $0130);
  FCSI0_CLK:= Pointer(Base + $0134);
  FCSI1_CLK:= Pointer(Base + $0138);

  FVE_CLK:= Pointer(Base + $013C);
  FAUDIO_CODEC_CLK:= Pointer(Base + $0140);
  FAVS_CLK:= Pointer(Base + $0144);
  FACE_CLK:= Pointer(Base + $0148);
  FLVDS_CLK:= Pointer(Base + $014C);
  FHDMI_CLK:= Pointer(Base + $0150);
  FMALI400_CLK:= Pointer(Base + $0154);
  FMBUS_SCLK_CFG:= Pointer(Base + $015C);
  FGMAC_CLK:= Pointer(Base + $0164);
  FHDMI1_RST:= Pointer(Base + $0170);
  FHDMI1_CTRL:= Pointer(Base + $0174);
  FHDMI1_SLOW_CLK:= Pointer(Base + $0178);
  FHDMI1_REPEAT_CLK:= Pointer(Base + $017C);

  FCLK_OUTA:= Pointer(Base + $01F0);
  FCLK_OUTB:= Pointer(Base + $01F4);

  //FOSC24M_CFG^:= FOSC24M_CFG^ or (($1 shl 16) + ($1 shl 15) + ($1 shl 1) + $1);
end;

destructor TCCU.Destroy;
begin
  TA20.Instance.FreeMMap(FCCU_BASE);
  inherited Destroy;
end;

(******************************************************************************)
function TCCU.Get_CorePLL: LongWord;
var P, M, N, K: LongWord;
begin
  Result:= FPLL1_CFG^;

  P:= 1 shl ((Result shr 16) and $03);
  N:= (Result shr 8) and $1F;
  M:= ((Result shr 0) and $03) + 1;
  K:= ((Result shr 4) and $03) + 1;

  Result:= 24000000 * N * K div P div M;
end;

function TCCU.Get_AXI: LongWord;
var CLKSRC, Factor: LongWord;
begin
  Result:= FCPU_AHB_APB0_CFG^;

  CLKSRC:= (Result shr 16) and $03;
  Factor:= ((Result shr 0) and $03) + 1;

  case CLKSRC of
  0: Result:= 32000;
  1: Result:= 24000000;
  2: Result:= Get_CorePLL;
  else
    Result:= 0;
  end;
end;

function TCCU.Get_AHB: LongWord;
var Factor: LongWord;
begin
  Factor:= (FCPU_AHB_APB0_CFG^ shr 4) and $03;
  Result:= Get_AXI shr Factor;
end;

function TCCU.Get_APB0: LongWord;
var Factor: LongWord;
begin
  Factor:= (FCPU_AHB_APB0_CFG^ shr 8) and $03;

  if (Factor > 0) then
    Result:= Get_AHB shr Factor
  else
    Result:= Get_AHB shr 1;
end;

function TCCU.Get_APB1: LongWord;
var Factor: LongWord;
begin
  Result:= 0;

  Factor:= (FAPB1_CLK_DIV^ shr 24) and $03;
  if (Factor = 0) then Result:= 24000000;
end;


(******************************************************************************)
procedure TCCU.CLK_SetGPIO(ED: Boolean);
begin
  case ED of
  False: FAPB0_GATING^:= FAPB0_GATING^ and not ($1 shl 5);
  True: FAPB0_GATING^:= FAPB0_GATING^ or ($1 shl 5);
  end;
end;

function TCCU.CLK_GetGPIO: Boolean;
begin
  Result:= (FAPB0_GATING^ and ($1 shl 5) = 1); 
end;
	
procedure TCCU.CLK_SetTWI(IndexTWI: Byte; ED: Boolean);
var Bit: Byte;
begin
  case IndexTWI of
  0..3: Bit:= IndexTWI;
  4: Bit:= 15;
  end;

  case ED of
  False: FAPB1_GATING^:= FAPB1_GATING^ and not ($1 shl Bit);
  True: FAPB1_GATING^:= FAPB1_GATING^ or ($1 shl Bit);
  end;  
end;

function TCCU.CLK_GetTWI(IndexTWI: Byte): Boolean;
begin
  case IndexTWI of
  0..3: Result:= (FAPB1_GATING^ and ($1 shl IndexTWI) = 1);
  4: Result:= (FAPB1_GATING^ and ($1 shl 15) = 1);
  end;
end;

procedure TCCU.CLK_SetSPI(IndexSPI: Byte; ED: Boolean);
begin
  case ED of
  False: FAHB_GATING0^:= FAHB_GATING0^ and not ($1 shl (IndexSPI + 20));
  True: FAHB_GATING0^:= FAHB_GATING0^ or ($1 shl (IndexSPI + 20));
  end;
end;

function TCCU.CLK_GetSPI(IndexSPI: Byte): Boolean;
begin
  Result:= (FAHB_GATING0^ and ($1 shl (IndexSPI + 20)) = 1);
end;


finalization
  TCCU.Instance.Release;

end.