use core::marker::PhantomData;
use volatile::*;

#[repr(C, packed)]
pub struct PowerManager {
    pub control: ReadWrite<u8>,          // Offset: 0x00 (R/W  8) Control
    pub sleep: ReadWrite<u8>,            // Offset: 0x01 (R/W  8) Sleep Mode
    pub external_control: ReadWrite<u8>, // Offset: 0x02 (R/W  8) External Reset Controller
    reserved_1: [u8; 5],
    pub cpu_select: ReadWrite<u8>,       // Offset: 0x08 (R/W  8) CPU Clock Select
    pub apba_select: ReadWrite<u8>,      // Offset: 0x09 (R/W  8) APBA Clock Select
    pub apbb_select: ReadWrite<u8>,      // Offset: 0x0A (R/W  8) APBB Clock Select
    pub apbc_select: ReadWrite<u8>,      // Offset: 0x0B (R/W  8) APBC Clock Select
    reserved_2: [u8; 8],
    pub ahb_mask: ReadWrite<u32>,        // Offset: 0x14 (R/W 32) AHB Mask
    pub apba_mask: ReadWrite<u32>,       // Offset: 0x18 (R/W 32) APBA Mask
    pub apbb_mask: ReadWrite<u32>,       // Offset: 0x1C (R/W 32) APBB Mask
    pub apbc_mask: ReadWrite<u32>,       // Offset: 0x20 (R/W 32) APBC Mask
    reserved_3: [u8; 10],
    pub interrupt_clear: ReadWrite<u8>,  // Offset: 0x34 (R/W  8) Interrupt Enable Clear
    pub interrupt_set: ReadWrite<u8>,    // Offset: 0x35 (R/W  8) Interrupt Enable Set
    pub interrupt_flag: ReadWrite<u8>,   // Offset: 0x36 (R/W  8) Interrupt Flag Status and Clear
    reserved_4: [u8; 1],
    pub reset_cause: ReadOnly<u8>,       // Offset: 0x38 (R/   8) Reset Cause
    private: PhantomData<()>,            // This type cannot be constructed.
}

pub const PM_APBAMASK_GCLK: u32 = 1 << 3;

impl PowerManager {
    pub unsafe fn set_cpu_and_bus_blocks(&mut self) {
        // Now that all system clocks are configured, we can set CPU and APBx BUS clocks.
        // There[sic] values are normally the one present after Reset.
        let PM_CPUSEL_CPUDIV_POS = 0; // (PM_CPUSEL) CPU Prescaler Selection
        let PM_CPUSEL_CPUDIV_DIV1_VAL = 0x0;  // (PM_CPUSEL) Divide by 1
        let PM_CPUSEL_CPUDIV_DIV1 = PM_CPUSEL_CPUDIV_DIV1_VAL << PM_CPUSEL_CPUDIV_POS;
        let PM_APBASEL_APBADIV_DIV1_VAL = 0x0;  // (PM_APBASEL) Divide by 1
        let PM_APBBSEL_APBBDIV_DIV1_VAL = 0x0;  // (PM_APBBSEL) Divide by 1
        let PM_APBCSEL_APBCDIV_DIV1_VAL = 0x0;  // (PM_APBCSEL) Divide by 1

        self.cpu_select.write(PM_CPUSEL_CPUDIV_DIV1);
        self.apba_select.write(PM_APBASEL_APBADIV_DIV1_VAL);
        self.apbb_select.write(PM_APBBSEL_APBBDIV_DIV1_VAL);
        self.apbc_select.write(PM_APBCSEL_APBCDIV_DIV1_VAL);
    }
}