From c901732558fbee5b798bb7a065f7832d3a6bbc84 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Wed, 23 Jul 2025 17:04:41 +0100 Subject: [PATCH 01/15] clk: exynos: Fix always true test In exynos7420_peric1_get_rate the variable ret is declared as an 'unsigned int' but is then used to receive the return value of clk_get_by_index which returns an int. The value of ret is then tested for being less than 0 which will always fail for an unsigned variable. Fix this by declaring ret as an 'int' so that the test for the error condition is valid. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Signed-off-by: Minkyu Kang --- drivers/clk/exynos/clk-exynos7420.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/exynos/clk-exynos7420.c b/drivers/clk/exynos/clk-exynos7420.c index 3aa751bf4e4..7de4e688f03 100644 --- a/drivers/clk/exynos/clk-exynos7420.c +++ b/drivers/clk/exynos/clk-exynos7420.c @@ -192,7 +192,7 @@ static int exynos7420_clk_top0_probe(struct udevice *dev) static ulong exynos7420_peric1_get_rate(struct clk *clk) { struct clk in_clk; - unsigned int ret; + int ret; unsigned long freq = 0; switch (clk->id) { From 3b36c242eb338f19c4436f5deba66a65ee85071d Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 6 Aug 2025 17:27:01 -0500 Subject: [PATCH 02/15] usb: host: dwc3-of-simple: Add exynos850 compatible Enable support for Exynos850 SoC in DWC3 host glue layer driver. Signed-off-by: Sam Protsenko Reviewed-by: Marek Vasut Signed-off-by: Minkyu Kang --- drivers/usb/host/dwc3-of-simple.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/host/dwc3-of-simple.c b/drivers/usb/host/dwc3-of-simple.c index d52e7d22d1a..362178b7900 100644 --- a/drivers/usb/host/dwc3-of-simple.c +++ b/drivers/usb/host/dwc3-of-simple.c @@ -91,6 +91,7 @@ static int dwc3_of_simple_remove(struct udevice *dev) static const struct udevice_id dwc3_of_simple_ids[] = { { .compatible = "amlogic,meson-gxl-dwc3" }, { .compatible = "rockchip,rk3399-dwc3" }, + { .compatible = "samsung,exynos850-dwusb3" }, { .compatible = "ti,dwc3" }, { } }; From cb64735bdd487ebc6f8eff790bd966152e9e466b Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 6 Aug 2025 17:27:02 -0500 Subject: [PATCH 03/15] board: samsung: e850-96: Set ethaddr Set the environment variable for Ethernet MAC address (ethaddr). Use the SoC ID to make sure it's unique. It'll be formatted in a way that follows the consecutive style of the serial number ("serial#" variable), i.e.: OTP_CHIPID0 = 0xf51c8113 OTP_CHIPID1 = 0x236 get_chip_id() = 0x236f51c8113 serial# = 00000236f51c8113 ethaddr = 02:36:f5:1c:81:13 where corresponding bytes of the MAC address are: mac_addr[0] = 0x02 // OTP_CHIPID1[15:8] mac_addr[1] = 0x36 // OTP_CHIPID1[7:0] mac_addr[2] = 0xf5 // OTP_CHIPID0[31:24] mac_addr[3] = 0x1c // OTP_CHIPID0[23:16] mac_addr[4] = 0x81 // OTP_CHIPID0[15:8] mac_addr[5] = 0x13 // OTP_CHIPID0[7:0] because OTP_CHIPID1 has only 16 significant bits (with actual ID values), and all 32 bits of OTP_CHIPID0 are significant. Signed-off-by: Sam Protsenko Signed-off-by: Minkyu Kang --- board/samsung/e850-96/e850-96.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/board/samsung/e850-96/e850-96.c b/board/samsung/e850-96/e850-96.c index a6c264d1248..d489716bfdf 100644 --- a/board/samsung/e850-96/e850-96.c +++ b/board/samsung/e850-96/e850-96.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "fw.h" @@ -92,11 +93,35 @@ static void setup_serial(void) env_set("serial#", serial_str); } +static void setup_ethaddr(void) +{ + u64 serial_num; + u32 mac_hi, mac_lo; + u8 mac_addr[6]; + + if (env_get("ethaddr")) + return; + + serial_num = get_chip_id(); + mac_lo = (u32)serial_num; /* OTP_CHIPID0 */ + mac_hi = (u32)(serial_num >> 32UL); /* OTP_CHIPID1 */ + mac_addr[0] = (mac_hi >> 8) & 0xff; + mac_addr[1] = mac_hi & 0xff; + mac_addr[2] = (mac_lo >> 24) & 0xff; + mac_addr[3] = (mac_lo >> 16) & 0xff; + mac_addr[4] = (mac_lo >> 8) & 0xff; + mac_addr[5] = mac_lo & 0xff; + mac_addr[0] &= ~0x1; /* make sure it's not a multicast address */ + if (is_valid_ethaddr(mac_addr)) + eth_env_set_enetaddr("ethaddr", mac_addr); +} + int board_late_init(void) { int err; setup_serial(); + setup_ethaddr(); /* * Do this in board_late_init() to make sure MMC is not probed before From 6ebd9aa6d3aad4e87aed58400fdd56c69db48c70 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 6 Aug 2025 17:27:03 -0500 Subject: [PATCH 04/15] board: samsung: e850-96: Add ACPM code Add functions to access I3C bus via APM (Active Power Management) core by using ACPM IPC protocol. It will be further used for configuring PMIC chip voltage regulators. Signed-off-by: Sam Protsenko Signed-off-by: Minkyu Kang --- board/samsung/e850-96/Makefile | 2 +- board/samsung/e850-96/acpm.c | 169 +++++++++++++++++++++++++++++++++ board/samsung/e850-96/acpm.h | 27 ++++++ 3 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 board/samsung/e850-96/acpm.c create mode 100644 board/samsung/e850-96/acpm.h diff --git a/board/samsung/e850-96/Makefile b/board/samsung/e850-96/Makefile index 71d46ea3d2b..08eb8dca5bf 100644 --- a/board/samsung/e850-96/Makefile +++ b/board/samsung/e850-96/Makefile @@ -3,4 +3,4 @@ # Copyright (C) 2024, Linaro Limited # Sam Protsenko -obj-y := e850-96.o fw.o +obj-y := e850-96.o fw.o acpm.o diff --git a/board/samsung/e850-96/acpm.c b/board/samsung/e850-96/acpm.c new file mode 100644 index 00000000000..1cc5c6d0e4a --- /dev/null +++ b/board/samsung/e850-96/acpm.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 Linaro Ltd. + * Author: Sam Protsenko + * + * ACPM (Active Clock and Power Management) is an IPC protocol for communicating + * with APM (Active Power Management) core. The message exchange between AP + * (Application Processor) and APM is happening by using shared memory in SRAM + * (iRAM) and generating interrupts using Mailbox block. By using this IPC + * interface it's possible to offload power management tasks to APM core, which + * acts as a supervisor for CPU. One of the main tasks of APM is controlling + * PMIC chip over I3C bus. So in order to access PMIC chip registers it's + * recommended to do so by sending corresponding commands to APM via ACPM IPC + * protocol. The IPC interaction sequence looks like this: + * + * AP (CPU) <-> ACPM IPC (Mailbox + SRAM) <-> APM <-> I3C <-> PMIC + * + * This file contains functions for accessing I3C bus via APM block using + * ACPM IPC. + */ + +#include +#include +#include +#include "acpm.h" + +/* Mailbox registers */ +#define MBOX_INTGR0 0x8 /* Interrupt Generation */ +#define MBOX_INTCR1 0x20 /* Interrupt Clear */ +#define MBOX_INTSR1 0x28 /* Interrupt Status */ +#define MBOX_INTGR_OFFSET 16 +#define MBOX_TIMEOUT (1 * USEC_PER_SEC) + +/* APM shared memory registers */ +#define SHMEM_SR0 0x0 +#define SHMEM_SR1 0x4 +#define SHMEM_SR2 0x8 +#define SHMEM_SR3 0xc + +/* IPC functions */ +#define IPC_FUNC_READ 0x0 +#define IPC_FUNC_WRITE 0x1 +/* Command 0 shifts and masks */ +#define IPC_REG_SHIFT 0 +#define IPC_REG_MASK 0xff +#define IPC_TYPE_SHIFT 8 +#define IPC_TYPE_MASK 0xf +#define IPC_CHANNEL_SHIFT 12 +#define IPC_CHANNEL_MASK 0xf +/* Command 1 shifts and masks */ +#define IPC_FUNC_SHIFT 0 +#define IPC_FUNC_MASK 0xff +#define IPC_WRITE_VAL_SHIFT 8 +#define IPC_WRITE_VAL_MASK 0xff +/* Command 3 shifts and masks */ +#define IPC_DEST_SHIFT 8 +#define IPC_DEST_MASK 0xff +#define IPC_RETURN_SHIFT 24 +#define IPC_RETURN_MASK 0xff + +/** + * acpm_ipc_send_data_async() - Send data to I3C block over ACPM IPC + * @acpm: ACPM data + * @cmd0: Command 0 value to send + * @cmd1: Command 1 value to send + */ +static void acpm_ipc_send_data_async(struct acpm *acpm, u32 cmd0, u32 cmd1) +{ + u32 irq_bit = 1 << acpm->ipc_ch; + u32 intgr = irq_bit << MBOX_INTGR_OFFSET; + + /* Write data to the shared memory */ + writel(cmd0, acpm->sram_base + SHMEM_SR0); + writel(cmd1, acpm->sram_base + SHMEM_SR1); + dsb(); + + /* Generate interrupt for I3C block */ + writel(intgr, acpm->mbox_base + MBOX_INTGR0); +} + +/** + * acpm_ipc_wait_resp() - Read response data from I3C block over ACPM IPC + * @acpm: ACPM data + * @cmd2: Will contain read value for command 2 + * @cmd3: Will contain read value for command 3 + * + * Return: 0 on success or negative value on error. + */ +static int acpm_ipc_wait_resp(struct acpm *acpm, u32 *cmd2, u32 *cmd3) +{ + u32 irq_bit = 1 << acpm->ipc_ch; + u32 reg; + int ret; + + /* Wait for the interrupt from I3C block */ + ret = readl_poll_timeout(acpm->mbox_base + MBOX_INTSR1, reg, + reg & irq_bit, MBOX_TIMEOUT); + if (ret < 0) + return ret; + + /* Clear the interrupt */ + writel(irq_bit, acpm->mbox_base + MBOX_INTCR1); + + /* Read data from the shared memory */ + *cmd2 = readl(acpm->sram_base + SHMEM_SR2); + *cmd3 = readl(acpm->sram_base + SHMEM_SR3); + + return 0; +} + +/** + * acpm_i3c_read() - Read an I3C register of some I3C slave device + * @acpm: ACPM data + * @ch: I3C channel (bus) number (0-15) + * @addr: I3C address of slave device (0-15) + * @reg: Address of I3C register in the slave device to read from + * @val: Will contain the read value + * + * Return: 0 on success or non-zero code on error (may be positive). + */ +int acpm_i3c_read(struct acpm *acpm, u8 ch, u8 addr, u8 reg, u8 *val) +{ + u32 cmd[4] = { 0 }; + u8 ret; + + cmd[0] = (ch & IPC_CHANNEL_MASK) << IPC_CHANNEL_SHIFT | + (addr & IPC_TYPE_MASK) << IPC_TYPE_SHIFT | + (reg & IPC_REG_MASK) << IPC_REG_SHIFT; + cmd[1] = IPC_FUNC_READ << IPC_FUNC_SHIFT; + + acpm_ipc_send_data_async(acpm, cmd[0], cmd[1]); + ret = acpm_ipc_wait_resp(acpm, &cmd[2], &cmd[3]); + if (ret) + return ret; + + *val = (cmd[3] >> IPC_DEST_SHIFT) & IPC_DEST_MASK; + ret = (cmd[3] >> IPC_RETURN_SHIFT) & IPC_RETURN_MASK; + return ret; +} + +/** + * acpm_i3c_write() - Write an I3C register of some I3C slave device + * @acpm: ACPM data + * @ch: I3C channel (bus) number (0-15) + * @addr: I3C address of slave device (0-15) + * @reg: Address of I3C register in the slave device to write into + * @val: Value to write + * + * Return: 0 on success or non-zero code on error (may be positive). + */ +int acpm_i3c_write(struct acpm *acpm, u8 ch, u8 addr, u8 reg, u8 val) +{ + u32 cmd[4] = { 0 }; + u8 ret; + + cmd[0] = (ch & IPC_CHANNEL_MASK) << IPC_CHANNEL_SHIFT | + (addr & IPC_TYPE_MASK) << IPC_TYPE_SHIFT | + (reg & IPC_REG_MASK) << IPC_REG_SHIFT; + cmd[1] = IPC_FUNC_WRITE << IPC_FUNC_SHIFT | + (val & IPC_WRITE_VAL_MASK) << IPC_WRITE_VAL_SHIFT; + + acpm_ipc_send_data_async(acpm, cmd[0], cmd[1]); + ret = acpm_ipc_wait_resp(acpm, &cmd[2], &cmd[3]); + if (ret) + return ret; + + ret = (cmd[3] >> IPC_RETURN_SHIFT) & IPC_RETURN_MASK; + return ret; +} diff --git a/board/samsung/e850-96/acpm.h b/board/samsung/e850-96/acpm.h new file mode 100644 index 00000000000..9373969209f --- /dev/null +++ b/board/samsung/e850-96/acpm.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 Linaro Ltd. + * Sam Protsenko + */ + +#ifndef __E850_96_ACPM_H +#define __E850_96_ACPM_H + +#include + +/** + * struct acpm - Data for I3C communication over ACPM IPC protocol + * @mbox_base: Base address of APM mailbox block + * @sram_base: Base address of shared memory used for APM messages + * @ipc_ch: Mailbox channel number used for communication with I3C block (0-15) + */ +struct acpm { + void __iomem *mbox_base; + void __iomem *sram_base; + u8 ipc_ch; +}; + +int acpm_i3c_read(struct acpm *acpm, u8 ch, u8 addr, u8 reg, u8 *val); +int acpm_i3c_write(struct acpm *acpm, u8 ch, u8 addr, u8 reg, u8 val); + +#endif /* __E850_96_ACPM_H */ From 17b1d78484446215320de90b4f6df13fbc85b6a3 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 6 Aug 2025 17:27:04 -0500 Subject: [PATCH 05/15] board: samsung: e850-96: Add PMIC code Add functions for configuring voltage regulators on S2MPU12 PMIC chip for E850-96 board. The chip is accessed by commanding APM core (via ACPM IPC protocol) to perform corresponding transfers over I3C bus. The most important regulator being set up is LDO24 used for LAN9514 chip power. As LAN9514 implements USB hub and Ethernet controller functionality, it's crucial to enable and configure LDO24 to be able to use it further. While at it, configure the rest of regulators that might be needed later, both in the bootloader and in kernel. Signed-off-by: Sam Protsenko Signed-off-by: Minkyu Kang --- board/samsung/e850-96/Makefile | 2 +- board/samsung/e850-96/pmic.c | 144 +++++++++++++++++++++++++++++++++ board/samsung/e850-96/pmic.h | 14 ++++ 3 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 board/samsung/e850-96/pmic.c create mode 100644 board/samsung/e850-96/pmic.h diff --git a/board/samsung/e850-96/Makefile b/board/samsung/e850-96/Makefile index 08eb8dca5bf..76b8d47994e 100644 --- a/board/samsung/e850-96/Makefile +++ b/board/samsung/e850-96/Makefile @@ -3,4 +3,4 @@ # Copyright (C) 2024, Linaro Limited # Sam Protsenko -obj-y := e850-96.o fw.o acpm.o +obj-y := e850-96.o fw.o acpm.o pmic.o diff --git a/board/samsung/e850-96/pmic.c b/board/samsung/e850-96/pmic.c new file mode 100644 index 00000000000..037fd4844c5 --- /dev/null +++ b/board/samsung/e850-96/pmic.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 Linaro Ltd. + * Author: Sam Protsenko + * + * This file contains functions for S2MPU12 PMIC regulators configuration. + * + * Example of voltage calculation for LDO24 and LDO32: + * - V_min = 1800 mV + * - V_step = 25 mV + * - V_wanted = 3300 mV + * - register value: (V_wanted - V_min) / V_step = 60 = 0x3c + * + * NOTE: 0x3c value might mean different voltage for other LDOs. + */ + +#include +#include +#include "pmic.h" + +/* PMIC definitions */ +#define S2MPU12_CHANNEL 0 /* I3C bus number of PMIC */ +#define S2MPU12_PM_ADDR 0x1 /* I3C slave addr of PM part */ + +/* PMIC I3C registers */ +#define S2MPU12_PM_LDO1_CTRL 0x2b +#define S2MPU12_PM_LDO_CTRL(n) (S2MPU12_PM_LDO1_CTRL + (n) - 1) + +/* LDOx_CTRL values */ +#define S2MPU12_LDO_CTRL_OUT_MASK (0x3 << 6) +#define S2MPU12_LDO_CTRL_OUT_ALWAYS_ON (0x3 << 6) + +struct pmic_ldo { + u8 num; /* LDO number */ + u8 en; /* "enable" bits value in LDOx_CTRL register */ + u8 out; /* "output voltage" bits value in LDOx_CTRL register */ +}; + +/* List of LDOs to enable only */ +static u8 pmic_ldos_en[] = { + 2, /* 1.8V/450mA: multiple lines */ + 11, /* 3.0V/150mA: AVDD33_USB20 */ + 23, /* 2.85V/800mA: VDD_EMMC_2P85 */ + 27, /* 3.0V/150mA: MIPI_SWITCH_3V3 */ + 28, /* 1.8V/150mA: HDMI_CONV_1V8 */ + 30, /* 1.8V/150mA: NPU_VDD18 */ +}; + +/* List of LDOs to enable and set output voltage */ +static struct pmic_ldo pmic_ldos_en_out[] = { + { + .num = 24, /* 3.0V/800mA: VDD_LAN (LAN9514) */ + .en = S2MPU12_LDO_CTRL_OUT_ALWAYS_ON, + .out = 0x3c, /* means 3.3V for LDO24 */ + }, { + .num = 32, /* 3.3V/300mA: CAM_VDD (RPi camera module) */ + .en = S2MPU12_LDO_CTRL_OUT_ALWAYS_ON, + .out = 0x3c, /* means 3.3V for LDO32 */ + }, +}; + +/* Enable specified LDO */ +static int pmic_ldo_set_en(struct acpm *acpm, u8 ldo) +{ + const u8 reg = S2MPU12_PM_LDO_CTRL(ldo); + u8 val; + int err; + + err = acpm_i3c_read(acpm, S2MPU12_CHANNEL, S2MPU12_PM_ADDR, reg, &val); + if (err) + return err; + + val &= ~S2MPU12_LDO_CTRL_OUT_MASK; + val |= S2MPU12_LDO_CTRL_OUT_ALWAYS_ON; + + return acpm_i3c_write(acpm, S2MPU12_CHANNEL, S2MPU12_PM_ADDR, reg, val); +} + +/* Enable specified LDO and set its voltage to 0xc0 value */ +static int pmic_ldo_set_en_out(struct acpm *acpm, struct pmic_ldo *ldo) +{ + const u8 reg = S2MPU12_PM_LDO_CTRL(ldo->num); + const u8 val = ldo->en | ldo->out; + + return acpm_i3c_write(acpm, S2MPU12_CHANNEL, S2MPU12_PM_ADDR, reg, val); +} + +#ifdef DEBUG +static void pmic_trace_ldo(struct acpm *acpm, u8 ldo) +{ + const u8 reg = S2MPU12_PM_LDO_CTRL(ldo); + u8 val; + int err; + + err = acpm_i3c_read(acpm, S2MPU12_CHANNEL, S2MPU12_PM_ADDR, reg, &val); + if (err) + printf(" S2MPU12_PM_LDO%u_CTRL: Read error!\n", ldo); + else + printf(" S2MPU12_PM_LDO%u_CTRL: 0x%x\n", ldo, val); +} + +static void pmic_trace_ldos(struct acpm *acpm) +{ + size_t i; + + printf("Tracing LDOs...\n"); + for (i = 0; i < ARRAY_SIZE(pmic_ldos_en); ++i) + pmic_trace_ldo(acpm, pmic_ldos_en[i]); + for (i = 0; i < ARRAY_SIZE(pmic_ldos_en_out); ++i) + pmic_trace_ldo(acpm, pmic_ldos_en_out[i].num); +} +#endif + +/** + * pmic_init() - Enable power regulators in S2MPU12 PMIC. + * @acpm: Data for I3C communication with PMIC over ACPM protocol + * + * Enable LDOs needed for devices used in the bootloader and kernel. + * + * Return: 0 on success or non-zero code on error. + */ +int pmic_init(struct acpm *acpm) +{ + size_t i; + int err; + + for (i = 0; i < ARRAY_SIZE(pmic_ldos_en); ++i) { + err = pmic_ldo_set_en(acpm, pmic_ldos_en[i]); + if (err) + return -EIO; + } + + for (i = 0; i < ARRAY_SIZE(pmic_ldos_en_out); ++i) { + err = pmic_ldo_set_en_out(acpm, &pmic_ldos_en_out[i]); + if (err) + return -EIO; + } + +#ifdef DEBUG + pmic_trace_ldos(acpm); +#endif + + return 0; +} diff --git a/board/samsung/e850-96/pmic.h b/board/samsung/e850-96/pmic.h new file mode 100644 index 00000000000..46624c2ebd4 --- /dev/null +++ b/board/samsung/e850-96/pmic.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 Linaro Ltd. + * Sam Protsenko + */ + +#ifndef __E850_96_PMIC_H +#define __E850_96_PMIC_H + +#include "acpm.h" + +int pmic_init(struct acpm *acpm); + +#endif /* __E850_96_PMIC_H */ From 8fefc2eb9610b75270f0376980dae952d16fc84b Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 6 Aug 2025 17:27:05 -0500 Subject: [PATCH 06/15] board: samsung: e850-96: Configure PMIC regulators Make use of PMIC configuration routines and enable all LDOs that might be useful for bootloader and kernel. The most crucial regulator being enabled at the moment is LDO24 which provides power to LAN9514 chip. That makes Ethernet controller and USB hub functional. Signed-off-by: Sam Protsenko Signed-off-by: Minkyu Kang --- board/samsung/e850-96/e850-96.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/board/samsung/e850-96/e850-96.c b/board/samsung/e850-96/e850-96.c index d489716bfdf..2cf874fcf7a 100644 --- a/board/samsung/e850-96/e850-96.c +++ b/board/samsung/e850-96/e850-96.c @@ -11,11 +11,18 @@ #include #include #include "fw.h" +#include "pmic.h" /* OTP Controller base address and register offsets */ -#define EXYNOS850_OTP_BASE 0x10000000 -#define OTP_CHIPID0 0x4 -#define OTP_CHIPID1 0x8 +#define EXYNOS850_OTP_BASE 0x10000000 +#define OTP_CHIPID0 0x4 +#define OTP_CHIPID1 0x8 + +/* ACPM and PMIC definitions */ +#define EXYNOS850_MBOX_APM2AP_BASE 0x11900000 +#define EXYNOS850_APM_SRAM_BASE 0x02039000 /* in iRAM */ +#define EXYNOS850_APM_SHMEM_OFFSET 0x3200 +#define EXYNOS850_IPC_AP_I3C 10 struct efi_fw_image fw_images[] = { { @@ -56,6 +63,13 @@ struct efi_capsule_update_info update_info = { .images = fw_images, }; +static struct acpm acpm = { + .mbox_base = (void __iomem *)EXYNOS850_MBOX_APM2AP_BASE, + .sram_base = (void __iomem *)(EXYNOS850_APM_SRAM_BASE + + EXYNOS850_APM_SHMEM_OFFSET), + .ipc_ch = EXYNOS850_IPC_AP_I3C, +}; + int dram_init(void) { return fdtdec_setup_mem_size_base(); @@ -133,3 +147,14 @@ int board_late_init(void) return 0; } + +int power_init_board(void) +{ + int err; + + err = pmic_init(&acpm); + if (err) + printf("ERROR: Failed to configure PMIC (%d)\n", err); + + return 0; +} From c542140d7cade7d2f14a7a0f260de9b196a97d00 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 6 Aug 2025 17:27:06 -0500 Subject: [PATCH 07/15] board: samsung: e850-96: Extract device info from fw loading code Make it possible to provide the information about storage device where LDFW firmware resides to the firmware loading routine. The firmware loader code shouldn't have that data hard-coded anyway, and it also allows for implementing more dynamic behavior later, like choosing the storage device containing LDFW via some environment variables. No functional change. Signed-off-by: Sam Protsenko Signed-off-by: Minkyu Kang --- board/samsung/e850-96/e850-96.c | 9 ++++++- board/samsung/e850-96/fw.c | 47 ++++++++++++++++++++------------- board/samsung/e850-96/fw.h | 4 ++- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/board/samsung/e850-96/e850-96.c b/board/samsung/e850-96/e850-96.c index 2cf874fcf7a..b5f6ba23432 100644 --- a/board/samsung/e850-96/e850-96.c +++ b/board/samsung/e850-96/e850-96.c @@ -24,6 +24,12 @@ #define EXYNOS850_APM_SHMEM_OFFSET 0x3200 #define EXYNOS850_IPC_AP_I3C 10 +/* LDFW firmware definitions */ +#define LDFW_NWD_ADDR 0x88000000 +#define EMMC_IFNAME "mmc" +#define EMMC_DEV_NUM 0 +#define EMMC_ESP_PART 1 + struct efi_fw_image fw_images[] = { { .image_type_id = E850_96_FWBL1_IMAGE_GUID, @@ -141,7 +147,8 @@ int board_late_init(void) * Do this in board_late_init() to make sure MMC is not probed before * efi_init_early(). */ - err = load_ldfw(); + err = load_ldfw(EMMC_IFNAME, EMMC_DEV_NUM, EMMC_ESP_PART, + LDFW_NWD_ADDR); if (err) printf("ERROR: LDFW loading failed (%d)\n", err); diff --git a/board/samsung/e850-96/fw.c b/board/samsung/e850-96/fw.c index 8f64e759b43..64235c01a25 100644 --- a/board/samsung/e850-96/fw.c +++ b/board/samsung/e850-96/fw.c @@ -11,13 +11,9 @@ #include #include "fw.h" -#define EMMC_IFACE "mmc" -#define EMMC_DEV_NUM 0 #define LDFW_RAW_PART "ldfw" -#define LDFW_FAT_PART "esp" #define LDFW_FAT_PATH "/EFI/firmware/ldfw.bin" -#define LDFW_NWD_ADDR 0x88000000 #define LDFW_MAGIC 0x10adab1e #define SMC_CMD_LOAD_LDFW -0x500 #define SDM_HW_RESET_STATUS 0x1230 @@ -39,21 +35,25 @@ struct ldfw_header { }; /* Load LDFW binary as a file from FAT partition */ -static int read_fw_from_fat(const char *part_name, const char *path, void *buf) +static int read_fw_from_fat(const char *ifname, int dev, int part, + const char *path, void *buf) { - char dev_part_str[8]; + struct blk_desc *blk_desc; loff_t len_read; int err; - snprintf(dev_part_str, sizeof(dev_part_str), "%d#%s", EMMC_DEV_NUM, - LDFW_FAT_PART); - - err = fs_set_blk_dev(EMMC_IFACE, dev_part_str, FS_TYPE_FAT); - if (err) { - debug("%s: Can't set block device\n", __func__); + blk_desc = blk_get_dev(ifname, dev); + if (!blk_desc) { + debug("%s: Can't get block device\n", __func__); return -ENODEV; } + err = fs_set_blk_dev_with_part(blk_desc, part); + if (err) { + debug("%s: Can't set partition\n", __func__); + return -ENOENT; + } + err = fs_read(path, (ulong)buf, 0, 0, &len_read); if (err) { debug("%s: Can't read LDFW file\n", __func__); @@ -64,16 +64,17 @@ static int read_fw_from_fat(const char *part_name, const char *path, void *buf) } /* Load LDFW binary from raw partition on block device into RAM buffer */ -static int read_fw_from_raw(const char *part_name, void *buf) +static int read_fw_from_raw(const char *ifname, int dev, const char *part_name, + void *buf) { struct blk_desc *blk_desc; struct disk_partition part; unsigned long cnt; int part_num; - blk_desc = blk_get_dev(EMMC_IFACE, EMMC_DEV_NUM); + blk_desc = blk_get_dev(ifname, dev); if (!blk_desc) { - debug("%s: Can't get eMMC device\n", __func__); + debug("%s: Can't get block device\n", __func__); return -ENODEV; } @@ -92,9 +93,17 @@ static int read_fw_from_raw(const char *part_name, void *buf) return 0; } -int load_ldfw(void) +/** + * load_ldfw - Load the loadable firmware (LDFW) + * @ifname: Interface name of the block device to load the firmware from + * @dev: Device number + * @part: Partition number + * @addr: Temporary memory (Normal World) to use for loading the firmware + * + * Return: 0 on success or a negative value on error. + */ +int load_ldfw(const char *ifname, int dev, int part, phys_addr_t addr) { - const phys_addr_t addr = (phys_addr_t)LDFW_NWD_ADDR; struct ldfw_header *hdr; struct arm_smccc_res res; void *buf = (void *)addr; @@ -102,9 +111,9 @@ int load_ldfw(void) int err, i; /* First try to read LDFW from EFI partition, then from the raw one */ - err = read_fw_from_fat(LDFW_FAT_PART, LDFW_FAT_PATH, buf); + err = read_fw_from_fat(ifname, dev, part, LDFW_FAT_PATH, buf); if (err) { - err = read_fw_from_raw(LDFW_RAW_PART, buf); + err = read_fw_from_raw(ifname, dev, LDFW_RAW_PART, buf); if (err) return err; } diff --git a/board/samsung/e850-96/fw.h b/board/samsung/e850-96/fw.h index 472664e4ed2..73d9615d4a9 100644 --- a/board/samsung/e850-96/fw.h +++ b/board/samsung/e850-96/fw.h @@ -7,6 +7,8 @@ #ifndef __E850_96_FW_H #define __E850_96_FW_H -int load_ldfw(void); +#include + +int load_ldfw(const char *ifname, int dev, int part, phys_addr_t addr); #endif /* __E850_96_FW_H */ From 75f75832d0a97a8c1e1167eb3a6d3dbd1d4eae54 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 6 Aug 2025 17:27:07 -0500 Subject: [PATCH 08/15] board: samsung: e850-96: Add bootdev var to choose boot device Provide a way for the user to select which storage to load the LDFW firmware from, by setting the corresponding environment variables: - bootdev: block device interface name - bootdevnum: block device number - bootdevpart: partition number This might be useful when the OS is flashed and booted from a different storage device than eMMC (e.g. USB flash drive). In this case it should be sufficient to just set: => setenv bootdev usb => env save assuming that the USB drive layout follows the same partitioning scheme as defined in $partitions. Signed-off-by: Sam Protsenko Signed-off-by: Minkyu Kang --- board/samsung/e850-96/e850-96.c | 38 +++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/board/samsung/e850-96/e850-96.c b/board/samsung/e850-96/e850-96.c index b5f6ba23432..3df241edde2 100644 --- a/board/samsung/e850-96/e850-96.c +++ b/board/samsung/e850-96/e850-96.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "fw.h" #include "pmic.h" @@ -136,21 +137,40 @@ static void setup_ethaddr(void) eth_env_set_enetaddr("ethaddr", mac_addr); } -int board_late_init(void) +/* + * Call this in board_late_init() to avoid probing block devices before + * efi_init_early(). + */ +void load_firmware(void) { + const char *ifname; + ulong dev, part; int err; - setup_serial(); - setup_ethaddr(); + ifname = env_get("bootdev"); + if (!ifname) + ifname = EMMC_IFNAME; + dev = env_get_ulong("bootdevnum", 10, EMMC_DEV_NUM); + part = env_get_ulong("bootdevpart", 10, EMMC_ESP_PART); - /* - * Do this in board_late_init() to make sure MMC is not probed before - * efi_init_early(). - */ - err = load_ldfw(EMMC_IFNAME, EMMC_DEV_NUM, EMMC_ESP_PART, - LDFW_NWD_ADDR); + if (!strcmp(ifname, "usb")) { + printf("Starting USB (bootdev=usb)...\n"); + err = usb_init(); + if (err) + return; + } + + printf("Loading LDFW firmware (from %s %ld)...\n", ifname, dev); + err = load_ldfw(ifname, dev, part, LDFW_NWD_ADDR); if (err) printf("ERROR: LDFW loading failed (%d)\n", err); +} + +int board_late_init(void) +{ + setup_serial(); + setup_ethaddr(); + load_firmware(); return 0; } From e52e4320ba8db5a0a42f0bf53b7ac98fcfa83a14 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 6 Aug 2025 17:27:08 -0500 Subject: [PATCH 09/15] configs: e850-96: Disable CONFIG_DEFAULT_FDT_FILE Linux kernel should use some separate device tree obtained from another source anyway. For example the dtb file can be read from /boot directory in eMMC rootfs partition, either by GRUB or U-Boot. Using U-Boot's device tree blob to provide it to the kernel (when CONFIG_DEFAULT_FDT_FILE is set and nobody else overrides this choice) might lead to undesired effects when booting the OS. For example, if a user sets "dr_mode" property to "host" value in U-Boot's dts to enable USB host capabilities in U-Boot, it might confuse usb-conn-gpio driver in Linux kernel later like this: platform connector: deferred probe pending: usb-conn-gpio: failed to get role switch Disable CONFIG_DEFAULT_FDT_FILE option to avoid any possible confusion. Signed-off-by: Sam Protsenko Signed-off-by: Minkyu Kang --- board/samsung/e850-96/e850-96.env | 2 +- configs/e850-96_defconfig | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/board/samsung/e850-96/e850-96.env b/board/samsung/e850-96/e850-96.env index aed7a71046d..992318b0ab2 100644 --- a/board/samsung/e850-96/e850-96.env +++ b/board/samsung/e850-96/e850-96.env @@ -5,7 +5,7 @@ fdt_addr_r=0x8c000000 scriptaddr=0x8c100000 pxefile_addr_r=0x8c200000 ramdisk_addr_r=0x8c300000 -fdtfile=CONFIG_DEFAULT_FDT_FILE +fdtfile=exynos/exynos850-e850-96.dtb dfu_alt_info= rawemmc raw 0 0x747c000 mmcpart 1; diff --git a/configs/e850-96_defconfig b/configs/e850-96_defconfig index b4066d87460..412c99468f2 100644 --- a/configs/e850-96_defconfig +++ b/configs/e850-96_defconfig @@ -19,7 +19,6 @@ CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y CONFIG_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y CONFIG_BOOTSTD_FULL=y -CONFIG_DEFAULT_FDT_FILE="exynos850-e850-96.dtb" # CONFIG_DISPLAY_CPUINFO is not set # CONFIG_BOARD_INIT is not set CONFIG_BOARD_LATE_INIT=y From 88a465b47cd625775cc0a693b1d86914ca76cf5f Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 6 Aug 2025 17:27:09 -0500 Subject: [PATCH 10/15] configs: e850-96: Enable USB host support Exynos850 SoC has a dual-role USB controller which can be configured in USB host role. As it's the only one USB controller on the board, it's shared between "device" USB connector (micro-USB) and host USB connectors. The hardware automatically powers on the host related parts when the micro-USB cable (for device role) is being disconnected. Also, as U-Boot lacks dynamic USB role switching capability, the only way to switch the role at the moment is to modify "dr_mode" property in U-Boot's device tree file here: dts/upstream/src/arm64/exynos/exynos850-e850-96.dts This won't affect the dynamic role switching later in Linux kernel, as a separate (different) device tree blob is provided to the kernel. Enable the USB host support and corresponding commands to make it functional in E850-96 board. Signed-off-by: Sam Protsenko Signed-off-by: Minkyu Kang --- configs/e850-96_defconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/configs/e850-96_defconfig b/configs/e850-96_defconfig index 412c99468f2..d7cd1b4b8ba 100644 --- a/configs/e850-96_defconfig +++ b/configs/e850-96_defconfig @@ -29,6 +29,8 @@ CONFIG_CMD_CLK=y CONFIG_CMD_DFU=y CONFIG_CMD_GPT=y CONFIG_CMD_MMC=y +CONFIG_CMD_USB=y +CONFIG_CMD_USB_MASS_STORAGE=y CONFIG_CMD_EFIDEBUG=y # CONFIG_CMD_DATE is not set CONFIG_CMD_RTC=y @@ -63,6 +65,9 @@ CONFIG_SYSRESET=y CONFIG_SYSRESET_SYSCON=y CONFIG_USB=y CONFIG_DM_USB_GADGET=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_DWC3=y +CONFIG_USB_XHCI_DWC3_OF_SIMPLE=y CONFIG_USB_DWC3=y CONFIG_USB_DWC3_GENERIC=y CONFIG_USB_GADGET=y From e1d02147b3f015139e3dcc705a97486b07b640ca Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 6 Aug 2025 17:27:10 -0500 Subject: [PATCH 11/15] configs: e850-96: Enable Ethernet LAN9514 is a chip on E850-96 board which acts as a USB host hub and Ethernet controller. It's controlled via USB lines when DWC3 is configured to be in USB host role (by setting the "dr_mode" property to "host" value in e850-96 dts file). Enable network support and LAN9514 chip support. This makes Ethernet functional on E850-96 board. Signed-off-by: Sam Protsenko Signed-off-by: Minkyu Kang --- configs/e850-96_defconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/configs/e850-96_defconfig b/configs/e850-96_defconfig index d7cd1b4b8ba..8f9659ce050 100644 --- a/configs/e850-96_defconfig +++ b/configs/e850-96_defconfig @@ -42,7 +42,7 @@ CONFIG_ENV_IS_IN_MMC=y CONFIG_ENV_REDUNDANT=y CONFIG_ENV_RELOC_GD_ENV_ADDR=y CONFIG_ENV_MMC_EMMC_HW_PARTITION=2 -CONFIG_NO_NET=y +CONFIG_NET_LWIP=y CONFIG_CLK_EXYNOS850=y CONFIG_DFU_MMC=y CONFIG_USB_FUNCTION_FASTBOOT=y @@ -70,6 +70,8 @@ CONFIG_USB_XHCI_DWC3=y CONFIG_USB_XHCI_DWC3_OF_SIMPLE=y CONFIG_USB_DWC3=y CONFIG_USB_DWC3_GENERIC=y +CONFIG_USB_HOST_ETHER=y +CONFIG_USB_ETHER_SMSC95XX=y CONFIG_USB_GADGET=y CONFIG_USB_GADGET_MANUFACTURER="Samsung" CONFIG_USB_GADGET_VENDOR_NUM=0x18d1 From 869a18c7e3cbfae613d7829139635de62e96b36c Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Fri, 22 Aug 2025 20:54:38 +0200 Subject: [PATCH 12/15] ARM: exynos: use correct exynos4210-origen SoC in Kconfig There exists both a Origen board based on exynos4210, and a board based on exynos4412. U-boot only supports the one based on exynos 4210, but Kconfig string was accidentally written as Exynos4412 Origen in previous migration to Kconfig. Fix the string to clear up confusion, and to not give the impression that both types of Origen boards are supported. Fixes: 72df68cc6b73 ("exynos: kconfig: move board select menu and common settings") Signed-off-by: Henrik Grimler Signed-off-by: Minkyu Kang --- arch/arm/mach-exynos/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index 28193039cb8..2cd67d02386 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -88,7 +88,7 @@ config TARGET_S5PC210_UNIVERSAL select MISC_COMMON config TARGET_ORIGEN - bool "Exynos4412 Origen board" + bool "Exynos4210 Origen board" select EXYNOS4210 select SUPPORT_SPL From 354b3d789289ceeba0ce1e911def9f3d28ee8c98 Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Fri, 22 Aug 2025 20:54:39 +0200 Subject: [PATCH 13/15] ARM: exynos: pinmux: fix parentheses alignments For multi-line commands the lines should preferably be aligned with the opening parenthesis. Signed-off-by: Henrik Grimler Signed-off-by: Minkyu Kang --- arch/arm/mach-exynos/pinmux.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-exynos/pinmux.c b/arch/arm/mach-exynos/pinmux.c index 48c325190d5..bd82e1ac750 100644 --- a/arch/arm/mach-exynos/pinmux.c +++ b/arch/arm/mach-exynos/pinmux.c @@ -102,7 +102,7 @@ static int exynos5_mmc_config(int peripheral, int flags) } if ((flags & PINMUX_FLAG_8BIT_MODE) && !start_ext) { debug("SDMMC device %d does not support 8bit mode", - peripheral); + peripheral); return -1; } if (flags & PINMUX_FLAG_8BIT_MODE) { @@ -902,7 +902,7 @@ static int exynos4_pinmux_decode_periph_id(const void *blob, int node) u32 cell[3]; err = fdtdec_get_int_array(blob, node, "interrupts", cell, - ARRAY_SIZE(cell)); + ARRAY_SIZE(cell)); if (err) { debug(" invalid peripheral id\n"); return PERIPH_ID_NONE; @@ -917,7 +917,7 @@ static int exynos5_pinmux_decode_periph_id(const void *blob, int node) u32 cell[3]; err = fdtdec_get_int_array(blob, node, "interrupts", cell, - ARRAY_SIZE(cell)); + ARRAY_SIZE(cell)); if (err) return PERIPH_ID_NONE; From 949f71c13eafdaf699d4cb0ecdd6fa2950d7d01f Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Fri, 22 Aug 2025 20:54:40 +0200 Subject: [PATCH 14/15] ARM: exynos: pinmux: add newlines to debug messages To make stdout messages easier to read and understand. Signed-off-by: Henrik Grimler Signed-off-by: Minkyu Kang --- arch/arm/mach-exynos/pinmux.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/arch/arm/mach-exynos/pinmux.c b/arch/arm/mach-exynos/pinmux.c index bd82e1ac750..ed46ea03355 100644 --- a/arch/arm/mach-exynos/pinmux.c +++ b/arch/arm/mach-exynos/pinmux.c @@ -32,7 +32,7 @@ static void exynos5_uart_config(int peripheral) count = 2; break; default: - debug("%s: invalid peripheral %d", __func__, peripheral); + debug("%s: invalid peripheral %d\n", __func__, peripheral); return; } for (i = start; i < start + count; i++) { @@ -63,7 +63,7 @@ static void exynos5420_uart_config(int peripheral) count = 2; break; default: - debug("%s: invalid peripheral %d", __func__, peripheral); + debug("%s: invalid peripheral %d\n", __func__, peripheral); return; } @@ -97,11 +97,11 @@ static int exynos5_mmc_config(int peripheral, int flags) start_ext = 0; break; default: - debug("%s: invalid peripheral %d", __func__, peripheral); + debug("%s: invalid peripheral %d\n", __func__, peripheral); return -1; } if ((flags & PINMUX_FLAG_8BIT_MODE) && !start_ext) { - debug("SDMMC device %d does not support 8bit mode", + debug("SDMMC device %d does not support 8bit mode\n", peripheral); return -1; } @@ -145,12 +145,12 @@ static int exynos5420_mmc_config(int peripheral, int flags) break; default: start = 0; - debug("%s: invalid peripheral %d", __func__, peripheral); + debug("%s: invalid peripheral %d\n", __func__, peripheral); return -1; } if ((flags & PINMUX_FLAG_8BIT_MODE) && !start_ext) { - debug("SDMMC device %d does not support 8bit mode", + debug("SDMMC device %d does not support 8bit mode\n", peripheral); return -1; } @@ -453,7 +453,7 @@ void exynos5420_spi_config(int peripheral) default: cfg = 0; pin = 0; - debug("%s: invalid peripheral %d", __func__, peripheral); + debug("%s: invalid peripheral %d\n", __func__, peripheral); return; } @@ -522,7 +522,7 @@ static int exynos5_pinmux_config(int peripheral, int flags) gpio_cfg_pin(EXYNOS5_GPIO_B20, S5P_GPIO_FUNC(2)); break; default: - debug("%s: invalid peripheral %d", __func__, peripheral); + debug("%s: invalid peripheral %d\n", __func__, peripheral); return -1; } @@ -570,7 +570,7 @@ static int exynos5420_pinmux_config(int peripheral, int flags) gpio_cfg_pin(EXYNOS5420_GPIO_B20, S5P_GPIO_FUNC(2)); break; default: - debug("%s: invalid peripheral %d", __func__, peripheral); + debug("%s: invalid peripheral %d\n", __func__, peripheral); return -1; } @@ -683,7 +683,7 @@ static void exynos4_uart_config(int peripheral) count = 2; break; default: - debug("%s: invalid peripheral %d", __func__, peripheral); + debug("%s: invalid peripheral %d\n", __func__, peripheral); return; } for (i = start; i < (start + count); i++) { @@ -797,7 +797,7 @@ static void exynos4x12_uart_config(int peripheral) count = 2; break; default: - debug("%s: invalid peripheral %d", __func__, peripheral); + debug("%s: invalid peripheral %d\n", __func__, peripheral); return; } for (i = start; i < (start + count); i++) { @@ -834,7 +834,7 @@ static int exynos4_pinmux_config(int peripheral, int flags) debug("SDMMC device %d not implemented\n", peripheral); return -1; default: - debug("%s: invalid peripheral %d", __func__, peripheral); + debug("%s: invalid peripheral %d\n", __func__, peripheral); return -1; } @@ -869,7 +869,7 @@ static int exynos4x12_pinmux_config(int peripheral, int flags) debug("SDMMC device %d not implemented\n", peripheral); return -1; default: - debug("%s: invalid peripheral %d", __func__, peripheral); + debug("%s: invalid peripheral %d\n", __func__, peripheral); return -1; } From 93c3404de9b2320bfd07cd3ce4ab0660f0d39523 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Mon, 1 Sep 2025 16:13:14 +0100 Subject: [PATCH 15/15] spi: exynos: Remove extra term from test In spi_rx_tx there comes a test for execution of a code block that allows execution if rxp is not NULL or stopping is true. However all the code in this block relies on rxp being valid so allowing entry just if stopping is true does not make sense. So remove this from the test expression leaving just a NULL check for rxp. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Signed-off-by: Minkyu Kang --- drivers/spi/exynos_spi.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/spi/exynos_spi.c b/drivers/spi/exynos_spi.c index 1b9bf004b7c..7f2965f8321 100644 --- a/drivers/spi/exynos_spi.c +++ b/drivers/spi/exynos_spi.c @@ -105,14 +105,10 @@ static int spi_rx_tx(struct exynos_spi_priv *priv, int todo, uint out_bytes, in_bytes; int toread; unsigned start = get_timer(0); - int stopping; int step; out_bytes = in_bytes = todo; - stopping = priv->skip_preamble && (flags & SPI_XFER_END) && - !(priv->mode & SPI_SLAVE); - /* * Try to transfer words if we can. This helps read performance at * SPI clock speeds above about 20MHz. @@ -161,12 +157,10 @@ static int spi_rx_tx(struct exynos_spi_priv *priv, int todo, while (rx_lvl >= step) { temp = readl(®s->rx_data); if (priv->skip_preamble) { - if (temp == SPI_PREAMBLE_END_BYTE) { + if (temp == SPI_PREAMBLE_END_BYTE) priv->skip_preamble = 0; - stopping = 0; - } } else { - if (rxp || stopping) { + if (rxp) { if (step == 4) *(uint32_t *)rxp = temp; else