summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2026-08-29 09:56:00 -0600
committerTom Rini <[email protected]>2026-08-29 09:56:00 -0600
commit856a00aeb61c1d4e7825257e54219599f8e34a6c (patch)
tree0b02f08085d5db33af937a34e861e86c7d60fa84
parent043b4280138d45c50340c0f899935b83be4375b7 (diff)
parented58bafd0b24e15b734c624bdaf00b876e5d32e6 (diff)
Merge tag 'u-boot-rockchip-2027.01-20260828' of https://git.u-boot-project.org/u-boot/custodians/u-boot-rockchip into next
- Fixed possible SPI hangs when only PICO is routed (TX-only), - Added support for ROC-RK3399-PC-PLUS (via roc-pc-rk3399_defconfig)
-rw-r--r--arch/arm/dts/rk3399-roc-pc-u-boot.dtsi26
-rw-r--r--board/firefly/roc-pc-rk3399/roc-pc-rk3399.c42
-rw-r--r--configs/roc-pc-rk3399_defconfig5
-rw-r--r--doc/board/rockchip/rockchip.rst2
-rw-r--r--drivers/mtd/spi/spi-nor-ids.c1
-rw-r--r--drivers/spi/rk_spi.c33
-rw-r--r--drivers/spi/spi-uclass.c16
-rw-r--r--include/spi.h2
-rw-r--r--test/dm/spi.c39
9 files changed, 137 insertions, 29 deletions
diff --git a/arch/arm/dts/rk3399-roc-pc-u-boot.dtsi b/arch/arm/dts/rk3399-roc-pc-u-boot.dtsi
index 883d399a06a..e7e4a2c8907 100644
--- a/arch/arm/dts/rk3399-roc-pc-u-boot.dtsi
+++ b/arch/arm/dts/rk3399-roc-pc-u-boot.dtsi
@@ -10,30 +10,14 @@
config {
sysreset-gpio = <&gpio1 RK_PA6 GPIO_ACTIVE_HIGH>;
};
+};
- vcc_hub_en: vcc_hub_en-regulator {
- compatible = "regulator-fixed";
- enable-active-high;
- gpio = <&gpio2 RK_PA4 GPIO_ACTIVE_HIGH>;
- pinctrl-names = "default";
- pinctrl-0 = <&hub_rst>;
- regulator-name = "vcc_hub_en";
- regulator-always-on;
- };
+&i2c7 {
+ bootph-pre-ram;
};
-/*
- * should be placed inside mp8859, but not until mp8859 has
- * its own dt-binding.
- */
-&dc_12v {
- compatible = "regulator-fixed";
- regulator-name = "dc_12v";
- regulator-always-on;
- regulator-boot-on;
- regulator-min-microvolt = <12000000>;
- regulator-max-microvolt = <12000000>;
- vin-supply = <&vcc_vbus_typec0>;
+&i2c7_xfer {
+ bootph-pre-ram;
};
&gpio4 {
diff --git a/board/firefly/roc-pc-rk3399/roc-pc-rk3399.c b/board/firefly/roc-pc-rk3399/roc-pc-rk3399.c
index 6937a27176f..5a71e94ae1e 100644
--- a/board/firefly/roc-pc-rk3399/roc-pc-rk3399.c
+++ b/board/firefly/roc-pc-rk3399/roc-pc-rk3399.c
@@ -5,14 +5,24 @@
#include <dm.h>
#include <env.h>
+#include <fdtdec.h>
+#include <i2c.h>
+#include <image.h>
#include <log.h>
#include <spl_gpio.h>
+#include <asm/global_data.h>
#include <asm/io.h>
#include <asm/arch-rockchip/cru.h>
#include <asm/arch-rockchip/gpio.h>
#include <asm/arch-rockchip/grf_rk3399.h>
+#define ROC_PC_MP8859_BUS "i2c@ff160000"
+#define ROC_PC_MP8859_ADDR 0x66
+#define ROC_PC_PLUS_FDTFILE "rockchip/rk3399-roc-pc-plus.dtb"
+
+DECLARE_GLOBAL_DATA_PTR;
+
#ifdef CONFIG_XPL_BUILD
#define PMUGRF_BASE 0xff320000
@@ -54,4 +64,36 @@ void led_setup(void)
spl_gpio_output(gpio0, GPIO(BANK_B, 5), 1);
}
+static bool is_roc_pc_plus(void)
+{
+ struct udevice *bus, *dev;
+
+ if (!(CONFIG_IS_ENABLED(I2C) && CONFIG_IS_ENABLED(DM_I2C)))
+ return false;
+
+ if (uclass_get_device_by_name(UCLASS_I2C, ROC_PC_MP8859_BUS, &bus))
+ return false;
+
+ return dm_i2c_probe(bus, ROC_PC_MP8859_ADDR, 0, &dev);
+}
+
+int board_fit_config_name_match(const char *name)
+{
+ if (is_roc_pc_plus())
+ return strcmp(name, ROC_PC_PLUS_FDTFILE);
+
+ return strcmp(name, CONFIG_DEFAULT_FDT_FILE);
+}
+
#endif
+
+int rk_board_late_init(void)
+{
+ if (!fdt_node_check_compatible(gd->fdt_blob, 0,
+ "firefly,roc-rk3399-pc-plus"))
+ env_set("fdtfile", ROC_PC_PLUS_FDTFILE);
+ else
+ env_set("fdtfile", CONFIG_DEFAULT_FDT_FILE);
+
+ return 0;
+}
diff --git a/configs/roc-pc-rk3399_defconfig b/configs/roc-pc-rk3399_defconfig
index 1df5cb03adb..4347bab598a 100644
--- a/configs/roc-pc-rk3399_defconfig
+++ b/configs/roc-pc-rk3399_defconfig
@@ -17,6 +17,7 @@ CONFIG_SYS_LOAD_ADDR=0x800800
CONFIG_SF_DEFAULT_BUS=1
CONFIG_DEBUG_UART_BASE=0xFF1A0000
CONFIG_DEBUG_UART_CLOCK=24000000
+CONFIG_ENV_OFFSET_REDUND=0x3F0000
CONFIG_SPL_SPI_FLASH_SUPPORT=y
CONFIG_SPL_SPI=y
CONFIG_DEBUG_UART=y
@@ -26,6 +27,7 @@ CONFIG_DISPLAY_BOARDINFO_LATE=y
CONFIG_SPL_MAX_SIZE=0x40000
# CONFIG_SPL_RAW_IMAGE_SUPPORT is not set
CONFIG_SPL_ENV_SUPPORT=y
+CONFIG_SPL_I2C=y
CONFIG_SPL_SPI_LOAD=y
CONFIG_SYS_SPI_U_BOOT_OFFS=0xE0000
CONFIG_SPL_ATF_NO_PLATFORM_PARAM=y
@@ -37,8 +39,10 @@ CONFIG_CMD_USB=y
# CONFIG_CMD_SETEXPR is not set
CONFIG_CMD_TIME=y
CONFIG_SPL_OF_CONTROL=y
+CONFIG_OF_LIST="rockchip/rk3399-roc-pc rockchip/rk3399-roc-pc-plus"
CONFIG_OF_SPL_REMOVE_PROPS="clock-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents"
CONFIG_ENV_IS_IN_SPI_FLASH=y
+CONFIG_ENV_REDUNDANT=y
CONFIG_ENV_RELOC_GD_ENV_ADDR=y
CONFIG_ROCKCHIP_GPIO=y
CONFIG_SYS_I2C_ROCKCHIP=y
@@ -49,6 +53,7 @@ CONFIG_MMC_SDHCI_SDMA=y
CONFIG_MMC_SDHCI_ROCKCHIP=y
CONFIG_SPI_FLASH_SFDP_SUPPORT=y
CONFIG_SPI_FLASH_WINBOND=y
+CONFIG_SPI_FLASH_XMC=y
CONFIG_DM_ETH_PHY=y
CONFIG_ETH_DESIGNWARE=y
CONFIG_GMAC_ROCKCHIP=y
diff --git a/doc/board/rockchip/rockchip.rst b/doc/board/rockchip/rockchip.rst
index de2df3046e2..d9c4b753949 100644
--- a/doc/board/rockchip/rockchip.rst
+++ b/doc/board/rockchip/rockchip.rst
@@ -81,7 +81,7 @@ List of mainline supported Rockchip boards:
- 96boards RK3399 Ficus (ficus-rk3399)
- 96boards Rock960 (rock960-rk3399)
- Firefly-RK3399 (firefly_rk3399)
- - Firefly ROC-RK3399-PC
+ - Firefly ROC-RK3399-PC/ROC-RK3399-PC-PLUS (roc-pc-rk3399)
- FriendlyElec NanoPC-T4 (nanopc-t4-rk3399)
- FriendlyElec NanoPi M4 (nanopi-m4-rk3399)
- FriendlyElec NanoPi M4B (nanopi-m4b-rk3399)
diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c
index 31a2ba49a87..90aa1025176 100644
--- a/drivers/mtd/spi/spi-nor-ids.c
+++ b/drivers/mtd/spi/spi-nor-ids.c
@@ -673,6 +673,7 @@ const struct flash_info spi_nor_ids[] = {
{ INFO("XM25QH64A", 0x207017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("XM25QH64C", 0x204017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("XM25QH128A", 0x207018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { INFO("XM25QH128C", 0x204018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("XM25QU128C", 0x204118, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("XM25QH256C", 0x204019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
{ INFO("XM25QU256C", 0x204119, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
diff --git a/drivers/spi/rk_spi.c b/drivers/spi/rk_spi.c
index 2c3d70ba715..54befd54b09 100644
--- a/drivers/spi/rk_spi.c
+++ b/drivers/spi/rk_spi.c
@@ -283,6 +283,20 @@ static int rockchip_spi_probe(struct udevice *bus)
return 0;
}
+/*
+ * A device that declares spi-{tx,rx}-bus-width = <0> has no wire in that
+ * direction, so the controller can drop the matching FIFO entirely instead
+ * of clocking bytes nobody reads.
+ */
+static u32 rkspi_base_tmod(struct rockchip_spi_priv *priv)
+{
+ if (priv->mode & SPI_NO_RX)
+ return TMOD_TO;
+ if (priv->mode & SPI_NO_TX)
+ return TMOD_RO;
+ return TMOD_TR;
+}
+
static int rockchip_spi_claim_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
@@ -329,8 +343,8 @@ static int rockchip_spi_claim_bus(struct udevice *dev)
/* Frame Format */
ctrlr0 |= FRF_SPI << FRF_SHIFT;
- /* Tx and Rx mode */
- ctrlr0 |= TMOD_TR << TMOD_SHIFT;
+ /* Configure RX/TX mode */
+ ctrlr0 |= rkspi_base_tmod(priv) << TMOD_SHIFT;
writel(ctrlr0, &regs->ctrlr0);
@@ -472,7 +486,11 @@ static int rockchip_spi_xfer(struct udevice *dev, unsigned int bitlen,
writel(todo - 1, &regs->ctrlr1);
rkspi_enable_chip(regs, true);
- toread = todo;
+ /*
+ * When the RX wire is not routed, the RX FIFO never fills,
+ * so waiting on it would hang.
+ */
+ toread = (priv->mode & SPI_NO_RX) ? 0 : todo;
/* Only write if we have something to write */
towrite = out ? todo : 0;
while (toread || towrite) {
@@ -492,9 +510,10 @@ static int rockchip_spi_xfer(struct udevice *dev, unsigned int bitlen,
}
/*
- * In case that there's a transmit-component, we need to wait
- * until the control goes idle before we can disable the SPI
- * control logic (as this will implicitly flush the FIFOs).
+ * With a transmit component the TX FIFO can still hold data
+ * that has not been shifted onto the wire. Wait until the
+ * controller goes idle before disabling it, as disabling
+ * clears the FIFOs.
*/
if (out) {
ret = rkspi_wait_till_not_busy(regs);
@@ -513,7 +532,7 @@ static int rockchip_spi_xfer(struct udevice *dev, unsigned int bitlen,
if (!out)
clrsetbits_le32(&regs->ctrlr0,
TMOD_MASK << TMOD_SHIFT,
- TMOD_TR << TMOD_SHIFT);
+ rkspi_base_tmod(priv) << TMOD_SHIFT);
return ret;
}
diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c
index 120565df149..dd1843ffac1 100644
--- a/drivers/spi/spi-uclass.c
+++ b/drivers/spi/spi-uclass.c
@@ -105,6 +105,7 @@ int dm_spi_set_wordlen(struct udevice *dev, unsigned int wordlen)
int dm_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
+ struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
struct udevice *bus = dev->parent;
struct dm_spi_ops *ops = spi_get_ops(bus);
@@ -113,6 +114,15 @@ int dm_spi_xfer(struct udevice *dev, unsigned int bitlen,
if (!ops->xfer)
return -ENOSYS;
+ /*
+ * A device with no wire in one direction cannot transfer in it,
+ * so reject the request here rather than in every driver.
+ */
+ if (din && (slave_plat->mode & SPI_NO_RX))
+ return -EINVAL;
+ if (dout && (slave_plat->mode & SPI_NO_TX))
+ return -EINVAL;
+
return ops->xfer(dev, bitlen, dout, din, flags);
}
@@ -229,6 +239,9 @@ static int spi_child_post_bind(struct udevice *dev)
/* Device DUAL/QUAD mode */
value = dev_read_u32_default(dev, "spi-tx-bus-width", 1);
switch (value) {
+ case 0:
+ mode |= SPI_NO_TX;
+ break;
case 1:
break;
case 2:
@@ -247,6 +260,9 @@ static int spi_child_post_bind(struct udevice *dev)
value = dev_read_u32_default(dev, "spi-rx-bus-width", 1);
switch (value) {
+ case 0:
+ mode |= SPI_NO_RX;
+ break;
case 1:
break;
case 2:
diff --git a/include/spi.h b/include/spi.h
index 97096a77526..f477763bdc6 100644
--- a/include/spi.h
+++ b/include/spi.h
@@ -34,6 +34,8 @@ struct spinand_info;
#define SPI_RX_QUAD BIT(13) /* receive with 4 wires */
#define SPI_TX_OCTAL BIT(14) /* transmit with 8 wires */
#define SPI_RX_OCTAL BIT(15) /* receive with 8 wires */
+#define SPI_NO_TX BIT(16) /* no transmit wire */
+#define SPI_NO_RX BIT(17) /* no receive wire */
/* Header byte that marks the start of the message */
#define SPI_PREAMBLE_END_BYTE 0xec
diff --git a/test/dm/spi.c b/test/dm/spi.c
index a89ba06274f..a0da12ab6bc 100644
--- a/test/dm/spi.c
+++ b/test/dm/spi.c
@@ -216,3 +216,42 @@ static int dm_test_spi_xfer(struct unit_test_state *uts)
return 0;
}
DM_TEST(dm_test_spi_xfer, UTF_SCAN_PDATA | UTF_SCAN_FDT);
+
+/* Test that a transfer is rejected when the device has no wire for it */
+static int dm_test_spi_xfer_no_rx_tx(struct unit_test_state *uts)
+{
+ struct dm_spi_slave_plat *plat;
+ struct spi_slave *slave;
+ struct udevice *bus;
+ const int busnum = 0, cs = 0;
+ const char dout[5] = {0x9f};
+ unsigned char din[5];
+ uint saved_mode;
+
+ ut_assertok(spi_get_bus_and_cs(busnum, cs, &bus, &slave));
+ ut_assertok(spi_claim_bus(slave));
+ plat = dev_get_parent_plat(slave->dev);
+ saved_mode = plat->mode;
+
+ plat->mode |= SPI_NO_RX;
+ ut_asserteq(-EINVAL, spi_xfer(slave, 40, dout, din,
+ SPI_XFER_BEGIN | SPI_XFER_END));
+
+ plat->mode = saved_mode | SPI_NO_TX;
+ ut_asserteq(-EINVAL, spi_xfer(slave, 40, dout, din,
+ SPI_XFER_BEGIN | SPI_XFER_END));
+
+ plat->mode = saved_mode;
+ spi_release_bus(slave);
+
+ /*
+ * Since we are about to destroy all devices, we must tell sandbox
+ * to forget the emulation device
+ */
+#if CONFIG_IS_ENABLED(DM_SPI_FLASH)
+ sandbox_sf_unbind_emul(state_get_current(), busnum, cs);
+#endif
+
+ return 0;
+}
+DM_TEST(dm_test_spi_xfer_no_rx_tx, UTF_SCAN_PDATA | UTF_SCAN_FDT);