reflow-oven-control-sw/stm-firmware/stm-periph/rcc-manager.c

184 lines
4.5 KiB
C

/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stm-periph/rcc-manager.h>
#include <helper-macros/helper-macros.h>
#include <stdlib.h>
#include <stm32/stm32f4xx.h>
struct rcc_enable_count {
volatile uint32_t *rcc_reg_addr;
uint32_t enable_bit_cnt;
uint8_t rcc_enable_bit_pos;
};
#if RCC_ENABLE_MANAGER_STATIC
static struct rcc_enable_count enable_count_list[RCC_ENABLE_MANAGER_COUNT] = {0};
#else
#error "RCC manager with dynamic memory not implemented!"
#endif
#if RCC_ENABLE_MANAGER_STATIC
static struct rcc_enable_count *search_enable_entry_in_list(volatile uint32_t *reg_addr, uint8_t bit_pos)
{
unsigned int i;
struct rcc_enable_count *ret_element = NULL;
struct rcc_enable_count *current_element;
for (i = 0; i < COUNT_OF(enable_count_list); i++) {
current_element = &enable_count_list[i];
/* Check if register address and bit position match */
if (reg_addr != current_element->rcc_reg_addr)
continue;
if (bit_pos != current_element->rcc_enable_bit_pos)
continue;
/* Found entry. Wohoo! */
ret_element = current_element;
}
return ret_element;
}
static struct rcc_enable_count *enable_entry_list_get_free_entry()
{
struct rcc_enable_count *ret_ptr = NULL;
const int list_len = COUNT_OF(enable_count_list);
int i;
for (i = 0; i < list_len; i++) {
if (enable_count_list[i].rcc_reg_addr == NULL) {
ret_ptr = &enable_count_list[i];
/* Clear the count value to be safe */
ret_ptr->enable_bit_cnt = 0;
break;
}
}
return ret_ptr;
}
static void enable_entry_list_remove_entry(struct rcc_enable_count *entry)
{
if (!entry)
return;
entry->rcc_reg_addr = NULL;
entry->enable_bit_cnt = 0;
entry->rcc_enable_bit_pos = 0;
}
#endif
int rcc_manager_enable_clock(volatile uint32_t *rcc_enable_register, uint8_t bit_no)
{
int ret_val = 0;
struct rcc_enable_count *entry;
if (!rcc_enable_register || bit_no > 31) {
return -1000;
}
/* Enable the clock in any case, no matter what follows */
*rcc_enable_register |= (1U<<bit_no);
/* Check if bit is already in list */
entry = search_enable_entry_in_list(rcc_enable_register, bit_no);
if (!entry) {
/* Create ne entry at first free place in list */
entry = enable_entry_list_get_free_entry();
/* Check if entry is valid. If not return */
if (!entry) {
ret_val = -1;
goto ret_error_code;
}
/* Set entry */
entry->rcc_reg_addr = rcc_enable_register;
entry->rcc_enable_bit_pos = bit_no;
}
/* Increment enable counter */
entry->enable_bit_cnt++;
ret_error_code:
return ret_val;
}
int rcc_manager_disable_clock(volatile uint32_t *rcc_enable_register, uint8_t bit_no)
{
int ret_val = -1;
struct rcc_enable_count *entry;
if (!rcc_enable_register || bit_no > 31) {
return -1000;
}
entry = search_enable_entry_in_list(rcc_enable_register, bit_no);
/* If entry is found and has a count of zero, disable clock */
if (entry) {
/* Found entry => Decrement count and delete if zero */
entry->enable_bit_cnt--;
if (entry->enable_bit_cnt <= 0) {
enable_entry_list_remove_entry(entry);
/* Disable clock */
*rcc_enable_register &= ~(1U<<bit_no);
}
ret_val = 0;
}
return ret_val;
}
enum rcc_reset_source rcc_manager_get_reset_cause(bool clear_flags)
{
enum rcc_reset_source ret = 0;
uint32_t rcc_csr;
rcc_csr = RCC->CSR;
if (rcc_csr & RCC_CSR_LPWRRSTF)
ret |= RCC_RESET_SOURCE_LOW_POWER;
if (rcc_csr & RCC_CSR_WWDGRSTF)
ret |= RCC_RESET_SOURCE_WWD;
if (rcc_csr & RCC_CSR_WDGRSTF)
ret |= RCC_RESET_SOURCE_IWDG;
if (rcc_csr & RCC_CSR_SFTRSTF)
ret |= RCC_RESET_SOURCE_SOFTWARE;
if (rcc_csr & RCC_CSR_PORRSTF)
ret |= RCC_RESET_SOURCE_POWER_ON;
if (rcc_csr & RCC_CSR_PADRSTF)
ret |= RCC_RESET_SOURCE_PIN;
if (rcc_csr & RCC_CSR_BORRSTF)
ret |= RCC_RESET_BOR_POR;
if (clear_flags)
RCC->CSR |= RCC_CSR_RMVF;
return ret;
}