rockbox/firmware/target/arm/stm32/echoplayer/clock-echoplayer.c
Aidan MacDonald 87f2024631 echoplayer: clock FMC from PLL1Q
Unlinking the FMC from the bus clock should make it
simpler to do CPU frequency scaling later on.

Change-Id: Ia87bbe3dd01ff25ea1520309680017b400471bfe
2026-03-04 10:23:23 -05:00

213 lines
6.1 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "clock-stm32h7.h"
#include "lcd-echoplayer.h"
#include "panic.h"
#include "regs/stm32h743/flash.h"
#include "regs/stm32h743/fmc.h"
#include "regs/stm32h743/pwr.h"
#include "regs/stm32h743/rcc.h"
#include "regs/stm32h743/syscfg.h"
#define PLL1Q_FREQ 240000000
#define PLL3R_FREQ 6000000
/* Flag to use VOS0 */
#define STM32H743_USE_VOS0 (CPU_FREQ > 400000000)
INIT_ATTR static void init_hse(void)
{
reg_writef(RCC_CR, HSEON(1));
while (!reg_readf(RCC_CR, HSERDY));
}
INIT_ATTR static void init_pll(void)
{
/* For simplicity, PLL parameters are hardcoded */
_Static_assert(STM32_HSE_FREQ == 24000000,
"HSE frequency not correct");
_Static_assert(LCD_DOTCLOCK_FREQ <= PLL3R_FREQ,
"PLL3R too slow for LCD");
_Static_assert(PLL1Q_FREQ == 240000000,
"PLL1Q parameters not correct");
/*
* Use HSE/4 input for PLL1
* Use HSE/12 input for PLL3
* PLL2 reserved for audio; configured in target PCM code
*/
reg_writef(RCC_PLLCKSELR,
PLLSRC_V(HSE),
DIVM1(4),
DIVM2(0),
DIVM3(12));
/* Enable PLL1P, PLL1Q, PLL3R */
reg_writef(RCC_PLLCFGR,
DIVP1EN(1),
DIVQ1EN(1),
DIVR1EN(0),
DIVP2EN(0),
DIVQ2EN(0),
DIVR2EN(0),
DIVP3EN(0),
DIVQ3EN(0),
DIVR3EN(1),
PLL1RGE_V(4_8MHZ),
PLL1VCOSEL_V(WIDE),
PLL1FRACEN(0),
PLL3RGE_V(1_2MHZ),
PLL3VCOSEL_V(MEDIUM),
PLL3FRACEN(0));
reg_writef(RCC_PLL1DIVR,
DIVN(80 - 1), /* 6 * 80 = 480 MHz */
DIVP(1 - 1), /* 480 / 1 = 480 MHz */
DIVQ(2 - 1), /* 480 / 2 = 240 MHz */
DIVR(1 - 1));
reg_writef(RCC_PLL3DIVR,
DIVN(90 - 1), /* 2 * 90 = 180 MHz */
DIVP(1 - 1),
DIVQ(1 - 1),
DIVR(30 - 1)); /* 180 / 30 = 6 MHz */
reg_writef(RCC_CR, PLL1ON(1), PLL3ON(1));
while (!reg_readf(RCC_CR, PLL1RDY));
while (!reg_readf(RCC_CR, PLL3RDY));
}
INIT_ATTR static void init_vos(void)
{
reg_writef(PWR_D3CR, VOS_V(VOS1));
while (!reg_readf(PWR_D3CR, VOSRDY));
if (STM32H743_USE_VOS0)
{
reg_writef(RCC_APB4ENR, SYSCFGEN(1));
/* Set ODEN bit to enter VOS0 */
reg_writef(SYSCFG_PWRCFG, ODEN(1));
while (!reg_readf(PWR_D3CR, VOSRDY));
reg_writef(RCC_APB4ENR, SYSCFGEN(0));
}
}
INIT_ATTR static void init_system_clock(void)
{
/* Enable HCLK /2 divider (CPU is at 480 MHz, HCLK limit is 240 MHz) */
reg_writef(RCC_D1CFGR, HPRE(8));
while (reg_readf(RCC_D1CFGR, HPRE) != 8);
/* Enable ABP /2 dividers (HCLK/2, APB limit is 120 MHz) */
reg_writef(RCC_D1CFGR, D1PPRE(4));
reg_writef(RCC_D2CFGR, D2PPRE1(4), D2PPRE2(4));
reg_writef(RCC_D3CFGR, D3PPRE(4));
/* Switch to PLL1P system clock source */
reg_writef(RCC_CFGR, SW_V(PLL1P));
while (reg_readf(RCC_CFGR, SWS) != BV_RCC_CFGR_SWS_PLL1P);
/* Reduce flash access latency */
reg_writef(FLASH_ACR, LATENCY(4), WRHIGHFREQ(2));
while (reg_readf(FLASH_ACR, LATENCY) != 4);
}
INIT_ATTR static void init_lse(void)
{
/*
* Skip if LSE and RTC are already enabled.
*/
if (reg_readf(RCC_BDCR, LSERDY) &&
reg_readf(RCC_BDCR, RTCEN))
return;
/*
* Enable LSE and set it as RTC clock source,
* then re-enable backup domain write protection.
*/
reg_writef(PWR_CR1, DBP(1));
/* Reset backup domain */
reg_writef(RCC_BDCR, BDRST(1));
reg_writef(RCC_BDCR, BDRST(0));
reg_writef(RCC_BDCR, LSEON(1), LSEDRV(3));
while (!reg_readf(RCC_BDCR, LSERDY));
reg_writef(RCC_BDCR, RTCEN(1), RTCSEL_V(LSE));
reg_writef(PWR_CR1, DBP(0));
}
INIT_ATTR static void init_periph_clock(void)
{
reg_writef(RCC_D1CCIPR, SDMMCSEL_V(PLL1Q));
reg_writef(RCC_D2CCIP1R, SAI1SEL_V(PLL2P), SPI45SEL_V(HSE));
reg_writef(RCC_D2CCIP2R, I2C123SEL_V(HSI));
/* Enable AXI SRAM in sleep mode to allow DMA'ing out of it */
reg_writef(RCC_AHB3LPENR, AXISRAMEN(1));
}
void echoplayer_clock_init(void)
{
init_hse();
init_pll();
init_vos();
init_system_clock();
init_lse();
init_periph_clock();
}
const struct stm32_clock sdmmc1_ker_clock = {
.frequency = PLL1Q_FREQ,
.en_reg = ITA_RCC_AHB3ENR,
.en_bit = BM_RCC_AHB3ENR_SDMMC1EN,
.lpen_reg = ITA_RCC_AHB3LPENR,
.lpen_bit = BM_RCC_AHB3LPENR_SDMMC1EN,
};
const struct stm32_clock ltdc_ker_clock = {
.frequency = PLL3R_FREQ,
.en_reg = ITA_RCC_APB3ENR,
.en_bit = BM_RCC_APB3ENR_LTDCEN,
.lpen_reg = ITA_RCC_APB3LPENR,
.lpen_bit = BM_RCC_APB3ENR_LTDCEN,
};
const struct stm32_clock spi5_ker_clock = {
.frequency = STM32_HSE_FREQ,
.en_reg = ITA_RCC_APB2ENR,
.en_bit = BM_RCC_APB2ENR_SPI5EN,
.lpen_reg = ITA_RCC_APB2LPENR,
.lpen_bit = BM_RCC_APB2ENR_SPI5EN,
};
const struct stm32_clock i2c1_ker_clock = {
.frequency = STM32_HSI_FREQ,
.en_reg = ITA_RCC_APB1LENR,
.en_bit = BM_RCC_APB1LENR_I2C1EN,
.lpen_reg = ITA_RCC_APB1LLPENR,
.lpen_bit = BM_RCC_APB1LENR_I2C1EN,
};