drivers: clock_control: add support for FlexSPI reclock on NXP iMX RT10XX
Add support for reclocking the FlexSPI on NXP iMX RT10XX. This functionality requires an SOC specific clock function to set the clock rate, since the FlexSPI must be reset directly before applying the new clock frequency. Note that all clock constants are defined in this commit, since the memc flexspi driver now depends on a clock node being present. Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
This commit is contained in:
parent
e92e4c249d
commit
f81113e948
14 changed files with 170 additions and 63 deletions
|
@ -316,16 +316,64 @@ static int mcux_ccm_get_subsys_rate(const struct device *dev,
|
|||
/ (CLOCK_GetDiv(kCLOCK_Sai3PreDiv) + 1)
|
||||
/ (CLOCK_GetDiv(kCLOCK_Sai3Div) + 1);
|
||||
break;
|
||||
#endif
|
||||
#if DT_NODE_HAS_STATUS(DT_NODELABEL(flexspi), okay)
|
||||
case IMX_CCM_FLEXSPI_CLK:
|
||||
*rate = CLOCK_GetClockRootFreq(kCLOCK_FlexspiClkRoot);
|
||||
break;
|
||||
#endif
|
||||
#if DT_NODE_HAS_STATUS(DT_NODELABEL(flexspi2), okay)
|
||||
case IMX_CCM_FLEXSPI2_CLK:
|
||||
*rate = CLOCK_GetClockRootFreq(kCLOCK_Flexspi2ClkRoot);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since this function is used to reclock the FlexSPI when running in
|
||||
* XIP, it must be located in RAM when MEMC Flexspi driver is enabled.
|
||||
*/
|
||||
#ifdef CONFIG_MEMC_MCUX_FLEXSPI
|
||||
#define CCM_SET_FUNC_ATTR __ramfunc
|
||||
#else
|
||||
#define CCM_SET_FUNC_ATTR
|
||||
#endif
|
||||
|
||||
static int CCM_SET_FUNC_ATTR mcux_ccm_set_subsys_rate(const struct device *dev,
|
||||
clock_control_subsys_t subsys,
|
||||
clock_control_subsys_rate_t rate)
|
||||
{
|
||||
uint32_t clock_name = (uintptr_t)subsys;
|
||||
uint32_t clock_rate = (uintptr_t)rate;
|
||||
|
||||
switch (clock_name) {
|
||||
case IMX_CCM_FLEXSPI_CLK:
|
||||
__fallthrough;
|
||||
case IMX_CCM_FLEXSPI2_CLK:
|
||||
#if defined(CONFIG_SOC_SERIES_IMX_RT10XX) && defined(CONFIG_MEMC_MCUX_FLEXSPI)
|
||||
/* The SOC is using the FlexSPI for XIP. Therefore,
|
||||
* the FlexSPI itself must be managed within the function,
|
||||
* which is SOC specific.
|
||||
*/
|
||||
return flexspi_clock_set_freq(clock_name, clock_rate);
|
||||
#endif
|
||||
default:
|
||||
/* Silence unused variable warning */
|
||||
ARG_UNUSED(clock_rate);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const struct clock_control_driver_api mcux_ccm_driver_api = {
|
||||
.on = mcux_ccm_on,
|
||||
.off = mcux_ccm_off,
|
||||
.get_rate = mcux_ccm_get_subsys_rate,
|
||||
.set_rate = mcux_ccm_set_subsys_rate,
|
||||
};
|
||||
|
||||
static int mcux_ccm_init(const struct device *dev)
|
||||
|
|
|
@ -433,8 +433,9 @@ static int flash_flexspi_hyperflash_write(const struct device *dev, off_t offset
|
|||
key = irq_lock();
|
||||
}
|
||||
|
||||
/* Clock FlexSPI at 84 MHZ (42MHz SCLK in DDR mode) */
|
||||
(void)memc_flexspi_update_clock(&data->controller, &data->config,
|
||||
data->port, MEMC_FLEXSPI_CLOCK_42M);
|
||||
data->port, MHZ(84));
|
||||
|
||||
while (len) {
|
||||
/* Writing between two page sizes crashes the platform so we
|
||||
|
@ -477,8 +478,9 @@ static int flash_flexspi_hyperflash_write(const struct device *dev, off_t offset
|
|||
len -= i;
|
||||
}
|
||||
|
||||
/* Clock FlexSPI at 332 MHZ (166 MHz SCLK in DDR mode) */
|
||||
(void)memc_flexspi_update_clock(&data->controller, &data->config,
|
||||
data->port, MEMC_FLEXSPI_CLOCK_166M);
|
||||
data->port, MHZ(332));
|
||||
|
||||
#ifdef CONFIG_HAS_MCUX_CACHE
|
||||
DCACHE_InvalidateByRange((uint32_t) dst, size);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/drivers/pinctrl.h>
|
||||
#include <zephyr/drivers/clock_control.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <soc.h>
|
||||
|
||||
|
@ -60,6 +61,8 @@ struct memc_flexspi_data {
|
|||
struct port_lut port_luts[kFLEXSPI_PortCount];
|
||||
struct memc_flexspi_buf_cfg *buf_cfg;
|
||||
uint8_t buf_cfg_cnt;
|
||||
const struct device *clock_dev;
|
||||
clock_control_subsys_t clock_subsys;
|
||||
};
|
||||
|
||||
void memc_flexspi_wait_bus_idle(const struct device *dev)
|
||||
|
@ -79,30 +82,56 @@ bool memc_flexspi_is_running_xip(const struct device *dev)
|
|||
|
||||
int memc_flexspi_update_clock(const struct device *dev,
|
||||
flexspi_device_config_t *device_config,
|
||||
flexspi_port_t port, enum memc_flexspi_clock_t clock)
|
||||
flexspi_port_t port, uint32_t freq_hz)
|
||||
{
|
||||
#if CONFIG_SOC_SERIES_IMX_RT10XX
|
||||
struct memc_flexspi_data *data = dev->data;
|
||||
uint32_t rate;
|
||||
uint32_t key;
|
||||
int ret;
|
||||
|
||||
/* To reclock the FlexSPI, we should:
|
||||
* - disable the module
|
||||
* - set the new clock
|
||||
* - reenable the module
|
||||
* - reset the module
|
||||
* We CANNOT XIP at any point during this process
|
||||
*/
|
||||
key = irq_lock();
|
||||
memc_flexspi_wait_bus_idle(dev);
|
||||
|
||||
FLEXSPI_Enable(data->base, false);
|
||||
|
||||
flexspi_clock_set_div(clock == MEMC_FLEXSPI_CLOCK_166M ? 0 : 3);
|
||||
|
||||
FLEXSPI_Enable(data->base, true);
|
||||
ret = clock_control_set_rate(data->clock_dev, data->clock_subsys,
|
||||
(clock_control_subsys_rate_t)freq_hz);
|
||||
if (ret < 0) {
|
||||
irq_unlock(key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to update the DLL value before we call clock_control_get_rate,
|
||||
* because this will cause XIP (flash reads) to occur. Although the
|
||||
* true flash clock is not known, assume the set_rate function programmed
|
||||
* a value close to what we requested.
|
||||
*/
|
||||
device_config->flexspiRootClk = freq_hz;
|
||||
FLEXSPI_UpdateDllValue(data->base, device_config, port);
|
||||
memc_flexspi_reset(dev);
|
||||
|
||||
device_config->flexspiRootClk = flexspi_clock_get_freq();
|
||||
memc_flexspi_wait_bus_idle(dev);
|
||||
ret = clock_control_get_rate(data->clock_dev, data->clock_subsys, &rate);
|
||||
if (ret < 0) {
|
||||
irq_unlock(key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
device_config->flexspiRootClk = rate;
|
||||
FLEXSPI_UpdateDllValue(data->base, device_config, port);
|
||||
|
||||
memc_flexspi_reset(dev);
|
||||
|
||||
irq_unlock(key);
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return -ENOTSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
int memc_flexspi_set_device_config(const struct device *dev,
|
||||
|
@ -332,6 +361,9 @@ static int memc_flexspi_pm_action(const struct device *dev, enum pm_device_actio
|
|||
.buf_cfg_cnt = sizeof(buf_cfg_##n) / \
|
||||
sizeof(struct memc_flexspi_buf_cfg), \
|
||||
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
|
||||
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
|
||||
.clock_subsys = (clock_control_subsys_t) \
|
||||
DT_INST_CLOCKS_CELL(n, name), \
|
||||
}; \
|
||||
\
|
||||
PM_DEVICE_DT_INST_DEFINE(n, memc_flexspi_pm_action); \
|
||||
|
|
|
@ -8,13 +8,6 @@
|
|||
#include <sys/types.h>
|
||||
#include <fsl_flexspi.h>
|
||||
|
||||
enum memc_flexspi_clock_t {
|
||||
/* Flexspi clock 332M, DDR mode, internal clock 166M. */
|
||||
MEMC_FLEXSPI_CLOCK_166M,
|
||||
/* Flexspi clock 83M, DDR mode, internal clock 42M. */
|
||||
MEMC_FLEXSPI_CLOCK_42M,
|
||||
};
|
||||
|
||||
/* Size of a command in the LUT table */
|
||||
#define MEMC_FLEXSPI_CMD_SIZE 4U
|
||||
|
||||
|
@ -43,17 +36,17 @@ bool memc_flexspi_is_running_xip(const struct device *dev);
|
|||
/**
|
||||
* @brief Update clock selection of the FlexSPI device
|
||||
*
|
||||
* Updates clock selection of the FlexSPI device to a new clock speed.
|
||||
* Updates clock frequency of FlexSPI to new clock speed.
|
||||
*
|
||||
* @param dev: FlexSPI device
|
||||
* @param device_config: External device configuration.
|
||||
* @param port: FlexSPI port to use for this external device
|
||||
* @param clock: new clock selection to apply
|
||||
* @param freq_hz: new clock frequency to apply
|
||||
* @return 0 on success, negative value on failure
|
||||
*/
|
||||
int memc_flexspi_update_clock(const struct device *dev,
|
||||
flexspi_device_config_t *device_config,
|
||||
flexspi_port_t port, enum memc_flexspi_clock_t clock);
|
||||
flexspi_port_t port, uint32_t freq_hz);
|
||||
|
||||
/**
|
||||
* @brief configure new FlexSPI device
|
||||
|
|
|
@ -126,6 +126,7 @@
|
|||
ahb-bufferable;
|
||||
ahb-cacheable;
|
||||
status = "disabled";
|
||||
clocks = <&ccm IMX_CCM_FLEXSPI_CLK 0x0 0x0>;
|
||||
};
|
||||
|
||||
flexspi2: spi@402a4000 {
|
||||
|
@ -137,6 +138,7 @@
|
|||
ahb-bufferable;
|
||||
ahb-cacheable;
|
||||
status = "disabled";
|
||||
clocks = <&ccm IMX_CCM_FLEXSPI2_CLK 0x0 0x0>;
|
||||
};
|
||||
|
||||
semc: semc0@402f0000 {
|
||||
|
|
|
@ -95,6 +95,7 @@
|
|||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "disabled";
|
||||
clocks = <&ccm IMX_CCM_FLEXSPI_CLK 0x0 0>;
|
||||
};
|
||||
|
||||
flexspi2: spi@400d0000 {
|
||||
|
@ -104,6 +105,7 @@
|
|||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "disabled";
|
||||
clocks = <&ccm IMX_CCM_FLEXSPI2_CLK 0x0 0>;
|
||||
};
|
||||
|
||||
semc: semc0@400d4000 {
|
||||
|
|
|
@ -687,6 +687,7 @@
|
|||
interrupts = <42 0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&clkctl1 MCUX_FLEXSPI_CLK>;
|
||||
};
|
||||
|
||||
&flexspi2 {
|
||||
|
@ -695,6 +696,7 @@
|
|||
interrupts = <42 0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&clkctl1 MCUX_FLEXSPI2_CLK>;
|
||||
};
|
||||
|
||||
&nvic {
|
||||
|
|
|
@ -501,6 +501,7 @@
|
|||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "disabled";
|
||||
clocks = <&clkctl1 MCUX_FLEXSPI_CLK>;
|
||||
};
|
||||
|
||||
&nvic {
|
||||
|
|
|
@ -58,4 +58,7 @@
|
|||
#define IMX_CCM_ENET_CLK 0x0E00UL
|
||||
#define IMX_CCM_ENET_PLL 0x0E01UL
|
||||
|
||||
#define IMX_CCM_FLEXSPI_CLK 0x0F00UL
|
||||
#define IMX_CCM_FLEXSPI2_CLK 0x0F01UL
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_IMX_CCM_H_ */
|
||||
|
|
|
@ -92,5 +92,8 @@
|
|||
#define IMX_CCM_ENET_CLK 0x3000UL
|
||||
#define IMX_CCM_ENET_PLL 0x3001UL
|
||||
|
||||
/* FLEXSPI */
|
||||
#define IMX_CCM_FLEXSPI_CLK 0x4000UL
|
||||
#define IMX_CCM_FLEXSPI2_CLK 0x4001UL
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_IMX_CCM_REV2_H_ */
|
||||
|
|
|
@ -53,6 +53,9 @@
|
|||
|
||||
#define MCUX_DMIC_CLK 35
|
||||
|
||||
#define MCUX_FLEXSPI_CLK 36
|
||||
#define MCUX_FLEXSPI2_CLK 37
|
||||
|
||||
#define MCUX_MRT_CLK 40
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_MCUX_LPC_SYSCON_H_ */
|
||||
|
|
|
@ -28,9 +28,11 @@ if(CONFIG_PM)
|
|||
zephyr_sources_ifdef(CONFIG_SOC_SERIES_IMX_RT11XX power_rt11xx.c)
|
||||
endif()
|
||||
|
||||
if (CONFIG_FLASH_MCUX_FLEXSPI_XIP AND CONFIG_SOC_SERIES_IMX_RT10XX AND CONFIG_MEMC)
|
||||
if (CONFIG_SOC_SERIES_IMX_RT10XX AND CONFIG_MEMC_MCUX_FLEXSPI)
|
||||
zephyr_sources(flexspi_rt10xx.c)
|
||||
zephyr_code_relocate(FILES flexspi_rt10xx.c LOCATION ITCM_TEXT)
|
||||
if (CONFIG_FLASH_MCUX_FLEXSPI_XIP)
|
||||
zephyr_code_relocate(FILES flexspi_rt10xx.c LOCATION ITCM_TEXT)
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if (CONFIG_PM AND CONFIG_SOC_SERIES_IMX_RT10XX)
|
||||
|
|
|
@ -5,50 +5,63 @@
|
|||
*/
|
||||
|
||||
#include <fsl_clock.h>
|
||||
#include <fsl_flexspi.h>
|
||||
#include <soc.h>
|
||||
#include <errno.h>
|
||||
#include <zephyr/irq.h>
|
||||
#include <zephyr/dt-bindings/clock/imx_ccm.h>
|
||||
|
||||
/* reimplementation of non-inline CLOCK_GetFreq(kCLOCK_Usb1PllPfd0Clk) */
|
||||
static uint32_t clock_get_usb1_pll_pfd0_clk(void)
|
||||
uint32_t flexspi_clock_set_freq(uint32_t clock_name, uint32_t rate)
|
||||
{
|
||||
uint32_t freq;
|
||||
uint8_t divider;
|
||||
uint32_t root_rate;
|
||||
FLEXSPI_Type *flexspi;
|
||||
clock_div_t div_sel;
|
||||
clock_ip_name_t clk_name;
|
||||
|
||||
if (!CLOCK_IsPllEnabled(CCM_ANALOG, kCLOCK_PllUsb1)) {
|
||||
return 0;
|
||||
switch (clock_name) {
|
||||
case IMX_CCM_FLEXSPI_CLK:
|
||||
/* Get clock root frequency */
|
||||
root_rate = CLOCK_GetClockRootFreq(kCLOCK_FlexspiClkRoot) *
|
||||
(CLOCK_GetDiv(kCLOCK_FlexspiDiv) + 1);
|
||||
flexspi = (FLEXSPI_Type *)DT_REG_ADDR(DT_NODELABEL(flexspi));
|
||||
div_sel = kCLOCK_FlexspiDiv;
|
||||
clk_name = kCLOCK_FlexSpi;
|
||||
break;
|
||||
#if DT_NODE_HAS_STATUS(DT_NODELABEL(flexspi2), okay)
|
||||
case IMX_CCM_FLEXSPI2_CLK:
|
||||
/* Get clock root frequency */
|
||||
root_rate = CLOCK_GetClockRootFreq(kCLOCK_Flexspi2ClkRoot) *
|
||||
(CLOCK_GetDiv(kCLOCK_Flexspi2Div) + 1);
|
||||
flexspi = (FLEXSPI_Type *)DT_REG_ADDR(DT_NODELABEL(flexspi2));
|
||||
div_sel = kCLOCK_Flexspi2Div;
|
||||
clk_name = kCLOCK_FlexSpi2;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
/* Select a divider based on root frequency.
|
||||
* if we can't get an exact divider, round down
|
||||
*/
|
||||
divider = ((root_rate + (rate - 1)) / rate) - 1;
|
||||
/* Cap divider to max value */
|
||||
divider = MIN(divider, kCLOCK_FlexspiDivBy8);
|
||||
|
||||
freq = CLOCK_GetPllBypassRefClk(CCM_ANALOG, kCLOCK_PllUsb1);
|
||||
|
||||
if (CLOCK_IsPllBypassed(CCM_ANALOG, kCLOCK_PllUsb1)) {
|
||||
return freq;
|
||||
while (FLEXSPI_GetBusIdleStatus(flexspi) == false) {
|
||||
/* Spin */
|
||||
}
|
||||
FLEXSPI_Enable(flexspi, false);
|
||||
|
||||
freq *= ((CCM_ANALOG->PLL_USB1 & CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK) != 0) ? 22 : 20;
|
||||
CLOCK_DisableClock(clk_name);
|
||||
|
||||
/* get current USB1 PLL PFD output frequency */
|
||||
freq /= (CCM_ANALOG->PFD_480 & CCM_ANALOG_PFD_480_PFD0_FRAC_MASK) >>
|
||||
CCM_ANALOG_PFD_480_PFD0_FRAC_SHIFT;
|
||||
freq *= 18;
|
||||
CLOCK_SetDiv(div_sel, divider);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
void flexspi_clock_set_div(uint32_t value)
|
||||
{
|
||||
CLOCK_DisableClock(kCLOCK_FlexSpi);
|
||||
|
||||
CLOCK_SetDiv(kCLOCK_FlexspiDiv, value);
|
||||
|
||||
CLOCK_EnableClock(kCLOCK_FlexSpi);
|
||||
}
|
||||
|
||||
uint32_t flexspi_clock_get_freq(void)
|
||||
{
|
||||
uint32_t divider;
|
||||
uint32_t frequency;
|
||||
|
||||
divider = CLOCK_GetDiv(kCLOCK_FlexspiDiv);
|
||||
|
||||
frequency = clock_get_usb1_pll_pfd0_clk() / (divider + 1);
|
||||
|
||||
return frequency;
|
||||
CLOCK_EnableClock(clk_name);
|
||||
|
||||
FLEXSPI_Enable(flexspi, true);
|
||||
|
||||
FLEXSPI_SoftwareReset(flexspi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -32,8 +32,9 @@ void imxrt_pre_init_display_interface(void);
|
|||
void imxrt_post_init_display_interface(void);
|
||||
#endif
|
||||
|
||||
void flexspi_clock_set_div(uint32_t value);
|
||||
uint32_t flexspi_clock_get_freq(void);
|
||||
#ifdef CONFIG_MEMC
|
||||
uint32_t flexspi_clock_set_freq(uint32_t clock_name, uint32_t rate);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue