hk32-partial-hal 0.3.1

HK32 partial HAL
use crate::pac::{
    flash::regs::{Ar, Keyr},
    FLASH,
};

pub const FLASH_APP_START_ADDR: u32 = 0x08000000;
pub const FLASH_PAGE_SIZE: u32 = 256;
pub const FLASH_APP_SIZE_ALL: u32 = 64 * 1024;
pub const FLASH_APP_SIZE_DEFAULT: u32 = 61 * 1024;
pub const FLASH_PAGE_NUM_ALL: u32 = FLASH_APP_SIZE_ALL / FLASH_PAGE_SIZE;
pub const FLASH_PAGE_NUM_DEFAULT: u32 = FLASH_APP_SIZE_DEFAULT / FLASH_PAGE_SIZE;

const FLASH_KEY1: Keyr = Keyr(0x45670123);
const FLASH_KEY2: Keyr = Keyr(0xCDEF89AB);
// #define FLASH_OPTKEY1  ((uint32_t)(0x45670123U ))
// #define FLASH_OPTKEY2  ((uint32_t)(0xCDEF89ABU ))
const FLASH_OPTKEY1: Keyr = Keyr(0x45670123);
const FLASH_OPTKEY2: Keyr = Keyr(0xCDEF89AB);

const FLASH_ER_PRG_TIMEOUT: u32 = 0x000B0000;

/**

 * @brief  Clears the FLASH's pending flags.
 *         FLASH_FLAG_PGERR: FLASH Programming error flag flag
 *         FLASH_FLAG_WRPERR: FLASH Write protected error flag
 *         FLASH_FLAG_EOP: FLASH End of Programming flag
 * @retval None
 */
pub fn flash_clear_flags() {
    // unlock the flash to reset the falsh_cr_lock
    if FLASH.cr().read().lock() {
        /* Unlocking the program memory access */
        FLASH.keyr().write_value(FLASH_KEY1);
        FLASH.keyr().write_value(FLASH_KEY2);
        /* Clear the flags */
        FLASH.sr().write(|w| {
            w.set_pgerr(true);
            w.set_wrprterr(true);
            w.set_eop(true);
        });
        FLASH.cr().modify(|w| w.set_lock(true));
    } else {
        FLASH.sr().write(|w| {
            w.set_pgerr(true);
            w.set_wrprterr(true);
            w.set_eop(true);
        });
    }
}

/**

 * @brief  Unlocks the FLASH control register and program memory access.
 * @retval None
 */
pub fn flash_unlock() {
    if FLASH.cr().read().lock() {
        /* Unlocking the program memory access */
        FLASH.keyr().write_value(FLASH_KEY1);
        FLASH.keyr().write_value(FLASH_KEY2);
    }
}

/**

 * @brief  Locks the Program memory access.
 * @retval None
 */
pub fn flash_lock() {
    FLASH.cr().modify(|w| w.set_lock(true));
}

/**

 * @brief  Unlocks the option bytes block access.
 * @retval None
 */
pub fn flash_ob_unlock() {
    if !FLASH.cr().read().optwre() {
        /* Unlocking the option bytes block access */
        FLASH.keyr().write_value(FLASH_OPTKEY1);
        FLASH.keyr().write_value(FLASH_OPTKEY2);
    }
}

/**

 * @brief  Locks the Program memory access.
 * @retval None
 */
pub fn flash_ob_lock() {
    FLASH.cr().modify(|w| w.set_optwre(false));
}

/**

 * @brief  Erases a specified page in program memory.
 * @note   To correctly run this function, the FLASH_Unlock() function must be called before.
 * @note   Call the FLASH_Lock() to disable the flash memory access (recommended
 *         to protect the FLASH memory against possible unwanted operation)
 * @param  Page_Address: The page address in program memory to be erased.
 * @note   A Page is erased in the Program memory only if the address to load
 *         is the start address of a page (multiple of 1024 bytes).
 * @retval FLASH Status: The returned value can be:
 *         FLASH_ERROR_PROGRAM, FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
 */
pub fn flash_erase_page(page: u32) -> FlashStatus {
    /* Wait for last operation to be completed */
    let mut status = flash_wait_for_last_operation(FLASH_ER_PRG_TIMEOUT);

    if status == FlashStatus::Complete {
        /* If the previous operation is completed, proceed to erase the page */
        FLASH.cr().modify(|w| w.set_per(true));
        FLASH.ar().write_value(Ar(page));
        FLASH.cr().modify(|w| w.set_strt(true));
        /* Wait for last operation to be completed */
        status = flash_wait_for_last_operation(FLASH_ER_PRG_TIMEOUT);

        /* Disable the PER Bit */
        FLASH.cr().modify(|w| w.set_per(false));
    }

    return status;
}

/**

 * @brief  Programs a word at a specified address.
 * @note   To correctly run this function, the FLASH_Unlock() function must be called before.
 * @note   Call the FLASH_Lock() to disable the flash memory access (recommended
 *         to protect the FLASH memory against possible unwanted operation)
 * @param  Address: specifies the address to be programmed.
 * @param  Data: specifies the data to be programmed.
 * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG,
 *         FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
 */
pub fn flash_program_word(address: u32, data: u32) -> FlashStatus {
    /* Wait for last operation to be completed */
    let mut status = flash_wait_for_last_operation(FLASH_ER_PRG_TIMEOUT);
    if status == FlashStatus::Complete {
        #[cfg(feature = "hk32c030xx")]
        FLASH.cr().modify(|w| w.set_wpg(true));
        #[cfg(feature = "hk32f0301mxxc")]
        FLASH.cr().modify(|w| w.set_pg(true));

        // *(__IO uint32_t*)Address = Data;
        unsafe { (address as *mut u32).write_volatile(data) }

        /* Wait for last operation to be completed */
        status = flash_wait_for_last_operation(FLASH_ER_PRG_TIMEOUT);

        /* Disable the WPG Bit */
        #[cfg(feature = "hk32c030xx")]
        FLASH.cr().modify(|w| w.set_wpg(false));
        #[cfg(feature = "hk32f0301mxxc")]
        FLASH.cr().modify(|w| w.set_pg(false));
    }

    return status;
}

/// @brief  Sets the interrupt vector offset.
/// @param  offset: The interrupt vector offset.
/// @retval None
pub fn flash_int_vec_offset(offset: u32) {
    FLASH.int_vec_offset().write(|w| w.0 = offset);
}

#[derive(Debug, PartialEq)]
pub enum FlashStatus {
    Busy,
    ErrorWRP,
    ErrorProgram,
    Complete,
    Timeout,
}

fn flash_get_status() -> FlashStatus {
    let sr = FLASH.sr().read();
    let status;

    if sr.bsy() {
        status = FlashStatus::Busy;
    } else {
        if sr.wrprterr() {
            status = FlashStatus::ErrorWRP;
        } else {
            if sr.pgerr() {
                status = FlashStatus::ErrorProgram;
            } else {
                status = FlashStatus::Complete;
            }
        }
    }

    return status;
}

fn flash_wait_for_last_operation(mut timeout: u32) -> FlashStatus {
    let mut status = flash_get_status();
    while status == FlashStatus::Busy && timeout > 0 {
        status = flash_get_status();
        timeout -= 1;
    }
    if timeout == 0 {
        status = FlashStatus::Timeout;
    }

    return status;
}