From 5bad0bc4f7beeba8e972005fd0901cf368315815 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Thu, 17 Jul 2025 19:15:21 -0600 Subject: pci: Add missing to pcie_iproc.c This driver references the SZ_ macros while relying on an indirection inclusion of . Add the missing include directly. Signed-off-by: Tom Rini --- drivers/pci/pcie_iproc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pci/pcie_iproc.c b/drivers/pci/pcie_iproc.c index 360ef1b011f..a6ed0189178 100644 --- a/drivers/pci/pcie_iproc.c +++ b/drivers/pci/pcie_iproc.c @@ -13,6 +13,7 @@ #include #include #include +#include #define EP_PERST_SOURCE_SELECT_SHIFT 2 #define EP_PERST_SOURCE_SELECT BIT(EP_PERST_SOURCE_SELECT_SHIFT) -- cgit v1.2.3 From cb8e5727089b6069f314a2f232e105f7c2127dbb Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Thu, 17 Jul 2025 19:15:26 -0600 Subject: pci: Tighten some PCI controller dependencies A large number of PCI controllers cannot build without access to some platform specific header files. Express those requirements in Kconfig as well. Signed-off-by: Tom Rini --- drivers/pci/Kconfig | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 8ffd88c722d..b8568267ff8 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -183,6 +183,7 @@ config SYS_FSL_PCI_VER_3_X config PCIE_FSL bool "FSL PowerPC PCIe support" select SYS_FSL_PCI_VER_3_X if ARCH_T2080 || ARCH_T4240 + depends on PPC help Say Y here if you want to enable PCIe controller support on FSL PowerPC MPC85xx, MPC86xx, B series, P series and T series SoCs. @@ -190,6 +191,7 @@ config PCIE_FSL config PCI_MPC85XX bool "MPC85XX PowerPC PCI support" + depends on MPC85xx help Say Y here if you want to enable PCI controller support on FSL PowerPC MPC85xx SoC. @@ -265,6 +267,7 @@ config PCIE_LAYERSCAPE config PCIE_LAYERSCAPE_RC bool "Layerscape PCIe Root Complex mode support" + depends on ARM select PCIE_LAYERSCAPE help Enable Layerscape PCIe Root Complex mode driver support. The Layerscape @@ -286,6 +289,7 @@ config PCI_IOMMU_EXTRA_MAPPINGS config PCIE_LAYERSCAPE_EP bool "Layerscape PCIe Endpoint mode support" + depends on ARM select PCIE_LAYERSCAPE select PCI_ENDPOINT help @@ -296,6 +300,7 @@ config PCIE_LAYERSCAPE_EP config PCIE_LAYERSCAPE_GEN4 bool "Layerscape Gen4 PCIe support" + depends on ARM help Support PCIe Gen4 on NXP Layerscape SoCs, which may have one or several PCIe controllers. The PCIe controller can work in RC or -- cgit v1.2.3 From f0572603514c7a5286121ebd1b13ba1b450512e9 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Thu, 17 Jul 2025 19:15:30 -0600 Subject: nvme: Tighten requirements on NVME_APPLE driver This driver requires Apple rtkit headers in order to build. Express that requirement in Kconfig as well. Signed-off-by: Tom Rini --- drivers/nvme/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/nvme/Kconfig b/drivers/nvme/Kconfig index d138867666b..b69fc4ed53d 100644 --- a/drivers/nvme/Kconfig +++ b/drivers/nvme/Kconfig @@ -11,6 +11,7 @@ config NVME config NVME_APPLE bool "Apple NVMe controller support" + depends on ARCH_APPLE select NVME help This option enables support for the NVMe storage -- cgit v1.2.3 From 493c3da3ac530229ca4c4caadd5df041f6c25eb2 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Thu, 17 Jul 2025 19:15:52 -0600 Subject: sandbox: Add more dummy functions to mimic other architectures This adds more common functions found on other architectures that will allow for more compile-testing of drivers. These are either dummy functions as we do not need them or mappings to existing functions, similar to how other architectures handle it. Signed-off-by: Tom Rini --- drivers/i3c/master/dw-i3c-master.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index d96eb9b134e..0c4af7e528a 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -21,10 +21,6 @@ #include #include -#ifdef CONFIG_SANDBOX -#define cpu_relax() do {} while (0) -#endif - static u8 even_parity(u8 p) { p ^= p >> 4; -- cgit v1.2.3 From bd644d9613ee0cdc3bd2ac3bc7bcc52b09d821e7 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Fri, 25 Jul 2025 11:41:12 +0100 Subject: clk: cdce9xx: Fix use of dev_read_u32_default The function dev_read_u32_default does not return an error and the variable 'val' is unsigned so testing for >= 0 will always be true. It looks like the code was attempting to return -1 if xtal-load-pf was not present but that cannot work. Instead use dev_read_u32 which returns an error code separately from writing the value into the passed pointer. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Fixes: 260777fc2333 ("clk: cdce9xx: add support for cdce9xx clock synthesizer") Acked-by: Quentin Schulz --- drivers/clk/clk-cdce9xx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/clk-cdce9xx.c b/drivers/clk/clk-cdce9xx.c index e5f74e714d5..996cc30683e 100644 --- a/drivers/clk/clk-cdce9xx.c +++ b/drivers/clk/clk-cdce9xx.c @@ -116,8 +116,7 @@ static int cdce9xx_clk_probe(struct udevice *dev) ret = clk_get_by_index(dev, 0, &clk); data->xtal_rate = clk_get_rate(&clk); - val = dev_read_u32_default(dev, "xtal-load-pf", -1); - if (val >= 0) + if (!dev_read_u32(dev, "xtal-load-pf", &val)) cdce9xx_reg_write(dev, CDCE9XX_REG_XCSEL, val << 3); return 0; -- cgit v1.2.3 From 916f4337d1d3db4d16040abff39a5f4419589ead Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Fri, 25 Jul 2025 12:48:22 +0100 Subject: gpio: dwapb_gpio: Using wrong function to free memory In gpio_dwapb_bind plat is used to reference memory allocated by devm_kcalloc but it is attempted to be freed using kfree. Instead free this memory using the correct devm_kfree function. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Acked-by: Quentin Schulz --- drivers/gpio/dwapb_gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/dwapb_gpio.c b/drivers/gpio/dwapb_gpio.c index 04639a4cb68..7ab48780332 100644 --- a/drivers/gpio/dwapb_gpio.c +++ b/drivers/gpio/dwapb_gpio.c @@ -193,7 +193,7 @@ static int gpio_dwapb_bind(struct udevice *dev) ofnode_get_name(node)); plat->name = strdup(name); if (!plat->name) { - kfree(plat); + devm_kfree(dev, plat); return -ENOMEM; } } -- cgit v1.2.3 From 814ddd7824db7d43dbad3bf70154e0e692a00d1e Mon Sep 17 00:00:00 2001 From: Chali Anis Date: Tue, 29 Jul 2025 22:19:08 -0400 Subject: pinctrl: gpio: sx150x: fix compilation warnings. Fixes: 5451504256d3 ("pinctrl: gpio: sx150x: add Semtech SX150x I2C GPIO expander and pinctrl driver") Signed-off-by: Chali Anis --- drivers/pinctrl/pinctrl-sx150x.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-sx150x.c b/drivers/pinctrl/pinctrl-sx150x.c index 324d7af8fcd..cddde8822ed 100644 --- a/drivers/pinctrl/pinctrl-sx150x.c +++ b/drivers/pinctrl/pinctrl-sx150x.c @@ -7,7 +7,6 @@ * */ -#include #include #include #include @@ -22,6 +21,8 @@ #include #include #include +#include +#include #define err(format, arg...) printf("ERR:" format "\n", ##arg) #define dbg(format, arg...) printf("DBG:" format "\n", ##arg) @@ -413,7 +414,7 @@ static int sx150x_reg_read(struct sx150x_pinctrl_priv *pctl, unsigned int reg, { int ret, n; const int width = sx150x_reg_width(pctl, reg); - unsigned int idx, val; + unsigned int val, idx; /* * There are four potential cases covered by this function: @@ -444,8 +445,9 @@ static int sx150x_reg_read(struct sx150x_pinctrl_priv *pctl, unsigned int reg, * reg 3 [ 3 3 2 2 1 1 0 0 ] */ - for (n = width, val = 0, idx = reg; n > 0; n -= 8, idx) { + for (n = width, val = 0; n > 0; n -= 8) { val <<= 8; + idx = reg; ret = dm_i2c_reg_read(pctl->i2c, idx); if (ret < 0) @@ -475,7 +477,6 @@ static int sx150x_reg_write(struct sx150x_pinctrl_priv *pctl, unsigned int reg, if (ret < 0) return ret; - reg; n -= 8; } while (n >= 0); @@ -724,7 +725,7 @@ static const struct udevice_id sx150x_pinctrl_of_match[] = { {}, }; -static const struct pinconf_param sx150x_conf_params[] = { +static const struct pinconf_param __maybe_unused sx150x_conf_params[] = { { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 }, { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 }, { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 }, @@ -750,7 +751,7 @@ static const char *sx150x_pinctrl_get_pin_name(struct udevice *dev, return pin_name; } -static int sx150x_pinctrl_conf_set(struct udevice *dev, unsigned int pin, +static int __maybe_unused sx150x_pinctrl_conf_set(struct udevice *dev, unsigned int pin, unsigned int param, unsigned int arg) { int ret; @@ -834,13 +835,11 @@ static int sx150x_pinctrl_conf_set(struct udevice *dev, unsigned int pin, static int sx150x_pinctrl_bind(struct udevice *dev) { struct sx150x_pinctrl_priv *pctl = dev_get_plat(dev); - int ret, reg; + int ret; if (!dev_read_bool(dev, "gpio-controller")) return 0; - reg = (int)dev_read_addr_ptr(dev); - ret = device_bind(dev, &sx150x_gpio_driver, dev_read_name(dev), NULL, dev_ofnode(dev), &pctl->gpio); if (ret) @@ -861,7 +860,10 @@ static int sx150x_pinctrl_probe(struct udevice *dev) pctl->data = drv_data; - reg = (int)dev_read_addr_ptr(dev); + reg = dev_read_u32_default(dev, "reg", -ENODEV); + if (reg < 0) + return -ENODEV; + ret = dm_i2c_probe(dev->parent, reg, 0, &pctl->i2c); if (ret) { err("Cannot find I2C chip %02x (%d)", reg, ret); -- cgit v1.2.3 From 7807ed921314cd7af83fd88162d0b8c6fb20a9ca Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 30 Jul 2025 10:03:39 +0200 Subject: pinctrl: sx150x: reformat and fixup Copyright header The Linux pinctrl-sx150 was originally written as a GPIO driver and fully rewritten by me as a Pinctrl driver and extended by other contributors. Fixup the Copyright header style and correctly report the Copyright headers from the Linux driver. Signed-off-by: Neil Armstrong --- drivers/pinctrl/pinctrl-sx150x.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-sx150x.c b/drivers/pinctrl/pinctrl-sx150x.c index cddde8822ed..0d64f4d5ad6 100644 --- a/drivers/pinctrl/pinctrl-sx150x.c +++ b/drivers/pinctrl/pinctrl-sx150x.c @@ -3,8 +3,12 @@ * Copyright (C) 2024, Exfo Inc - All Rights Reserved * * Author: Anis CHALI - * inspired and adapted from linux driver of sx150x written by Gregory Bean - * + * + * Inspired and adapted from the Linux pinctrl-sx150x driver: + * Copyright (c) 2016, BayLibre, SAS. All rights reserved. + * Author: Neil Armstrong + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * Author: Gregory Bean */ #include -- cgit v1.2.3 From 4266b8a2839f4e3eee2d7ce5f61af0c0ed0fe3b9 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Mon, 4 Aug 2025 16:32:51 +0100 Subject: net: e1000: Free temporary buffer on exit In do_e1000_spi_checksum a temporary buffer is allocated but never freed. Add code to free on exit. Also refactor the code to make the exit code common. This issue found by Smatch. Signed-off-by: Andrew Goodbody --- drivers/net/e1000_spi.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/e1000_spi.c b/drivers/net/e1000_spi.c index 1e830b99f1d..33d38b57824 100644 --- a/drivers/net/e1000_spi.c +++ b/drivers/net/e1000_spi.c @@ -472,6 +472,7 @@ static int do_e1000_spi_checksum(struct cmd_tbl *cmdtp, struct e1000_hw *hw, uint16_t i, length, checksum = 0, checksum_reg; uint16_t *buffer; bool upd; + int ret = 0; if (argc == 0) upd = 0; @@ -493,14 +494,15 @@ static int do_e1000_spi_checksum(struct cmd_tbl *cmdtp, struct e1000_hw *hw, /* Acquire the EEPROM */ if (e1000_acquire_eeprom(hw)) { E1000_ERR(hw, "EEPROM SPI cannot be acquired!\n"); - return 1; + ret = 1; + goto free_exit; } /* Read the EEPROM */ if (e1000_spi_eeprom_dump(hw, buffer, 0, length, true) < 0) { E1000_ERR(hw, "Interrupted!\n"); - e1000_release_eeprom(hw); - return 1; + ret = 1; + goto release_exit; } /* Compute the checksum and read the expected value */ @@ -513,8 +515,8 @@ static int do_e1000_spi_checksum(struct cmd_tbl *cmdtp, struct e1000_hw *hw, if (checksum_reg == checksum) { printf("%s: INFO: EEPROM checksum is correct! (0x%04hx)\n", hw->name, checksum); - e1000_release_eeprom(hw); - return 0; + ret = 0; + goto release_exit; } /* Hrm, verification failed, print an error */ @@ -524,8 +526,8 @@ static int do_e1000_spi_checksum(struct cmd_tbl *cmdtp, struct e1000_hw *hw, /* If they didn't ask us to update it, just return an error */ if (!upd) { - e1000_release_eeprom(hw); - return 1; + ret = 1; + goto release_exit; } /* Ok, correct it! */ @@ -534,12 +536,15 @@ static int do_e1000_spi_checksum(struct cmd_tbl *cmdtp, struct e1000_hw *hw, if (e1000_spi_eeprom_program(hw, &buffer[i], i * sizeof(uint16_t), sizeof(uint16_t), true)) { E1000_ERR(hw, "Interrupted!\n"); - e1000_release_eeprom(hw); - return 1; + ret = 1; + /* goto release_exit; */ } +release_exit: e1000_release_eeprom(hw); - return 0; +free_exit: + free(buffer); + return ret; } int do_e1000_spi(struct cmd_tbl *cmdtp, struct e1000_hw *hw, -- cgit v1.2.3 From e2837ecddc244198e3ebeac9ddb18fd439b0131f Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 4 Aug 2025 15:51:11 -0600 Subject: sm: Rework the Kconfig logic here The symbol "SM" is a library symbol and should not be prompted for. It should be selected by the drivers that use it. In this case we need to add a SANDBOX_SM symbol for the sandbox driver. The meson SM driver cannot build on other platforms, so add the appropriate dependency. Reviewed-by: Peter Robinson Signed-off-by: Tom Rini --- drivers/sm/Kconfig | 11 ++++++++++- drivers/sm/Makefile | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/sm/Kconfig b/drivers/sm/Kconfig index 926af286330..8e500ed2ede 100644 --- a/drivers/sm/Kconfig +++ b/drivers/sm/Kconfig @@ -1,8 +1,17 @@ config SM - bool "Enable Secure Monitor driver support" + bool config MESON_SM bool "Amlogic Secure Monitor driver" + depends on ARCH_MESON select SM help Say y here to enable the Amlogic secure monitor driver. + +config SANDBOX_SM + bool "Sandbox Secure Monitor driver" + depends on SANDBOX + select SM + help + Say y here to enable the Sandbox driver for the secure monitor + uclass. diff --git a/drivers/sm/Makefile b/drivers/sm/Makefile index da81ee898ab..5ac947350bd 100644 --- a/drivers/sm/Makefile +++ b/drivers/sm/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-y += sm-uclass.o -obj-$(CONFIG_SANDBOX) += sandbox-sm.o +obj-$(CONFIG_SM) += sm-uclass.o +obj-$(CONFIG_SANDBOX_SM) += sandbox-sm.o obj-$(CONFIG_MESON_SM) += meson-sm.o -- cgit v1.2.3 From e7a95ee2b575d7f0f44da3b3aeb951508eac1511 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 4 Aug 2025 15:52:53 -0600 Subject: pinctrl: Tighten some pinctrl driver dependencies A few pinctrl drivers cannot build without access to some platform specific header files. Express those requirements in Kconfig as well. Signed-off-by: Tom Rini --- drivers/pinctrl/Kconfig | 7 ++++--- drivers/pinctrl/tegra/Kconfig | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 5e2808abc8a..48119694031 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -197,7 +197,7 @@ config PINCTRL_AR933X config PINCTRL_AT91 bool "AT91 pinctrl driver" - depends on DM + depends on DM && ARCH_AT91 help This option is to enable the AT91 pinctrl driver for AT91 PIO controller. @@ -213,13 +213,14 @@ config PINCTRL_AT91 config PINCTRL_AT91PIO4 bool "AT91 PIO4 pinctrl driver" - depends on DM + depends on DM && ARCH_AT91 help This option is to enable the AT91 pinctrl driver for AT91 PIO4 controller which is available on SAMA5D2 SoC. config PINCTRL_INTEL bool "Standard Intel pin-control and pin-mux driver" + depends on X86 help Recent Intel chips such as Apollo Lake (APL) use a common pin control and GPIO scheme. The settings for this come from an SoC-specific @@ -248,7 +249,7 @@ config PINCTRL_QCA953X config PINCTRL_QE bool "QE based pinctrl driver, like on mpc83xx" - depends on DM + depends on DM && MPC83xx help This option is to enable the QE pinctrl driver for QE based io controller. diff --git a/drivers/pinctrl/tegra/Kconfig b/drivers/pinctrl/tegra/Kconfig index 669d8e258e4..d2630289d75 100644 --- a/drivers/pinctrl/tegra/Kconfig +++ b/drivers/pinctrl/tegra/Kconfig @@ -2,7 +2,7 @@ config PINCTRL_TEGRA bool "Nvidia Tegra pinctrl driver" - depends on DM + depends on DM && ARCH_TEGRA help Support pin multiplexing control on Nvidia Tegra SoCs. The driver is an overlay to existing driver and allows @@ -11,7 +11,7 @@ config PINCTRL_TEGRA config SPL_PINCTRL_TEGRA bool "Nvidia Tegra SPL pinctrl driver" - depends on SPL_PINCTRL + depends on SPL_PINCTRL && ARCH_TEGRA help Enables support of pre-DM version of pin multiplexing control driver used on SPL stage for board setup and -- cgit v1.2.3 From 678be99d563199551ed57fd771471eb1d2680e6e Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 4 Aug 2025 15:53:53 -0600 Subject: pwm: Tighten some pwm driver dependencies A few pwm drivers cannot build without access to some platform specific header files. Express those requirements in Kconfig as well. Signed-off-by: Tom Rini --- drivers/pwm/Kconfig | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index e4c676d75c2..06f42f699de 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -42,7 +42,7 @@ config PWM_CROS_EC config PWM_EXYNOS bool "Enable support for the Exynos PWM" - depends on DM_PWM + depends on DM_PWM && ARCH_EXYNOS help This PWM is found on Samsung Exynos 5250 and other Samsung SoCs. It supports a programmable period and duty cycle. A 32-bit counter is @@ -51,6 +51,7 @@ config PWM_EXYNOS config PWM_IMX bool "Enable support for i.MX27 and later PWM" + depends on MACH_IMX help This PWM is found i.MX27 and later i.MX SoCs. @@ -70,7 +71,7 @@ config PWM_MTK config PWM_ROCKCHIP bool "Enable support for the Rockchip PWM" - depends on DM_PWM + depends on DM_PWM && ARCH_ROCKCHIP help This PWM is found on RK3288 and other Rockchip SoCs. It supports a programmable period and duty cycle. A 32-bit counter is used. @@ -98,7 +99,7 @@ config PWM_SIFIVE config PWM_TEGRA bool "Enable support for the Tegra PWM" - depends on DM_PWM + depends on DM_PWM && ARCH_TEGRA help This PWM is found on Tegra 20 and other Nvidia SoCs. It supports four channels with a programmable period and duty cycle. Only a @@ -115,7 +116,7 @@ config PWM_STM32 config PWM_SUNXI bool "Enable support for the Allwinner Sunxi PWM" - depends on DM_PWM + depends on DM_PWM && ARCH_SUNXI help This PWM is found on H3, A64 and other Allwinner SoCs. It supports a programmable period and duty cycle. A 16-bit counter is used. -- cgit v1.2.3 From 00ad9ed4a775a6908a8654935f61df38e59178df Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 4 Aug 2025 15:53:54 -0600 Subject: pwm: pwm-aspeed: Add missing to pwm-aspeed.c This driver references the logarithmic macros while relying on an indirection inclusion of . Add the missing include directly. Signed-off-by: Tom Rini --- drivers/pwm/pwm-aspeed.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pwm/pwm-aspeed.c b/drivers/pwm/pwm-aspeed.c index ebc9d9a8975..eefe018448f 100644 --- a/drivers/pwm/pwm-aspeed.c +++ b/drivers/pwm/pwm-aspeed.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From 0ae7725e874a65d86ccdc964048940a490185972 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 4 Aug 2025 15:54:25 -0600 Subject: reset: Tighten some reset driver dependencies A few reset drivers cannot build without access to some platform specific header files. Express those requirements in Kconfig as well. Signed-off-by: Tom Rini --- drivers/reset/Kconfig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index e92bb8a7c39..74c267dfc4e 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -74,8 +74,8 @@ config RESET_UNIPHIER config RESET_AST2500 bool "Reset controller driver for AST2500 SoCs" - depends on DM_RESET - default y if ASPEED_AST2500 + depends on DM_RESET && ASPEED_AST2500 + default y help Support for reset controller on AST2500 SoC. Say Y if you want to control reset signals of different peripherals @@ -83,8 +83,8 @@ config RESET_AST2500 config RESET_AST2600 bool "Reset controller driver for AST2600 SoCs" - depends on DM_RESET - default y if ASPEED_AST2600 + depends on DM_RESET && ASPEED_AST2600 + default y help Support for reset controller on AST2600 SoC. Say Y if you want to control reset signals of different peripherals @@ -223,7 +223,7 @@ config RESET_ZYNQMP config RESET_DRA7 bool "Support for TI's DRA7 Reset driver" - depends on DM_RESET + depends on DM_RESET && ARCH_OMAP2PLUS help Support for TI DRA7-RESET subsystem. Basic Assert/Deassert is supported. -- cgit v1.2.3 From 548ae9d1a537464254afac63f802179a24c645d6 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 4 Aug 2025 15:54:24 -0600 Subject: remoteproc: Tighten some remoteproc driver dependencies The TI IPU remoteproc driver cannot build without access to some platform specific header files. Express that requirements in Kconfig as well. Signed-off-by: Tom Rini --- drivers/remoteproc/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index a3ecea05e20..e9f19a69433 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -125,6 +125,7 @@ config REMOTEPROC_TI_PRU config REMOTEPROC_TI_IPU bool "Support for TI's K3 based IPU remoteproc driver" select REMOTEPROC + depends on ARCH_K3 depends on DM depends on SPL_DRIVERS_MISC depends on SPL_FS_LOADER -- cgit v1.2.3 From 01bc65a0e9b2e0bac7af28bf102ee0df4f98b205 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 4 Aug 2025 15:57:15 -0600 Subject: soc: Tighten some soc driver dependencies The Qualcomm Snapdragon "SoC" driver cannot build without access to some ARM64 specific functionality. Express that requirements in Kconfig as well. Reviewed-by: Casey Connolly Signed-off-by: Tom Rini --- drivers/soc/qcom/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 4aa7833930c..8243805e46a 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -4,6 +4,7 @@ # menuconfig SOC_QCOM bool "Qualcomm SOC drivers support" + depends on ARM64 help Say Y here if you want to enable Qualcomm SOC drivers support. -- cgit v1.2.3 From 8b0eac68e5cb7b5bc55a4955672159c337f6afa2 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 4 Aug 2025 15:57:16 -0600 Subject: sound: Tighten some sound driver dependencies A few sound drivers cannot build without access to some platform specific header files. Express those requirements in Kconfig as well. Reviewed-by: Peter Robinson Signed-off-by: Tom Rini --- drivers/sound/Kconfig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/sound/Kconfig b/drivers/sound/Kconfig index 0948d8caab0..81de9b30f39 100644 --- a/drivers/sound/Kconfig +++ b/drivers/sound/Kconfig @@ -23,7 +23,7 @@ config I2S config I2S_ROCKCHIP bool "Enable I2S support for Rockchip SoCs" - depends on I2S + depends on I2S && ARCH_ROCKCHIP help Rockchip SoCs support an I2S interface for sending audio data to an audio codec. This option enables support for this, using one of the @@ -32,7 +32,7 @@ config I2S_ROCKCHIP config I2S_SAMSUNG bool "Enable I2C support for Samsung SoCs" - depends on I2S + depends on I2S && ARCH_EXYNOS help Samsung Exynos SoCs support an I2S interface for sending audio data to an audio codec. This option enables support for this, @@ -51,7 +51,7 @@ config SOUND_DA7219 config SOUND_I8254 bool "Intel i8254 timer / beeper" - depends on SOUND + depends on SOUND && X86 help This enables support for a beeper that uses the i8254 timer chip. This can emit beeps at a fixed frequency. It is possible to control @@ -82,7 +82,7 @@ config SOUND_IVYBRIDGE config I2S_TEGRA bool "Enable I2S support for Nvidia Tegra SoCs" - depends on I2S + depends on I2S && ARCH_TEGRA select TEGRA124_DMA help Nvidia Tegra SoCs support several I2S interfaces for sending audio -- cgit v1.2.3 From 38931172edd71c27101ac19361819d945ae337d8 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 4 Aug 2025 15:57:19 -0600 Subject: sysreset: Tighten some sysreset driver dependencies The MPC83xx sysreset driver cannot build without access to some architecture specific header files. Express that requirements in Kconfig as well. Reviewed-by: Peter Robinson Signed-off-by: Tom Rini --- drivers/sysreset/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig index 67edf004205..0181f6cd581 100644 --- a/drivers/sysreset/Kconfig +++ b/drivers/sysreset/Kconfig @@ -283,6 +283,7 @@ config SYSRESET_TPL_X86 config SYSRESET_MPC83XX bool "Enable support MPC83xx SoC family reboot driver" + depends on PPC help Reboot support for NXP MPC83xx SoCs. -- cgit v1.2.3 From b86ace06d37bb2ef97dbd79723089f3b46e065a2 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 4 Aug 2025 15:57:10 -0600 Subject: rtc: Tighten some rtc driver dependencies The Marvell RTC rtc driver cannot build without access to some platform specific header files. Express that requirements in Kconfig as well. Signed-off-by: Tom Rini --- drivers/rtc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 79b879d68d1..ed903999f06 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -218,7 +218,7 @@ config RTC_PL031 config RTC_MV bool "Enable Marvell RTC driver" - depends on DM_RTC + depends on DM_RTC && ARCH_KIRKWOOD help Enable Marvell RTC driver. This driver supports the rtc that is present on some Marvell SoCs. -- cgit v1.2.3 From d7daa9274b1f7fad8f24c6020dc6225a1585de2f Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 4 Aug 2025 15:57:11 -0600 Subject: rtc: mc146818: Fix building on more architectures This driver makes calls to in8/out8(). On PowerPC these are separate and real calls but elsewhere they are able to simply be wrappers to inb/outb. Rework this logic to be able to build this driver on more platforms. Signed-off-by: Tom Rini --- drivers/rtc/mc146818.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/mc146818.c b/drivers/rtc/mc146818.c index c0d86c6d063..08bc528aeaf 100644 --- a/drivers/rtc/mc146818.c +++ b/drivers/rtc/mc146818.c @@ -11,9 +11,9 @@ #include #include #include - -#if defined(CONFIG_X86) || defined(CONFIG_TARGET_MALTA) #include + +#if !defined(CONFIG_PPC) #define in8(p) inb(p) #define out8(p, v) outb(v, p) #endif -- cgit v1.2.3 From 91122ea8f03b59b1fef7b9861734f6278ca4a123 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 4 Aug 2025 15:57:12 -0600 Subject: rtc: pl031: Correct function type of pl031_write_reg When calling writel we do not have a return value to check or pass along. This function should therefore be void and not return what writel gives us. Signed-off-by: Tom Rini --- drivers/rtc/pl031.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/pl031.c b/drivers/rtc/pl031.c index 855ee913416..6f189bc503e 100644 --- a/drivers/rtc/pl031.c +++ b/drivers/rtc/pl031.c @@ -39,11 +39,11 @@ static inline u32 pl031_read_reg(struct udevice *dev, int reg) return readl(pdata->base + reg); } -static inline u32 pl031_write_reg(struct udevice *dev, int reg, u32 value) +static inline void pl031_write_reg(struct udevice *dev, int reg, u32 value) { struct pl031_plat *pdata = dev_get_plat(dev); - return writel(value, pdata->base + reg); + writel(value, pdata->base + reg); } /* -- cgit v1.2.3 From 37f0a8b8cb032f453487f54a575eb0701f5103e7 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 4 Aug 2025 15:57:13 -0600 Subject: serial: Tighten some serial driver dependencies A few serial drivers cannot build without access to some platform specific header files. Express those requirements in Kconfig as well. Signed-off-by: Tom Rini --- drivers/serial/Kconfig | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 1f2f2468eb0..d7d59e02dd0 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -665,6 +665,7 @@ config ARM_DCC config ATMEL_USART bool "Atmel USART support" + depends on ARCH_AT91 help Select this to enable USART support for Atmel SoCs. It can be configured in the device tree, and input clock frequency can @@ -703,8 +704,8 @@ config BCM6345_SERIAL config COREBOOT_SERIAL bool "Coreboot UART support" - depends on DM_SERIAL - default y if SYS_COREBOOT + depends on DM_SERIAL && SYS_COREBOOT + default y select SYS_NS16550 help Select this to enable a ns16550-style UART where the platform data @@ -724,7 +725,7 @@ config COREBOOT_SERIAL_FROM_DBG2 config CORTINA_UART bool "Cortina UART support" - depends on DM_SERIAL + depends on DM_SERIAL && TARGET_PRESIDIO_ASIC help Select this to enable UART support for Cortina-Access UART devices found on CAxxxx SoCs. @@ -738,6 +739,7 @@ config FSL_LINFLEXUART config FSL_LPUART bool "Freescale LPUART support" + depends on MACH_IMX || ARCH_LS1021A || ARCH_LS1028A help Select this to enable a Low Power UART for Freescale VF610 and QorIQ Layerscape devices. @@ -748,12 +750,14 @@ config LPUART config MVEBU_A3700_UART bool "UART support for Armada 3700" + depends on ARCH_MVEBU help Choose this option to add support for UART driver on the Marvell Armada 3700 SoC. The base address is configured via DT. config MCFUART bool "Freescale ColdFire UART support" + depends on M68K help Choose this option to add support for UART driver on the ColdFire SoC's family. The serial communication channel provides a full-duplex @@ -879,7 +883,7 @@ config PL01X_SERIAL config ROCKCHIP_SERIAL bool "Rockchip on-chip UART support" - depends on DM_SERIAL + depends on DM_SERIAL && ARCH_ROCKCHIP select SYS_NS16550 help Select this to enable a debug UART for Rockchip devices when using @@ -1110,7 +1114,7 @@ config MTK_SERIAL config MT7620_SERIAL bool "UART driver for MediaTek MT7620 and earlier SoCs" - depends on DM_SERIAL + depends on DM_SERIAL && SOC_MT7620 help Select this to enable UART support for MediaTek MT7620 and earlier SoCs. This driver uses driver model and requires a device tree -- cgit v1.2.3 From 546be69f05817ac3a16dddc847ee59c4342e7709 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 4 Aug 2025 15:57:14 -0600 Subject: serial: linflexuart: Remove unused driver This driver is unused. Remove it. Signed-off-by: Tom Rini --- drivers/serial/Kconfig | 7 -- drivers/serial/Makefile | 1 - drivers/serial/serial_linflexuart.c | 216 ------------------------------------ 3 files changed, 224 deletions(-) delete mode 100644 drivers/serial/serial_linflexuart.c (limited to 'drivers') diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index d7d59e02dd0..bc05d2f1508 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -730,13 +730,6 @@ config CORTINA_UART Select this to enable UART support for Cortina-Access UART devices found on CAxxxx SoCs. -config FSL_LINFLEXUART - bool "Freescale Linflex UART support" - depends on DM_SERIAL - help - Select this to enable the Linflex serial module found on some - NXP SoCs like S32V234. - config FSL_LPUART bool "Freescale LPUART support" depends on MACH_IMX || ARCH_LS1021A || ARCH_LS1028A diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index c32e3fcd439..8eaae62b0fc 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -36,7 +36,6 @@ obj-$(CONFIG_SCIF_CONSOLE) += serial_sh.o obj-$(CONFIG_SEMIHOSTING_SERIAL) += serial_semihosting.o obj-$(CONFIG_ZYNQ_SERIAL) += serial_zynq.o obj-$(CONFIG_FSL_LPUART) += serial_lpuart.o -obj-$(CONFIG_FSL_LINFLEXUART) += serial_linflexuart.o obj-$(CONFIG_ARC_SERIAL) += serial_arc.o obj-$(CONFIG_UNIPHIER_SERIAL) += serial_uniphier.o obj-$(CONFIG_STM32_SERIAL) += serial_stm32.o diff --git a/drivers/serial/serial_linflexuart.c b/drivers/serial/serial_linflexuart.c deleted file mode 100644 index 24ecb236d51..00000000000 --- a/drivers/serial/serial_linflexuart.c +++ /dev/null @@ -1,216 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2013-2016 Freescale Semiconductor, Inc. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define US1_TDRE (1 << 7) -#define US1_RDRF (1 << 5) -#define UC2_TE (1 << 3) -#define LINCR1_INIT (1 << 0) -#define LINCR1_MME (1 << 4) -#define LINCR1_BF (1 << 7) -#define LINSR_LINS_INITMODE (0x00001000) -#define LINSR_LINS_MASK (0x0000F000) -#define UARTCR_UART (1 << 0) -#define UARTCR_WL0 (1 << 1) -#define UARTCR_PCE (1 << 2) -#define UARTCR_PC0 (1 << 3) -#define UARTCR_TXEN (1 << 4) -#define UARTCR_RXEN (1 << 5) -#define UARTCR_PC1 (1 << 6) -#define UARTSR_DTF (1 << 1) -#define UARTSR_DRF (1 << 2) -#define UARTSR_RMB (1 << 9) - -DECLARE_GLOBAL_DATA_PTR; - -static void _linflex_serial_setbrg(struct linflex_fsl *base, int baudrate) -{ - u32 clk = mxc_get_clock(MXC_UART_CLK); - u32 ibr, fbr; - - if (!baudrate) - baudrate = CONFIG_BAUDRATE; - - ibr = (u32) (clk / (16 * gd->baudrate)); - fbr = (u32) (clk % (16 * gd->baudrate)) * 16; - - __raw_writel(ibr, &base->linibrr); - __raw_writel(fbr, &base->linfbrr); -} - -static int _linflex_serial_getc(struct linflex_fsl *base) -{ - char c; - - if (!(__raw_readb(&base->uartsr) & UARTSR_DRF)) - return -EAGAIN; - - if (!(__raw_readl(&base->uartsr) & UARTSR_RMB)) - return -EAGAIN; - - c = __raw_readl(&base->bdrm); - __raw_writeb((__raw_readb(&base->uartsr) | (UARTSR_DRF | UARTSR_RMB)), - &base->uartsr); - return c; -} - -static int _linflex_serial_putc(struct linflex_fsl *base, const char c) -{ - __raw_writeb(c, &base->bdrl); - - if (!(__raw_readb(&base->uartsr) & UARTSR_DTF)) - return -EAGAIN; - - __raw_writeb((__raw_readb(&base->uartsr) | UARTSR_DTF), &base->uartsr); - - return 0; -} - -/* - * Initialise the serial port with the given baudrate. The settings - * are always 8 data bits, no parity, 1 stop bit, no start bits. - */ -static int _linflex_serial_init(struct linflex_fsl *base) -{ - volatile u32 ctrl; - - /* set the Linflex in master mode amd activate by-pass filter */ - ctrl = LINCR1_BF | LINCR1_MME; - __raw_writel(ctrl, &base->lincr1); - - /* init mode */ - ctrl |= LINCR1_INIT; - __raw_writel(ctrl, &base->lincr1); - - /* waiting for init mode entry - TODO: add a timeout */ - while ((__raw_readl(&base->linsr) & LINSR_LINS_MASK) != - LINSR_LINS_INITMODE); - - /* set UART bit to allow writing other bits */ - __raw_writel(UARTCR_UART, &base->uartcr); - - /* provide data bits, parity, stop bit, etc */ - serial_setbrg(); - - /* 8 bit data, no parity, Tx and Rx enabled, UART mode */ - __raw_writel(UARTCR_PC1 | UARTCR_RXEN | UARTCR_TXEN | UARTCR_PC0 - | UARTCR_WL0 | UARTCR_UART, &base->uartcr); - - ctrl = __raw_readl(&base->lincr1); - ctrl &= ~LINCR1_INIT; - __raw_writel(ctrl, &base->lincr1); /* end init mode */ - - return 0; -} - -struct linflex_serial_plat { - struct linflex_fsl *base_addr; - u8 port_id; /* do we need this? */ -}; - -struct linflex_serial_priv { - struct linflex_fsl *lfuart; -}; - -int linflex_serial_setbrg(struct udevice *dev, int baudrate) -{ - struct linflex_serial_priv *priv = dev_get_priv(dev); - - _linflex_serial_setbrg(priv->lfuart, baudrate); - - return 0; -} - -static int linflex_serial_getc(struct udevice *dev) -{ - struct linflex_serial_priv *priv = dev_get_priv(dev); - - return _linflex_serial_getc(priv->lfuart); -} - -static int linflex_serial_putc(struct udevice *dev, const char ch) -{ - - struct linflex_serial_priv *priv = dev_get_priv(dev); - - return _linflex_serial_putc(priv->lfuart, ch); -} - -static int linflex_serial_pending(struct udevice *dev, bool input) -{ - struct linflex_serial_priv *priv = dev_get_priv(dev); - uint32_t uartsr = __raw_readl(&priv->lfuart->uartsr); - - if (input) - return ((uartsr & UARTSR_DRF) && (uartsr & UARTSR_RMB)) ? 1 : 0; - else - return uartsr & UARTSR_DTF ? 0 : 1; -} - -static void linflex_serial_init_internal(struct linflex_fsl *lfuart) -{ - _linflex_serial_init(lfuart); - _linflex_serial_setbrg(lfuart, CONFIG_BAUDRATE); - return; -} - -static int linflex_serial_probe(struct udevice *dev) -{ - struct linflex_serial_plat *plat = dev_get_plat(dev); - struct linflex_serial_priv *priv = dev_get_priv(dev); - - priv->lfuart = (struct linflex_fsl *)plat->base_addr; - linflex_serial_init_internal(priv->lfuart); - - return 0; -} - -static const struct dm_serial_ops linflex_serial_ops = { - .putc = linflex_serial_putc, - .pending = linflex_serial_pending, - .getc = linflex_serial_getc, - .setbrg = linflex_serial_setbrg, -}; - -U_BOOT_DRIVER(serial_linflex) = { - .name = "serial_linflex", - .id = UCLASS_SERIAL, - .probe = linflex_serial_probe, - .ops = &linflex_serial_ops, - .flags = DM_FLAG_PRE_RELOC, - .priv_auto = sizeof(struct linflex_serial_priv), -}; - -#ifdef CONFIG_DEBUG_UART_LINFLEXUART - -#include - -static inline void _debug_uart_init(void) -{ - struct linflex_fsl *base = (struct linflex_fsl *)CONFIG_VAL(DEBUG_UART_BASE); - - linflex_serial_init_internal(base); -} - -static inline void _debug_uart_putc(int ch) -{ - struct linflex_fsl *base = (struct linflex_fsl *)CONFIG_VAL(DEBUG_UART_BASE); - - /* XXX: Is this OK? Should this use the non-DM version? */ - _linflex_serial_putc(base, ch); -} - -DEBUG_UART_FUNCS - -#endif /* CONFIG_DEBUG_UART_LINFLEXUART */ -- cgit v1.2.3 From c39a8001ca862fbb676ddb788bc86f22c243bb88 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 4 Aug 2025 15:54:23 -0600 Subject: ram: Tighten some ram driver dependencies A few ram drivers cannot build without access to some platform specific header files. Express those requirements in Kconfig as well. Signed-off-by: Tom Rini --- drivers/ram/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig index edb8e254d5b..d6964497309 100644 --- a/drivers/ram/Kconfig +++ b/drivers/ram/Kconfig @@ -39,7 +39,7 @@ config VPL_RAM config STM32_SDRAM bool "Enable STM32 SDRAM support" - depends on RAM + depends on RAM && ARCH_STM32 help STM32F7 family devices support flexible memory controller(FMC) to support external memories like sdram, psram & nand. @@ -47,7 +47,7 @@ config STM32_SDRAM config MPC83XX_SDRAM bool "Enable MPC83XX SDRAM support" - depends on RAM + depends on RAM && MPC83xx help Enable support for the internal DDR Memory Controller of the MPC83xx family of SoCs. Both static configurations, as well as configuring @@ -67,7 +67,7 @@ config K3_AM654_DDRSS config K3_DDRSS bool "Enable K3 DDRSS support" - depends on RAM + depends on RAM && ARCH_K3 choice depends on K3_DDRSS -- cgit v1.2.3 From 145a6f447e47f679218f8886c06622b19f4e22a6 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Tue, 5 Aug 2025 17:10:26 +0100 Subject: net: phy: cortina: Ensure memory allocated is freed In cs4340_upload_firmware a buffer is allocated with malloc but this is never freed. The pointer to this buffer, addr, is not even kept unchanged. But in some cases addr is not a buffer allocated by malloc. Introduce the use of another pointer to keep track of the buffer and to know if it needs to be freed. This issue was found by Smatch. Signed-off-by: Andrew Goodbody --- drivers/net/phy/cortina.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/net/phy/cortina.c b/drivers/net/phy/cortina.c index be480ecef6c..dec024844b5 100644 --- a/drivers/net/phy/cortina.c +++ b/drivers/net/phy/cortina.c @@ -135,6 +135,7 @@ void cs4340_upload_firmware(struct phy_device *phydev) int i, line_cnt = 0, column_cnt = 0; struct cortina_reg_config fw_temp; char *addr = NULL; + char *to_be_freed = NULL; ulong cortina_fw_addr = (ulong)cs4340_get_fw_addr(); #ifdef CONFIG_TFABOOT @@ -147,6 +148,7 @@ void cs4340_upload_firmware(struct phy_device *phydev) size_t fw_length = CONFIG_CORTINA_FW_LENGTH; addr = malloc(CONFIG_CORTINA_FW_LENGTH); + to_be_freed = addr; ret = nand_read(get_nand_dev_by_index(0), (loff_t)cortina_fw_addr, &fw_length, (u_char *)addr); if (ret == -EUCLEAN) { @@ -158,6 +160,7 @@ void cs4340_upload_firmware(struct phy_device *phydev) struct spi_flash *ucode_flash; addr = malloc(CONFIG_CORTINA_FW_LENGTH); + to_be_freed = addr; ucode_flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, CONFIG_SF_DEFAULT_CS, CONFIG_SF_DEFAULT_SPEED, CONFIG_SF_DEFAULT_MODE); if (!ucode_flash) { @@ -179,6 +182,7 @@ void cs4340_upload_firmware(struct phy_device *phydev) puts("Failed to find MMC device for Cortina ucode\n"); } else { addr = malloc(CONFIG_CORTINA_FW_LENGTH); + to_be_freed = addr; printf("MMC read: dev # %u, block # %u, count %u ...\n", dev, blk, cnt); mmc_init(mmc); @@ -199,6 +203,7 @@ void cs4340_upload_firmware(struct phy_device *phydev) size_t fw_length = CONFIG_CORTINA_FW_LENGTH; addr = malloc(CONFIG_CORTINA_FW_LENGTH); + to_be_freed = addr; ret = nand_read(get_nand_dev_by_index(0), (loff_t)cortina_fw_addr, &fw_length, (u_char *)addr); @@ -211,6 +216,7 @@ void cs4340_upload_firmware(struct phy_device *phydev) struct spi_flash *ucode_flash; addr = malloc(CONFIG_CORTINA_FW_LENGTH); + to_be_freed = addr; ucode_flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, CONFIG_SF_DEFAULT_CS, CONFIG_SF_DEFAULT_SPEED, CONFIG_SF_DEFAULT_MODE); if (!ucode_flash) { @@ -232,6 +238,7 @@ void cs4340_upload_firmware(struct phy_device *phydev) puts("Failed to find MMC device for Cortina ucode\n"); } else { addr = malloc(CONFIG_CORTINA_FW_LENGTH); + to_be_freed = addr; printf("MMC read: dev # %u, block # %u, count %u ...\n", dev, blk, cnt); mmc_init(mmc); @@ -280,6 +287,8 @@ void cs4340_upload_firmware(struct phy_device *phydev) 0xffff; phy_write(phydev, 0x00, fw_temp.reg_addr, fw_temp.reg_value); } + if (to_be_freed) + free(to_be_freed); } #endif -- cgit v1.2.3 From 1edd1fd539f52cadcb57c0c79c504f46f9540045 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Wed, 6 Aug 2025 11:43:47 +0100 Subject: net: ti: am65-cpsw-nuss: Initialise ret In am65_cpsw_phy_init it is not certain that ret will be assigned to before it reaches the 'return ret' statement. Initialise ret to ensure that ret is valid. This issue was found by Smatch. Signed-off-by: Andrew Goodbody --- drivers/net/ti/am65-cpsw-nuss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ti/am65-cpsw-nuss.c b/drivers/net/ti/am65-cpsw-nuss.c index 9b69f36d04d..723dde284fb 100644 --- a/drivers/net/ti/am65-cpsw-nuss.c +++ b/drivers/net/ti/am65-cpsw-nuss.c @@ -628,7 +628,7 @@ static int am65_cpsw_phy_init(struct udevice *dev) struct eth_pdata *pdata = dev_get_plat(dev); struct phy_device *phydev; u32 supported = PHY_GBIT_FEATURES; - int ret; + int ret = 0; phydev = dm_eth_phy_connect(dev); if (!phydev) { -- cgit v1.2.3 From 2a61c56dea1d7860d4a44afad3f54814ff0b805b Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Wed, 6 Aug 2025 11:56:58 +0100 Subject: net: ti: icssg: Remove impossible test port_id is an unsigned variable so cannot be negative. Remove the test checking for port_id being less than 0. This issue was found by Smatch. Signed-off-by: Andrew Goodbody --- drivers/net/ti/icssg_prueth.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ti/icssg_prueth.c b/drivers/net/ti/icssg_prueth.c index d8df3c9afb0..12a162b9d68 100644 --- a/drivers/net/ti/icssg_prueth.c +++ b/drivers/net/ti/icssg_prueth.c @@ -647,8 +647,6 @@ static int prueth_probe(struct udevice *dev) return -EINVAL; } - if (port_id < 0) - continue; if (disabled) continue; -- cgit v1.2.3 From cb76b208390d98658fc8e97c28dc0080a65c63bc Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Wed, 6 Aug 2025 08:55:02 -0600 Subject: timer: fttmr010_timer: Remove unused driver This driver is unused. Remove it. Signed-off-by: Tom Rini --- drivers/timer/Kconfig | 7 ---- drivers/timer/Makefile | 1 - drivers/timer/fttmr010_timer.c | 91 ------------------------------------------ 3 files changed, 99 deletions(-) delete mode 100644 drivers/timer/fttmr010_timer.c (limited to 'drivers') diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index cb6fc0e7fda..d3231441b13 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -166,13 +166,6 @@ config DESIGNWARE_APB_TIMER Enables support for the Designware APB Timer driver. This timer is present on Altera SoCFPGA SoCs. -config FTTMR010_TIMER - bool "Faraday Technology timer support" - depends on TIMER - help - Select this to enable support for the timer found on - devices using Faraday Technology's IP. - config GXP_TIMER bool "HPE GXP Timer" depends on TIMER diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile index 21db0d317fe..a72e411fb2f 100644 --- a/drivers/timer/Makefile +++ b/drivers/timer/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_$(PHASE_)ATMEL_PIT_TIMER) += atmel_pit_timer.o obj-$(CONFIG_$(PHASE_)ATMEL_TCB_TIMER) += atmel_tcb_timer.o obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence-ttc.o obj-$(CONFIG_DESIGNWARE_APB_TIMER) += dw-apb-timer.o -obj-$(CONFIG_FTTMR010_TIMER) += fttmr010_timer.o obj-$(CONFIG_GXP_TIMER) += gxp-timer.o obj-$(CONFIG_MPC83XX_TIMER) += mpc83xx_timer.o obj-$(CONFIG_NOMADIK_MTU_TIMER) += nomadik-mtu-timer.o diff --git a/drivers/timer/fttmr010_timer.c b/drivers/timer/fttmr010_timer.c deleted file mode 100644 index c41bbfc1d57..00000000000 --- a/drivers/timer/fttmr010_timer.c +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2009 Faraday Technology - * Po-Yu Chuang - * - * 23/08/2022 Port to DM - */ -#include -#include -#include -#include -#include -#include -#include - -#define TIMER_LOAD_VAL 0xffffffff - -struct fttmr010_timer_priv { - struct fttmr010 __iomem *regs; -}; - -static u64 fttmr010_timer_get_count(struct udevice *dev) -{ - struct fttmr010_timer_priv *priv = dev_get_priv(dev); - struct fttmr010 *tmr = priv->regs; - u32 now = TIMER_LOAD_VAL - readl(&tmr->timer3_counter); - - /* increment tbu if tbl has rolled over */ - if (now < gd->arch.tbl) - gd->arch.tbu++; - gd->arch.tbl = now; - - return ((u64)gd->arch.tbu << 32) | gd->arch.tbl; -} - -static int fttmr010_timer_probe(struct udevice *dev) -{ - struct fttmr010_timer_priv *priv = dev_get_priv(dev); - struct fttmr010 *tmr; - unsigned int cr; - - priv->regs = dev_read_addr_ptr(dev); - if (!priv->regs) - return -EINVAL; - tmr = priv->regs; - - debug("Faraday FTTMR010 timer revision 0x%08X\n", readl(&tmr->revision)); - - /* disable timers */ - writel(0, &tmr->cr); - - /* setup timer */ - writel(TIMER_LOAD_VAL, &tmr->timer3_load); - writel(TIMER_LOAD_VAL, &tmr->timer3_counter); - writel(0, &tmr->timer3_match1); - writel(0, &tmr->timer3_match2); - - /* we don't want timer to issue interrupts */ - writel(FTTMR010_TM3_MATCH1 | - FTTMR010_TM3_MATCH2 | - FTTMR010_TM3_OVERFLOW, - &tmr->interrupt_mask); - - cr = readl(&tmr->cr); - cr |= FTTMR010_TM3_CLOCK; /* use external clock */ - cr |= FTTMR010_TM3_ENABLE; - writel(cr, &tmr->cr); - - gd->arch.tbl = 0; - gd->arch.tbu = 0; - - return 0; -} - -static const struct timer_ops fttmr010_timer_ops = { - .get_count = fttmr010_timer_get_count, -}; - -static const struct udevice_id fttmr010_timer_ids[] = { - { .compatible = "faraday,fttmr010-timer" }, - {} -}; - -U_BOOT_DRIVER(fttmr010_timer) = { - .name = "fttmr010_timer", - .id = UCLASS_TIMER, - .of_match = fttmr010_timer_ids, - .priv_auto = sizeof(struct fttmr010_timer_priv), - .probe = fttmr010_timer_probe, - .ops = &fttmr010_timer_ops, -}; -- cgit v1.2.3 From 3f486367bc8ba415fa0acbe72e57bfbfe3ffaca1 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Wed, 6 Aug 2025 08:55:03 -0600 Subject: timer: Tighten some timer driver dependencies A few timer drivers cannot build without access to some platform specific header files. Express those requirements in Kconfig as well. Signed-off-by: Tom Rini --- drivers/timer/Kconfig | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index d3231441b13..81c154b378a 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -96,8 +96,8 @@ config ARM_TWD_TIMER config AST_TIMER bool "Aspeed ast2400/ast2500 timer support" - depends on TIMER - default y if ARCH_ASPEED + depends on TIMER && ARCH_ASPEED + default y help Select this to enable timer for Aspeed ast2400/ast2500 devices. This is a simple sys timer driver, it is compatible with lib/time.c, @@ -108,17 +108,10 @@ config AST_TIMER config AST_IBEX_TIMER bool "Aspeed ast2700 Ibex timer" - depends on TIMER + depends on TIMER && RISCV help Select this to enable a timer support for the Ibex RV32-based MCUs in AST2700. -config ATCPIT100_TIMER - bool "ATCPIT100 timer support" - depends on TIMER - help - Select this to enable a ATCPIT100 timer which will be embedded - in AE3XX, AE250 boards. - config ATMEL_PIT_TIMER bool "Atmel periodic interval timer support" depends on TIMER @@ -161,7 +154,7 @@ config CADENCE_TTC_TIMER config DESIGNWARE_APB_TIMER bool "Designware APB Timer" - depends on TIMER + depends on TIMER && (ARCH_SOCFPGA || ARCH_ROCKCHIP) help Enables support for the Designware APB Timer driver. This timer is present on Altera SoCFPGA SoCs. @@ -175,7 +168,7 @@ config GXP_TIMER config MPC83XX_TIMER bool "MPC83xx timer support" - depends on TIMER + depends on TIMER && PPC help Select this to enable support for the timer found on devices based on the MPC83xx family of SoCs. @@ -217,14 +210,14 @@ config NPCM_TIMER config OMAP_TIMER bool "Omap timer support" - depends on TIMER + depends on TIMER && (ARCH_OMAP2PLUS || ARCH_K3) help Select this to enable an timer for Omap devices. config ORION_TIMER bool "Orion timer support" - depends on TIMER - default y if ARCH_KIRKWOOD || (ARCH_MVEBU && ARMADA_32BIT) + depends on TIMER && (ARCH_KIRKWOOD || (ARCH_MVEBU && ARMADA_32BIT)) + default y select TIMER_EARLY if ARCH_MVEBU help Select this to enable an timer for Orion and Armada devices @@ -239,7 +232,7 @@ config RISCV_TIMER config ROCKCHIP_TIMER bool "Rockchip timer support" - depends on TIMER + depends on TIMER && ARCH_ROCKCHIP help Select this to enable support for the timer found on Rockchip devices. @@ -268,14 +261,14 @@ config SP804_TIMER config STM32_TIMER bool "STM32 timer support" - depends on TIMER + depends on TIMER && ARCH_STM32 help Select this to enable support for the timer found on STM32 devices. config TEGRA_TIMER bool "Tegra timer support" - depends on TIMER + depends on TIMER && ARCH_TEGRA select TIMER_EARLY help Select this to enable support for the timer found on -- cgit v1.2.3 From 26d9bd1ccd6b344d1cc9ddb3d50981b106fed3b8 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Wed, 6 Aug 2025 16:47:46 +0100 Subject: phy: keystone-usb: Do not negate return code In keystone_usb_init the return code from psc_enable_module should be returned as is rather than being negated. This issue was found by Smatch. Signed-off-by: Andrew Goodbody --- drivers/phy/keystone-usb-phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/phy/keystone-usb-phy.c b/drivers/phy/keystone-usb-phy.c index cfc15203d63..460efbe768d 100644 --- a/drivers/phy/keystone-usb-phy.c +++ b/drivers/phy/keystone-usb-phy.c @@ -41,7 +41,7 @@ static int keystone_usb_init(struct phy *phy) rc = psc_enable_module(keystone->psc_domain); if (rc) { debug("Cannot enable USB module"); - return -rc; + return rc; } mdelay(10); -- cgit v1.2.3 From fc96c1de5b719561db15259009a9ef22e671ee3d Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Wed, 6 Aug 2025 18:03:26 +0100 Subject: phy: ti: j721e-wiz: Set error code before goto In j721e_wiz_probe the test for too many lanes jumps to the error exit path without assigning an error code which could lead to calling code silently ignoring the failure. Set the error code. This issue was found by Smatch. Signed-off-by: Andrew Goodbody --- drivers/phy/ti/phy-j721e-wiz.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c index c69a342e2b4..6e2d4bc2b05 100644 --- a/drivers/phy/ti/phy-j721e-wiz.c +++ b/drivers/phy/ti/phy-j721e-wiz.c @@ -1201,6 +1201,7 @@ static int j721e_wiz_probe(struct udevice *dev) if (num_lanes > WIZ_MAX_LANES) { dev_err(dev, "Cannot support %d lanes\n", num_lanes); + rc = -EINVAL; goto err_addr_to_resource; } -- cgit v1.2.3 From 460db5b44dba0ec93bbfec3bdf537fba7a487714 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Fri, 8 Aug 2025 11:47:43 +0100 Subject: ram: k3-ddrss: Use logical and not bitwise The test for the interrupt LPDDR4_INTR_BIST_DONE is using a bitwise and but the test is simple logic so use the more appropriate logical and. This issue was found by Smatch. Signed-off-by: Andrew Goodbody --- drivers/ram/k3-ddrss/k3-ddrss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c index 6590d57ad84..b86e2448eb5 100644 --- a/drivers/ram/k3-ddrss/k3-ddrss.c +++ b/drivers/ram/k3-ddrss/k3-ddrss.c @@ -626,7 +626,7 @@ static void k3_lpddr4_bist_init_mem_region(struct k3_ddrss_desc *ddrss, while (i < BIST_MEM_INIT_TIMEOUT) { status = driverdt->checkctlinterrupt(pd, LPDDR4_INTR_BIST_DONE, &int_status); - if (!status & int_status) { + if (!status && int_status) { /* Clear LPDDR4_INTR_BIST_DONE */ driverdt->ackctlinterrupt(pd, LPDDR4_INTR_BIST_DONE); break; -- cgit v1.2.3 From 64204ab107b51c60e490292ed1eda40a9a64d7d9 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Thu, 7 Aug 2025 11:04:02 +0100 Subject: pinctrl: single: Remove unreachable code In single_read there is a switch block with a default label. All cases in the switch block, including the default, return directly. So any code following the switch block is unreachable. Remove the unreachable code. This issue was found by Smatch. Signed-off-by: Andrew Goodbody --- drivers/pinctrl/pinctrl-single.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index a3802d22d4f..c96b293aadf 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -109,8 +109,6 @@ static unsigned int single_read(struct udevice *dev, void *reg) default: /* 32 bits */ return readl(reg); } - - return readb(reg); } static void single_write(struct udevice *dev, unsigned int val, void *reg) -- cgit v1.2.3 From 4a2f360bd280b2b5af1c5daffbc189590c83c995 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Thu, 7 Aug 2025 11:04:03 +0100 Subject: pinctrl: stmfx: Remove duplicated code In stmfx_read_reg there is duplicated code to detect ret < 0 and return ret if so. Remove one version of it. This issue was found by Smatch. Signed-off-by: Andrew Goodbody --- drivers/pinctrl/pinctrl-stmfx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-stmfx.c b/drivers/pinctrl/pinctrl-stmfx.c index 61f335c4eb1..0d5778faef9 100644 --- a/drivers/pinctrl/pinctrl-stmfx.c +++ b/drivers/pinctrl/pinctrl-stmfx.c @@ -87,7 +87,7 @@ static int stmfx_read_reg(struct udevice *dev, u8 reg_base, uint offset) if (ret < 0) return ret; - return ret < 0 ? ret : !!(ret & mask); + return !!(ret & mask); } static int stmfx_write_reg(struct udevice *dev, u8 reg_base, uint offset, -- cgit v1.2.3 From ad3a33e57742bab08a8ca1a9e34c262b6fbf0268 Mon Sep 17 00:00:00 2001 From: Stanley Chu Date: Thu, 7 Aug 2025 13:32:19 +0800 Subject: pinctrl: npcm8xx: add support for setting VCD input source Add pinmux for the VCD input to use the HSYNC signal. Signed-off-by: Stanley Chu Signed-off-by: Jim Liu --- drivers/pinctrl/nuvoton/pinctrl-npcm8xx.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/pinctrl/nuvoton/pinctrl-npcm8xx.c b/drivers/pinctrl/nuvoton/pinctrl-npcm8xx.c index 67e564f85c3..c960ca3393d 100644 --- a/drivers/pinctrl/nuvoton/pinctrl-npcm8xx.c +++ b/drivers/pinctrl/nuvoton/pinctrl-npcm8xx.c @@ -23,6 +23,7 @@ #define TIPRSTC 0x50 #define CORSTC 0x5c #define FLOCKR1 0x74 +#define INTCR 0x3c #define INTCR4 0xc0 #define I2CSEGSEL 0xe0 #define MFSEL1 0x260 @@ -280,6 +281,7 @@ struct npcm8xx_pinctrl_priv { FUNC(lkgpo2, FLOCKR1, 8, 9) \ FUNC(nprd_smi, FLOCKR1, 20, 190) \ FUNC(mmcwp, FLOCKR1, 24, 153) \ + FUNC(vcdhs, INTCR, 27) \ FUNC(rg2refck, INTCR4, 6) \ FUNC(r1en, INTCR4, 12) \ FUNC(r2en, INTCR4, 13) \ -- cgit v1.2.3 From 3c632fc090dab1838cedb525e09d049a903c37a6 Mon Sep 17 00:00:00 2001 From: Stanley Chu Date: Thu, 7 Aug 2025 13:32:20 +0800 Subject: i2c: npcm: fix consecutive dm_i2c_read/write error When doing a dm_i2c_read followed by a dm_i2c_write, the subsequent transaction may get npcm_i2c_check_sda error because the module is still busy in STOP condition in previous dm_i2c_read. Always check and wait for module to be out of busy before starting an i2c transaction. Signed-off-by: Stanley Chu Signed-off-by: Jim Liu --- drivers/i2c/npcm_i2c.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/i2c/npcm_i2c.c b/drivers/i2c/npcm_i2c.c index c64752e1467..bff0d04f1a5 100644 --- a/drivers/i2c/npcm_i2c.c +++ b/drivers/i2c/npcm_i2c.c @@ -34,6 +34,7 @@ #define SMBCTL3_SDA_LVL BIT(6) /* SMBCST */ +#define SMBCST_BUSY BIT(0) #define SMBCST_BB BIT(1) #define SMBCST_TGSCL BIT(5) @@ -479,11 +480,17 @@ static int npcm_i2c_xfer(struct udevice *dev, struct npcm_i2c_bus *bus = dev_get_priv(dev); struct npcm_i2c_regs *reg = bus->reg; int ret = 0, err = 0; + u8 val; if (nmsgs < 1 || nmsgs > 2) { printf("%s: commands not support\n", __func__); return -EREMOTEIO; } + + /* Wait for module out of busy */ + if (readb_poll_timeout(®->cst, val, !(val & SMBCST_BUSY), 1000)) + return -EBUSY; + /* clear ST register */ writeb(0xFF, ®->st); -- cgit v1.2.3 From 121927e37be0725139d5b07d08361ac266685808 Mon Sep 17 00:00:00 2001 From: Jim Liu Date: Thu, 7 Aug 2025 13:32:22 +0800 Subject: misc: npcm_host_intf: Add Arbel eSPI workaround Enabling an eSPI channel(e.g. Peripheral Channel) during an eSPI transaction might cause the BMC eSPI module to transition to a wrong state and therefore respond with FATAL_ERROR on incoming transaction. Add workaround to avoid the module getting into the wrong state. Signed-off-by: Stanley Chu Signed-off-by: Jim Liu --- drivers/misc/npcm_host_intf.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/npcm_host_intf.c b/drivers/misc/npcm_host_intf.c index 58bab888c3c..2c89bd7a167 100644 --- a/drivers/misc/npcm_host_intf.c +++ b/drivers/misc/npcm_host_intf.c @@ -22,6 +22,8 @@ /* ESPI Register offsets */ #define ESPICFG 0x4 #define ESPIHINDP 0x80 +#define ESPI_TEN 0xF0 +#define ESPI_ENG 0xF1 /* MFSEL bit fileds */ #define MFSEL1_LPCSEL BIT(26) @@ -40,6 +42,9 @@ #define AUTO_HS2 BIT(12) #define AUTO_HS3 BIT(16) +#define ESPI_TEN_ENABLE 0x55 +#define ESPI_TEN_DISABLE 0 + static int npcm_host_intf_bind(struct udevice *dev) { struct regmap *syscon; @@ -83,6 +88,13 @@ static int npcm_host_intf_bind(struct udevice *dev) val &= ~(CHSUPP_MASK | IOMODE_MASK | MAXFREQ_MASK); val |= IOMODE_SDQ | MAXFREQ_33MHZ | FIELD_PREP(CHSUPP_MASK, ch_supp); writel(val, base + ESPICFG); + + if (device_is_compatible(dev, "nuvoton,npcm845-host-intf")) { + /* Workaround: avoid eSPI module getting into wrong state */ + writeb(ESPI_TEN_ENABLE, base + ESPI_TEN); + writeb(BIT(6), base + ESPI_ENG); + writeb(ESPI_TEN_DISABLE, base + ESPI_TEN); + } } else if (!strcmp(type, "lpc")) { /* Select LPC pin function */ regmap_update_bits(syscon, MFSEL4, MFSEL4_ESPISEL, 0); -- cgit v1.2.3 From 987880581646d5836d2485e5f7a5af6ce8600da1 Mon Sep 17 00:00:00 2001 From: Jim Liu Date: Thu, 7 Aug 2025 13:32:23 +0800 Subject: misc: npcm_host_intf: Disable pending KCS/BPC interrupts If there is an unhandled KCS/BPC pending interrupt after reboot, the KCS/BPC Linux driver may trigger interrupts immediately upon registering the irq. However, since the driver is not yet initialized to handle them, this can lead to unexpected behavior. To prevent this, disable KCS/BPC interrupts in u-boot to avoid pending interrupts from being raised before the Linux driver is fully initialized. Signed-off-by: Stanley Chu Signed-off-by: Jim Liu --- drivers/misc/npcm_host_intf.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/npcm_host_intf.c b/drivers/misc/npcm_host_intf.c index 2c89bd7a167..e3b0663625b 100644 --- a/drivers/misc/npcm_host_intf.c +++ b/drivers/misc/npcm_host_intf.c @@ -45,10 +45,20 @@ #define ESPI_TEN_ENABLE 0x55 #define ESPI_TEN_DISABLE 0 +/* KCS/BPC interrupt control */ +#define BPCFEN 0x46 +#define FRIE BIT(3) +#define HRIE BIT(4) +#define KCS1CTL 0x18 +#define KCS2CTL 0x2a +#define KCS3CTL 0x3c +#define IBFIE BIT(0) +#define OBEIE BIT(1) + static int npcm_host_intf_bind(struct udevice *dev) { struct regmap *syscon; - void __iomem *base; + void __iomem *base, *kcs_base; u32 ch_supp, val; u32 ioaddr; const char *type; @@ -104,6 +114,15 @@ static int npcm_host_intf_bind(struct udevice *dev) /* Release host wait */ setbits_8(SMC_CTL_REG_ADDR, SMC_CTL_HOSTWAIT); + kcs_base = dev_read_addr_index_ptr(dev, 1); + if (kcs_base) { + /* Disable KCS/BPC interrupts */ + clrbits_8(kcs_base + BPCFEN, FRIE | HRIE); + clrbits_8(kcs_base + KCS1CTL, IBFIE | OBEIE); + clrbits_8(kcs_base + KCS2CTL, IBFIE | OBEIE); + clrbits_8(kcs_base + KCS3CTL, IBFIE | OBEIE); + } + return 0; } -- cgit v1.2.3 From e416d165723c56658b4a688e790cdcd968c6cfc9 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Thu, 7 Aug 2025 10:16:55 +0100 Subject: pinctrl: nexell: Cannot test unsigned to be negative In s5pxx18_pinctrl_set_state testing count to be negative will always fail as count is unsigned despite receiving the return value of a function that returns an int. Change count and idx to be of type int to allow the test to work as expected and remove the need for any implicit casts. Also change pin to be u32 which is what all called functions expect. This issue was found by Smatch. Signed-off-by: Andrew Goodbody --- drivers/pinctrl/nexell/pinctrl-s5pxx18.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/nexell/pinctrl-s5pxx18.c b/drivers/pinctrl/nexell/pinctrl-s5pxx18.c index e7d0994f29e..a6ae5764fbc 100644 --- a/drivers/pinctrl/nexell/pinctrl-s5pxx18.c +++ b/drivers/pinctrl/nexell/pinctrl-s5pxx18.c @@ -130,7 +130,8 @@ static int is_pin_alive(const char *name) static int s5pxx18_pinctrl_set_state(struct udevice *dev, struct udevice *config) { - unsigned int count, idx, pin; + u32 pin; + int count, idx; unsigned int pinfunc, pinpud, pindrv; unsigned long reg; const char *name; -- cgit v1.2.3 From da938a4254b4cf9d4e7b894a0a30536b0d9266c5 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Tue, 12 Aug 2025 11:36:53 +0100 Subject: sound: rt5677: Cannot test unsigned for being negative In rt5677_bic_or the call to rt5677_i2c_read returns an int so old should also be an int to receive that value and then be able to test it for being negative which would indicate an error. This issue was found by Smatch. Signed-off-by: Andrew Goodbody --- drivers/sound/rt5677.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/sound/rt5677.c b/drivers/sound/rt5677.c index b5c997c6dd5..a9d1e94951a 100644 --- a/drivers/sound/rt5677.c +++ b/drivers/sound/rt5677.c @@ -94,8 +94,8 @@ static int rt5677_i2c_write(struct rt5677_priv *priv, uint reg, uint data) static int rt5677_bic_or(struct rt5677_priv *priv, uint reg, uint bic, uint set) { - uint old, new_value; - int ret; + uint new_value; + int old, ret; old = rt5677_i2c_read(priv, reg); if (old < 0) -- cgit v1.2.3 From f780ad4be9fe84fbdd874455c7518e0576e4d845 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Fri, 8 Aug 2025 13:00:22 +0100 Subject: remoteproc: ti_k3_arm64: Cannot set or compare u8 to 16bits In the struct ti_sci_proc the fields proc_id and host_id are declared as u8 so cannot be set to nor compared with a macro defined with a value using 16 bits. Change the macro to only use 8 bits to make the code work as expected. This issue was found by Smatch. Signed-off-by: Andrew Goodbody --- drivers/remoteproc/ti_k3_arm64_rproc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/remoteproc/ti_k3_arm64_rproc.c b/drivers/remoteproc/ti_k3_arm64_rproc.c index d3eb957b2e4..403c6bed2e8 100644 --- a/drivers/remoteproc/ti_k3_arm64_rproc.c +++ b/drivers/remoteproc/ti_k3_arm64_rproc.c @@ -19,7 +19,7 @@ #include #include "ti_sci_proc.h" -#define INVALID_ID 0xffff +#define INVALID_ID 0xff #define GTC_CNTCR_REG 0x0 #define GTC_CNTFID0_REG 0x20 -- cgit v1.2.3 From c9cd480b5c5496b809d954424ce3554673c278d3 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Fri, 8 Aug 2025 13:00:23 +0100 Subject: remoteproc: k3-r5: Ensure ret is initialised In k3_r5f_split_reset and k3_r5f_unprepare ret may not have been assigned to before the code reaches the return ret at the function exit. This issue was found by Smatch. Signed-off-by: Andrew Goodbody --- drivers/remoteproc/ti_k3_r5f_rproc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/remoteproc/ti_k3_r5f_rproc.c b/drivers/remoteproc/ti_k3_r5f_rproc.c index f4bab6868ee..3a25ef6bf87 100644 --- a/drivers/remoteproc/ti_k3_r5f_rproc.c +++ b/drivers/remoteproc/ti_k3_r5f_rproc.c @@ -441,7 +441,7 @@ proc_release: static int k3_r5f_split_reset(struct k3_r5f_core *core) { - int ret; + int ret = 0; dev_dbg(core->dev, "%s\n", __func__); @@ -476,7 +476,7 @@ static int k3_r5f_unprepare(struct udevice *dev) { struct k3_r5f_core *core = dev_get_priv(dev); struct k3_r5f_cluster *cluster = core->cluster; - int ret; + int ret = 0; dev_dbg(dev, "%s\n", __func__); -- cgit v1.2.3 From b90927bd0d81096239ff4a48a71c3d9e87ef7209 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Tue, 12 Aug 2025 11:13:49 +0100 Subject: soc: ti: k3-navss-ringacc: NULL check before dereference Move the first dereference of ring to after the NULL check has occurred. This will prevent any possible dereference of NULL. This issue was found by Smatch. Signed-off-by: Andrew Goodbody --- drivers/soc/ti/k3-navss-ringacc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/soc/ti/k3-navss-ringacc.c b/drivers/soc/ti/k3-navss-ringacc.c index 14114a65830..ac29158d489 100644 --- a/drivers/soc/ti/k3-navss-ringacc.c +++ b/drivers/soc/ti/k3-navss-ringacc.c @@ -632,12 +632,14 @@ err_free_ops: int k3_nav_ringacc_ring_cfg(struct k3_nav_ring *ring, struct k3_nav_ring_cfg *cfg) { - struct k3_nav_ringacc *ringacc = ring->parent; + struct k3_nav_ringacc *ringacc; int ret = 0; if (!ring || !cfg) return -EINVAL; + ringacc = ring->parent; + if (ringacc->dual_ring) return k3_dmaring_ring_cfg(ring, cfg); -- cgit v1.2.3 From 13ca68b104ea75d54e86816ba6d86ef5e70a2668 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Tue, 12 Aug 2025 11:13:50 +0100 Subject: soc: ti: k3-navss-ringacc: Do not use uninitialised variable In k3_nav_ringacc_probe_dt there can be no error code returned from dev_read_u32_default so ret is not assigned to and should not be used. Remove the use of ret from the dev_err call as it is unitialised. This issue was found by Smatch. Signed-off-by: Andrew Goodbody --- drivers/soc/ti/k3-navss-ringacc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/soc/ti/k3-navss-ringacc.c b/drivers/soc/ti/k3-navss-ringacc.c index ac29158d489..d37c454143b 100644 --- a/drivers/soc/ti/k3-navss-ringacc.c +++ b/drivers/soc/ti/k3-navss-ringacc.c @@ -932,7 +932,7 @@ static int k3_nav_ringacc_probe_dt(struct k3_nav_ringacc *ringacc) ringacc->num_rings = dev_read_u32_default(dev, "ti,num-rings", 0); if (!ringacc->num_rings) { - dev_err(dev, "ti,num-rings read failure %d\n", ret); + dev_err(dev, "ti,num-rings read failure\n"); return -EINVAL; } -- cgit v1.2.3 From 520d9c2521f4082209ce5121be1b3c1eae98994c Mon Sep 17 00:00:00 2001 From: Neha Malcom Francis Date: Tue, 12 Aug 2025 18:13:16 +0530 Subject: ram: k3-ddrss: Use DDR address instead of system address for ecc_regions Let ecc_regions[x].start reflect the start of the ECC region in terms of DDR addressing rather than system addressing. This will make it easier to extend the usage of the same ecc_regions structure for multi-DDR systems as well. Reviewed-by: Udit Kumar Signed-off-by: Neha Malcom Francis --- drivers/ram/k3-ddrss/k3-ddrss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c index b86e2448eb5..f19a17ecec7 100644 --- a/drivers/ram/k3-ddrss/k3-ddrss.c +++ b/drivers/ram/k3-ddrss/k3-ddrss.c @@ -752,7 +752,7 @@ static void k3_ddrss_lpddr4_ecc_init(struct k3_ddrss_desc *ddrss) u32 val; /* Only Program region 0 which covers full ddr space */ - k3_ddrss_set_ecc_range_r0(base, ecc_region_start - ddrss->ddr_bank_base[0], ecc_range); + k3_ddrss_set_ecc_range_r0(base, ecc_region_start, ecc_range); /* Enable ECC, RMW, WR_ALLOC */ writel(DDRSS_ECC_CTRL_REG_ECC_EN | DDRSS_ECC_CTRL_REG_RMW_EN | @@ -817,7 +817,7 @@ static int k3_ddrss_probe(struct udevice *dev) k3_ddrss_lpddr4_ecc_calc_reserved_mem(ddrss); /* Always configure one region that covers full DDR space */ - ddrss->ecc_regions[0].start = ddrss->ddr_bank_base[0]; + ddrss->ecc_regions[0].start = 0; ddrss->ecc_regions[0].range = ddrss->ddr_ram_size - ddrss->ecc_reserved_space; k3_ddrss_lpddr4_ecc_init(ddrss); } -- cgit v1.2.3 From e511c651f6dd0ec89a4b7dec25140540740cf13e Mon Sep 17 00:00:00 2001 From: Neha Malcom Francis Date: Tue, 12 Aug 2025 18:13:17 +0530 Subject: ram: k3-ddrss: s/K3_DDRSS_MAX_ECC_REGIONS/K3_DDRSS_MAX_ECC_REG To prevent checkpatch warning once we start using this macro more frequently, shorten the length of it. While at it, also move the structure k3_ddrss_ecc_region above k3_msmc so that future patches can have it as a member of k3_msmc. Reviewed-by: Udit Kumar Signed-off-by: Neha Malcom Francis --- drivers/ram/k3-ddrss/k3-ddrss.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c index f19a17ecec7..2fdbd4609a8 100644 --- a/drivers/ram/k3-ddrss/k3-ddrss.c +++ b/drivers/ram/k3-ddrss/k3-ddrss.c @@ -121,6 +121,13 @@ enum emif_active { EMIF_ALL }; +#define K3_DDRSS_MAX_ECC_REG 3 + +struct k3_ddrss_ecc_region { + u64 start; + u64 range; +}; + struct k3_msmc { enum intrlv_gran gran; enum intrlv_size size; @@ -129,13 +136,6 @@ struct k3_msmc { enum emif_active active; }; -#define K3_DDRSS_MAX_ECC_REGIONS 3 - -struct k3_ddrss_ecc_region { - u64 start; - u64 range; -}; - struct k3_ddrss_desc { struct udevice *dev; void __iomem *ddrss_ss_cfg; @@ -155,7 +155,7 @@ struct k3_ddrss_desc { lpddr4_obj *driverdt; lpddr4_config config; lpddr4_privatedata pd; - struct k3_ddrss_ecc_region ecc_regions[K3_DDRSS_MAX_ECC_REGIONS]; + struct k3_ddrss_ecc_region ecc_regions[K3_DDRSS_MAX_ECC_REG]; u64 ecc_reserved_space; u64 ddr_bank_base[CONFIG_NR_DRAM_BANKS]; u64 ddr_bank_size[CONFIG_NR_DRAM_BANKS]; -- cgit v1.2.3 From 1c70e33b0aca6d5d84145636400e496feb19c8b9 Mon Sep 17 00:00:00 2001 From: Neha Malcom Francis Date: Tue, 12 Aug 2025 18:13:18 +0530 Subject: ram: k3-ddrss: Add comment about ecc_reserved_space The reserved space needed for storing the parity remains the same no matter the size of the region that is being protected. Add this as a comment for better code understanding. Reviewed-by: Udit Kumar Signed-off-by: Neha Malcom Francis --- drivers/ram/k3-ddrss/k3-ddrss.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c index 2fdbd4609a8..d35adc840f4 100644 --- a/drivers/ram/k3-ddrss/k3-ddrss.c +++ b/drivers/ram/k3-ddrss/k3-ddrss.c @@ -737,6 +737,12 @@ static void k3_ddrss_lpddr4_ecc_calc_reserved_mem(struct k3_ddrss_desc *ddrss) { fdtdec_setup_mem_size_base_lowest(); + /* + * For every 512-byte data block, 64 bytes are used to store inline ECC + * information into a reserved region. It remains 1/9th of the total DDR + * size irrespective of the size of the region under protection. + */ + ddrss->ecc_reserved_space = ddrss->ddr_ram_size; do_div(ddrss->ecc_reserved_space, 9); -- cgit v1.2.3 From f43f710122541f870ba164e8121445003032c71c Mon Sep 17 00:00:00 2001 From: Neha Malcom Francis Date: Tue, 12 Aug 2025 18:13:19 +0530 Subject: ram: k3-ddrss: Add support for a partial inline ECC region Instead of defaulting to choosing the entire DDR region when enabling inline ECC, allow picking of a range within the DDR space using DT to enable. It expects such a node within the memory node, in the absence of which we resort to enabling inline ECC for the entire DDR region: inline_ecc: protected@9e780000 { device_type = "ecc"; reg = <0x9e780000 0x0080000>; bootph-all; }; Signed-off-by: Neha Malcom Francis --- drivers/ram/k3-ddrss/k3-ddrss.c | 60 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c index d35adc840f4..61d4f3d7aa4 100644 --- a/drivers/ram/k3-ddrss/k3-ddrss.c +++ b/drivers/ram/k3-ddrss/k3-ddrss.c @@ -155,6 +155,7 @@ struct k3_ddrss_desc { lpddr4_obj *driverdt; lpddr4_config config; lpddr4_privatedata pd; + struct k3_ddrss_ecc_region ecc_range; struct k3_ddrss_ecc_region ecc_regions[K3_DDRSS_MAX_ECC_REG]; u64 ecc_reserved_space; u64 ddr_bank_base[CONFIG_NR_DRAM_BANKS]; @@ -733,6 +734,30 @@ static void k3_ddrss_ddr_reg_init(struct k3_ddrss_desc *ddrss) writel(DDRSS_ECC_CTRL_REG_DEFAULT, ddrss->ddrss_ss_cfg + DDRSS_ECC_CTRL_REG); } +static void k3_ddrss_ddr_inline_ecc_base_size_calc(struct k3_ddrss_ecc_region *range) +{ + fdt_addr_t base; + fdt_size_t size; + ofnode node1; + + node1 = ofnode_null(); + + do { + node1 = ofnode_by_prop_value(node1, "device_type", "ecc", 4); + } while (!ofnode_is_enabled(node1)); + + base = ofnode_get_addr_size(node1, "reg", &size); + + if (base == FDT_ADDR_T_NONE) { + debug("%s: Failed to get ECC node reg and size\n", __func__); + range->start = 0; + range->range = 0; + } else { + range->start = base; + range->range = size; + } +} + static void k3_ddrss_lpddr4_ecc_calc_reserved_mem(struct k3_ddrss_desc *ddrss) { fdtdec_setup_mem_size_base_lowest(); @@ -783,8 +808,11 @@ static void k3_ddrss_lpddr4_ecc_init(struct k3_ddrss_desc *ddrss) static int k3_ddrss_probe(struct udevice *dev) { + u64 end; int ret; struct k3_ddrss_desc *ddrss = dev_get_priv(dev); + __maybe_unused u64 ddr_ram_size, ecc_res; + __maybe_unused struct k3_ddrss_ecc_region *range = &ddrss->ecc_range; debug("%s(dev=%p)\n", __func__, dev); @@ -822,9 +850,35 @@ static int k3_ddrss_probe(struct udevice *dev) k3_ddrss_lpddr4_ecc_calc_reserved_mem(ddrss); - /* Always configure one region that covers full DDR space */ - ddrss->ecc_regions[0].start = 0; - ddrss->ecc_regions[0].range = ddrss->ddr_ram_size - ddrss->ecc_reserved_space; + k3_ddrss_ddr_inline_ecc_base_size_calc(range); + + end = ddrss->ecc_range.start + ddrss->ecc_range.range; + ddr_ram_size = ddrss->ddr_ram_size; + ecc_res = ddrss->ecc_reserved_space; + + if (!range->range) { + /* Configure entire DDR space by default */ + debug("%s: Defaulting to protecting entire DDR space using inline ECC\n", + __func__); + ddrss->ecc_range.start = ddrss->ddr_bank_base[0]; + ddrss->ecc_range.range = ddr_ram_size - ecc_res; + } else { + ddrss->ecc_range.start = range->start; + ddrss->ecc_range.range = range->range; + } + + /* + * As we are converting the system address to the DDR controller + * address, account for case when the region is in the second + * bank + */ + if (end > (ddr_ram_size - ecc_res)) + ddrss->ecc_regions[0].range = ddr_ram_size - ecc_res; + else + ddrss->ecc_regions[0].range = ddrss->ecc_range.range; + + ddrss->ecc_regions[0].start = ddrss->ecc_range.start - ddrss->ddr_bank_base[0]; + k3_ddrss_lpddr4_ecc_init(ddrss); } -- cgit v1.2.3 From 3a0793fe9be3f0e6026d31dec8545a6b0e102ccd Mon Sep 17 00:00:00 2001 From: Neha Malcom Francis Date: Tue, 12 Aug 2025 18:13:20 +0530 Subject: ram: k3-ddrss: Add CONFIG_K3_MULTI_DDR As we increase the functionalities that the K3 DDRSS sub-system support, it is becoming more evident that the same logic cannot apply to both single as well as multiple DDR controller devices. Add CONFIG_K3_MULTI_DDR to be used to differentiate between the two. Reviewed-by: Udit Kumar Signed-off-by: Neha Malcom Francis --- drivers/ram/Kconfig | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig index d6964497309..cfbfa1252d0 100644 --- a/drivers/ram/Kconfig +++ b/drivers/ram/Kconfig @@ -128,6 +128,16 @@ config K3_INLINE_ECC need to be primed with a predefined value prior to enabling ECC check. +config K3_MULTI_DDR + bool "Enable support for multiple K3 DDRSS controllers" + depends on K3_DDRSS + help + Enabling this option adds support for configuring multiple DDR memory + controllers for K3 devices. The external memory interleave layer + present in the MSMC (Multicore Shared Memory Controller) is + responsible for interleaving between the controllers. + default y if SOC_K3_J721S2 || SOC_K3_J784S4 + source "drivers/ram/aspeed/Kconfig" source "drivers/ram/cadence/Kconfig" source "drivers/ram/octeon/Kconfig" -- cgit v1.2.3 From 2310aac8ae0180ad78da1d44657f846610296e4d Mon Sep 17 00:00:00 2001 From: Neha Malcom Francis Date: Tue, 12 Aug 2025 18:13:21 +0530 Subject: ram: k3-ddrss: Add support for number of controllers under MSMC In K3 multi-DDR systems, the MSMC is responsible for the interleave mechanism across all the DDR controllers. Add support for MSMC to obtain the number of controllers it's responsible for using the DT. Reviewed-by: Udit Kumar Signed-off-by: Neha Malcom Francis --- drivers/ram/k3-ddrss/k3-ddrss.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c index 61d4f3d7aa4..075e664ff1a 100644 --- a/drivers/ram/k3-ddrss/k3-ddrss.c +++ b/drivers/ram/k3-ddrss/k3-ddrss.c @@ -134,6 +134,7 @@ struct k3_msmc { enum ecc_enable enable; enum emif_config config; enum emif_active active; + u32 num_ddr_controllers; }; struct k3_ddrss_desc { @@ -1013,6 +1014,13 @@ static int k3_msmc_probe(struct udevice *dev) return -EINVAL; } + ret = device_get_child_count(dev); + if (ret <= 0) { + dev_err(dev, "no child ddr nodes present"); + return -EINVAL; + } + msmc->num_ddr_controllers = ret; + return 0; } -- cgit v1.2.3 From c32ac5b3b934942fdcd97ee631ca2362032e0e53 Mon Sep 17 00:00:00 2001 From: Neha Malcom Francis Date: Tue, 12 Aug 2025 18:13:22 +0530 Subject: ram: k3-ddrss: Add support for MSMC calculation of DDR inline ECC regions Add support for calculation of the protected regions for each DDR in multi-DDR systems. Since MSMC is the parent node of the individual DDRs as well as responsible for their interleaving, it only makes sense for MSMC to contain the logic for dividing the regions. Signed-off-by: Neha Malcom Francis --- drivers/ram/k3-ddrss/k3-ddrss.c | 106 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) (limited to 'drivers') diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c index 075e664ff1a..c7938000314 100644 --- a/drivers/ram/k3-ddrss/k3-ddrss.c +++ b/drivers/ram/k3-ddrss/k3-ddrss.c @@ -59,6 +59,7 @@ #define SINGLE_DDR_SUBSYSTEM 0x1 #define MULTI_DDR_SUBSYSTEM 0x2 +#define MAX_MULTI_DDR 4 #define MULTI_DDR_CFG0 0x00114100 #define MULTI_DDR_CFG1 0x00114104 @@ -82,6 +83,24 @@ enum intrlv_gran { GRAN_16GB }; +u64 gran_bytes[] = { + 0x80, + 0x200, + 0x800, + 0x1000, + 0x4000, + 0x8000, + 0x80000, + 0x40000000, + 0x60000000, + 0x80000000, + 0xC0000000, + 0x100000000, + 0x180000000, + 0x200000000, + 0x400000000 +}; + enum intrlv_size { SIZE_0, SIZE_128MB, @@ -135,6 +154,7 @@ struct k3_msmc { enum emif_config config; enum emif_active active; u32 num_ddr_controllers; + struct k3_ddrss_ecc_region R0[MAX_MULTI_DDR]; }; struct k3_ddrss_desc { @@ -942,6 +962,80 @@ U_BOOT_DRIVER(k3_ddrss) = { .priv_auto = sizeof(struct k3_ddrss_desc), }; +__maybe_unused static int k3_msmc_calculate_r0_regions(struct k3_msmc *msmc) +{ + u32 n1; + u32 size, ret = 0; + u32 gran = gran_bytes[msmc->gran]; + u32 num_ddr = msmc->num_ddr_controllers; + struct k3_ddrss_ecc_region *range = NULL; + struct k3_ddrss_ecc_region R[num_ddr]; + + range = kzalloc(sizeof(range), GFP_KERNEL); + if (!range) { + ret = -ENOMEM; + return ret; + } + + k3_ddrss_ddr_inline_ecc_base_size_calc(range); + + if (!range->range) { + ret = -EINVAL; + goto range_err; + } + + memset(R, 0, num_ddr); + + /* Find the first controller in the range */ + n1 = ((range->start / gran) % num_ddr); + size = range->range; + + if (size < gran) { + R[n1].start = range->start - 0x80000000; + R[n1].range = range->start + range->range - 0x80000000; + } else { + u32 chunk_start_addr = range->start; + u32 chunk_size = range->range; + + while (chunk_size > 0) { + u32 edge; + u32 end = range->start + range->range; + + if ((chunk_start_addr % gran) == 0) + edge = chunk_start_addr + gran; + else + edge = ((chunk_start_addr + (gran - 1)) & (-gran)); + + if (edge > end) + break; + + if (R[n1].start == 0) + R[n1].start = chunk_start_addr; + + R[n1].range = edge - R[n1].start; + chunk_size = end - edge; + chunk_start_addr = edge; + + if (n1 == (num_ddr - 1)) + n1 = 0; + else + n1++; + } + + for (int i = 0; i < num_ddr; i++) + R[i].start = (R[i].start - 0x80000000 - (gran * i)) / num_ddr; + } + + for (int i = 0; i < num_ddr; i++) { + msmc->R0[i].start = R[i].start; + msmc->R0[i].range = R[i].range; + } + +range_err: + free(range); + return ret; +} + static int k3_msmc_set_config(struct k3_msmc *msmc) { u32 ddr_cfg0 = 0; @@ -1021,6 +1115,18 @@ static int k3_msmc_probe(struct udevice *dev) } msmc->num_ddr_controllers = ret; + if (IS_ENABLED(CONFIG_K3_MULTI_DDR) && IS_ENABLED(CONFIG_K3_INLINE_ECC)) { + ret = k3_msmc_calculate_r0_regions(msmc); + if (ret) { + /* Default to enabling inline ECC for entire DDR region */ + debug("%s: calculation of inline ECC regions failed, defaulting to entire region\n", + __func__); + + /* Use first R0 entry as a flag to denote MSMC calculation failure */ + msmc->R0[0].start = -1; + } + } + return 0; } -- cgit v1.2.3 From d1efbc8d65702f9eb7d032e904e092a2fd3075b3 Mon Sep 17 00:00:00 2001 From: Neha Malcom Francis Date: Tue, 12 Aug 2025 18:13:23 +0530 Subject: ram: k3-ddrss: Add support for partial inline ECC in multi-DDR systems The existing approach does not account for interleaving in the DDRs when setting up regions. There is support for MSMC to calculate the regions for each DDR, so modify k3_ddrss_probe to set the regions accordingly for multi-DDR systems. Signed-off-by: Neha Malcom Francis --- drivers/ram/k3-ddrss/k3-ddrss.c | 62 +++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c index c7938000314..9b4c0c68d91 100644 --- a/drivers/ram/k3-ddrss/k3-ddrss.c +++ b/drivers/ram/k3-ddrss/k3-ddrss.c @@ -829,11 +829,13 @@ static void k3_ddrss_lpddr4_ecc_init(struct k3_ddrss_desc *ddrss) static int k3_ddrss_probe(struct udevice *dev) { - u64 end; + u64 end, bank0, bank1; int ret; struct k3_ddrss_desc *ddrss = dev_get_priv(dev); __maybe_unused u64 ddr_ram_size, ecc_res; + __maybe_unused u32 inst, st; __maybe_unused struct k3_ddrss_ecc_region *range = &ddrss->ecc_range; + __maybe_unused struct k3_msmc *msmc_parent = NULL; debug("%s(dev=%p)\n", __func__, dev); @@ -874,31 +876,67 @@ static int k3_ddrss_probe(struct udevice *dev) k3_ddrss_ddr_inline_ecc_base_size_calc(range); end = ddrss->ecc_range.start + ddrss->ecc_range.range; + inst = ddrss->instance; ddr_ram_size = ddrss->ddr_ram_size; ecc_res = ddrss->ecc_reserved_space; + bank0 = ddrss->ddr_bank_base[0]; + bank1 = ddrss->ddr_bank_base[1]; if (!range->range) { /* Configure entire DDR space by default */ debug("%s: Defaulting to protecting entire DDR space using inline ECC\n", __func__); - ddrss->ecc_range.start = ddrss->ddr_bank_base[0]; + ddrss->ecc_range.start = bank0; ddrss->ecc_range.range = ddr_ram_size - ecc_res; } else { ddrss->ecc_range.start = range->start; ddrss->ecc_range.range = range->range; } - /* - * As we are converting the system address to the DDR controller - * address, account for case when the region is in the second - * bank - */ - if (end > (ddr_ram_size - ecc_res)) - ddrss->ecc_regions[0].range = ddr_ram_size - ecc_res; - else - ddrss->ecc_regions[0].range = ddrss->ecc_range.range; + st = ddrss->ecc_range.start; + + if (!CONFIG_IS_ENABLED(K3_MULTI_DDR)) { + if (end > (ddr_ram_size - ecc_res)) + ddrss->ecc_regions[0].range = ddr_ram_size - ecc_res; + else + ddrss->ecc_regions[0].range = ddrss->ecc_range.range; - ddrss->ecc_regions[0].start = ddrss->ecc_range.start - ddrss->ddr_bank_base[0]; + /* Check in which bank we are */ + if (st > bank1) + ddrss->ecc_regions[0].start = st - bank1 + ddrss->ddr_bank_size[0]; + else + ddrss->ecc_regions[0].start = st - bank0; + } else { + /* For multi-DDR, we rely on MSMC's calculation of regions for each DDR */ + msmc_parent = kzalloc(sizeof(msmc_parent), GFP_KERNEL); + if (!msmc_parent) + return -ENOMEM; + + msmc_parent = dev_get_priv(dev->parent); + if (!msmc_parent) { + printf("%s: could not get MSMC parent to set up inline ECC regions\n", + __func__); + kfree(msmc_parent); + return -EINVAL; + } + + if (msmc_parent->R0[0].start < 0) { + /* Configure entire DDR space by default */ + ddrss->ecc_regions[0].start = ddrss->ddr_bank_base[0]; + ddrss->ecc_regions[0].range = ddr_ram_size - ecc_res; + } else { + end = msmc_parent->R0[inst].start + msmc_parent->R0[inst].range; + + if (end > (ddr_ram_size - ecc_res)) + ddrss->ecc_regions[0].range = ddr_ram_size - ecc_res; + else + ddrss->ecc_regions[0].range = msmc_parent->R0[inst].range; + + ddrss->ecc_regions[0].start = msmc_parent->R0[inst].start; + } + + kfree(msmc_parent); + } k3_ddrss_lpddr4_ecc_init(ddrss); } -- cgit v1.2.3 From 0824703fb2b49ed64edee6f8db483bedfa0189dd Mon Sep 17 00:00:00 2001 From: Neha Malcom Francis Date: Tue, 12 Aug 2025 18:13:24 +0530 Subject: ram: k3-ddrss: Support multiple ECC regions for a single controller K3 Inline ECC mechanism can support up to 3 regions of inline ECC, add this support for single controller. Signed-off-by: Neha Malcom Francis --- drivers/ram/k3-ddrss/k3-ddrss.c | 125 ++++++++++++++++++++++++++-------------- 1 file changed, 83 insertions(+), 42 deletions(-) (limited to 'drivers') diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c index 9b4c0c68d91..5144470b931 100644 --- a/drivers/ram/k3-ddrss/k3-ddrss.c +++ b/drivers/ram/k3-ddrss/k3-ddrss.c @@ -176,7 +176,7 @@ struct k3_ddrss_desc { lpddr4_obj *driverdt; lpddr4_config config; lpddr4_privatedata pd; - struct k3_ddrss_ecc_region ecc_range; + struct k3_ddrss_ecc_region ecc_ranges[K3_DDRSS_MAX_ECC_REG]; struct k3_ddrss_ecc_region ecc_regions[K3_DDRSS_MAX_ECC_REG]; u64 ecc_reserved_space; u64 ddr_bank_base[CONFIG_NR_DRAM_BANKS]; @@ -576,10 +576,26 @@ void k3_lpddr4_start(struct k3_ddrss_desc *ddrss) } } -static void k3_ddrss_set_ecc_range_r0(u32 base, u64 start_address, u64 size) +static void k3_ddrss_set_ecc_range_rx(u32 x, u32 base, u64 start_address, u64 size) { - writel((start_address) >> 16, base + DDRSS_ECC_R0_STR_ADDR_REG); - writel((start_address + size - 1) >> 16, base + DDRSS_ECC_R0_END_ADDR_REG); + u32 start_reg, end_reg; + + switch (x) { + case 1: + start_reg = DDRSS_ECC_R1_STR_ADDR_REG; + end_reg = DDRSS_ECC_R1_END_ADDR_REG; + break; + case 2: + start_reg = DDRSS_ECC_R2_STR_ADDR_REG; + end_reg = DDRSS_ECC_R2_END_ADDR_REG; + break; + default: + start_reg = DDRSS_ECC_R0_STR_ADDR_REG; + end_reg = DDRSS_ECC_R0_END_ADDR_REG; + break; + } + writel((start_address) >> 16, base + start_reg); + writel((start_address + size - 1) >> 16, base + end_reg); } #define BIST_MODE_MEM_INIT 4 @@ -755,27 +771,40 @@ static void k3_ddrss_ddr_reg_init(struct k3_ddrss_desc *ddrss) writel(DDRSS_ECC_CTRL_REG_DEFAULT, ddrss->ddrss_ss_cfg + DDRSS_ECC_CTRL_REG); } +ofnode get_next_ecc_node(ofnode ecc) +{ + do { + ecc = ofnode_by_prop_value(ecc, "device_type", "ecc", 4); + } while (!ofnode_is_enabled(ecc)); + + return ecc; +} + static void k3_ddrss_ddr_inline_ecc_base_size_calc(struct k3_ddrss_ecc_region *range) { fdt_addr_t base; fdt_size_t size; - ofnode node1; - - node1 = ofnode_null(); - - do { - node1 = ofnode_by_prop_value(node1, "device_type", "ecc", 4); - } while (!ofnode_is_enabled(node1)); - - base = ofnode_get_addr_size(node1, "reg", &size); + ofnode ecc_node = ofnode_null(); - if (base == FDT_ADDR_T_NONE) { - debug("%s: Failed to get ECC node reg and size\n", __func__); + ecc_node = get_next_ecc_node(ecc_node); + if (!ofnode_valid(ecc_node)) { + debug("%s: No ECC node, enabling for entire region\n", __func__); range->start = 0; range->range = 0; - } else { + return; + } + + for (int i = 0; i < K3_DDRSS_MAX_ECC_REG; i++) { + base = ofnode_get_addr_size(ecc_node, "reg", &size); + if (base == FDT_ADDR_T_NONE) { + range->start = 0; + range->range = 0; + break; + } range->start = base; range->range = size; + range++; + ecc_node = get_next_ecc_node(ecc_node); } } @@ -798,13 +827,21 @@ static void k3_ddrss_lpddr4_ecc_calc_reserved_mem(struct k3_ddrss_desc *ddrss) static void k3_ddrss_lpddr4_ecc_init(struct k3_ddrss_desc *ddrss) { - u64 ecc_region_start = ddrss->ecc_regions[0].start; - u64 ecc_range = ddrss->ecc_regions[0].range; + u64 ecc_region0_start = ddrss->ecc_regions[0].start; + u64 ecc_range0 = ddrss->ecc_regions[0].range; + u64 ecc_region1_start = ddrss->ecc_regions[1].start; + u64 ecc_range1 = ddrss->ecc_regions[1].range; + u64 ecc_region2_start = ddrss->ecc_regions[2].start; + u64 ecc_range2 = ddrss->ecc_regions[2].range; u32 base = (u32)ddrss->ddrss_ss_cfg; u32 val; /* Only Program region 0 which covers full ddr space */ - k3_ddrss_set_ecc_range_r0(base, ecc_region_start, ecc_range); + k3_ddrss_set_ecc_range_rx(0, base, ecc_region0_start, ecc_range0); + if (ecc_range1) + k3_ddrss_set_ecc_range_rx(1, base, ecc_region1_start, ecc_range1); + if (ecc_range2) + k3_ddrss_set_ecc_range_rx(2, base, ecc_region2_start, ecc_range2); /* Enable ECC, RMW, WR_ALLOC */ writel(DDRSS_ECC_CTRL_REG_ECC_EN | DDRSS_ECC_CTRL_REG_RMW_EN | @@ -829,12 +866,12 @@ static void k3_ddrss_lpddr4_ecc_init(struct k3_ddrss_desc *ddrss) static int k3_ddrss_probe(struct udevice *dev) { - u64 end, bank0, bank1; + u64 end, bank0, bank1, bank0_size; int ret; struct k3_ddrss_desc *ddrss = dev_get_priv(dev); __maybe_unused u64 ddr_ram_size, ecc_res; - __maybe_unused u32 inst, st; - __maybe_unused struct k3_ddrss_ecc_region *range = &ddrss->ecc_range; + __maybe_unused u32 inst; + __maybe_unused struct k3_ddrss_ecc_region *range = ddrss->ecc_ranges; __maybe_unused struct k3_msmc *msmc_parent = NULL; debug("%s(dev=%p)\n", __func__, dev); @@ -875,39 +912,43 @@ static int k3_ddrss_probe(struct udevice *dev) k3_ddrss_ddr_inline_ecc_base_size_calc(range); - end = ddrss->ecc_range.start + ddrss->ecc_range.range; - inst = ddrss->instance; - ddr_ram_size = ddrss->ddr_ram_size; - ecc_res = ddrss->ecc_reserved_space; bank0 = ddrss->ddr_bank_base[0]; bank1 = ddrss->ddr_bank_base[1]; + bank0_size = ddrss->ddr_bank_size[0]; if (!range->range) { /* Configure entire DDR space by default */ debug("%s: Defaulting to protecting entire DDR space using inline ECC\n", __func__); - ddrss->ecc_range.start = bank0; - ddrss->ecc_range.range = ddr_ram_size - ecc_res; + ddrss->ecc_ranges[0].start = bank0; + ddrss->ecc_ranges[0].range = ddr_ram_size - ecc_res; } else { - ddrss->ecc_range.start = range->start; - ddrss->ecc_range.range = range->range; + ddrss->ecc_ranges[0].start = range->start; + ddrss->ecc_ranges[0].range = range->range; } - st = ddrss->ecc_range.start; - if (!CONFIG_IS_ENABLED(K3_MULTI_DDR)) { - if (end > (ddr_ram_size - ecc_res)) - ddrss->ecc_regions[0].range = ddr_ram_size - ecc_res; - else - ddrss->ecc_regions[0].range = ddrss->ecc_range.range; + struct k3_ddrss_ecc_region *r = range; - /* Check in which bank we are */ - if (st > bank1) - ddrss->ecc_regions[0].start = st - bank1 + ddrss->ddr_bank_size[0]; - else - ddrss->ecc_regions[0].start = st - bank0; + for (int i = 0; (i < K3_DDRSS_MAX_ECC_REG) && (r->range != 0); i++, r++) { + end = r->start + r->range; + ddr_ram_size = ddrss->ddr_ram_size; + ecc_res = ddrss->ecc_reserved_space; + + if (end > (ddr_ram_size - ecc_res)) + ddrss->ecc_regions[i].range = ddr_ram_size - ecc_res; + else + ddrss->ecc_regions[i].range = r->range; + + /* Check in which bank we are */ + if (r->start > bank1) + ddrss->ecc_regions[i].start = r->start - bank1 + bank0_size; + else + ddrss->ecc_regions[i].start = r->start - bank0; + } } else { /* For multi-DDR, we rely on MSMC's calculation of regions for each DDR */ + inst = ddrss->instance; msmc_parent = kzalloc(sizeof(msmc_parent), GFP_KERNEL); if (!msmc_parent) return -ENOMEM; @@ -922,7 +963,7 @@ static int k3_ddrss_probe(struct udevice *dev) if (msmc_parent->R0[0].start < 0) { /* Configure entire DDR space by default */ - ddrss->ecc_regions[0].start = ddrss->ddr_bank_base[0]; + ddrss->ecc_regions[0].start = bank0; ddrss->ecc_regions[0].range = ddr_ram_size - ecc_res; } else { end = msmc_parent->R0[inst].start + msmc_parent->R0[inst].range; -- cgit v1.2.3 From 3943531a54680b76813960655458e88f49f6bfb7 Mon Sep 17 00:00:00 2001 From: Chintan Vankar Date: Thu, 31 Jul 2025 13:29:36 +0530 Subject: net: ti: am65-cpsw-nuss: Define bind method for CPSW driver CPSW driver is defined as UCLASS_MISC driver which needs to be probed explicitly. Define bind method for CPSW driver to scan and bind ethernet-ports with UCLASS_ETH driver which will eventually probe CPSW driver and avoid probing CPSW driver explicitly. Signed-off-by: Chintan Vankar --- drivers/net/ti/am65-cpsw-nuss.c | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ti/am65-cpsw-nuss.c b/drivers/net/ti/am65-cpsw-nuss.c index 9b69f36d04d..754076d807c 100644 --- a/drivers/net/ti/am65-cpsw-nuss.c +++ b/drivers/net/ti/am65-cpsw-nuss.c @@ -705,7 +705,6 @@ static int am65_cpsw_probe_nuss(struct udevice *dev) struct am65_cpsw_common *cpsw_common = dev_get_priv(dev); ofnode ports_np, node; int ret, i; - struct udevice *port_dev; cpsw_common->dev = dev; cpsw_common->ss_base = dev_read_addr(dev); @@ -732,6 +731,7 @@ static int am65_cpsw_probe_nuss(struct udevice *dev) ports_np = dev_read_subnode(dev, "ethernet-ports"); if (!ofnode_valid(ports_np)) { ret = -ENOENT; + dev_err(dev, "Invalid device tree node %d\n", ret); goto out; } @@ -763,12 +763,6 @@ static int am65_cpsw_probe_nuss(struct udevice *dev) continue; cpsw_common->ports[port_id].disabled = disabled; - if (disabled) - continue; - - ret = device_bind_driver_to_node(dev, "am65_cpsw_nuss_port", ofnode_get_name(node), node, &port_dev); - if (ret) - dev_err(dev, "Failed to bind to %s node\n", ofnode_get_name(node)); } for (i = 0; i < AM65_CPSW_CPSWNU_MAX_PORTS; i++) { @@ -798,6 +792,37 @@ out: return ret; } +static int am65_cpsw_nuss_bind(struct udevice *dev) +{ + struct uclass_driver *drv; + struct udevice *port_dev; + ofnode ports_np, node; + int ret; + + drv = lists_uclass_lookup(UCLASS_ETH); + if (!drv) { + puts("Cannot find eth driver"); + return -ENOENT; + } + + ports_np = dev_read_subnode(dev, "ethernet-ports"); + if (!ofnode_valid(ports_np)) + return -ENOENT; + + ofnode_for_each_subnode(node, ports_np) { + const char *node_name; + + node_name = ofnode_get_name(node); + + ret = device_bind_driver_to_node(dev, "am65_cpsw_nuss_port", node_name, node, + &port_dev); + if (ret) + dev_err(dev, "Failed to bind to %s node\n", node_name); + } + + return ret; +} + static const struct udevice_id am65_cpsw_nuss_ids[] = { { .compatible = "ti,am654-cpsw-nuss" }, { .compatible = "ti,j721e-cpsw-nuss" }, @@ -809,6 +834,7 @@ U_BOOT_DRIVER(am65_cpsw_nuss) = { .name = "am65_cpsw_nuss", .id = UCLASS_MISC, .of_match = am65_cpsw_nuss_ids, + .bind = am65_cpsw_nuss_bind, .probe = am65_cpsw_probe_nuss, .priv_auto = sizeof(struct am65_cpsw_common), }; -- cgit v1.2.3 From 333b50d32d76ded3d69e3f65d47ca0de1d32dcc4 Mon Sep 17 00:00:00 2001 From: Chintan Vankar Date: Thu, 31 Jul 2025 13:29:42 +0530 Subject: net: ti: Kconfig: Enable SPL_SYSCON config for CPSW TI's Ethernet switch needs system controllers enabled in R5SPL stage while booting via Ethernet. Enable SPL_SYSCON config for CONFIG_TI_AM65_CPSW_NUSS. Reviewed-by: Alexander Sverdlin Signed-off-by: Chintan Vankar --- drivers/net/ti/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/ti/Kconfig b/drivers/net/ti/Kconfig index ddfa95a0b7e..3d495a56670 100644 --- a/drivers/net/ti/Kconfig +++ b/drivers/net/ti/Kconfig @@ -46,6 +46,7 @@ config TI_AM65_CPSW_NUSS imply MISC imply SYSCON imply MDIO_TI_CPSW + imply SPL_SYSCON select PHYLIB help This driver supports TI K3 MCU CPSW Nuss Ethernet controller -- cgit v1.2.3 From 2b751d42c35c36f7e3739d7bc7064f014e1263c7 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Tue, 12 Aug 2025 11:26:06 +0100 Subject: sound: maxim_codec: Fix coding mistake In maxim_i2c_read the code mistakenly just returned the return value from dm_i2c_read leaving the following code unreachable. Instead assign ret to be the return value from dm_i2c_read so that the following code can operate as expected. This issue was found by Smatch. Signed-off-by: Andrew Goodbody --- drivers/sound/maxim_codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/sound/maxim_codec.c b/drivers/sound/maxim_codec.c index 98f094c0e9a..505a739ad7d 100644 --- a/drivers/sound/maxim_codec.c +++ b/drivers/sound/maxim_codec.c @@ -45,7 +45,7 @@ unsigned int maxim_i2c_read(struct maxim_priv *priv, unsigned int reg, { int ret; - return dm_i2c_read(priv->dev, reg, data, 1); + ret = dm_i2c_read(priv->dev, reg, data, 1); if (ret != 0) { debug("%s: Error while reading register %#04x\n", __func__, reg); -- cgit v1.2.3 From e4507f4a0a7ac3b5346934df19c2daed626cc404 Mon Sep 17 00:00:00 2001 From: Maxim Kochetkov Date: Wed, 13 Aug 2025 08:54:32 +0300 Subject: serial-uclass: set GD_FLG_SERIAL_READY only when cur_serial_dev is assigned serial_find_console_or_panic() may left cur_serial_dev unassigned if REQUIRE_SERIAL_CONSOLE is not set. Setting GD_FLG_SERIAL_READY in this situation confuses serial console code. It tries to use unassigned driver instead of debug port and stops printing. So check cur_serial_dev before setting GD_FLG_SERIAL_READY to allow console to keep printing via debug port. Signed-off-by: Maxim Kochetkov --- drivers/serial/serial-uclass.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c index a08678dde4e..7b381ca12a0 100644 --- a/drivers/serial/serial-uclass.c +++ b/drivers/serial/serial-uclass.c @@ -191,7 +191,8 @@ int serial_init(void) { #if CONFIG_IS_ENABLED(SERIAL_PRESENT) serial_find_console_or_panic(); - gd->flags |= GD_FLG_SERIAL_READY; + if (gd->cur_serial_dev) + gd->flags |= GD_FLG_SERIAL_READY; if (IS_ENABLED(CONFIG_OF_SERIAL_BAUD)) { int ret = 0; -- cgit v1.2.3 From 15642829127cd503838f28254a1f3e080103ca4b Mon Sep 17 00:00:00 2001 From: Hari Nagalla Date: Wed, 13 Aug 2025 16:47:05 -0500 Subject: remoteproc: k3-r5: Add support for single cpu mode Add early boot support for AM64 single cpu mode configuration. In single CPU mode the 2nd core of the R5F cluster can't be used or unavailable. Signed-off-by: Hari Nagalla --- drivers/remoteproc/ti_k3_r5f_rproc.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/remoteproc/ti_k3_r5f_rproc.c b/drivers/remoteproc/ti_k3_r5f_rproc.c index 3a25ef6bf87..48401bc6eb6 100644 --- a/drivers/remoteproc/ti_k3_r5f_rproc.c +++ b/drivers/remoteproc/ti_k3_r5f_rproc.c @@ -233,7 +233,7 @@ static int k3_r5f_prepare(struct udevice *dev) dev_dbg(dev, "%s\n", __func__); - if (cluster->mode == CLUSTER_MODE_LOCKSTEP) + if ((cluster->mode == CLUSTER_MODE_LOCKSTEP) || (cluster->mode == CLUSTER_MODE_SINGLECPU)) ret = k3_r5f_lockstep_release(cluster); else ret = k3_r5f_split_release(core); @@ -269,6 +269,13 @@ static int k3_r5f_core_sanity_check(struct k3_r5f_core *core) return -EINVAL; } + if (cluster->mode == CLUSTER_MODE_SINGLECPU && !is_primary_core(core)) { + dev_err(core->dev, + "Invalid op: Trying to start secondary core %d in single CPU mode\n", + core->tsp.proc_id); + return -EINVAL; + } + if (cluster->mode == CLUSTER_MODE_SPLIT && !is_primary_core(core)) { if (!core->cluster->cores[0]->in_use) { dev_err(core->dev, @@ -768,7 +775,7 @@ static void k3_r5f_core_adjust_tcm_sizes(struct k3_r5f_core *core) { struct k3_r5f_cluster *cluster = core->cluster; - if (cluster->mode == CLUSTER_MODE_LOCKSTEP) + if ((cluster->mode == CLUSTER_MODE_LOCKSTEP) || (cluster->mode == CLUSTER_MODE_SINGLECPU)) return; if (!core->ipdata->tcm_is_double) -- cgit v1.2.3 From 2792cbf5d281b519692c546849638b2141046a4e Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Thu, 14 Aug 2025 20:51:43 +0530 Subject: remoteproc: k3: update compatible for am654 syscon The existing compatible name for U-Boot's k3 system controller driver i.e "ti,am625-system-controller" has been added to linux[1] device-tree. This compatible in kernel is meant for configuring the Control Module registers (CTRL_MMR0). However in U-Boot, the matching driver was being used to load the system firmware on the secure M-cores by the R5 SPL and therefore must be updated to a different compatible to avoid conflicts. Therefore, this patch renames all references of the compatible to "ti,am654-tisci-rproc-r5". The "-r5" is appended so as to avoid any future conflicts since r5 specific compatibles should only be useful for U-Boot. [1]: 5959618631fe ("dt-bindings: mfd: ti,j721e-system-controller: Add compatible string for AM654") https://lore.kernel.org/r/20250421214620.3770172-2-afd@ti.com Signed-off-by: Anshul Dalal --- drivers/remoteproc/k3_system_controller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/remoteproc/k3_system_controller.c b/drivers/remoteproc/k3_system_controller.c index 71238a6058a..e59c010de7e 100644 --- a/drivers/remoteproc/k3_system_controller.c +++ b/drivers/remoteproc/k3_system_controller.c @@ -327,7 +327,7 @@ static const struct k3_sysctrler_desc k3_sysctrler_am654_desc = { static const struct udevice_id k3_sysctrler_ids[] = { { - .compatible = "ti,am654-system-controller", + .compatible = "ti,am654-tisci-rproc-r5", .data = (ulong)&k3_sysctrler_am654_desc, }, {} -- cgit v1.2.3 From 91595c96a53360dce696c2da694b1983c91d64f6 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Fri, 15 Aug 2025 08:17:19 +0200 Subject: i2c: omap24xx_i2c: remove unused members of struct omap_i2c The clk and clk_id members of struct omap_i2c are not used anywhere, and AFAICT never have been. Signed-off-by: Rasmus Villemoes Reviewed-by: Aniket Limaye --- drivers/i2c/omap24xx_i2c.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c index a6361d3d17d..2c6f8ea5901 100644 --- a/drivers/i2c/omap24xx_i2c.c +++ b/drivers/i2c/omap24xx_i2c.c @@ -134,12 +134,10 @@ static const u8 __maybe_unused reg_map_ip_v2[] = { }; struct omap_i2c { - struct udevice *clk; int ip_rev; struct i2c *regs; unsigned int speed; int waitdelay; - int clk_id; }; static inline const u8 *omap_i2c_get_ip_reg_map(int ip_rev) -- cgit v1.2.3 From 5727df875002ee8233c983aaed923c04a945be8c Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Wed, 6 Aug 2025 08:55:17 -0600 Subject: tpm: tpm_tis_st33zp24: Remove unused drivers The tpm_tis_st33zp24_i2c and tpm_tis_st33zp24_spi drivers are unused. Remove them. Signed-off-by: Tom Rini Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- drivers/tpm/Kconfig | 18 - drivers/tpm/Makefile | 2 - drivers/tpm/tpm_tis_st33zp24_i2c.c | 545 ------------------------------ drivers/tpm/tpm_tis_st33zp24_spi.c | 674 ------------------------------------- 4 files changed, 1239 deletions(-) delete mode 100644 drivers/tpm/tpm_tis_st33zp24_i2c.c delete mode 100644 drivers/tpm/tpm_tis_st33zp24_spi.c (limited to 'drivers') diff --git a/drivers/tpm/Kconfig b/drivers/tpm/Kconfig index 01bc686d367..219ea606b50 100644 --- a/drivers/tpm/Kconfig +++ b/drivers/tpm/Kconfig @@ -75,24 +75,6 @@ config TPM_AUTH_SESSIONS TPM_LoadKey2 and TPM_GetPubKey are provided. Both features are available using the 'tpm' command, too. -config TPM_ST33ZP24_I2C - bool "STMicroelectronics ST33ZP24 I2C TPM" - depends on TPM_V1 && DM_I2C - ---help--- - This driver supports STMicroelectronics TPM devices connected on the I2C bus. - The usual tpm operations and the 'tpm' command can be used to talk - to the device using the standard TPM Interface Specification (TIS) - protocol - -config TPM_ST33ZP24_SPI - bool "STMicroelectronics ST33ZP24 SPI TPM" - depends on TPM_V1 && DM_SPI - ---help--- - This driver supports STMicroelectronics TPM devices connected on the SPI bus. - The usual tpm operations and the 'tpm' command can be used to talk - to the device using the standard TPM Interface Specification (TIS) - protocol - config TPM_FLUSH_RESOURCES bool "Enable TPM resource flushing support" depends on TPM_V1 diff --git a/drivers/tpm/Makefile b/drivers/tpm/Makefile index 76e516dbbaf..b83ce703ec0 100644 --- a/drivers/tpm/Makefile +++ b/drivers/tpm/Makefile @@ -7,8 +7,6 @@ obj-$(CONFIG_TPM_ATMEL_TWI) += tpm_atmel_twi.o obj-$(CONFIG_TPM_TIS_INFINEON) += tpm_tis_infineon.o obj-$(CONFIG_TPM_TIS_LPC) += tpm_tis_lpc.o obj-$(CONFIG_TPM_TIS_SANDBOX) += tpm_tis_sandbox.o sandbox_common.o -obj-$(CONFIG_TPM_ST33ZP24_I2C) += tpm_tis_st33zp24_i2c.o -obj-$(CONFIG_TPM_ST33ZP24_SPI) += tpm_tis_st33zp24_spi.o obj-$(CONFIG_$(PHASE_)TPM2_CR50_I2C) += cr50_i2c.o obj-$(CONFIG_TPM2_TIS_SANDBOX) += tpm2_tis_sandbox.o sandbox_common.o diff --git a/drivers/tpm/tpm_tis_st33zp24_i2c.c b/drivers/tpm/tpm_tis_st33zp24_i2c.c deleted file mode 100644 index 1a265b28b22..00000000000 --- a/drivers/tpm/tpm_tis_st33zp24_i2c.c +++ /dev/null @@ -1,545 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * STMicroelectronics TPM ST33ZP24 I2C UBOOT driver - * - * Copyright (C) 2016, STMicroelectronics - All Rights Reserved - * Author(s): Christophe Ricard for STMicroelectronics. - * - * Description: Device driver for ST33ZP24 I2C TPM TCG. - * - * This device driver implements the TPM interface as defined in - * the TCG TPM Interface Spec version 1.21, revision 1.0 and the - * STMicroelectronics Protocol Stack Specification version 1.2.0. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tpm_tis.h" -#include "tpm_internal.h" - -#define TPM_ACCESS 0x0 -#define TPM_STS 0x18 -#define TPM_DATA_FIFO 0x24 - -#define LOCALITY0 0 - -#define TPM_DUMMY_BYTE 0xAA -#define TPM_ST33ZP24_I2C_SLAVE_ADDR 0x13 - -#define TPM_WRITE_DIRECTION 0x80 - -/* - * st33zp24_i2c_write8_reg - * Send byte to the TIS register according to the ST33ZP24 I2C protocol. - * @param: tpm_register, the tpm tis register where the data should be written - * @param: tpm_data, the tpm_data to write inside the tpm_register - * @param: tpm_size, The length of the data - * @return: Number of byte written successfully else an error code. - */ -static int st33zp24_i2c_write8_reg(struct udevice *dev, u8 tpm_register, - const u8 *tpm_data, size_t tpm_size) -{ - struct tpm_chip_priv *chip_priv = dev_get_uclass_priv(dev); - - chip_priv->buf[0] = tpm_register; - memcpy(chip_priv->buf + 1, tpm_data, tpm_size); - - return dm_i2c_write(dev, 0, chip_priv->buf, tpm_size + 1); -} - -/* -* st33zp24_i2c_read8_reg -* Recv byte from the TIS register according to the ST33ZP24 I2C protocol. -* @param: tpm_register, the tpm tis register where the data should be read -* @param: tpm_data, the TPM response -* @param: tpm_size, tpm TPM response size to read. -* @return: Number of byte read successfully else an error code. -*/ -static int st33zp24_i2c_read8_reg(struct udevice *dev, u8 tpm_register, - u8 *tpm_data, size_t tpm_size) -{ - int status; - u8 data; - - data = TPM_DUMMY_BYTE; - status = st33zp24_i2c_write8_reg(dev, tpm_register, &data, 1); - if (status < 0) - return status; - - return dm_i2c_read(dev, 0, tpm_data, tpm_size); -} - -/* - * st33zp24_i2c_write - * Send byte to the TIS register according to the ST33ZP24 I2C protocol. - * @param: phy_id, the phy description - * @param: tpm_register, the tpm tis register where the data should be written - * @param: tpm_data, the tpm_data to write inside the tpm_register - * @param: tpm_size, the length of the data - * @return: number of byte written successfully: should be one if success. - */ -static int st33zp24_i2c_write(struct udevice *dev, u8 tpm_register, - const u8 *tpm_data, size_t tpm_size) -{ - return st33zp24_i2c_write8_reg(dev, tpm_register | TPM_WRITE_DIRECTION, - tpm_data, tpm_size); -} - -/* - * st33zp24_i2c_read - * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. - * @param: phy_id, the phy description - * @param: tpm_register, the tpm tis register where the data should be read - * @param: tpm_data, the TPM response - * @param: tpm_size, tpm TPM response size to read. - * @return: number of byte read successfully: should be one if success. - */ -static int st33zp24_i2c_read(struct udevice *dev, u8 tpm_register, - u8 *tpm_data, size_t tpm_size) -{ - return st33zp24_i2c_read8_reg(dev, tpm_register, tpm_data, tpm_size); -} - -/* - * st33zp24_i2c_release_locality release the active locality - * @param: chip, the tpm chip description. - */ -static void st33zp24_i2c_release_locality(struct udevice *dev) -{ - u8 data = TPM_ACCESS_ACTIVE_LOCALITY; - - st33zp24_i2c_write(dev, TPM_ACCESS, &data, 1); -} - -/* - * st33zp24_i2c_check_locality if the locality is active - * @param: chip, the tpm chip description - * @return: the active locality or -EACCES. - */ -static int st33zp24_i2c_check_locality(struct udevice *dev) -{ - struct tpm_chip *chip = dev_get_priv(dev); - u8 data; - u8 status; - - status = st33zp24_i2c_read(dev, TPM_ACCESS, &data, 1); - if (!status && (data & - (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == - (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) - return chip->locality; - - return -EACCES; -} - -/* - * st33zp24_i2c_request_locality request the TPM locality - * @param: chip, the chip description - * @return: the active locality or negative value. - */ -static int st33zp24_i2c_request_locality(struct udevice *dev) -{ - struct tpm_chip *chip = dev_get_priv(dev); - unsigned long start, stop; - long ret; - u8 data; - - if (st33zp24_i2c_check_locality(dev) == chip->locality) - return chip->locality; - - data = TPM_ACCESS_REQUEST_USE; - ret = st33zp24_i2c_write(dev, TPM_ACCESS, &data, 1); - if (ret < 0) - return ret; - - /* wait for locality activated */ - start = get_timer(0); - stop = chip->timeout_a; - do { - if (st33zp24_i2c_check_locality(dev) >= 0) - return chip->locality; - udelay(TPM_TIMEOUT_MS * 1000); - } while (get_timer(start) < stop); - - return -EACCES; -} - -/* - * st33zp24_i2c_status return the TPM_STS register - * @param: chip, the tpm chip description - * @return: the TPM_STS register value. - */ -static u8 st33zp24_i2c_status(struct udevice *dev) -{ - u8 data; - - st33zp24_i2c_read(dev, TPM_STS, &data, 1); - - return data; -} - -/* - * st33zp24_i2c_get_burstcount return the burstcount address 0x19 0x1A - * @param: chip, the chip description - * return: the burstcount or -TPM_DRIVER_ERR in case of error. - */ -static int st33zp24_i2c_get_burstcount(struct udevice *dev) -{ - struct tpm_chip *chip = dev_get_priv(dev); - unsigned long start, stop; - int burstcnt, status; - u8 tpm_reg, temp; - - /* wait for burstcount */ - start = get_timer(0); - stop = chip->timeout_d; - do { - tpm_reg = TPM_STS + 1; - status = st33zp24_i2c_read(dev, tpm_reg, &temp, 1); - if (status < 0) - return -EBUSY; - - tpm_reg = TPM_STS + 2; - burstcnt = temp; - status = st33zp24_i2c_read(dev, tpm_reg, &temp, 1); - if (status < 0) - return -EBUSY; - - burstcnt |= temp << 8; - if (burstcnt) - return burstcnt; - udelay(TIS_SHORT_TIMEOUT_MS * 1000); - } while (get_timer(start) < stop); - - return -EBUSY; -} - -/* - * st33zp24_i2c_cancel, cancel the current command execution or - * set STS to COMMAND READY. - * @param: chip, tpm_chip description. - */ -static void st33zp24_i2c_cancel(struct udevice *dev) -{ - u8 data; - - data = TPM_STS_COMMAND_READY; - st33zp24_i2c_write(dev, TPM_STS, &data, 1); -} - -/* - * st33zp24_i2c_wait_for_stat wait for a TPM_STS value - * @param: chip, the tpm chip description - * @param: mask, the value mask to wait - * @param: timeout, the timeout - * @param: status, - * @return: the tpm status, 0 if success, -ETIME if timeout is reached. - */ -static int st33zp24_i2c_wait_for_stat(struct udevice *dev, u8 mask, - unsigned long timeout, int *status) -{ - unsigned long start, stop; - - /* Check current status */ - *status = st33zp24_i2c_status(dev); - if ((*status & mask) == mask) - return 0; - - start = get_timer(0); - stop = timeout; - do { - udelay(TPM_TIMEOUT_MS * 1000); - *status = st33zp24_i2c_status(dev); - if ((*status & mask) == mask) - return 0; - } while (get_timer(start) < stop); - - return -ETIME; -} - -/* - * st33zp24_i2c_recv_data receive data - * @param: chip, the tpm chip description - * @param: buf, the buffer where the data are received - * @param: count, the number of data to receive - * @return: the number of bytes read from TPM FIFO. - */ -static int st33zp24_i2c_recv_data(struct udevice *dev, u8 *buf, size_t count) -{ - struct tpm_chip *chip = dev_get_priv(dev); - int size = 0, burstcnt, len, ret, status; - - while (size < count && - st33zp24_i2c_wait_for_stat(dev, TPM_STS_DATA_AVAIL | TPM_STS_VALID, - chip->timeout_c, &status) == 0) { - burstcnt = st33zp24_i2c_get_burstcount(dev); - if (burstcnt < 0) - return burstcnt; - len = min_t(int, burstcnt, count - size); - ret = st33zp24_i2c_read(dev, TPM_DATA_FIFO, buf + size, len); - if (ret < 0) - return ret; - - size += len; - } - - return size; -} - -/* - * st33zp24_i2c_recv received TPM response through TPM phy. - * @param: chip, tpm_chip description. - * @param: buf, the buffer to store data. - * @param: count, the number of bytes that can received (sizeof buf). - * @return: Returns zero in case of success else -EIO. - */ -static int st33zp24_i2c_recv(struct udevice *dev, u8 *buf, size_t count) -{ - struct tpm_chip *chip = dev_get_priv(dev); - int size; - unsigned int expected; - - if (!chip) - return -ENODEV; - - if (count < TPM_HEADER_SIZE) { - size = -EIO; - goto out; - } - - size = st33zp24_i2c_recv_data(dev, buf, TPM_HEADER_SIZE); - if (size < TPM_HEADER_SIZE) { - debug("TPM error, unable to read header\n"); - goto out; - } - - expected = get_unaligned_be32(buf + 2); - if (expected > count || expected < TPM_HEADER_SIZE) { - size = -EIO; - goto out; - } - - size += st33zp24_i2c_recv_data(dev, &buf[TPM_HEADER_SIZE], - expected - TPM_HEADER_SIZE); - if (size < expected) { - debug("TPM error, unable to read remaining bytes of result\n"); - size = -EIO; - goto out; - } - -out: - st33zp24_i2c_cancel(dev); - st33zp24_i2c_release_locality(dev); - - return size; -} - -/* - * st33zp24_i2c_send send TPM commands through TPM phy. - * @param: chip, tpm_chip description. - * @param: buf, the buffer to send. - * @param: len, the number of bytes to send. - * @return: Returns zero in case of success else the negative error code. - */ -static int st33zp24_i2c_send(struct udevice *dev, const u8 *buf, size_t len) -{ - struct tpm_chip *chip = dev_get_priv(dev); - u32 i, size; - int burstcnt, ret, status; - u8 data, tpm_stat; - - if (!chip) - return -ENODEV; - if (len < TPM_HEADER_SIZE) - return -EIO; - - ret = st33zp24_i2c_request_locality(dev); - if (ret < 0) - return ret; - - tpm_stat = st33zp24_i2c_status(dev); - if ((tpm_stat & TPM_STS_COMMAND_READY) == 0) { - st33zp24_i2c_cancel(dev); - if (st33zp24_i2c_wait_for_stat(dev, TPM_STS_COMMAND_READY, - chip->timeout_b, &status) < 0) { - ret = -ETIME; - goto out_err; - } - } - - for (i = 0; i < len - 1;) { - burstcnt = st33zp24_i2c_get_burstcount(dev); - if (burstcnt < 0) - return burstcnt; - - size = min_t(int, len - i - 1, burstcnt); - ret = st33zp24_i2c_write(dev, TPM_DATA_FIFO, buf + i, size); - if (ret < 0) - goto out_err; - - i += size; - } - - tpm_stat = st33zp24_i2c_status(dev); - if ((tpm_stat & TPM_STS_DATA_EXPECT) == 0) { - ret = -EIO; - goto out_err; - } - - ret = st33zp24_i2c_write(dev, TPM_DATA_FIFO, buf + len - 1, 1); - if (ret < 0) - goto out_err; - - tpm_stat = st33zp24_i2c_status(dev); - if ((tpm_stat & TPM_STS_DATA_EXPECT) != 0) { - ret = -EIO; - goto out_err; - } - - data = TPM_STS_GO; - ret = st33zp24_i2c_write(dev, TPM_STS, &data, 1); - if (ret < 0) - goto out_err; - - return len; - -out_err: - st33zp24_i2c_cancel(dev); - st33zp24_i2c_release_locality(dev); - - return ret; -} - -static int st33zp24_i2c_cleanup(struct udevice *dev) -{ - st33zp24_i2c_cancel(dev); - /* - * The TPM needs some time to clean up here, - * so we sleep rather than keeping the bus busy - */ - mdelay(2); - st33zp24_i2c_release_locality(dev); - - return 0; -} - -static int st33zp24_i2c_init(struct udevice *dev) -{ - struct tpm_chip *chip = dev_get_priv(dev); - - chip->is_open = 1; - - /* Default timeouts - these could move to the device tree */ - chip->timeout_a = TIS_SHORT_TIMEOUT_MS; - chip->timeout_b = TIS_LONG_TIMEOUT_MS; - chip->timeout_c = TIS_SHORT_TIMEOUT_MS; - chip->timeout_d = TIS_SHORT_TIMEOUT_MS; - - chip->locality = LOCALITY0; - - /* - * A timeout query to TPM can be placed here. - * Standard timeout values are used so far - */ - - return 0; -} - -static int st33zp24_i2c_open(struct udevice *dev) -{ - struct tpm_chip *chip = dev_get_priv(dev); - int rc; - - debug("%s: start\n", __func__); - if (chip->is_open) - return -EBUSY; - - rc = st33zp24_i2c_init(dev); - if (rc < 0) - chip->is_open = 0; - - return rc; -} - -static int st33zp24_i2c_close(struct udevice *dev) -{ - struct tpm_chip *chip = dev_get_priv(dev); - - if (chip->is_open) { - st33zp24_i2c_release_locality(dev); - chip->is_open = 0; - chip->vend_dev = 0; - } - - return 0; -} - -static int st33zp24_i2c_get_desc(struct udevice *dev, char *buf, int size) -{ - struct tpm_chip *chip = dev_get_priv(dev); - - if (size < 50) - return -ENOSPC; - - return snprintf(buf, size, "1.2 TPM (%s, chip type %s device-id 0x%x)", - chip->is_open ? "open" : "closed", - dev->name, - chip->vend_dev >> 16); -} - -static const struct tpm_ops st33zp24_i2c_tpm_ops = { - .open = st33zp24_i2c_open, - .close = st33zp24_i2c_close, - .recv = st33zp24_i2c_recv, - .send = st33zp24_i2c_send, - .cleanup = st33zp24_i2c_cleanup, - .get_desc = st33zp24_i2c_get_desc, -}; - -static int st33zp24_i2c_probe(struct udevice *dev) -{ - struct tpm_chip *chip = dev_get_priv(dev); - - /* Default timeouts */ - chip->timeout_a = TIS_SHORT_TIMEOUT_MS; - chip->timeout_b = TIS_LONG_TIMEOUT_MS; - chip->timeout_c = TIS_SHORT_TIMEOUT_MS; - chip->timeout_d = TIS_SHORT_TIMEOUT_MS; - - chip->locality = LOCALITY0; - - i2c_set_chip_offset_len(dev, 0); - - debug("ST33ZP24 I2C TPM from STMicroelectronics found\n"); - - return 0; -} - -static int st33zp24_i2c_remove(struct udevice *dev) -{ - st33zp24_i2c_release_locality(dev); - - return 0; -} - -static const struct udevice_id st33zp24_i2c_ids[] = { - { .compatible = "st,st33zp24-i2c" }, - { } -}; - -U_BOOT_DRIVER(st33zp24_i2c) = { - .name = "st33zp24-i2c", - .id = UCLASS_TPM, - .of_match = of_match_ptr(st33zp24_i2c_ids), - .probe = st33zp24_i2c_probe, - .remove = st33zp24_i2c_remove, - .ops = &st33zp24_i2c_tpm_ops, - .priv_auto = sizeof(struct tpm_chip), -}; diff --git a/drivers/tpm/tpm_tis_st33zp24_spi.c b/drivers/tpm/tpm_tis_st33zp24_spi.c deleted file mode 100644 index 2cf690328d8..00000000000 --- a/drivers/tpm/tpm_tis_st33zp24_spi.c +++ /dev/null @@ -1,674 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * STMicroelectronics TPM ST33ZP24 SPI UBOOT driver - * - * Copyright (C) 2016, STMicroelectronics - All Rights Reserved - * Author(s): Christophe Ricard for STMicroelectronics. - * - * Description: Device driver for ST33ZP24 SPI TPM TCG. - * - * This device driver implements the TPM interface as defined in - * the TCG TPM Interface Spec version 1.21, revision 1.0 and the - * STMicroelectronics Protocol Stack Specification version 1.2.0. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tpm_tis.h" -#include "tpm_internal.h" - -#define TPM_ACCESS 0x0 -#define TPM_STS 0x18 -#define TPM_DATA_FIFO 0x24 - -#define LOCALITY0 0 - -#define TPM_DATA_FIFO 0x24 -#define TPM_INTF_CAPABILITY 0x14 - -#define TPM_DUMMY_BYTE 0x00 -#define TPM_WRITE_DIRECTION 0x80 - -#define MAX_SPI_LATENCY 15 -#define LOCALITY0 0 - -#define ST33ZP24_OK 0x5A -#define ST33ZP24_UNDEFINED_ERR 0x80 -#define ST33ZP24_BADLOCALITY 0x81 -#define ST33ZP24_TISREGISTER_UKNOWN 0x82 -#define ST33ZP24_LOCALITY_NOT_ACTIVATED 0x83 -#define ST33ZP24_HASH_END_BEFORE_HASH_START 0x84 -#define ST33ZP24_BAD_COMMAND_ORDER 0x85 -#define ST33ZP24_INCORECT_RECEIVED_LENGTH 0x86 -#define ST33ZP24_TPM_FIFO_OVERFLOW 0x89 -#define ST33ZP24_UNEXPECTED_READ_FIFO 0x8A -#define ST33ZP24_UNEXPECTED_WRITE_FIFO 0x8B -#define ST33ZP24_CMDRDY_SET_WHEN_PROCESSING_HASH_END 0x90 -#define ST33ZP24_DUMMY_BYTES 0x00 - -/* - * TPM command can be up to 2048 byte, A TPM response can be up to - * 1024 byte. - * Between command and response, there are latency byte (up to 15 - * usually on st33zp24 2 are enough). - * - * Overall when sending a command and expecting an answer we need if - * worst case: - * 2048 (for the TPM command) + 1024 (for the TPM answer). We need - * some latency byte before the answer is available (max 15). - * We have 2048 + 1024 + 15. - */ -#define ST33ZP24_SPI_BUFFER_SIZE (TPM_BUFSIZE + (TPM_BUFSIZE / 2) +\ - MAX_SPI_LATENCY) - -struct st33zp24_spi_phy { - int latency; - - u8 tx_buf[ST33ZP24_SPI_BUFFER_SIZE]; - u8 rx_buf[ST33ZP24_SPI_BUFFER_SIZE]; -}; - -static int st33zp24_spi_status_to_errno(u8 code) -{ - switch (code) { - case ST33ZP24_OK: - return 0; - case ST33ZP24_UNDEFINED_ERR: - case ST33ZP24_BADLOCALITY: - case ST33ZP24_TISREGISTER_UKNOWN: - case ST33ZP24_LOCALITY_NOT_ACTIVATED: - case ST33ZP24_HASH_END_BEFORE_HASH_START: - case ST33ZP24_BAD_COMMAND_ORDER: - case ST33ZP24_UNEXPECTED_READ_FIFO: - case ST33ZP24_UNEXPECTED_WRITE_FIFO: - case ST33ZP24_CMDRDY_SET_WHEN_PROCESSING_HASH_END: - return -EPROTO; - case ST33ZP24_INCORECT_RECEIVED_LENGTH: - case ST33ZP24_TPM_FIFO_OVERFLOW: - return -EMSGSIZE; - case ST33ZP24_DUMMY_BYTES: - return -ENOSYS; - } - return code; -} - -/* - * st33zp24_spi_send - * Send byte to TPM register according to the ST33ZP24 SPI protocol. - * @param: tpm, the chip description - * @param: tpm_register, the tpm tis register where the data should be written - * @param: tpm_data, the tpm_data to write inside the tpm_register - * @param: tpm_size, The length of the data - * @return: should be zero if success else a negative error code. - */ -static int st33zp24_spi_write(struct udevice *dev, u8 tpm_register, - const u8 *tpm_data, size_t tpm_size) -{ - int total_length = 0, ret; - struct spi_slave *slave = dev_get_parent_priv(dev); - struct st33zp24_spi_phy *phy = dev_get_plat(dev); - - u8 *tx_buf = (u8 *)phy->tx_buf; - u8 *rx_buf = phy->rx_buf; - - tx_buf[total_length++] = TPM_WRITE_DIRECTION | LOCALITY0; - tx_buf[total_length++] = tpm_register; - - if (tpm_size > 0 && tpm_register == TPM_DATA_FIFO) { - tx_buf[total_length++] = tpm_size >> 8; - tx_buf[total_length++] = tpm_size; - } - memcpy(tx_buf + total_length, tpm_data, tpm_size); - total_length += tpm_size; - - memset(tx_buf + total_length, TPM_DUMMY_BYTE, phy->latency); - - total_length += phy->latency; - - ret = spi_claim_bus(slave); - if (ret < 0) - return ret; - - ret = spi_xfer(slave, total_length * 8, tx_buf, rx_buf, - SPI_XFER_BEGIN | SPI_XFER_END); - if (ret < 0) - return ret; - - spi_release_bus(slave); - - if (ret == 0) - ret = rx_buf[total_length - 1]; - - return st33zp24_spi_status_to_errno(ret); -} - -/* - * spi_st33zp24_spi_read8_reg - * Recv byte from the TIS register according to the ST33ZP24 SPI protocol. - * @param: tpm, the chip description - * @param: tpm_loc, the locality to read register from - * @param: tpm_register, the tpm tis register where the data should be read - * @param: tpm_data, the TPM response - * @param: tpm_size, tpm TPM response size to read. - * @return: should be zero if success else a negative error code. - */ -static u8 st33zp24_spi_read8_reg(struct udevice *dev, u8 tpm_register, - u8 *tpm_data, size_t tpm_size) -{ - int total_length = 0, ret; - struct spi_slave *slave = dev_get_parent_priv(dev); - struct st33zp24_spi_phy *phy = dev_get_plat(dev); - - u8 *tx_buf = (u8 *)phy->tx_buf; - u8 *rx_buf = phy->rx_buf; - - /* Pre-Header */ - tx_buf[total_length++] = LOCALITY0; - tx_buf[total_length++] = tpm_register; - - memset(&tx_buf[total_length], TPM_DUMMY_BYTE, - phy->latency + tpm_size); - total_length += phy->latency + tpm_size; - - ret = spi_claim_bus(slave); - if (ret < 0) - return 0; - - ret = spi_xfer(slave, total_length * 8, tx_buf, rx_buf, - SPI_XFER_BEGIN | SPI_XFER_END); - if (ret < 0) - return 0; - - spi_release_bus(slave); - - if (tpm_size > 0 && ret == 0) { - ret = rx_buf[total_length - tpm_size - 1]; - memcpy(tpm_data, rx_buf + total_length - tpm_size, tpm_size); - } - return ret; -} - -/* - * st33zp24_spi_recv - * Recv byte from the TIS register according to the ST33ZP24 SPI protocol. - * @param: phy_id, the phy description - * @param: tpm_register, the tpm tis register where the data should be read - * @param: tpm_data, the TPM response - * @param: tpm_size, tpm TPM response size to read. - * @return: number of byte read successfully: should be one if success. - */ -static int st33zp24_spi_read(struct udevice *dev, u8 tpm_register, - u8 *tpm_data, size_t tpm_size) -{ - int ret; - - ret = st33zp24_spi_read8_reg(dev, tpm_register, tpm_data, tpm_size); - if (!st33zp24_spi_status_to_errno(ret)) - return tpm_size; - - return ret; -} - -static int st33zp24_spi_evaluate_latency(struct udevice *dev) -{ - int latency = 1, status = 0; - u8 data = 0; - struct st33zp24_spi_phy *phy = dev_get_plat(dev); - - while (!status && latency < MAX_SPI_LATENCY) { - phy->latency = latency; - status = st33zp24_spi_read8_reg(dev, TPM_INTF_CAPABILITY, - &data, 1); - latency++; - } - if (status < 0) - return status; - if (latency == MAX_SPI_LATENCY) - return -ENODEV; - - return latency - 1; -} - -/* - * st33zp24_spi_release_locality release the active locality - * @param: chip, the tpm chip description. - */ -static void st33zp24_spi_release_locality(struct udevice *dev) -{ - u8 data = TPM_ACCESS_ACTIVE_LOCALITY; - - st33zp24_spi_write(dev, TPM_ACCESS, &data, 1); -} - -/* - * st33zp24_spi_check_locality if the locality is active - * @param: chip, the tpm chip description - * @return: the active locality or -EACCES. - */ -static int st33zp24_spi_check_locality(struct udevice *dev) -{ - u8 data; - u8 status; - struct tpm_chip *chip = dev_get_priv(dev); - - status = st33zp24_spi_read(dev, TPM_ACCESS, &data, 1); - if (status && (data & - (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == - (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) - return chip->locality; - - return -EACCES; -} - -/* - * st33zp24_spi_request_locality request the TPM locality - * @param: chip, the chip description - * @return: the active locality or negative value. - */ -static int st33zp24_spi_request_locality(struct udevice *dev) -{ - unsigned long start, stop; - long ret; - u8 data; - struct tpm_chip *chip = dev_get_priv(dev); - - if (st33zp24_spi_check_locality(dev) == chip->locality) - return chip->locality; - - data = TPM_ACCESS_REQUEST_USE; - ret = st33zp24_spi_write(dev, TPM_ACCESS, &data, 1); - if (ret < 0) - return ret; - - /* wait for locality activated */ - start = get_timer(0); - stop = chip->timeout_a; - do { - if (st33zp24_spi_check_locality(dev) >= 0) - return chip->locality; - udelay(TPM_TIMEOUT_MS * 1000); - } while (get_timer(start) < stop); - - return -EACCES; -} - -/* - * st33zp24_spi_status return the TPM_STS register - * @param: chip, the tpm chip description - * @return: the TPM_STS register value. - */ -static u8 st33zp24_spi_status(struct udevice *dev) -{ - u8 data; - - st33zp24_spi_read(dev, TPM_STS, &data, 1); - return data; -} - -/* - * st33zp24_spi_get_burstcount return the burstcount address 0x19 0x1A - * @param: chip, the chip description - * return: the burstcount or -TPM_DRIVER_ERR in case of error. - */ -static int st33zp24_spi_get_burstcount(struct udevice *dev) -{ - struct tpm_chip *chip = dev_get_priv(dev); - unsigned long start, stop; - int burstcnt, status; - u8 tpm_reg, temp; - - /* wait for burstcount */ - start = get_timer(0); - stop = chip->timeout_d; - do { - tpm_reg = TPM_STS + 1; - status = st33zp24_spi_read(dev, tpm_reg, &temp, 1); - if (status < 0) - return -EBUSY; - - tpm_reg = TPM_STS + 2; - burstcnt = temp; - status = st33zp24_spi_read(dev, tpm_reg, &temp, 1); - if (status < 0) - return -EBUSY; - - burstcnt |= temp << 8; - if (burstcnt) - return burstcnt; - udelay(TIS_SHORT_TIMEOUT_MS * 1000); - } while (get_timer(start) < stop); - - return -EBUSY; -} - -/* - * st33zp24_spi_cancel, cancel the current command execution or - * set STS to COMMAND READY. - * @param: chip, tpm_chip description. - */ -static void st33zp24_spi_cancel(struct udevice *dev) -{ - u8 data; - - data = TPM_STS_COMMAND_READY; - st33zp24_spi_write(dev, TPM_STS, &data, 1); -} - -/* - * st33zp24_spi_wait_for_stat wait for a TPM_STS value - * @param: chip, the tpm chip description - * @param: mask, the value mask to wait - * @param: timeout, the timeout - * @param: status, - * @return: the tpm status, 0 if success, -ETIME if timeout is reached. - */ -static int st33zp24_spi_wait_for_stat(struct udevice *dev, u8 mask, - unsigned long timeout, int *status) -{ - unsigned long start, stop; - - /* Check current status */ - *status = st33zp24_spi_status(dev); - if ((*status & mask) == mask) - return 0; - - start = get_timer(0); - stop = timeout; - do { - udelay(TPM_TIMEOUT_MS * 1000); - *status = st33zp24_spi_status(dev); - if ((*status & mask) == mask) - return 0; - } while (get_timer(start) < stop); - - return -ETIME; -} - -/* - * st33zp24_spi_recv_data receive data - * @param: chip, the tpm chip description - * @param: buf, the buffer where the data are received - * @param: count, the number of data to receive - * @return: the number of bytes read from TPM FIFO. - */ -static int st33zp24_spi_recv_data(struct udevice *dev, u8 *buf, size_t count) -{ - struct tpm_chip *chip = dev_get_priv(dev); - int size = 0, burstcnt, len, ret, status; - - while (size < count && - st33zp24_spi_wait_for_stat(dev, TPM_STS_DATA_AVAIL | TPM_STS_VALID, - chip->timeout_c, &status) == 0) { - burstcnt = st33zp24_spi_get_burstcount(dev); - if (burstcnt < 0) - return burstcnt; - len = min_t(int, burstcnt, count - size); - ret = st33zp24_spi_read(dev, TPM_DATA_FIFO, buf + size, len); - if (ret < 0) - return ret; - - size += len; - } - return size; -} - -/* - * st33zp24_spi_recv received TPM response through TPM phy. - * @param: chip, tpm_chip description. - * @param: buf, the buffer to store data. - * @param: count, the number of bytes that can received (sizeof buf). - * @return: Returns zero in case of success else -EIO. - */ -static int st33zp24_spi_recv(struct udevice *dev, u8 *buf, size_t count) -{ - struct tpm_chip *chip = dev_get_priv(dev); - int size; - unsigned int expected; - - if (!chip) - return -ENODEV; - - if (count < TPM_HEADER_SIZE) { - size = -EIO; - goto out; - } - - size = st33zp24_spi_recv_data(dev, buf, TPM_HEADER_SIZE); - if (size < TPM_HEADER_SIZE) { - debug("TPM error, unable to read header\n"); - goto out; - } - - expected = get_unaligned_be32(buf + 2); - if (expected > count || expected < TPM_HEADER_SIZE) { - size = -EIO; - goto out; - } - - size += st33zp24_spi_recv_data(dev, &buf[TPM_HEADER_SIZE], - expected - TPM_HEADER_SIZE); - if (size < expected) { - debug("TPM error, unable to read remaining bytes of result\n"); - size = -EIO; - goto out; - } - -out: - st33zp24_spi_cancel(dev); - st33zp24_spi_release_locality(dev); - - return size; -} - -/* - * st33zp24_spi_send send TPM commands through TPM phy. - * @param: chip, tpm_chip description. - * @param: buf, the buffer to send. - * @param: len, the number of bytes to send. - * @return: Returns zero in case of success else the negative error code. - */ -static int st33zp24_spi_send(struct udevice *dev, const u8 *buf, size_t len) -{ - struct tpm_chip *chip = dev_get_priv(dev); - u32 i, size; - int burstcnt, ret, status; - u8 data, tpm_stat; - - if (!chip) - return -ENODEV; - if (len < TPM_HEADER_SIZE) - return -EIO; - - ret = st33zp24_spi_request_locality(dev); - if (ret < 0) - return ret; - - tpm_stat = st33zp24_spi_status(dev); - if ((tpm_stat & TPM_STS_COMMAND_READY) == 0) { - st33zp24_spi_cancel(dev); - if (st33zp24_spi_wait_for_stat(dev, TPM_STS_COMMAND_READY, - chip->timeout_b, &status) < 0) { - ret = -ETIME; - goto out_err; - } - } - - for (i = 0; i < len - 1;) { - burstcnt = st33zp24_spi_get_burstcount(dev); - if (burstcnt < 0) - return burstcnt; - - size = min_t(int, len - i - 1, burstcnt); - ret = st33zp24_spi_write(dev, TPM_DATA_FIFO, buf + i, size); - if (ret < 0) - goto out_err; - - i += size; - } - - tpm_stat = st33zp24_spi_status(dev); - if ((tpm_stat & TPM_STS_DATA_EXPECT) == 0) { - ret = -EIO; - goto out_err; - } - - ret = st33zp24_spi_write(dev, TPM_DATA_FIFO, buf + len - 1, 1); - if (ret < 0) - goto out_err; - - tpm_stat = st33zp24_spi_status(dev); - if ((tpm_stat & TPM_STS_DATA_EXPECT) != 0) { - ret = -EIO; - goto out_err; - } - - data = TPM_STS_GO; - ret = st33zp24_spi_write(dev, TPM_STS, &data, 1); - if (ret < 0) - goto out_err; - - return len; - -out_err: - st33zp24_spi_cancel(dev); - st33zp24_spi_release_locality(dev); - - return ret; -} - -static int st33zp24_spi_cleanup(struct udevice *dev) -{ - st33zp24_spi_cancel(dev); - /* - * The TPM needs some time to clean up here, - * so we sleep rather than keeping the bus busy - */ - mdelay(2); - st33zp24_spi_release_locality(dev); - - return 0; -} - -static int st33zp24_spi_init(struct udevice *dev) -{ - struct tpm_chip *chip = dev_get_priv(dev); - struct st33zp24_spi_phy *phy = dev_get_plat(dev); - - chip->is_open = 1; - - /* Default timeouts - these could move to the device tree */ - chip->timeout_a = TIS_SHORT_TIMEOUT_MS; - chip->timeout_b = TIS_LONG_TIMEOUT_MS; - chip->timeout_c = TIS_SHORT_TIMEOUT_MS; - chip->timeout_d = TIS_SHORT_TIMEOUT_MS; - - chip->locality = LOCALITY0; - - phy->latency = st33zp24_spi_evaluate_latency(dev); - if (phy->latency <= 0) - return -ENODEV; - - /* - * A timeout query to TPM can be placed here. - * Standard timeout values are used so far - */ - - return 0; -} - -static int st33zp24_spi_open(struct udevice *dev) -{ - struct tpm_chip *chip = dev_get_priv(dev); - int rc; - - debug("%s: start\n", __func__); - if (chip->is_open) - return -EBUSY; - - rc = st33zp24_spi_init(dev); - if (rc < 0) - chip->is_open = 0; - - return rc; -} - -static int st33zp24_spi_close(struct udevice *dev) -{ - struct tpm_chip *chip = dev_get_priv(dev); - - if (chip->is_open) { - st33zp24_spi_release_locality(dev); - chip->is_open = 0; - chip->vend_dev = 0; - } - - return 0; -} - -static int st33zp24_spi_get_desc(struct udevice *dev, char *buf, int size) -{ - struct tpm_chip *chip = dev_get_priv(dev); - - if (size < 50) - return -ENOSPC; - - return snprintf(buf, size, "1.2 TPM (%s, chip type %s device-id 0x%x)", - chip->is_open ? "open" : "closed", - dev->name, - chip->vend_dev >> 16); -} - -const struct tpm_ops st33zp24_spi_tpm_ops = { - .open = st33zp24_spi_open, - .close = st33zp24_spi_close, - .recv = st33zp24_spi_recv, - .send = st33zp24_spi_send, - .cleanup = st33zp24_spi_cleanup, - .get_desc = st33zp24_spi_get_desc, -}; - -static int st33zp24_spi_probe(struct udevice *dev) -{ - struct tpm_chip_priv *uc_priv = dev_get_uclass_priv(dev); - - uc_priv->duration_ms[TPM_SHORT] = TIS_SHORT_TIMEOUT_MS; - uc_priv->duration_ms[TPM_MEDIUM] = TIS_LONG_TIMEOUT_MS; - uc_priv->duration_ms[TPM_LONG] = TIS_LONG_TIMEOUT_MS; - uc_priv->retry_time_ms = TPM_TIMEOUT_MS; - - debug("ST33ZP24 SPI TPM from STMicroelectronics found\n"); - - return 0; -} - -static int st33zp24_spi_remove(struct udevice *dev) -{ - st33zp24_spi_release_locality(dev); - - return 0; -} - -static const struct udevice_id st33zp24_spi_ids[] = { - { .compatible = "st,st33zp24-spi" }, - { } -}; - -U_BOOT_DRIVER(st33zp24_spi_spi) = { - .name = "st33zp24-spi", - .id = UCLASS_TPM, - .of_match = of_match_ptr(st33zp24_spi_ids), - .probe = st33zp24_spi_probe, - .remove = st33zp24_spi_remove, - .ops = &st33zp24_spi_tpm_ops, - .priv_auto = sizeof(struct tpm_chip), - .plat_auto = sizeof(struct st33zp24_spi_phy), -}; -- cgit v1.2.3 From 80afd60d9c7e24ef193b0d59ce98a3d86a70fcb5 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Wed, 6 Aug 2025 08:55:18 -0600 Subject: tpm: cr50_i2c: Make use of 'z' for printing size_t When printing the contents of an size_t variable we need to use z prefix to the format character in order to get the correct format type depending on 32 or 64bit-ness. Signed-off-by: Tom Rini Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- drivers/tpm/cr50_i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tpm/cr50_i2c.c b/drivers/tpm/cr50_i2c.c index 5b2d5ccb146..14a94f8d4a8 100644 --- a/drivers/tpm/cr50_i2c.c +++ b/drivers/tpm/cr50_i2c.c @@ -388,7 +388,7 @@ static int cr50_i2c_recv(struct udevice *dev, u8 *buf, size_t buf_len) int status; int ret; - log_debug("%s: buf_len=%x\n", __func__, buf_len); + log_debug("%s: buf_len=%zx\n", __func__, buf_len); if (buf_len < TPM_HEADER_SIZE) return -E2BIG; @@ -465,7 +465,7 @@ static int cr50_i2c_send(struct udevice *dev, const u8 *buf, size_t len) ulong timeout; int ret; - log_debug("len=%x\n", len); + log_debug("len=%zx\n", len); timeout = timer_get_us() + TIMEOUT_LONG_US; do { ret = cr50_i2c_status(dev); -- cgit v1.2.3 From 83b1c7fd7119ae654aba70b2451b519035b5403d Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Wed, 6 Aug 2025 08:55:19 -0600 Subject: tpm: tpm_tis_infineon: Make use of 'z' for printing size_t When printing the contents of an size_t variable we need to use z prefix to the format character in order to get the correct format type depending on 32 or 64bit-ness. Signed-off-by: Tom Rini Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- drivers/tpm/tpm_tis_infineon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tpm/tpm_tis_infineon.c b/drivers/tpm/tpm_tis_infineon.c index e2f6238cbc7..de6566fdb9e 100644 --- a/drivers/tpm/tpm_tis_infineon.c +++ b/drivers/tpm/tpm_tis_infineon.c @@ -396,7 +396,7 @@ static int tpm_tis_i2c_recv(struct udevice *dev, u8 *buf, size_t count) expected = get_unaligned_be32(buf + TPM_RSP_SIZE_BYTE); if ((size_t)expected > count || (size_t)expected < TPM_HEADER_SIZE) { - debug("Error size=%x, expected=%x, count=%x\n", size, expected, + debug("Error size=%x, expected=%x, count=%zx\n", size, expected, count); return -ENOSPC; } @@ -429,7 +429,7 @@ static int tpm_tis_i2c_send(struct udevice *dev, const u8 *buf, size_t len) int retry = 0; u8 sts = TPM_STS_GO; - debug("%s: len=%d\n", __func__, len); + debug("%s: len=%zd\n", __func__, len); if (len > TPM_DEV_BUFSIZE) return -E2BIG; /* Command is too long for our tpm, sorry */ -- cgit v1.2.3 From 73b23838c44b152deef68b7c22d923f0ca7a563f Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Wed, 13 Aug 2025 11:40:05 +0100 Subject: tpm: tis_infineon: Cannot test unsigned for being negative tpm_tis_i2c_get_burstcount returns a size_t but also returns -EBUSY if the TPM is surrently busy. As size_t is an unsigned type simply testing for < 0 will not work so change the test for being equal to -EBUSY which will work. Also remove the trivial comments. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Acked-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- drivers/tpm/tpm_tis_infineon.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/tpm/tpm_tis_infineon.c b/drivers/tpm/tpm_tis_infineon.c index de6566fdb9e..30f23f8610a 100644 --- a/drivers/tpm/tpm_tis_infineon.c +++ b/drivers/tpm/tpm_tis_infineon.c @@ -353,8 +353,7 @@ static int tpm_tis_i2c_recv_data(struct udevice *dev, u8 *buf, size_t count) while (size < count) { burstcnt = tpm_tis_i2c_get_burstcount(dev); - /* burstcount < 0 -> tpm is busy */ - if (burstcnt < 0) + if (burstcnt == -EBUSY) return burstcnt; /* Limit received data to max left */ @@ -449,8 +448,7 @@ static int tpm_tis_i2c_send(struct udevice *dev, const u8 *buf, size_t len) burstcnt = tpm_tis_i2c_get_burstcount(dev); - /* burstcount < 0 -> tpm is busy */ - if (burstcnt < 0) + if (burstcnt == -EBUSY) return burstcnt; while (count < len) { -- cgit v1.2.3 From d0e8a208f6f09c1719fe17b3208b5b239f42cf69 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 23 Jul 2025 11:06:46 +0200 Subject: serial: uartlite: Use private data instead of platform plat data should be used only in probe or of_to_plat to fill it information from DT. Then in probe platform data should be stored in private structure which should be used by the other driver functions. Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/8d32af596f80a2220d9f5d7fb98476e6d2b5f303.1753261604.git.michal.simek@amd.com --- drivers/serial/serial_xuartlite.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/serial_xuartlite.c b/drivers/serial/serial_xuartlite.c index eb234108746..00155aba5eb 100644 --- a/drivers/serial/serial_xuartlite.c +++ b/drivers/serial/serial_xuartlite.c @@ -35,6 +35,10 @@ struct uartlite_plat { struct uartlite *regs; }; +struct uartlite_priv { + struct uartlite *regs; +}; + static u32 uart_in32(void __iomem *addr) { if (little_endian) @@ -53,8 +57,8 @@ static void uart_out32(void __iomem *addr, u32 val) static int uartlite_serial_putc(struct udevice *dev, const char ch) { - struct uartlite_plat *plat = dev_get_plat(dev); - struct uartlite *regs = plat->regs; + struct uartlite_priv *priv = dev_get_priv(dev); + struct uartlite *regs = priv->regs; if (uart_in32(®s->status) & SR_TX_FIFO_FULL) return -EAGAIN; @@ -66,8 +70,8 @@ static int uartlite_serial_putc(struct udevice *dev, const char ch) static int uartlite_serial_getc(struct udevice *dev) { - struct uartlite_plat *plat = dev_get_plat(dev); - struct uartlite *regs = plat->regs; + struct uartlite_priv *priv = dev_get_priv(dev); + struct uartlite *regs = priv->regs; if (!(uart_in32(®s->status) & SR_RX_FIFO_VALID_DATA)) return -EAGAIN; @@ -77,8 +81,8 @@ static int uartlite_serial_getc(struct udevice *dev) static int uartlite_serial_pending(struct udevice *dev, bool input) { - struct uartlite_plat *plat = dev_get_plat(dev); - struct uartlite *regs = plat->regs; + struct uartlite_priv *priv = dev_get_priv(dev); + struct uartlite *regs = priv->regs; if (input) return uart_in32(®s->status) & SR_RX_FIFO_VALID_DATA; @@ -89,9 +93,12 @@ static int uartlite_serial_pending(struct udevice *dev, bool input) static int uartlite_serial_probe(struct udevice *dev) { struct uartlite_plat *plat = dev_get_plat(dev); + struct uartlite_priv *priv = dev_get_priv(dev); struct uartlite *regs = plat->regs; int ret; + priv->regs = regs; + uart_out32(®s->control, 0); uart_out32(®s->control, ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX); ret = uart_in32(®s->status); @@ -131,6 +138,7 @@ U_BOOT_DRIVER(serial_uartlite) = { .id = UCLASS_SERIAL, .of_match = uartlite_serial_ids, .of_to_plat = uartlite_serial_of_to_plat, + .priv_auto = sizeof(struct uartlite_priv), .plat_auto = sizeof(struct uartlite_plat), .probe = uartlite_serial_probe, .ops = &uartlite_serial_ops, -- cgit v1.2.3 From 3a85a27e34624f1b0f08979ea914e0cadfcbf74d Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 23 Jul 2025 11:06:47 +0200 Subject: serial: uartlite: Add support for OF_PLATDATA The first change is to list DM_DRIVER_ALIAS for compatible string to be able to match the driver. Only xps one is listed because opb one is likely unused for quite a long time. The second change is to add dtplat structure to plat data and fill register base in probe. Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/b494dbad529e919d33977b8ea6e6dbcd14e78907.1753261604.git.michal.simek@amd.com --- drivers/serial/serial_xuartlite.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/serial/serial_xuartlite.c b/drivers/serial/serial_xuartlite.c index 00155aba5eb..6bfd0e085e8 100644 --- a/drivers/serial/serial_xuartlite.c +++ b/drivers/serial/serial_xuartlite.c @@ -32,7 +32,11 @@ struct uartlite { }; struct uartlite_plat { +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct dtd_serial_uartlite dtplat; +#else struct uartlite *regs; +#endif }; struct uartlite_priv { @@ -94,9 +98,16 @@ static int uartlite_serial_probe(struct udevice *dev) { struct uartlite_plat *plat = dev_get_plat(dev); struct uartlite_priv *priv = dev_get_priv(dev); - struct uartlite *regs = plat->regs; + struct uartlite *regs; int ret; +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct dtd_serial_uartlite *dtplat = &plat->dtplat; + + regs = (struct uartlite *)dtplat->reg[0]; +#else + regs = plat->regs; +#endif priv->regs = regs; uart_out32(®s->control, 0); @@ -112,6 +123,7 @@ static int uartlite_serial_probe(struct udevice *dev) return 0; } +#if !CONFIG_IS_ENABLED(OF_PLATDATA) static int uartlite_serial_of_to_plat(struct udevice *dev) { struct uartlite_plat *plat = dev_get_plat(dev); @@ -120,6 +132,7 @@ static int uartlite_serial_of_to_plat(struct udevice *dev) return 0; } +#endif static const struct dm_serial_ops uartlite_serial_ops = { .putc = uartlite_serial_putc, @@ -137,13 +150,17 @@ U_BOOT_DRIVER(serial_uartlite) = { .name = "serial_uartlite", .id = UCLASS_SERIAL, .of_match = uartlite_serial_ids, +#if !CONFIG_IS_ENABLED(OF_PLATDATA) .of_to_plat = uartlite_serial_of_to_plat, +#endif .priv_auto = sizeof(struct uartlite_priv), .plat_auto = sizeof(struct uartlite_plat), .probe = uartlite_serial_probe, .ops = &uartlite_serial_ops, }; +DM_DRIVER_ALIAS(serial_uartlite, xlnx_xps_uartlite_1_00_a) + #ifdef CONFIG_DEBUG_UART_UARTLITE #include -- cgit v1.2.3 From 6d491e8913f07b9ad4f9a86fdb0aab2007e72af3 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Fri, 25 Jul 2025 14:01:20 +0200 Subject: clk: zynqmp: Mark zynqmp_clk_ops as const Operations are not changing that's why mark them as const which ensure that structure will be moved from .data section to .rodata section. Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/92eb9e90189d8b96246966633478662076da7185.1753444878.git.michal.simek@amd.com --- drivers/clk/clk_zynqmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/clk_zynqmp.c b/drivers/clk/clk_zynqmp.c index 4f67c958d0f..7a433a667a4 100644 --- a/drivers/clk/clk_zynqmp.c +++ b/drivers/clk/clk_zynqmp.c @@ -882,7 +882,7 @@ static int zynqmp_clk_enable(struct clk *clk) return ret; } -static struct clk_ops zynqmp_clk_ops = { +static const struct clk_ops zynqmp_clk_ops = { .set_rate = zynqmp_clk_set_rate, .get_rate = zynqmp_clk_get_rate, .enable = zynqmp_clk_enable, -- cgit v1.2.3 From 4b2679efc5f50ac38b9c2f20b3beec56283efddb Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Fri, 25 Jul 2025 14:01:21 +0200 Subject: mailbox: zynqmp-ipi: Mark zynqmp_ipi_dest_mbox_ops as const Operations are not changing that's why mark them as const which ensure that structure will be moved from .data section to .rodata section. Also mark them as static because they are not used out of this file. Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/b7e5dc8841f6e904a7365f2ed45248609c007ddd.1753444878.git.michal.simek@amd.com --- drivers/mailbox/zynqmp-ipi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mailbox/zynqmp-ipi.c b/drivers/mailbox/zynqmp-ipi.c index f62277385fb..79266c56e79 100644 --- a/drivers/mailbox/zynqmp-ipi.c +++ b/drivers/mailbox/zynqmp-ipi.c @@ -241,7 +241,7 @@ static int zynqmp_ipi_probe(struct udevice *dev) return 0; }; -struct mbox_ops zynqmp_ipi_dest_mbox_ops = { +static const struct mbox_ops zynqmp_ipi_dest_mbox_ops = { .send = zynqmp_ipi_send, .recv = zynqmp_ipi_recv, }; -- cgit v1.2.3 From 25801ef2d46d44fe1a137971bf4e4d1c0cc529ce Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Fri, 25 Jul 2025 14:01:22 +0200 Subject: dm: core: Mark root_info as const root_info driver structure is not changing that's why mark them as const which ensure that structure will be moved from .data section to .rodata section. Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/18d92a93a9863ed0452e82a1f8e0ff9205adb4f9.1753444878.git.michal.simek@amd.com --- drivers/core/root.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/core/root.c b/drivers/core/root.c index e53381e3b32..d43645f34dd 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -32,7 +32,7 @@ DECLARE_GLOBAL_DATA_PTR; -static struct driver_info root_info = { +static const struct driver_info root_info = { .name = "root_driver", }; -- cgit v1.2.3 From a51b7dfc6fec0aa76367114af59a7114b040b090 Mon Sep 17 00:00:00 2001 From: Venkatesh Yadav Abbarapu Date: Thu, 24 Jul 2025 10:14:02 +0530 Subject: ufs: amd-versal2: Configure RMMI and M-PHY registers for HS mode Configure RMMI and M-PHY registers for HS mode required for selection of bit rate series A or B. If it is not a calibrated part, then switch back to SLOWAUTO_MODE and skip all these configurations. Implemented below sequence as per the DWC RMMI databook. 1. Override RMMI CBRATESEL with the desired rate. 2. Set TX_CFGUPDT_0 to 1'b1 for one TX_CFGCLK_0 cycle. 3. Override PHY rx_req to 1, then poll on PHY rx_ack register till it goes 1(both lanes). 4. Override PHY rx_req to 0, then poll on PHY rx_ack register till it goes 0(both lanes). 5. Remove PHY rx_req override(both lanes). 6. Start the LS PMC. Signed-off-by: Venkatesh Yadav Abbarapu Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20250724044402.260149-1-venkatesh.abbarapu@amd.com Signed-off-by: Michal Simek --- drivers/ufs/ufs-amd-versal2.c | 112 ++++++++++++++++++++++++++++++++++++++++++ drivers/ufs/ufs.c | 15 ++++++ drivers/ufs/ufs.h | 3 ++ drivers/ufs/ufshcd-dwc.h | 3 ++ 4 files changed, 133 insertions(+) (limited to 'drivers') diff --git a/drivers/ufs/ufs-amd-versal2.c b/drivers/ufs/ufs-amd-versal2.c index 1c5ed538370..896dda2de4e 100644 --- a/drivers/ufs/ufs-amd-versal2.c +++ b/drivers/ufs/ufs-amd-versal2.c @@ -26,6 +26,10 @@ #define MPHY_FAST_RX_AFE_CAL BIT(2) #define MPHY_FW_CALIB_CFG_VAL BIT(8) +#define MPHY_RX_OVRD_EN BIT(3) +#define MPHY_RX_OVRD_VAL BIT(2) +#define MPHY_RX_ACK_MASK BIT(0) + #define TX_RX_CFG_RDY_MASK GENMASK(3, 0) #define TIMEOUT_MICROSEC 1000000L @@ -422,10 +426,118 @@ static int ufs_versal2_link_startup_notify(struct ufs_hba *hba, return ret; } +static int ufs_versal2_phy_ratesel(struct ufs_hba *hba, u32 activelanes, u32 rx_req) +{ + u32 time_left, reg, lane; + int ret; + + for (lane = 0; lane < activelanes; lane++) { + time_left = TIMEOUT_MICROSEC; + ret = ufs_versal2_phy_reg_read(hba, RX_OVRD_IN_1(lane), ®); + if (ret) + return ret; + + reg |= MPHY_RX_OVRD_EN; + if (rx_req) + reg |= MPHY_RX_OVRD_VAL; + else + reg &= ~MPHY_RX_OVRD_VAL; + + ret = ufs_versal2_phy_reg_write(hba, RX_OVRD_IN_1(lane), reg); + if (ret) + return ret; + + do { + ret = ufs_versal2_phy_reg_read(hba, RX_PCS_OUT(lane), ®); + if (ret) + return ret; + + reg &= MPHY_RX_ACK_MASK; + if (reg == rx_req) + break; + + time_left--; + mdelay(5); + } while (time_left); + + if (!time_left) { + dev_err(hba->dev, "Invalid Rx Ack value.\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int ufs_get_max_pwr_mode(struct ufs_hba *hba, + struct ufs_pwr_mode_info *max_pwr_info) +{ + struct ufs_versal2_priv *priv = dev_get_priv(hba->dev); + u32 lane, reg, rate = 0; + int ret = 0; + + /* If it is not a calibrated part, switch PWRMODE to SLOW_MODE */ + if (!priv->attcompval0 && !priv->attcompval1 && + !priv->ctlecompval0 && !priv->ctlecompval1) { + max_pwr_info->info.pwr_rx = SLOWAUTO_MODE; + max_pwr_info->info.pwr_tx = SLOWAUTO_MODE; + max_pwr_info->info.gear_rx = UFS_PWM_G1; + max_pwr_info->info.gear_tx = UFS_PWM_G1; + max_pwr_info->info.lane_tx = 1; + max_pwr_info->info.lane_rx = 1; + max_pwr_info->info.hs_rate = 0; + return 0; + } + + if (max_pwr_info->info.pwr_rx == SLOWAUTO_MODE || + max_pwr_info->info.pwr_tx == SLOWAUTO_MODE) + return 0; + + if (max_pwr_info->info.hs_rate == PA_HS_MODE_B) + rate = 1; + + /* Select the rate */ + ret = ufshcd_dme_set(hba, UIC_ARG_MIB(CBRATESEL), rate); + if (ret) + return ret; + + ret = ufshcd_dme_set(hba, UIC_ARG_MIB(VS_MPHYCFGUPDT), 1); + if (ret) + return ret; + + ret = ufs_versal2_phy_ratesel(hba, max_pwr_info->info.lane_tx, 1); + if (ret) + return ret; + + ret = ufs_versal2_phy_ratesel(hba, max_pwr_info->info.lane_tx, 0); + if (ret) + return ret; + + /* Remove rx_req override */ + for (lane = 0; lane < max_pwr_info->info.lane_tx; lane++) { + ret = ufs_versal2_phy_reg_read(hba, RX_OVRD_IN_1(lane), ®); + if (ret) + return ret; + + reg &= ~MPHY_RX_OVRD_EN; + ret = ufs_versal2_phy_reg_write(hba, RX_OVRD_IN_1(lane), reg); + if (ret) + return ret; + } + + if (max_pwr_info->info.lane_tx == UFS_LANE_2 && + max_pwr_info->info.lane_rx == UFS_LANE_2) + ret = ufshcd_dme_configure_adapt(hba, max_pwr_info->info.gear_tx, + PA_INITIAL_ADAPT); + + return 0; +} + static struct ufs_hba_ops ufs_versal2_hba_ops = { .init = ufs_versal2_init, .link_startup_notify = ufs_versal2_link_startup_notify, .hce_enable_notify = ufs_versal2_hce_enable_notify, + .get_max_pwr_mode = ufs_get_max_pwr_mode, }; static int ufs_versal2_probe(struct udevice *dev) diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c index 91f6ad3bfef..57e6e8c013b 100644 --- a/drivers/ufs/ufs.c +++ b/drivers/ufs/ufs.c @@ -226,6 +226,21 @@ static int ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) return 0; } +int ufshcd_dme_configure_adapt(struct ufs_hba *hba, + int agreed_gear, + int adapt_val) +{ + int ret; + + if (agreed_gear < UFS_HS_G4) + adapt_val = PA_NO_ADAPT; + + ret = ufshcd_dme_set(hba, + UIC_ARG_MIB(PA_TXHSADAPTTYPE), + adapt_val); + return ret; +} + /** * ufshcd_dme_set_attr - UIC command for DME_SET, DME_PEER_SET * diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h index 53137fae3a8..0337ac5996b 100644 --- a/drivers/ufs/ufs.h +++ b/drivers/ufs/ufs.h @@ -428,6 +428,9 @@ enum uic_link_state { #define ATTR_SET_NOR 0 /* NORMAL */ #define ATTR_SET_ST 1 /* STATIC */ +int ufshcd_dme_configure_adapt(struct ufs_hba *hba, + int agreed_gear, + int adapt_val); int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel, u8 attr_set, u32 mib_val, u8 peer); int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, diff --git a/drivers/ufs/ufshcd-dwc.h b/drivers/ufs/ufshcd-dwc.h index fc1bcca8ccb..f7d27736f44 100644 --- a/drivers/ufs/ufshcd-dwc.h +++ b/drivers/ufs/ufshcd-dwc.h @@ -17,6 +17,7 @@ #define CBREFCLKCTRL2 0x8132 #define CBCRCTRL 0x811F #define CBC10DIRECTCONF2 0x810E +#define CBRATESEL 0x8114 #define CBCREGADDRLSB 0x8116 #define CBCREGADDRMSB 0x8117 #define CBCREGWRLSB 0x8118 @@ -32,6 +33,8 @@ #define MRX_FSM_STATE 0xC1 /* M-PHY registers */ +#define RX_OVRD_IN_1(n) (0x3006 + ((n) * 0x100)) +#define RX_PCS_OUT(n) (0x300F + ((n) * 0x100)) #define FAST_FLAGS(n) (0x401C + ((n) * 0x100)) #define RX_AFE_ATT_IDAC(n) (0x4000 + ((n) * 0x100)) #define RX_AFE_CTLE_IDAC(n) (0x4001 + ((n) * 0x100)) -- cgit v1.2.3 From b5da53046c303d7cff586b366f225dc4c3b64686 Mon Sep 17 00:00:00 2001 From: "Lucien.Jheng" Date: Sun, 17 Aug 2025 23:02:03 +0800 Subject: misc: fs_loader: Add request_firmware_into_buf_via_script() for flexible firmware loading via U-Boot script This commit introduces a new API, request_firmware_into_buf_via_script(), to the fs_loader framework. This function allows firmware to be loaded into memory using a user-defined U-Boot script, providing greater flexibility for firmware loading scenarios that cannot be handled by static file paths or device/partition selection alone. Key features: - The API runs a specified U-Boot script (by name), which is responsible for loading the firmware into memory by any means (e.g., load from MMC, USB, network, etc.). - The script must set two environment variables: 'fw_addr' (the memory address where the firmware is loaded) and 'fw_size' (the size of the firmware in bytes). - The function validates these variables, copies the firmware into a newly allocated buffer (using memdup), and returns the pointer via the provided double pointer argument. - The maximum allowed firmware size is checked to prevent buffer overflows. - The environment variables are cleared after use to avoid stale data. - Detailed error messages are provided for all failure conditions to aid debugging. Usage example: 1. Define a U-Boot script in the environment that loads the firmware and sets the required variables: => env set my_fw_script 'load mmc 0:1 ${loadaddr} firmware.bin && env set fw_addr ${loadaddr} && env set fw_size ${filesize}' 2. In your code, call the new API: void *fw_buf = NULL; int ret = request_firmware_into_buf_via_script(&fw_buf, 0x46000000, "my_fw_script"); if (ret < 0) return ret; This approach allows board integrators and users to customize the firmware loading process without modifying the source code, simply by changing the script in the U-Boot environment. Signed-off-by: Lucien.Jheng Reviewed-by: Marek Vasut [trini: Fix printf of size_t needing to use %zx] Signed-off-by: Tom Rini --- drivers/misc/fs_loader.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/fs_loader.c b/drivers/misc/fs_loader.c index 66803f4b997..60296d55f23 100644 --- a/drivers/misc/fs_loader.c +++ b/drivers/misc/fs_loader.c @@ -228,6 +228,52 @@ int request_firmware_into_buf(struct udevice *dev, return ret; } +int request_firmware_into_buf_via_script(void **buf, size_t max_size, + const char *script_name) +{ + ulong addr, size; + int ret; + char cmd[32]; + + if (!buf || !script_name || !max_size) + return -EINVAL; + + /* Create command to run the firmware loading script */ + snprintf(cmd, sizeof(cmd), "run %s", script_name); + + /* Run the firmware loading script */ + ret = run_command_list(cmd, -1, 0); + if (ret) { + log_err("Firmware loading script '%s' not defined or failed.\n", + script_name); + return -EINVAL; + } + + /* Find out where the firmware got loaded and how long it is */ + addr = env_get_hex("fw_addr", 0); + size = env_get_hex("fw_size", 0); + + /* Clear the variables set by the firmware loading script */ + env_set("fw_addr", NULL); + env_set("fw_size", NULL); + + if (!addr || !size) { + log_err("Firmware address (0x%lx) or size (0x%lx) are invalid.\n", + addr, size); + return -EINVAL; + } + + if (size > max_size) { + log_err("Loaded firmware size 0x%lx exceeded maximum allowed size 0x%zx.\n", + size, max_size); + return -E2BIG; + } + + memcpy(*buf, (void *)addr, size); + + return 0; +} + static int fs_loader_of_to_plat(struct udevice *dev) { u32 phandlepart[2]; -- cgit v1.2.3 From 377159bfb8ee35ce16200818cfa55cdfba8c69e9 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Thu, 24 Jul 2025 16:32:55 +0100 Subject: ddr: fsl: Provide initial value for zqcs_init In the case of !zq_en zqcs_init is never assigned to although its value is used. Correct by initialising zqcs_init to 0. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Signed-off-by: Peng Fan --- drivers/ddr/fsl/ctrl_regs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ddr/fsl/ctrl_regs.c b/drivers/ddr/fsl/ctrl_regs.c index 9a25192c079..fea08c9000f 100644 --- a/drivers/ddr/fsl/ctrl_regs.c +++ b/drivers/ddr/fsl/ctrl_regs.c @@ -2173,7 +2173,7 @@ static void set_ddr_zq_cntl(fsl_ddr_cfg_regs_t *ddr, unsigned int zq_en) /* Normal Operation Short Calibration Time (tZQCS) */ unsigned int zqcs = 0; #ifdef CONFIG_SYS_FSL_DDR4 - unsigned int zqcs_init; + unsigned int zqcs_init = 0; #endif if (zq_en) { -- cgit v1.2.3 From 44d321f0092a42b2295bd6eac41199604cf7d8c9 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Mon, 4 Aug 2025 17:03:57 +0100 Subject: net: fm: Correct test for timeout In bmi_rx_port_disable and bmi_tx_port_disable the use of post-decrement on the test in the while loop for a timeout means that timeout will be equal to -1 on exit in that case. Adjust the test for this expected value. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Signed-off-by: Peng Fan --- drivers/net/fm/eth.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/fm/eth.c b/drivers/net/fm/eth.c index 63fe4b2d33c..c83b789693a 100644 --- a/drivers/net/fm/eth.c +++ b/drivers/net/fm/eth.c @@ -169,7 +169,7 @@ static void bmi_rx_port_disable(struct fm_bmi_rx_port *rx_port) /* wait until the rx port is not busy */ while ((in_be32(&rx_port->fmbm_rst) & FMBM_RST_BSY) && timeout--) ; - if (!timeout) + if (timeout == -1) printf("%s - timeout\n", __func__); } @@ -199,7 +199,7 @@ static void bmi_tx_port_disable(struct fm_bmi_tx_port *tx_port) /* wait until the tx port is not busy */ while ((in_be32(&tx_port->fmbm_tst) & FMBM_TST_BSY) && timeout--) ; - if (!timeout) + if (timeout == -1) printf("%s - timeout\n", __func__); } -- cgit v1.2.3 From 270798a4209aeb23fce72ebf10e62630f474e431 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Mon, 4 Aug 2025 17:03:58 +0100 Subject: net: fm: NULL check dev before dereference In fm_eth_bind there is a dereference of dev before it is NULL checked. Add a NULL check before the first dereference and remove a later NULL check that is now redundant. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Signed-off-by: Peng Fan --- drivers/net/fm/eth.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/fm/eth.c b/drivers/net/fm/eth.c index c83b789693a..f0e7c0eca42 100644 --- a/drivers/net/fm/eth.c +++ b/drivers/net/fm/eth.c @@ -727,12 +727,15 @@ static int fm_eth_bind(struct udevice *dev) char mac_name[11]; u32 fm, num; + if (!dev) + return -EINVAL; + if (ofnode_read_u32(ofnode_get_parent(dev_ofnode(dev)), "cell-index", &fm)) { printf("FMan node property cell-index missing\n"); return -EINVAL; } - if (dev && dev_read_u32(dev, "cell-index", &num)) { + if (dev_read_u32(dev, "cell-index", &num)) { printf("FMan MAC node property cell-index missing\n"); return -EINVAL; } -- cgit v1.2.3 From 5b95f666fbd8f9c9c43726c7ced229762e4a35e0 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Mon, 4 Aug 2025 17:03:59 +0100 Subject: net: fm: Correct test for timeout In memac_wait_until_free and memac_wait_until_done the use of post-decrement on the test in the while loop for a timeout means that timeout will be equal to -1 on exit in that case. Adjust the test for this expected value. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Signed-off-by: Peng Fan --- drivers/net/fm/memac_phy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/fm/memac_phy.c b/drivers/net/fm/memac_phy.c index 26425d94ae5..1ad3c053593 100644 --- a/drivers/net/fm/memac_phy.c +++ b/drivers/net/fm/memac_phy.c @@ -46,7 +46,7 @@ static int memac_wait_until_free(struct memac_mdio_controller *regs) while ((memac_in_32(®s->mdio_stat) & MDIO_STAT_BSY) && timeout--) ; - if (!timeout) { + if (timeout == -1) { printf("timeout waiting for MDIO bus to be free\n"); return -ETIMEDOUT; } @@ -64,7 +64,7 @@ static int memac_wait_until_done(struct memac_mdio_controller *regs) while ((memac_in_32(®s->mdio_stat) & MDIO_STAT_BSY) && timeout--) ; - if (!timeout) { + if (timeout == -1) { printf("timeout waiting for MDIO operation to complete\n"); return -ETIMEDOUT; } -- cgit v1.2.3 From f4c2a12611d3ffdbd624ededf0fce192f26dddaf Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Mon, 4 Aug 2025 17:56:55 +0100 Subject: net: fsl_enetc: Fix copy/paste error In netc_blk_ctrl_probe the test for failure of the function clk_prepare_enable should not return PTR_ERR(ipg_clk) as it does not check IS_ERR(ipg_clk) instead it should return err as that is what is holding the error code in this case. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Signed-off-by: Peng Fan --- drivers/net/fsl_enetc_netc_blk_ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/fsl_enetc_netc_blk_ctrl.c b/drivers/net/fsl_enetc_netc_blk_ctrl.c index 46b68d3d8a4..fecd66eb15a 100644 --- a/drivers/net/fsl_enetc_netc_blk_ctrl.c +++ b/drivers/net/fsl_enetc_netc_blk_ctrl.c @@ -293,7 +293,7 @@ static int netc_blk_ctrl_probe(struct udevice *dev) err = clk_prepare_enable(ipg_clk); if (err) { dev_err(dev, "Enable ipg clock failed\n"); - return PTR_ERR(ipg_clk); + return err; } regs = dev_read_addr_name(dev, "ierb"); -- cgit v1.2.3 From 756580d090e5dcb3c45d1f0147cc596ae4efd961 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Mon, 4 Aug 2025 17:56:56 +0100 Subject: net: fsl-mc: NULL check dflt_dpio before dereference In dpio_exit there is a NULL check for dflt_dpio but it happens after dpio_dflt has been dereferenced a number of times already. Instead move the NULL check to first thing in the function. Also assign NULL to dflt_dpio after free in both dpio_init and dpio_exit. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Signed-off-by: Peng Fan --- drivers/net/fsl-mc/mc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/fsl-mc/mc.c b/drivers/net/fsl-mc/mc.c index 86daf0fb2bb..999a9912e2f 100644 --- a/drivers/net/fsl-mc/mc.c +++ b/drivers/net/fsl-mc/mc.c @@ -1178,6 +1178,7 @@ err_get_api_ver: dflt_dpio->dpio_id); err_create: free(dflt_dpio); + dflt_dpio = NULL; err_calloc: return err; } @@ -1186,6 +1187,9 @@ static int dpio_exit(void) { int err; + if (!dflt_dpio) + return -ENODEV; + err = dpio_disable(dflt_mc_io, MC_CMD_NO_FLAGS, dflt_dpio->dpio_handle); if (err < 0) { printf("dpio_disable() failed: %d\n", err); @@ -1211,8 +1215,8 @@ static int dpio_exit(void) printf("Exit: DPIO.%d\n", dflt_dpio->dpio_id); #endif - if (dflt_dpio) - free(dflt_dpio); + free(dflt_dpio); + dflt_dpio = NULL; return 0; err: -- cgit v1.2.3 From 9ca756cee7e8f9d6ecc0a69fa785caabd61f3f6a Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Mon, 4 Aug 2025 17:56:57 +0100 Subject: net: fsl-mc: NULL check dflt_dpbp before dereference In dpbp_exit there is a NULL check for dflt_dpbp after it is dereferenced a number of times. Instead move the NULL check to early in the function. Also assign NULL to dflt_dpbp after free in both dpbp_init and dpbp_exit. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Signed-off-by: Peng Fan --- drivers/net/fsl-mc/mc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/fsl-mc/mc.c b/drivers/net/fsl-mc/mc.c index 999a9912e2f..8c882c7fcf5 100644 --- a/drivers/net/fsl-mc/mc.c +++ b/drivers/net/fsl-mc/mc.c @@ -1438,6 +1438,7 @@ err_close: err_open: err_create: free(dflt_dpbp); + dflt_dpbp = NULL; err_calloc: return err; } @@ -1446,6 +1447,9 @@ static int dpbp_exit(void) { int err; + if (!dflt_dpbp) + return -ENODEV; + err = dpbp_destroy(dflt_mc_io, dflt_dprc_handle, MC_CMD_NO_FLAGS, dflt_dpbp->dpbp_id); if (err < 0) { @@ -1457,8 +1461,8 @@ static int dpbp_exit(void) printf("Exit: DPBP.%d\n", dflt_dpbp->dpbp_attr.id); #endif - if (dflt_dpbp) - free(dflt_dpbp); + free(dflt_dpbp); + dflt_dpbp = NULL; return 0; err: -- cgit v1.2.3 From 010a4c5c553763430b3e9628ae2c82de3d5f45dd Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Mon, 4 Aug 2025 17:56:58 +0100 Subject: net: fsl-mc: NULL check dflt_dpni before dereference In dpni_exit there is a NULL check for dflt_dpni after it is dereferenced a number of times. Instead move the NULL check to early in the function. Also assign NULL to dflt_dpni after free in both dpni_init and dpni_exit. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Signed-off-by: Peng Fan --- drivers/net/fsl-mc/mc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/fsl-mc/mc.c b/drivers/net/fsl-mc/mc.c index 8c882c7fcf5..aad852f8151 100644 --- a/drivers/net/fsl-mc/mc.c +++ b/drivers/net/fsl-mc/mc.c @@ -1539,6 +1539,7 @@ err_get_version: dflt_dpni->dpni_id); err_create: free(dflt_dpni); + dflt_dpni = NULL; err_calloc: return err; } @@ -1547,6 +1548,9 @@ static int dpni_exit(void) { int err; + if (!dflt_dpni) + return -ENODEV; + err = dpni_destroy(dflt_mc_io, dflt_dprc_handle, MC_CMD_NO_FLAGS, dflt_dpni->dpni_id); if (err < 0) { @@ -1558,8 +1562,8 @@ static int dpni_exit(void) printf("Exit: DPNI.%d\n", dflt_dpni->dpni_id); #endif - if (dflt_dpni) - free(dflt_dpni); + free(dflt_dpni); + dflt_dpni = NULL; return 0; err: -- cgit v1.2.3 From 05b11146363039a00bff09c77be077ebdaf1f5d0 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Mon, 4 Aug 2025 17:56:59 +0100 Subject: net: fsl-mc: Incorrect variable used in error path In mc_fixup_dpc_mac_addr noff is assigned the return value from fdt_add_subnode so that is the variable that should be passed to fdt_strerror and returned when negative. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Signed-off-by: Peng Fan --- drivers/net/fsl-mc/mc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/fsl-mc/mc.c b/drivers/net/fsl-mc/mc.c index aad852f8151..c8ed702f50a 100644 --- a/drivers/net/fsl-mc/mc.c +++ b/drivers/net/fsl-mc/mc.c @@ -366,8 +366,8 @@ static int mc_fixup_dpc_mac_addr(void *blob, int dpmac_id, noff = fdt_add_subnode(blob, nodeoffset, mac_name); if (noff < 0) { printf("fdt_add_subnode: err=%s\n", - fdt_strerror(err)); - return err; + fdt_strerror(noff)); + return noff; } /* add default property of fixed link */ -- cgit v1.2.3 From dc37adfc040877db359abdc7eab19adffb8fc722 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Fri, 8 Aug 2025 11:29:33 +0100 Subject: drivers: qe: avoid double free() Avoid calling free(addr) twice if the device for ucode is not found. This patch repeats a similar fix but that only applied to code without CONFIG_TFABOOT enabled. This patch applies to the code with CONFIG_TFABOOT enabled. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Signed-off-by: Peng Fan --- drivers/qe/qe.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/qe/qe.c b/drivers/qe/qe.c index 69b7de084e5..25880db37cf 100644 --- a/drivers/qe/qe.c +++ b/drivers/qe/qe.c @@ -255,7 +255,6 @@ void u_qe_init(void) struct mmc *mmc = find_mmc_device(CONFIG_ENV_MMC_DEVICE_INDEX); if (!mmc) { - free(addr); printf("\nMMC cannot find device for ucode\n"); } else { printf("\nMMC read: dev # %u, block # %u, count %u ...\n", -- cgit v1.2.3 From 4d3183723f34d58cb5decec77e8bf64b84d309d1 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Mon, 11 Aug 2025 17:25:17 +0100 Subject: serial: lpuart: Return value from correct variable In get_lpuart_clk_rate if the call to clk_get_rate returns an error then the call to return should pass the value of the error which is in rate rather than ret which will be 0 as its value is not affected by this error. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Signed-off-by: Peng Fan --- drivers/serial/serial_lpuart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c index 93602797b83..9fdb6503085 100644 --- a/drivers/serial/serial_lpuart.c +++ b/drivers/serial/serial_lpuart.c @@ -129,7 +129,7 @@ static int get_lpuart_clk_rate(struct udevice *dev, u32 *clk_rate) rate = clk_get_rate(&clk); if ((long)rate <= 0) { dev_err(dev, "Failed to get clk rate: %ld\n", (long)rate); - return ret; + return rate; } *clk_rate = rate; return 0; -- cgit v1.2.3 From 1363f9c1a0652448687a4a457507881aa1670ba2 Mon Sep 17 00:00:00 2001 From: Tomas Alvarez Vanoli Date: Wed, 20 Aug 2025 10:17:19 +0200 Subject: fsl_dspi: set scaler values for CS-SCK and SCK-CS delays These values were calculated but not set. They are required for the calculation of the delays, as stated in the "QorIQ LS1043A Reference Manual, Rev. 6, 07/2020" page 2172. The delays are calculated as (1/freq)*PCSSCK*CSSCK and (1/freq)*PASC*ASC. Signed-off-by: Tomas Alvarez Vanoli Reviewed-by: Vladimir Oltean Signed-off-by: Peng Fan --- drivers/spi/fsl_dspi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/fsl_dspi.c b/drivers/spi/fsl_dspi.c index 545561ad116..412993ad377 100644 --- a/drivers/spi/fsl_dspi.c +++ b/drivers/spi/fsl_dspi.c @@ -473,7 +473,9 @@ static int fsl_dspi_child_pre_probe(struct udevice *dev) priv->ctar_val[slave_plat->cs[0]] = DSPI_CTAR_DEFAULT_VALUE | DSPI_CTAR_PCSSCK(pcssck) | - DSPI_CTAR_PASC(pasc); + DSPI_CTAR_CSSCK(cssck) | + DSPI_CTAR_PASC(pasc) | + DSPI_CTAR_ASC(asc); debug("DSPI pre_probe slave device on CS %u, max_hz %u, mode 0x%x.\n", slave_plat->cs[0], slave_plat->max_hz, slave_plat->mode); -- cgit v1.2.3 From 8b97d7569a48d15949cc9274f40bae013e24a2a4 Mon Sep 17 00:00:00 2001 From: "Anthony Pighin (Nokia)" Date: Wed, 27 Aug 2025 16:12:21 +0800 Subject: drivers: crypto: fsl: rng: Reinitialize job ring u-boot internals were being corrupted following an EFI callback to get_rng(). One of the many footprints was a corruption of the EFI protocols linked list. A request for >16 bytes of random data is broken into smaller requests. Those requests are fed in a loop to the CAAM RNG, which uses a job queue ring for interaction. However, the job queue descriptor is created only at probe time. That descriptor may end up needing an endian swap (LS1046A) before being fed to the CAAM RNG. This corrupts the descriptor for the next iteration, since it will be blindly endian swapped yet again. Two issues arise. The number of words to endian swap is taken from the input descriptor itself. So on the second iteration, the length has been corrupted. This results in a corruption past the end of the descriptor: whatever is after in memory is endian swapped too. Second, some of the entries in the descriptor are DMA addresses. If the descriptor is still somehow considered valid after swapping, the data at the corrupted DMA address is now trampled. Linux properly initializes the descriptor for each iteration. This is what is now done with this commit. Signed-off-by: Anthony Pighin Signed-off-by: Peng Fan --- drivers/crypto/fsl/rng.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/fsl/rng.c b/drivers/crypto/fsl/rng.c index 786a710f5fb..06f37a8b4de 100644 --- a/drivers/crypto/fsl/rng.c +++ b/drivers/crypto/fsl/rng.c @@ -23,11 +23,26 @@ struct caam_rng_priv { u8 data[CAAM_RNG_MAX_FIFO_STORE_SIZE] __aligned(ARCH_DMA_MINALIGN); }; +static int caam_init_desc(struct caam_rng_priv *priv) +{ + ulong size = ALIGN(CAAM_RNG_DESC_LEN, ARCH_DMA_MINALIGN); + + inline_cnstr_jobdesc_rng(priv->desc, priv->data, + CAAM_RNG_MAX_FIFO_STORE_SIZE); + + flush_dcache_range((unsigned long)priv->desc, + (unsigned long)priv->desc + size); + + return 0; +} + static int caam_rng_read_one(struct caam_rng_priv *priv) { int size = ALIGN(CAAM_RNG_MAX_FIFO_STORE_SIZE, ARCH_DMA_MINALIGN); int ret; + caam_init_desc(priv); + ret = run_descriptor_jr(priv->desc); if (ret < 0) return -EIO; @@ -63,12 +78,8 @@ static int caam_rng_read(struct udevice *dev, void *data, size_t len) static int caam_rng_probe(struct udevice *dev) { struct caam_rng_priv *priv = dev_get_priv(dev); - ulong size = ALIGN(CAAM_RNG_DESC_LEN, ARCH_DMA_MINALIGN); - inline_cnstr_jobdesc_rng(priv->desc, priv->data, - CAAM_RNG_MAX_FIFO_STORE_SIZE); - flush_dcache_range((unsigned long)priv->desc, - (unsigned long)priv->desc + size); + caam_init_desc(priv); return 0; } -- cgit v1.2.3 From 446b7b8f2d9b5b9bcd473c91f1e45cbb77be6c5f Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Thu, 7 Aug 2025 15:28:57 +0100 Subject: power: axp: Remove redundant code In axp_init after checking the chip ID there is an else clause that returns ret. ret is guaranteed to be 0 at this point as the code would have returned above if not. The next statement is a return 0 so the return ret is redundant, remove it. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Reviewed-by: Quentin Schulz --- drivers/power/axp818.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/power/axp818.c b/drivers/power/axp818.c index 83ae6ecc138..01f9a209dc6 100644 --- a/drivers/power/axp818.c +++ b/drivers/power/axp818.c @@ -248,8 +248,6 @@ int axp_init(void) if (!(axp_chip_id == 0x51)) return -ENODEV; - else - return ret; return 0; } -- cgit v1.2.3 From aa136393c8fd448e903e3fd3297efb13e54c42d7 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Thu, 7 Aug 2025 15:28:58 +0100 Subject: power: power_i2c: ret is uninitialised if not DM_I2C In pmic_reg_read ret is only assigned to inside #if CONFIG_IS_ENABLED(DM_I2C) so move the test and return ret inside as well and also guard the declaration of ret with CONFIG_IS_ENABLED(DM_I2C) to prevent a warning about an unused variable. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Reviewed-by: Quentin Schulz --- drivers/power/power_i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/power/power_i2c.c b/drivers/power/power_i2c.c index c2fc1c6b42f..58289ecce67 100644 --- a/drivers/power/power_i2c.c +++ b/drivers/power/power_i2c.c @@ -75,7 +75,7 @@ int pmic_reg_read(struct pmic *p, u32 reg, u32 *val) { unsigned char buf[4] = { 0 }; u32 ret_val = 0; - int ret; + int __maybe_unused ret; if (check_reg(p, reg)) return -EINVAL; @@ -91,9 +91,9 @@ int pmic_reg_read(struct pmic *p, u32 reg, u32 *val) return -ENXIO; } ret = dm_i2c_read(dev, reg, buf, pmic_i2c_tx_num); -#endif if (ret) return ret; +#endif switch (pmic_i2c_tx_num) { case 3: -- cgit v1.2.3 From 42f959d0b091b779f235d7d0e54a794d4d857917 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Thu, 7 Aug 2025 17:35:18 +0100 Subject: power: regulator: lp873x: Cannot test unsigned for being negative In lp873x_buck_val and lp873x_ldo_val hex is an unsigned variable being assigned the return value from a function that returns int. Change hex to be an int so that the following test for an error as a negative value will work as expected. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Reviewed-by: Frieder Schrempf --- drivers/power/regulator/lp873x_regulator.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/power/regulator/lp873x_regulator.c b/drivers/power/regulator/lp873x_regulator.c index 271a7e45139..62d91a23f77 100644 --- a/drivers/power/regulator/lp873x_regulator.c +++ b/drivers/power/regulator/lp873x_regulator.c @@ -83,8 +83,8 @@ static int lp873x_buck_hex2volt(int hex) static int lp873x_buck_val(struct udevice *dev, int op, int *uV) { - unsigned int hex, adr; - int ret; + unsigned int adr; + int hex, ret; struct dm_regulator_uclass_plat *uc_pdata; uc_pdata = dev_get_uclass_plat(dev); @@ -177,8 +177,8 @@ static int lp873x_ldo_hex2volt(int hex) static int lp873x_ldo_val(struct udevice *dev, int op, int *uV) { - unsigned int hex, adr; - int ret; + unsigned int adr; + int hex, ret; struct dm_regulator_uclass_plat *uc_pdata; -- cgit v1.2.3 From 8f90028ccdbfc74487c4b40bd6fe5d34c3309b7a Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Thu, 7 Aug 2025 17:35:19 +0100 Subject: power: regulator: lp87565: Cannot test unsigned for being negative In lp87565_buck_val hex is an unsigned variable being assigned the return value from a function that returns int. Change hex to be an int so that the following test for an error as a negative value will work as expected. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Reviewed-by: Frieder Schrempf --- drivers/power/regulator/lp87565_regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/power/regulator/lp87565_regulator.c b/drivers/power/regulator/lp87565_regulator.c index 2212cb5c651..ea9c7685e9f 100644 --- a/drivers/power/regulator/lp87565_regulator.c +++ b/drivers/power/regulator/lp87565_regulator.c @@ -82,8 +82,8 @@ static int lp87565_buck_val2volt(int val) static int lp87565_buck_val(struct udevice *dev, int op, int *uV) { - unsigned int hex, adr; - int ret; + unsigned int adr; + int hex, ret; struct dm_regulator_uclass_plat *uc_pdata; uc_pdata = dev_get_uclass_plat(dev); -- cgit v1.2.3 From 96312ed79630838b09368c8c07ec164fd40a025c Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Thu, 7 Aug 2025 17:35:20 +0100 Subject: power: regulator: palmas: Cannot test unsigned for being negative In palmas_smps_val and palmas_ldo_val hex is an unsigned variable being assigned the return value from a function that returns int. Change hex to be an int so that the following test for an error as a negative value will work as expected. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Reviewed-by: Frieder Schrempf --- drivers/power/regulator/palmas_regulator.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/power/regulator/palmas_regulator.c b/drivers/power/regulator/palmas_regulator.c index 2286eac93fb..7212062c8c8 100644 --- a/drivers/power/regulator/palmas_regulator.c +++ b/drivers/power/regulator/palmas_regulator.c @@ -115,8 +115,8 @@ static int palmas_smps_hex2volt(int hex, bool range) static int palmas_smps_val(struct udevice *dev, int op, int *uV) { - unsigned int hex, adr; - int ret; + unsigned int adr; + int hex, ret; bool range; struct dm_regulator_uclass_plat *uc_pdata; @@ -251,8 +251,8 @@ static int palmas_ldo_hex2volt(int hex) static int palmas_ldo_val(struct udevice *dev, int op, int *uV) { - unsigned int hex, adr; - int ret; + unsigned int adr; + int hex, ret; struct dm_regulator_uclass_plat *uc_pdata; -- cgit v1.2.3 From cdc0e32e9c56fc3d14007d4c93394e8bcc8daa08 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Thu, 7 Aug 2025 17:35:21 +0100 Subject: power: regulator: rzg2l-usbphy: Add parenthesis to return expression In order to get the expected result from rzg2l_usbphy_regulator_get_enable the return expression needs parenthesis so that the binary and is performed before the double logical not. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Reviewed-by: Frieder Schrempf --- drivers/power/regulator/rzg2l-usbphy-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/regulator/rzg2l-usbphy-regulator.c b/drivers/power/regulator/rzg2l-usbphy-regulator.c index 451f04c140e..0354555d0b5 100644 --- a/drivers/power/regulator/rzg2l-usbphy-regulator.c +++ b/drivers/power/regulator/rzg2l-usbphy-regulator.c @@ -27,7 +27,7 @@ static int rzg2l_usbphy_regulator_get_enable(struct udevice *dev) { struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(dev->parent); - return !!readl(priv->regs + VBENCTL) & VBENCTL_VBUS_SEL; + return !!(readl(priv->regs + VBENCTL) & VBENCTL_VBUS_SEL); } static const struct dm_regulator_ops rzg2l_usbphy_regulator_ops = { -- cgit v1.2.3 From c18435e648f7004dee1a80d06dbc06223d3b76b3 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Thu, 7 Aug 2025 17:35:23 +0100 Subject: power: regulator: tps65941: Cannot test unsigned for being negative In tps65941_buck_val and tps65941_ldo_val hex is an unsigned variable being assigned the return value from a function that returns int. Change hex to be an int so that the following test for an error as a negative value will work as expected. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Reviewed-by: Frieder Schrempf --- drivers/power/regulator/tps65941_regulator.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/power/regulator/tps65941_regulator.c b/drivers/power/regulator/tps65941_regulator.c index 13f94b730d4..2561d6f4c6c 100644 --- a/drivers/power/regulator/tps65941_regulator.c +++ b/drivers/power/regulator/tps65941_regulator.c @@ -277,8 +277,8 @@ static const struct tps65941_reg_conv_ops buck_conv_ops[] = { static int tps65941_buck_val(struct udevice *dev, int op, int *uV) { - unsigned int hex, adr; - int ret, delta, uwait, slew, idx; + unsigned int adr; + int hex, ret, delta, uwait, slew, idx; struct dm_regulator_uclass_plat *uc_pdata; const struct tps65941_reg_conv_ops *conv_ops; ulong chip_id; @@ -479,8 +479,8 @@ static const struct tps65941_reg_conv_ops ldo_conv_ops[] = { static int tps65941_ldo_val(struct udevice *dev, int op, int *uV) { - unsigned int hex, adr; - int ret, ret_volt, idx, ldo_bypass; + unsigned int adr; + int hex, ret, ret_volt, idx, ldo_bypass; struct dm_regulator_uclass_plat *uc_pdata; const struct tps65941_reg_conv_ops *conv_ops; ulong chip_id; -- cgit v1.2.3 From 31a309ff3eca5a9bb4fa8228d5270d792e7e0522 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Fri, 4 Jul 2025 15:50:20 -0600 Subject: power: Correct dependencies on POWER_LEGACY The POWER_LEGACY option functionally depends on not having DM_PMIC enabled, so add that here. Signed-off-by: Tom Rini --- drivers/power/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index eed65058e66..fd1c871f0db 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -15,6 +15,7 @@ if POWER config POWER_LEGACY bool "Legacy power support" + depends on !DM_PMIC help Note: This is a legacy option. Use DM_PMIC instead. -- cgit v1.2.3 From 384079802a3eec3fff9578903285acc93ceda76f Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 4 Aug 2025 15:51:13 -0600 Subject: power: Tighten some power driver dependencies The MediaTek mt6323 power driver cannot build without access to some platform specific header files. Express that requirements in Kconfig as well. Signed-off-by: Tom Rini --- drivers/power/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index fd1c871f0db..d17337c0c3f 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -463,6 +463,7 @@ config TWL4030_POWER config POWER_MT6323 bool "Poweroff driver for mediatek mt6323" + depends on ARCH_MEDIATEK select CMD_POWEROFF help This adds poweroff driver for mt6323 -- cgit v1.2.3 From b7360bd9e8e05dcfd1b4e5fd1c996aabf919e180 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Mon, 11 Aug 2025 15:11:57 +0200 Subject: Revert "pmic: pca9450: Add optional SD_VSEL GPIO for LDO5" This reverts commit 2add0511757e2c5897a88b57c5ea8c912140e60f. It turns out that all boards using the PCA9450 actually have the SD_VSEL input connected to the VSELECT signal of the SoCs SD/MMC interface. Therefore we don't need manual control for this signal via GPIO and there aren't any users. This is equivalent to the following change in Linux: c73be62caabb ("Revert "regulator: pca9450: Add SD_VSEL GPIO for LDO5"") Signed-off-by: Frieder Schrempf Signed-off-by: Peng Fan --- drivers/power/pmic/pca9450.c | 22 ---------------------- 1 file changed, 22 deletions(-) (limited to 'drivers') diff --git a/drivers/power/pmic/pca9450.c b/drivers/power/pmic/pca9450.c index 9d875f8bdbe..0e3d14abf15 100644 --- a/drivers/power/pmic/pca9450.c +++ b/drivers/power/pmic/pca9450.c @@ -6,12 +6,9 @@ #include #include #include -#include #include -#include #include #include -#include #include #include #include @@ -29,10 +26,6 @@ static const struct pmic_child_info pmic_children_info[] = { { }, }; -struct pca9450_priv { - struct gpio_desc *sd_vsel_gpio; -}; - static int pca9450_reg_count(struct udevice *dev) { return PCA9450_REG_NUM; @@ -85,21 +78,7 @@ static int pca9450_bind(struct udevice *dev) static int pca9450_probe(struct udevice *dev) { - struct pca9450_priv *priv = dev_get_priv(dev); unsigned int reset_ctrl; - int ret = 0; - - if (CONFIG_IS_ENABLED(DM_GPIO) && CONFIG_IS_ENABLED(DM_REGULATOR_PCA9450)) { - priv->sd_vsel_gpio = devm_gpiod_get_optional(dev, "sd-vsel", - GPIOD_IS_OUT | - GPIOD_IS_OUT_ACTIVE); - if (IS_ERR(priv->sd_vsel_gpio)) { - ret = PTR_ERR(priv->sd_vsel_gpio); - dev_err(dev, "Failed to request SD_VSEL GPIO: %d\n", ret); - if (ret) - return ret; - } - } if (ofnode_read_bool(dev_ofnode(dev), "nxp,wdog_b-warm-reset")) reset_ctrl = PCA9450_PMIC_RESET_WDOG_B_CFG_WARM; @@ -132,5 +111,4 @@ U_BOOT_DRIVER(pmic_pca9450) = { .bind = pca9450_bind, .probe = pca9450_probe, .ops = &pca9450_ops, - .priv_auto = sizeof(struct pca9450_priv), }; -- cgit v1.2.3 From 8a04fbd9afd635d37c9afc1b56d014d134861e29 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Mon, 11 Aug 2025 15:11:58 +0200 Subject: pmic: pca9450: Fix enable register for LDO5 The LDO5 regulator has two configuration registers, but only LDO5CTRL_L contains the bits for enabling/disabling the regulator. This is equivalent to the following change in Linux: f5aab0438ef1 ("regulator: pca9450: Fix enable register for LDO5") Fixes: 326337fb005f ("pmic: pca9450: Add regulator driver") Signed-off-by: Frieder Schrempf Signed-off-by: Peng Fan --- drivers/power/regulator/pca9450.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/regulator/pca9450.c b/drivers/power/regulator/pca9450.c index a2a34244723..27db55e688d 100644 --- a/drivers/power/regulator/pca9450.c +++ b/drivers/power/regulator/pca9450.c @@ -144,7 +144,7 @@ static struct pca9450_plat pca9450_reg_data[] = { PCA_DATA("LDO4", PCA9450_LDO4CTRL, HW_STATE_CONTROL, PCA9450_LDO4CTRL, PCA9450_LDO34_MASK, pca9450_ldo34_vranges), - PCA_DATA("LDO5", PCA9450_LDO5CTRL_H, HW_STATE_CONTROL, + PCA_DATA("LDO5", PCA9450_LDO5CTRL_L, HW_STATE_CONTROL, PCA9450_LDO5CTRL_H, PCA9450_LDO5_MASK, pca9450_ldo5_vranges), }; -- cgit v1.2.3 From addfe4544630a96a1cd03daf822a196af42b7a73 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Mon, 11 Aug 2025 15:11:59 +0200 Subject: pmic: pca9450: Fix control register for LDO5 For LDO5 we need to be able to check the status of the SD_VSEL input in order to know which control register is used. Read the status of the SD_VSEL signal via GPIO and use the correct register accordingly. To use this, the LDO5 node in the devicetree needs the sd-vsel-gpios property to reference the GPIO that is used to read back the SD_VSEL status internally. Please note that the SION bit in the IOMUX must be set if the signal is muxed as VSELECT and controlled by the USDHC controller. This is equivalent to the following change in Linux: 3ce6f4f943dd ("regulator: pca9450: Fix control register for LDO5") Signed-off-by: Frieder Schrempf Signed-off-by: Peng Fan --- drivers/power/regulator/pca9450.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/power/regulator/pca9450.c b/drivers/power/regulator/pca9450.c index 27db55e688d..9d8d142b464 100644 --- a/drivers/power/regulator/pca9450.c +++ b/drivers/power/regulator/pca9450.c @@ -7,9 +7,12 @@ * ROHM BD71837 regulator driver */ +#include #include +#include #include #include +#include #include #include #include @@ -52,6 +55,7 @@ struct pca9450_plat { u8 volt_mask; struct pca9450_vrange *ranges; unsigned int numranges; + struct gpio_desc *sd_vsel_gpio; }; #define PCA_RANGE(_min, _vstep, _sel_low, _sel_hi) \ @@ -222,13 +226,23 @@ static int pca9450_set_enable(struct udevice *dev, bool enable) val); } +static u8 pca9450_get_vsel_reg(struct pca9450_plat *plat) +{ + if (!strcmp(plat->name, "LDO5") && + (plat->sd_vsel_gpio && !dm_gpio_get_value(plat->sd_vsel_gpio)) { + return PCA9450_LDO5CTRL_L; + } + + return plat->volt_reg; +} + static int pca9450_get_value(struct udevice *dev) { struct pca9450_plat *plat = dev_get_plat(dev); unsigned int reg, tmp; int i, ret; - ret = pmic_reg_read(dev->parent, plat->volt_reg); + ret = pmic_reg_read(dev->parent, pca9450_get_vsel_reg(plat)); if (ret < 0) return ret; @@ -274,7 +288,7 @@ static int pca9450_set_value(struct udevice *dev, int uvolt) if (!found) return -EINVAL; - return pmic_clrsetbits(dev->parent, plat->volt_reg, + return pmic_clrsetbits(dev->parent, pca9450_get_vsel_reg(plat), plat->volt_mask, sel); } @@ -335,6 +349,19 @@ static int pca9450_regulator_probe(struct udevice *dev) *plat = pca9450_reg_data[i]; + if (!strcmp(plat->name, "LDO5")) { + if (CONFIG_IS_ENABLED(DM_GPIO) && CONFIG_IS_ENABLED(DM_REGULATOR_PCA9450)) { + plat->sd_vsel_gpio = devm_gpiod_get_optional(dev, "sd-vsel", + GPIOD_IS_IN); + if (IS_ERR(plat->sd_vsel_gpio)) { + ret = PTR_ERR(plat->sd_vsel_gpio); + dev_err(dev, "Failed to request SD_VSEL GPIO: %d\n", ret); + if (ret) + return ret; + } + } + } + return 0; } -- cgit v1.2.3 From 281829f5ca1645c3bffa130d04753a1a76b64dfb Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Mon, 11 Aug 2025 15:12:00 +0200 Subject: pmic: pca9450: Handle hardware with fixed SD_VSEL for LDO5 There are two ways to set the output voltage of the LD05 regulator. First by writing to the voltage selection registers and second by toggling the SD_VSEL signal. Usually board designers connect SD_VSEL to the VSELECT signal controlled by the USDHC controller, but in some cases the signal is hardwired to a fixed low level (therefore selecting 3.3V as initial value for allowing to boot from the SD card). In these cases, the voltage is only determined by the value of the LDO5CTRL_L register. Introduce a property nxp,sd-vsel-fixed-low to let the driver know that SD_VSEL is low and there is no GPIO to actually get that information from dynamically. This is equivalent to the following change in Linux: c8c1ab2c5cb7 ("regulator: pca9450: Handle hardware with fixed SD_VSEL for LDO5") Signed-off-by: Frieder Schrempf Signed-off-by: Peng Fan --- drivers/power/regulator/pca9450.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/regulator/pca9450.c b/drivers/power/regulator/pca9450.c index 9d8d142b464..3bb0c71dde7 100644 --- a/drivers/power/regulator/pca9450.c +++ b/drivers/power/regulator/pca9450.c @@ -56,6 +56,7 @@ struct pca9450_plat { struct pca9450_vrange *ranges; unsigned int numranges; struct gpio_desc *sd_vsel_gpio; + bool sd_vsel_fixed_low; }; #define PCA_RANGE(_min, _vstep, _sel_low, _sel_hi) \ @@ -229,7 +230,8 @@ static int pca9450_set_enable(struct udevice *dev, bool enable) static u8 pca9450_get_vsel_reg(struct pca9450_plat *plat) { if (!strcmp(plat->name, "LDO5") && - (plat->sd_vsel_gpio && !dm_gpio_get_value(plat->sd_vsel_gpio)) { + ((plat->sd_vsel_gpio && !dm_gpio_get_value(plat->sd_vsel_gpio)) || + plat->sd_vsel_fixed_low)) { return PCA9450_LDO5CTRL_L; } @@ -360,6 +362,8 @@ static int pca9450_regulator_probe(struct udevice *dev) return ret; } } + + plat->sd_vsel_fixed_low = dev_read_bool(dev, "nxp,sd-vsel-fixed-low"); } return 0; -- cgit v1.2.3 From 38443338c7d491852d21d6c5e7e742fc347b9a44 Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Fri, 22 Aug 2025 20:33:23 +0200 Subject: power: pmic: s2mps11: look for both {voltage-,}regulators Linux's DTSes uses regulators { }; while u-boot's DTSes uses voltage-regulators { };. Look for regulators, and fallback to voltage-regulators if not found, so that both type of DTSes can be used with the driver. Signed-off-by: Henrik Grimler Reviewed-by: Anand Moon Signed-off-by: Peng Fan --- drivers/power/pmic/s2mps11.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/power/pmic/s2mps11.c b/drivers/power/pmic/s2mps11.c index 17780017035..3aa8cad0827 100644 --- a/drivers/power/pmic/s2mps11.c +++ b/drivers/power/pmic/s2mps11.c @@ -52,11 +52,14 @@ static int s2mps11_probe(struct udevice *dev) ofnode regulators_node; int children; - regulators_node = dev_read_subnode(dev, "voltage-regulators"); + regulators_node = dev_read_subnode(dev, "regulators"); if (!ofnode_valid(regulators_node)) { - debug("%s: %s regulators subnode not found!\n", __func__, - dev->name); - return -ENXIO; + regulators_node = dev_read_subnode(dev, "voltage-regulators"); + if (!ofnode_valid(regulators_node)) { + debug("%s: %s regulators subnode not found!\n", __func__, + dev->name); + return -ENXIO; + } } debug("%s: '%s' - found regulators subnode\n", __func__, dev->name); -- cgit v1.2.3 From 983a16f38627c49704811c12ca7c6d134b08289c Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Fri, 22 Aug 2025 20:33:25 +0200 Subject: power: pmic: s2mps11: remove check for voltage-regulators node All devicetrees that use s2mps11 driver have been converted to use regulators { };, so we can safely drop the voltage-regulators fallback check. Signed-off-by: Henrik Grimler Reviewed-by: Anand Moon Signed-off-by: Peng Fan --- drivers/power/pmic/s2mps11.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/power/pmic/s2mps11.c b/drivers/power/pmic/s2mps11.c index 3aa8cad0827..6e819579030 100644 --- a/drivers/power/pmic/s2mps11.c +++ b/drivers/power/pmic/s2mps11.c @@ -54,12 +54,9 @@ static int s2mps11_probe(struct udevice *dev) regulators_node = dev_read_subnode(dev, "regulators"); if (!ofnode_valid(regulators_node)) { - regulators_node = dev_read_subnode(dev, "voltage-regulators"); - if (!ofnode_valid(regulators_node)) { - debug("%s: %s regulators subnode not found!\n", __func__, - dev->name); - return -ENXIO; - } + debug("%s: %s regulators subnode not found!\n", __func__, + dev->name); + return -ENXIO; } debug("%s: '%s' - found regulators subnode\n", __func__, dev->name); -- cgit v1.2.3 From 0fdd3b4243359fd81d11923b711c28230795cc54 Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Fri, 22 Aug 2025 20:33:28 +0200 Subject: power: pmic: fix typo and capitalisation in max8997 Kconfig help msg To make the help message slightly easier to understand. Signed-off-by: Henrik Grimler Signed-off-by: Peng Fan --- drivers/power/pmic/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index ec7ccc3a63f..b1a5b1c2a1f 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -228,9 +228,9 @@ config PMIC_MAX8997 This is a Power Management IC with RTC, Fuel Gauge, MUIC control on Chip. - 21x LDOs - 12x GPIOs - - Haptic Motor driver + - Haptic motor driver - RTC with two alarms - - Fueal Gauge and One backup battery charger + - Fuel Gauge and one backup battery charger - MUIC - Others -- cgit v1.2.3 From 920404409cf1bb7644199b4194bf137fc62cff82 Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Fri, 22 Aug 2025 20:33:29 +0200 Subject: power: pmic: max8997: support maxim,max8997-pmic compatible as well Linux's DTSes uses maxim,max8997-pmic, so check for this compatible as well so that max8997 pmic driver can support both u-boot and Linux's DTSes. Signed-off-by: Henrik Grimler Signed-off-by: Peng Fan --- drivers/power/pmic/max8997.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/power/pmic/max8997.c b/drivers/power/pmic/max8997.c index 4afa6c84ef8..ecdad505b26 100644 --- a/drivers/power/pmic/max8997.c +++ b/drivers/power/pmic/max8997.c @@ -47,6 +47,7 @@ static struct dm_pmic_ops max8997_ops = { static const struct udevice_id max8997_ids[] = { { .compatible = "maxim,max8997" }, + { .compatible = "maxim,max8997-pmic" }, { }, }; -- cgit v1.2.3 From 31c376cf4f383e57ce2053c3e4b8c7138fdb1e6e Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Fri, 22 Aug 2025 20:33:31 +0200 Subject: power: pmic: max8997: drop maxim,max8997 compatible All u-boot users now use maxim,max8997-pmic instead, as does Linux's DTSes, so we can now safely drop the maxim,max8997 compatible. Signed-off-by: Henrik Grimler Reviewed-by: Anand Moon Signed-off-by: Peng Fan --- drivers/power/pmic/max8997.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/pmic/max8997.c b/drivers/power/pmic/max8997.c index ecdad505b26..fb65a3ca730 100644 --- a/drivers/power/pmic/max8997.c +++ b/drivers/power/pmic/max8997.c @@ -46,7 +46,6 @@ static struct dm_pmic_ops max8997_ops = { }; static const struct udevice_id max8997_ids[] = { - { .compatible = "maxim,max8997" }, { .compatible = "maxim,max8997-pmic" }, { }, }; -- cgit v1.2.3 From 9065b87f35018a4e993e7dfb8a8ec29474785f76 Mon Sep 17 00:00:00 2001 From: Primoz Fiser Date: Thu, 28 Aug 2025 13:24:04 +0200 Subject: power: pmic: pca9450: Add support for system reset The family of PCA9450 PMICs have the ability to perform system resets. Restarting via PMIC is preferred method of restarting the system as all the peripherals are brought to a know state after a power-cycle. The PCA9450 features a cold restart procedure which is initiated by an I2C command 0x14 to the SW_RST register. Support in Linux for restarting via PCA9450 PMIC has been added by Linux commit 6157e62b07d9 ("regulator: pca9450: Add restart handler"). Now add support for it also in the U-Boot via sysreset framework. Signed-off-by: Primoz Fiser Reviewed-by: Paul Geurts Signed-off-by: Peng Fan --- drivers/power/pmic/pca9450.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'drivers') diff --git a/drivers/power/pmic/pca9450.c b/drivers/power/pmic/pca9450.c index 0e3d14abf15..8b98c2239e1 100644 --- a/drivers/power/pmic/pca9450.c +++ b/drivers/power/pmic/pca9450.c @@ -6,13 +6,17 @@ #include #include #include +#include +#include #include #include #include +#include #include #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -79,6 +83,15 @@ static int pca9450_bind(struct udevice *dev) static int pca9450_probe(struct udevice *dev) { unsigned int reset_ctrl; + int ret; + + if (CONFIG_IS_ENABLED(SYSRESET)) { + ret = device_bind_driver_to_node(dev, "pca9450_sysreset", + "pca9450_sysreset", + dev_ofnode(dev), NULL); + if (ret) + return ret; + } if (ofnode_read_bool(dev_ofnode(dev), "nxp,wdog_b-warm-reset")) reset_ctrl = PCA9450_PMIC_RESET_WDOG_B_CFG_WARM; @@ -112,3 +125,34 @@ U_BOOT_DRIVER(pmic_pca9450) = { .probe = pca9450_probe, .ops = &pca9450_ops, }; + +#ifdef CONFIG_SYSRESET +static int pca9450_sysreset_request(struct udevice *dev, enum sysreset_t type) +{ + u8 cmd = PCA9450_SW_RST_COLD_RST; + + if (type != SYSRESET_COLD) + return -EPROTONOSUPPORT; + + if (pmic_write(dev->parent, PCA9450_SW_RST, &cmd, 1)) { + dev_err(dev, "reset command failed\n"); + } else { + /* tRESTART is 250ms, delay 300ms just to be sure */ + mdelay(300); + /* Should not get here, warn if we do */ + dev_warn(dev, "didn't respond to reset command\n"); + } + + return -EINPROGRESS; +} + +static struct sysreset_ops pca9450_sysreset_ops = { + .request = pca9450_sysreset_request, +}; + +U_BOOT_DRIVER(pca9450_sysreset) = { + .name = "pca9450_sysreset", + .id = UCLASS_SYSRESET, + .ops = &pca9450_sysreset_ops, +}; +#endif /* CONFIG_SYSRESET */ -- cgit v1.2.3 From ad197b31b3f88d53c349995d8bbaeba4b18b8f2b Mon Sep 17 00:00:00 2001 From: Primoz Fiser Date: Thu, 28 Aug 2025 13:24:05 +0200 Subject: power: pmic: pca9450: Add support for reset status PCA9450 PMIC supports reading the reset status from the PWRON_STAT register. Bits 7-4 give indication of the PMIC reset cause: - PWRON (BIT7) - Power ON triggered by PMIC_ON_REQ input line, - WDOGB (BIT6) - Boot after cold reset by WDOGB pin (watchdog reset), - SW_RST (BIT5) - Boot after cold reset initiated by the software, - PMIC_RST (BIT4) - Boot after PMIC_RST_B input line trigger. Add support for reading reset status via the sysreset framework in a convenient printable format. Signed-off-by: Primoz Fiser Reviewed-by: Paul Geurts Signed-off-by: Peng Fan --- drivers/power/pmic/pca9450.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'drivers') diff --git a/drivers/power/pmic/pca9450.c b/drivers/power/pmic/pca9450.c index 8b98c2239e1..e5c1f037b61 100644 --- a/drivers/power/pmic/pca9450.c +++ b/drivers/power/pmic/pca9450.c @@ -146,8 +146,46 @@ static int pca9450_sysreset_request(struct udevice *dev, enum sysreset_t type) return -EINPROGRESS; } +int pca9450_sysreset_get_status(struct udevice *dev, char *buf, int size) +{ + const char *reason; + int ret; + u8 reg; + + ret = pmic_read(dev->parent, PCA9450_PWRON_STAT, ®, 1); + if (ret) + return ret; + + switch (reg) { + case PCA9450_PWRON_STAT_PWRON_MASK: + reason = "PWRON"; + break; + case PCA9450_PWRON_STAT_WDOG_MASK: + reason = "WDOGB"; + break; + case PCA9450_PWRON_STAT_SW_RST_MASK: + reason = "SW_RST"; + break; + case PCA9450_PWRON_STAT_PMIC_RST_MASK: + reason = "PMIC_RST"; + break; + default: + reason = "UNKNOWN"; + break; + } + + ret = snprintf(buf, size, "Reset Status: %s\n", reason); + if (ret < 0) { + dev_err(dev, "Write reset status error (err = %d)\n", ret); + return -EIO; + } + + return 0; +} + static struct sysreset_ops pca9450_sysreset_ops = { .request = pca9450_sysreset_request, + .get_status = pca9450_sysreset_get_status, }; U_BOOT_DRIVER(pca9450_sysreset) = { -- cgit v1.2.3 From 95994d4e5996610c8205597b2849b48eaaf2baff Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Wed, 30 Jul 2025 17:52:56 +0100 Subject: mmc: iproc_sdhci: Cannot test unsigned variable for negative In sdhci_iproc_execute_tuning the variable tuning_loop_counter is unsigned and therefore will always fail the test for it being less than 0. Fix this by changing the variable type to be s8. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Reviewed-by: Peng Fan Signed-off-by: Peng Fan --- drivers/mmc/iproc_sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/iproc_sdhci.c b/drivers/mmc/iproc_sdhci.c index 7ab74ff117a..654672a546f 100644 --- a/drivers/mmc/iproc_sdhci.c +++ b/drivers/mmc/iproc_sdhci.c @@ -182,7 +182,7 @@ static int sdhci_iproc_execute_tuning(struct mmc *mmc, u8 opcode) u32 ctrl; u32 blocksize = SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, 64); struct sdhci_host *host = dev_get_priv(mmc->dev); - char tuning_loop_counter = MAX_TUNING_LOOP; + s8 tuning_loop_counter = MAX_TUNING_LOOP; int ret = 0; sdhci_start_tuning(host); -- cgit v1.2.3 From 140562d3a2b447079499d773a7d21ec1728692cb Mon Sep 17 00:00:00 2001 From: Bhimeswararao Matsa Date: Fri, 29 Aug 2025 07:41:31 +0530 Subject: mmc: core: drop space before newline in trace printf Remove unnecessary whitespace before '\n' in trace printf format strings (checkpatch warning QUOTED_WHITESPACE_BEFORE_NEWLINE). No functional change intended. Signed-off-by: Bhimeswararao Matsa Signed-off-by: Peng Fan --- drivers/mmc/mmc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 5f2efbe6df9..20afcffde3d 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -125,21 +125,21 @@ void mmmc_trace_after_send(struct mmc *mmc, struct mmc_cmd *cmd, int ret) printf("\t\tMMC_RSP_NONE\n"); break; case MMC_RSP_R1: - printf("\t\tMMC_RSP_R1,5,6,7 \t 0x%08x \n", + printf("\t\tMMC_RSP_R1,5,6,7 \t 0x%08x\n", cmd->response[0]); break; case MMC_RSP_R1b: - printf("\t\tMMC_RSP_R1b\t\t 0x%08x \n", + printf("\t\tMMC_RSP_R1b\t\t 0x%08x\n", cmd->response[0]); break; case MMC_RSP_R2: - printf("\t\tMMC_RSP_R2\t\t 0x%08x \n", + printf("\t\tMMC_RSP_R2\t\t 0x%08x\n", cmd->response[0]); - printf("\t\t \t\t 0x%08x \n", + printf("\t\t \t\t 0x%08x\n", cmd->response[1]); - printf("\t\t \t\t 0x%08x \n", + printf("\t\t \t\t 0x%08x\n", cmd->response[2]); - printf("\t\t \t\t 0x%08x \n", + printf("\t\t \t\t 0x%08x\n", cmd->response[3]); printf("\n"); printf("\t\t\t\t\tDUMPING DATA\n"); @@ -154,7 +154,7 @@ void mmmc_trace_after_send(struct mmc *mmc, struct mmc_cmd *cmd, int ret) } break; case MMC_RSP_R3: - printf("\t\tMMC_RSP_R3,4\t\t 0x%08x \n", + printf("\t\tMMC_RSP_R3,4\t\t 0x%08x\n", cmd->response[0]); break; default: -- cgit v1.2.3 From e0f9a4fb576c3f4233393194161e48941823cbc3 Mon Sep 17 00:00:00 2001 From: Ben Hoelker Date: Mon, 18 Aug 2025 10:30:00 +1200 Subject: drivers: rtc: max313xx: Add delay after setting date The MAX31331 was not correctly updating the seconds when setting the time and would return the seconds previously set. Like the MAX31343, a delay needs to be added after setting the time. Wait one second after writing so that the date command shows the correct time. Reviewed-by: Chris Packham Reviewed-by: Bruce Adams Signed-off-by: Ben Hoelker --- drivers/rtc/max313xx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/rtc/max313xx.c b/drivers/rtc/max313xx.c index 2cb3f245041..f0d38b10c97 100644 --- a/drivers/rtc/max313xx.c +++ b/drivers/rtc/max313xx.c @@ -308,6 +308,7 @@ static int max313xx_set_time(struct udevice *dev, const struct rtc_time *t) return ret; break; + case ID_MAX31331: case ID_MAX31343: /* Time is not updated for 1 second after writing */ /* Sleep here so the date command shows the new time */ -- cgit v1.2.3 From a3f0a8e7a1be4843363967e63d8e5c7b725ce2fc Mon Sep 17 00:00:00 2001 From: Alif Zakuan Yuslaimi Date: Sun, 17 Aug 2025 19:55:02 -0700 Subject: misc: fs_loader: Initialize actread variable Initialize the actread variable to prevent undefined behavior that can occur if the variable is used before being assigned a value. This will help to prevent potential issues, especially if actread is used (e.g., read, incremented, or returned) before being explicitly set elsewhere in the code. Signed-off-by: Alif Zakuan Yuslaimi --- drivers/misc/fs_loader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/fs_loader.c b/drivers/misc/fs_loader.c index 60296d55f23..1fdfd8636e0 100644 --- a/drivers/misc/fs_loader.c +++ b/drivers/misc/fs_loader.c @@ -148,7 +148,7 @@ static int _request_firmware_prepare(struct udevice *dev, */ static int fw_get_filesystem_firmware(struct udevice *dev) { - loff_t actread; + loff_t actread = 0; char *storage_interface, *dev_part, *ubi_mtdpart, *ubi_volume; int ret; -- cgit v1.2.3 From a5b483a52a5561030ab3e50923a215fbed49d006 Mon Sep 17 00:00:00 2001 From: Mark Kettenis Date: Sat, 23 Aug 2025 16:03:56 +0200 Subject: pci: apple: Fix use of uninitialized variable Replace use of uninitialized variable with the PCI device number in an error message as this is what we use elsewhere to derive the PCIe port number. Use ofnode_read_pci_addr() to read the PCI address of the node and derive the device number from that. Signed-off-by: Mark Kettenis Reported-by: Andrew Goodbody --- drivers/pci/pcie_apple.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie_apple.c b/drivers/pci/pcie_apple.c index 6a8e715d4b6..f5fda9835dc 100644 --- a/drivers/pci/pcie_apple.c +++ b/drivers/pci/pcie_apple.c @@ -246,8 +246,9 @@ static int apple_pcie_setup_port(struct apple_pcie_priv *pcie, ofnode np) { struct apple_pcie_port *port; struct gpio_desc reset; + struct fdt_pci_addr pci_addr; fdt_addr_t addr; - u32 stat, idx; + u32 stat; int ret; char name[16]; @@ -259,12 +260,13 @@ static int apple_pcie_setup_port(struct apple_pcie_priv *pcie, ofnode np) if (!port) return -ENOMEM; - ret = ofnode_read_u32_index(np, "reg", 0, &idx); + ret = ofnode_read_pci_addr(np, FDT_PCI_SPACE_CONFIG, "reg", + &pci_addr, NULL); if (ret) return ret; /* Use the first reg entry to work out the port index */ - port->idx = idx >> 11; + port->idx = PCI_DEV(pci_addr.phys_hi); port->pcie = pcie; port->reset = reset; port->np = np; @@ -333,9 +335,10 @@ static int apple_pcie_setup_port(struct apple_pcie_priv *pcie, ofnode np) static int apple_pcie_probe(struct udevice *dev) { struct apple_pcie_priv *pcie = dev_get_priv(dev); + struct fdt_pci_addr pci_addr; fdt_addr_t addr; ofnode of_port; - int i, ret; + int ret; pcie->hw = (struct reg_info *)dev_get_driver_data(dev); @@ -357,9 +360,14 @@ static int apple_pcie_probe(struct udevice *dev) of_port = ofnode_next_subnode(of_port)) { if (!ofnode_is_enabled(of_port)) continue; + ret = ofnode_read_pci_addr(of_port, FDT_PCI_SPACE_CONFIG, + "reg", &pci_addr, NULL); + if (ret) + continue; ret = apple_pcie_setup_port(pcie, of_port); if (ret) { - dev_err(pcie->dev, "Port %d setup fail: %d\n", i, ret); + dev_err(pcie->dev, "Port %d setup fail: %d\n", + PCI_DEV(pci_addr.phys_hi), ret); return ret; } } -- cgit v1.2.3 From 5964c6f4ef0934130f41c347c771dfb97210490c Mon Sep 17 00:00:00 2001 From: Bhimeswararao Matsa Date: Fri, 29 Aug 2025 07:41:29 +0530 Subject: i2c: davinci: prefer kernel types (u8/u32) Replace uint8_t/uint32_t with u8/u32 to match U-Boot style (checkpatch PREFER_KERNEL_TYPES). No functional change. Signed-off-by: Bhimeswararao Matsa --- drivers/i2c/davinci_i2c.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/davinci_i2c.c b/drivers/i2c/davinci_i2c.c index 39132747208..9bba0600e3e 100644 --- a/drivers/i2c/davinci_i2c.c +++ b/drivers/i2c/davinci_i2c.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "davinci_i2c.h" /* Information about i2c controller */ @@ -87,7 +88,7 @@ static void _flush_rx(struct i2c_regs *i2c_base) static uint _davinci_i2c_setspeed(struct i2c_regs *i2c_base, uint speed) { - uint32_t div, psc; + u32 div, psc; psc = 2; /* SCLL + SCLH */ @@ -122,10 +123,10 @@ static void _davinci_i2c_init(struct i2c_regs *i2c_base, udelay(1000); } -static int _davinci_i2c_read(struct i2c_regs *i2c_base, uint8_t chip, - uint32_t addr, int alen, uint8_t *buf, int len) +static int _davinci_i2c_read(struct i2c_regs *i2c_base, u8 chip, + u32 addr, int alen, u8 *buf, int len) { - uint32_t tmp; + u32 tmp; int i; if ((alen < 0) || (alen > 2)) { @@ -220,10 +221,10 @@ static int _davinci_i2c_read(struct i2c_regs *i2c_base, uint8_t chip, return 0; } -static int _davinci_i2c_write(struct i2c_regs *i2c_base, uint8_t chip, - uint32_t addr, int alen, uint8_t *buf, int len) +static int _davinci_i2c_write(struct i2c_regs *i2c_base, u8 chip, + u32 addr, int alen, u8 *buf, int len) { - uint32_t tmp; + u32 tmp; int i; if ((alen < 0) || (alen > 2)) { @@ -302,7 +303,7 @@ static int _davinci_i2c_write(struct i2c_regs *i2c_base, uint8_t chip, return 0; } -static int _davinci_i2c_probe_chip(struct i2c_regs *i2c_base, uint8_t chip) +static int _davinci_i2c_probe_chip(struct i2c_regs *i2c_base, u8 chip) { int rc = 1; -- cgit v1.2.3 From c85b8071e7d3fd333f8a2fdd28083cb5ec3a0645 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 30 Aug 2025 22:39:54 +0200 Subject: virtio: blk: support block sizes exceeding 512 bytes QEMU allows to specify the logical block size via parameter logical_block_size of a virtio-blk-device. The communication channel via virtqueues remains based on 512 byte blocks even if the logical_block_size is larger. Consider the logical block size in the block device driver. Reported-by: Emil Renner Berthing Signed-off-by: Heinrich Schuchardt Tested-by: Emil Renner Berthing --- drivers/virtio/virtio_blk.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/virtio/virtio_blk.c b/drivers/virtio/virtio_blk.c index 2f999fc8bbe..4224e3c17f4 100644 --- a/drivers/virtio/virtio_blk.c +++ b/drivers/virtio/virtio_blk.c @@ -12,10 +12,17 @@ #include #include #include +#include #include "virtio_blk.h" +/** + * struct virtio_blk_priv - private date for virtio block device + */ struct virtio_blk_priv { + /** @virtqueue - virtqueue to process */ struct virtqueue *vq; + /** @blksz_shift - log2 of block size divided by 512 */ + u32 blksz_shift; }; static const u32 feature[] = { @@ -71,6 +78,8 @@ static ulong virtio_blk_do_req(struct udevice *dev, u64 sector, u8 status; int ret; + sector <<= priv->blksz_shift; + blkcnt <<= priv->blksz_shift; virtio_blk_init_header_sg(dev, sector, type, &out_hdr, &hdr_sg); sgs[num_out++] = &hdr_sg; @@ -109,7 +118,7 @@ static ulong virtio_blk_do_req(struct udevice *dev, u64 sector, ; log_debug("done\n"); - return status == VIRTIO_BLK_S_OK ? blkcnt : -EIO; + return status == VIRTIO_BLK_S_OK ? blkcnt >> priv->blksz_shift : -EIO; } static ulong virtio_blk_read(struct udevice *dev, lbaint_t start, @@ -177,15 +186,25 @@ static int virtio_blk_probe(struct udevice *dev) struct blk_desc *desc = dev_get_uclass_plat(dev); u64 cap; int ret; + u32 blk_size; ret = virtio_find_vqs(dev, 1, &priv->vq); if (ret) return ret; - desc->blksz = 512; - desc->log2blksz = 9; virtio_cread(dev, struct virtio_blk_config, capacity, &cap); desc->lba = cap; + if (!virtio_has_feature(dev, VIRTIO_BLK_F_BLK_SIZE)) { + virtio_cread(dev, struct virtio_blk_config, blk_size, &blk_size); + desc->blksz = blk_size; + if (!is_power_of_2(blk_size) || desc->blksz < 512) + return -EIO; + } else { + desc->blksz = 512; + } + desc->log2blksz = LOG2(desc->blksz); + priv->blksz_shift = desc->log2blksz - 9; + desc->lba >>= priv->blksz_shift; return 0; } -- cgit v1.2.3 From 41d9ac102536a07f8561533873bb8e772e61d54a Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Fri, 5 Sep 2025 11:25:13 +0000 Subject: gpio: adp5588: Add ADP5587 as compatible The ADP5587 is a simpler version of the ADP5588. The ADP5588 can configure two pins, C8 and C9, as GPIOs or light sensors. The ADP5587 does not include the light sensors. Signed-off-by: Philip Molloy --- drivers/gpio/adp5588_gpio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/adp5588_gpio.c b/drivers/gpio/adp5588_gpio.c index d081e169897..36304e48893 100644 --- a/drivers/gpio/adp5588_gpio.c +++ b/drivers/gpio/adp5588_gpio.c @@ -168,7 +168,7 @@ static int adp5588_ofdata_platdata(struct udevice *dev) revid = ret & ID_MASK; - printf("ADP5588 Detected: Rev %x, Rev ID %x\n", ret, revid); + printf("ADP558x Detected: Rev %x, Rev ID %x\n", ret, revid); for (i = 0, ret = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) { plat->dat_out[i] = adp5588_gpio_read(dev, GPIO_DAT_OUT1 + i); @@ -194,6 +194,7 @@ static const struct dm_gpio_ops adp5588_ops = { static const struct udevice_id adp5588_of_match_list[] = { { .compatible = "adi,adp5588"}, + { .compatible = "adi,adp5587"}, { /* sentinel */ } }; -- cgit v1.2.3 From 56aa947c2ad6cbdf4e7a4281e83689ac3fbc8498 Mon Sep 17 00:00:00 2001 From: Greg Malysa Date: Wed, 3 Sep 2025 19:42:01 -0400 Subject: net: dwc_eth_qos_adi: Add missing header Following header dependency cleanups, an implicit dependence on env.h was exposed in dwc_eth_qos_adi. However because this driver is not (yet) enabled in any defconfigs, build tests did not identify the missing header. This adds the missing #include so that the driver builds correctly when enabled. Signed-off-by: Greg Malysa --- drivers/net/dwc_eth_qos_adi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/dwc_eth_qos_adi.c b/drivers/net/dwc_eth_qos_adi.c index 0e6a901e303..fee50a88156 100644 --- a/drivers/net/dwc_eth_qos_adi.c +++ b/drivers/net/dwc_eth_qos_adi.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include -- cgit v1.2.3 From 2941e4c0476c69ef10e0900a8d9cac40a6dcc831 Mon Sep 17 00:00:00 2001 From: Greg Malysa Date: Wed, 3 Sep 2025 19:42:02 -0400 Subject: mmc: adi_sdhci: Update headers As part of the header dependency cleanup between 2025.07 and 2025.10, an implicit route to obtain SZ_128M from linux/sizes.h was removed. This adds an explicit reference to linux/sizes.h to fix build failures for this driver. Signed-off-by: Greg Malysa Reviewed-by: Peng Fan --- drivers/mmc/adi_sdhci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mmc/adi_sdhci.c b/drivers/mmc/adi_sdhci.c index 65a22cefb71..f58897b5218 100644 --- a/drivers/mmc/adi_sdhci.c +++ b/drivers/mmc/adi_sdhci.c @@ -15,6 +15,7 @@ #include #include #include +#include /* 400KHz is max freq for card ID etc. Use that as min */ #define EMMC_MIN_FREQ 400000 -- cgit v1.2.3 From 7be74f63f7ebeec1665a4f0f1cf3e41c5444518d Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 6 Sep 2025 02:01:18 +0200 Subject: thermal: Sort the Makefile Sort the Makefile alphabetically. No functional change. Signed-off-by: Marek Vasut --- drivers/thermal/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index b5ab0fc221f..18ad453f9b1 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -4,9 +4,9 @@ # Author: Nitin Garg obj-$(CONFIG_DM_THERMAL) += thermal-uclass.o -obj-$(CONFIG_SANDBOX) += thermal_sandbox.o -obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o obj-$(CONFIG_IMX_SCU_THERMAL) += imx_scu_thermal.o -obj-$(CONFIG_TI_DRA7_THERMAL) += ti-bandgap.o +obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o obj-$(CONFIG_IMX_TMU) += imx_tmu.o +obj-$(CONFIG_SANDBOX) += thermal_sandbox.o +obj-$(CONFIG_TI_DRA7_THERMAL) += ti-bandgap.o obj-$(CONFIG_TI_LM74_THERMAL) += ti-lm74.o -- cgit v1.2.3 From 27fc6a67a4ca0ab565a1135a30b1e132de3bbed8 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 6 Sep 2025 02:00:53 +0200 Subject: thermal: sandbox: Staticize sandbox_thermal_get_temp() Make sandbox_thermal_get_temp() static, since this is not called outside of the driver. No functional change. Signed-off-by: Marek Vasut --- drivers/thermal/thermal_sandbox.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/thermal/thermal_sandbox.c b/drivers/thermal/thermal_sandbox.c index 9af0d0247cb..b7c567d76cd 100644 --- a/drivers/thermal/thermal_sandbox.c +++ b/drivers/thermal/thermal_sandbox.c @@ -9,7 +9,7 @@ #include #include -int sandbox_thermal_get_temp(struct udevice *dev, int *temp) +static int sandbox_thermal_get_temp(struct udevice *dev, int *temp) { /* Simply return 100 deg C */ *temp = 100; -- cgit v1.2.3 From c6a4b44cdcc11431223a9d9db17de0c587d49c72 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 7 Sep 2025 01:00:44 +0200 Subject: phy: Reset init count on phy exit failure In case the PHY exit callback reports failure, reset init_count to 0 anyway, so the next attempt at PHY initialization might try to reinitialize the PHY and restore it to normal operation. Signed-off-by: Marek Vasut Reviewed-by: Siddharth Vadapalli --- drivers/phy/phy-uclass.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/phy/phy-uclass.c b/drivers/phy/phy-uclass.c index 714be123856..f8d4fb3b41b 100644 --- a/drivers/phy/phy-uclass.c +++ b/drivers/phy/phy-uclass.c @@ -274,7 +274,7 @@ int generic_phy_exit(struct phy *phy) { struct phy_counts *counts; struct phy_ops const *ops; - int ret; + int ret = 0; if (!generic_phy_valid(phy)) return 0; @@ -292,12 +292,11 @@ int generic_phy_exit(struct phy *phy) if (ret) { dev_err(phy->dev, "PHY: Failed to exit %s: %d.\n", phy->dev->name, ret); - return ret; } } counts->init_count = 0; - return 0; + return ret; } int generic_phy_power_on(struct phy *phy) -- cgit v1.2.3 From 0b5ae33eb31aa1a31d07152f1e59ebfc6c0a5424 Mon Sep 17 00:00:00 2001 From: Gatien Chevallier Date: Fri, 8 Aug 2025 16:03:57 +0200 Subject: ARM: stm32mp: replace RIFSC check access APIs Replace RIFSC check access APIs by grant/release access ones that handle the RIF semaphores. Signed-off-by: Gatien Chevallier Signed-off-by: Patrice Chotard Reviewed-by: Patrick Delaunay --- drivers/clk/stm32/clk-stm32mp25.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/stm32/clk-stm32mp25.c b/drivers/clk/stm32/clk-stm32mp25.c index 18c0b1cb867..b487f33b6c7 100644 --- a/drivers/clk/stm32/clk-stm32mp25.c +++ b/drivers/clk/stm32/clk-stm32mp25.c @@ -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); -- cgit v1.2.3 From b7328e2f39d946f8727d28a12df24fb8f351a0d0 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Thu, 11 Sep 2025 08:59:41 +0200 Subject: memory: Add STM32 Octo Memory Manager driver Octo Memory Manager driver (OMM) manages: - the muxing between 2 OSPI busses and 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 - the split of the memory area shared between the 2 OSPI instances. - chip select selection override. - the time between 2 transactions in multiplexed mode. - check firewall access. Signed-off-by: Patrice Chotard Reviewed-by: Patrick Delaunay --- drivers/memory/Kconfig | 17 ++ drivers/memory/Makefile | 1 + drivers/memory/stm32_omm.c | 421 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 439 insertions(+) create mode 100644 drivers/memory/stm32_omm.c (limited to 'drivers') diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 7c40f176987..e31c4532279 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -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 + tristate "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 diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index fdc83e4e1c8..77294fac69d 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -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 diff --git a/drivers/memory/stm32_omm.c b/drivers/memory/stm32_omm.c new file mode 100644 index 00000000000..d5a4e1b0683 --- /dev/null +++ b/drivers/memory/stm32_omm.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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, +}; -- cgit v1.2.3 From 01eb0a583860aa8bb9960427eac3fbcdbeae1f28 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Thu, 11 Sep 2025 08:59:42 +0200 Subject: spi: Add STM32MP2 Octo-SPI driver support Add STM32 OSPI driver, it supports : - support sNOR / sNAND devices. - Two functional modes: indirect (read/write) and memory-mapped (read). - Single-, dual-, quad-, and octal-SPI communication. - Single data rate (SDR). Signed-off-by: Patrice Chotard Reviewed-by: Patrick Delaunay --- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/stm32_ospi.c | 623 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 632 insertions(+) create mode 100644 drivers/spi/stm32_ospi.c (limited to 'drivers') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 1ae36b5a348..2960822211a 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -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 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index da91b18b6ed..5129d649f84 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -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 diff --git a/drivers/spi/stm32_ospi.c b/drivers/spi/stm32_ospi.c new file mode 100644 index 00000000000..01b8f8e4987 --- /dev/null +++ b/drivers/spi/stm32_ospi.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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, +}; -- cgit v1.2.3 From 63585689686fee83b382edacad249a4ec35d7c3d Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Mon, 8 Sep 2025 16:34:02 +0800 Subject: misc: fs_loader: allow returning actual firmware data size in request_firmware_into_buf_via_script() It's important to return the actual firmware data size as some firmware files may have no checksum and need the size as the only way for firmware validation check. Signed-off-by: Weijie Gao --- drivers/misc/fs_loader.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/fs_loader.c b/drivers/misc/fs_loader.c index 60296d55f23..87acd385e23 100644 --- a/drivers/misc/fs_loader.c +++ b/drivers/misc/fs_loader.c @@ -229,7 +229,8 @@ int request_firmware_into_buf(struct udevice *dev, } int request_firmware_into_buf_via_script(void **buf, size_t max_size, - const char *script_name) + const char *script_name, + size_t *retsize) { ulong addr, size; int ret; @@ -269,6 +270,9 @@ int request_firmware_into_buf_via_script(void **buf, size_t max_size, return -E2BIG; } + if (retsize) + *retsize = size; + memcpy(*buf, (void *)addr, size); return 0; -- cgit v1.2.3 From 5b1d3d83d68a713c492bfad991338d47069f337e Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Mon, 8 Sep 2025 16:34:07 +0800 Subject: misc: fs_loader: allow using long script name in request_firmware_into_buf_via_script() Use cmd_process() to remove the length limit of script name used for run_command(). Signed-off-by: Weijie Gao --- drivers/misc/fs_loader.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/fs_loader.c b/drivers/misc/fs_loader.c index 87acd385e23..6a6796c1931 100644 --- a/drivers/misc/fs_loader.c +++ b/drivers/misc/fs_loader.c @@ -232,18 +232,15 @@ int request_firmware_into_buf_via_script(void **buf, size_t max_size, const char *script_name, size_t *retsize) { + char *args[2] = { (char *)"run", (char *)script_name }; + int ret, repeatable; ulong addr, size; - int ret; - char cmd[32]; if (!buf || !script_name || !max_size) return -EINVAL; - /* Create command to run the firmware loading script */ - snprintf(cmd, sizeof(cmd), "run %s", script_name); - /* Run the firmware loading script */ - ret = run_command_list(cmd, -1, 0); + ret = cmd_process(0, 2, args, &repeatable, NULL); if (ret) { log_err("Firmware loading script '%s' not defined or failed.\n", script_name); -- cgit v1.2.3 From 465d76a0381dae83942f81329400b1fa2487185a Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Mon, 8 Sep 2025 16:34:13 +0800 Subject: net: mediatek: associate PHY device with dts node specified by phy-handle Associate PHY device with its device node specified by phy-handle property. This makes it possible for PHY drivers to read dedicated information to configure the PHY device. Signed-off-by: Weijie Gao --- drivers/net/mtk_eth/mtk_eth.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/mtk_eth/mtk_eth.c b/drivers/net/mtk_eth/mtk_eth.c index b172838ba3a..a35754ccd1f 100644 --- a/drivers/net/mtk_eth/mtk_eth.c +++ b/drivers/net/mtk_eth/mtk_eth.c @@ -100,6 +100,7 @@ struct mtk_eth_priv { bool pn_swap; struct phy_device *phydev; + ofnode phy_node; int phy_interface; int phy_addr; @@ -572,6 +573,7 @@ static int mtk_phy_probe(struct udevice *dev) if (!phydev) return -ENODEV; + phydev->node = priv->phy_node; phydev->supported &= PHY_GBIT_FEATURES; phydev->advertising = phydev->supported; @@ -1458,6 +1460,8 @@ static int mtk_eth_of_to_plat(struct udevice *dev) return ret; } + priv->phy_node = args.node; + priv->phy_addr = ofnode_read_s32_default(args.node, "reg", -1); if (priv->phy_addr < 0) { printf("error: phy address is not specified\n"); -- cgit v1.2.3 From b4b266fc139ad0a8856db6a7495f7e9a2cc27a6f Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Mon, 8 Sep 2025 16:34:18 +0800 Subject: net: phy: Add MediaTek built-in 2.5Gb ethernet PHY driver The MediaTek MT7987/MT7988 SoCs features a built-in 2.5Gb PHY connected to GMAC1. The PHY supports 10/100/1000/2500 Mbps full-duplex only. The PHY requires one or two firmware files. Firmware for MT7988 has already been added to upstream: mediatek/mt7988/i2p5ge-phy-pmb.bin. MT7987 has two firmware files which will be add to upstream later: i2p5ge-phy-pmb.bin and i2p5ge-phy-DSPBitTb.bin. Environment variable can be set for firmware data loading: mt7987_i2p5ge_load_pmb_firmware for i2p5ge-phy-pmb.bin mt7987_i2p5ge_load_dspbit_firmware for i2p5ge-phy-DSPBitTb.bin mt7988_i2p5ge_load_pmb_firmware for i2p5ge-phy-pmb.bin This driver allows dedicated weak functions to be overridden by board to provide the firmware data: mt7987_i2p5ge_get_fw() for MT7987 mt7988_i2p5ge_get_fw() for MT7988 To enable the PHY, add the following not to device tree: ð1 { status = "okay"; phy-mode = "xgmii"; phy-handle = <&phy15>; phy15: ethernet-phy@15 { compatible = "ethernet-phy-ieee802.3-c45"; reg = <15>; phy-mode = "xgmii"; }; }; Signed-off-by: Sky Huang Signed-off-by: Weijie Gao --- drivers/misc/fs_loader.c | 2 +- drivers/net/phy/Kconfig | 2 + drivers/net/phy/Makefile | 1 + drivers/net/phy/mediatek/Kconfig | 16 + drivers/net/phy/mediatek/Makefile | 4 + drivers/net/phy/mediatek/mtk-2p5ge.c | 627 +++++++++++++++++++++++++++++++++ drivers/net/phy/mediatek/mtk-phy-lib.c | 106 ++++++ drivers/net/phy/mediatek/mtk.h | 103 ++++++ 8 files changed, 860 insertions(+), 1 deletion(-) create mode 100644 drivers/net/phy/mediatek/Kconfig create mode 100644 drivers/net/phy/mediatek/Makefile create mode 100644 drivers/net/phy/mediatek/mtk-2p5ge.c create mode 100644 drivers/net/phy/mediatek/mtk-phy-lib.c create mode 100644 drivers/net/phy/mediatek/mtk.h (limited to 'drivers') diff --git a/drivers/misc/fs_loader.c b/drivers/misc/fs_loader.c index 6a6796c1931..c6c633f7c52 100644 --- a/drivers/misc/fs_loader.c +++ b/drivers/misc/fs_loader.c @@ -232,7 +232,7 @@ int request_firmware_into_buf_via_script(void **buf, size_t max_size, const char *script_name, size_t *retsize) { - char *args[2] = { (char *)"run", (char *)script_name }; + char *args[2] = { "run", (char *)script_name }; int ret, repeatable; ulong addr, size; diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 21bf983056a..185c6a3156e 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -184,6 +184,8 @@ config PHY_MARVELL_10G help Support for the Marvell Alaska MV88X3310 and compatible PHYs. +source "drivers/net/phy/mediatek/Kconfig" + config PHY_MESON_GXL bool "Amlogic Meson GXL Internal PHY support" diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index a119eb5e177..a339b8ac29d 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_PHY_ET1011C) += et1011c.o obj-$(CONFIG_PHY_LXT) += lxt.o obj-$(CONFIG_PHY_MARVELL) += marvell.o obj-$(CONFIG_PHY_MARVELL_10G) += marvell10g.o +obj-y += mediatek/ obj-$(CONFIG_PHY_MICREL_KSZ8XXX) += micrel_ksz8xxx.o obj-$(CONFIG_PHY_MICREL_KSZ90X1) += micrel_ksz90x1.o obj-$(CONFIG_PHY_MESON_GXL) += meson-gxl.o diff --git a/drivers/net/phy/mediatek/Kconfig b/drivers/net/phy/mediatek/Kconfig new file mode 100644 index 00000000000..7de7b65b4e6 --- /dev/null +++ b/drivers/net/phy/mediatek/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config MTK_NET_PHYLIB + tristate + +config PHY_MEDIATEK_2P5GE + tristate "MediaTek built-in 2.5Gb ethernet PHYs" + depends on OF_CONTROL && (TARGET_MT7987 || TARGET_MT7988) + select MTK_NET_PHYLIB + select FS_LOADER + help + Supports MediaTek SoC built-in 2.5Gb ethernet PHYs. + + This driver requires firmware download for PHY to enable its + functionality. The board can override certian firmware downloading + function to provide the firmware data. diff --git a/drivers/net/phy/mediatek/Makefile b/drivers/net/phy/mediatek/Makefile new file mode 100644 index 00000000000..bc8dd4e878c --- /dev/null +++ b/drivers/net/phy/mediatek/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_MTK_NET_PHYLIB) += mtk-phy-lib.o +obj-$(CONFIG_PHY_MEDIATEK_2P5GE) += mtk-2p5ge.o diff --git a/drivers/net/phy/mediatek/mtk-2p5ge.c b/drivers/net/phy/mediatek/mtk-2p5ge.c new file mode 100644 index 00000000000..ab5007389a9 --- /dev/null +++ b/drivers/net/phy/mediatek/mtk-2p5ge.c @@ -0,0 +1,627 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 MediaTek Inc. All Rights Reserved. + * + * Author: Sky Huang + * Author: Weijie Gao + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mtk.h" + +#define MTK_2P5GPHY_ID_MT7987 0x00339c91 +#define MTK_2P5GPHY_ID_MT7988 0x00339c11 + +#define PBUS_BASE 0x0f000000 +#define PBUS_SIZE 0x1f0024 + +#define MTK_2P5GPHY_PMD_REG 0x010000 +#define DO_NOT_RESET (MTK_2P5GPHY_PMD_REG + 0x28) +#define DO_NOT_RESET_XBZ BIT(0) +#define DO_NOT_RESET_PMA BIT(3) +#define DO_NOT_RESET_RX BIT(5) +#define FNPLL_PWR_CTRL1 (MTK_2P5GPHY_PMD_REG + 0x208) +#define RG_SPEED_MASK GENMASK(3, 0) +#define RG_SPEED_2500 BIT(3) +#define RG_SPEED_100 BIT(0) +#define FNPLL_PWR_CTRL_STATUS (MTK_2P5GPHY_PMD_REG + 0x20c) +#define RG_STABLE_MASK GENMASK(3, 0) +#define RG_SPEED_2500_STABLE BIT(3) +#define RG_SPEED_100_STABLE BIT(0) + +#define MTK_2P5GPHY_XBZ_PCS 0x030000 +#define PHY_CTRL_CONFIG (MTK_2P5GPHY_XBZ_PCS + 0x200) +#define PMU_WP (MTK_2P5GPHY_XBZ_PCS + 0x800) +#define WRITE_PROTECT_KEY 0xCAFEF00D +#define PMU_PMA_AUTO_CFG (MTK_2P5GPHY_XBZ_PCS + 0x820) +#define POWER_ON_AUTO_MODE BIT(16) +#define PMU_AUTO_MODE_EN BIT(0) +#define PMU_PMA_STATUS (MTK_2P5GPHY_XBZ_PCS + 0x840) +#define CLK_IS_DISABLED BIT(3) + +#define MTK_2P5GPHY_XBZ_PMA_RX 0x080000 +#define SMEM_WDAT0 (MTK_2P5GPHY_XBZ_PMA_RX + 0x5000) +#define SMEM_WDAT1 (MTK_2P5GPHY_XBZ_PMA_RX + 0x5004) +#define SMEM_WDAT2 (MTK_2P5GPHY_XBZ_PMA_RX + 0x5008) +#define SMEM_WDAT3 (MTK_2P5GPHY_XBZ_PMA_RX + 0x500c) +#define SMEM_CTRL (MTK_2P5GPHY_XBZ_PMA_RX + 0x5024) +#define SMEM_HW_RDATA_ZERO BIT(24) +#define SMEM_ADDR_REF_ADDR (MTK_2P5GPHY_XBZ_PMA_RX + 0x502c) +#define CM_CTRL_P01 (MTK_2P5GPHY_XBZ_PMA_RX + 0x5100) +#define CM_CTRL_P23 (MTK_2P5GPHY_XBZ_PMA_RX + 0x5124) +#define DM_CTRL_P01 (MTK_2P5GPHY_XBZ_PMA_RX + 0x5200) +#define DM_CTRL_P23 (MTK_2P5GPHY_XBZ_PMA_RX + 0x5224) + +#define MTK_2P5GPHY_CHIP_SCU 0x0cf800 +#define SYS_SW_RESET (MTK_2P5GPHY_CHIP_SCU + 0x128) +#define RESET_RST_CNT BIT(0) + +#define MTK_2P5GPHY_MCU_CSR 0x0f0000 +#define MD32_EN_CFG (MTK_2P5GPHY_MCU_CSR + 0x18) +#define MD32_EN BIT(0) + +#define MTK_2P5GPHY_PMB_FW 0x100000 + +#define MTK_2P5GPHY_FCM_BASE 0x0e0000 +#define FC_LWM (MTK_2P5GPHY_FCM_BASE + 0x14) +#define TX_FC_LWM_MASK GENMASK(31, 16) +#define MIN_IPG_NUM (MTK_2P5GPHY_FCM_BASE + 0x2c) +#define LS_MIN_IPG_NUM_MASK GENMASK(7, 0) +#define FIFO_CTRL (MTK_2P5GPHY_FCM_BASE + 0x40) +#define TX_SFIFO_IDLE_CNT_MASK GENMASK(31, 28) +#define TX_SFIFO_DEL_IPG_WM_MASK GENMASK(23, 16) + +#define MTK_2P5GPHY_APB_BASE 0x11c30000 +#define MTK_2P5GPHY_APB_SIZE 0x9c +#define SW_RESET 0x94 +#define MD32_RESTART_EN_CLEAR BIT(9) + +/* Registers on CL22 page 0 */ +#define PHY_AUX_CTRL_STATUS 0x1d +#define PHY_AUX_DPX_MASK GENMASK(5, 5) +#define PHY_AUX_SPEED_MASK GENMASK(4, 2) + +enum { + PHY_AUX_SPD_10 = 0, + PHY_AUX_SPD_100, + PHY_AUX_SPD_1000, + PHY_AUX_SPD_2500, +}; + +/* Registers on MDIO_MMD_VEND1 */ +#define MTK_PHY_LINK_STATUS_RELATED 0x147 +#define MTK_PHY_BYPASS_LINK_STATUS_OK BIT(4) +#define MTK_PHY_FORCE_LINK_STATUS_HCD BIT(3) + +#define MTK_PHY_AN_FORCE_SPEED_REG 0x313 +#define MTK_PHY_MASTER_FORCE_SPEED_SEL_EN BIT(7) +#define MTK_PHY_MASTER_FORCE_SPEED_SEL_MASK GENMASK(6, 0) + +#define MTK_PHY_LPI_PCS_DSP_CTRL 0x121 +#define MTK_PHY_LPI_SIG_EN_LO_THRESH100_MASK GENMASK(12, 8) + +#define MTK_PHY_PMA_PMD_SPEED_ABILITY 0x300 +#define CAP_100X_HDX BIT(14) +#define CAP_10T_HDX BIT(12) + +/* Registers on MDIO_MMD_VEND2 */ +#define MT7987_OPTIONS 0x110 +#define NORMAL_RETRAIN_DISABLE BIT(0) + +/* Registers on Token Ring debug nodes */ +/* ch_addr = 0x0, node_addr = 0xf, data_addr = 0x3c */ +#define AUTO_NP_10XEN BIT(6) + +/* Firmware file size */ +#define MT7987_2P5GE_PMB_FW_SIZE 0x18000 +#define MT7988_2P5GE_PMB_FW_SIZE 0x20000 + +#define MT7987_2P5GE_DSPBITTB_SIZE 0x7000 + +struct mtk_i2p5ge_fw_info { + u8 datecode[4]; + u8 plat[2]; + u8 ver[2]; +}; + +struct mtk_i2p5ge_priv { + void __iomem *reg_base; +}; + +static inline void pbus_write(struct mtk_i2p5ge_priv *priv, u32 offset, + u32 val) +{ + writel(val, priv->reg_base + offset); +} + +static inline void pbus_rmw(struct mtk_i2p5ge_priv *priv, u32 offset, u32 clr, + u32 set) +{ + clrsetbits_le32(priv->reg_base + offset, clr, set); +} + +static int mt798x_i2p5ge_download_fw(struct mtk_i2p5ge_priv *priv, + size_t fwsize, const void *fwdata) +{ + u32 __iomem *fwmem = priv->reg_base + MTK_2P5GPHY_PMB_FW; + const u32 *fw; + u32 i; + + /* Assume fw data is 4-byte aligned */ + fw = fwdata; + + for (i = 0; i < (fwsize >> 2); i++) + writel(fw[i], &fwmem[i]); + + return 0; +} + +static int mt798x_i2p5ge_phy_probe(struct phy_device *phydev) +{ + struct mtk_i2p5ge_priv *priv; + + priv = malloc(sizeof(*priv)); + if (!priv) + return -ENOMEM; + + priv->reg_base = ioremap(PBUS_BASE, PBUS_SIZE); + if (!priv->reg_base) { + free(priv); + return -ENODEV; + } + + phydev->priv = priv; + + return 0; +} + +static int mt798x_i2p5ge_phy_config(struct phy_device *phydev) +{ + + /* Setup LED */ + phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, + MTK_PHY_LED_ON_LINK10 | + MTK_PHY_LED_ON_LINK100 | + MTK_PHY_LED_ON_LINK1000 | + MTK_PHY_LED_ON_LINK2500); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_ON_CTRL, + MTK_PHY_LED_ON_FDX | MTK_PHY_LED_ON_HDX); + + /* Switch pinctrl after setting polarity to avoid bogus blinking */ + pinctrl_select_state(phydev->dev, "i2p5gbe-led"); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LPI_PCS_DSP_CTRL, + MTK_PHY_LPI_SIG_EN_LO_THRESH100_MASK, 0); + + /* Enable 16-bit next page exchange bit if 1000-BT isn't advertising */ + mtk_tr_modify(phydev, 0x0, 0xf, 0x3c, AUTO_NP_10XEN, + FIELD_PREP(AUTO_NP_10XEN, 0x1)); + + /* Set HW auto downshift */ + mtk_phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_1); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_AUX_CTRL_AND_STATUS, + MTK_PHY_ENABLE_DOWNSHIFT); + mtk_phy_restore_page(phydev); + + /* Configure parallel detction functionality */ + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_PMA_PMD_SPEED_ABILITY, + CAP_10T_HDX | CAP_100X_HDX); + phy_clear_bits_mmd(phydev, MDIO_DEVAD_NONE, + MII_ADVERTISE, + ADVERTISE_10HALF | ADVERTISE_100HALF); + + return 0; +} + +static void mt798x_i2p5ge_print_fw_info(const void *fwdata, size_t fwsize) +{ + const struct mtk_i2p5ge_fw_info *info; + u8 ver_minor, ver_patch; + + info = (const void *)((uintptr_t)fwdata + fwsize - sizeof(*info)); + + ver_minor = info->ver[1] >> 4; + ver_patch = info->ver[1] & 0xf; + + printf("Firmware loaded, date %02x%02x/%02x/%02x, ver %x.%u.%u\n", + info->datecode[0], info->datecode[1], info->datecode[2], + info->datecode[3], info->ver[0], ver_minor, ver_patch); +} + +int __weak mt7987_i2p5ge_get_fw(void **fw, size_t *fwsize, + void **dspfw, size_t *dspfwsize) +{ + void *pmb, *dsp; + int ret; + + pmb = malloc(MT7987_2P5GE_PMB_FW_SIZE); + if (!pmb) + return -ENOMEM; + + ret = request_firmware_into_buf_via_script( + &pmb, MT7987_2P5GE_PMB_FW_SIZE, + "mt7987_i2p5ge_load_pmb_firmware", fwsize); + if (ret) { + free(pmb); + return ret; + } + + dsp = malloc(MT7987_2P5GE_DSPBITTB_SIZE); + if (!dsp) { + free(pmb); + return -ENOMEM; + } + + ret = request_firmware_into_buf_via_script( + &dsp, MT7987_2P5GE_DSPBITTB_SIZE, + "mt7987_i2p5ge_load_dspbit_firmware", dspfwsize); + if (ret) { + free(pmb); + free(dsp); + return ret; + } + + *fw = pmb; + *dspfw = dsp; + + return 1; +} + +static int mt7987_i2p5ge_download_dspfw(struct mtk_i2p5ge_priv *priv, + const void *dspfw_data) +{ + const u32 *dspfw; + u32 i; + + pbus_rmw(priv, SMEM_CTRL, 0, SMEM_HW_RDATA_ZERO); + + pbus_rmw(priv, PHY_CTRL_CONFIG, 0, BIT(16)); + + /* Initialize data memory */ + pbus_rmw(priv, DM_CTRL_P01, 0, BIT(28)); + pbus_rmw(priv, DM_CTRL_P23, 0, BIT(28)); + + /* Initialize coefficient memory */ + pbus_rmw(priv, CM_CTRL_P01, 0, BIT(28)); + pbus_rmw(priv, CM_CTRL_P23, 0, BIT(28)); + + /* Initialize PM offset */ + pbus_write(priv, SMEM_ADDR_REF_ADDR, 0); + + /* Assume DSP bit data is 4-byte aligned */ + dspfw = dspfw_data; + + for (i = 0; i < (MT7987_2P5GE_DSPBITTB_SIZE >> 2); i += 4) { + pbus_write(priv, SMEM_WDAT0, dspfw[i]); + pbus_write(priv, SMEM_WDAT1, dspfw[i + 1]); + pbus_write(priv, SMEM_WDAT2, dspfw[i + 2]); + pbus_write(priv, SMEM_WDAT3, dspfw[i + 3]); + } + + pbus_rmw(priv, DM_CTRL_P01, BIT(28), 0); + pbus_rmw(priv, DM_CTRL_P23, BIT(28), 0); + + pbus_rmw(priv, CM_CTRL_P01, BIT(28), 0); + pbus_rmw(priv, CM_CTRL_P23, BIT(28), 0); + + return 0; +} + +static int mt7987_i2p5ge_phy_load_fw(struct phy_device *phydev) +{ + struct mtk_i2p5ge_priv *priv = phydev->priv; + size_t fw_size, dspfw_size; + void __iomem *apb_base; + void *fw, *dspfw; + int ret, fwrc; + u32 reg; + + apb_base = ioremap(MTK_2P5GPHY_APB_BASE, MTK_2P5GPHY_APB_SIZE); + if (!apb_base) + return -ENODEV; + + fwrc = mt7987_i2p5ge_get_fw(&fw, &fw_size, &dspfw, &dspfw_size); + if (fwrc < 0) { + dev_err(phydev->dev, "Failed to get firmware data\n"); + return -EINVAL; + } + + if (fw_size != MT7987_2P5GE_PMB_FW_SIZE) { + dev_err(phydev->dev, + "PMB firmware size mismatch (0x%zx != 0x%x)\n", + fw_size, MT7987_2P5GE_PMB_FW_SIZE); + ret = -EINVAL; + goto cleanup; + } + + if (dspfw_size != MT7987_2P5GE_DSPBITTB_SIZE) { + dev_err(phydev->dev, + "DSP code size mismatch (0x%zx != 0x%x)\n", + dspfw_size, MT7987_2P5GE_DSPBITTB_SIZE); + ret = -EINVAL; + goto cleanup; + } + + /* Force 2.5Gphy back to AN state */ + phy_set_bits_mmd(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); + mdelay(5); + phy_set_bits_mmd(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_PDOWN); + + clrbits_le16(apb_base + SW_RESET, MD32_RESTART_EN_CLEAR); + setbits_le16(apb_base + SW_RESET, MD32_RESTART_EN_CLEAR); + clrbits_le16(apb_base + SW_RESET, MD32_RESTART_EN_CLEAR); + + pbus_rmw(priv, MD32_EN_CFG, MD32_EN, 0); + + ret = mt798x_i2p5ge_download_fw(priv, MT7987_2P5GE_PMB_FW_SIZE, fw); + if (ret) + goto cleanup; + + /* Enable 100Mbps module clock. */ + pbus_rmw(priv, FNPLL_PWR_CTRL1, RG_SPEED_MASK, RG_SPEED_100); + + /* Check if 100Mbps module clock is ready. */ + ret = readl_poll_timeout(priv->reg_base + FNPLL_PWR_CTRL_STATUS, reg, + reg & RG_SPEED_100_STABLE, 10000); + if (ret) { + dev_err(phydev->dev, + "Timed out enabling 100Mbps module clock\n"); + } + + /* Enable 2.5Gbps module clock. */ + pbus_rmw(priv, FNPLL_PWR_CTRL1, RG_SPEED_MASK, RG_SPEED_2500); + + /* Check if 2.5Gbps module clock is ready. */ + ret = readl_poll_timeout(priv->reg_base + FNPLL_PWR_CTRL_STATUS, reg, + reg & RG_SPEED_2500_STABLE, 10000); + + if (ret) { + dev_err(phydev->dev, + "Timed out enabling 2.5Gbps module clock\n"); + } + + /* Disable AN */ + phy_clear_bits_mmd(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_ANENABLE); + + /* Force to run at 2.5G speed */ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_AN_FORCE_SPEED_REG, + MTK_PHY_MASTER_FORCE_SPEED_SEL_MASK, + MTK_PHY_MASTER_FORCE_SPEED_SEL_EN | + FIELD_PREP(MTK_PHY_MASTER_FORCE_SPEED_SEL_MASK, 0x1b)); + + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LINK_STATUS_RELATED, + MTK_PHY_BYPASS_LINK_STATUS_OK | + MTK_PHY_FORCE_LINK_STATUS_HCD); + + /* Set xbz, pma and rx as "do not reset" in order to input DSP code. */ + pbus_rmw(priv, DO_NOT_RESET, 0, + DO_NOT_RESET_XBZ | DO_NOT_RESET_PMA | DO_NOT_RESET_RX); + + pbus_rmw(priv, SYS_SW_RESET, RESET_RST_CNT, 0); + + pbus_write(priv, PMU_WP, WRITE_PROTECT_KEY); + + pbus_rmw(priv, PMU_PMA_AUTO_CFG, 0, + PMU_AUTO_MODE_EN | POWER_ON_AUTO_MODE); + + /* Check if clock in auto mode is disabled. */ + ret = readl_poll_timeout(priv->reg_base + PMU_PMA_STATUS, reg, + (reg & CLK_IS_DISABLED) == 0x0, 100000); + if (ret) + dev_err(phydev->dev, "Timed out enabling clock auto mode\n"); + + ret = mt7987_i2p5ge_download_dspfw(priv, dspfw); + if (ret) + goto cleanup; + + pbus_rmw(priv, MD32_EN_CFG, 0, MD32_EN); + + phy_set_bits_mmd(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); + + /* We need a delay here to stabilize initialization of MCU */ + mdelay(8); + + mt798x_i2p5ge_print_fw_info(fw, fw_size); + +cleanup: + if (fwrc > 0) { + free(fw); + free(dspfw); + } + + iounmap(apb_base); + + return ret; +} + +static int mt7987_i2p5ge_phy_config(struct phy_device *phydev) +{ + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, + MTK_PHY_LED_ON_POLARITY); + + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MT7987_OPTIONS, + NORMAL_RETRAIN_DISABLE); + + return mt798x_i2p5ge_phy_config(phydev); +} + +static int mt7987_i2p5ge_phy_probe(struct phy_device *phydev) +{ + int ret; + + ret = mt798x_i2p5ge_phy_probe(phydev); + if (ret) + return ret; + + return mt7987_i2p5ge_phy_load_fw(phydev); +} + +int __weak mt7988_i2p5ge_get_fw(void **fw, size_t *size) +{ + void *pmb; + int ret; + + pmb = malloc(MT7988_2P5GE_PMB_FW_SIZE); + if (!pmb) + return -ENOMEM; + + ret = request_firmware_into_buf_via_script( + &pmb, MT7988_2P5GE_PMB_FW_SIZE, + "mt7988_i2p5ge_load_pmb_firmware", size); + if (ret) { + free(pmb); + return ret; + } + + *fw = pmb; + + return 1; +} + +static int mt7988_i2p5ge_phy_load_fw(struct phy_device *phydev) +{ + struct mtk_i2p5ge_priv *priv = phydev->priv; + size_t fw_size; + int ret, fwrc; + void *fw; + + fwrc = mt7988_i2p5ge_get_fw(&fw, &fw_size); + if (fwrc < 0) { + dev_err(phydev->dev, "Failed to get firmware data\n"); + return -EINVAL; + } + + if (fw_size != MT7988_2P5GE_PMB_FW_SIZE) { + dev_err(phydev->dev, "Firmware size mismatch (0x%zx != 0x%x)\n", + fw_size, MT7988_2P5GE_PMB_FW_SIZE); + ret = -EINVAL; + goto cleanup; + } + + ret = mt798x_i2p5ge_download_fw(priv, MT7988_2P5GE_PMB_FW_SIZE, fw); + if (ret) + goto cleanup; + + pbus_rmw(priv, MD32_EN_CFG, MD32_EN, 0); + pbus_rmw(priv, MD32_EN_CFG, 0, MD32_EN); + + phy_set_bits_mmd(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); + + /* We need a delay here to stabilize initialization of MCU */ + mdelay(8); + + mt798x_i2p5ge_print_fw_info(fw, fw_size); + +cleanup: + if (fwrc > 0) + free(fw); + + return ret; +} + +static int mt7988_i2p5ge_phy_config(struct phy_device *phydev) +{ + phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, + MTK_PHY_LED_ON_POLARITY); + + return mt798x_i2p5ge_phy_config(phydev); +} + +static int mt7988_i2p5ge_phy_probe(struct phy_device *phydev) +{ + int ret; + + ret = mt798x_i2p5ge_phy_probe(phydev); + if (ret) + return ret; + + return mt7988_i2p5ge_phy_load_fw(phydev); +} + +static int mt798x_i2p5ge_phy_startup(struct phy_device *phydev) +{ + struct mtk_i2p5ge_priv *priv = phydev->priv; + int ret; + + ret = genphy_update_link(phydev); + if (ret) + return ret; + + /* Initialize speed/duplex */ + phydev->speed = SPEED_10; + phydev->duplex = DUPLEX_HALF; + + if (phydev->link) { + ret = phy_read(phydev, MDIO_DEVAD_NONE, PHY_AUX_CTRL_STATUS); + if (ret < 0) + return ret; + + switch (FIELD_GET(PHY_AUX_SPEED_MASK, ret)) { + case PHY_AUX_SPD_10: + phydev->speed = SPEED_10; + break; + + case PHY_AUX_SPD_100: + phydev->speed = SPEED_100; + break; + + case PHY_AUX_SPD_1000: + pbus_rmw(priv, FIFO_CTRL, + TX_SFIFO_IDLE_CNT_MASK | TX_SFIFO_DEL_IPG_WM_MASK, + FIELD_PREP(TX_SFIFO_IDLE_CNT_MASK, 0x1) | + FIELD_PREP(TX_SFIFO_DEL_IPG_WM_MASK, 0x10)); + pbus_rmw(priv, MIN_IPG_NUM, LS_MIN_IPG_NUM_MASK, + FIELD_PREP(LS_MIN_IPG_NUM_MASK, 0xa)); + pbus_rmw(priv, FC_LWM, TX_FC_LWM_MASK, + FIELD_PREP(TX_FC_LWM_MASK, 0x340)); + phydev->speed = SPEED_1000; + break; + + case PHY_AUX_SPD_2500: + phydev->speed = SPEED_2500; + break; + + default: + break; + } + + /* This PHY always operates in full duplex */ + phydev->duplex = DUPLEX_FULL; + } + + return 0; +} + +U_BOOT_PHY_DRIVER(mt7987_i2p5ge) = { + .name = "MediaTek MT7987 built-in 2.5GbE PHY", + .uid = MTK_2P5GPHY_ID_MT7987, + .mask = 0xfffffff0, + .features = PHY_10G_FEATURES, + .mmds = (MDIO_MMD_PCS | MDIO_MMD_AN | MDIO_MMD_VEND1 | MDIO_MMD_VEND2), + .probe = &mt7987_i2p5ge_phy_probe, + .config = &mt7987_i2p5ge_phy_config, + .startup = &mt798x_i2p5ge_phy_startup, + .shutdown = &genphy_shutdown, +}; + +U_BOOT_PHY_DRIVER(mt7988_i2p5ge) = { + .name = "MediaTek MT7988 built-in 2.5GbE PHY", + .uid = MTK_2P5GPHY_ID_MT7988, + .mask = 0xfffffff0, + .features = PHY_10G_FEATURES, + .mmds = (MDIO_MMD_PCS | MDIO_MMD_AN | MDIO_MMD_VEND1 | MDIO_MMD_VEND2), + .probe = &mt7988_i2p5ge_phy_probe, + .config = &mt7988_i2p5ge_phy_config, + .startup = &mt798x_i2p5ge_phy_startup, + .shutdown = &genphy_shutdown, +}; diff --git a/drivers/net/phy/mediatek/mtk-phy-lib.c b/drivers/net/phy/mediatek/mtk-phy-lib.c new file mode 100644 index 00000000000..55e7a6b6eec --- /dev/null +++ b/drivers/net/phy/mediatek/mtk-phy-lib.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 MediaTek Inc. All Rights Reserved. + * + * Author: Sky Huang + */ +#include +#include + +#include "mtk.h" + +void mtk_phy_select_page(struct phy_device *phydev, int page) +{ + phy_write(phydev, MDIO_DEVAD_NONE, MTK_EXT_PAGE_ACCESS, page); +} + +void mtk_phy_restore_page(struct phy_device *phydev) +{ + phy_write(phydev, MDIO_DEVAD_NONE, MTK_EXT_PAGE_ACCESS, + MTK_PHY_PAGE_STANDARD); +} + +/* Difference between functions with mtk_tr* and __mtk_tr* prefixes is + * mtk_tr* functions: wrapped by page switching operations + * __mtk_tr* functions: no page switching operations + */ +static void __mtk_tr_access(struct phy_device *phydev, bool read, u8 ch_addr, + u8 node_addr, u8 data_addr) +{ + u16 tr_cmd = BIT(15); /* bit 14 & 0 are reserved */ + + if (read) + tr_cmd |= BIT(13); + + tr_cmd |= (((ch_addr & 0x3) << 11) | + ((node_addr & 0xf) << 7) | + ((data_addr & 0x3f) << 1)); + dev_dbg(phydev->dev, "tr_cmd: 0x%x\n", tr_cmd); + phy_write(phydev, MDIO_DEVAD_NONE, 0x10, tr_cmd); +} + +static void __mtk_tr_read(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u16 *tr_high, u16 *tr_low) +{ + __mtk_tr_access(phydev, true, ch_addr, node_addr, data_addr); + *tr_low = phy_read(phydev, MDIO_DEVAD_NONE, 0x11); + *tr_high = phy_read(phydev, MDIO_DEVAD_NONE, 0x12); + dev_dbg(phydev->dev, "tr_high read: 0x%x, tr_low read: 0x%x\n", + *tr_high, *tr_low); +} + +u32 mtk_tr_read(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr) +{ + u16 tr_high; + u16 tr_low; + + mtk_phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); + __mtk_tr_read(phydev, ch_addr, node_addr, data_addr, &tr_high, &tr_low); + mtk_phy_restore_page(phydev); + + return (tr_high << 16) | tr_low; +} + +static void __mtk_tr_write(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 tr_data) +{ + phy_write(phydev, MDIO_DEVAD_NONE, 0x11, tr_data & 0xffff); + phy_write(phydev, MDIO_DEVAD_NONE, 0x12, tr_data >> 16); + dev_dbg(phydev->dev, "tr_high write: 0x%x, tr_low write: 0x%x\n", + tr_data >> 16, tr_data & 0xffff); + __mtk_tr_access(phydev, false, ch_addr, node_addr, data_addr); +} + +void __mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 mask, u32 set) +{ + u32 tr_data; + u16 tr_high; + u16 tr_low; + + __mtk_tr_read(phydev, ch_addr, node_addr, data_addr, &tr_high, &tr_low); + tr_data = (tr_high << 16) | tr_low; + tr_data = (tr_data & ~mask) | set; + __mtk_tr_write(phydev, ch_addr, node_addr, data_addr, tr_data); +} + +void mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 mask, u32 set) +{ + mtk_phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); + __mtk_tr_modify(phydev, ch_addr, node_addr, data_addr, mask, set); + mtk_phy_restore_page(phydev); +} + +void __mtk_tr_set_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 set) +{ + __mtk_tr_modify(phydev, ch_addr, node_addr, data_addr, 0, set); +} + +void __mtk_tr_clr_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 clr) +{ + __mtk_tr_modify(phydev, ch_addr, node_addr, data_addr, clr, 0); +} diff --git a/drivers/net/phy/mediatek/mtk.h b/drivers/net/phy/mediatek/mtk.h new file mode 100644 index 00000000000..68044cbdb32 --- /dev/null +++ b/drivers/net/phy/mediatek/mtk.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025 MediaTek Inc. All Rights Reserved. + * + * Author: Sky Huang + * + * Common definition for Mediatek Ethernet PHYs + */ + +#ifndef _MTK_EPHY_H_ +#define _MTK_EPHY_H_ + +#define MTK_EXT_PAGE_ACCESS 0x1f +#define MTK_PHY_PAGE_STANDARD 0x0000 +#define MTK_PHY_PAGE_EXTENDED_1 0x0001 +#define MTK_PHY_AUX_CTRL_AND_STATUS 0x14 +/* suprv_media_select_RefClk */ +#define MTK_PHY_LP_DETECTED_MASK GENMASK(7, 6) +#define MTK_PHY_ENABLE_DOWNSHIFT BIT(4) + +#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 + +/* Registers on Token Ring debug nodes */ +/* ch_addr = 0x0, node_addr = 0xf, data_addr = 0x2 */ +#define AN_STATE_MASK GENMASK(22, 19) +#define AN_STATE_SHIFT 19 +#define AN_STATE_TX_DISABLE 1 + +/* ch_addr = 0x0, node_addr = 0xf, data_addr = 0x3c */ +#define AN_NEW_LP_CNT_LIMIT_MASK GENMASK(23, 20) +#define AUTO_NP_10XEN BIT(6) + +/* Registers on MDIO_MMD_VEND1 */ +#define MTK_PHY_LINK_STATUS_MISC (0xa2) +#define MTK_PHY_FINAL_SPEED_1000 BIT(3) + +/* Registers on MDIO_MMD_VEND2 */ +#define MTK_PHY_LED0_ON_CTRL 0x24 +#define MTK_PHY_LED1_ON_CTRL 0x26 +#define MTK_GPHY_LED_ON_MASK GENMASK(6, 0) +#define MTK_2P5GPHY_LED_ON_MASK GENMASK(7, 0) +#define MTK_PHY_LED_ON_LINK1000 BIT(0) +#define MTK_PHY_LED_ON_LINK100 BIT(1) +#define MTK_PHY_LED_ON_LINK10 BIT(2) +#define MTK_PHY_LED_ON_LINKDOWN BIT(3) +#define MTK_PHY_LED_ON_FDX BIT(4) /* Full duplex */ +#define MTK_PHY_LED_ON_HDX BIT(5) /* Half duplex */ +#define MTK_PHY_LED_ON_FORCE_ON BIT(6) +#define MTK_PHY_LED_ON_LINK2500 BIT(7) +#define MTK_PHY_LED_ON_POLARITY BIT(14) +#define MTK_PHY_LED_ON_ENABLE BIT(15) + +#define MTK_PHY_LED0_BLINK_CTRL 0x25 +#define MTK_PHY_LED1_BLINK_CTRL 0x27 +#define MTK_PHY_LED_BLINK_1000TX BIT(0) +#define MTK_PHY_LED_BLINK_1000RX BIT(1) +#define MTK_PHY_LED_BLINK_100TX BIT(2) +#define MTK_PHY_LED_BLINK_100RX BIT(3) +#define MTK_PHY_LED_BLINK_10TX BIT(4) +#define MTK_PHY_LED_BLINK_10RX BIT(5) +#define MTK_PHY_LED_BLINK_COLLISION BIT(6) +#define MTK_PHY_LED_BLINK_RX_CRC_ERR BIT(7) +#define MTK_PHY_LED_BLINK_RX_IDLE_ERR BIT(8) +#define MTK_PHY_LED_BLINK_FORCE_BLINK BIT(9) +#define MTK_PHY_LED_BLINK_2500TX BIT(10) +#define MTK_PHY_LED_BLINK_2500RX BIT(11) + +#define MTK_GPHY_LED_ON_SET (MTK_PHY_LED_ON_LINK1000 | \ + MTK_PHY_LED_ON_LINK100 | \ + MTK_PHY_LED_ON_LINK10) +#define MTK_GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \ + MTK_PHY_LED_BLINK_100RX | \ + MTK_PHY_LED_BLINK_10RX) +#define MTK_GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \ + MTK_PHY_LED_BLINK_100RX | \ + MTK_PHY_LED_BLINK_10RX) + +#define MTK_2P5GPHY_LED_ON_SET (MTK_PHY_LED_ON_LINK2500 | \ + MTK_GPHY_LED_ON_SET) +#define MTK_2P5GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \ + MTK_GPHY_LED_RX_BLINK_SET) +#define MTK_2P5GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \ + MTK_GPHY_LED_TX_BLINK_SET) + +#define MTK_PHY_LED_STATE_FORCE_ON 0 +#define MTK_PHY_LED_STATE_FORCE_BLINK 1 +#define MTK_PHY_LED_STATE_NETDEV 2 + +void mtk_phy_select_page(struct phy_device *phydev, int page); +void mtk_phy_restore_page(struct phy_device *phydev); + +u32 mtk_tr_read(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr); +void __mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 mask, u32 set); +void mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 mask, u32 set); +void __mtk_tr_set_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 set); +void __mtk_tr_clr_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 clr); + +#endif /* _MTK_EPHY_H_ */ -- cgit v1.2.3 From fbcf53680be59fd8144f865f8cfd840319cb5c0d Mon Sep 17 00:00:00 2001 From: Yao Zi Date: Wed, 13 Aug 2025 10:09:25 +0000 Subject: clk: thead: th1520-ap: Mark drivers as DM_FLAG_PRE_RELOC It's common that UARTs are bound and probed before U-Boot relocation, in which case the clocks of UART and UART's pincontroller must be registered first. Let's apply DM_FLAG_PRE_RELOC to the driver, allowing it to bind before relocation. Signed-off-by: Yao Zi Reviewed-by: Leo Yu-Chi Liang --- drivers/clk/thead/clk-th1520-ap.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/thead/clk-th1520-ap.c b/drivers/clk/thead/clk-th1520-ap.c index 822cf0809d5..6899e1b595a 100644 --- a/drivers/clk/thead/clk-th1520-ap.c +++ b/drivers/clk/thead/clk-th1520-ap.c @@ -235,6 +235,7 @@ U_BOOT_DRIVER(th1520_clk_div) = { .name = "th1520_clk_div", .id = UCLASS_CLK, .ops = &ccu_div_ops, + .flags = DM_FLAG_PRE_RELOC, }; static unsigned long th1520_pll_vco_recalc_rate(struct clk *clk, @@ -302,6 +303,7 @@ U_BOOT_DRIVER(th1520_clk_pll) = { .name = "th1520_clk_pll", .id = UCLASS_CLK, .ops = &clk_pll_ops, + .flags = DM_FLAG_PRE_RELOC, }; static struct ccu_pll cpu_pll0_clk = { @@ -1030,4 +1032,5 @@ U_BOOT_DRIVER(th1520_clk) = { .of_match = th1520_clk_match, .probe = th1520_clk_probe, .ops = &th1520_clk_ops, + .flags = DM_FLAG_PRE_RELOC, }; -- cgit v1.2.3 From 9d8a4728e14eb3971652c30dc262312d5a544a4e Mon Sep 17 00:00:00 2001 From: Yao Zi Date: Wed, 13 Aug 2025 10:09:26 +0000 Subject: pinctrl: th1520: Mark driver as DM_FLAG_PRE_RELOC It's common that UARTs are bound and probed before U-Boot relocation, in which case the UART's pincontroller and pinconfig must be probed first. Let's apply DM_FLAG_PRE_RELOC to the driver, allow it to bind before relocation. Signed-off-by: Yao Zi Reviewed-by: Leo Yu-Chi Liang --- drivers/pinctrl/pinctrl-th1520.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-th1520.c b/drivers/pinctrl/pinctrl-th1520.c index be7e508f8a4..4eead0875d8 100644 --- a/drivers/pinctrl/pinctrl-th1520.c +++ b/drivers/pinctrl/pinctrl-th1520.c @@ -697,4 +697,5 @@ U_BOOT_DRIVER(th1520_pinctrl) = { .probe = th1520_pinctrl_probe, .priv_auto = sizeof(struct th1520_pinctrl), .ops = &th1520_pinctrl_ops, + .flags = DM_FLAG_PRE_RELOC, }; -- cgit v1.2.3 From cb1a70a856a53a435c7bb75d211ec51fa2855011 Mon Sep 17 00:00:00 2001 From: Hal Feng Date: Mon, 1 Sep 2025 16:24:18 +0800 Subject: pcie: starfive: Remove the redundant print of probe success The dev_err() is used incorrectly and we don't need the driver to state probe success. Signed-off-by: Hal Feng Reviewed-by: Leo Yu-Chi Liang --- drivers/pci/pcie_starfive_jh7110.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie_starfive_jh7110.c b/drivers/pci/pcie_starfive_jh7110.c index 51aca7359ff..0908ae16b67 100644 --- a/drivers/pci/pcie_starfive_jh7110.c +++ b/drivers/pci/pcie_starfive_jh7110.c @@ -292,8 +292,6 @@ static int starfive_pcie_probe(struct udevice *dev) if (ret) return ret; - dev_err(dev, "Starfive PCIe bus probed.\n"); - return 0; } -- cgit v1.2.3 From 7fa1ee7fb99fe36ba512dc221ce77fef9d01cc98 Mon Sep 17 00:00:00 2001 From: Jamie Gibbons Date: Tue, 2 Sep 2025 11:11:02 +0100 Subject: misc: mpfs_syscontroller: add functions to read device tree overlays Include functions to use the system controller to read the device tree overlays which supports auto update functionality. Signed-off-by: Jamie Gibbons Reviewed-by: Leo Yu-Chi Liang --- drivers/misc/mpfs_syscontroller.c | 200 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/mpfs_syscontroller.c b/drivers/misc/mpfs_syscontroller.c index 41e80815ab5..f608d5518b0 100644 --- a/drivers/misc/mpfs_syscontroller.c +++ b/drivers/misc/mpfs_syscontroller.c @@ -22,7 +22,27 @@ #include #include +#define SYS_SPI_CMD 0x50 +#define SYS_SPI_MAILBOX_DATA_LEN 17 +#define SYS_SPI_MAILBOX_SRC_OFFSET 8 +#define SYS_SPI_MAILBOX_LENGTH_OFFSET 12 +#define SYS_SPI_MAILBOX_FREQ_OFFSET 16 +#define SYS_SPI_MAILBOX_FREQ 3 +#define SPI_FLASH_ADDR 0x400 + /* Descriptor table */ +#define START_OFFSET 4 +#define END_OFFSET 8 +#define SIZE_OFFSET 12 +#define DESC_NEXT 12 +#define DESC_RESERVED_SIZE 0 +#define DESC_SIZE 16 + +#define DESIGN_MAGIC_0 0x4d /* 'M' */ +#define DESIGN_MAGIC_1 0x43 /* 'C'*/ +#define DESIGN_MAGIC_2 0x48 /* 'H'*/ +#define DESIGN_MAGIC_3 0x50 /* 'P'*/ + #define CMD_OPCODE 0x0u #define CMD_DATA_SIZE 0U #define CMD_DATA NULL @@ -109,6 +129,186 @@ int mpfs_syscontroller_read_sernum(struct mpfs_sys_serv *sys_serv_priv, u8 *devi } EXPORT_SYMBOL(mpfs_syscontroller_read_sernum); +static u16 mpfs_syscontroller_service_spi_copy(struct mpfs_sys_serv *sys_serv_priv, u64 dst_addr, u32 src_addr, u32 length) +{ + int ret; + u32 mailbox_format[SYS_SPI_MAILBOX_DATA_LEN]; + + *(u64 *)mailbox_format = dst_addr; + mailbox_format[SYS_SPI_MAILBOX_SRC_OFFSET/4] = src_addr; + mailbox_format[SYS_SPI_MAILBOX_LENGTH_OFFSET/4] = length; + mailbox_format[SYS_SPI_MAILBOX_FREQ_OFFSET/4] = SYS_SPI_MAILBOX_FREQ; + + struct mpfs_mss_response response = { + .resp_status = 0U, + .resp_msg = mailbox_format, + .resp_size = RESP_BYTES}; + struct mpfs_mss_msg msg = { + .cmd_opcode = SYS_SPI_CMD, + .cmd_data_size = SYS_SPI_MAILBOX_DATA_LEN, + .response = &response, + .cmd_data = (u8 *)mailbox_format, + .mbox_offset = MBOX_OFFSET, + .resp_offset = RESP_OFFSET}; + + ret = mpfs_syscontroller_run_service(sys_serv_priv->sys_controller, &msg); + if (ret) { + dev_err(sys_serv_priv->sys_controller->chan.dev, "Service failed: %d, abort. Failure: %u\n", ret, msg.response->resp_status); + } + + return ret; +} + +static u16 mpfs_syscontroller_get_dtbo_desc_header(struct mpfs_sys_serv *sys_serv_priv, u8 *desc_data, u32 desc_addr) +{ + u32 length, no_of_descs; + int ret = -ENOENT; + + /* Get first four bytes to calculate length */ + ret = mpfs_syscontroller_service_spi_copy(sys_serv_priv, (u64)desc_data, desc_addr, BYTES_4); + if (!ret) { + no_of_descs = *((u32 *)desc_data); + if (no_of_descs) { + length = DESC_SIZE + ((no_of_descs - 1) * DESC_SIZE); + ret = mpfs_syscontroller_service_spi_copy(sys_serv_priv, (u64)desc_data, desc_addr, + length); + } + } + + return ret; +} + +static u8 *mpfs_syscontroller_get_dtbo(struct mpfs_sys_serv *sys_serv_priv, u32 start_addr, u32 size) +{ + int ret; + u8 *dtbo; + + /* Intentionally never freed, even on success so that u-boot "userspace" can access it. */ + dtbo = (u8 *)malloc(size); + + ret = mpfs_syscontroller_service_spi_copy(sys_serv_priv, (u64)dtbo, start_addr, size); + if (ret) { + free(dtbo); + dtbo = NULL; + } + + return dtbo; +} + +static void mpfs_syscontroller_parse_desc_header(struct mpfs_sys_serv *sys_serv_priv, u8 *desc_header, u8 *no_of_dtbo, u32 *dtbos_size) +{ + u32 dtbo_desc_start_addr; + u32 dtbo_desc_size; + u32 no_of_descs; + u16 i; + u8 dtbo_name[16]; + u8 dtbo_addr[20]; + u8 *desc; + u8 *dtbo; + + no_of_descs = *((u32 *)desc_header); + + for (i = 0; i < no_of_descs; i++) { + desc = &desc_header[START_OFFSET + (DESC_NEXT * i)]; + /* + * The dtbo info structure contains addresses that are relative + * to the start of structure, so the offset of the structure in + * flash must be added to get the actual start address. + */ + dtbo_desc_start_addr = *((u32 *)desc) + SPI_FLASH_ADDR; + + desc = &desc_header[SIZE_OFFSET + (DESC_NEXT * i)]; + dtbo_desc_size = *((u32 *)desc); + + dtbo = mpfs_syscontroller_get_dtbo(sys_serv_priv, dtbo_desc_start_addr, dtbo_desc_size); + if (dtbo) { + sprintf(dtbo_name, "dtbo_image%d", *no_of_dtbo); + sprintf(dtbo_addr, "0x%llx", (u64)dtbo); + env_set(dtbo_name, dtbo_addr); + ++*no_of_dtbo; + *dtbos_size += dtbo_desc_size; + } + } +} + +void mpfs_syscontroller_process_dtbo(struct mpfs_sys_serv *sys_serv_priv) +{ + u32 desc_length; + u32 dtbo_desc_addr; + u32 dtbo_addr[5]; + u16 i, hart, no_of_harts; + u8 design_info_desc[256]; + u8 dtbo_desc_data[256]; + u8 no_of_dtbos[8]; + u8 dtbo_size[8]; + u8 *desc; + u8 no_of_dtbo = 0; + u32 dtbos_size = 0; + int ret; + + /* Read first 10 bytes to verify the descriptor is found or not */ + ret = mpfs_syscontroller_service_spi_copy(sys_serv_priv, (u64)design_info_desc, SPI_FLASH_ADDR, 10); + if (ret) { + sprintf(no_of_dtbos, "%d", no_of_dtbo); + env_set("no_of_overlays", no_of_dtbos); + sprintf(dtbo_size, "%d", dtbos_size); + env_set("dtbo_size", dtbo_size); + return; + } + + if (design_info_desc[0] != DESIGN_MAGIC_0 || + design_info_desc[1] != DESIGN_MAGIC_1 || + design_info_desc[2] != DESIGN_MAGIC_2 || + design_info_desc[3] != DESIGN_MAGIC_3) { + dev_dbg(sys_serv_priv->dev, "magic not found in desc structure.\n"); + sprintf(no_of_dtbos, "%d", no_of_dtbo); + env_set("no_of_overlays", no_of_dtbos); + sprintf(dtbo_size, "%d", dtbos_size); + env_set("dtbo_size", dtbo_size); + return; + } + desc_length = *((u32 *)&design_info_desc[4]); + /* Read Design descriptor */ + ret = mpfs_syscontroller_service_spi_copy(sys_serv_priv, (u64)design_info_desc, + SPI_FLASH_ADDR, desc_length); + if (ret) + return; + + no_of_harts = *((u16 *)&design_info_desc[10]); + + for (hart = 0; hart < no_of_harts; hart++) { + /* Start address of DTBO descriptor */ + desc = &design_info_desc[(0x4 * hart) + 0xc]; + + dtbo_desc_addr = *((u32 *)desc); + dtbo_addr[hart] = dtbo_desc_addr; + + if (!dtbo_addr[hart]) + continue; + + for (i = 0; i < hart; i++) { + if (dtbo_addr[hart] == dtbo_addr[i]) + continue; + } + + if (hart && hart == i) + continue; + + dtbo_desc_addr += SPI_FLASH_ADDR; + ret = mpfs_syscontroller_get_dtbo_desc_header(sys_serv_priv, dtbo_desc_data, + dtbo_desc_addr); + if (ret) + continue; + else + mpfs_syscontroller_parse_desc_header(sys_serv_priv, dtbo_desc_data, &no_of_dtbo, &dtbos_size); + } + sprintf(no_of_dtbos, "%d", no_of_dtbo); + env_set("no_of_overlays", no_of_dtbos); + sprintf(dtbo_size, "%d", dtbos_size); + env_set("dtbo_size", dtbo_size); +} +EXPORT_SYMBOL(mpfs_syscontroller_process_dtbo); + static int mpfs_syscontroller_probe(struct udevice *dev) { struct mpfs_syscontroller_priv *sys_controller = dev_get_priv(dev); -- cgit v1.2.3 From 133c6acdaca4d2cfc142600e34f7390a179b4f30 Mon Sep 17 00:00:00 2001 From: Eoin Dickson Date: Tue, 9 Sep 2025 18:23:47 +0530 Subject: gpio: mpfs_gpio: fix compilation warnings mchp_gpio_get_value() should return int instead of bool, and some casts are needed. Signed-off-by: Eoin Dickson Reviewed-by: Leo Yu-Chi Liang --- drivers/gpio/mpfs_gpio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/mpfs_gpio.c b/drivers/gpio/mpfs_gpio.c index 9bbeada4ef5..35eed596c10 100644 --- a/drivers/gpio/mpfs_gpio.c +++ b/drivers/gpio/mpfs_gpio.c @@ -75,7 +75,7 @@ static int mchp_gpio_direction_output(struct udevice *dev, u32 offset, int value return 0; } -static bool mchp_gpio_get_value(struct udevice *dev, u32 offset) +static int mchp_gpio_get_value(struct udevice *dev, u32 offset) { struct mchp_gpio_plat *plat = dev_get_plat(dev); struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); @@ -136,7 +136,7 @@ static int mchp_gpio_probe(struct udevice *dev) struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); char name[18], *str; - plat->regs = dev_get_driver_data(dev); + plat->regs = (struct mpfs_gpio_reg_offsets *)dev_get_driver_data(dev); sprintf(name, "gpio@%4lx_", (uintptr_t)plat->base); str = strdup(name); if (!str) @@ -160,10 +160,10 @@ static const struct mpfs_gpio_reg_offsets coregpio_reg_offsets = { static const struct udevice_id mchp_gpio_match[] = { { .compatible = "microchip,mpfs-gpio", - .data = &mpfs_reg_offsets, + .data = (unsigned long)&mpfs_reg_offsets, }, { .compatible = "microchip,coregpio-rtl-v3", - .data = &coregpio_reg_offsets, + .data = (unsigned long)&coregpio_reg_offsets, }, { /* end of list */ } }; -- cgit v1.2.3 From b717a4090fb0fda4814bbc9d9a91396710294cfb Mon Sep 17 00:00:00 2001 From: John Ripple Date: Tue, 9 Sep 2025 13:53:22 -0600 Subject: imx8: Add ahab_commit command The ahab_commit command allows the user to commit into the SECO fuses that control the SRK key revocation information. This is used to Revoke compromised SRK keys. To use ahab_commit, the boot container must be built with an SRK revocation bit mask that is not 0x0. For the SPSDK provided by NXP, this means setting the 'srk_revoke_mask' option in the config file used to sign the boot container. The 'ahab_commit 0x10' can then be used to commit the SRK revocation information into the SECO fuses. Signed-off-by: John Ripple --- drivers/misc/imx8/scu_api.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/imx8/scu_api.c b/drivers/misc/imx8/scu_api.c index 8985ab6584d..d9cc7acb970 100644 --- a/drivers/misc/imx8/scu_api.c +++ b/drivers/misc/imx8/scu_api.c @@ -1286,3 +1286,34 @@ int sc_seco_secvio_dgo_config(sc_ipc_t ipc, u8 id, u8 access, u32 *data) return ret; } + +int sc_seco_commit(sc_ipc_t ipc, u32 *info) +{ + struct udevice *dev = gd->arch.scu_dev; + struct sc_rpc_msg_s msg; + int size = sizeof(struct sc_rpc_msg_s); + int ret; + + /* Fill in header */ + RPC_VER(&msg) = SC_RPC_VERSION; + RPC_SIZE(&msg) = 2U; + RPC_SVC(&msg) = (u8)SC_RPC_SVC_SECO; + RPC_FUNC(&msg) = (u8)SECO_FUNC_COMMIT; + + /* Fill in send message */ + RPC_U32(&msg, 0U) = *info; + + /* Call RPC */ + ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size); + if (ret) + return ret; + + /* Copy out result */ + ret = (int)RPC_R8(&msg); + + /* Copy out receive message */ + if (!ret) + *info = RPC_U32(&msg, 0U); + + return ret; +} -- cgit v1.2.3 From 00ef795981d0c2df52e7fdd91d57a924b941dbfb Mon Sep 17 00:00:00 2001 From: Ye Li Date: Thu, 11 Sep 2025 18:56:04 +0800 Subject: pci: dw: Fix wrong register used for PCI_COMMAND Wirting to command register should use PCI_COMMAND not PCI_PRIMARY_BUS Signed-off-by: Ye Li --- drivers/pci/pcie_dw_common.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie_dw_common.c b/drivers/pci/pcie_dw_common.c index c4cad019373..4113a9f03cf 100644 --- a/drivers/pci/pcie_dw_common.c +++ b/drivers/pci/pcie_dw_common.c @@ -7,7 +7,6 @@ * * Copyright (C) 2018 Texas Instruments, Inc */ - #include #include #include @@ -385,7 +384,7 @@ void pcie_dw_setup_host(struct pcie_dw *pci) 0xffffff, 0x00ff0100); /* setup command register */ - clrsetbits_le32(pci->dbi_base + PCI_PRIMARY_BUS, + clrsetbits_le32(pci->dbi_base + PCI_COMMAND, 0xffff, PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_SERR); -- cgit v1.2.3 From f8b188350815df2e0436eb113e7c9300ca9cd02e Mon Sep 17 00:00:00 2001 From: Ye Li Date: Thu, 11 Sep 2025 18:56:05 +0800 Subject: pci: pcie_dw_imx: Add iMX9 support to the driver Adding iMX95/iMX94 support to the dw driver. Follow kernel driver stype to use flags to distinguish the characteristic of different platforms. Signed-off-by: Ye Li --- drivers/pci/Kconfig | 2 +- drivers/pci/pcie_dw_imx.c | 420 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 377 insertions(+), 45 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index b8568267ff8..6c2cda1a966 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -461,7 +461,7 @@ config PCIE_STARFIVE_JH7110 config PCIE_DW_IMX bool "i.MX DW PCIe controller support" - depends on ARCH_IMX8M + depends on ARCH_IMX8M || ARCH_IMX9 select PCIE_DW_COMMON select DM_REGULATOR select REGMAP diff --git a/drivers/pci/pcie_dw_imx.c b/drivers/pci/pcie_dw_imx.c index fdb463710ba..f84c7180560 100644 --- a/drivers/pci/pcie_dw_imx.c +++ b/drivers/pci/pcie_dw_imx.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2024 Linaro Ltd. + * Copyright 2025 NXP * * Author: Sumit Garg */ @@ -45,6 +46,47 @@ #define IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE_EN BIT(10) #define IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE BIT(11) +#define IMX95_PCIE_PHY_GEN_CTRL 0x0 +#define IMX95_PCIE_REF_USE_PAD BIT(17) + +#define IMX95_PCIE_PHY_MPLLA_CTRL 0x10 +#define IMX95_PCIE_PHY_MPLL_STATE BIT(30) + +#define IMX95_PCIE_SS_RW_REG_0 0xf0 +#define IMX95_PCIE_REF_CLKEN BIT(23) +#define IMX95_PCIE_PHY_CR_PARA_SEL BIT(9) +#define IMX95_PCIE_SS_RW_REG_1 0xf4 +#define IMX95_PCIE_CLKREQ_OVERRIDE_EN BIT(8) +#define IMX95_PCIE_CLKREQ_OVERRIDE_VAL BIT(9) +#define IMX95_PCIE_SYS_AUX_PWR_DET BIT(31) + +#define IMX95_PE0_GEN_CTRL_1 0x1050 +#define IMX95_PCIE_DEVICE_TYPE GENMASK(3, 0) + +#define IMX95_PE0_GEN_CTRL_3 0x1058 +#define IMX95_PCIE_LTSSM_EN BIT(0) + +#define IMX95_PCIE_RST_CTRL 0x3010 +#define IMX95_PCIE_COLD_RST BIT(0) + +#define GEN3_RELATED_OFF 0x890 +#define GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL BIT(0) +#define GEN3_RELATED_OFF_RXEQ_RGRDLESS_RXTS BIT(13) +#define GEN3_RELATED_OFF_GEN3_EQ_DISABLE BIT(16) +#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_SHIFT 24 +#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK GENMASK(25, 24) +#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_16_0GT 0x1 + +#define IMX_PCIE_FLAG_HAS_PHYDRV BIT(3) +#define IMX_PCIE_FLAG_HAS_APP_RESET BIT(4) +#define IMX_PCIE_FLAG_HAS_SERDES BIT(6) + +#define IMX_PCIE_MAX_INSTANCES 2 + +/* Parameters for the waiting for PCIe PHY PLL to lock s*/ +#define PHY_PLL_LOCK_WAIT_USLEEP_MAX 200 +#define PHY_PLL_LOCK_WAIT_TIMEOUT (2000 * PHY_PLL_LOCK_WAIT_USLEEP_MAX / 1000) + struct pcie_dw_imx { /* Must be first member of the struct */ struct pcie_dw dw; @@ -54,20 +96,203 @@ struct pcie_dw_imx { struct reset_ctl apps_reset; struct phy phy; struct udevice *vpcie; + void *info; + u32 max_link_speed; + bool enable_ext_refclk; + bool supports_clkreq; }; struct pcie_chip_info { + u32 flags; + const u32 ltssm_off; + const u32 ltssm_mask; + const u32 mode_off[IMX_PCIE_MAX_INSTANCES]; + const u32 mode_mask[IMX_PCIE_MAX_INSTANCES]; const char *gpr; + void (*init_phy)(struct pcie_dw_imx *priv); + int (*enable_ref_clk)(struct pcie_dw_imx *priv, bool enable); + int (*core_reset)(struct pcie_dw_imx *priv, bool assert); + int (*wait_pll_lock)(struct pcie_dw_imx *priv); + void (*post_config)(struct pcie_dw_imx *priv); }; +static void imx95_pcie_init_phy(struct pcie_dw_imx *priv) +{ +/* + * Workaround for ERR051624: The Controller Without Vaux Cannot + * Exit L23 Ready Through Beacon or PERST# De-assertion + * + * When the auxiliary power is not available the controller + * cannot exit from L23 Ready with beacon or PERST# de-assertion + * when main power is not removed. + * + * Workaround: Set SS_RW_REG_1[SYS_AUX_PWR_DET] to 1. + */ + regmap_update_bits(priv->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_1, + IMX95_PCIE_SYS_AUX_PWR_DET, IMX95_PCIE_SYS_AUX_PWR_DET); + + regmap_update_bits(priv->iomuxc_gpr, + IMX95_PCIE_SS_RW_REG_0, + IMX95_PCIE_PHY_CR_PARA_SEL, + IMX95_PCIE_PHY_CR_PARA_SEL); + + if (priv->enable_ext_refclk) { + /* External clock is used as reference clock */ + regmap_update_bits(priv->iomuxc_gpr, + IMX95_PCIE_PHY_GEN_CTRL, + IMX95_PCIE_REF_USE_PAD, + IMX95_PCIE_REF_USE_PAD); + regmap_update_bits(priv->iomuxc_gpr, + IMX95_PCIE_SS_RW_REG_0, + IMX95_PCIE_REF_CLKEN, 0); + } else { + regmap_update_bits(priv->iomuxc_gpr, + IMX95_PCIE_PHY_GEN_CTRL, + IMX95_PCIE_REF_USE_PAD, 0); + + regmap_update_bits(priv->iomuxc_gpr, + IMX95_PCIE_SS_RW_REG_0, + IMX95_PCIE_REF_CLKEN, + IMX95_PCIE_REF_CLKEN); + } + + /* Force CLKREQ# low by override */ + if (!priv->supports_clkreq) + regmap_update_bits(priv->iomuxc_gpr, + IMX95_PCIE_SS_RW_REG_1, + IMX95_PCIE_CLKREQ_OVERRIDE_EN | + IMX95_PCIE_CLKREQ_OVERRIDE_VAL, + IMX95_PCIE_CLKREQ_OVERRIDE_EN | + IMX95_PCIE_CLKREQ_OVERRIDE_VAL); +} + +static int imx95_pcie_wait_for_phy_pll_lock(struct pcie_dw_imx *priv) +{ + u32 val; + + if (regmap_read_poll_timeout(priv->iomuxc_gpr, + IMX95_PCIE_PHY_MPLLA_CTRL, val, + val & IMX95_PCIE_PHY_MPLL_STATE, + PHY_PLL_LOCK_WAIT_USLEEP_MAX, + PHY_PLL_LOCK_WAIT_TIMEOUT)) { + printf("PCIe PLL lock timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int imx95_pcie_core_reset(struct pcie_dw_imx *priv, bool assert) +{ + u32 val; + + if (assert) { + /* + * From i.MX95 PCIe PHY perspective, the COLD reset toggle + * should be complete after power-up by the following sequence. + * > 10us(at power-up) + * > 10ns(warm reset) + * |<------------>| + * ______________ + * phy_reset ____/ \________________ + * ____________ + * ref_clk_en_______________________/ + * Toggle COLD reset aligned with this sequence for i.MX95 PCIe. + */ + regmap_update_bits(priv->iomuxc_gpr, IMX95_PCIE_RST_CTRL, + IMX95_PCIE_COLD_RST, IMX95_PCIE_COLD_RST); + /* + * Make sure the write to IMX95_PCIE_RST_CTRL is flushed to the + * hardware by doing a read. Otherwise, there is no guarantee + * that the write has reached the hardware before udelay(). + */ + regmap_read(priv->iomuxc_gpr, IMX95_PCIE_RST_CTRL, + &val); + udelay(15); + regmap_update_bits(priv->iomuxc_gpr, IMX95_PCIE_RST_CTRL, + IMX95_PCIE_COLD_RST, 0); + regmap_read(priv->iomuxc_gpr, IMX95_PCIE_RST_CTRL, + &val); + udelay(10); + } + + return 0; +} + +static void imx95_pcie_post_config(struct pcie_dw_imx *priv) +{ + u32 val; + + /* + * Workaround for ERR051586: Compliance with 8GT/s Receiver + * Impedance ECN + * + * The default value of GEN3_RELATED_OFF[GEN3_ZRXDC_NONCOMPL] is + * 1 which makes receiver non-compliant with the ZRX-DC + * parameter for 2.5 GT/s when operating at 8 GT/s or higher. It + * causes unnecessary timeout in L1. + * + * Workaround: Program GEN3_RELATED_OFF[GEN3_ZRXDC_NONCOMPL] to 0. + */ + dw_pcie_dbi_write_enable(&priv->dw, true); + val = readl(priv->dw.dbi_base + GEN3_RELATED_OFF); + val &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL; + writel(val, priv->dw.dbi_base + GEN3_RELATED_OFF); + dw_pcie_dbi_write_enable(&priv->dw, false); +} + +static int imx8mm_pcie_enable_ref_clk(struct pcie_dw_imx *priv, bool enable) +{ + regmap_update_bits(priv->iomuxc_gpr, IOMUXC_GPR14_OFFSET, + IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE, + enable ? 0 : IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE); + regmap_update_bits(priv->iomuxc_gpr, IOMUXC_GPR14_OFFSET, + IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE_EN, + enable ? IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE_EN : 0); + return 0; +} + static const struct pcie_chip_info imx8mm_chip_info = { + .flags = IMX_PCIE_FLAG_HAS_APP_RESET | IMX_PCIE_FLAG_HAS_PHYDRV, .gpr = "fsl,imx8mm-iomuxc-gpr", + .enable_ref_clk = imx8mm_pcie_enable_ref_clk, }; static const struct pcie_chip_info imx8mp_chip_info = { + .flags = IMX_PCIE_FLAG_HAS_APP_RESET | IMX_PCIE_FLAG_HAS_PHYDRV, .gpr = "fsl,imx8mp-iomuxc-gpr", + .enable_ref_clk = imx8mm_pcie_enable_ref_clk, +}; + +static const struct pcie_chip_info imx95_chip_info = { + .flags = IMX_PCIE_FLAG_HAS_SERDES, + .ltssm_off = IMX95_PE0_GEN_CTRL_3, + .ltssm_mask = IMX95_PCIE_LTSSM_EN, + .mode_off[0] = IMX95_PE0_GEN_CTRL_1, + .mode_mask[0] = IMX95_PCIE_DEVICE_TYPE, + .init_phy = imx95_pcie_init_phy, + .core_reset = imx95_pcie_core_reset, + .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock, + .post_config = imx95_pcie_post_config, }; +static void imx_pcie_configure_type(struct pcie_dw_imx *priv) +{ + struct pcie_chip_info *info = (struct pcie_chip_info *)(priv->info); + unsigned int mask, val, mode; + + mode = PCI_EXP_TYPE_ROOT_PORT; + + /* If mode_mask is 0, then generic PHY driver is used to set the mode */ + if (!info->mode_mask[0]) + return; + + mask = info->mode_mask[0]; + val = mode << (ffs(mask) - 1); + + regmap_update_bits(priv->iomuxc_gpr, info->mode_off[0], mask, val); +} + static void pcie_dw_configure(struct pcie_dw_imx *priv, u32 cap_speed) { dw_pcie_dbi_write_enable(&priv->dw, true); @@ -75,17 +300,34 @@ static void pcie_dw_configure(struct pcie_dw_imx *priv, u32 cap_speed) clrsetbits_le32(priv->dw.dbi_base + PCIE_LINK_CAPABILITY, TARGET_LINK_SPEED_MASK, cap_speed); + clrsetbits_le32(priv->dw.dbi_base + PCIE_LINK_CTL_2, + TARGET_LINK_SPEED_MASK, cap_speed); + dw_pcie_dbi_write_enable(&priv->dw, false); } static void imx_pcie_ltssm_enable(struct pcie_dw_imx *priv) { - reset_deassert(&priv->apps_reset); + struct pcie_chip_info *info = (struct pcie_chip_info *)(priv->info); + + if (info->ltssm_mask) + regmap_update_bits(priv->iomuxc_gpr, info->ltssm_off, info->ltssm_mask, + info->ltssm_mask); + + if (info->flags & IMX_PCIE_FLAG_HAS_APP_RESET) + reset_deassert(&priv->apps_reset); } static void imx_pcie_ltssm_disable(struct pcie_dw_imx *priv) { - reset_assert(&priv->apps_reset); + struct pcie_chip_info *info = (struct pcie_chip_info *)(priv->info); + + if (info->ltssm_mask) + regmap_update_bits(priv->iomuxc_gpr, info->ltssm_off, + info->ltssm_mask, 0); + + if (info->flags & IMX_PCIE_FLAG_HAS_APP_RESET) + reset_assert(&priv->apps_reset); } static bool is_link_up(u32 val) @@ -122,6 +364,11 @@ static int pcie_link_up(struct pcie_dw_imx *priv, u32 cap_speed) static int imx_pcie_assert_core_reset(struct pcie_dw_imx *priv) { + struct pcie_chip_info *info = (struct pcie_chip_info *)(priv->info); + + if (info->core_reset) + info->core_reset(priv, true); + if (dm_gpio_is_valid(&priv->reset_gpio)) { dm_gpio_set_value(&priv->reset_gpio, 1); mdelay(20); @@ -133,6 +380,7 @@ static int imx_pcie_assert_core_reset(struct pcie_dw_imx *priv) static int imx_pcie_clk_enable(struct pcie_dw_imx *priv) { int ret; + struct pcie_chip_info *info = (struct pcie_chip_info *)(priv->info); ret = clk_enable_bulk(&priv->clks); if (ret) @@ -142,11 +390,8 @@ static int imx_pcie_clk_enable(struct pcie_dw_imx *priv) * Set the over ride low and enabled make sure that * REF_CLK is turned on. */ - regmap_update_bits(priv->iomuxc_gpr, IOMUXC_GPR14_OFFSET, - IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE, 0); - regmap_update_bits(priv->iomuxc_gpr, IOMUXC_GPR14_OFFSET, - IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE_EN, - IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE_EN); + if (info->enable_ref_clk) + info->enable_ref_clk(priv, true); /* allow the clocks to stabilize */ udelay(500); @@ -156,6 +401,11 @@ static int imx_pcie_clk_enable(struct pcie_dw_imx *priv) static void imx_pcie_deassert_core_reset(struct pcie_dw_imx *priv) { + struct pcie_chip_info *info = (struct pcie_chip_info *)(priv->info); + + if (info->core_reset) + info->core_reset(priv, false); + if (!dm_gpio_is_valid(&priv->reset_gpio)) return; @@ -170,10 +420,11 @@ static int pcie_dw_imx_probe(struct udevice *dev) struct pcie_dw_imx *priv = dev_get_priv(dev); struct udevice *ctlr = pci_get_controller(dev); struct pci_controller *hose = dev_get_uclass_priv(ctlr); + struct pcie_chip_info *info = (void *)dev_get_driver_data(dev); int ret; if (priv->vpcie) { - ret = regulator_set_enable(priv->vpcie, true); + ret = regulator_set_enable_if_allowed(priv->vpcie, true); if (ret) { dev_err(dev, "failed to enable vpcie regulator\n"); return ret; @@ -186,31 +437,49 @@ static int pcie_dw_imx_probe(struct udevice *dev) return ret; } + if (info->init_phy) + info->init_phy(priv); + + imx_pcie_configure_type(priv); + ret = imx_pcie_clk_enable(priv); if (ret) { dev_err(dev, "failed to enable clocks\n"); goto err_clk; } - ret = generic_phy_init(&priv->phy); - if (ret) { - dev_err(dev, "failed to initialize PHY\n"); - goto err_phy_init; - } + if (info->flags & IMX_PCIE_FLAG_HAS_PHYDRV) { + ret = generic_phy_init(&priv->phy); + if (ret) { + dev_err(dev, "failed to initialize PHY\n"); + goto err_phy_init; + } - ret = generic_phy_power_on(&priv->phy); - if (ret) { - dev_err(dev, "failed to power on PHY\n"); - goto err_phy_power; + ret = generic_phy_power_on(&priv->phy); + if (ret) { + dev_err(dev, "failed to power on PHY\n"); + goto err_phy_power; + } } imx_pcie_deassert_core_reset(priv); + if (info->wait_pll_lock) { + ret = info->wait_pll_lock(priv); + if (ret) { + dev_err(dev, "failed to wait pll lock\n"); + goto err_link; + } + } + + if (info->post_config) + info->post_config(priv); + priv->dw.first_busno = dev_seq(dev); priv->dw.dev = dev; pcie_dw_setup_host(&priv->dw); - if (pcie_link_up(priv, LINK_SPEED_GEN_1)) { + if (pcie_link_up(priv, priv->max_link_speed)) { printf("PCIE-%d: Link down\n", dev_seq(dev)); ret = -ENODEV; goto err_link; @@ -229,26 +498,41 @@ static int pcie_dw_imx_probe(struct udevice *dev) return 0; err_link: - generic_shutdown_phy(&priv->phy); + if (info->flags & IMX_PCIE_FLAG_HAS_PHYDRV) + generic_shutdown_phy(&priv->phy); err_phy_power: - generic_phy_exit(&priv->phy); + if (info->flags & IMX_PCIE_FLAG_HAS_PHYDRV) + generic_phy_exit(&priv->phy); err_phy_init: - clk_disable_bulk(&priv->clks); + clk_release_bulk(&priv->clks); err_clk: imx_pcie_deassert_core_reset(priv); + dm_gpio_free(dev, &priv->reset_gpio); + + if (priv->vpcie) + regulator_set_enable_if_allowed(priv->vpcie, false); + return ret; } static int pcie_dw_imx_remove(struct udevice *dev) { struct pcie_dw_imx *priv = dev_get_priv(dev); + struct pcie_chip_info *info = (void *)dev_get_driver_data(dev); + + if (info->flags & IMX_PCIE_FLAG_HAS_PHYDRV) + generic_shutdown_phy(&priv->phy); - generic_shutdown_phy(&priv->phy); dm_gpio_free(dev, &priv->reset_gpio); - reset_free(&priv->apps_reset); + if (info->flags & IMX_PCIE_FLAG_HAS_APP_RESET) + reset_free(&priv->apps_reset); + clk_release_bulk(&priv->clks); + if (priv->vpcie) + regulator_set_enable_if_allowed(priv->vpcie, false); + return 0; } @@ -257,7 +541,9 @@ static int pcie_dw_imx_of_to_plat(struct udevice *dev) struct pcie_chip_info *info = (void *)dev_get_driver_data(dev); struct pcie_dw_imx *priv = dev_get_priv(dev); ofnode gpr; - int ret; + int ret, index; + + priv->info = info; /* Get the controller base address */ priv->dw.dbi_base = (void *)dev_read_addr_name(dev, "dbi"); @@ -274,17 +560,29 @@ static int pcie_dw_imx_of_to_plat(struct udevice *dev) return -EINVAL; } + priv->dw.atu_base = (void *)dev_read_addr_name_ptr(dev, "atu"); + if (!priv->dw.atu_base) + dev_dbg(dev, "failed to get atu address from dtb\n"); + ret = clk_get_bulk(dev, &priv->clks); if (ret) { dev_err(dev, "failed to get PCIe clks\n"); return ret; } - ret = reset_get_by_name(dev, "apps", &priv->apps_reset); - if (ret) { - dev_err(dev, - "Failed to get PCIe apps reset control\n"); - goto err_reset; + index = ofnode_stringlist_search(dev_ofnode(dev), "clock-names", "ext-ref"); + if (index < 0) + priv->enable_ext_refclk = false; + else + priv->enable_ext_refclk = true; + + if (info->flags & IMX_PCIE_FLAG_HAS_APP_RESET) { + ret = reset_get_by_name(dev, "apps", &priv->apps_reset); + if (ret) { + dev_err(dev, + "Failed to get PCIe apps reset control\n"); + goto err_reset; + } } ret = gpio_request_by_name(dev, "reset-gpio", 0, &priv->reset_gpio, @@ -294,26 +592,58 @@ static int pcie_dw_imx_of_to_plat(struct udevice *dev) goto err_gpio; } - ret = generic_phy_get_by_name(dev, "pcie-phy", &priv->phy); - if (ret) { - dev_err(dev, "failed to get pcie phy\n"); - goto err_phy; + if (info->flags & IMX_PCIE_FLAG_HAS_PHYDRV) { + ret = generic_phy_get_by_name(dev, "pcie-phy", &priv->phy); + if (ret) { + dev_err(dev, "failed to get pcie phy\n"); + goto err_phy; + } } - gpr = ofnode_by_compatible(ofnode_null(), info->gpr); - if (ofnode_equal(gpr, ofnode_null())) { - dev_err(dev, "unable to find GPR node\n"); - ret = -ENODEV; - goto err_phy; + if (info->flags & IMX_PCIE_FLAG_HAS_SERDES) { + void __iomem *app_base; + fdt_size_t app_size; + struct regmap_config config; + + app_base = (void *)dev_read_addr_size_name(dev, "app", &app_size); + if ((fdt_addr_t)app_base == FDT_ADDR_T_NONE) { + dev_err(dev, "failed to get app_base address\n"); + return -EINVAL; + } + + config.r_start = (ulong)app_base; + config.r_size = (ulong)app_size; + config.reg_offset_shift = 0; + config.width = REGMAP_SIZE_32; + + priv->iomuxc_gpr = devm_regmap_init(dev, NULL, NULL, &config); + if (IS_ERR(priv->iomuxc_gpr)) { + dev_err(dev, "unable to remap gpr\n"); + ret = PTR_ERR(priv->iomuxc_gpr); + goto err_phy; + } } - priv->iomuxc_gpr = syscon_node_to_regmap(gpr); - if (IS_ERR(priv->iomuxc_gpr)) { - dev_err(dev, "unable to find iomuxc registers\n"); - ret = PTR_ERR(priv->iomuxc_gpr); - goto err_phy; + if (info->gpr) { + gpr = ofnode_by_compatible(ofnode_null(), info->gpr); + if (ofnode_equal(gpr, ofnode_null())) { + dev_err(dev, "unable to find GPR node\n"); + ret = -ENODEV; + goto err_phy; + } + + priv->iomuxc_gpr = syscon_node_to_regmap(gpr); + if (IS_ERR(priv->iomuxc_gpr)) { + dev_err(dev, "unable to find iomuxc registers\n"); + ret = PTR_ERR(priv->iomuxc_gpr); + goto err_phy; + } } + priv->max_link_speed = dev_read_u32_default(dev, "fsl,max-link-speed", LINK_SPEED_GEN_1); + + priv->supports_clkreq = dev_read_bool(dev, "supports-clkreq"); + /* vpcie-supply regulator is optional */ device_get_supply_regulator(dev, "vpcie-supply", &priv->vpcie); @@ -322,7 +652,8 @@ static int pcie_dw_imx_of_to_plat(struct udevice *dev) err_phy: dm_gpio_free(dev, &priv->reset_gpio); err_gpio: - reset_free(&priv->apps_reset); + if (info->flags & IMX_PCIE_FLAG_HAS_APP_RESET) + reset_free(&priv->apps_reset); err_reset: clk_release_bulk(&priv->clks); @@ -337,6 +668,7 @@ static const struct dm_pci_ops pcie_dw_imx_ops = { static const struct udevice_id pcie_dw_imx_ids[] = { { .compatible = "fsl,imx8mm-pcie", .data = (ulong)&imx8mm_chip_info, }, { .compatible = "fsl,imx8mp-pcie", .data = (ulong)&imx8mp_chip_info, }, + { .compatible = "fsl,imx95-pcie", .data = (ulong)&imx95_chip_info, }, { } }; -- cgit v1.2.3 From d680ac6cfd052ced54f51d7cfe081ad291550e44 Mon Sep 17 00:00:00 2001 From: Ye Li Date: Thu, 11 Sep 2025 18:56:06 +0800 Subject: clk: imx: Add imx95 blkctrl clock driver Add iMX95 blkctrl clock driver which implements clocks for HSIOMIX blkctrl and LVDS blkctrl. Since multiple blkctrl device for different blkctrl may be enabled, and each has dedicated clock id from 0. We must enable CLK_AUTO_ID to avoid conflict on clock id. Signed-off-by: Ye Li --- drivers/clk/imx/Kconfig | 9 ++ drivers/clk/imx/Makefile | 1 + drivers/clk/imx/clk-imx95-blkctrl.c | 170 ++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 drivers/clk/imx/clk-imx95-blkctrl.c (limited to 'drivers') diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig index 74d5fe73f94..644ab162af4 100644 --- a/drivers/clk/imx/Kconfig +++ b/drivers/clk/imx/Kconfig @@ -167,3 +167,12 @@ config CLK_IMXRT1170 select CLK_CCF help This enables support clock driver for i.MXRT1170 platforms. + +config CLK_IMX95_BLKCTRL + bool "Enable i.MX95 blkctrl clock driver" + depends on IMX95 || IMX94 + select CLK + select CLK_CCF + select CLK_AUTO_ID + help + Enable support for clocks in i.MX95 MIX blkctrl like HSIO and LVDS. diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile index b10221a195c..f2fd6ff8ca0 100644 --- a/drivers/clk/imx/Makefile +++ b/drivers/clk/imx/Makefile @@ -25,3 +25,4 @@ obj-$(CONFIG_$(PHASE_)CLK_IMX93) += clk-imx93.o clk-fracn-gppll.o \ obj-$(CONFIG_$(PHASE_)CLK_IMXRT1020) += clk-imxrt1020.o obj-$(CONFIG_$(PHASE_)CLK_IMXRT1050) += clk-imxrt1050.o obj-$(CONFIG_$(PHASE_)CLK_IMXRT1170) += clk-imxrt1170.o +obj-$(CONFIG_CLK_IMX95_BLKCTRL) += clk-imx95-blkctrl.o diff --git a/drivers/clk/imx/clk-imx95-blkctrl.c b/drivers/clk/imx/clk-imx95-blkctrl.c new file mode 100644 index 00000000000..3bf6f9415e2 --- /dev/null +++ b/drivers/clk/imx/clk-imx95-blkctrl.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2023-2025 NXP + * + */ + +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +enum { + CLK_GATE, + CLK_DIVIDER, + CLK_MUX, +}; + +struct imx95_blk_ctl_clk_dev_data { + const char *name; + const char * const *parent_names; + u32 num_parents; + u32 reg; + u32 bit_idx; + u32 clk_type; + u32 flags; + u32 flags2; + u32 type; +}; + +struct imx95_blk_ctl_dev_data { + const struct imx95_blk_ctl_clk_dev_data *clk_dev_data; + u32 num_clks; + u32 clk_reg_offset; +}; + +static const struct imx95_blk_ctl_clk_dev_data hsio_blk_ctl_clk_dev_data[] = { + [0] = { + .name = "hsio_blk_ctl_clk", + .parent_names = (const char *[]){ "hsiopll", }, + .num_parents = 1, + .reg = 0, + .bit_idx = 6, + .type = CLK_GATE, + .flags = CLK_SET_RATE_PARENT, + } +}; + +static const struct imx95_blk_ctl_dev_data hsio_blk_ctl_dev_data = { + .num_clks = 1, + .clk_dev_data = hsio_blk_ctl_clk_dev_data, + .clk_reg_offset = 0, +}; + +static const struct imx95_blk_ctl_clk_dev_data imx95_lvds_clk_dev_data[] = { + [IMX95_CLK_DISPMIX_LVDS_PHY_DIV] = { + .name = "ldb_phy_div", + .parent_names = (const char *[]){ "ldbpll", }, + .num_parents = 1, + .reg = 0, + .bit_idx = 0, + .type = CLK_DIVIDER, + .flags2 = CLK_DIVIDER_POWER_OF_TWO, + }, + + [IMX95_CLK_DISPMIX_LVDS_CH0_GATE] = { + .name = "lvds_ch0_gate", + .parent_names = (const char *[]){ "ldb_phy_div", }, + .num_parents = 1, + .reg = 0, + .bit_idx = 1, + .type = CLK_GATE, + .flags = CLK_SET_RATE_PARENT, + .flags2 = CLK_GATE_SET_TO_DISABLE, + }, + [IMX95_CLK_DISPMIX_LVDS_CH1_GATE] = { + .name = "lvds_ch1_gate", + .parent_names = (const char *[]){ "ldb_phy_div", }, + .num_parents = 1, + .reg = 0, + .bit_idx = 2, + .type = CLK_GATE, + .flags = CLK_SET_RATE_PARENT, + .flags2 = CLK_GATE_SET_TO_DISABLE, + }, + [IMX95_CLK_DISPMIX_PIX_DI0_GATE] = { + .name = "lvds_di0_gate", + .parent_names = (const char *[]){ "ldb_pll_div7", }, + .num_parents = 1, + .reg = 0, + .bit_idx = 3, + .type = CLK_GATE, + .flags = CLK_SET_RATE_PARENT, + .flags2 = CLK_GATE_SET_TO_DISABLE, + }, + [IMX95_CLK_DISPMIX_PIX_DI1_GATE] = { + .name = "lvds_di1_gate", + .parent_names = (const char *[]){ "ldb_pll_div7", }, + .num_parents = 1, + .reg = 0, + .bit_idx = 4, + .type = CLK_GATE, + .flags = CLK_SET_RATE_PARENT, + .flags2 = CLK_GATE_SET_TO_DISABLE, + }, +}; + +static const struct imx95_blk_ctl_dev_data imx95_lvds_csr_dev_data = { + .num_clks = ARRAY_SIZE(imx95_lvds_clk_dev_data), + .clk_dev_data = imx95_lvds_clk_dev_data, + .clk_reg_offset = 0, +}; + +static int imx95_blkctrl_clk_probe(struct udevice *dev) +{ + int i; + void __iomem *addr; + struct imx95_blk_ctl_dev_data *dev_data = (void *)dev_get_driver_data(dev); + const struct imx95_blk_ctl_clk_dev_data *clk_dev_data; + + addr = dev_read_addr_ptr(dev); + if (addr == (void *)FDT_ADDR_T_NONE) { + dev_err(dev, "No blkctrl register base address\n"); + return -EINVAL; + } + + if (!dev_data) { + dev_err(dev, "driver data is NULL\n"); + return -EINVAL; + } + + clk_dev_data = dev_data->clk_dev_data; + for (i = 0; i < dev_data->num_clks; i++) { + if (clk_dev_data[i].clk_type == CLK_GATE) { + dev_clk_dm(dev, i, clk_register_gate(dev, clk_dev_data[i].name, clk_dev_data[i].parent_names[0], + clk_dev_data[i].flags, addr + dev_data->clk_reg_offset, clk_dev_data[i].bit_idx, + clk_dev_data[i].flags2, NULL)); + } else if (clk_dev_data[i].clk_type == CLK_DIVIDER) { + dev_clk_dm(dev, i, + clk_register_divider(dev, clk_dev_data[i].name, clk_dev_data[i].parent_names[0], + clk_dev_data[i].flags, addr + dev_data->clk_reg_offset, clk_dev_data[i].bit_idx, 1, + clk_dev_data[i].flags2)); + } else if (clk_dev_data[i].clk_type == CLK_MUX) { + dev_clk_dm(dev, i, + clk_register_mux(dev, clk_dev_data[i].name, clk_dev_data[i].parent_names, + clk_dev_data[i].num_parents, clk_dev_data[i].flags, addr + dev_data->clk_reg_offset, + clk_dev_data[i].bit_idx, 1, clk_dev_data[i].flags2)); + } + } + + return 0; +} + +static const struct udevice_id imx95_blkctrl_clk_ids[] = { + { .compatible = "nxp,imx95-lvds-csr", .data = (ulong)&imx95_lvds_csr_dev_data, }, + { .compatible = "nxp,imx95-hsio-blk-ctl", .data = (ulong)&hsio_blk_ctl_dev_data, }, + { }, +}; + +U_BOOT_DRIVER(imx95_blkctrl_clk) = { + .name = "imx95_blkctrl_clk", + .id = UCLASS_CLK, + .of_match = imx95_blkctrl_clk_ids, + .ops = &ccf_clk_ops, + .probe = imx95_blkctrl_clk_probe, + .flags = DM_FLAG_PRE_RELOC, +}; -- cgit v1.2.3 From f98d812e5353408ef77a46bad1f1cdc793ff8a03 Mon Sep 17 00:00:00 2001 From: Ye Li Date: Thu, 11 Sep 2025 18:56:07 +0800 Subject: power: regulator: Add vin-supply for GPIO and Fixed regulators Enable the vin-supply when probing the regulator device. Signed-off-by: Ye Li --- drivers/power/regulator/regulator_common.c | 10 ++++++++++ drivers/power/regulator/regulator_common.h | 1 + 2 files changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/power/regulator/regulator_common.c b/drivers/power/regulator/regulator_common.c index e3565d32a01..c80f10c3aa3 100644 --- a/drivers/power/regulator/regulator_common.c +++ b/drivers/power/regulator/regulator_common.c @@ -45,6 +45,16 @@ int regulator_common_of_to_plat(struct udevice *dev, dev_read_u32_default(dev, "u-boot,off-on-delay-us", 0); } + ret = device_get_supply_regulator(dev, "vin-supply", &plat->vin_supply); + if (ret) { + debug("Regulator vin regulator not defined: %d\n", ret); + if (ret != -ENOENT) + return ret; + } + + if (plat->vin_supply) + regulator_set_enable_if_allowed(plat->vin_supply, true); + return 0; } diff --git a/drivers/power/regulator/regulator_common.h b/drivers/power/regulator/regulator_common.h index d4962899d83..799c968d0b6 100644 --- a/drivers/power/regulator/regulator_common.h +++ b/drivers/power/regulator/regulator_common.h @@ -14,6 +14,7 @@ struct regulator_common_plat { unsigned int startup_delay_us; unsigned int off_on_delay_us; unsigned int enable_count; + struct udevice *vin_supply; }; int regulator_common_of_to_plat(struct udevice *dev, -- cgit v1.2.3 From 87119e0f793d55bc540ac98fea9d75c3cb5ac8be Mon Sep 17 00:00:00 2001 From: Ye Li Date: Thu, 11 Sep 2025 18:56:08 +0800 Subject: clk: clk-uclass: Fix clk_set_default_rates issue clk_set_rate returns the actual clock rate, When assigned clock rate is higher than 0x7FFFFFFF, the return value will be recognized as error. Change to IS_ERR_VALUE to check the return value. Signed-off-by: Ye Li --- drivers/clk/clk-uclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 7262e89b512..3dbe1ce9441 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -358,7 +358,7 @@ static int clk_set_default_rates(struct udevice *dev, ret = clk_set_rate(c, rates[index]); - if (ret < 0) { + if (IS_ERR_VALUE(ret)) { dev_warn(dev, "failed to set rate on clock index %d (%ld) (error = %d)\n", index, clk.id, ret); -- cgit v1.2.3 From 2727de799a644f4db15e8682503f783a62fa4af2 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Tue, 16 Sep 2025 10:57:35 +0800 Subject: power: regulator: pfuze100: Fix accessing the regulator desc se_desc loop check is wrong, it relies on the desc always has the expected name to end of the loop. It works because the device tree has the expected name as of now, but this may not be always true. Drop se_desc by moving the check into probe and fix the loop check. Reported-by: Andrew Goodbody Cc: Tom Rini Cc: Fabio Estevam Signed-off-by: Peng Fan --- drivers/power/regulator/pfuze100.c | 44 +++++++++++++++----------------------- 1 file changed, 17 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/power/regulator/pfuze100.c b/drivers/power/regulator/pfuze100.c index bf3a7019411..f864b1d8834 100644 --- a/drivers/power/regulator/pfuze100.c +++ b/drivers/power/regulator/pfuze100.c @@ -241,56 +241,46 @@ static struct dm_regulator_mode pfuze_ldo_modes[] = { MODE(LDO_MODE_ON, LDO_MODE_ON, "LDO_MODE_ON"), }; -static struct pfuze100_regulator_desc *se_desc(struct pfuze100_regulator_desc *desc, - int size, - const char *name) -{ - int i; - - for (i = 0; i < size; desc++) { - if (!strcmp(desc->name, name)) - return desc; - continue; - } - - return NULL; -} - static int pfuze100_regulator_probe(struct udevice *dev) { struct dm_regulator_uclass_plat *uc_pdata; struct pfuze100_regulator_plat *plat = dev_get_plat(dev); struct pfuze100_regulator_desc *desc; + int i, size; switch (dev_get_driver_data(dev_get_parent(dev))) { case PFUZE100: - desc = se_desc(pfuze100_regulators, - ARRAY_SIZE(pfuze100_regulators), - dev->name); + desc = pfuze100_regulators; + size = ARRAY_SIZE(pfuze100_regulators); break; case PFUZE200: - desc = se_desc(pfuze200_regulators, - ARRAY_SIZE(pfuze200_regulators), - dev->name); + desc = pfuze200_regulators; + size = ARRAY_SIZE(pfuze200_regulators); break; case PFUZE3000: - desc = se_desc(pfuze3000_regulators, - ARRAY_SIZE(pfuze3000_regulators), - dev->name); + desc = pfuze3000_regulators; + size = ARRAY_SIZE(pfuze3000_regulators); break; default: debug("Unsupported PFUZE\n"); return -EINVAL; } - if (!desc) { + + for (i = 0; i < size; i++) { + if (strcmp(desc[i].name, dev->name)) + continue; + break; + } + + if (i == size) { debug("Do not support regulator %s\n", dev->name); return -EINVAL; } - plat->desc = desc; + plat->desc = &desc[i]; uc_pdata = dev_get_uclass_plat(dev); - uc_pdata->type = desc->type; + uc_pdata->type = desc[i].type; if (uc_pdata->type == REGULATOR_TYPE_BUCK) { if (!strcmp(dev->name, "swbst")) { uc_pdata->mode = pfuze_swbst_modes; -- cgit v1.2.3 From edc666f1cf721c12ebc2fc096d362f8b14004d3a Mon Sep 17 00:00:00 2001 From: Ricardo Simoes Date: Wed, 17 Sep 2025 17:59:32 +0200 Subject: clk: imx6q: Add definition for missing PWM clocks Following the work done in commit 7f39ad5a ("clk: imx6q: Add definition for IMX6QDL_CLK_PWM1"), this commit adds definitions for PWM2, PWM3, and PWM4 clocks. Allowing one to use these PWM modules together with DM_CLK. Note that the solution was verified only against PWM3. Signed-off-by: Ricardo Simoes Signed-off-by: Mark Jonas --- drivers/clk/imx/clk-imx6q.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index 13239f2f64d..b69355cefc7 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -156,6 +156,12 @@ static int imx6q_clk_probe(struct udevice *dev) imx_clk_gate2(dev, "i2c3", "ipg_per", base + 0x70, 10)); clk_dm(IMX6QDL_CLK_PWM1, imx_clk_gate2(dev, "pwm1", "ipg_per", base + 0x78, 16)); + clk_dm(IMX6QDL_CLK_PWM2, + imx_clk_gate2(dev, "pwm2", "ipg_per", base + 0x78, 18)); + clk_dm(IMX6QDL_CLK_PWM3, + imx_clk_gate2(dev, "pwm3", "ipg_per", base + 0x78, 20)); + clk_dm(IMX6QDL_CLK_PWM4, + imx_clk_gate2(dev, "pwm4", "ipg_per", base + 0x78, 22)); clk_dm(IMX6QDL_CLK_ENET, imx_clk_gate2(dev, "enet", "ipg", base + 0x6c, 10)); clk_dm(IMX6QDL_CLK_ENET_REF, -- cgit v1.2.3 From 6c98e6014b3a36933860d02cb757565ec2fc80a4 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Mon, 1 Sep 2025 16:00:46 +0100 Subject: power: regulator: Fix incorrect use of binary and In regulator_list_autoset there is a test for ret being non-zero and error being zero but it uses the binary '&' instead of the logical '&&' which could well lead to unexpected results. Correct this to use the logical '&&' instead. This issue found by Smatch. Signed-off-by: Andrew Goodbody Reviewed-by: Peng Fan Signed-off-by: Peng Fan --- drivers/power/regulator/regulator-uclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c index 2a59a1b79c2..94c52cf555b 100644 --- a/drivers/power/regulator/regulator-uclass.c +++ b/drivers/power/regulator/regulator-uclass.c @@ -389,7 +389,7 @@ int regulator_list_autoset(const char *list_platname[], ret = regulator_autoset_by_name(list_platname[i], &dev); if (ret != -EMEDIUMTYPE && verbose) regulator_show(dev, ret); - if (ret & !error) + if (ret && ret != -EALREADY && !error) error = ret; if (list_devp) -- cgit v1.2.3 From 3cabc6bf7e16c7e2a1156392c31e40f678cc7026 Mon Sep 17 00:00:00 2001 From: Bhimeswararao Matsa Date: Mon, 1 Sep 2025 19:14:10 +0530 Subject: mmc: core: style fixes in mmc.c Fix a couple of style issues reported by checkpatch.pl: - Replace `#ifdef CONFIG_MMC_TRACE` with `#if IS_ENABLED(CONFIG_MMC_TRACE)` to follow the preferred kernel style for config-dependent branches. - Drop explicit zero initialization of a static variable. No functional change intended. Signed-off-by: Bhimeswararao Matsa Signed-off-by: Peng Fan --- drivers/mmc/mmc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 20afcffde3d..b1cfa3cd7c2 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -104,8 +104,7 @@ __weak int board_mmc_getcd(struct mmc *mmc) return -1; } #endif - -#ifdef CONFIG_MMC_TRACE +#if IS_ENABLED(CONFIG_MMC_TRACE) void mmmc_trace_before_send(struct mmc *mmc, struct mmc_cmd *cmd) { printf("CMD_SEND:%d\n", cmd->cmdidx); @@ -3190,7 +3189,7 @@ static int mmc_probe(struct bd_info *bis) int mmc_initialize(struct bd_info *bis) { - static int initialized = 0; + static int initialized; int ret; if (initialized) /* Avoid initializing mmc multiple times */ return 0; -- cgit v1.2.3 From f83a9e5df4cbb792e4b1c3f826b05f3fe8249dcc Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Thu, 11 Sep 2025 15:50:18 -0600 Subject: usb: dwc3: Tighten driver glue dependencies A few of the platform specific DWC3 host glue drivers cannot build without access to some platform specific header files. Express those requirements in Kconfig as well. Reviewed-by: Marek Vasut Signed-off-by: Tom Rini --- drivers/usb/dwc3/Kconfig | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 682a6910655..744dfa90463 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -17,6 +17,7 @@ comment "Platform Glue Driver Support" config USB_DWC3_OMAP bool "Texas Instruments OMAP5 and similar Platforms" + depends on ARCH_OMAP2PLUS help Some platforms from Texas Instruments like OMAP5, DRA7xxx and AM437x use this IP for USB2/3 functionality. @@ -39,14 +40,14 @@ config SPL_USB_DWC3_GENERIC config SPL_USB_DWC3_AM62 bool "TI AM62 USB wrapper" - depends on SPL_DM_USB && SPL_USB_DWC3_GENERIC && SPL_SYSCON + depends on SPL_DM_USB && SPL_USB_DWC3_GENERIC && SPL_SYSCON && ARCH_K3 help Select this for TI AM62 Platforms. This wrapper supports Host and Peripheral operation modes. config USB_DWC3_AM62 bool "TI AM62 USB wrapper" - depends on DM_USB && USB_DWC3_GENERIC && SYSCON + depends on DM_USB && USB_DWC3_GENERIC && SYSCON && ARCH_K3 help Select this for TI AM62 Platforms. This wrapper supports Host and Peripheral operation modes. @@ -80,7 +81,7 @@ config USB_DWC3_UNIPHIER config USB_DWC3_LAYERSCAPE bool "Freescale Layerscape platform support" depends on DM_USB && USB_DWC3 - depends on !USB_XHCI_FSL + depends on !USB_XHCI_FSL && ARM help Select this for Freescale Layerscape Platforms. @@ -99,12 +100,14 @@ menu "PHY Subsystem" config USB_DWC3_PHY_OMAP bool "TI OMAP SoC series USB DRD PHY driver" + depends on ARCH_OMAP2PLUS help Enable single driver for both USB2 PHY programming and USB3 PHY programming for TI SoCs. config USB_DWC3_PHY_SAMSUNG bool "Exynos5 SoC series USB DRD PHY driver" + depends on ARCH_EXYNOS help Enable USB DRD PHY support for Exynos 5 SoC series. This driver provides PHY interface for USB 3.0 DRD controller -- cgit v1.2.3 From 70553ec85f0002900e817ba64f84ff818bc28755 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Thu, 11 Sep 2025 15:50:19 -0600 Subject: usb: gadget: Tighten requirements on USB_GADGET_ATMEL_USBA This driver requires some mach-at91 specific header files in order to build. Express that requirement in Kconfig as well. Reviewed-by: Marek Vasut Signed-off-by: Tom Rini --- drivers/usb/gadget/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 0121f9872ae..29c2f27fa05 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -93,6 +93,7 @@ config USB_GADGET_PRODUCT_NUM config USB_GADGET_ATMEL_USBA bool "Atmel USBA" + depends on ARCH_AT91 select USB_GADGET_DUALSPEED help USBA is the integrated high-speed USB Device controller on -- cgit v1.2.3 From 219751e8c7259cb6e86a564e70fdf6c967862d55 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Thu, 11 Sep 2025 15:50:20 -0600 Subject: usb: gadget: bcm_udc_otg: Remove unused driver This driver is unused since the removal of the bcm28155_ap board in commit 0f6807e77b07 ("arm: Remove bcm28155_ap board"). Remove it. Fixes: 0f6807e77b07 ("arm: Remove bcm28155_ap board") Signed-off-by: Tom Rini Reviewed-by: Marek Vasut --- drivers/usb/gadget/Kconfig | 5 ---- drivers/usb/gadget/Makefile | 1 - drivers/usb/gadget/bcm_udc_otg.h | 19 ------------- drivers/usb/gadget/bcm_udc_otg_phy.c | 54 ------------------------------------ 4 files changed, 79 deletions(-) delete mode 100644 drivers/usb/gadget/bcm_udc_otg.h delete mode 100644 drivers/usb/gadget/bcm_udc_otg_phy.c (limited to 'drivers') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 29c2f27fa05..c59d1d6252c 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -99,11 +99,6 @@ config USB_GADGET_ATMEL_USBA USBA is the integrated high-speed USB Device controller on the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel. -config USB_GADGET_BCM_UDC_OTG_PHY - bool "Broadcom UDC OTG PHY" - help - Enable the Broadcom UDC OTG physical device interface. - config USB_GADGET_AT91 bool "Atmel AT91 USB Gadget Controller" depends on ARCH_AT91 diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index db5f8895a33..7af5f6e6d63 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -17,7 +17,6 @@ endif ifdef CONFIG_USB_GADGET obj-$(CONFIG_USB_GADGET_AT91) += at91_udc.o obj-$(CONFIG_USB_GADGET_ATMEL_USBA) += atmel_usba_udc.o -obj-$(CONFIG_USB_GADGET_BCM_UDC_OTG_PHY) += bcm_udc_otg_phy.o obj-$(CONFIG_USB_GADGET_DWC2_OTG) += dwc2_udc_otg.o obj-$(CONFIG_USB_GADGET_DWC2_OTG_PHY) += dwc2_udc_otg_phy.o obj-$(CONFIG_USB_GADGET_MAX3420) += max3420_udc.o diff --git a/drivers/usb/gadget/bcm_udc_otg.h b/drivers/usb/gadget/bcm_udc_otg.h deleted file mode 100644 index 48370f37d8a..00000000000 --- a/drivers/usb/gadget/bcm_udc_otg.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright 2015 Broadcom Corporation. - */ - -#ifndef __BCM_UDC_OTG_H -#define __BCM_UDC_OTG_H - -static inline void wfld_set(uintptr_t addr, uint32_t fld_val, uint32_t fld_mask) -{ - writel(((readl(addr) & ~(fld_mask)) | (fld_val)), (addr)); -} - -static inline void wfld_clear(uintptr_t addr, uint32_t fld_mask) -{ - writel((readl(addr) & ~(fld_mask)), (addr)); -} - -#endif diff --git a/drivers/usb/gadget/bcm_udc_otg_phy.c b/drivers/usb/gadget/bcm_udc_otg_phy.c deleted file mode 100644 index 9875191091c..00000000000 --- a/drivers/usb/gadget/bcm_udc_otg_phy.c +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright 2015 Broadcom Corporation. - */ - -#include -#include -#include -#include -#include - -#include "dwc2_udc_otg_priv.h" -#include "bcm_udc_otg.h" - -void otg_phy_init(struct dwc2_udc *dev) -{ - /* turn on the USB OTG clocks */ - clk_usb_otg_enable((void *)HSOTG_BASE_ADDR); - - /* set Phy to driving mode */ - wfld_clear(HSOTG_CTRL_BASE_ADDR + HSOTG_CTRL_PHY_P1CTL_OFFSET, - HSOTG_CTRL_PHY_P1CTL_NON_DRIVING_MASK); - - udelay(100); - - /* clear Soft Disconnect */ - wfld_clear(HSOTG_BASE_ADDR + HSOTG_DCTL_OFFSET, - HSOTG_DCTL_SFTDISCON_MASK); - - /* invoke Reset (active low) */ - wfld_clear(HSOTG_CTRL_BASE_ADDR + HSOTG_CTRL_PHY_P1CTL_OFFSET, - HSOTG_CTRL_PHY_P1CTL_SOFT_RESET_MASK); - - /* Reset needs to be asserted for 2ms */ - udelay(2000); - - /* release Reset */ - wfld_set(HSOTG_CTRL_BASE_ADDR + HSOTG_CTRL_PHY_P1CTL_OFFSET, - HSOTG_CTRL_PHY_P1CTL_SOFT_RESET_MASK, - HSOTG_CTRL_PHY_P1CTL_SOFT_RESET_MASK); -} - -void otg_phy_off(struct dwc2_udc *dev) -{ - /* Soft Disconnect */ - wfld_set(HSOTG_BASE_ADDR + HSOTG_DCTL_OFFSET, - HSOTG_DCTL_SFTDISCON_MASK, - HSOTG_DCTL_SFTDISCON_MASK); - - /* set Phy to non-driving (reset) mode */ - wfld_set(HSOTG_CTRL_BASE_ADDR + HSOTG_CTRL_PHY_P1CTL_OFFSET, - HSOTG_CTRL_PHY_P1CTL_NON_DRIVING_MASK, - HSOTG_CTRL_PHY_P1CTL_NON_DRIVING_MASK); -} -- cgit v1.2.3 From 56edb1cc76c1de79b852d51fa79fb64142f1e901 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Thu, 11 Sep 2025 15:50:21 -0600 Subject: usb: gadget: max3420_udc: Remove unused driver This driver was never enabled by any platforms after being added to the tree over 5 years ago. Remove it. Signed-off-by: Tom Rini Reviewed-by: Marek Vasut --- drivers/usb/gadget/Kconfig | 6 - drivers/usb/gadget/Makefile | 1 - drivers/usb/gadget/max3420_udc.c | 879 --------------------------------------- 3 files changed, 886 deletions(-) delete mode 100644 drivers/usb/gadget/max3420_udc.c (limited to 'drivers') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index c59d1d6252c..e845e46e0b7 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -152,12 +152,6 @@ config CI_UDC Say Y here to enable device controller functionality of the ChipIdea driver. -config USB_GADGET_MAX3420 - bool "MAX3420 USB Over SPI" - depends on DM_SPI - help - MAX3420, from MAXIM, implements USB-over-SPI Full-Speed device controller. - config USB_GADGET_VBUS_DRAW int "Maximum VBUS Power usage (2-500 mA)" range 2 500 diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 7af5f6e6d63..f2aebf4e480 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -19,7 +19,6 @@ obj-$(CONFIG_USB_GADGET_AT91) += at91_udc.o obj-$(CONFIG_USB_GADGET_ATMEL_USBA) += atmel_usba_udc.o obj-$(CONFIG_USB_GADGET_DWC2_OTG) += dwc2_udc_otg.o obj-$(CONFIG_USB_GADGET_DWC2_OTG_PHY) += dwc2_udc_otg_phy.o -obj-$(CONFIG_USB_GADGET_MAX3420) += max3420_udc.o obj-$(CONFIG_USB_RENESAS_USBHS) += rcar/ ifndef CONFIG_XPL_BUILD obj-$(CONFIG_USB_GADGET_DOWNLOAD) += g_dnl.o diff --git a/drivers/usb/gadget/max3420_udc.c b/drivers/usb/gadget/max3420_udc.c deleted file mode 100644 index 557a1f0644e..00000000000 --- a/drivers/usb/gadget/max3420_udc.c +++ /dev/null @@ -1,879 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MAX3420_MAX_EPS 4 -#define EP_MAX_PACKET 64 /* Same for all Endpoints */ -#define EPNAME_SIZE 16 /* Buffer size for endpoint name */ - -#define MAX3420_SPI_DIR_RD 0 /* read register from MAX3420 */ -#define MAX3420_SPI_DIR_WR 1 /* write register to MAX3420 */ - -/* SPI commands: */ -#define MAX3420_SPI_ACK_MASK BIT(0) -#define MAX3420_SPI_DIR_MASK BIT(1) -#define MAX3420_SPI_REG_MASK GENMASK(7, 3) - -#define MAX3420_REG_EP0FIFO 0 -#define MAX3420_REG_EP1FIFO 1 -#define MAX3420_REG_EP2FIFO 2 -#define MAX3420_REG_EP3FIFO 3 -#define MAX3420_REG_SUDFIFO 4 -#define MAX3420_REG_EP0BC 5 -#define MAX3420_REG_EP1BC 6 -#define MAX3420_REG_EP2BC 7 -#define MAX3420_REG_EP3BC 8 - -#define MAX3420_REG_EPSTALLS 9 - #define bACKSTAT BIT(6) - #define bSTLSTAT BIT(5) - #define bSTLEP3IN BIT(4) - #define bSTLEP2IN BIT(3) - #define bSTLEP1OUT BIT(2) - #define bSTLEP0OUT BIT(1) - #define bSTLEP0IN BIT(0) - -#define MAX3420_REG_CLRTOGS 10 - #define bEP3DISAB BIT(7) - #define bEP2DISAB BIT(6) - #define bEP1DISAB BIT(5) - #define bCTGEP3IN BIT(4) - #define bCTGEP2IN BIT(3) - #define bCTGEP1OUT BIT(2) - -#define MAX3420_REG_EPIRQ 11 -#define MAX3420_REG_EPIEN 12 - #define bSUDAVIRQ BIT(5) - #define bIN3BAVIRQ BIT(4) - #define bIN2BAVIRQ BIT(3) - #define bOUT1DAVIRQ BIT(2) - #define bOUT0DAVIRQ BIT(1) - #define bIN0BAVIRQ BIT(0) - -#define MAX3420_REG_USBIRQ 13 -#define MAX3420_REG_USBIEN 14 - #define bOSCOKIRQ BIT(0) - #define bRWUDNIRQ BIT(1) - #define bBUSACTIRQ BIT(2) - #define bURESIRQ BIT(3) - #define bSUSPIRQ BIT(4) - #define bNOVBUSIRQ BIT(5) - #define bVBUSIRQ BIT(6) - #define bURESDNIRQ BIT(7) - -#define MAX3420_REG_USBCTL 15 - #define bHOSCSTEN BIT(7) - #define bVBGATE BIT(6) - #define bCHIPRES BIT(5) - #define bPWRDOWN BIT(4) - #define bCONNECT BIT(3) - #define bSIGRWU BIT(2) - -#define MAX3420_REG_CPUCTL 16 - #define bIE BIT(0) - -#define MAX3420_REG_PINCTL 17 - #define bEP3INAK BIT(7) - #define bEP2INAK BIT(6) - #define bEP0INAK BIT(5) - #define bFDUPSPI BIT(4) - #define bINTLEVEL BIT(3) - #define bPOSINT BIT(2) - #define bGPXB BIT(1) - #define bGPXA BIT(0) - -#define MAX3420_REG_REVISION 18 - -#define MAX3420_REG_FNADDR 19 - #define FNADDR_MASK 0x7f - -#define MAX3420_REG_IOPINS 20 -#define MAX3420_REG_IOPINS2 21 -#define MAX3420_REG_GPINIRQ 22 -#define MAX3420_REG_GPINIEN 23 -#define MAX3420_REG_GPINPOL 24 -#define MAX3420_REG_HIRQ 25 -#define MAX3420_REG_HIEN 26 -#define MAX3420_REG_MODE 27 -#define MAX3420_REG_PERADDR 28 -#define MAX3420_REG_HCTL 29 -#define MAX3420_REG_HXFR 30 -#define MAX3420_REG_HRSL 31 - -struct max3420_req { - struct usb_request usb_req; - struct list_head queue; - struct max3420_ep *ep; -}; - -struct max3420_ep { - struct max3420_udc *udc; - struct list_head queue; - char name[EPNAME_SIZE]; - unsigned int maxpacket; - struct usb_ep ep_usb; - int halted; - int id; -}; - -struct max3420_udc { - struct max3420_ep ep[MAX3420_MAX_EPS]; - struct usb_gadget_driver *driver; - bool softconnect; - struct usb_ctrlrequest setup; - struct max3420_req ep0req; - struct usb_gadget gadget; - struct spi_slave *slave; - struct udevice *dev; - u8 ep0buf[64]; - int remote_wkp; - bool suspended; -}; - -#define to_max3420_req(r) container_of((r), struct max3420_req, usb_req) -#define to_max3420_ep(e) container_of((e), struct max3420_ep, ep_usb) -#define to_udc(g) container_of((g), struct max3420_udc, gadget) - -static void spi_ack_ctrl(struct max3420_udc *udc) -{ - struct spi_slave *slave = udc->slave; - u8 txdata[1]; - - txdata[0] = FIELD_PREP(MAX3420_SPI_ACK_MASK, 1); - spi_xfer(slave, sizeof(txdata), txdata, NULL, SPI_XFER_ONCE); -} - -static u8 spi_rd8_ack(struct max3420_udc *udc, u8 reg, int ackstat) -{ - struct spi_slave *slave = udc->slave; - u8 txdata[2], rxdata[2]; - - txdata[0] = FIELD_PREP(MAX3420_SPI_REG_MASK, reg) | - FIELD_PREP(MAX3420_SPI_DIR_MASK, MAX3420_SPI_DIR_RD) | - FIELD_PREP(MAX3420_SPI_ACK_MASK, ackstat ? 1 : 0); - - rxdata[0] = 0; - rxdata[1] = 0; - spi_xfer(slave, sizeof(txdata), txdata, rxdata, SPI_XFER_ONCE); - - return rxdata[1]; -} - -static u8 spi_rd8(struct max3420_udc *udc, u8 reg) -{ - return spi_rd8_ack(udc, reg, 0); -} - -static void spi_wr8_ack(struct max3420_udc *udc, u8 reg, u8 val, int ackstat) -{ - struct spi_slave *slave = udc->slave; - u8 txdata[2]; - - txdata[0] = FIELD_PREP(MAX3420_SPI_REG_MASK, reg) | - FIELD_PREP(MAX3420_SPI_DIR_MASK, MAX3420_SPI_DIR_WR) | - FIELD_PREP(MAX3420_SPI_ACK_MASK, ackstat ? 1 : 0); - txdata[1] = val; - - spi_xfer(slave, sizeof(txdata), txdata, NULL, SPI_XFER_ONCE); -} - -static void spi_wr8(struct max3420_udc *udc, u8 reg, u8 val) -{ - spi_wr8_ack(udc, reg, val, 0); -} - -static void spi_rd_buf(struct max3420_udc *udc, u8 reg, void *buf, u8 len) -{ - struct spi_slave *slave = udc->slave; - u8 txdata[1]; - - txdata[0] = FIELD_PREP(MAX3420_SPI_REG_MASK, reg) | - FIELD_PREP(MAX3420_SPI_DIR_MASK, MAX3420_SPI_DIR_RD); - - spi_xfer(slave, sizeof(txdata), txdata, NULL, SPI_XFER_BEGIN); - spi_xfer(slave, len * 8, NULL, buf, SPI_XFER_END); -} - -static void spi_wr_buf(struct max3420_udc *udc, u8 reg, void *buf, u8 len) -{ - struct spi_slave *slave = udc->slave; - u8 txdata[1]; - - txdata[0] = FIELD_PREP(MAX3420_SPI_REG_MASK, reg) | - FIELD_PREP(MAX3420_SPI_DIR_MASK, MAX3420_SPI_DIR_WR); - - spi_xfer(slave, sizeof(txdata), txdata, NULL, SPI_XFER_BEGIN); - spi_xfer(slave, len * 8, buf, NULL, SPI_XFER_END); -} - -/* 0 if not-connected */ -int g_dnl_board_usb_cable_connected(void) -{ - return 1; -} - -static void spi_max3420_enable(struct max3420_ep *ep, int enable) -{ - struct max3420_udc *udc = ep->udc; - u8 epdis, epien; - - if (ep->id == 0) - return; - - epien = spi_rd8(udc, MAX3420_REG_EPIEN); - epdis = spi_rd8(udc, MAX3420_REG_CLRTOGS); - - if (enable) { - epdis &= ~BIT(ep->id + 4); - epien |= BIT(ep->id + 1); - } else { - epdis |= BIT(ep->id + 4); - epien &= ~BIT(ep->id + 1); - } - - spi_wr8(udc, MAX3420_REG_CLRTOGS, epdis); - spi_wr8(udc, MAX3420_REG_EPIEN, epien); -} - -static int -max3420_ep_enable(struct usb_ep *_ep, - const struct usb_endpoint_descriptor *desc) -{ - struct max3420_ep *ep = to_max3420_ep(_ep); - - _ep->desc = desc; - _ep->maxpacket = usb_endpoint_maxp(desc) & 0x7ff; - - spi_max3420_enable(ep, 1); - - return 0; -} - -static void max3420_req_done(struct max3420_req *req, int status) -{ - struct max3420_ep *ep = req->ep; - - if (req->usb_req.status == -EINPROGRESS) - req->usb_req.status = status; - else - status = req->usb_req.status; - - if (status && status != -ESHUTDOWN) - dev_err(ep->udc->dev, "%s done %p, status %d\n", - ep->ep_usb.name, req, status); - - if (req->usb_req.complete) - req->usb_req.complete(&ep->ep_usb, &req->usb_req); -} - -static void max3420_ep_nuke(struct max3420_ep *ep, int status) -{ - struct max3420_req *req, *r; - - list_for_each_entry_safe(req, r, &ep->queue, queue) { - list_del_init(&req->queue); - max3420_req_done(req, status); - } -} - -static int max3420_ep_disable(struct usb_ep *_ep) -{ - struct max3420_ep *ep = to_max3420_ep(_ep); - - _ep->desc = NULL; - max3420_ep_nuke(ep, -ESHUTDOWN); - spi_max3420_enable(ep, 0); - - return 0; -} - -static struct usb_request * -max3420_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) -{ - struct max3420_ep *ep = to_max3420_ep(_ep); - struct max3420_req *req = kzalloc(sizeof(*req), gfp_flags); - - if (!req) - return NULL; - - req->ep = ep; - INIT_LIST_HEAD(&req->queue); - - return &req->usb_req; -} - -static void -max3420_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) -{ - kfree(to_max3420_req(_req)); -} - -static int -max3420_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) -{ - struct max3420_req *req = to_max3420_req(_req); - struct max3420_ep *ep = to_max3420_ep(_ep); - - _req->status = -EINPROGRESS; - _req->actual = 0; - list_add_tail(&req->queue, &ep->queue); - - return 0; -} - -static int max3420_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) -{ - struct max3420_req *req = to_max3420_req(_req); - - list_del_init(&req->queue); - max3420_req_done(req, -ECONNRESET); - - return 0; -} - -static int max3420_ep_set_halt(struct usb_ep *_ep, int halt) -{ - struct max3420_ep *ep = to_max3420_ep(_ep); - struct max3420_udc *udc = ep->udc; - u8 epstalls; - - if (ep->id == 0) /* can't stall EP0 */ - return 0; - - epstalls = spi_rd8(udc, MAX3420_REG_EPSTALLS); - if (halt) { - ep->halted = 1; - epstalls |= BIT(ep->id + 1); - } else { - u8 clrtogs; - - ep->halted = 0; - epstalls &= ~BIT(ep->id + 1); - clrtogs = spi_rd8(udc, MAX3420_REG_CLRTOGS); - clrtogs |= BIT(ep->id + 1); - spi_wr8(udc, MAX3420_REG_CLRTOGS, clrtogs); - } - spi_wr8(udc, MAX3420_REG_EPSTALLS, epstalls | bACKSTAT); - - return 0; -} - -static const struct usb_ep_ops max3420_ep_ops = { - .enable = max3420_ep_enable, - .disable = max3420_ep_disable, - .alloc_request = max3420_ep_alloc_request, - .free_request = max3420_ep_free_request, - .queue = max3420_ep_queue, - .dequeue = max3420_ep_dequeue, - .set_halt = max3420_ep_set_halt, -}; - -static void __max3420_stop(struct max3420_udc *udc) -{ - u8 val; - - /* Disable IRQ to CPU */ - spi_wr8(udc, MAX3420_REG_CPUCTL, 0); - - val = spi_rd8(udc, MAX3420_REG_USBCTL); - val |= bPWRDOWN; - val |= bHOSCSTEN; - spi_wr8(udc, MAX3420_REG_USBCTL, val); -} - -static void __max3420_start(struct max3420_udc *udc) -{ - u8 val; - - /* configure SPI */ - spi_wr8(udc, MAX3420_REG_PINCTL, bFDUPSPI); - - /* Chip Reset */ - spi_wr8(udc, MAX3420_REG_USBCTL, bCHIPRES); - mdelay(5); - spi_wr8(udc, MAX3420_REG_USBCTL, 0); - - /* Poll for OSC to stabilize */ - while (1) { - val = spi_rd8(udc, MAX3420_REG_USBIRQ); - if (val & bOSCOKIRQ) - break; - cond_resched(); - } - - /* Enable PULL-UP only when Vbus detected */ - val = spi_rd8(udc, MAX3420_REG_USBCTL); - val |= bVBGATE | bCONNECT; - spi_wr8(udc, MAX3420_REG_USBCTL, val); - - val = bURESDNIRQ | bURESIRQ; - spi_wr8(udc, MAX3420_REG_USBIEN, val); - - /* Enable only EP0 interrupts */ - val = bIN0BAVIRQ | bOUT0DAVIRQ | bSUDAVIRQ; - spi_wr8(udc, MAX3420_REG_EPIEN, val); - - /* Enable IRQ to CPU */ - spi_wr8(udc, MAX3420_REG_CPUCTL, bIE); -} - -static int max3420_udc_start(struct usb_gadget *gadget, - struct usb_gadget_driver *driver) -{ - struct max3420_udc *udc = to_udc(gadget); - - udc->driver = driver; - udc->remote_wkp = 0; - udc->softconnect = true; - - __max3420_start(udc); - - return 0; -} - -static int max3420_udc_stop(struct usb_gadget *gadget) -{ - struct max3420_udc *udc = to_udc(gadget); - - udc->driver = NULL; - udc->softconnect = false; - - __max3420_stop(udc); - - return 0; -} - -static int max3420_wakeup(struct usb_gadget *gadget) -{ - struct max3420_udc *udc = to_udc(gadget); - u8 usbctl; - - /* Only if wakeup allowed by host */ - if (!udc->remote_wkp || !udc->suspended) - return 0; - - /* Set Remote-Wakeup Signal*/ - usbctl = spi_rd8(udc, MAX3420_REG_USBCTL); - usbctl |= bSIGRWU; - spi_wr8(udc, MAX3420_REG_USBCTL, usbctl); - - mdelay(5); - - /* Clear Remote-WkUp Signal*/ - usbctl = spi_rd8(udc, MAX3420_REG_USBCTL); - usbctl &= ~bSIGRWU; - spi_wr8(udc, MAX3420_REG_USBCTL, usbctl); - - udc->suspended = false; - - return 0; -} - -static const struct usb_gadget_ops max3420_udc_ops = { - .udc_start = max3420_udc_start, - .udc_stop = max3420_udc_stop, - .wakeup = max3420_wakeup, -}; - -static struct usb_endpoint_descriptor ep0_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_CONTROL, - .wMaxPacketSize = cpu_to_le16(EP_MAX_PACKET), -}; - -static void max3420_getstatus(struct max3420_udc *udc) -{ - struct max3420_ep *ep; - u16 status = 0; - - switch (udc->setup.bRequestType & USB_RECIP_MASK) { - case USB_RECIP_DEVICE: - /* Get device status */ - status = 0 << USB_DEVICE_SELF_POWERED; - status |= (udc->remote_wkp << USB_DEVICE_REMOTE_WAKEUP); - break; - case USB_RECIP_INTERFACE: - if (udc->driver->setup(&udc->gadget, &udc->setup) < 0) - goto stall; - break; - case USB_RECIP_ENDPOINT: - ep = &udc->ep[udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK]; - if (ep->halted) - status = 1 << USB_ENDPOINT_HALT; - break; - default: - goto stall; - } - - status = cpu_to_le16(status); - spi_wr_buf(udc, MAX3420_REG_EP0FIFO, &status, 2); - spi_wr8_ack(udc, MAX3420_REG_EP0BC, 2, 1); - return; -stall: - dev_err(udc->dev, "Can't respond to getstatus request\n"); - spi_wr8(udc, MAX3420_REG_EPSTALLS, bSTLEP0IN | bSTLEP0OUT | bSTLSTAT); -} - -static void max3420_set_clear_feature(struct max3420_udc *udc) -{ - int set = udc->setup.bRequest == USB_REQ_SET_FEATURE; - struct max3420_ep *ep; - int id; - - switch (udc->setup.bRequestType) { - case USB_RECIP_DEVICE: - if (udc->setup.wValue != USB_DEVICE_REMOTE_WAKEUP) - break; - - if (udc->setup.bRequest == USB_REQ_SET_FEATURE) - udc->remote_wkp = 1; - else - udc->remote_wkp = 0; - - return spi_ack_ctrl(udc); - - case USB_RECIP_ENDPOINT: - if (udc->setup.wValue != USB_ENDPOINT_HALT) - break; - - id = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK; - ep = &udc->ep[id]; - - max3420_ep_set_halt(&ep->ep_usb, set); - return; - default: - break; - } - - dev_err(udc->dev, "Can't respond to SET/CLEAR FEATURE\n"); - spi_wr8(udc, MAX3420_REG_EPSTALLS, bSTLEP0IN | bSTLEP0OUT | bSTLSTAT); -} - -static void max3420_handle_setup(struct max3420_udc *udc) -{ - struct usb_ctrlrequest setup; - u8 addr; - - spi_rd_buf(udc, MAX3420_REG_SUDFIFO, (void *)&setup, 8); - - udc->setup = setup; - udc->setup.wValue = cpu_to_le16(setup.wValue); - udc->setup.wIndex = cpu_to_le16(setup.wIndex); - udc->setup.wLength = cpu_to_le16(setup.wLength); - - switch (udc->setup.bRequest) { - case USB_REQ_GET_STATUS: - /* Data+Status phase form udc */ - if ((udc->setup.bRequestType & - (USB_DIR_IN | USB_TYPE_MASK)) != - (USB_DIR_IN | USB_TYPE_STANDARD)) { - break; - } - return max3420_getstatus(udc); - case USB_REQ_SET_ADDRESS: - /* Status phase from udc */ - if (udc->setup.bRequestType != (USB_DIR_OUT | - USB_TYPE_STANDARD | USB_RECIP_DEVICE)) - break; - addr = spi_rd8_ack(udc, MAX3420_REG_FNADDR, 1); - dev_dbg(udc->dev, "Assigned Address=%d/%d\n", - udc->setup.wValue, addr); - return; - case USB_REQ_CLEAR_FEATURE: - case USB_REQ_SET_FEATURE: - /* Requests with no data phase, status phase from udc */ - if ((udc->setup.bRequestType & USB_TYPE_MASK) - != USB_TYPE_STANDARD) - break; - return max3420_set_clear_feature(udc); - default: - break; - } - - if (udc->driver->setup(&udc->gadget, &setup) < 0) { - /* Stall EP0 */ - spi_wr8(udc, MAX3420_REG_EPSTALLS, - bSTLEP0IN | bSTLEP0OUT | bSTLSTAT); - } -} - -static int do_data(struct max3420_udc *udc, int ep_id, int in) -{ - struct max3420_ep *ep = &udc->ep[ep_id]; - struct max3420_req *req; - int done, length, psz; - void *buf; - - if (list_empty(&ep->queue)) - return 0; - - req = list_first_entry(&ep->queue, struct max3420_req, queue); - buf = req->usb_req.buf + req->usb_req.actual; - - psz = ep->ep_usb.maxpacket; - length = req->usb_req.length - req->usb_req.actual; - length = min(length, psz); - - if (length == 0) { - done = 1; - goto xfer_done; - } - - done = 0; - if (in) { - spi_wr_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, length); - spi_wr8(udc, MAX3420_REG_EP0BC + ep_id, length); - if (length < psz) - done = 1; - } else { - psz = spi_rd8(udc, MAX3420_REG_EP0BC + ep_id); - length = min(length, psz); - spi_rd_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, length); - if (length < ep->ep_usb.maxpacket) - done = 1; - } - - req->usb_req.actual += length; - - if (req->usb_req.actual == req->usb_req.length) - done = 1; - -xfer_done: - if (done) { - list_del_init(&req->queue); - - if (ep_id == 0) - spi_ack_ctrl(udc); - - max3420_req_done(req, 0); - } - - return 1; -} - -static int max3420_handle_irqs(struct max3420_udc *udc) -{ - u8 epien, epirq, usbirq, usbien, reg[4]; - int ret = 0; - - spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 4); - epirq = reg[0]; - epien = reg[1]; - usbirq = reg[2]; - usbien = reg[3]; - - usbirq &= usbien; - epirq &= epien; - - if (epirq & bSUDAVIRQ) { - spi_wr8(udc, MAX3420_REG_EPIRQ, bSUDAVIRQ); - max3420_handle_setup(udc); - return 1; - } - - if (usbirq & bVBUSIRQ) { - spi_wr8(udc, MAX3420_REG_USBIRQ, bVBUSIRQ); - dev_dbg(udc->dev, "Cable plugged in\n"); - g_dnl_clear_detach(); - return 1; - } - - if (usbirq & bNOVBUSIRQ) { - spi_wr8(udc, MAX3420_REG_USBIRQ, bNOVBUSIRQ); - dev_dbg(udc->dev, "Cable pulled out\n"); - g_dnl_trigger_detach(); - return 1; - } - - if (usbirq & bURESIRQ) { - spi_wr8(udc, MAX3420_REG_USBIRQ, bURESIRQ); - return 1; - } - - if (usbirq & bURESDNIRQ) { - spi_wr8(udc, MAX3420_REG_USBIRQ, bURESDNIRQ); - spi_wr8(udc, MAX3420_REG_USBIEN, bURESDNIRQ | bURESIRQ); - spi_wr8(udc, MAX3420_REG_EPIEN, bSUDAVIRQ - | bIN0BAVIRQ | bOUT0DAVIRQ); - return 1; - } - - if (usbirq & bSUSPIRQ) { - spi_wr8(udc, MAX3420_REG_USBIRQ, bSUSPIRQ); - dev_dbg(udc->dev, "USB Suspend - Enter\n"); - udc->suspended = true; - return 1; - } - - if (usbirq & bBUSACTIRQ) { - spi_wr8(udc, MAX3420_REG_USBIRQ, bBUSACTIRQ); - dev_dbg(udc->dev, "USB Suspend - Exit\n"); - udc->suspended = false; - return 1; - } - - if (usbirq & bRWUDNIRQ) { - spi_wr8(udc, MAX3420_REG_USBIRQ, bRWUDNIRQ); - dev_dbg(udc->dev, "Asked Host to wakeup\n"); - return 1; - } - - if (usbirq & bOSCOKIRQ) { - spi_wr8(udc, MAX3420_REG_USBIRQ, bOSCOKIRQ); - dev_dbg(udc->dev, "Osc stabilized, start work\n"); - return 1; - } - - if (epirq & bOUT0DAVIRQ && do_data(udc, 0, 0)) { - spi_wr8_ack(udc, MAX3420_REG_EPIRQ, bOUT0DAVIRQ, 1); - ret = 1; - } - - if (epirq & bIN0BAVIRQ && do_data(udc, 0, 1)) - ret = 1; - - if (epirq & bOUT1DAVIRQ && do_data(udc, 1, 0)) { - spi_wr8_ack(udc, MAX3420_REG_EPIRQ, bOUT1DAVIRQ, 1); - ret = 1; - } - - if (epirq & bIN2BAVIRQ && do_data(udc, 2, 1)) - ret = 1; - - if (epirq & bIN3BAVIRQ && do_data(udc, 3, 1)) - ret = 1; - - return ret; -} - -static int max3420_irq(struct max3420_udc *udc) -{ - do_data(udc, 0, 1); /* get done with the EP0 ZLP */ - - return max3420_handle_irqs(udc); -} - -static void max3420_setup_eps(struct max3420_udc *udc) -{ - int i; - - INIT_LIST_HEAD(&udc->gadget.ep_list); - INIT_LIST_HEAD(&udc->ep[0].ep_usb.ep_list); - - for (i = 0; i < MAX3420_MAX_EPS; i++) { - struct max3420_ep *ep = &udc->ep[i]; - - INIT_LIST_HEAD(&ep->queue); - - ep->id = i; - ep->udc = udc; - ep->ep_usb.ops = &max3420_ep_ops; - ep->ep_usb.name = ep->name; - ep->ep_usb.maxpacket = EP_MAX_PACKET; - - if (i == 0) { - ep->ep_usb.desc = &ep0_desc; - snprintf(ep->name, EPNAME_SIZE, "ep0"); - continue; - } - - list_add_tail(&ep->ep_usb.ep_list, &udc->gadget.ep_list); - - if (i == 1) - snprintf(ep->name, EPNAME_SIZE, "ep1out-bulk"); - else - snprintf(ep->name, EPNAME_SIZE, "ep%din-bulk", i); - }; -} - -static void max3420_setup_spi(struct max3420_udc *udc) -{ - u8 reg[8]; - - spi_claim_bus(udc->slave); - spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 8); - /* configure SPI */ - spi_wr8(udc, MAX3420_REG_PINCTL, bFDUPSPI); -} - -static int max3420_udc_probe(struct udevice *dev) -{ - struct max3420_udc *udc = dev_get_priv(dev); - struct dm_spi_slave_plat *slave_pdata; - struct udevice *bus = dev->parent; - int busnum = dev_seq(bus); - unsigned int cs; - uint speed, mode; - struct udevice *spid; - - slave_pdata = dev_get_parent_plat(dev); - cs = slave_pdata->cs; - speed = slave_pdata->max_hz; - mode = slave_pdata->mode; - _spi_get_bus_and_cs(busnum, cs, speed, mode, false, "spi_generic_drv", - NULL, &spid, &udc->slave); - - udc->dev = dev; - udc->gadget.ep0 = &udc->ep[0].ep_usb; - udc->gadget.max_speed = USB_SPEED_FULL; - udc->gadget.speed = USB_SPEED_FULL; - udc->gadget.is_dualspeed = 0; - udc->gadget.ops = &max3420_udc_ops; - udc->gadget.name = "max3420-udc"; - - max3420_setup_eps(udc); - max3420_setup_spi(udc); - - usb_add_gadget_udc((struct device *)dev, &udc->gadget); - - return 0; -} - -static int max3420_udc_remove(struct udevice *dev) -{ - struct max3420_udc *udc = dev_get_priv(dev); - - usb_del_gadget_udc(&udc->gadget); - - spi_release_bus(udc->slave); - - return 0; -} - -static int max3420_gadget_handle_interrupts(struct udevice *dev) -{ - struct max3420_udc *udc = dev_get_priv(dev); - - return max3420_irq(udc); -} - -static const struct usb_gadget_generic_ops max3420_gadget_ops = { - .handle_interrupts = max3420_gadget_handle_interrupts, -}; - -static const struct udevice_id max3420_ids[] = { - { .compatible = "maxim,max3421-udc" }, - { } -}; - -U_BOOT_DRIVER(max3420_generic_udc) = { - .name = "max3420-udc", - .id = UCLASS_USB_GADGET_GENERIC, - .of_match = max3420_ids, - .ops = &max3420_gadget_ops, - .probe = max3420_udc_probe, - .remove = max3420_udc_remove, - .priv_auto = sizeof(struct max3420_udc), -}; -- cgit v1.2.3 From be8c07b606b84b5500971d8e518e80152588bd54 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Thu, 11 Sep 2025 15:50:22 -0600 Subject: usb: host: Tighten USB host driver dependencies A few of the USB host drivers cannot build without access to some platform specific header files. Express those requirements in Kconfig as well. Reviewed-by: Marek Vasut Signed-off-by: Tom Rini --- drivers/usb/host/Kconfig | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 5c9e8fc9d15..2cf687fc4f3 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -121,8 +121,9 @@ config USB_XHCI_DRA7XX_INDEX config USB_XHCI_FSL bool "Support for NXP Layerscape on-chip xHCI USB controller" - default y if ARCH_LS1021A || FSL_LSCH3 || FSL_LSCH2 + depends on ARCH_LS1021A || FSL_LSCH3 || FSL_LSCH2 depends on !SPL_NO_USB + default y help Enables support for the on-chip xHCI controller on NXP Layerscape SoCs. @@ -286,7 +287,8 @@ config USB_EHCI_TEGRA config USB_EHCI_ZYNQ bool "Support for Xilinx Zynq on-chip EHCI USB controller" - default y if ARCH_ZYNQ + depends on ARCH_ZYNQ + default y select USB_EHCI_IS_TDI ---help--- Enable support for Zynq on-chip EHCI USB controller @@ -303,6 +305,7 @@ config EHCI_HCD_INIT_AFTER_RESET config USB_EHCI_FSL bool "Support for FSL on-chip EHCI USB controller" + depends on PPC select EHCI_HCD_INIT_AFTER_RESET select SYS_FSL_USB_INTERNAL_UTMI_PHY if MPC85xx && \ !(ARCH_B4860 || ARCH_B4420 || ARCH_P4080 || ARCH_P1020 || ARCH_P2020) @@ -362,6 +365,7 @@ config USB_OHCI_GENERIC config USB_OHCI_DA8XX bool "Support for da850 OHCI USB controller" + depends on ARCH_DAVINCI help Enable support for the da850 USB controller. -- cgit v1.2.3 From 3285584429ba09df602011784f40e2c2d59a26b9 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Thu, 11 Sep 2025 15:50:23 -0600 Subject: usb: host: Tighten USB musb-new host glue driver dependencies A few of the USB musb-new host glue drivers cannot build without access to some platform specific header files. Express those requirements in Kconfig as well. Reviewed-by: Marek Vasut Signed-off-by: Tom Rini --- drivers/usb/musb-new/Kconfig | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/musb-new/Kconfig b/drivers/usb/musb-new/Kconfig index ad9072a5327..0e2b2b5fb85 100644 --- a/drivers/usb/musb-new/Kconfig +++ b/drivers/usb/musb-new/Kconfig @@ -52,9 +52,11 @@ config USB_MUSB_OMAP2PLUS config USB_MUSB_AM35X bool "AM35x" + depends on ARCH_OMAP2PLUS config USB_MUSB_DSPS bool "TI DSPS platforms" + depends on ARCH_OMAP2PLUS config USB_MUSB_MT85XX bool "Enable Mediatek MT85XX DRC USB controller" -- cgit v1.2.3 From a2d62d3b1d357dccc2e9ed56a4e7bdbb030ca06f Mon Sep 17 00:00:00 2001 From: Alice Guo Date: Tue, 23 Sep 2025 10:14:54 +0800 Subject: cpu: imx94: Add support for i.MX94 in get_imx_type_str() Add a case for i.MX94 to return the correct string identifier in the get_imx_type_str() function. This ensures proper CPU type reporting for i.MX94 platforms. Signed-off-by: Alice Guo --- drivers/cpu/imx8_cpu.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/cpu/imx8_cpu.c b/drivers/cpu/imx8_cpu.c index 950630453f9..630919a3642 100644 --- a/drivers/cpu/imx8_cpu.c +++ b/drivers/cpu/imx8_cpu.c @@ -113,6 +113,8 @@ static const char *get_imx_type_str(u32 imxtype) return "91(01)";/* iMX91 9x9 Specific feature */ case MXC_CPU_IMX95: return "95"; + case MXC_CPU_IMX94: + return "94"; default: return "??"; } -- cgit v1.2.3 From 3661d4dc6d81bcf27887c553f400fd4bcdeb2ad2 Mon Sep 17 00:00:00 2001 From: Alice Guo Date: Tue, 23 Sep 2025 10:14:59 +0800 Subject: pinctrl: nxp: Add i.MX94 daisy register offset Define the daisy register offset for i.MX94 at 0x608 within the iomuxc register space. This enables correct pad selection for daisy chain configuration on i.MX94 platforms. Signed-off-by: Jacky Bai Signed-off-by: Alice Guo Acked-by: Peng Fan --- drivers/pinctrl/nxp/pinctrl-imx-scmi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/nxp/pinctrl-imx-scmi.c b/drivers/pinctrl/nxp/pinctrl-imx-scmi.c index aed47be337d..781835c6852 100644 --- a/drivers/pinctrl/nxp/pinctrl-imx-scmi.c +++ b/drivers/pinctrl/nxp/pinctrl-imx-scmi.c @@ -16,6 +16,7 @@ #include "pinctrl-imx.h" #define DAISY_OFFSET_IMX95 0x408 +#define DAISY_OFFSET_IMX94 0x608 /* SCMI pin control types */ #define PINCTRL_TYPE_MUX 192 @@ -133,6 +134,8 @@ static int imx_scmi_pinctrl_probe(struct udevice *dev) if (IS_ENABLED(CONFIG_IMX95)) priv->daisy_offset = DAISY_OFFSET_IMX95; + else if (IS_ENABLED(CONFIG_IMX94)) + priv->daisy_offset = DAISY_OFFSET_IMX94; else return -EINVAL; @@ -141,7 +144,7 @@ static int imx_scmi_pinctrl_probe(struct udevice *dev) static int imx_scmi_pinctrl_bind(struct udevice *dev) { - if (IS_ENABLED(CONFIG_IMX95)) + if (IS_ENABLED(CONFIG_IMX95) || IS_ENABLED(CONFIG_IMX94)) return 0; return -ENODEV; -- cgit v1.2.3 From c320edc9c09a46db5a7815d157ff412f444274b8 Mon Sep 17 00:00:00 2001 From: Ye Li Date: Wed, 24 Sep 2025 10:11:24 +0800 Subject: spi: nxp_fspi: Use second last LUT entry for AHB read Use a dedicated LUT (second last) for AHB read command, so we can directly read from the AHB memory-mapped address and booting M core for XIP on Flexspi NOR. Signed-off-by: Ye Li --- drivers/spi/nxp_fspi.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/nxp_fspi.c b/drivers/spi/nxp_fspi.c index 6d97b8eefc9..2f9abdc7795 100644 --- a/drivers/spi/nxp_fspi.c +++ b/drivers/spi/nxp_fspi.c @@ -539,6 +539,15 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f, fspi_writel(f, lutval[i], base + target_lut_reg); } + if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_IN && + op->addr.nbytes) { + lut_offset = (f->devtype_data->lut_num - 2) * 4 * 4; + for (i = 0; i < ARRAY_SIZE(lutval); i++) { + target_lut_reg = FSPI_LUT_BASE + lut_offset + i * 4; + fspi_writel(f, lutval[i], base + target_lut_reg); + } + } + dev_dbg(f->dev, "CMD[%x] lutval[0:%x \t 1:%x \t 2:%x \t 3:%x], size: 0x%08x\n", op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3], op->data.nbytes); @@ -943,9 +952,10 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f) /* * The driver only uses one single LUT entry, that is updated on * each call of exec_op(). Index 0 is preset at boot with a basic - * read operation, so let's use the last entry. + * read operation, last entry is used for dynamic lut, the second + * last entry is used for AHB read. */ - seqid_lut = f->devtype_data->lut_num - 1; + seqid_lut = f->devtype_data->lut_num - 2; /* AHB Read - Set lut sequence ID for all CS. */ fspi_writel(f, seqid_lut, base + FSPI_FLSHA1CR2); fspi_writel(f, seqid_lut, base + FSPI_FLSHA2CR2); -- cgit v1.2.3 From 2866c332180fa8f36a03c41943b45d80c6992f15 Mon Sep 17 00:00:00 2001 From: Ye Li Date: Wed, 24 Sep 2025 10:11:25 +0800 Subject: spi: nxp_fspi: Support i.MX8QXP flexspi Add the compatible string and driver data for i.MX8QXP. Signed-off-by: Ye Li --- drivers/spi/nxp_fspi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/nxp_fspi.c b/drivers/spi/nxp_fspi.c index 2f9abdc7795..6e2aee4672b 100644 --- a/drivers/spi/nxp_fspi.c +++ b/drivers/spi/nxp_fspi.c @@ -337,6 +337,15 @@ static struct nxp_fspi_devtype_data imxrt1170_data = { .little_endian = true, }; +static const struct nxp_fspi_devtype_data imx8qxp_data = { + .rxfifo = SZ_512, /* (64 * 64 bits) */ + .txfifo = SZ_1K, /* (128 * 64 bits) */ + .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ + .quirks = 0, + .lut_num = 32, + .little_endian = true, /* little-endian */ +}; + struct nxp_fspi { struct udevice *dev; void __iomem *iobase; @@ -1081,6 +1090,7 @@ static const struct udevice_id nxp_fspi_ids[] = { { .compatible = "nxp,lx2160a-fspi", .data = (ulong)&lx2160a_data, }, { .compatible = "nxp,imx8mm-fspi", .data = (ulong)&imx8mm_data, }, { .compatible = "nxp,imx8mp-fspi", .data = (ulong)&imx8mm_data, }, + { .compatible = "nxp,imx8qxp-fspi", .data = (ulong)&imx8qxp_data, }, { .compatible = "nxp,imxrt1170-fspi", .data = (ulong)&imxrt1170_data, }, { } }; -- cgit v1.2.3 From 8e8fdb6681b916b9db769fb8e082e1160e2801f4 Mon Sep 17 00:00:00 2001 From: Ye Li Date: Wed, 24 Sep 2025 10:11:26 +0800 Subject: spi: nxp_fspi: Support i.MX8DXL flexspi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to i.MX8DXL A1 errata ERR050601, concurrent read accesses from the A35 cores to the peripherals within the LSIO subsystem (region 0_5DXX_XXXX) and address spaces in the regions [0_0000_0000 – 0_1BFF_FFFF] and [4_0000_0000 – 4_3FFF_FFFF] can collide and cause data corruption in the returned data, with no failure report. Even a single A35 core accessing both these regions can trigger the issue because an A35 core can have more than one parallel read operation in progress. The flexspi0 AHB memory is in LSIO region mentioned in above errata. So we can't use AHB read, only can read data from FIFO. Add the compatible string for 8DXL and use a flag for the IPS read. Signed-off-by: Ye Li --- drivers/spi/nxp_fspi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/nxp_fspi.c b/drivers/spi/nxp_fspi.c index 6e2aee4672b..3dfb54b1968 100644 --- a/drivers/spi/nxp_fspi.c +++ b/drivers/spi/nxp_fspi.c @@ -346,6 +346,15 @@ static const struct nxp_fspi_devtype_data imx8qxp_data = { .little_endian = true, /* little-endian */ }; +static const struct nxp_fspi_devtype_data imx8dxl_data = { + .rxfifo = SZ_512, /* (64 * 64 bits) */ + .txfifo = SZ_1K, /* (128 * 64 bits) */ + .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ + .quirks = FSPI_QUIRK_USE_IP_ONLY, + .lut_num = 32, + .little_endian = true, /* little-endian */ +}; + struct nxp_fspi { struct udevice *dev; void __iomem *iobase; @@ -1091,6 +1100,7 @@ static const struct udevice_id nxp_fspi_ids[] = { { .compatible = "nxp,imx8mm-fspi", .data = (ulong)&imx8mm_data, }, { .compatible = "nxp,imx8mp-fspi", .data = (ulong)&imx8mm_data, }, { .compatible = "nxp,imx8qxp-fspi", .data = (ulong)&imx8qxp_data, }, + { .compatible = "nxp,imx8dxl-fspi", .data = (ulong)&imx8dxl_data, }, { .compatible = "nxp,imxrt1170-fspi", .data = (ulong)&imxrt1170_data, }, { } }; -- cgit v1.2.3 From 320b191ec820705f24ab7f7981dc3b60ab9e0ee8 Mon Sep 17 00:00:00 2001 From: Ye Li Date: Wed, 24 Sep 2025 10:11:27 +0800 Subject: spi: nxp_fspi: Support i.MX8ULP flexspi Add i.MX8ULP flexspi compatible string and driver data. The flexspi on i.MX8ULP only has 16 LUT sequences and uses 1KB RX FIFO. Signed-off-by: Ye Li --- drivers/spi/nxp_fspi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/nxp_fspi.c b/drivers/spi/nxp_fspi.c index 3dfb54b1968..7086a2a264a 100644 --- a/drivers/spi/nxp_fspi.c +++ b/drivers/spi/nxp_fspi.c @@ -355,6 +355,15 @@ static const struct nxp_fspi_devtype_data imx8dxl_data = { .little_endian = true, /* little-endian */ }; +static const struct nxp_fspi_devtype_data imx8ulp_data = { + .rxfifo = SZ_1K, /* (128 * 64 bits) */ + .txfifo = SZ_1K, /* (128 * 64 bits) */ + .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ + .quirks = 0, + .lut_num = 16, + .little_endian = true, /* little-endian */ +}; + struct nxp_fspi { struct udevice *dev; void __iomem *iobase; @@ -1101,6 +1110,7 @@ static const struct udevice_id nxp_fspi_ids[] = { { .compatible = "nxp,imx8mp-fspi", .data = (ulong)&imx8mm_data, }, { .compatible = "nxp,imx8qxp-fspi", .data = (ulong)&imx8qxp_data, }, { .compatible = "nxp,imx8dxl-fspi", .data = (ulong)&imx8dxl_data, }, + { .compatible = "nxp,imx8ulp-fspi", .data = (ulong)&imx8ulp_data, }, { .compatible = "nxp,imxrt1170-fspi", .data = (ulong)&imxrt1170_data, }, { } }; -- cgit v1.2.3 From 5d2ef97c66f0a432c859cfdf64ef696017619ad6 Mon Sep 17 00:00:00 2001 From: Naresh Kumar Ravulapalli Date: Fri, 8 Aug 2025 02:36:59 -0700 Subject: drivers: ddr: altera: Check IOSSM mailbox compatibility Compatibility check of IOSSM mailbox with U-Boot is performed by verifying the mailbox specification version. If check fails, appropriate error message is displayed. Signed-off-by: Naresh Kumar Ravulapalli Reviewed-by: Tien Fong Chee --- drivers/ddr/altera/iossm_mailbox.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/ddr/altera/iossm_mailbox.c b/drivers/ddr/altera/iossm_mailbox.c index 21f94959a04..2a2f86a650e 100644 --- a/drivers/ddr/altera/iossm_mailbox.c +++ b/drivers/ddr/altera/iossm_mailbox.c @@ -38,6 +38,8 @@ #define IOSSM_STATUS_CMD_RESPONSE_ERROR(n) FIELD_GET(IOSSM_STATUS_CMD_RESPONSE_ERROR_MASK, n) #define IOSSM_STATUS_GENERAL_ERROR_MASK GENMASK(4, 1) #define IOSSM_STATUS_GENERAL_ERROR(n) FIELD_GET(IOSSM_STATUS_GENERAL_ERROR_MASK, n) +#define IOSSM_MAILBOX_SPEC_VERSION_MASK GENMASK(2, 0) +#define IOSSM_MAILBOX_SPEC_VERSION(n) FIELD_GET(IOSSM_MAILBOX_SPEC_VERSION_MASK, n) /* Offset of Mailbox Read-only Registers */ #define IOSSM_MAILBOX_HEADER_OFFSET 0x0 @@ -383,6 +385,23 @@ err: return ret; } +static bool is_mailbox_spec_compatible(struct io96b_info *io96b_ctrl) +{ + u32 mailbox_header; + u8 mailbox_spec_ver; + + mailbox_header = readl(io96b_ctrl->io96b[0].io96b_csr_addr + + IOSSM_MAILBOX_HEADER_OFFSET); + mailbox_spec_ver = IOSSM_MAILBOX_SPEC_VERSION(mailbox_header); + printf("%s: IOSSM mailbox version: %d\n", __func__, mailbox_spec_ver); + + /* for now there are two mailbox spec versions, 0 and 1; only version 1 is compatible */ + if (!mailbox_spec_ver) + return false; + + return true; +} + /* * Initial function to be called to set memory interface IP type and instance ID * IP type and instance ID need to be determined before sending mailbox command @@ -392,6 +411,11 @@ void io96b_mb_init(struct io96b_info *io96b_ctrl) int i, j; u32 mem_intf_info_0, mem_intf_info_1; + if (!is_mailbox_spec_compatible(io96b_ctrl)) { + printf("DDR: Failed to get compatible mailbox version\n"); + hang(); + } + debug("%s: num_instance %d\n", __func__, io96b_ctrl->num_instance); for (i = 0; i < io96b_ctrl->num_instance; i++) { -- cgit v1.2.3 From 63ef1c7a7391e7440bdfbffedd2cc5d9007707cd Mon Sep 17 00:00:00 2001 From: Naresh Kumar Ravulapalli Date: Fri, 8 Aug 2025 02:42:42 -0700 Subject: drivers: ddr: altera: Correct DDR calibration status check Bit 3 of the seq2core register is no longer set to indicate calibration completion. Instead, added polling of the seq2core register until it reads 0b00000111, signaling that the Nios processor has started the calibration process. Signed-off-by: Naresh Kumar Ravulapalli Reviewed-by: Tien Fong Chee --- drivers/ddr/altera/sdram_soc64.c | 6 +++--- drivers/ddr/altera/sdram_soc64.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/ddr/altera/sdram_soc64.c b/drivers/ddr/altera/sdram_soc64.c index f8fc92060db..2d0093c591c 100644 --- a/drivers/ddr/altera/sdram_soc64.c +++ b/drivers/ddr/altera/sdram_soc64.c @@ -85,11 +85,11 @@ int emif_reset(struct altera_sdram_plat *plat) debug("DDR: Triggerring emif reset\n"); hmc_ecc_writel(plat, DDR_HMC_CORE2SEQ_INT_REQ, RSTHANDSHAKECTRL); - /* if seq2core[3] = 0, we are good */ + /* if seq2core[2:0] = 0b0000_0111, we are good */ ret = wait_for_bit_le32((const void *)(plat->hmc + RSTHANDSHAKESTAT), - DDR_HMC_SEQ2CORE_INT_RESP_MASK, - false, 1000, false); + DDR_HMC_SEQ2CORE_INT_REQ_ACK_MASK, + true, 1000, false); if (ret) { printf("DDR: failed to get ack from EMIF\n"); return ret; diff --git a/drivers/ddr/altera/sdram_soc64.h b/drivers/ddr/altera/sdram_soc64.h index 6031cef560e..6fe0653922c 100644 --- a/drivers/ddr/altera/sdram_soc64.h +++ b/drivers/ddr/altera/sdram_soc64.h @@ -77,7 +77,7 @@ struct altera_sdram_plat { #define DDR_HMC_INTMODE_INTMODE_SET_MSK BIT(0) #define DDR_HMC_RSTHANDSHAKE_MASK 0x0000000f #define DDR_HMC_CORE2SEQ_INT_REQ 0x0000000f -#define DDR_HMC_SEQ2CORE_INT_RESP_MASK BIT(3) +#define DDR_HMC_SEQ2CORE_INT_REQ_ACK_MASK GENMASK(2, 0) #define DDR_HMC_HPSINTFCSEL_ENABLE_MASK 0x001f1f1f #define DDR_HMC_ERRINTEN_INTMASK \ -- cgit v1.2.3 From 022b2159b5d24556b7623906de147260fe46e0f2 Mon Sep 17 00:00:00 2001 From: Alif Zakuan Yuslaimi Date: Mon, 8 Sep 2025 19:11:15 -0700 Subject: drivers: clk: agilex: Support for enable/disable API Update Agilex clock driver to support enabling or disabling the peripheral clocks via clock driver model APIs. The caller will pass the clock ID to this driver and the driver will then proceed to manipulate the desired bit in the Agilex clock manager peripheral PLL register based on the given clock ID. Signed-off-by: Alif Zakuan Yuslaimi Reviewed-by: Tien Fong Chee --- drivers/clk/altera/clk-agilex.c | 120 ++++++++++++++++++++++++++++++++++++++++ drivers/clk/altera/clk-agilex.h | 20 +++++++ 2 files changed, 140 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/altera/clk-agilex.c b/drivers/clk/altera/clk-agilex.c index 242740a4b00..46b04895cc5 100644 --- a/drivers/clk/altera/clk-agilex.c +++ b/drivers/clk/altera/clk-agilex.c @@ -22,6 +22,8 @@ DECLARE_GLOBAL_DATA_PTR; struct socfpga_clk_plat { void __iomem *regs; + int pllgrp; + int bitmask; }; /* @@ -643,8 +645,125 @@ static ulong socfpga_clk_get_rate(struct clk *clk) } } +static int bitmask_from_clk_id(struct clk *clk) +{ + struct socfpga_clk_plat *plat = dev_get_plat(clk->dev); + + switch (clk->id) { + case AGILEX_MPU_CLK: + plat->pllgrp = CLKMGR_MAINPLL_EN; + plat->bitmask = CLKMGR_MAINPLLGRP_EN_MPUCLK_MASK; + break; + case AGILEX_L4_MAIN_CLK: + plat->pllgrp = CLKMGR_MAINPLL_EN; + plat->bitmask = CLKMGR_MAINPLLGRP_EN_L4MAINCLK_MASK; + break; + case AGILEX_L4_MP_CLK: + plat->pllgrp = CLKMGR_MAINPLL_EN; + plat->bitmask = CLKMGR_MAINPLLGRP_EN_L4MPCLK_MASK; + break; + case AGILEX_L4_SP_CLK: + plat->pllgrp = CLKMGR_MAINPLL_EN; + plat->bitmask = CLKMGR_MAINPLLGRP_EN_L4SPCLK_MASK; + break; + case AGILEX_CS_AT_CLK: + plat->pllgrp = CLKMGR_MAINPLL_EN; + plat->bitmask = CLKMGR_MAINPLLGRP_EN_CSCLK_MASK; + break; + case AGILEX_CS_TRACE_CLK: + plat->pllgrp = CLKMGR_MAINPLL_EN; + plat->bitmask = CLKMGR_MAINPLLGRP_EN_CSCLK_MASK; + break; + case AGILEX_CS_PDBG_CLK: + plat->pllgrp = CLKMGR_MAINPLL_EN; + plat->bitmask = CLKMGR_MAINPLLGRP_EN_CSCLK_MASK; + break; + case AGILEX_CS_TIMER_CLK: + plat->pllgrp = CLKMGR_MAINPLL_EN; + plat->bitmask = CLKMGR_MAINPLLGRP_EN_CSTIMERCLK_MASK; + break; + case AGILEX_S2F_USER0_CLK: + plat->pllgrp = CLKMGR_MAINPLL_EN; + plat->bitmask = CLKMGR_MAINPLLGRP_EN_S2FUSER0CLK_MASK; + break; + case AGILEX_EMAC0_CLK: + plat->pllgrp = CLKMGR_PERPLL_EN; + plat->bitmask = CLKMGR_PERPLLGRP_EN_EMAC0CLK_MASK; + break; + case AGILEX_EMAC1_CLK: + plat->pllgrp = CLKMGR_PERPLL_EN; + plat->bitmask = CLKMGR_PERPLLGRP_EN_EMAC1CLK_MASK; + break; + case AGILEX_EMAC2_CLK: + plat->pllgrp = CLKMGR_PERPLL_EN; + plat->bitmask = CLKMGR_PERPLLGRP_EN_EMAC2CLK_MASK; + break; + case AGILEX_EMAC_PTP_CLK: + plat->pllgrp = CLKMGR_PERPLL_EN; + plat->bitmask = CLKMGR_PERPLLGRP_EN_EMACPTPCLK_MASK; + break; + case AGILEX_GPIO_DB_CLK: + plat->pllgrp = CLKMGR_PERPLL_EN; + plat->bitmask = CLKMGR_PERPLLGRP_EN_GPIODBCLK_MASK; + break; + case AGILEX_SDMMC_CLK: + plat->pllgrp = CLKMGR_PERPLL_EN; + plat->bitmask = CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK; + break; + case AGILEX_S2F_USER1_CLK: + plat->pllgrp = CLKMGR_PERPLL_EN; + plat->bitmask = CLKMGR_PERPLLGRP_EN_S2FUSER1CLK_MASK; + break; + case AGILEX_PSI_REF_CLK: + plat->pllgrp = CLKMGR_PERPLL_EN; + plat->bitmask = CLKMGR_PERPLLGRP_EN_PSIREFCLK_MASK; + break; + case AGILEX_USB_CLK: + plat->pllgrp = CLKMGR_PERPLL_EN; + plat->bitmask = CLKMGR_PERPLLGRP_EN_USBCLK_MASK; + break; + case AGILEX_SPI_M_CLK: + plat->pllgrp = CLKMGR_PERPLL_EN; + plat->bitmask = CLKMGR_PERPLLGRP_EN_SPIMCLK_MASK; + break; + case AGILEX_NAND_CLK: + plat->pllgrp = CLKMGR_PERPLL_EN; + plat->bitmask = CLKMGR_PERPLLGRP_EN_NANDCLK_MASK; + break; + default: + return -ENXIO; + } + + return 0; +} + static int socfpga_clk_enable(struct clk *clk) { + struct socfpga_clk_plat *plat = dev_get_plat(clk->dev); + uintptr_t base_addr = (uintptr_t)plat->regs; + int ret; + + ret = bitmask_from_clk_id(clk); + if (ret) + return ret; + + setbits_le32(base_addr + plat->pllgrp, plat->bitmask); + + return 0; +} + +static int socfpga_clk_disable(struct clk *clk) +{ + struct socfpga_clk_plat *plat = dev_get_plat(clk->dev); + uintptr_t base_addr = (uintptr_t)plat->regs; + int ret; + + ret = bitmask_from_clk_id(clk); + if (ret) + return ret; + + clrbits_le32(base_addr + plat->pllgrp, plat->bitmask); + return 0; } @@ -672,6 +791,7 @@ static int socfpga_clk_of_to_plat(struct udevice *dev) static struct clk_ops socfpga_clk_ops = { .enable = socfpga_clk_enable, + .disable = socfpga_clk_disable, .get_rate = socfpga_clk_get_rate, }; diff --git a/drivers/clk/altera/clk-agilex.h b/drivers/clk/altera/clk-agilex.h index b3e8841a512..be639957940 100644 --- a/drivers/clk/altera/clk-agilex.h +++ b/drivers/clk/altera/clk-agilex.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2019 Intel Corporation + * Copyright (C) 2025 Altera Corporation */ #ifndef _CLK_AGILEX_ @@ -210,7 +211,26 @@ struct cm_config { #define CLKMGR_LOSTLOCK_SET_MASK BIT(0) +#define CLKMGR_MAINPLLGRP_EN_MPUCLK_MASK BIT(0) +#define CLKMGR_MAINPLLGRP_EN_L4MAINCLK_MASK BIT(1) +#define CLKMGR_MAINPLLGRP_EN_L4MPCLK_MASK BIT(2) +#define CLKMGR_MAINPLLGRP_EN_L4SPCLK_MASK BIT(3) +#define CLKMGR_MAINPLLGRP_EN_CSCLK_MASK BIT(4) +#define CLKMGR_MAINPLLGRP_EN_CSTIMERCLK_MASK BIT(5) +#define CLKMGR_MAINPLLGRP_EN_S2FUSER0CLK_MASK BIT(6) + +#define CLKMGR_PERPLLGRP_EN_EMAC0CLK_MASK BIT(0) +#define CLKMGR_PERPLLGRP_EN_EMAC1CLK_MASK BIT(1) +#define CLKMGR_PERPLLGRP_EN_EMAC2CLK_MASK BIT(2) +#define CLKMGR_PERPLLGRP_EN_EMACPTPCLK_MASK BIT(3) +#define CLKMGR_PERPLLGRP_EN_GPIODBCLK_MASK BIT(4) #define CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK BIT(5) +#define CLKMGR_PERPLLGRP_EN_S2FUSER1CLK_MASK BIT(6) +#define CLKMGR_PERPLLGRP_EN_PSIREFCLK_MASK BIT(7) +#define CLKMGR_PERPLLGRP_EN_USBCLK_MASK BIT(8) +#define CLKMGR_PERPLLGRP_EN_SPIMCLK_MASK BIT(9) +#define CLKMGR_PERPLLGRP_EN_NANDCLK_MASK BIT(10) + #define CLKMGR_PERPLLGRP_EMACCTL_EMAC0SELB_OFFSET 26 #define CLKMGR_PERPLLGRP_EMACCTL_EMAC0SELB_MASK BIT(26) #define CLKMGR_PERPLLGRP_EMACCTL_EMAC1SELB_OFFSET 27 -- cgit v1.2.3 From ab27182cac8ff14621c73c6609aaf3036fab1c0a Mon Sep 17 00:00:00 2001 From: Alif Zakuan Yuslaimi Date: Mon, 8 Sep 2025 19:11:16 -0700 Subject: mmc: socfpga_dw_mmc: Enable/disable SDMMC clock via API Update the driver to enable or disable the SDMMC clock via clock driver model API instead of doing it in the driver itself. This allows for scalability of the driver for various SoCFPGA devices. Signed-off-by: Alif Zakuan Yuslaimi Reviewed-by: Tien Fong Chee --- drivers/mmc/socfpga_dw_mmc.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/socfpga_dw_mmc.c b/drivers/mmc/socfpga_dw_mmc.c index 3b86bc9b18c..db4e0129c2e 100644 --- a/drivers/mmc/socfpga_dw_mmc.c +++ b/drivers/mmc/socfpga_dw_mmc.c @@ -29,7 +29,9 @@ struct socfpga_dwmci_plat { /* socfpga implmentation specific driver private data */ struct dwmci_socfpga_priv_data { + struct udevice *dev; struct dwmci_host host; + struct clk mmc_clk_ciu; unsigned int drvsel; unsigned int smplsel; }; @@ -51,28 +53,23 @@ static void socfpga_dwmci_reset(struct udevice *dev) static int socfpga_dwmci_clksel(struct dwmci_host *host) { struct dwmci_socfpga_priv_data *priv = host->priv; + int ret; + u32 sdmmc_mask = ((priv->smplsel & 0x7) << SYSMGR_SDMMC_SMPLSEL_SHIFT) | ((priv->drvsel & 0x7) << SYSMGR_SDMMC_DRVSEL_SHIFT); - /* Get clock manager base address */ - struct udevice *clkmgr_dev; - int ret = uclass_get_device_by_name(UCLASS_CLK, "clock-controller@ffd10000", &clkmgr_dev); - + ret = clk_get_by_name(priv->dev, "ciu", &priv->mmc_clk_ciu); if (ret) { - printf("Failed to get clkmgr device: %d\n", ret); + debug("%s: Failed to get SDMMC clock from dts\n", __func__); return ret; } - fdt_addr_t clkmgr_base = dev_read_addr(clkmgr_dev); - - if (clkmgr_base == FDT_ADDR_T_NONE) { - printf("Failed to read base address from clkmgr DT node\n"); - return -EINVAL; - } - /* Disable SDMMC clock. */ - clrbits_le32(clkmgr_base + CLKMGR_PERPLL_EN, - CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK); + ret = clk_disable(&priv->mmc_clk_ciu); + if (ret) { + printf("%s: Failed to disable SDMMC clock\n", __func__); + return ret; + } debug("%s: drvsel %d smplsel %d\n", __func__, priv->drvsel, priv->smplsel); @@ -92,8 +89,11 @@ static int socfpga_dwmci_clksel(struct dwmci_host *host) #endif /* Enable SDMMC clock */ - setbits_le32(clkmgr_base + CLKMGR_PERPLL_EN, - CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK); + ret = clk_enable(&priv->mmc_clk_ciu); + if (ret) { + printf("%s: Failed to enable SDMMC clock\n", __func__); + return ret; + } return 0; } @@ -169,6 +169,7 @@ static int socfpga_dwmmc_probe(struct udevice *dev) struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct dwmci_socfpga_priv_data *priv = dev_get_priv(dev); struct dwmci_host *host = &priv->host; + priv->dev = dev; int ret; ret = socfpga_dwmmc_get_clk_rate(dev); -- cgit v1.2.3 From 924a9fc4021cf4899c6b1e26d28336f412aa296f Mon Sep 17 00:00:00 2001 From: Naresh Kumar Ravulapalli Date: Wed, 10 Sep 2025 22:21:11 -0700 Subject: drivers: clk: agilex: Fix EMAC clock source selection Fix the incorrect bit masking and bit shift used to compute EMAC control which in turn is used to select EMAC clock from EMAC source A or B. Signed-off-by: Naresh Kumar Ravulapalli Reviewed-by: Tien Fong Chee --- drivers/clk/altera/clk-agilex.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/altera/clk-agilex.c b/drivers/clk/altera/clk-agilex.c index 46b04895cc5..19c4e8220db 100644 --- a/drivers/clk/altera/clk-agilex.c +++ b/drivers/clk/altera/clk-agilex.c @@ -546,14 +546,14 @@ static u32 clk_get_emac_clk_hz(struct socfpga_clk_plat *plat, u32 emac_id) /* Get EMAC clock source */ ctl = CM_REG_READL(plat, CLKMGR_PERPLL_EMACCTL); if (emac_id == AGILEX_EMAC0_CLK) - ctl = (ctl >> CLKMGR_PERPLLGRP_EMACCTL_EMAC0SELB_OFFSET) & - CLKMGR_PERPLLGRP_EMACCTL_EMAC0SELB_MASK; + ctl = (ctl & CLKMGR_PERPLLGRP_EMACCTL_EMAC0SELB_MASK) >> + CLKMGR_PERPLLGRP_EMACCTL_EMAC0SELB_OFFSET; else if (emac_id == AGILEX_EMAC1_CLK) - ctl = (ctl >> CLKMGR_PERPLLGRP_EMACCTL_EMAC1SELB_OFFSET) & - CLKMGR_PERPLLGRP_EMACCTL_EMAC1SELB_MASK; + ctl = (ctl & CLKMGR_PERPLLGRP_EMACCTL_EMAC1SELB_MASK) >> + CLKMGR_PERPLLGRP_EMACCTL_EMAC1SELB_OFFSET; else if (emac_id == AGILEX_EMAC2_CLK) - ctl = (ctl >> CLKMGR_PERPLLGRP_EMACCTL_EMAC2SELB_OFFSET) & - CLKMGR_PERPLLGRP_EMACCTL_EMAC2SELB_MASK; + ctl = (ctl & CLKMGR_PERPLLGRP_EMACCTL_EMAC2SELB_MASK) >> + CLKMGR_PERPLLGRP_EMACCTL_EMAC2SELB_OFFSET; else return 0; -- cgit v1.2.3 From 2ff686bcfd14689de3d6a6da9c35340449025ef5 Mon Sep 17 00:00:00 2001 From: Naresh Kumar Ravulapalli Date: Wed, 10 Sep 2025 22:21:12 -0700 Subject: drivers: clk: agilex: Use FIELD_GET during EMAC clock selection FIELD_GET() macro is used during EMAC clock source selection for better code readability and maintainability. Signed-off-by: Naresh Kumar Ravulapalli Reviewed-by: Tien Fong Chee --- drivers/clk/altera/clk-agilex.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/altera/clk-agilex.c b/drivers/clk/altera/clk-agilex.c index 19c4e8220db..fdbf834bb2f 100644 --- a/drivers/clk/altera/clk-agilex.c +++ b/drivers/clk/altera/clk-agilex.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -546,14 +547,11 @@ static u32 clk_get_emac_clk_hz(struct socfpga_clk_plat *plat, u32 emac_id) /* Get EMAC clock source */ ctl = CM_REG_READL(plat, CLKMGR_PERPLL_EMACCTL); if (emac_id == AGILEX_EMAC0_CLK) - ctl = (ctl & CLKMGR_PERPLLGRP_EMACCTL_EMAC0SELB_MASK) >> - CLKMGR_PERPLLGRP_EMACCTL_EMAC0SELB_OFFSET; + ctl = FIELD_GET(CLKMGR_PERPLLGRP_EMACCTL_EMAC0SELB_MASK, ctl); else if (emac_id == AGILEX_EMAC1_CLK) - ctl = (ctl & CLKMGR_PERPLLGRP_EMACCTL_EMAC1SELB_MASK) >> - CLKMGR_PERPLLGRP_EMACCTL_EMAC1SELB_OFFSET; + ctl = FIELD_GET(CLKMGR_PERPLLGRP_EMACCTL_EMAC1SELB_MASK, ctl); else if (emac_id == AGILEX_EMAC2_CLK) - ctl = (ctl & CLKMGR_PERPLLGRP_EMACCTL_EMAC2SELB_MASK) >> - CLKMGR_PERPLLGRP_EMACCTL_EMAC2SELB_OFFSET; + ctl = FIELD_GET(CLKMGR_PERPLLGRP_EMACCTL_EMAC2SELB_MASK, ctl); else return 0; -- cgit v1.2.3 From 3ce8a0e9115eaa0cdfc142459814b2283cf01785 Mon Sep 17 00:00:00 2001 From: Chance Yang Date: Tue, 26 Aug 2025 11:36:17 +0800 Subject: fastboot: Fix has-slot command always returning yes for fb_nand The issue was a mismatch in return value conventions between functions: - getvar_get_part_info() expects >= 0 for success - fb_nand_lookup() returns 0 on success, 1 on failure (from mtdparts_init and find_dev_and_part) When partition didn't exist, fb_nand_lookup returned 1, but fastboot_nand_get_part_info passed it directly to getvar_get_part_info, which treated 1 >= 0 as success, causing has-slot to always return yes. Fix by converting positive return values to -ENOENT in fastboot_nand_get_part_info to match the expected error convention. Signed-off-by: Chance Yang Reviewed-by: Mattijs Korpershoek Link: https://lore.kernel.org/r/20250826-master-v2-1-30b787a2f9fd@kneron.us Signed-off-by: Mattijs Korpershoek --- drivers/fastboot/fb_nand.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/fastboot/fb_nand.c b/drivers/fastboot/fb_nand.c index afc64fd5280..6df3917e129 100644 --- a/drivers/fastboot/fb_nand.c +++ b/drivers/fastboot/fb_nand.c @@ -157,8 +157,13 @@ int fastboot_nand_get_part_info(const char *part_name, struct part_info **part_info, char *response) { struct mtd_info *mtd = NULL; + int ret; + + ret = fb_nand_lookup(part_name, &mtd, part_info, response); + if (ret) + return -ENOENT; - return fb_nand_lookup(part_name, &mtd, part_info, response); + return ret; } /** -- cgit v1.2.3 From f58667587243ce652aaa7d0dd6557a2fb2da5020 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Mon, 29 Sep 2025 17:25:23 +0100 Subject: usb: gadget: atmel: Add missing null check Add in the missing null check for udc->driver that is present at other points in the function before it is dereferenced. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Reviewed-by: Mattijs Korpershoek Reviewed-by: Marek Vasut Link: https://lore.kernel.org/r/20250929-atmel_usba_udc-v1-1-e1426271e12a@linaro.org Signed-off-by: Mattijs Korpershoek --- drivers/usb/gadget/atmel_usba_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 72f68dba3a7..f7a92ded6da 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1147,7 +1147,7 @@ static int usba_udc_irq(struct usba_udc *udc) reset_all_endpoints(udc); if (udc->gadget.speed != USB_SPEED_UNKNOWN && - udc->driver->disconnect) { + udc->driver && udc->driver->disconnect) { udc->gadget.speed = USB_SPEED_UNKNOWN; spin_unlock(&udc->lock); udc->driver->disconnect(&udc->gadget); -- cgit v1.2.3 From 5ac61383b2db15b037e87bd353f726eafc49e181 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Mon, 29 Sep 2025 17:40:43 +0100 Subject: usb: dwc2: Add missing null check Add in the missing null check for dev->driver that is present at other points in the function before it is dereferenced. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Reviewed-by: Mattijs Korpershoek Reviewed-by: Marek Vasut Link: https://lore.kernel.org/r/20250929-usb_dwc2-v1-1-863133dcbcde@linaro.org Signed-off-by: Mattijs Korpershoek --- drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c b/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c index fca052b4556..5a7f50ebaa5 100644 --- a/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c +++ b/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c @@ -526,7 +526,7 @@ static int dwc2_udc_irq(int irq, void *_dev) if (gotgint & GOTGINT_SES_END_DET) { debug_cond(DEBUG_ISR, "\t\tSession End Detected\n"); /* Let gadget detect disconnected state */ - if (dev->driver->disconnect) { + if (dev->driver && dev->driver->disconnect) { spin_unlock_irqrestore(&dev->lock, flags); dev->driver->disconnect(&dev->gadget); spin_lock_irqsave(&dev->lock, flags); -- cgit v1.2.3 From 8cd4a5e94ba2ab73fde3301d6314549dd5a25cb4 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Mon, 29 Sep 2025 10:11:57 +0100 Subject: usb: dwc3: Remove redundant test In dwc3_ep0_complete_data there is a test for 'r' being null and the code will return at that point if so. After that point 'r' is guaranteed to not be null and testing for that is redundant. Remove the test for 'r' being non-null. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Reviewed-by: Mattijs Korpershoek Reviewed-by: Marek Vasut Link: https://lore.kernel.org/r/20250929-dwc3_ep0-v1-1-1d5c58933bde@linaro.org Signed-off-by: Mattijs Korpershoek --- drivers/usb/dwc3/ep0.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index c656cbe25ce..680756532f0 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -799,10 +799,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, status = DWC3_TRB_SIZE_TRBSTS(trb->size); if (status == DWC3_TRBSTS_SETUP_PENDING) { dev_dbg(dwc->dev, "Setup Pending received"); - - if (r) - dwc3_gadget_giveback(ep0, r, -ECONNRESET); - + dwc3_gadget_giveback(ep0, r, -ECONNRESET); return; } -- cgit v1.2.3 From 1a367adfd66c28d8d2fdb2957dfba5434b987de1 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 2 May 2025 08:51:45 +0200 Subject: net: mdio: mux-meson-gxl: set reversed bit when using internal phy This bit is necessary to receive packets from the internal PHY. Without this bit set, no activity occurs on the interface. Normally u-boot sets this bit, but if u-boot is compiled without net support, the interface will be up but without any activity. The vendor SDK sets this bit along with the PHY_ID bits. Ported from the Linux change at [1] from Da Xu merged in commit [2]. [1] https://lore.kernel.org/all/20250425192009.1439508-1-da@libre.computer/ [2] b23285e93bef ("net: mdio: mux-meson-gxl: set reversed bit when using internal phy") Suggested-by: Da Xue Link: https://lore.kernel.org/r/20250502-u-boot-topic-mdio-mux-gxl-bit28-v1-1-399f6c3db154@linaro.org Signed-off-by: Neil Armstrong --- drivers/net/mdio_mux_meson_gxl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/mdio_mux_meson_gxl.c b/drivers/net/mdio_mux_meson_gxl.c index 8ef3ae598b7..31898ed437e 100644 --- a/drivers/net/mdio_mux_meson_gxl.c +++ b/drivers/net/mdio_mux_meson_gxl.c @@ -19,6 +19,7 @@ #define REG2_LEDACT GENMASK(23, 22) #define REG2_LEDLINK GENMASK(25, 24) #define REG2_DIV4SEL BIT(27) +#define REG2_REVERSED BIT(28) #define REG2_ADCBYPASS BIT(30) #define REG2_CLKINSEL BIT(31) #define ETH_REG3 0x4 @@ -66,7 +67,7 @@ static int meson_gxl_enable_internal_mdio(struct mdio_mux_meson_gxl_priv *priv) * The only constraint is that it must match the one in * drivers/net/phy/meson-gxl.c to properly match the PHY. */ - writel(FIELD_PREP(REG2_PHYID, EPHY_GXL_ID), + writel(REG2_REVERSED | FIELD_PREP(REG2_PHYID, EPHY_GXL_ID), priv->regs + ETH_REG2); /* Enable the internal phy */ -- cgit v1.2.3 From 033dbc7f9ec969168a2f48766cb16aa26fa381d8 Mon Sep 17 00:00:00 2001 From: Yang Xiwen Date: Tue, 17 Jun 2025 01:01:17 +0800 Subject: pinctrl: meson: support gpio toggle command meson_gpio_get() always assumes gpio is configured to input mode. This is incorrect and breaks `gpio toggle` command: gpio: pin aobus-banks2 (gpio 2) value is 0 Warning: value of pin is still 1 Fix it by adding the logic to handle both input and output mode. Fixes: 2009a8d03fe5 ("pinctrl: meson: add GPIO support") Signed-off-by: Yang Xiwen Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20250617-meson_ppinctrl-v3-1-218d9321a8d2@outlook.com Signed-off-by: Neil Armstrong --- drivers/pinctrl/meson/pinctrl-meson.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c index babf1bccc96..7dbaf966f93 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.c +++ b/drivers/pinctrl/meson/pinctrl-meson.c @@ -117,8 +117,26 @@ int meson_gpio_get(struct udevice *dev, unsigned int offset) struct meson_pinctrl *priv = dev_get_priv(dev->parent); unsigned int reg, bit; int ret; + enum gpio_func_t direction; + enum meson_reg_type reg_type; - ret = meson_gpio_calc_reg_and_bit(dev->parent, offset, REG_IN, ®, + direction = meson_gpio_get_direction(dev, offset); + + switch (direction) { + case GPIOF_INPUT: + reg_type = REG_IN; + break; + + case GPIOF_OUTPUT: + reg_type = REG_OUT; + break; + + default: + dev_warn(dev, "Failed to get current direction of Pin %u\n", offset); + return -EINVAL; + } + + ret = meson_gpio_calc_reg_and_bit(dev->parent, offset, reg_type, ®, &bit); if (ret) return ret; -- cgit v1.2.3 From b332723882f82f6feff027fb733c4b7e43bdef80 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Tue, 22 Jul 2025 15:06:14 +0100 Subject: adc: meson-saradc: uint cannot be less than zero timeout is declared as a uint but then tested for being less than zero which must always fail. Change the while loop for a pre-decrement on timeout and test timeout for being zero. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20250722-meson_saradc-v1-1-1ab45d53da9d@linaro.org Signed-off-by: Neil Armstrong --- drivers/adc/meson-saradc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/adc/meson-saradc.c b/drivers/adc/meson-saradc.c index 60e348968fb..0144ff828c5 100644 --- a/drivers/adc/meson-saradc.c +++ b/drivers/adc/meson-saradc.c @@ -205,9 +205,9 @@ static int meson_saradc_lock(struct meson_saradc_priv *priv) do { udelay(1); regmap_read(priv->regmap, MESON_SAR_ADC_DELAY, &val); - } while (val & MESON_SAR_ADC_DELAY_BL30_BUSY && timeout--); + } while (val & MESON_SAR_ADC_DELAY_BL30_BUSY && --timeout); - if (timeout < 0) { + if (!timeout) { printf("Timeout while waiting for BL30 unlock\n"); return -ETIMEDOUT; } @@ -256,9 +256,9 @@ static int meson_saradc_wait_busy_clear(struct meson_saradc_priv *priv) do { udelay(1); regmap_read(priv->regmap, MESON_SAR_ADC_REG0, ®val); - } while (FIELD_GET(MESON_SAR_ADC_REG0_BUSY_MASK, regval) && timeout--); + } while (FIELD_GET(MESON_SAR_ADC_REG0_BUSY_MASK, regval) && --timeout); - if (timeout < 0) + if (!timeout) return -ETIMEDOUT; return 0; -- cgit v1.2.3 From bb2d7ea6f2a8b9539f9daf526b5f87c29ce413b4 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Wed, 23 Jul 2025 17:37:24 +0100 Subject: clk: meson: Remove unreachable code A second return following the first return is unreachable code so remove it. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20250723-clk_meson-v1-1-8cd6e73145a4@linaro.org Signed-off-by: Neil Armstrong --- drivers/clk/meson/g12a.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c index 5d7faaa3eab..a7a42b2edb6 100644 --- a/drivers/clk/meson/g12a.c +++ b/drivers/clk/meson/g12a.c @@ -916,8 +916,6 @@ static ulong meson_clk_set_rate_by_id(struct clk *clk, unsigned long id, return -EINVAL; case CLKID_PCIE_PLL: return meson_pcie_pll_set_rate(clk, rate); - - return 0; case CLKID_VPU: return meson_clk_set_rate_by_id(clk, meson_mux_get_parent(clk, CLKID_VPU), rate, -- cgit v1.2.3 From b1e2cbd65cdfbc32c222eccedac11f53390694cf Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Fri, 8 Aug 2025 10:34:43 +0100 Subject: pwm: meson: Stop premature exit from for loop In meson_pwm_probe the for loop attempts to get the name of a clock but the following if..else statements only perform useful work if -ENODATA is returned from clk_get_by_name. If clk_get_by_name simply succeeds then this results in a premature exit from the for loop and the following code can never be reached. Make the else clause only apply for an error return from clk_get_by_name. This issue was found by Smatch. Signed-off-by: Andrew Goodbody Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20250808-pwm_meson-v1-1-cddb7e5f76bd@linaro.org Signed-off-by: Neil Armstrong --- drivers/pwm/pwm-meson.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index c2597d8b669..caa7af085fa 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -359,8 +359,9 @@ static int meson_pwm_probe(struct udevice *dev) /* We have our source clock, do not alter HW clock mux */ continue; - } else + } else if (err) { return err; + } /* Get id in list */ for (p = 0 ; p < data->num_parents ; ++p) { -- cgit v1.2.3