Merge tag 'u-boot-stm32-20250919' of https://source.denx.de/u-boot/custodians/u-boot-stm into next

CI:
  - https://source.denx.de/u-boot/custodians/u-boot-stm/-/pipelines/27668

STM32MP2:
  - Add SPI flashes support
  - Add RIFSC system bus driver fixes
This commit is contained in:
Tom Rini
2025-09-19 08:06:22 -06:00
13 changed files with 1272 additions and 73 deletions

View File

@@ -709,6 +709,7 @@ F: drivers/gpio/stm32_gpio.c
F: drivers/hwspinlock/stm32_hwspinlock.c
F: drivers/i2c/stm32f7_i2c.c
F: drivers/mailbox/stm32-ipcc.c
F: drivers/memory/stm32-omm.c
F: drivers/misc/stm32mp_fuse.c
F: drivers/misc/stm32_rcc.c
F: drivers/mmc/stm32_sdmmc2.c
@@ -725,6 +726,7 @@ F: drivers/rng/optee_rng.c
F: drivers/rng/stm32_rng.c
F: drivers/rtc/stm32_rtc.c
F: drivers/serial/serial_stm32.*
F: drivers/spi/stm32_ospi.c
F: drivers/spi/stm32_qspi.c
F: drivers/spi/stm32_spi.c
F: drivers/video/stm32/stm32_ltdc.c

View File

@@ -12,6 +12,47 @@
};
};
&flash0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "fsbla1";
reg = <0x00000000 0x00040000>;
};
partition@40000 {
label = "fsbla2";
reg = <0x00040000 0x00040000>;
};
partition@80000 {
label = "metadata1";
reg = <0x00080000 0x00040000>;
};
partition@C0000 {
label = "metadata2";
reg = <0x000C0000 0x00040000>;
};
partition@100000 {
label = "fip-a";
reg = <0x00100000 0x00400000>;
};
partition@500000 {
label = "fip-b";
reg = <0x00500000 0x00400000>;
};
partition@900000 {
label = "u-boot-env";
reg = <0x00900000 0x00080000>;
};
partition@980000 {
label = "nor-user";
reg = <0x00980000 0x03680000>;
};
};
};
&usart2 {
bootph-all;
};

View File

@@ -8,19 +8,53 @@
#include <linux/types.h>
#if IS_ENABLED(CONFIG_STM32MP21X) || IS_ENABLED(CONFIG_STM32MP23X) || IS_ENABLED(CONFIG_STM32MP25X)
/**
* stm32_rifsc_check_access - Check RIF accesses for given device node
* stm32_rifsc_grant_access_by_id - Grant RIFSC access for a given peripheral using its ID
*
* @device_node Node of the device for which the accesses are checked
* @device_node Node of the peripheral
* @id ID of the peripheral of which access should be granted
*/
int stm32_rifsc_check_access(ofnode device_node);
int stm32_rifsc_grant_access_by_id(ofnode device_node, u32 id);
/**
* stm32_rifsc_check_access - Check RIF accesses for given id
* stm32_rifsc_grant_access_by_id - Grant RIFSC access for a given peripheral using its node
*
* @device_node Node of the device to get a reference on RIFSC
* @id ID of the resource to check
* @id node of the peripheral of which access should be granted
*/
int stm32_rifsc_check_access_by_id(ofnode device_node, u32 id);
int stm32_rifsc_grant_access(ofnode device_node);
/**
* stm32_rifsc_release_access_by_id - Release RIFSC access for a given peripheral using its ID
*
* @device_node Node of the peripheral
* @id ID of the peripheral of which access should be released
*/
void stm32_rifsc_release_access_by_id(ofnode device_node, u32 id);
/**
* stm32_rifsc_release_access_by_id - Release RIFSC access for a given peripheral using its node
*
* @id node of the peripheral of which access should be released
*/
void stm32_rifsc_release_access(ofnode device_node);
#else
static inline int stm32_rifsc_grant_access_by_id(ofnode device_node, u32 id)
{
return -EACCES;
}
static inline int stm32_rifsc_grant_access(ofnode device_node)
{
return -EACCES;
}
static inline void stm32_rifsc_release_access_by_id(ofnode device_node, u32 id)
{
}
static inline void stm32_rifsc_release_access(ofnode device_node)
{
}
#endif
#endif /* MACH_RIF_H*/

View File

@@ -61,42 +61,41 @@ struct stm32_rifsc_child_plat {
u32 domain_id;
};
static bool stm32_rif_is_semaphore_available(void *base, u32 id)
static bool stm32_rif_is_semaphore_available(void *addr)
{
void *addr = base + RIFSC_RISC_PER0_SEMCR(id);
return !(readl(addr) & SEMCR_MUTEX);
}
static int stm32_rif_acquire_semaphore(void *base, u32 id)
static int stm32_rifsc_acquire_semaphore(void *base, u32 id)
{
void *addr = base + RIFSC_RISC_PER0_SEMCR(id);
/* Check that the semaphore is available */
if (!stm32_rif_is_semaphore_available(base, id))
if (!stm32_rif_is_semaphore_available(addr) &&
FIELD_GET(RIFSC_RISC_SCID_MASK, (readl(addr)) != RIF_CID1))
return -EACCES;
setbits_le32(addr, SEMCR_MUTEX);
/* Check that CID1 has the semaphore */
if (stm32_rif_is_semaphore_available(base, id) ||
if (stm32_rif_is_semaphore_available(addr) ||
FIELD_GET(RIFSC_RISC_SCID_MASK, (readl(addr)) != RIF_CID1))
return -EACCES;
return 0;
}
static int stm32_rif_release_semaphore(void *base, u32 id)
static int stm32_rifsc_release_semaphore(void *base, u32 id)
{
void *addr = base + RIFSC_RISC_PER0_SEMCR(id);
if (stm32_rif_is_semaphore_available(base, id))
if (stm32_rif_is_semaphore_available(addr))
return 0;
clrbits_le32(addr, SEMCR_MUTEX);
/* Ok if another compartment takes the semaphore before the check */
if (!stm32_rif_is_semaphore_available(base, id) &&
if (!stm32_rif_is_semaphore_available(addr) &&
FIELD_GET(RIFSC_RISC_SCID_MASK, (readl(addr)) == RIF_CID1))
return -EACCES;
@@ -105,11 +104,10 @@ static int stm32_rif_release_semaphore(void *base, u32 id)
static int rifsc_parse_access_controller(ofnode node, struct ofnode_phandle_args *args)
{
int ret;
int ret = ofnode_parse_phandle_with_args(node, "access-controllers",
"#access-controller-cells", 0,
0, args);
ret = ofnode_parse_phandle_with_args(node, "access-controllers",
"#access-controller-cells", 0,
0, args);
if (ret) {
log_debug("failed to parse access-controller (%d)\n", ret);
return ret;
@@ -170,8 +168,8 @@ static int rifsc_check_access(void *base, u32 id)
log_debug("Not in semaphore whitelist for peripheral %d\n", id);
return -EACCES;
}
if (!stm32_rif_is_semaphore_available(base, id) &&
!(FIELD_GET(RIFSC_RISC_SCID_MASK, sem_reg_value) & BIT(RIF_CID1))) {
if (!stm32_rif_is_semaphore_available(base + RIFSC_RISC_PER0_SEMCR(id)) &&
!(FIELD_GET(RIFSC_RISC_SCID_MASK, sem_reg_value) & RIF_CID1)) {
log_debug("Semaphore unavailable for peripheral %d\n", id);
return -EACCES;
}
@@ -187,42 +185,24 @@ skip_cid_check:
return 0;
}
int stm32_rifsc_check_access_by_id(ofnode device_node, u32 id)
int stm32_rifsc_grant_access_by_id(ofnode device_node, u32 id)
{
struct ofnode_phandle_args args;
int err;
if (id >= STM32MP25_RIFSC_ENTRIES)
return -EINVAL;
err = rifsc_parse_access_controller(device_node, &args);
if (err)
return err;
return rifsc_check_access((void *)ofnode_get_addr(args.node), id);
}
int stm32_rifsc_check_access(ofnode device_node)
{
struct ofnode_phandle_args args;
int err;
err = rifsc_parse_access_controller(device_node, &args);
if (err)
return err;
return rifsc_check_access((void *)ofnode_get_addr(args.node), args.args[0]);
}
static int stm32_rifsc_child_pre_probe(struct udevice *dev)
{
struct stm32_rifsc_plat *plat = dev_get_plat(dev->parent);
struct stm32_rifsc_child_plat *child_plat = dev_get_parent_plat(dev);
u32 cid_reg_value;
void *rifsc_base;
int err;
u32 id = child_plat->domain_id;
cid_reg_value = readl(plat->base + RIFSC_RISC_PER0_CIDCFGR(id));
err = rifsc_parse_access_controller(device_node, &args);
if (err)
panic("Failed to parse access-controllers property\n");
rifsc_base = (void *)ofnode_get_addr(args.node);
err = rifsc_check_access(rifsc_base, id);
if (err)
return err;
cid_reg_value = readl(rifsc_base + RIFSC_RISC_PER0_CIDCFGR(id));
/*
* If the peripheral is in semaphore mode, take the semaphore so that
@@ -230,39 +210,81 @@ static int stm32_rifsc_child_pre_probe(struct udevice *dev)
*/
if (cid_reg_value & CIDCFGR_SEMEN &&
(FIELD_GET(RIFSC_RISC_SEMWL_MASK, cid_reg_value) & BIT(RIF_CID1))) {
err = stm32_rif_acquire_semaphore(plat->base, id);
err = stm32_rifsc_acquire_semaphore(rifsc_base, id);
if (err) {
dev_err(dev, "Couldn't acquire RIF semaphore for peripheral %d (%d)\n",
id, err);
pr_err("Couldn't acquire RIF semaphore for peripheral %d (%d)\n",
id, err);
return err;
}
dev_dbg(dev, "Acquiring semaphore for peripheral %d\n", id);
pr_debug("Acquiring RIF semaphore for peripheral %d\n", id);
}
return 0;
}
static int stm32_rifsc_child_post_remove(struct udevice *dev)
int stm32_rifsc_grant_access(ofnode device_node)
{
struct stm32_rifsc_plat *plat = dev_get_plat(dev->parent);
struct stm32_rifsc_child_plat *child_plat = dev_get_parent_plat(dev);
u32 cid_reg_value;
struct ofnode_phandle_args args;
int err;
u32 id = child_plat->domain_id;
cid_reg_value = readl(plat->base + RIFSC_RISC_PER0_CIDCFGR(id));
err = rifsc_parse_access_controller(device_node, &args);
if (err)
return err;
/*
* If the peripheral is in semaphore mode, release the semaphore so that
* there's no ownership.
*/
return stm32_rifsc_grant_access_by_id(device_node, args.args[0]);
}
void stm32_rifsc_release_access_by_id(ofnode device_node, u32 id)
{
struct ofnode_phandle_args args;
u32 cid_reg_value;
void *rifsc_base;
int err;
err = rifsc_parse_access_controller(device_node, &args);
if (err)
panic("Failed to parse access-controllers property\n");
rifsc_base = (void *)ofnode_get_addr(args.node);
cid_reg_value = readl(rifsc_base + RIFSC_RISC_PER0_CIDCFGR(id));
/* If the peripheral is in semaphore mode, release it if we have the ownership */
if (cid_reg_value & CIDCFGR_SEMEN &&
(FIELD_GET(RIFSC_RISC_SEMWL_MASK, cid_reg_value) & BIT(RIF_CID1))) {
err = stm32_rif_release_semaphore(plat->base, id);
if (err)
dev_err(dev, "Couldn't release rif semaphore for peripheral %d (%d)\n",
id, err);
err = stm32_rifsc_release_semaphore(rifsc_base, id);
if (err) {
panic("Couldn't release RIF semaphore for peripheral %d (%d)\n", id, err);
}
pr_debug("Releasing RIF semaphore for peripheral %d\n", id);
}
}
void stm32_rifsc_release_access(ofnode device_node)
{
struct ofnode_phandle_args args;
int err;
err = rifsc_parse_access_controller(device_node, &args);
if (err)
panic("Failed to parse access-controllers property\n");
stm32_rifsc_release_access_by_id(device_node, args.args[0]);
}
static int stm32_rifsc_child_pre_probe(struct udevice *dev)
{
struct stm32_rifsc_child_plat *child_plat = dev_get_parent_plat(dev);
return stm32_rifsc_grant_access_by_id(dev_ofnode(dev), child_plat->domain_id);
}
static int stm32_rifsc_child_post_remove(struct udevice *dev)
{
struct stm32_rifsc_child_plat *child_plat = dev_get_parent_plat(dev);
stm32_rifsc_release_access_by_id(dev_ofnode(dev), child_plat->domain_id);
return 0;
}

View File

@@ -33,6 +33,7 @@ CONFIG_CMD_GPIO=y
CONFIG_CMD_PWM=y
# CONFIG_CMD_LOADB is not set
CONFIG_CMD_MMC=y
CONFIG_CMD_MTD=y
CONFIG_CMD_CACHE=y
CONFIG_CMD_TIME=y
CONFIG_CMD_RNG=y
@@ -61,11 +62,22 @@ CONFIG_DM_I2C=y
CONFIG_SYS_I2C_STM32F7=y
CONFIG_LED=y
CONFIG_LED_GPIO=y
CONFIG_STM32_OMM=y
CONFIG_SUPPORT_EMMC_BOOT=y
CONFIG_STM32_SDMMC2=y
CONFIG_MTD=y
CONFIG_DM_MTD=y
CONFIG_USE_SYS_MAX_FLASH_BANKS=y
CONFIG_SPI_FLASH=y
CONFIG_MTD_SPI_NAND=y
CONFIG_DM_SPI_FLASH=y
CONFIG_SPI_FLASH_SFDP_SUPPORT=y
CONFIG_SPI_FLASH_SOFT_RESET=y
CONFIG_SPI_FLASH_MACRONIX=y
CONFIG_SPI_FLASH_SPANSION=y
CONFIG_SPI_FLASH_STMICRO=y
CONFIG_SPI_FLASH_WINBOND=y
# CONFIG_SPI_FLASH_USE_4K_SECTORS is not set
CONFIG_SPI_FLASH_MTD=y
CONFIG_PINCONF=y
CONFIG_DM_REGULATOR_FIXED=y
CONFIG_DM_REGULATOR_GPIO=y
@@ -77,6 +89,7 @@ CONFIG_DM_RNG=y
CONFIG_SERIAL_RX_BUFFER=y
CONFIG_SPI=y
CONFIG_DM_SPI=y
CONFIG_STM32_OSPI=y
# CONFIG_OPTEE_TA_AVB is not set
CONFIG_WDT=y
CONFIG_WDT_STM32MP=y

View File

@@ -430,7 +430,7 @@ static int stm32mp25_check_security(struct udevice *dev, void __iomem *base,
u32 index = (u32)cfg->sec_id;
if (index & SEC_RIFSC_FLAG)
ret = stm32_rifsc_check_access_by_id(dev_ofnode(dev),
ret = stm32_rifsc_grant_access_by_id(dev_ofnode(dev),
index & ~SEC_RIFSC_FLAG);
else
ret = stm32_rcc_get_access(dev, index);

View File

@@ -37,6 +37,23 @@ config STM32_FMC2_EBI
devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
SOCs containing the FMC2 External Bus Interface.
config STM32_OMM
bool "STM32 Octo Memory Manager"
depends on ARCH_STM32MP
help
This driver manages the muxing between the 2 OSPI busses and
the 2 output ports. There are 4 possible muxing configurations:
- direct mode (no multiplexing): OSPI1 output is on port 1 and OSPI2
output is on port 2
- OSPI1 and OSPI2 are multiplexed over the same output port 1
- swapped mode (no multiplexing), OSPI1 output is on port 2,
OSPI2 output is on port 1
- OSPI1 and OSPI2 are multiplexed over the same output port 2
It also manages :
- the split of the memory area shared between the 2 OSPI instances.
- chip select selection override.
- the time between 2 transactions in multiplexed mode.
config TI_AEMIF
tristate "Texas Instruments AEMIF driver"
depends on ARCH_KEYSTONE || ARCH_DAVINCI

View File

@@ -2,6 +2,7 @@
obj-$(CONFIG_MEMORY) += memory-uclass.o
obj-$(CONFIG_SANDBOX_MEMORY) += memory-sandbox.o
obj-$(CONFIG_STM32_FMC2_EBI) += stm32-fmc2-ebi.o
obj-$(CONFIG_STM32_OMM) += stm32_omm.o
obj-$(CONFIG_ATMEL_EBI) += atmel_ebi.o
obj-$(CONFIG_TI_AEMIF) += ti-aemif.o ti-aemif-cs.o
obj-$(CONFIG_TI_GPMC) += ti-gpmc.o

421
drivers/memory/stm32_omm.c Normal file
View File

@@ -0,0 +1,421 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
/*
* Copyright (C) 2025, STMicroelectronics - All Rights Reserved
*/
#define LOG_CATEGORY UCLASS_NOP
#include <clk.h>
#include <dm.h>
#include <regmap.h>
#include <reset.h>
#include <syscon.h>
#include <asm/io.h>
#include <dm/device_compat.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <dm/of_addr.h>
#include <dm/of_access.h>
#include <linux/bitfield.h>
#include <linux/ioport.h>
#include <mach/rif.h>
/* OCTOSPI control register */
#define OCTOSPIM_CR 0
#define CR_MUXEN BIT(0)
#define CR_MUXENMODE_MASK GENMASK(1, 0)
#define CR_CSSEL_OVR_EN BIT(4)
#define CR_CSSEL_OVR_MASK GENMASK(6, 5)
#define CR_REQ2ACK_MASK GENMASK(23, 16)
#define OMM_CHILD_NB 2
#define OMM_CLK_NB 3
#define OMM_RESET_NB 3
#define NSEC_PER_SEC 1000000000L
struct stm32_omm_plat {
phys_addr_t regs_base;
struct regmap *syscfg_regmap;
struct clk clk[OMM_CLK_NB];
struct reset_ctl reset_ctl[OMM_RESET_NB];
resource_size_t mm_ospi2_size;
u32 mux;
u32 cssel_ovr;
u32 req2ack;
u32 amcr_base;
u32 amcr_mask;
unsigned long clk_rate_max;
u8 nb_child;
};
static int stm32_omm_set_amcr(struct udevice *dev, bool set)
{
struct stm32_omm_plat *plat = dev_get_plat(dev);
unsigned int amcr, read_amcr;
amcr = plat->mm_ospi2_size / SZ_64M;
if (set)
regmap_update_bits(plat->syscfg_regmap, plat->amcr_base,
plat->amcr_mask, amcr);
/* read AMCR and check coherency with memory-map areas defined in DT */
regmap_read(plat->syscfg_regmap, plat->amcr_base, &read_amcr);
read_amcr = read_amcr >> (ffs(plat->amcr_mask) - 1);
if (amcr != read_amcr) {
dev_err(dev, "AMCR value not coherent with DT memory-map areas\n");
return -EINVAL;
}
return 0;
}
static int stm32_omm_toggle_child_clock(struct udevice *dev, bool enable)
{
struct stm32_omm_plat *plat = dev_get_plat(dev);
int i, ret;
for (i = 0; i < plat->nb_child; i++) {
if (enable) {
ret = clk_enable(&plat->clk[i + 1]);
if (ret) {
dev_err(dev, "Can not enable clock\n");
goto clk_error;
}
} else {
clk_disable(&plat->clk[i + 1]);
}
}
return 0;
clk_error:
while (i--)
clk_disable(&plat->clk[i + 1]);
return ret;
}
static int stm32_omm_disable_child(struct udevice *dev)
{
struct stm32_omm_plat *plat = dev_get_plat(dev);
int ret;
u8 i;
ret = stm32_omm_toggle_child_clock(dev, true);
if (ret)
return ret;
for (i = 0; i < plat->nb_child; i++) {
/* reset OSPI to ensure CR_EN bit is set to 0 */
reset_assert(&plat->reset_ctl[i + 1]);
udelay(2);
reset_deassert(&plat->reset_ctl[i + 1]);
}
return stm32_omm_toggle_child_clock(dev, false);
}
static int stm32_omm_configure(struct udevice *dev)
{
struct stm32_omm_plat *plat = dev_get_plat(dev);
int ret;
u32 mux = 0;
u32 cssel_ovr = 0;
u32 req2ack = 0;
/* Ensure both OSPI instance are disabled before configuring OMM */
ret = stm32_omm_disable_child(dev);
if (ret)
return ret;
ret = clk_enable(&plat->clk[0]);
if (ret) {
dev_err(dev, "Failed to enable OMM clock (%d)\n", ret);
return ret;
}
reset_assert(&plat->reset_ctl[0]);
udelay(2);
reset_deassert(&plat->reset_ctl[0]);
if (plat->mux & CR_MUXEN) {
if (plat->req2ack) {
req2ack = DIV_ROUND_UP(plat->req2ack,
NSEC_PER_SEC / plat->clk_rate_max) - 1;
if (req2ack > 256)
req2ack = 256;
}
req2ack = FIELD_PREP(CR_REQ2ACK_MASK, req2ack);
clrsetbits_le32(plat->regs_base + OCTOSPIM_CR, CR_REQ2ACK_MASK,
req2ack);
/*
* If the mux is enabled, the 2 OSPI clocks have to be
* always enabled
*/
ret = stm32_omm_toggle_child_clock(dev, true);
if (ret)
return ret;
}
if (plat->cssel_ovr != 0xff) {
cssel_ovr = FIELD_PREP(CR_CSSEL_OVR_MASK, cssel_ovr);
cssel_ovr |= CR_CSSEL_OVR_EN;
clrsetbits_le32(plat->regs_base + OCTOSPIM_CR, CR_CSSEL_OVR_MASK,
cssel_ovr);
}
mux = FIELD_PREP(CR_MUXENMODE_MASK, plat->mux);
clrsetbits_le32(plat->regs_base + OCTOSPIM_CR, CR_MUXENMODE_MASK, mux);
clk_disable(&plat->clk[0]);
return stm32_omm_set_amcr(dev, true);
}
static void stm32_omm_release_childs(ofnode *child_list, u8 nb_child)
{
u8 i;
for (i = 0; i < nb_child; i++)
stm32_rifsc_release_access(child_list[i]);
}
static int stm32_omm_probe(struct udevice *dev)
{
struct stm32_omm_plat *plat = dev_get_plat(dev);
ofnode child_list[OMM_CHILD_NB];
ofnode child;
int ret;
u8 child_access_granted = 0;
bool child_access[OMM_CHILD_NB];
/* check child's access */
for (child = ofnode_first_subnode(dev_ofnode(dev));
ofnode_valid(child);
child = ofnode_next_subnode(child)) {
if (plat->nb_child > OMM_CHILD_NB) {
dev_err(dev, "Bad DT, found too much children\n");
return -E2BIG;
}
if (!ofnode_device_is_compatible(child, "st,stm32mp25-ospi"))
return -EINVAL;
ret = stm32_rifsc_grant_access(child);
if (ret < 0 && ret != -EACCES)
return ret;
child_access[plat->nb_child] = false;
if (!ret) {
child_access_granted++;
child_access[plat->nb_child] = true;
}
child_list[plat->nb_child] = child;
plat->nb_child++;
}
if (plat->nb_child != OMM_CHILD_NB)
return -EINVAL;
/* check if OMM's resource access is granted */
ret = stm32_rifsc_grant_access(dev_ofnode(dev));
if (ret < 0 && ret != -EACCES)
goto end;
/* All child's access are granted ? */
if (!ret && child_access_granted == plat->nb_child) {
ret = stm32_omm_configure(dev);
if (ret)
goto end;
} else {
dev_dbg(dev, "Octo Memory Manager resource's access not granted\n");
/*
* AMCR can't be set, so check if current value is coherent
* with memory-map areas defined in DT
*/
ret = stm32_omm_set_amcr(dev, false);
}
end:
stm32_omm_release_childs(child_list, plat->nb_child);
stm32_rifsc_release_access(dev_ofnode(dev));
return ret;
}
static int stm32_omm_of_to_plat(struct udevice *dev)
{
struct stm32_omm_plat *plat = dev_get_plat(dev);
static const char * const clocks_name[] = {"omm", "ospi1", "ospi2"};
static const char * const mm_name[] = { "ospi1", "ospi2" };
static const char * const resets_name[] = {"omm", "ospi1", "ospi2"};
struct resource res, res1, mm_res;
struct ofnode_phandle_args args;
struct udevice *child;
unsigned long clk_rate;
struct clk child_clk;
int ret, idx;
u8 i;
plat->regs_base = dev_read_addr(dev);
if (plat->regs_base == FDT_ADDR_T_NONE)
return -EINVAL;
ret = dev_read_resource_byname(dev, "memory_map", &mm_res);
if (ret) {
dev_err(dev, "can't get omm_mm mmap resource(ret = %d)!\n", ret);
return ret;
}
for (i = 0; i < OMM_CLK_NB; i++) {
ret = clk_get_by_name(dev, clocks_name[i], &plat->clk[i]);
if (ret < 0) {
dev_err(dev, "Can't find I/O manager clock %s\n", clocks_name[i]);
return ret;
}
ret = reset_get_by_name(dev, resets_name[i], &plat->reset_ctl[i]);
if (ret < 0) {
dev_err(dev, "Can't find I/O manager reset %s\n", resets_name[i]);
return ret;
}
}
/* parse children's clock */
plat->clk_rate_max = 0;
device_foreach_child(child, dev) {
ret = clk_get_by_index(child, 0, &child_clk);
if (ret) {
dev_err(dev, "Failed to get clock for %s\n",
dev_read_name(child));
return ret;
}
clk_rate = clk_get_rate(&child_clk);
if (!clk_rate) {
dev_err(dev, "Invalid clock rate\n");
return -EINVAL;
}
if (clk_rate > plat->clk_rate_max)
plat->clk_rate_max = clk_rate;
}
plat->mux = dev_read_u32_default(dev, "st,omm-mux", 0);
plat->req2ack = dev_read_u32_default(dev, "st,omm-req2ack-ns", 0);
plat->cssel_ovr = dev_read_u32_default(dev, "st,omm-cssel-ovr", 0xff);
plat->mm_ospi2_size = 0;
for (i = 0; i < 2; i++) {
idx = dev_read_stringlist_search(dev, "memory-region-names",
mm_name[i]);
if (idx < 0)
continue;
/* res1 only used on second loop iteration */
res1.start = res.start;
res1.end = res.end;
dev_read_phandle_with_args(dev, "memory-region", NULL, 0, idx,
&args);
ret = ofnode_read_resource(args.node, 0, &res);
if (ret) {
dev_err(dev, "unable to resolve memory region\n");
return ret;
}
/* check that memory region fits inside OMM memory map area */
if (!resource_contains(&mm_res, &res)) {
dev_err(dev, "%s doesn't fit inside OMM memory map area\n",
mm_name[i]);
dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
res.start, res.end,
mm_res.start, mm_res.end);
return -EFAULT;
}
if (i == 1) {
plat->mm_ospi2_size = resource_size(&res);
/* check that OMM memory region 1 doesn't overlap memory region 2 */
if (resource_overlaps(&res, &res1)) {
dev_err(dev, "OMM memory-region %s overlaps memory region %s\n",
mm_name[0], mm_name[1]);
dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
res1.start, res1.end, res.start, res.end);
return -EFAULT;
}
}
}
plat->syscfg_regmap = syscon_regmap_lookup_by_phandle(dev, "st,syscfg-amcr");
if (IS_ERR(plat->syscfg_regmap)) {
dev_err(dev, "Failed to get st,syscfg-amcr property\n");
ret = PTR_ERR(plat->syscfg_regmap);
return ret;
}
ret = dev_read_u32_index(dev, "st,syscfg-amcr", 1, &plat->amcr_base);
if (ret) {
dev_err(dev, "Failed to get st,syscfg-amcr base\n");
return ret;
}
ret = dev_read_u32_index(dev, "st,syscfg-amcr", 2, &plat->amcr_mask);
if (ret) {
dev_err(dev, "Failed to get st,syscfg-amcr mask\n");
return ret;
}
return 0;
};
static int stm32_omm_bind(struct udevice *dev)
{
int ret = 0, err = 0;
ofnode node;
for (node = ofnode_first_subnode(dev_ofnode(dev));
ofnode_valid(node);
node = ofnode_next_subnode(node)) {
const char *node_name = ofnode_get_name(node);
if (!ofnode_is_enabled(node) || stm32_rifsc_grant_access(node)) {
dev_dbg(dev, "%s failed to bind\n", node_name);
continue;
}
err = lists_bind_fdt(dev, node, NULL, NULL,
gd->flags & GD_FLG_RELOC ? false : true);
if (err && !ret) {
ret = err;
dev_dbg(dev, "%s: ret=%d\n", node_name, ret);
}
}
if (ret)
dev_dbg(dev, "Some drivers failed to bind\n");
return ret;
}
static const struct udevice_id stm32_omm_ids[] = {
{ .compatible = "st,stm32mp25-omm", },
{},
};
U_BOOT_DRIVER(stm32_omm) = {
.name = "stm32_omm",
.id = UCLASS_NOP,
.probe = stm32_omm_probe,
.of_match = stm32_omm_ids,
.of_to_plat = stm32_omm_of_to_plat,
.plat_auto = sizeof(struct stm32_omm_plat),
.bind = stm32_omm_bind,
};

View File

@@ -530,6 +530,14 @@ config SPI_SUNXI
Same controller driver can reuse in all Allwinner SoC variants.
config STM32_OSPI
bool "STM32MP2 OSPI driver"
depends on STM32MP25X && STM32_OMM
help
Enable the STM32MP2 Octo-SPI (OSPI) driver. This driver can be
used to access the SPI NOR flash chips on platforms embedding
this ST IP core.
config STM32_QSPI
bool "STM32F7 QSPI driver"
depends on STM32F4 || STM32F7 || ARCH_STM32MP

View File

@@ -75,6 +75,7 @@ obj-$(CONFIG_SPI_SIFIVE) += spi-sifive.o
obj-$(CONFIG_SPI_SN_F_OSPI) += spi-sn-f-ospi.o
obj-$(CONFIG_SPI_SUNXI) += spi-sunxi.o
obj-$(CONFIG_SH_QSPI) += sh_qspi.o
obj-$(CONFIG_STM32_OSPI) += stm32_ospi.o
obj-$(CONFIG_STM32_QSPI) += stm32_qspi.o
obj-$(CONFIG_STM32_SPI) += stm32_spi.o
obj-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o

623
drivers/spi/stm32_ospi.c Normal file
View File

@@ -0,0 +1,623 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
/*
* Copyright (C) 2025, STMicroelectronics - All Rights Reserved
*/
#define LOG_CATEGORY UCLASS_SPI
#include <clk.h>
#include <dm.h>
#include <log.h>
#include <reset.h>
#include <spi.h>
#include <spi-mem.h>
#include <syscon.h>
#include <asm/io.h>
#include <dm/device_compat.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/ioport.h>
#include <linux/sizes.h>
/* OCTOSPI control register */
#define OSPI_CR 0x00
#define OSPI_CR_EN BIT(0)
#define OSPI_CR_ABORT BIT(1)
#define OSPI_CR_TCEN BIT(3)
#define OSPI_CR_FSEL BIT(7)
#define OSPI_CR_FTHRES_MASK GENMASK(13, 8)
#define OSPI_CR_FTHRES_SHIFT 8
#define OSPI_CR_CSSEL BIT(24)
#define OSPI_CR_FMODE_SHIFT 28
#define OSPI_CR_FMODE_MASK GENMASK(29, 28)
/* OCTOSPI device configuration register */
#define OSPI_DCR1 0x08
#define OSPI_DCR1_CKMODE BIT(0)
#define OSPI_DCR1_DLYBYP BIT(3)
#define OSPI_DCR1_CSHT_SHIFT 8
#define OSPI_DCR1_CSHT_MASK GENMASK(13, 8)
#define OSPI_DCR1_DEVSIZE_MASK GENMASK(20, 16)
#define OSPI_DCR1_MTYP_MASK GENMASK(26, 24)
/* OCTOSPI device configuration register 2 */
#define OSPI_DCR2 0x0c
#define OSPI_DCR2_PRESC_SHIFT 0
#define OSPI_DCR2_PRESC_MASK GENMASK(7, 0)
/* OCTOSPI status register */
#define OSPI_SR 0x20
#define OSPI_SR_TEF BIT(0)
#define OSPI_SR_TCF BIT(1)
#define OSPI_SR_FTF BIT(2)
#define OSPI_SR_BUSY BIT(5)
/* OCTOSPI flag clear register */
#define OSPI_FCR 0x24
#define OSPI_FCR_CTEF BIT(0)
#define OSPI_FCR_CTCF BIT(1)
/* OCTOSPI data length register */
#define OSPI_DLR 0x40
/* OCTOSPI address register */
#define OSPI_AR 0x48
/* OCTOSPI data configuration register */
#define OSPI_DR 0x50
/* OCTOSPI communication configuration register */
#define OSPI_CCR 0x100
#define OSPI_CCR_IMODE_SHIFT 0
#define OSPI_CCR_IMODE_MASK GENMASK(2, 0)
#define OSPI_CCR_ADMODE_SHIFT 8
#define OSPI_CCR_ADMODE_MASK GENMASK(10, 8)
#define OSPI_CCR_ADSIZE_SHIFT 12
#define OSPI_CCR_DMODE_SHIFT 24
#define OSPI_CCR_DMODE_MASK GENMASK(26, 24)
#define OSPI_CCR_IND_WRITE 0
#define OSPI_CCR_IND_READ 1
#define OSPI_CCR_MEM_MAP 3
/* OCTOSPI timing configuration register */
#define OSPI_TCR 0x108
#define OSPI_TCR_DCYC_SHIFT 0x0
#define OSPI_TCR_DCYC_MASK GENMASK(4, 0)
#define OSPI_TCR_SSHIFT BIT(30)
/* OCTOSPI instruction register */
#define OSPI_IR 0x110
#define OSPI_MAX_MMAP_SZ SZ_256M
#define OSPI_MAX_CHIP 2
#define OSPI_FIFO_TIMEOUT_US 30000
#define OSPI_ABT_TIMEOUT_US 100000
#define OSPI_BUSY_TIMEOUT_US 100000
#define OSPI_CMD_TIMEOUT_US 1000000
struct stm32_ospi_flash {
u32 cr;
u32 dcr;
u32 dcr2;
bool initialized;
};
struct stm32_ospi_priv {
struct stm32_ospi_flash flash[OSPI_MAX_CHIP];
int cs_used;
};
struct stm32_ospi_plat {
phys_addr_t regs_base; /* register base address */
phys_addr_t mm_base; /* memory map base address */
resource_size_t mm_size;
struct clk clk;
struct reset_ctl_bulk rst_ctl;
ulong clock_rate;
};
static int stm32_ospi_mm(struct udevice *dev,
const struct spi_mem_op *op)
{
struct stm32_ospi_plat *ospi_plat = dev_get_plat(dev);
memcpy_fromio(op->data.buf.in,
(void __iomem *)ospi_plat->mm_base + op->addr.val,
op->data.nbytes);
return 0;
}
static void stm32_ospi_read_fifo(void *val, phys_addr_t addr, u8 len)
{
switch (len) {
case sizeof(u32):
*((u32 *)val) = readl_relaxed(addr);
break;
case sizeof(u16):
*((u16 *)val) = readw_relaxed(addr);
break;
case sizeof(u8):
*((u8 *)val) = readb_relaxed(addr);
};
schedule();
}
static void stm32_ospi_write_fifo(void *val, phys_addr_t addr, u8 len)
{
switch (len) {
case sizeof(u32):
writel_relaxed(*((u32 *)val), addr);
break;
case sizeof(u16):
writew_relaxed(*((u16 *)val), addr);
break;
case sizeof(u8):
writeb_relaxed(*((u8 *)val), addr);
};
}
int stm32_ospi_tx_poll(struct udevice *dev, void *buf, u32 len, bool read)
{
struct stm32_ospi_plat *ospi_plat = dev_get_plat(dev);
phys_addr_t regs_base = ospi_plat->regs_base;
void (*fifo)(void *val, phys_addr_t addr, u8 len);
u32 sr;
int ret;
u8 step = 1;
if (read)
fifo = stm32_ospi_read_fifo;
else
fifo = stm32_ospi_write_fifo;
while (len) {
ret = readl_poll_timeout(regs_base + OSPI_SR, sr,
sr & OSPI_SR_FTF,
OSPI_FIFO_TIMEOUT_US);
if (ret) {
dev_err(dev, "fifo timeout (len:%d stat:%#x)\n",
len, sr);
return ret;
}
if (!IS_ALIGNED((uintptr_t)buf, sizeof(u32))) {
if (!IS_ALIGNED((uintptr_t)buf, sizeof(u16)))
step = sizeof(u8);
else
step = min((u32)len, (u32)sizeof(u16));
}
/* Buf is aligned */
else if (len >= sizeof(u32))
step = sizeof(u32);
else if (len >= sizeof(u16))
step = sizeof(u16);
else if (len)
step = sizeof(u8);
fifo(buf, regs_base + OSPI_DR, step);
len -= step;
buf += step;
}
return 0;
}
static int stm32_ospi_tx(struct udevice *dev,
const struct spi_mem_op *op,
u8 mode)
{
void *buf;
if (!op->data.nbytes)
return 0;
if (mode == OSPI_CCR_MEM_MAP)
return stm32_ospi_mm(dev, op);
if (op->data.dir == SPI_MEM_DATA_IN)
buf = op->data.buf.in;
else
buf = (void *)op->data.buf.out;
return stm32_ospi_tx_poll(dev, buf, op->data.nbytes,
op->data.dir == SPI_MEM_DATA_IN);
}
static int stm32_ospi_get_mode(u8 buswidth)
{
if (buswidth == 8)
return 4;
if (buswidth == 4)
return 3;
return buswidth;
}
int stm32_ospi_wait_for_not_busy(struct udevice *dev)
{
struct stm32_ospi_plat *ospi_plat = dev_get_plat(dev);
phys_addr_t regs_base = ospi_plat->regs_base;
u32 sr;
int ret;
ret = readl_poll_timeout(regs_base + OSPI_SR, sr, !(sr & OSPI_SR_BUSY),
OSPI_BUSY_TIMEOUT_US);
if (ret)
dev_err(dev, "busy timeout (stat:%#x)\n", sr);
return ret;
}
int stm32_ospi_wait_cmd(struct udevice *dev)
{
struct stm32_ospi_plat *ospi_plat = dev_get_plat(dev);
phys_addr_t regs_base = ospi_plat->regs_base;
u32 sr;
int ret = 0;
ret = readl_poll_timeout(regs_base + OSPI_SR, sr,
sr & OSPI_SR_TCF,
OSPI_CMD_TIMEOUT_US);
if (ret) {
dev_err(dev, "cmd timeout (stat:%#x)\n", sr);
} else if (readl(regs_base + OSPI_SR) & OSPI_SR_TEF) {
dev_err(dev, "transfer error (stat:%#x)\n", sr);
ret = -EIO;
}
/* clear flags */
writel(OSPI_FCR_CTCF | OSPI_FCR_CTEF, regs_base + OSPI_FCR);
if (!ret)
ret = stm32_ospi_wait_for_not_busy(dev);
return ret;
}
static int stm32_ospi_exec_op(struct spi_slave *slave,
const struct spi_mem_op *op)
{
struct stm32_ospi_plat *ospi_plat = dev_get_plat(slave->dev->parent);
phys_addr_t regs_base = ospi_plat->regs_base;
u32 cr, ccr = 0, addr_max;
int timeout, ret;
int dmode;
u8 mode = OSPI_CCR_IND_WRITE;
u8 dcyc = 0;
dev_dbg(slave->dev, "%s: cmd:%#x mode:%d.%d.%d.%d addr:%#llx len:%#x\n",
__func__, op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth,
op->dummy.buswidth, op->data.buswidth,
op->addr.val, op->data.nbytes);
addr_max = op->addr.val + op->data.nbytes + 1;
if (op->data.dir == SPI_MEM_DATA_IN && op->data.nbytes) {
if (addr_max < ospi_plat->mm_size && op->addr.buswidth)
mode = OSPI_CCR_MEM_MAP;
else
mode = OSPI_CCR_IND_READ;
}
if (op->data.nbytes)
writel(op->data.nbytes - 1, regs_base + OSPI_DLR);
clrsetbits_le32(regs_base + OSPI_CR, OSPI_CR_FMODE_MASK,
mode << OSPI_CR_FMODE_SHIFT);
ccr |= (stm32_ospi_get_mode(op->cmd.buswidth) << OSPI_CCR_IMODE_SHIFT) &
OSPI_CCR_IMODE_MASK;
if (op->addr.nbytes) {
ccr |= ((op->addr.nbytes - 1) << OSPI_CCR_ADSIZE_SHIFT);
ccr |= (stm32_ospi_get_mode(op->addr.buswidth)
<< OSPI_CCR_ADMODE_SHIFT) & OSPI_CCR_ADMODE_MASK;
}
if (op->dummy.buswidth && op->dummy.nbytes)
dcyc = op->dummy.nbytes * 8 / op->dummy.buswidth;
clrsetbits_le32(regs_base + OSPI_TCR, OSPI_TCR_DCYC_MASK,
dcyc << OSPI_TCR_DCYC_SHIFT);
if (op->data.nbytes) {
dmode = stm32_ospi_get_mode(op->data.buswidth);
ccr |= (dmode << OSPI_CCR_DMODE_SHIFT) & OSPI_CCR_DMODE_MASK;
}
writel(ccr, regs_base + OSPI_CCR);
/* set instruction, must be set after ccr register update */
writel(op->cmd.opcode, regs_base + OSPI_IR);
if (op->addr.nbytes && mode != OSPI_CCR_MEM_MAP)
writel(op->addr.val, regs_base + OSPI_AR);
ret = stm32_ospi_tx(slave->dev->parent, op, mode);
/*
* Abort in:
* -error case
* -read memory map: prefetching must be stopped if we read the last
* byte of device (device size - fifo size). like device size is not
* knows, the prefetching is always stop.
*/
if (ret || mode == OSPI_CCR_MEM_MAP)
goto abort;
/* Wait end of tx in indirect mode */
ret = stm32_ospi_wait_cmd(slave->dev->parent);
if (ret)
goto abort;
return 0;
abort:
setbits_le32(regs_base + OSPI_CR, OSPI_CR_ABORT);
/* Wait clear of abort bit by hw */
timeout = readl_poll_timeout(regs_base + OSPI_CR, cr,
!(cr & OSPI_CR_ABORT),
OSPI_ABT_TIMEOUT_US);
writel(OSPI_FCR_CTCF, regs_base + OSPI_FCR);
if (ret || timeout)
dev_err(slave->dev, "%s ret:%d abort timeout:%d\n", __func__,
ret, timeout);
return ret;
}
static int stm32_ospi_probe(struct udevice *bus)
{
struct stm32_ospi_priv *priv = dev_get_priv(bus);
struct stm32_ospi_plat *ospi_plat;
phys_addr_t regs_base;
int ret;
ospi_plat = dev_get_plat(bus);
regs_base = ospi_plat->regs_base;
ret = clk_enable(&ospi_plat->clk);
if (ret) {
dev_err(bus, "failed to enable clock\n");
return ret;
}
/* Reset OSPI controller */
reset_assert_bulk(&ospi_plat->rst_ctl);
udelay(2);
reset_deassert_bulk(&ospi_plat->rst_ctl);
priv->cs_used = -1;
setbits_le32(regs_base + OSPI_TCR, OSPI_TCR_SSHIFT);
clrsetbits_le32(regs_base + OSPI_CR, OSPI_CR_FTHRES_MASK,
3 << OSPI_CR_FTHRES_SHIFT);
/* Set dcr devsize to max address */
setbits_le32(regs_base + OSPI_DCR1,
OSPI_DCR1_DEVSIZE_MASK | OSPI_DCR1_DLYBYP);
return 0;
}
static int stm32_ospi_claim_bus(struct udevice *dev)
{
struct stm32_ospi_priv *priv = dev_get_priv(dev->parent);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
struct stm32_ospi_plat *ospi_plat = dev_get_plat(dev->parent);
phys_addr_t regs_base = ospi_plat->regs_base;
unsigned int slave_cs = slave_plat->cs[0];
if (slave_cs >= OSPI_MAX_CHIP)
return -ENODEV;
if (priv->cs_used != slave_cs) {
struct stm32_ospi_flash *flash = &priv->flash[slave_cs];
priv->cs_used = slave_cs;
if (flash->initialized) {
/* Set the configuration: speed + cs */
writel(flash->cr, regs_base + OSPI_CR);
writel(flash->dcr, regs_base + OSPI_DCR1);
writel(flash->dcr2, regs_base + OSPI_DCR2);
} else {
/* Set chip select */
clrsetbits_le32(regs_base + OSPI_CR,
OSPI_CR_CSSEL,
priv->cs_used ? OSPI_CR_CSSEL : 0);
/* Save the configuration: speed + cs */
flash->cr = readl(regs_base + OSPI_CR);
flash->dcr = readl(regs_base + OSPI_DCR1);
flash->dcr2 = readl(regs_base + OSPI_DCR2);
flash->initialized = true;
}
}
setbits_le32(regs_base + OSPI_CR, OSPI_CR_EN);
return 0;
}
static int stm32_ospi_release_bus(struct udevice *dev)
{
struct stm32_ospi_plat *ospi_plat = dev_get_plat(dev->parent);
phys_addr_t regs_base = ospi_plat->regs_base;
clrbits_le32(regs_base + OSPI_CR, OSPI_CR_EN);
return 0;
}
static int stm32_ospi_set_speed(struct udevice *bus, uint speed)
{
struct stm32_ospi_plat *ospi_plat = dev_get_plat(bus);
phys_addr_t regs_base = ospi_plat->regs_base;
u32 ospi_clk = ospi_plat->clock_rate;
u32 prescaler = 255;
u32 csht;
int ret;
if (speed > 0) {
prescaler = 0;
if (ospi_clk) {
prescaler = DIV_ROUND_UP(ospi_clk, speed) - 1;
if (prescaler > 255)
prescaler = 255;
}
}
csht = (DIV_ROUND_UP((5 * ospi_clk) / (prescaler + 1), 100000000)) - 1;
ret = stm32_ospi_wait_for_not_busy(bus);
if (ret)
return ret;
clrsetbits_le32(regs_base + OSPI_DCR2, OSPI_DCR2_PRESC_MASK,
prescaler << OSPI_DCR2_PRESC_SHIFT);
clrsetbits_le32(regs_base + OSPI_DCR1, OSPI_DCR1_CSHT_MASK,
csht << OSPI_DCR1_CSHT_SHIFT);
return 0;
}
static int stm32_ospi_set_mode(struct udevice *bus, uint mode)
{
struct stm32_ospi_plat *ospi_plat = dev_get_plat(bus);
phys_addr_t regs_base = ospi_plat->regs_base;
const char *str_rx, *str_tx;
int ret;
ret = stm32_ospi_wait_for_not_busy(bus);
if (ret)
return ret;
if ((mode & SPI_CPHA) && (mode & SPI_CPOL))
setbits_le32(regs_base + OSPI_DCR1, OSPI_DCR1_CKMODE);
else if (!(mode & SPI_CPHA) && !(mode & SPI_CPOL))
clrbits_le32(regs_base + OSPI_DCR1, OSPI_DCR1_CKMODE);
else
return -ENODEV;
if (mode & SPI_CS_HIGH)
return -ENODEV;
if (mode & SPI_RX_OCTAL)
str_rx = "octal";
else if (mode & SPI_RX_QUAD)
str_rx = "quad";
else if (mode & SPI_RX_DUAL)
str_rx = "dual";
else
str_rx = "single";
if (mode & SPI_TX_OCTAL)
str_tx = "octal";
else if (mode & SPI_TX_QUAD)
str_tx = "quad";
else if (mode & SPI_TX_DUAL)
str_tx = "dual";
else
str_tx = "single";
dev_dbg(bus, "mode=%d rx: %s, tx: %s\n", mode, str_rx, str_tx);
return 0;
}
static const struct spi_controller_mem_ops stm32_ospi_mem_ops = {
.exec_op = stm32_ospi_exec_op,
};
static const struct dm_spi_ops stm32_ospi_ops = {
.claim_bus = stm32_ospi_claim_bus,
.release_bus = stm32_ospi_release_bus,
.set_speed = stm32_ospi_set_speed,
.set_mode = stm32_ospi_set_mode,
.mem_ops = &stm32_ospi_mem_ops,
};
static int stm32_ospi_of_to_plat(struct udevice *dev)
{
struct stm32_ospi_plat *plat = dev_get_plat(dev);
struct resource res;
struct ofnode_phandle_args args;
const fdt32_t *reg;
int ret, len;
reg = dev_read_prop(dev, "reg", &len);
if (!reg) {
dev_err(dev, "Can't get regs base address\n");
return -ENOENT;
}
plat->regs_base = (phys_addr_t)dev_translate_address(dev, reg);
/* optional */
ret = dev_read_phandle_with_args(dev, "memory-region", NULL, 0, 0, &args);
if (!ret) {
ret = ofnode_read_resource(args.node, 0, &res);
if (ret) {
dev_err(dev, "Can't get mmap base address(%d)\n", ret);
return ret;
}
plat->mm_base = res.start;
plat->mm_size = resource_size(&res);
if (plat->mm_size > OSPI_MAX_MMAP_SZ) {
dev_err(dev, "Incorrect memory-map size: %lld Bytes\n", plat->mm_size);
return -EINVAL;
}
dev_dbg(dev, "%s: regs_base=<0x%llx> mm_base=<0x%llx> mm_size=<0x%x>\n",
__func__, plat->regs_base, plat->mm_base, (u32)plat->mm_size);
} else {
plat->mm_base = 0;
plat->mm_size = 0;
dev_info(dev, "memory-region property not found (%d)\n", ret);
}
ret = clk_get_by_index(dev, 0, &plat->clk);
if (ret < 0) {
dev_err(dev, "Failed to get clock\n");
return ret;
}
ret = reset_get_bulk(dev, &plat->rst_ctl);
if (ret && ret != -ENOENT) {
dev_err(dev, "Failed to get reset\n");
return ret;
}
plat->clock_rate = clk_get_rate(&plat->clk);
if (!plat->clock_rate)
return -EINVAL;
return ret;
};
static const struct udevice_id stm32_ospi_ids[] = {
{ .compatible = "st,stm32mp25-ospi" },
{ }
};
U_BOOT_DRIVER(stm32_ospi) = {
.name = "stm32_ospi",
.id = UCLASS_SPI,
.of_match = stm32_ospi_ids,
.of_to_plat = stm32_ospi_of_to_plat,
.ops = &stm32_ospi_ops,
.plat_auto = sizeof(struct stm32_ospi_plat),
.priv_auto = sizeof(struct stm32_ospi_priv),
.probe = stm32_ospi_probe,
};

View File

@@ -135,6 +135,22 @@ static inline unsigned long resource_type(const struct resource *res)
return res->flags & IORESOURCE_TYPE_BITS;
}
/* True iff r1 completely contains r2 */
static inline bool resource_contains(struct resource *r1, struct resource *r2)
{
if (resource_type(r1) != resource_type(r2))
return false;
if (r1->flags & IORESOURCE_UNSET || r2->flags & IORESOURCE_UNSET)
return false;
return r1->start <= r2->start && r1->end >= r2->end;
}
/* True if any part of r1 overlaps r2 */
static inline bool resource_overlaps(struct resource *r1, struct resource *r2)
{
return r1->start <= r2->end && r1->end >= r2->start;
}
/* Convenience shorthand with allocation */
#define request_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name), 0)
#define __request_mem_region(start,n,name, excl) __request_region(&iomem_resource, (start), (n), (name), excl)