// Copyright 2016 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package allwinner

import (
	"time"

	"periph.io/x/host/v3/cpu"
)

// ReadTime returns the time on a monotonic timer.
//
// It only works if allwinner-dma successfully loaded. Otherwise it returns 0.
func ReadTime() time.Duration {
	if drvDMA.timerMemory == nil {
		return 0
	}
	v := uint64(drvDMA.timerMemory.counterHigh)<<32 | uint64(drvDMA.timerMemory.counterLow)
	if v == 0 {
		// BUG(maruel): Implement using AVS_CNT0_REG on A64.
		return 0
	}
	// BUG(maruel): Assumes that counterCtrl & timerPLL6 is not set.
	const tick = time.Microsecond / 24
	return time.Duration(v) * tick
}

// Nanospin spins the CPU without calling into the kernel code if possible.
func Nanospin(t time.Duration) {
	start := ReadTime()
	if start == 0 {
		// Use the slow generic version.
		cpu.Nanospin(t)
		return
	}
	for ReadTime()-start < t {
	}
}

//

const (
	// 31:3 reserved
	timerPLL6            timerCtrl = 2 << 1 // CONT64_CLK_SRC_SEL; OSC24M if not set;
	timerReadLatchEnable timerCtrl = 1 << 1 // CONT64_RLATCH_EN; 1 to latch the counter to the registers
	timerClear                     = 1 << 0 // CONT64_CLR_EN; clears the counter
)

// R8: Page 96
type timerCtrl uint32

// timerMap is the mapping of important registers across CPUs.
type timerMap struct {
	reserved0   [0x80 / 4]uint32 //
	cntCtl      timerCtrl        // 0x80 AVS_CNT_CTL_REG AVS Control Register
	cnt0        uint32           // 0x84 AVS_CNT0_REG AVS Counter 0 Register
	cnt1        uint32           // 0x88 AVS_CNT1_REG AVS Counter 1 Register
	cndDrv      uint32           // 0x8C AVS_CNT_DIV_REG AVS Divisor Register
	reserved1   [0x10 / 4]uint32 // On R8 only.
	counterCtrl timerCtrl        // 0x0A0 COUNTER64_CTRL_REG 64-bit Counter control
	counterLow  uint32           // 0x0A4 COUNTER64_LOW_REG 64-bit Counter low
	counterHigh uint32           // 0x0A8 COUNTER64_HI_REG 64-bit Counter high
}

// A64: Page 161.
type timerMapA64 struct {
	reserved0  uint32    // 0x0 TMR_IRQ_EN_REG Timer IRQ Enable Register
	reserved1  uint32    // 0x4 TMR_IRQ_STA_REG Timer Status Register
	reserved2  uint32    // 0x10 TMR0_CTRL_REG Timer 0 Control Register
	reserved3  uint32    // 0x14 TMR0_INTV_VALUE_REG Timer 0 Interval Value Register
	reserved4  uint32    // 0x18 TMR0_CUR_VALUE_REG Timer 0 Current Value Register
	reserved5  uint32    // 0x20 TMR1_CTRL_REG Timer 1 Control Register
	reserved6  uint32    // 0x24 TMR1_INTV_VALUE_REG Timer 1 Interval Value Register
	reserved7  uint32    // 0x28 TMR1_CUR_VALUE_REG Timer 1 Current Value Register
	cntCtl     timerCtrl // 0x80 AVS_CNT_CTL_REG AVS Control Register
	cnt0       uint32    // 0x84 AVS_CNT0_REG AVS Counter 0 Register
	cnt1       uint32    // 0x88 AVS_CNT1_REG AVS Counter 1 Register
	cndDrv     uint32    // 0x8C AVS_CNT_DIV_REG AVS Divisor Register
	reserved8  uint32    // 0xA0 WDOG0_IRQ_EN_REG Watchdog 0 IRQ Enable Register
	reserved9  uint32    // 0xA4 WDOG0_IRQ_STA_REG Watchdog 0 Status Register
	reserved10 uint32    // 0xB0 WDOG0_CTRL_REG Watchdog 0 Control Register
	reserved11 uint32    // 0xB4 WDOG0_CFG_REG Watchdog 0 Configuration Register
	reserved12 uint32    // 0xB8 WDOG0_MODE_REG Watchdog 0 Mode Register
}

// R8: Page 85
type timerMapR8 struct {
	reserved0   uint32       // 0x000 ASYNC_TMR_IRQ_EN_REG Timer IRQ Enable
	reserved1   uint32       // 0x004 ASYNC_TMR_IRQ_STAS_REG Timer Status
	reserved2   [2]uint32    // 0x008-0x00C
	reserved3   uint32       // 0x010 ASYNC_TMR0_CTRL_REG Timer 0 Control
	reserved4   uint32       // 0x014 ASYNC_TMR0_INTV_VALUE_REG Timer 0 Interval Value
	reserved5   uint32       // 0x018 ASYNC_TMR0_CURNT_VALUE_REG Timer 0 Current Value
	reserved6   uint32       // 0x01C
	reserved7   uint32       // 0x020 ASYNC_TMR1_CTRL_REG Timer 1 Control
	reserved8   uint32       // 0x024 ASYNC_TMR1_INTV_VALUE_REG Timer 1 Interval Value
	reserved9   uint32       // 0x028 ASYNC_TMR1_CURNT_VALUE_REG Timer 1 Current Value
	reserved10  uint32       // 0x02C
	reserved11  uint32       // 0x030 ASYNC_TMR2_CTRL_REG Timer 2 Control
	reserved12  uint32       // 0x034 ASYNC_TMR2_INTV_VALUE_REG Timer 2 Interval Value
	reserved13  uint32       // 0x038 ASYNC_TMR2_CURNT_VALUE_REG Timer 2 Current Value
	reserved14  uint32       // 0x03C
	reserved15  uint32       // 0x040 ASYNC_TMR3_CTRL_REG Timer 3 Control
	reserved16  uint32       // 0x044 ASYNC_TMR3_INTV_VALUE_REG Timer 3 Interval Value
	reserved17  [2]uint32    // 0x048-0x04C
	reserved18  uint32       // 0x050 ASYNC_TMR4_CTRL_REG Timer 4 Control
	reserved19  uint32       // 0x054 ASYNC_TMR4_INTV_VALUE_REG Timer 4 Interval Value
	reserved20  uint32       // 0x058 ASYNC_TMR4_CURNT_VALUE_REG Timer 4 Current Value
	reserved21  uint32       // 0x05C
	reserved22  uint32       // 0x060 ASYNC_TMR5_CTRL_REG Timer 5 Control
	reserved23  uint32       // 0x064 ASYNC_TMR5_INTV_VALUE_REG Timer 5 Interval Value
	reserved24  uint32       // 0x068 ASYNC_TMR5_CURNT_VALUE_REG Timer 5 Current Value
	reserved25  [5]uint32    // 0x06C-0x07C
	cntCtl      timerCtrl    // 0x080 AVS_CNT_CTL_REG AVS Control Register
	cnt0        uint32       // 0x084 AVS_CNT0_REG AVS Counter 0 Register
	cnt1        uint32       // 0x088 AVS_CNT1_REG AVS Counter 1 Register
	cndDiv      uint32       // 0x08C AVS_CNT_DIVISOR_REG AVS Divisor
	reserved26  uint32       // 0x090 WDOG_CTRL_REG
	reserved27  uint32       // 0x094 WDOG_MODE_REG Watchdog Mode
	reserved28  [2]uint32    // 0x098-0x09C
	counterCtrl timerCtrl    // 0x0A0 COUNTER64_CTRL_REG 64-bit Counter control
	counterLow  uint32       // 0x0A4 COUNTER64_LOW_REG 64-bit Counter low
	counterHigh uint32       // 0x0A8 COUNTER64_HI_REG 64-bit Counter high
	reserved29  [0x94]uint32 // 0x0AC-0x13C
	reserved30  uint32       // 0x140 CPU_CFG_REG CPU configuration register
}
