summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/bootcount/Kconfig2
-rw-r--r--drivers/clk/clk_versal.c1
-rw-r--r--drivers/clk/mediatek/clk-mt8189.c44
-rw-r--r--drivers/clk/mediatek/clk-mtk.c22
-rw-r--r--drivers/clk/meson/g12a.c5
-rw-r--r--drivers/clk/meson/gxbb.c5
-rw-r--r--drivers/clk/qcom/Kconfig8
-rw-r--r--drivers/clk/qcom/Makefile1
-rw-r--r--drivers/clk/qcom/clock-milos.c196
-rw-r--r--drivers/clk/qcom/clock-sc7280.c3
-rw-r--r--drivers/clk/renesas/Kconfig6
-rw-r--r--drivers/clk/renesas/Makefile1
-rw-r--r--drivers/clk/renesas/r8a78000-cpg.c282
-rw-r--r--drivers/clk/rockchip/clk_rk3528.c2
-rw-r--r--drivers/clk/rockchip/clk_rk3576.c54
-rw-r--r--drivers/clk/stm32/Kconfig2
-rw-r--r--drivers/core/of_access.c52
-rw-r--r--drivers/core/ofnode.c48
-rw-r--r--drivers/cpu/imx8_cpu.c7
-rw-r--r--drivers/ddr/imx/imx9/Kconfig2
-rw-r--r--drivers/ddr/imx/phy/helper.c13
-rw-r--r--drivers/dfu/Kconfig2
-rw-r--r--drivers/fastboot/Kconfig4
-rw-r--r--drivers/fastboot/fb_common.c4
-rw-r--r--drivers/firmware/psci.c4
-rw-r--r--drivers/firmware/scmi/sandbox-scmi_agent.c2
-rw-r--r--drivers/fpga/versalpl.c2
-rw-r--r--drivers/fpga/xilinx.c7
-rw-r--r--drivers/gpio/74x164_gpio.c17
-rw-r--r--drivers/gpio/qcom_pmic_gpio.c1
-rw-r--r--drivers/gpio/qcom_spmi_gpio.c2
-rw-r--r--drivers/i2c/Kconfig20
-rw-r--r--drivers/i2c/Makefile1
-rw-r--r--drivers/i2c/designware_i2c_pci.c2
-rw-r--r--drivers/i2c/soft_i2c.c418
-rw-r--r--drivers/iommu/apple_dart.c1
-rw-r--r--drivers/mailbox/Kconfig2
-rw-r--r--drivers/mailbox/mpfs-mbox.c106
-rw-r--r--drivers/mmc/Kconfig2
-rw-r--r--drivers/mmc/bcm2835_sdhci.c4
-rw-r--r--drivers/mmc/bcmstb_sdhci.c6
-rw-r--r--drivers/mmc/msm_sdhci.c3
-rw-r--r--drivers/mmc/mtk-sd.c4
-rw-r--r--drivers/mmc/sdhci-cadence.c108
-rw-r--r--drivers/mmc/sdhci-cadence.h5
-rw-r--r--drivers/mmc/sdhci-cadence6.c45
-rw-r--r--drivers/mtd/nand/raw/sunxi_nand.c291
-rw-r--r--drivers/mtd/nand/raw/sunxi_nand.h3
-rw-r--r--drivers/mtd/nand/raw/sunxi_nand_spl.c129
-rw-r--r--drivers/net/Kconfig6
-rw-r--r--drivers/net/fsl_enetc.c57
-rw-r--r--drivers/net/fsl_enetc.h1
-rw-r--r--drivers/net/fsl_enetc_xpcs_phy.c970
-rw-r--r--drivers/net/macb.c94
-rw-r--r--drivers/net/macb.h2
-rw-r--r--drivers/net/phy/Kconfig4
-rw-r--r--drivers/net/phy/adin.c13
-rw-r--r--drivers/net/phy/airoha/Kconfig1
-rw-r--r--drivers/net/phy/airoha/air_en8811.c60
-rw-r--r--drivers/net/phy/aquantia.c2
-rw-r--r--drivers/net/phy/dp83867.c16
-rw-r--r--drivers/net/phy/mscc.c11
-rw-r--r--drivers/net/sandbox.c2
-rw-r--r--drivers/net/ti/cpsw.c188
-rw-r--r--drivers/net/ti/icssg_prueth.c5
-rw-r--r--drivers/net/xilinx_axi_emac.c20
-rw-r--r--drivers/net/zynq_gem.c61
-rw-r--r--drivers/nvme/nvme-uclass.c2
-rw-r--r--drivers/nvme/nvme.c57
-rw-r--r--drivers/nvme/nvme_apple.c5
-rw-r--r--drivers/pci/Kconfig11
-rw-r--r--drivers/pci/Makefile1
-rw-r--r--drivers/pci/pci-uclass.c2
-rw-r--r--drivers/pci/pcie_brcmstb.c387
-rw-r--r--drivers/pci/pcie_dw_amd.c250
-rw-r--r--drivers/phy/Kconfig19
-rw-r--r--drivers/phy/Makefile2
-rw-r--r--drivers/phy/phy-common-props.c286
-rw-r--r--drivers/phy/phy-mtk-ufs.c128
-rw-r--r--drivers/phy/phy-mtk-xsphy.c600
-rw-r--r--drivers/phy/qcom/Kconfig8
-rw-r--r--drivers/phy/qcom/Makefile1
-rw-r--r--drivers/phy/qcom/phy-qcom-qmp-combo.c643
-rw-r--r--drivers/phy/qcom/phy-qcom-qmp-common.h62
-rw-r--r--drivers/phy/qcom/phy-qcom-qmp-dp-com-v3.h18
-rw-r--r--drivers/phy/qcom/phy-qcom-qmp-pcs-usb-v4.h34
-rw-r--r--drivers/phy/qcom/phy-qcom-qmp-ufs.c88
-rw-r--r--drivers/phy/qcom/phy-qcom-qmp.h17
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt7981.c16
-rw-r--r--drivers/pinctrl/pinctrl-apple.c1
-rw-r--r--drivers/pinctrl/qcom/Kconfig8
-rw-r--r--drivers/pinctrl/qcom/Makefile1
-rw-r--r--drivers/pinctrl/qcom/pinctrl-milos.c103
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sc7280.c522
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sunxi.c1
-rw-r--r--drivers/power/domain/Kconfig16
-rw-r--r--drivers/power/domain/Makefile2
-rw-r--r--drivers/power/domain/apple-pmgr.c1
-rw-r--r--drivers/power/domain/power-domain-uclass.c7
-rw-r--r--drivers/power/domain/qcom-rpmhpd.c278
-rw-r--r--drivers/power/domain/renesas-r8a78000-power-domain.c427
-rw-r--r--drivers/power/pmic/mtk-pwrap.c118
-rw-r--r--drivers/power/regulator/Kconfig10
-rw-r--r--drivers/power/regulator/Makefile2
-rw-r--r--drivers/power/regulator/pfuze100.c164
-rw-r--r--drivers/power/regulator/qcom-rpmh-regulator.c139
-rw-r--r--drivers/power/regulator/tps65941_regulator.c15
-rw-r--r--drivers/remoteproc/Kconfig9
-rw-r--r--drivers/remoteproc/Makefile1
-rw-r--r--drivers/remoteproc/renesas_rsip.c358
-rw-r--r--drivers/reset/Kconfig28
-rw-r--r--drivers/reset/Makefile4
-rw-r--r--drivers/reset/reset-brcmstb-rescal.c103
-rw-r--r--drivers/reset/reset-brcmstb.c97
-rw-r--r--drivers/reset/stm32/stm32-reset-mp21.c2
-rw-r--r--drivers/rtc/ds1307.c152
-rw-r--r--drivers/scsi/scsi.c24
-rw-r--r--drivers/soc/soc_xilinx_zynqmp.c45
-rw-r--r--drivers/spi/Kconfig2
-rw-r--r--drivers/spi/apple_spi.c1
-rw-r--r--drivers/spi/fsl_espi.c6
-rw-r--r--drivers/spi/mtk_snor.c193
-rw-r--r--drivers/spi/sandbox_spi.c17
-rw-r--r--drivers/spi/spi-sunxi.c9
-rw-r--r--drivers/spi/spi-uclass.c22
-rw-r--r--drivers/spi/stm32_spi.c71
-rw-r--r--drivers/sysreset/Kconfig15
-rw-r--r--drivers/sysreset/Makefile1
-rw-r--r--drivers/sysreset/sysreset-uclass.c37
-rw-r--r--drivers/sysreset/sysreset_qcom-psci.c45
-rw-r--r--drivers/tee/optee/core.c5
-rw-r--r--drivers/ufs/ufs-mediatek.c42
-rw-r--r--drivers/ufs/ufs-mediatek.h2
-rw-r--r--drivers/ufs/ufs-renesas-rcar-gen5.c16
-rw-r--r--drivers/ufs/ufs-rockchip.c13
-rw-r--r--drivers/ufs/ufs-uclass.c10
-rw-r--r--drivers/usb/dwc3/core.c3
-rw-r--r--drivers/usb/emul/Kconfig1
-rw-r--r--drivers/usb/gadget/Kconfig3
-rw-r--r--drivers/usb/gadget/atmel_usba_udc.c18
-rw-r--r--drivers/usb/gadget/f_acm.c12
-rw-r--r--drivers/usb/gadget/rcar/common.c65
-rw-r--r--drivers/usb/gadget/rcar/renesas_usb.h1
-rw-r--r--drivers/usb/host/Makefile2
-rw-r--r--drivers/usb/host/usb-uclass.c2
-rw-r--r--drivers/usb/host/xhci-mvebu.c1
-rw-r--r--drivers/video/Kconfig8
-rw-r--r--drivers/video/Makefile3
-rw-r--r--drivers/video/bcm2835.c1
-rw-r--r--drivers/video/fonts/Makefile10
-rw-r--r--drivers/video/ilitek-ili9806e.c356
-rw-r--r--drivers/video/stm32/stm32_dsi.c7
-rw-r--r--drivers/virtio/virtio_blk.c123
-rw-r--r--drivers/virtio/virtio_mmio.c6
-rw-r--r--drivers/watchdog/Kconfig7
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/apple_wdt.c1
-rw-r--r--drivers/watchdog/renesas_wwdt.c162
158 files changed, 8627 insertions, 1412 deletions
diff --git a/drivers/bootcount/Kconfig b/drivers/bootcount/Kconfig
index 99b6c7534fd..4c0c8d89bb4 100644
--- a/drivers/bootcount/Kconfig
+++ b/drivers/bootcount/Kconfig
@@ -6,7 +6,7 @@ menuconfig BOOTCOUNT_LIMIT
bool "Enable support for checking boot count limit"
help
Enable checking for exceeding the boot count limit.
- More information: https://docs.u-boot.org/en/latest/api/bootcount.html
+ More information: https://docs.u-boot-project.org/en/latest/api/bootcount.html
if BOOTCOUNT_LIMIT
diff --git a/drivers/clk/clk_versal.c b/drivers/clk/clk_versal.c
index 78a2410ca21..2e0c382ef30 100644
--- a/drivers/clk/clk_versal.c
+++ b/drivers/clk/clk_versal.c
@@ -326,6 +326,7 @@ static int __versal_clock_get_parents(struct clock_parent *parents, u32 *data,
parent = &parents[i];
parent->id = data[i] & CLK_PARENTS_ID_MASK;
if (data[i] == DUMMY_PARENT) {
+ parent->id = 0;
strcpy(parent->name, "dummy_name");
parent->flag = 0;
} else {
diff --git a/drivers/clk/mediatek/clk-mt8189.c b/drivers/clk/mediatek/clk-mt8189.c
index 9e640059f11..d11947ee461 100644
--- a/drivers/clk/mediatek/clk-mt8189.c
+++ b/drivers/clk/mediatek/clk-mt8189.c
@@ -1641,6 +1641,46 @@ static const struct mtk_gate mminfra_config_clks[] = {
GATE_MMINFRA_CONFIG1(CLK_MMINFRA_GCE_26M, CLK_TOP_MMINFRA_SEL, 17),
};
+static const struct mtk_gate_regs ufscfg_ao_reg_cg_regs = {
+ .set_ofs = 0x8,
+ .clr_ofs = 0xc,
+ .sta_ofs = 0x4,
+};
+
+#define GATE_UFSCFG_AO_REG_EXT(_id, _parent, _shift) \
+ GATE_FLAGS(_id, _parent, &ufscfg_ao_reg_cg_regs, _shift, \
+ CLK_GATE_SETCLR | CLK_PARENT_EXT)
+
+#define GATE_UFSCFG_AO_REG_TOP(_id, _parent, _shift) \
+ GATE_FLAGS(_id, _parent, &ufscfg_ao_reg_cg_regs, _shift, \
+ CLK_GATE_SETCLR | CLK_PARENT_TOPCKGEN)
+
+static const struct mtk_gate ufs_config_ao_clks[] = {
+ GATE_UFSCFG_AO_REG_EXT(CLK_UFSCFG_AO_REG_UNIPRO_TX_SYM, CLK_PAD_CLK26M, 1),
+ GATE_UFSCFG_AO_REG_EXT(CLK_UFSCFG_AO_REG_UNIPRO_RX_SYM0, CLK_PAD_CLK26M, 2),
+ GATE_UFSCFG_AO_REG_EXT(CLK_UFSCFG_AO_REG_UNIPRO_RX_SYM1, CLK_PAD_CLK26M, 3),
+ GATE_UFSCFG_AO_REG_TOP(CLK_UFSCFG_AO_REG_UNIPRO_SYS, CLK_TOP_U_SEL, 4),
+ GATE_UFSCFG_AO_REG_EXT(CLK_UFSCFG_AO_REG_U_SAP_CFG, CLK_PAD_CLK26M, 5),
+ GATE_UFSCFG_AO_REG_TOP(CLK_UFSCFG_AO_REG_U_PHY_TOP_AHB_S_BUS, CLK_TOP_AXI_U_SEL, 6),
+};
+
+static const struct mtk_gate_regs ufscfg_pdn_reg_cg_regs = {
+ .set_ofs = 0x8,
+ .clr_ofs = 0xc,
+ .sta_ofs = 0x4,
+};
+
+#define GATE_UFSCFG_PDN_REG(_id, _parent, _shift) \
+ GATE_FLAGS(_id, _parent, &ufscfg_pdn_reg_cg_regs, _shift, \
+ CLK_GATE_SETCLR | CLK_PARENT_TOPCKGEN)
+
+static const struct mtk_gate ufs_config_pdn_clks[] = {
+ GATE_UFSCFG_PDN_REG(CLK_UFSCFG_REG_UFSHCI_UFS, CLK_TOP_U_SEL, 0),
+ GATE_UFSCFG_PDN_REG(CLK_UFSCFG_REG_UFSHCI_AES, CLK_TOP_AES_UFSFDE_SEL, 1),
+ GATE_UFSCFG_PDN_REG(CLK_UFSCFG_REG_UFSHCI_U_AHB, CLK_TOP_AXI_U_SEL, 3),
+ GATE_UFSCFG_PDN_REG(CLK_UFSCFG_REG_UFSHCI_U_AXI, CLK_TOP_MEM_SUB_U_SEL, 5),
+};
+
static const struct mtk_parent vlp_26m_oscd10_parents[] = {
EXT_PARENT(CLK_PAD_CLK26M),
TOP_PARENT(CLK_TOP_OSC_D10),
@@ -1955,6 +1995,8 @@ GATE_CLK_DATA(perao_clks);
GATE_CLK_DATA(imp_clks);
GATE_CLK_DATA(mm_clks);
GATE_CLK_DATA(mminfra_config_clks);
+GATE_CLK_DATA(ufs_config_ao_clks);
+GATE_CLK_DATA(ufs_config_pdn_clks);
GATE_CLK_DATA(vlpcfg_ao_clks);
static const struct udevice_id of_match_mt8189_clk_gate[] = {
@@ -1962,6 +2004,8 @@ static const struct udevice_id of_match_mt8189_clk_gate[] = {
{ .compatible = "mediatek,mt8189-iic-wrap", .data = (ulong)&imp_clks_data },
{ .compatible = "mediatek,mt8189-dispsys", .data = (ulong)&mm_clks_data },
{ .compatible = "mediatek,mt8189-mm-infra", .data = (ulong)&mminfra_config_clks_data },
+ { .compatible = "mediatek,mt8189-ufscfg-ao", .data = (ulong)&ufs_config_ao_clks_data },
+ { .compatible = "mediatek,mt8189-ufscfg-pdn", .data = (ulong)&ufs_config_pdn_clks_data },
{ .compatible = "mediatek,mt8189-vlpcfg-ao", .data = (ulong)&vlpcfg_ao_clks_data },
{ }
};
diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c
index 3557aeac3d5..9d0a6cd79cf 100644
--- a/drivers/clk/mediatek/clk-mtk.c
+++ b/drivers/clk/mediatek/clk-mtk.c
@@ -204,10 +204,6 @@ static ulong mtk_clk_find_parent_rate(struct clk *clk, int id,
return clk_get_rate(&parent);
}
-const struct clk_ops mtk_clk_apmixedsys_ops;
-const struct clk_ops mtk_clk_topckgen_ops;
-const struct clk_ops mtk_clk_infrasys_ops;
-
static ulong mtk_find_parent_rate(struct mtk_clk_priv *priv, struct clk *clk,
const int parent, u16 flags)
{
@@ -216,15 +212,21 @@ static ulong mtk_find_parent_rate(struct mtk_clk_priv *priv, struct clk *clk,
switch (flags & CLK_PARENT_MASK) {
case CLK_PARENT_APMIXED:
/* APMIXEDSYS can be parent or grandparent. */
- if (dev_get_driver_ops(clk->dev) == &mtk_clk_apmixedsys_ops)
+ if (dev_get_driver_ops(clk->dev) == &mtk_clk_apmixedsys_ops ||
+ dev_get_driver_ops(clk->dev) == &mtk_clk_fixed_pll_ops) {
parent_dev = clk->dev;
- else if (dev_get_driver_ops(priv->parent) == &mtk_clk_apmixedsys_ops)
+ } else if (dev_get_driver_ops(priv->parent) == &mtk_clk_apmixedsys_ops ||
+ dev_get_driver_ops(priv->parent) == &mtk_clk_fixed_pll_ops) {
parent_dev = priv->parent;
- else if (dev_get_driver_ops(dev_get_parent(priv->parent)) == &mtk_clk_apmixedsys_ops)
- parent_dev = dev_get_parent(priv->parent);
- else
- return -EINVAL;
+ } else {
+ struct udevice *grandparent_dev = dev_get_parent(priv->parent);
+ if (dev_get_driver_ops(grandparent_dev) == &mtk_clk_apmixedsys_ops ||
+ dev_get_driver_ops(grandparent_dev) == &mtk_clk_fixed_pll_ops)
+ parent_dev = grandparent_dev;
+ else
+ return -EINVAL;
+ }
break;
case CLK_PARENT_TOPCKGEN:
if (dev_get_driver_ops(clk->dev) == &mtk_clk_topckgen_ops)
diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
index a7a42b2edb6..f33938e1419 100644
--- a/drivers/clk/meson/g12a.c
+++ b/drivers/clk/meson/g12a.c
@@ -946,6 +946,11 @@ static ulong meson_clk_set_rate_by_id(struct clk *clk, unsigned long id,
case CLKID_HDMI:
return meson_clk_set_rate_by_id(clk, CLKID_HDMI_DIV,
rate, current_rate);
+ case CLKID_SD_EMMC_A_CLK0:
+ case CLKID_SD_EMMC_B_CLK0:
+ case CLKID_SD_EMMC_C_CLK0:
+ /* TOFIX: implement rate set for MMC clocks */
+ return 0;
default:
return -ENOENT;
}
diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c
index 218be45c2cb..bf46f4d50bc 100644
--- a/drivers/clk/meson/gxbb.c
+++ b/drivers/clk/meson/gxbb.c
@@ -900,6 +900,11 @@ static ulong meson_clk_set_rate_by_id(struct clk *clk, unsigned long id,
case CLKID_HDMI:
return meson_clk_set_rate_by_id(clk, CLKID_HDMI_DIV,
rate, current_rate);
+ case CLKID_SD_EMMC_A_CLK0:
+ case CLKID_SD_EMMC_B_CLK0:
+ case CLKID_SD_EMMC_C_CLK0:
+ /* TOFIX: implement rate set for MMC clocks */
+ return 0;
default:
return -ENOENT;
}
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 8504ed5d656..0a2ce55aaa2 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -47,6 +47,14 @@ config CLK_QCOM_IPQ9574
on the Snapdragon IPQ9574 SoC. This driver supports the clocks
and resets exposed by the GCC hardware block.
+config CLK_QCOM_MILOS
+ bool "Qualcomm Milos GCC"
+ select CLK_QCOM
+ help
+ Say Y here to enable support for the Global Clock Controller
+ on the Snapdragon Milos SoC. This driver supports the clocks
+ and resets exposed by the GCC hardware block.
+
config CLK_QCOM_QCM2290
bool "Qualcomm QCM2290 GCC"
select CLK_QCOM
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 82a5b166196..b96d61b603e 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_CLK_QCOM_APQ8096) += clock-apq8096.o
obj-$(CONFIG_CLK_QCOM_IPQ4019) += clock-ipq4019.o
obj-$(CONFIG_CLK_QCOM_IPQ5424) += clock-ipq5424.o
obj-$(CONFIG_CLK_QCOM_IPQ9574) += clock-ipq9574.o
+obj-$(CONFIG_CLK_QCOM_MILOS) += clock-milos.o
obj-$(CONFIG_CLK_QCOM_QCM2290) += clock-qcm2290.o
obj-$(CONFIG_CLK_QCOM_QCS404) += clock-qcs404.o
obj-$(CONFIG_CLK_QCOM_QCS8300) += clock-qcs8300.o
diff --git a/drivers/clk/qcom/clock-milos.c b/drivers/clk/qcom/clock-milos.c
new file mode 100644
index 00000000000..afe59108559
--- /dev/null
+++ b/drivers/clk/qcom/clock-milos.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Clock drivers for Qualcomm Milos
+ *
+ * (C) Copyright 2024 Linaro Ltd.
+ * (C) Copyright 2026 Luca Weiss <[email protected]>
+ */
+
+#include <clk-uclass.h>
+#include <dm.h>
+#include <linux/delay.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <linux/bug.h>
+#include <linux/bitops.h>
+#include <dt-bindings/clock/qcom,milos-gcc.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+
+#include "clock-qcom.h"
+
+/* On-board TCXO, TOFIX get from DT */
+#define TCXO_RATE 76800000
+
+/* bi_tcxo_div4 divided after RPMh output */
+#define TCXO_DIV4_RATE (TCXO_RATE / 4)
+
+static const struct freq_tbl ftbl_gcc_qupv3_wrap0_s3_clk_src[] = {
+ F(7372800, CFG_CLK_SRC_GPLL0_EVEN, 1, 384, 15625),
+ F(14745600, CFG_CLK_SRC_GPLL0_EVEN, 1, 768, 15625),
+ F(19200000, CFG_CLK_SRC_CXO, 1, 0, 0),
+ F(29491200, CFG_CLK_SRC_GPLL0_EVEN, 1, 1536, 15625),
+ F(32000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 8, 75),
+ F(48000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 4, 25),
+ F(51200000, CFG_CLK_SRC_GPLL0_EVEN, 1, 64, 375),
+ F(64000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 16, 75),
+ F(75000000, CFG_CLK_SRC_GPLL0_EVEN, 4, 0, 0),
+ F(80000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 4, 15),
+ F(96000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 8, 25),
+ F(100000000, CFG_CLK_SRC_GPLL0, 6, 0, 0),
+ { }
+};
+
+static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk_src[] = {
+ F(400000, CFG_CLK_SRC_CXO, 12, 1, 4),
+ F(25000000, CFG_CLK_SRC_GPLL0_EVEN, 12, 0, 0),
+ F(37500000, CFG_CLK_SRC_GPLL0_EVEN, 8, 0, 0),
+ F(50000000, CFG_CLK_SRC_GPLL0_EVEN, 6, 0, 0),
+ F(100000000, CFG_CLK_SRC_GPLL0_EVEN, 3, 0, 0),
+ /* TOFIX F(202000000, CFG_CLK_SRC_GPLL9, 4, 0, 0), */
+ { }
+};
+
+static const struct freq_tbl ftbl_gcc_usb30_prim_master_clk_src[] = {
+ F(66666667, CFG_CLK_SRC_GPLL0_EVEN, 4.5, 0, 0),
+ F(133333333, CFG_CLK_SRC_GPLL0, 4.5, 0, 0),
+ F(200000000, CFG_CLK_SRC_GPLL0, 3, 0, 0),
+ F(240000000, CFG_CLK_SRC_GPLL0, 2.5, 0, 0),
+ { }
+};
+
+static ulong milos_set_rate(struct clk *clk, ulong rate)
+{
+ struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+ const struct freq_tbl *freq;
+
+ switch (clk->id) {
+ case GCC_QUPV3_WRAP0_S5_CLK: /* UART5 */
+ freq = qcom_find_freq(ftbl_gcc_qupv3_wrap0_s3_clk_src, rate);
+ clk_rcg_set_rate_mnd(priv->base, 0x18500,
+ freq->pre_div, freq->m, freq->n, freq->src, 16);
+ return freq->freq;
+ case GCC_SDCC2_APPS_CLK:
+ freq = qcom_find_freq(ftbl_gcc_sdcc2_apps_clk_src, rate);
+ clk_rcg_set_rate_mnd(priv->base, 0x14018,
+ freq->pre_div, freq->m, freq->n, freq->src, 8);
+ return freq->freq;
+ case GCC_USB30_PRIM_MASTER_CLK:
+ freq = qcom_find_freq(ftbl_gcc_usb30_prim_master_clk_src, rate);
+ clk_rcg_set_rate_mnd(priv->base, 0x3902c,
+ freq->pre_div, freq->m, freq->n, freq->src, 8);
+ return freq->freq;
+ case GCC_USB30_PRIM_MOCK_UTMI_CLK:
+ clk_rcg_set_rate(priv->base, 0x39044, 0, 0);
+ return TCXO_DIV4_RATE;
+ default:
+ return 0;
+ }
+}
+
+static const struct gate_clk milos_clks[] = {
+ GATE_CLK(GCC_AGGRE_USB3_PRIM_AXI_CLK, 0x39090, BIT(0)),
+ GATE_CLK(GCC_QUPV3_WRAP0_S5_CLK, 0x52008, BIT(27)),
+ GATE_CLK(GCC_QUPV3_WRAP_0_M_AHB_CLK, 0x52008, BIT(20)),
+ GATE_CLK(GCC_QUPV3_WRAP_0_S_AHB_CLK, 0x52008, BIT(21)),
+ GATE_CLK(GCC_SDCC2_AHB_CLK, 0x14010, BIT(0)),
+ GATE_CLK(GCC_SDCC2_APPS_CLK, 0x14004, BIT(0)),
+ GATE_CLK(GCC_USB30_PRIM_MASTER_CLK, 0x39018, BIT(0)),
+ GATE_CLK(GCC_USB30_PRIM_MOCK_UTMI_CLK, 0x39028, BIT(0)),
+ GATE_CLK(GCC_USB30_PRIM_SLEEP_CLK, 0x39024, BIT(0)),
+ GATE_CLK(GCC_CFG_NOC_USB3_PRIM_AXI_CLK, 0x3908c, BIT(0)),
+};
+
+static int milos_enable(struct clk *clk)
+{
+ struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+
+ switch (clk->id) {
+ case GCC_AGGRE_USB3_PRIM_AXI_CLK:
+ qcom_gate_clk_en(priv, GCC_USB30_PRIM_MASTER_CLK);
+ break;
+ }
+
+ return qcom_gate_clk_en(priv, clk->id);
+}
+
+static const struct qcom_reset_map milos_gcc_resets[] = {
+ [GCC_QUSB2PHY_PRIM_BCR] = { 0x12000 },
+ [GCC_SDCC1_BCR] = { 0xa3000 },
+ [GCC_SDCC2_BCR] = { 0x14000 },
+ [GCC_UFS_PHY_BCR] = { 0x77000 },
+ [GCC_USB30_PRIM_BCR] = { 0x39000 },
+};
+
+static const struct qcom_power_map milos_gdscs[] = {
+ [UFS_PHY_GDSC] = { 0x77004 },
+ [UFS_MEM_PHY_GDSC] = { 0x9e000 },
+ [USB30_PRIM_GDSC] = { 0x39004 },
+};
+
+static struct msm_clk_data milos_gcc_data = {
+ .resets = milos_gcc_resets,
+ .num_resets = ARRAY_SIZE(milos_gcc_resets),
+ .clks = milos_clks,
+ .num_clks = ARRAY_SIZE(milos_clks),
+ .power_domains = milos_gdscs,
+ .num_power_domains = ARRAY_SIZE(milos_gdscs),
+
+ .enable = milos_enable,
+ .set_rate = milos_set_rate,
+};
+
+static const struct udevice_id gcc_milos_of_match[] = {
+ {
+ .compatible = "qcom,milos-gcc",
+ .data = (ulong)&milos_gcc_data,
+ },
+ { }
+};
+
+U_BOOT_DRIVER(gcc_milos) = {
+ .name = "gcc_milos",
+ .id = UCLASS_NOP,
+ .of_match = gcc_milos_of_match,
+ .bind = qcom_cc_bind,
+ .flags = DM_FLAG_PRE_RELOC | DM_FLAG_DEFAULT_PD_CTRL_OFF,
+};
+
+static ulong milos_rpmh_clk_set_rate(struct clk *clk, ulong rate)
+{
+ return (clk->rate = rate);
+}
+
+static ulong milos_rpmh_clk_get_rate(struct clk *clk)
+{
+ switch (clk->id) {
+ case RPMH_CXO_CLK:
+ return TCXO_DIV4_RATE;
+ default:
+ return clk->rate;
+ }
+}
+
+static int milos_rpmh_clk_nop(struct clk *clk)
+{
+ return 0;
+}
+
+static struct clk_ops milos_rpmh_clk_ops = {
+ .set_rate = milos_rpmh_clk_set_rate,
+ .get_rate = milos_rpmh_clk_get_rate,
+ .enable = milos_rpmh_clk_nop,
+ .disable = milos_rpmh_clk_nop,
+};
+
+static const struct udevice_id milos_rpmh_clk_ids[] = {
+ { .compatible = "qcom,milos-rpmh-clk" },
+ { }
+};
+
+U_BOOT_DRIVER(milos_rpmh_clk) = {
+ .name = "milos_rpmh_clk",
+ .id = UCLASS_CLK,
+ .of_match = milos_rpmh_clk_ids,
+ .ops = &milos_rpmh_clk_ops,
+ .flags = DM_FLAG_DEFAULT_PD_CTRL_OFF,
+};
diff --git a/drivers/clk/qcom/clock-sc7280.c b/drivers/clk/qcom/clock-sc7280.c
index 7b6ed826023..01c8587ac39 100644
--- a/drivers/clk/qcom/clock-sc7280.c
+++ b/drivers/clk/qcom/clock-sc7280.c
@@ -116,6 +116,7 @@ static const struct gate_clk sc7280_clks[] = {
GATE_CLK(GCC_USB30_PRIM_MOCK_UTMI_CLK, 0xf01c, 1),
GATE_CLK(GCC_USB3_PRIM_PHY_AUX_CLK, 0xf054, 1),
GATE_CLK(GCC_USB3_PRIM_PHY_COM_AUX_CLK, 0xf058, 1),
+ GATE_CLK(GCC_USB3_PRIM_PHY_PIPE_CLK, 0xf05c, 1),
GATE_CLK(GCC_CFG_NOC_USB3_SEC_AXI_CLK, 0x9e07c, 1),
GATE_CLK(GCC_USB30_SEC_MASTER_CLK, 0x9e010, 1),
GATE_CLK(GCC_AGGRE_USB3_SEC_AXI_CLK, 0x9e080, 1),
@@ -155,6 +156,8 @@ static const struct gate_clk sc7280_clks[] = {
GATE_CLK(GCC_UFS_1_CLKREF_EN, 0x8c000, BIT(0)),
GATE_CLK(GCC_SDCC2_AHB_CLK, 0x14008, BIT(0)),
GATE_CLK(GCC_SDCC2_APPS_CLK, 0x14004, BIT(0)),
+ GATE_CLK(GCC_SDCC1_AHB_CLK, 0x75004, BIT(0)),
+ GATE_CLK(GCC_SDCC1_APPS_CLK, 0x75008, BIT(0)),
};
static int sc7280_enable(struct clk *clk)
diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig
index 51c87cc3606..72f99e9fa1b 100644
--- a/drivers/clk/renesas/Kconfig
+++ b/drivers/clk/renesas/Kconfig
@@ -157,6 +157,12 @@ config CLK_R8A779H0
help
Enable this to support the clocks on Renesas R8A779H0 SoC.
+config CLK_R8A78000
+ bool "Renesas R8A78000 clock driver"
+ depends on CLK_RENESAS
+ help
+ Enable this to support the clocks on Renesas R8A78000 SoC.
+
config CLK_R9A06G032
bool "Renesas R9A06G032 clock driver"
depends on CLK_RENESAS
diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile
index 354035baf2d..fb8d4c1f2f6 100644
--- a/drivers/clk/renesas/Makefile
+++ b/drivers/clk/renesas/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_CLK_R8A779A0) += r8a779a0-cpg-mssr.o
obj-$(CONFIG_CLK_R8A779F0) += r8a779f0-cpg-mssr.o
obj-$(CONFIG_CLK_R8A779G0) += r8a779g0-cpg-mssr.o
obj-$(CONFIG_CLK_R8A779H0) += r8a779h0-cpg-mssr.o
+obj-$(CONFIG_CLK_R8A78000) += r8a78000-cpg.o
obj-$(CONFIG_CLK_R9A06G032) += r9a06g032-clocks.o
obj-$(CONFIG_CLK_RZG2L) += rzg2l-cpg.o
obj-$(CONFIG_CLK_R9A07G044) += r9a07g044-cpg.o
diff --git a/drivers/clk/renesas/r8a78000-cpg.c b/drivers/clk/renesas/r8a78000-cpg.c
new file mode 100644
index 00000000000..e9ca06476f6
--- /dev/null
+++ b/drivers/clk/renesas/r8a78000-cpg.c
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Renesas R-Car Gen5 CPG driver
+ *
+ * Copyright (C) 2026 Marek Vasut <[email protected]>
+ */
+
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <linux/clk-provider.h>
+
+#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
+#include <scmi_protocols.h>
+
+#include <dt-bindings/clock/r8a78000-clock-scmi.h>
+
+#if IS_ENABLED(CONFIG_CLK_SCMI)
+struct gen5_clk_priv {
+ struct udevice *clk;
+ u32 basever;
+};
+
+static struct clk *gen5_clk_get_by_scmi_id(struct clk *clk)
+{
+ struct gen5_clk_priv *priv = dev_get_priv(clk->dev);
+ struct udevice *sdev;
+ struct uclass *uc;
+
+ uclass_id_foreach_dev(UCLASS_CLK, sdev, uc)
+ if (sdev->seq_ == priv->clk->seq_ + clk->id + 1)
+ return dev_get_clk_ptr(sdev);
+
+ return NULL;
+}
+
+static ulong gen5_clk_round_rate(struct clk *clk, ulong rate)
+{
+ struct clk *scmi = gen5_clk_get_by_scmi_id(clk);
+
+ if (!scmi)
+ return -ENODEV;
+
+ return clk_round_rate(scmi, rate);
+}
+
+static ulong gen5_clk_get_rate(struct clk *clk)
+{
+ struct clk *scmi = gen5_clk_get_by_scmi_id(clk);
+
+ if (!scmi)
+ return -ENODEV;
+
+ return clk_get_rate(scmi);
+}
+
+static ulong gen5_clk_set_rate(struct clk *clk, ulong rate)
+{
+ struct clk *scmi = gen5_clk_get_by_scmi_id(clk);
+
+ if (!scmi)
+ return -ENODEV;
+
+ return clk_set_rate(scmi, rate);
+}
+
+static int gen5_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct clk *scmi = gen5_clk_get_by_scmi_id(clk);
+
+ if (!scmi)
+ return -ENODEV;
+
+ return clk_set_parent(scmi, parent);
+}
+
+static int gen5_clk_enable(struct clk *clk)
+{
+ struct clk *scmi = gen5_clk_get_by_scmi_id(clk);
+
+ if (!scmi)
+ return -ENODEV;
+
+ return clk_enable(scmi);
+}
+
+static int gen5_clk_disable(struct clk *clk)
+{
+ struct clk *scmi = gen5_clk_get_by_scmi_id(clk);
+
+ if (!scmi)
+ return -ENODEV;
+
+ return clk_disable(scmi);
+}
+
+struct clk_map_in {
+ u16 dt_id; /* DT binding clock ID */
+ u16 fw_id; /* SCMI firmware clock ID */
+};
+
+#define GEN5_SCMI_SDK_4_28 0x010a0000
+#define GEN5_SCMI_SDK_4_29 0x010b0000
+#define GEN5_SCMI_SDK_4_30 0x010c0000
+#define GEN5_SCMI_SDK_4_31 0x010d0000
+#define GEN5_SCMI_SDK_4_32 0x010e0000
+
+static const struct clk_map_in gen5_clk_map_dt_sdk_4_28[] = {
+ { SCP_CLOCK_ID_MDLC_UFS0, 202 },
+ { SCP_CLOCK_ID_MDLC_UFS1, 203 },
+ { SCP_CLOCK_ID_MDLC_SDHI0, 204 },
+ { SCP_CLOCK_ID_MDLC_XPCS0, 316 },
+ { SCP_CLOCK_ID_MDLC_XPCS1, 317 },
+ { SCP_CLOCK_ID_MDLC_XPCS2, 318 },
+ { SCP_CLOCK_ID_MDLC_XPCS3, 319 },
+ { SCP_CLOCK_ID_MDLC_XPCS4, 320 },
+ { SCP_CLOCK_ID_MDLC_XPCS5, 321 },
+ { SCP_CLOCK_ID_MDLC_XPCS6, 322 },
+ { SCP_CLOCK_ID_MDLC_XPCS7, 323 },
+ { SCP_CLOCK_ID_MDLC_RSW3, 324 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSN, 325 },
+ { SCP_CLOCK_ID_MDLC_RSW3AES, 326 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES0, 327 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES1, 328 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES2, 329 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES3, 330 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES4, 331 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES5, 332 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES6, 333 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES7, 334 },
+ { SCP_CLOCK_ID_MDLC_RSW3MFWD, 335 },
+ { SCP_CLOCK_ID_MDLC_MPPHY01, 344 },
+ { SCP_CLOCK_ID_MDLC_MPPHY11, 345 },
+ { SCP_CLOCK_ID_MDLC_MPPHY21, 346 },
+ { SCP_CLOCK_ID_MDLC_MPPHY31, 347 },
+ { SCP_CLOCK_ID_MDLC_MPPHY02, 348 },
+ { SCP_CLOCK_ID_CLK_S0D6_PERE_MAIN, 1691 },
+};
+
+static const struct clk_map_in gen5_clk_map_dt_sdk_4_31[] = {
+ { SCP_CLOCK_ID_MDLC_UFS0, 198 },
+ { SCP_CLOCK_ID_MDLC_UFS1, 199 },
+ { SCP_CLOCK_ID_MDLC_SDHI0, 200 },
+ { SCP_CLOCK_ID_MDLC_XPCS0, 312 },
+ { SCP_CLOCK_ID_MDLC_XPCS1, 313 },
+ { SCP_CLOCK_ID_MDLC_XPCS2, 314 },
+ { SCP_CLOCK_ID_MDLC_XPCS3, 315 },
+ { SCP_CLOCK_ID_MDLC_XPCS4, 316 },
+ { SCP_CLOCK_ID_MDLC_XPCS5, 317 },
+ { SCP_CLOCK_ID_MDLC_XPCS6, 318 },
+ { SCP_CLOCK_ID_MDLC_XPCS7, 319 },
+ { SCP_CLOCK_ID_MDLC_RSW3, 320 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSN, 321 },
+ { SCP_CLOCK_ID_MDLC_RSW3AES, 322 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES0, 323 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES1, 324 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES2, 325 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES3, 326 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES4, 327 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES5, 328 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES6, 329 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES7, 330 },
+ { SCP_CLOCK_ID_MDLC_RSW3MFWD, 331 },
+ { SCP_CLOCK_ID_MDLC_MPPHY01, 340 },
+ { SCP_CLOCK_ID_MDLC_MPPHY11, 341 },
+ { SCP_CLOCK_ID_MDLC_MPPHY21, 342 },
+ { SCP_CLOCK_ID_MDLC_MPPHY31, 343 },
+ { SCP_CLOCK_ID_MDLC_MPPHY02, 344 },
+ { SCP_CLOCK_ID_CLK_S0D6_PERE_MAIN, 1687 },
+};
+
+static int gen5_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
+{
+ struct gen5_clk_priv *priv = dev_get_priv(clk->dev);
+ const struct clk_map_in *map;
+ unsigned int map_size;
+ int i;
+
+ if (args->args_count != 1) {
+ debug("Invalid args_count: %d\n", args->args_count);
+ return -EINVAL;
+ }
+
+ if (priv->basever == GEN5_SCMI_SDK_4_28) {
+ map = gen5_clk_map_dt_sdk_4_28;
+ map_size = ARRAY_SIZE(gen5_clk_map_dt_sdk_4_28);
+ } else if (priv->basever == GEN5_SCMI_SDK_4_31 ||
+ priv->basever == GEN5_SCMI_SDK_4_32) {
+ map = gen5_clk_map_dt_sdk_4_31;
+ map_size = ARRAY_SIZE(gen5_clk_map_dt_sdk_4_31);
+ } else {
+ printf("Unsupported SCMI base protocol version %x\n", priv->basever);
+ return -EINVAL;
+ }
+
+ clk->id = -1;
+ for (i = 0; i < map_size; i++) {
+ if (map[i].dt_id != args->args[0])
+ continue;
+ clk->id = map[i].fw_id;
+ break;
+ }
+
+ if (clk->id == -1)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct clk_ops gen5_clk_ops = {
+ .round_rate = gen5_clk_round_rate,
+ .get_rate = gen5_clk_get_rate,
+ .set_rate = gen5_clk_set_rate,
+ .set_parent = gen5_clk_set_parent,
+ .enable = gen5_clk_enable,
+ .disable = gen5_clk_disable,
+ .of_xlate = gen5_clk_of_xlate,
+};
+
+static int gen5_clk_probe(struct udevice *dev)
+{
+ struct gen5_clk_priv *priv = dev_get_priv(dev);
+ struct udevice *agent;
+ int ret;
+
+ ret = uclass_get_device(UCLASS_SCMI_AGENT, 0, &agent);
+ if (ret)
+ return ret;
+
+ if (!agent)
+ return -ENODEV;
+
+ priv->basever = scmi_impl_version(agent);
+
+ return uclass_get_device_by_driver(UCLASS_CLK, DM_DRIVER_GET(scmi_clock),
+ &priv->clk);
+}
+#else
+static int gen5_clk_enable(struct clk *clk)
+{
+ return 0;
+}
+
+static int gen5_clk_disable(struct clk *clk)
+{
+ return 0;
+}
+
+static int gen5_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
+{
+ if (args->args_count != 1) {
+ debug("Invalid args_count: %d\n", args->args_count);
+ return -EINVAL;
+ }
+
+ clk->id = args->args[0];
+
+ return 0;
+}
+
+static const struct clk_ops gen5_clk_ops = {
+ .enable = gen5_clk_enable,
+ .disable = gen5_clk_disable,
+ .of_xlate = gen5_clk_of_xlate,
+};
+#endif
+
+static const struct udevice_id r8a78000_mdlc_ids[] = {
+ { .compatible = "renesas,r8a78000-cpg", },
+ { }
+};
+
+U_BOOT_DRIVER(clk_gen5) = {
+ .name = "clk_gen5",
+ .id = UCLASS_CLK,
+ .of_match = r8a78000_mdlc_ids,
+ .priv_auto = CONFIG_IS_ENABLED(CLK_SCMI, (sizeof(struct gen5_clk_priv)), (0)),
+ .ops = &gen5_clk_ops,
+ .probe = CONFIG_IS_ENABLED(CLK_SCMI, (gen5_clk_probe), (NULL)),
+ .flags = DM_FLAG_OS_PREPARE | DM_FLAG_VITAL,
+};
diff --git a/drivers/clk/rockchip/clk_rk3528.c b/drivers/clk/rockchip/clk_rk3528.c
index bcdc0f930d2..cf8c3a62349 100644
--- a/drivers/clk/rockchip/clk_rk3528.c
+++ b/drivers/clk/rockchip/clk_rk3528.c
@@ -1335,6 +1335,7 @@ static ulong rk3528_clk_get_rate(struct clk *clk)
DPLL);
break;
+ case CLK_REF_USB3OTG:
case TCLK_EMMC:
case TCLK_WDT_NS:
rate = OSC_HZ;
@@ -1455,6 +1456,7 @@ static ulong rk3528_clk_set_rate(struct clk *clk, ulong rate)
priv->ppll_hz = rockchip_pll_get_rate(&rk3528_pll_clks[PPLL],
priv->cru, PPLL);
break;
+ case CLK_REF_USB3OTG:
case TCLK_EMMC:
case TCLK_WDT_NS:
return (rate == OSC_HZ) ? 0 : -EINVAL;
diff --git a/drivers/clk/rockchip/clk_rk3576.c b/drivers/clk/rockchip/clk_rk3576.c
index 1026af27ca1..db8ce25852f 100644
--- a/drivers/clk/rockchip/clk_rk3576.c
+++ b/drivers/clk/rockchip/clk_rk3576.c
@@ -1549,6 +1549,24 @@ static ulong rk3576_gmac_get_clk(struct rk3576_clk_priv *priv, ulong clk_id)
con = readl(&cru->clksel_con[31]);
div = (con & CLK_GMAC1_125M_DIV_MASK) >> CLK_GMAC1_125M_DIV_SHIFT;
return DIV_TO_RATE(priv->cpll_hz, div);
+ case REFCLKO25M_GMAC0_OUT:
+ con = readl(&cru->clksel_con[36]);
+ div = (con & CLK_REFCLKO25M_GMAC0_DIV_MASK) >> CLK_REFCLKO25M_GMAC0_DIV_SHIFT;
+ src = (con & CLK_REFCLKO25M_GMAC0_SEL_MASK) >> CLK_REFCLKO25M_GMAC0_SEL_SHIFT;
+ if (src == CLK_REFCLKO25M_GMAC0_SEL_CPLL)
+ p_rate = priv->cpll_hz;
+ else
+ p_rate = priv->gpll_hz;
+ return DIV_TO_RATE(p_rate, div);
+ case REFCLKO25M_GMAC1_OUT:
+ con = readl(&cru->clksel_con[36]);
+ div = (con & CLK_REFCLKO25M_GMAC1_DIV_MASK) >> CLK_REFCLKO25M_GMAC1_DIV_SHIFT;
+ src = (con & CLK_REFCLKO25M_GMAC1_SEL_MASK) >> CLK_REFCLKO25M_GMAC1_SEL_SHIFT;
+ if (src == CLK_REFCLKO25M_GMAC1_SEL_CPLL)
+ p_rate = priv->cpll_hz;
+ else
+ p_rate = priv->gpll_hz;
+ return DIV_TO_RATE(p_rate, div);
default:
return -ENOENT;
}
@@ -1608,6 +1626,34 @@ static ulong rk3576_gmac_set_clk(struct rk3576_clk_priv *priv,
CLK_GMAC1_125M_DIV_MASK,
(div - 1) << CLK_GMAC1_125M_DIV_SHIFT);
break;
+ case REFCLKO25M_GMAC0_OUT:
+ if (!(priv->gpll_hz % rate)) {
+ src = CLK_REFCLKO25M_GMAC0_SEL_GPLL;
+ div = priv->gpll_hz / rate;
+ } else {
+ src = CLK_REFCLKO25M_GMAC0_SEL_CPLL;
+ div = priv->cpll_hz / rate;
+ }
+ rk_clrsetreg(&cru->clksel_con[36],
+ CLK_REFCLKO25M_GMAC0_SEL_MASK |
+ CLK_REFCLKO25M_GMAC0_DIV_MASK,
+ src << CLK_REFCLKO25M_GMAC0_SEL_SHIFT |
+ (div - 1) << CLK_REFCLKO25M_GMAC0_DIV_SHIFT);
+ break;
+ case REFCLKO25M_GMAC1_OUT:
+ if (!(priv->gpll_hz % rate)) {
+ src = CLK_REFCLKO25M_GMAC1_SEL_GPLL;
+ div = priv->gpll_hz / rate;
+ } else {
+ src = CLK_REFCLKO25M_GMAC1_SEL_CPLL;
+ div = priv->cpll_hz / rate;
+ }
+ rk_clrsetreg(&cru->clksel_con[36],
+ CLK_REFCLKO25M_GMAC1_SEL_MASK |
+ CLK_REFCLKO25M_GMAC1_DIV_MASK,
+ src << CLK_REFCLKO25M_GMAC1_SEL_SHIFT |
+ (div - 1) << CLK_REFCLKO25M_GMAC1_DIV_SHIFT);
+ break;
default:
return -ENOENT;
}
@@ -1987,6 +2033,8 @@ static ulong rk3576_clk_get_rate(struct clk *clk)
case HCLK_SDIO:
rate = rk3576_mmc_get_clk(priv, clk->id);
break;
+ case CLK_REF_USB3OTG0:
+ case CLK_REF_USB3OTG1:
case TCLK_EMMC:
case TCLK_WDT0:
rate = OSC_HZ;
@@ -2014,6 +2062,8 @@ static ulong rk3576_clk_get_rate(struct clk *clk)
case CLK_GMAC1_PTP_REF:
case CLK_GMAC0_125M_SRC:
case CLK_GMAC1_125M_SRC:
+ case REFCLKO25M_GMAC0_OUT:
+ case REFCLKO25M_GMAC1_OUT:
rate = rk3576_gmac_get_clk(priv, clk->id);
break;
case CLK_UART_FRAC_0:
@@ -2151,6 +2201,8 @@ static ulong rk3576_clk_set_rate(struct clk *clk, ulong rate)
case HCLK_SDIO:
ret = rk3576_mmc_set_clk(priv, clk->id, rate);
break;
+ case CLK_REF_USB3OTG0:
+ case CLK_REF_USB3OTG1:
case TCLK_EMMC:
case TCLK_WDT0:
ret = OSC_HZ;
@@ -2193,6 +2245,8 @@ static ulong rk3576_clk_set_rate(struct clk *clk, ulong rate)
case CLK_GMAC1_PTP_REF:
case CLK_GMAC0_125M_SRC:
case CLK_GMAC1_125M_SRC:
+ case REFCLKO25M_GMAC0_OUT:
+ case REFCLKO25M_GMAC1_OUT:
ret = rk3576_gmac_set_clk(priv, clk->id, rate);
break;
case CLK_UART_FRAC_0:
diff --git a/drivers/clk/stm32/Kconfig b/drivers/clk/stm32/Kconfig
index 4e488136eac..e63385d3051 100644
--- a/drivers/clk/stm32/Kconfig
+++ b/drivers/clk/stm32/Kconfig
@@ -49,7 +49,7 @@ config CLK_STM32MP21
config CLK_STM32MP25
bool "Enable RCC clock driver for STM32MP25"
depends on ARCH_STM32MP && CLK
- default y if STM32MP25X
+ default y if STM32MP23X || STM32MP25X
select CLK_STM32_CORE
help
Enable the STM32 clock (RCC) driver. Enable support for
diff --git a/drivers/core/of_access.c b/drivers/core/of_access.c
index b11e36202c1..969492aae37 100644
--- a/drivers/core/of_access.c
+++ b/drivers/core/of_access.c
@@ -598,6 +598,25 @@ int of_read_u64(const struct device_node *np, const char *propname, u64 *outp)
return of_read_u64_index(np, propname, 0, outp);
}
+int of_read_u64_array(const struct device_node *np, const char *propname,
+ u64 *out_values, size_t sz)
+{
+ const __be64 *val;
+
+ log_debug("%s: %s: ", __func__, propname);
+ val = of_find_property_value_of_size(np, propname,
+ sz * sizeof(*out_values));
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ log_debug("size %zd\n", sz);
+ while (sz--)
+ *out_values++ = be64_to_cpup(val++);
+
+ return 0;
+}
+
int of_property_match_string(const struct device_node *np, const char *propname,
const char *string)
{
@@ -845,6 +864,39 @@ int of_count_phandle_with_args(const struct device_node *np,
cell_count);
}
+/**
+ * of_property_count_elems_of_size - Count the number of elements in a property
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @elem_size: size of the individual element
+ *
+ * Search for a property in a device node and count the number of elements of
+ * size elem_size in it.
+ *
+ * Return: The number of elements on sucess, -EINVAL if the property does not
+ * exist or its length does not match a multiple of elem_size and -ENODATA if
+ * the property does not have a value.
+ */
+int of_property_count_elems_of_size(const struct device_node *np,
+ const char *propname, int elem_size)
+{
+ const struct property *prop = of_find_property(np, propname, NULL);
+
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+
+ if (prop->length % elem_size != 0) {
+ pr_err("size of %s in node %pOF is not a multiple of %d\n",
+ propname, np, elem_size);
+ return -EINVAL;
+ }
+
+ return prop->length / elem_size;
+}
+
static void of_alias_add(struct alias_prop *ap, struct device_node *np,
int id, const char *stem, int stem_len)
{
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
index 3a36b6fdd03..d605c0f7b7c 100644
--- a/drivers/core/ofnode.c
+++ b/drivers/core/ofnode.c
@@ -664,6 +664,54 @@ int ofnode_read_u32_array(ofnode node, const char *propname,
}
}
+int ofnode_read_u64_array(ofnode node, const char *propname,
+ u64 *out_values, size_t sz)
+{
+ assert(ofnode_valid(node));
+ log_debug("%s: %s: ", __func__, propname);
+
+ if (ofnode_is_np(node)) {
+ return of_read_u64_array(ofnode_to_np(node), propname,
+ out_values, sz);
+ } else {
+ int ret;
+
+ ret = fdtdec_get_long_array(ofnode_to_fdt(node),
+ ofnode_to_offset(node), propname,
+ out_values, sz);
+
+ /* get the error right, but space is more important in SPL */
+ if (!IS_ENABLED(CONFIG_XPL_BUILD)) {
+ if (ret == -FDT_ERR_NOTFOUND)
+ return -EINVAL;
+ else if (ret == -FDT_ERR_BADLAYOUT)
+ return -EOVERFLOW;
+ }
+ return ret;
+ }
+}
+
+int ofnode_count_elems_of_size(ofnode node, const char *propname, int elem_size)
+{
+ const char *prop;
+ int len;
+ assert(ofnode_valid(node));
+
+ if (ofnode_is_np(node)) {
+ return of_property_count_elems_of_size(node.np, propname, elem_size);
+ } else {
+ prop = fdt_getprop(ofnode_to_fdt(node), ofnode_to_offset(node), propname, &len);
+ if (!prop)
+ return -ENOENT;
+ if (len % elem_size != 0) {
+ log_debug("size of %s in node %pOF is not a multiple of %d\n",
+ propname, &node, elem_size);
+ return -EINVAL;
+ }
+ return len / elem_size;
+ }
+}
+
#if !CONFIG_IS_ENABLED(DM_INLINE_OFNODE)
bool ofnode_is_enabled(ofnode node)
{
diff --git a/drivers/cpu/imx8_cpu.c b/drivers/cpu/imx8_cpu.c
index 785c299eca5..3473712a423 100644
--- a/drivers/cpu/imx8_cpu.c
+++ b/drivers/cpu/imx8_cpu.c
@@ -222,7 +222,7 @@ static int cpu_imx_get_desc(const struct udevice *dev, char *buf, int size)
ret = snprintf(buf, size, "NXP i.MX%s Rev%s %s at %u MHz",
plat->type, plat->rev, plat->name, plat->freq_mhz);
- if (IS_ENABLED(CONFIG_IMX_TMU)) {
+ if (!IS_ENABLED(CONFIG_IMX8)) { /* imx8 does not have segment fuse */
switch (get_cpu_temp_grade(&minc, &maxc)) {
case TEMP_AUTOMOTIVE:
grade = "Automotive temperature grade";
@@ -231,7 +231,10 @@ static int cpu_imx_get_desc(const struct udevice *dev, char *buf, int size)
grade = "Industrial temperature grade";
break;
case TEMP_EXTCOMMERCIAL:
- grade = "Extended Consumer temperature grade";
+ if (IS_ENABLED(CONFIG_ARCH_IMX9))
+ grade = "Extended Industrial temperature grade";
+ else
+ grade = "Extended Consumer temperature grade";
break;
default:
grade = "Consumer temperature grade";
diff --git a/drivers/ddr/imx/imx9/Kconfig b/drivers/ddr/imx/imx9/Kconfig
index 0a45340ffb6..b953bca4f06 100644
--- a/drivers/ddr/imx/imx9/Kconfig
+++ b/drivers/ddr/imx/imx9/Kconfig
@@ -24,9 +24,9 @@ config IMX9_DRAM_INLINE_ECC
config SAVED_DRAM_TIMING_BASE
hex "Define the base address for saved dram timing"
+ default 0x2051C000
help
after DRAM is trained, need to save the dram related timming
info into memory for low power use.
- default 0x2051C000
endmenu
diff --git a/drivers/ddr/imx/phy/helper.c b/drivers/ddr/imx/phy/helper.c
index b0dfc3a0b4f..147ec9ab061 100644
--- a/drivers/ddr/imx/phy/helper.c
+++ b/drivers/ddr/imx/phy/helper.c
@@ -38,6 +38,8 @@ binman_sym_declare(ulong, ddr_2d_dmem_fw, image_pos);
binman_sym_declare(ulong, ddr_2d_dmem_fw, size);
#endif
+binman_sym_declare(ulong, u_boot_spl, image_pos);
+
/* We need PHY iMEM PHY is 32KB padded */
void ddr_load_train_firmware(enum fw_type type)
{
@@ -49,6 +51,7 @@ void ddr_load_train_firmware(enum fw_type type)
unsigned long dmem_start;
unsigned long imem_len = IMEM_LEN, dmem_len = DMEM_LEN;
static enum fw_type last_type = -1;
+ unsigned long spl_start = 0;
/* If FW doesn't change, we can save the loading. */
if (last_type == type)
@@ -67,6 +70,9 @@ void ddr_load_train_firmware(enum fw_type type)
dmem_start = imem_start + imem_len;
if (BINMAN_SYMS_OK) {
+ if (IS_ENABLED(CONFIG_IMX8MQ))
+ spl_start = binman_sym(ulong, u_boot_spl, image_pos);
+
switch (type) {
case FW_1D_IMAGE:
imem_start = binman_sym(ulong, ddr_1d_imem_fw, image_pos);
@@ -83,6 +89,13 @@ void ddr_load_train_firmware(enum fw_type type)
#endif
break;
}
+
+ if (IS_ENABLED(CONFIG_IMX8MQ)) {
+ imem_start -= spl_start;
+ imem_start += CONFIG_SPL_TEXT_BASE;
+ dmem_start -= spl_start;
+ dmem_start += CONFIG_SPL_TEXT_BASE;
+ }
}
pr_from32 = imem_start;
diff --git a/drivers/dfu/Kconfig b/drivers/dfu/Kconfig
index 962bda40ad2..eeae2fd65ad 100644
--- a/drivers/dfu/Kconfig
+++ b/drivers/dfu/Kconfig
@@ -11,7 +11,7 @@ config DFU_OVER_USB
config DFU_OVER_TFTP
bool
- depends on NET
+ depends on NET_LEGACY
config DFU_WRITE_ALT
bool
diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig
index 576c3ef8a45..90212fcf9ef 100644
--- a/drivers/fastboot/Kconfig
+++ b/drivers/fastboot/Kconfig
@@ -27,7 +27,7 @@ config USB_FUNCTION_FASTBOOT
This enables the USB part of the fastboot gadget.
config UDP_FUNCTION_FASTBOOT
- depends on NET
+ depends on NET_LEGACY
select FASTBOOT
bool "Enable fastboot protocol over UDP"
help
@@ -41,7 +41,7 @@ config UDP_FUNCTION_FASTBOOT_PORT
The fastboot protocol requires a UDP port number.
config TCP_FUNCTION_FASTBOOT
- depends on NET
+ depends on NET_LEGACY
select FASTBOOT
bool "Enable fastboot protocol over TCP"
help
diff --git a/drivers/fastboot/fb_common.c b/drivers/fastboot/fb_common.c
index dac5528f809..9c52e004588 100644
--- a/drivers/fastboot/fb_common.c
+++ b/drivers/fastboot/fb_common.c
@@ -191,13 +191,13 @@ void fastboot_handle_boot(int command, bool success)
switch (command) {
case FASTBOOT_COMMAND_BOOT:
fastboot_boot();
-#if CONFIG_IS_ENABLED(NET)
+#if CONFIG_IS_ENABLED(NET_LEGACY)
net_set_state(NETLOOP_SUCCESS);
#endif
break;
case FASTBOOT_COMMAND_CONTINUE:
-#if CONFIG_IS_ENABLED(NET)
+#if CONFIG_IS_ENABLED(NET_LEGACY)
net_set_state(NETLOOP_SUCCESS);
#endif
break;
diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c
index 2e3223e1c32..b6838a244d2 100644
--- a/drivers/firmware/psci.c
+++ b/drivers/firmware/psci.c
@@ -186,6 +186,10 @@ static int psci_bind(struct udevice *dev)
NULL);
if (ret)
pr_debug("PSCI System Reset was not bound.\n");
+ if (IS_ENABLED(CONFIG_SYSRESET_QCOM_PSCI) &&
+ device_bind_driver(dev, "qcom_psci-sysreset",
+ "qcom_psci-sysreset", NULL))
+ pr_debug("QCOM PSCI System Reset was not bound.\n");
}
/* From PSCI v1.0 onward we can discover services through ARM_SMCCC_FEATURE */
diff --git a/drivers/firmware/scmi/sandbox-scmi_agent.c b/drivers/firmware/scmi/sandbox-scmi_agent.c
index 010bf99fbc6..832bfb55711 100644
--- a/drivers/firmware/scmi/sandbox-scmi_agent.c
+++ b/drivers/firmware/scmi/sandbox-scmi_agent.c
@@ -1341,7 +1341,7 @@ static const struct udevice_id sandbox_scmi_test_ids[] = {
{ }
};
-struct scmi_agent_ops sandbox_scmi_test_ops = {
+static const struct scmi_agent_ops sandbox_scmi_test_ops = {
.of_get_channel = sandbox_scmi_of_get_channel,
.process_msg = sandbox_scmi_test_process_msg,
};
diff --git a/drivers/fpga/versalpl.c b/drivers/fpga/versalpl.c
index 630d1ecfea3..3cb56cc0dc9 100644
--- a/drivers/fpga/versalpl.c
+++ b/drivers/fpga/versalpl.c
@@ -17,7 +17,7 @@ static ulong versal_align_dma_buffer(ulong *buf, u32 len)
if ((ulong)buf != ALIGN((ulong)buf, ARCH_DMA_MINALIGN)) {
new_buf = (ulong *)ALIGN((ulong)buf, ARCH_DMA_MINALIGN);
- memcpy(new_buf, buf, len);
+ memmove(new_buf, buf, len);
buf = new_buf;
}
diff --git a/drivers/fpga/xilinx.c b/drivers/fpga/xilinx.c
index 25b348648ef..44d7ad6bd54 100644
--- a/drivers/fpga/xilinx.c
+++ b/drivers/fpga/xilinx.c
@@ -11,6 +11,7 @@
* Xilinx FPGA support
*/
+#include <env.h>
#include <fpga.h>
#include <log.h>
#include <virtex2.h>
@@ -92,7 +93,11 @@ int fpga_loadbitstream(int devnum, char *fpgadata, size_t size,
__func__);
printf("%s: Bitstream ID %s, current device ID %d/%s\n",
__func__, dataptr, devnum, xdesc->name);
- return FPGA_FAIL;
+ if (!CONFIG_IS_ENABLED(ENV_SUPPORT) ||
+ env_get_yesno("fpga_skip_idcheck") != 1)
+ return FPGA_FAIL;
+
+ printf("%s: Skipping ID check\n", __func__);
}
} else {
printf("%s: Please fill correct device ID to xilinx_desc\n",
diff --git a/drivers/gpio/74x164_gpio.c b/drivers/gpio/74x164_gpio.c
index 331428ccdb9..de2926894ec 100644
--- a/drivers/gpio/74x164_gpio.c
+++ b/drivers/gpio/74x164_gpio.c
@@ -10,17 +10,13 @@
#include <errno.h>
#include <dm.h>
-#include <fdtdec.h>
#include <malloc.h>
-#include <asm/global_data.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <dm/device_compat.h>
#include <dt-bindings/gpio/gpio.h>
#include <spi.h>
-DECLARE_GLOBAL_DATA_PTR;
-
/*
* struct gen_74x164_chip - Data for 74Hx164
*
@@ -127,11 +123,9 @@ static int gen_74x164_probe(struct udevice *dev)
{
struct gen_74x164_priv *priv = dev_get_priv(dev);
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ const u8 *defaults;
char *str, name[32];
int ret;
- const void *fdt = gd->fdt_blob;
- int node = dev_of_offset(dev);
-
snprintf(name, sizeof(name), "%s_", dev->name);
str = strdup(name);
if (!str)
@@ -141,16 +135,17 @@ static int gen_74x164_probe(struct udevice *dev)
* See Linux kernel:
* Documentation/devicetree/bindings/gpio/gpio-74x164.txt
*/
- priv->nregs = fdtdec_get_int(fdt, node, "registers-number", 1);
+ priv->nregs = dev_read_u32_default(dev, "registers-number", 1);
priv->buffer = calloc(priv->nregs, sizeof(u8));
if (!priv->buffer) {
ret = -ENOMEM;
goto free_str;
}
- ret = fdtdec_get_byte_array(fdt, node, "registers-default",
- priv->buffer, priv->nregs);
- if (ret)
+ defaults = dev_read_u8_array_ptr(dev, "registers-default", priv->nregs);
+ if (defaults)
+ memcpy(priv->buffer, defaults, priv->nregs);
+ else
dev_dbg(dev, "No registers-default property\n");
ret = gpio_request_by_name(dev, "oe-gpios", 0, &priv->oe,
diff --git a/drivers/gpio/qcom_pmic_gpio.c b/drivers/gpio/qcom_pmic_gpio.c
index 4458c55cd3d..6215f794e09 100644
--- a/drivers/gpio/qcom_pmic_gpio.c
+++ b/drivers/gpio/qcom_pmic_gpio.c
@@ -344,7 +344,6 @@ static int qcom_gpio_probe(struct udevice *dev)
static const struct udevice_id qcom_gpio_ids[] = {
{ .compatible = "qcom,pm8916-gpio" },
{ .compatible = "qcom,pm8994-gpio" }, /* 22 GPIO's */
- { .compatible = "qcom,pm8998-gpio" },
{ .compatible = "qcom,pms405-gpio" },
{ .compatible = "qcom,pm6125-gpio" },
{ .compatible = "qcom,pm8150-gpio" },
diff --git a/drivers/gpio/qcom_spmi_gpio.c b/drivers/gpio/qcom_spmi_gpio.c
index 1a7c7c48dfc..fc1aac8b534 100644
--- a/drivers/gpio/qcom_spmi_gpio.c
+++ b/drivers/gpio/qcom_spmi_gpio.c
@@ -747,10 +747,12 @@ static const struct udevice_id qcom_spmi_pmic_gpio_ids[] = {
{ .compatible = "qcom,pm6350-gpio" },
{ .compatible = "qcom,pm660l-gpio" },
{ .compatible = "qcom,pm7325-gpio" },
+ { .compatible = "qcom,pm7550-gpio" },
{ .compatible = "qcom,pm8550-gpio" },
{ .compatible = "qcom,pm8550b-gpio" },
{ .compatible = "qcom,pm8550ve-gpio" },
{ .compatible = "qcom,pm8550vs-gpio" },
+ { .compatible = "qcom,pm8998-gpio" },
{ .compatible = "qcom,pmk8550-gpio" },
{ .compatible = "qcom,pmr735d-gpio" },
{ }
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 55465dc1d46..37288a47eb7 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -624,26 +624,6 @@ config SH_I2C_CLOCK
default 104000000
endif
-config SYS_I2C_SOFT
- bool "Legacy software I2C interface"
- depends on !COMPILE_TEST
- help
- Enable the legacy software defined I2C interface
-
-config SYS_I2C_SOFT_SPEED
- int "Software I2C bus speed"
- depends on SYS_I2C_SOFT
- default 100000
- help
- Speed of the software I2C bus
-
-config SYS_I2C_SOFT_SLAVE
- hex "Software I2C slave address"
- depends on SYS_I2C_SOFT
- default 0xfe
- help
- Slave address of the software I2C bus
-
config SYS_I2C_OCTEON
bool "Octeon II/III/TX/TX2 I2C driver"
depends on (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2) && DM_I2C
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 5fe30d0df4f..2da649e97d3 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -47,7 +47,6 @@ obj-$(CONFIG_SYS_I2C_RZ_RIIC) += rz_riic.o
obj-$(CONFIG_SYS_I2C_S3C24X0) += s3c24x0_i2c.o exynos_hs_i2c.o
obj-$(CONFIG_SYS_I2C_SANDBOX) += sandbox_i2c.o i2c-emul-uclass.o
obj-$(CONFIG_SYS_I2C_SH) += sh_i2c.o
-obj-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o
obj-$(CONFIG_SYS_I2C_STM32F7) += stm32f7_i2c.o
obj-$(CONFIG_SYS_I2C_SUN6I_P2WI) += sun6i_p2wi.o
obj-$(CONFIG_SYS_I2C_SUN8I_RSB) += sun8i_rsb.o
diff --git a/drivers/i2c/designware_i2c_pci.c b/drivers/i2c/designware_i2c_pci.c
index c21c412231c..ad4122c2abd 100644
--- a/drivers/i2c/designware_i2c_pci.c
+++ b/drivers/i2c/designware_i2c_pci.c
@@ -168,7 +168,7 @@ static int dw_i2c_acpi_fill_ssdt(const struct udevice *dev,
return 0;
}
-struct acpi_ops dw_i2c_acpi_ops = {
+static struct acpi_ops dw_i2c_acpi_ops = {
.fill_ssdt = dw_i2c_acpi_fill_ssdt,
};
diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c
index 4102375e5b7..e69de29bb2d 100644
--- a/drivers/i2c/soft_i2c.c
+++ b/drivers/i2c/soft_i2c.c
@@ -1,418 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * (C) Copyright 2009
- * Heiko Schocher, DENX Software Engineering, [email protected].
- * Changes for multibus/multiadapter I2C support.
- *
- * (C) Copyright 2001, 2002
- * Wolfgang Denk, DENX Software Engineering, [email protected].
- *
- * This has been changed substantially by Gerald Van Baren, Custom IDEAS,
- * [email protected]. It was heavily influenced by LiMon, written by
- * Neil Russell.
- *
- * NOTE: This driver should be converted to driver model before June 2017.
- * Please see doc/driver-model/i2c-howto.rst for instructions.
- */
-
-#include <config.h>
-#if defined(CONFIG_AT91FAMILY)
-#include <asm/io.h>
-#include <asm/arch/hardware.h>
-#include <asm/arch/at91_pio.h>
-#ifdef CONFIG_ATMEL_LEGACY
-#include <asm/arch/gpio.h>
-#endif
-#endif
-#include <i2c.h>
-#include <linux/delay.h>
-
-#if defined(CONFIG_SOFT_I2C_GPIO_SCL)
-# include <asm/gpio.h>
-
-# ifndef I2C_GPIO_SYNC
-# define I2C_GPIO_SYNC
-# endif
-
-# ifndef I2C_INIT
-# define I2C_INIT \
- do { \
- gpio_request(CONFIG_SOFT_I2C_GPIO_SCL, "soft_i2c"); \
- gpio_request(CONFIG_SOFT_I2C_GPIO_SDA, "soft_i2c"); \
- } while (0)
-# endif
-
-# ifndef I2C_ACTIVE
-# define I2C_ACTIVE do { } while (0)
-# endif
-
-# ifndef I2C_TRISTATE
-# define I2C_TRISTATE do { } while (0)
-# endif
-
-# ifndef I2C_READ
-# define I2C_READ gpio_get_value(CONFIG_SOFT_I2C_GPIO_SDA)
-# endif
-
-# ifndef I2C_SDA
-# define I2C_SDA(bit) \
- do { \
- if (bit) \
- gpio_direction_input(CONFIG_SOFT_I2C_GPIO_SDA); \
- else \
- gpio_direction_output(CONFIG_SOFT_I2C_GPIO_SDA, 0); \
- I2C_GPIO_SYNC; \
- } while (0)
-# endif
-
-# ifndef I2C_SCL
-# define I2C_SCL(bit) \
- do { \
- gpio_direction_output(CONFIG_SOFT_I2C_GPIO_SCL, bit); \
- I2C_GPIO_SYNC; \
- } while (0)
-# endif
-
-# ifndef I2C_DELAY
-# define I2C_DELAY udelay(5) /* 1/4 I2C clock duration */
-# endif
-
-#endif
-
-/* #define DEBUG_I2C */
-
-#ifndef I2C_SOFT_DECLARATIONS
-# define I2C_SOFT_DECLARATIONS
-#endif
-
-/*-----------------------------------------------------------------------
- * Definitions
- */
-#define RETRIES 0
-
-#define I2C_ACK 0 /* PD_SDA level to ack a byte */
-#define I2C_NOACK 1 /* PD_SDA level to noack a byte */
-
-#ifdef DEBUG_I2C
-#define PRINTD(fmt,args...) do { \
- printf (fmt ,##args); \
- } while (0)
-#else
-#define PRINTD(fmt,args...)
-#endif
-
-/*-----------------------------------------------------------------------
- * Local functions
- */
-static void send_reset (void);
-static void send_start (void);
-static void send_stop (void);
-static void send_ack (int);
-static int write_byte (uchar byte);
-static uchar read_byte (int);
-
-/*-----------------------------------------------------------------------
- * Send a reset sequence consisting of 9 clocks with the data signal high
- * to clock any confused device back into an idle state. Also send a
- * <stop> at the end of the sequence for belts & suspenders.
- */
-static void send_reset(void)
-{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
- int j;
-
- I2C_SCL(1);
- I2C_SDA(1);
-#ifdef I2C_INIT
- I2C_INIT;
-#endif
- I2C_TRISTATE;
- for(j = 0; j < 9; j++) {
- I2C_SCL(0);
- I2C_DELAY;
- I2C_DELAY;
- I2C_SCL(1);
- I2C_DELAY;
- I2C_DELAY;
- }
- send_stop();
- I2C_TRISTATE;
-}
-
-/*-----------------------------------------------------------------------
- * START: High -> Low on SDA while SCL is High
- */
-static void send_start(void)
-{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
-
- I2C_DELAY;
- I2C_SDA(1);
- I2C_ACTIVE;
- I2C_DELAY;
- I2C_SCL(1);
- I2C_DELAY;
- I2C_SDA(0);
- I2C_DELAY;
-}
-
-/*-----------------------------------------------------------------------
- * STOP: Low -> High on SDA while SCL is High
- */
-static void send_stop(void)
-{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
-
- I2C_SCL(0);
- I2C_DELAY;
- I2C_SDA(0);
- I2C_ACTIVE;
- I2C_DELAY;
- I2C_SCL(1);
- I2C_DELAY;
- I2C_SDA(1);
- I2C_DELAY;
- I2C_TRISTATE;
-}
-
-/*-----------------------------------------------------------------------
- * ack should be I2C_ACK or I2C_NOACK
- */
-static void send_ack(int ack)
-{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
-
- I2C_SCL(0);
- I2C_DELAY;
- I2C_ACTIVE;
- I2C_SDA(ack);
- I2C_DELAY;
- I2C_SCL(1);
- I2C_DELAY;
- I2C_DELAY;
- I2C_SCL(0);
- I2C_DELAY;
-}
-
-/*-----------------------------------------------------------------------
- * Send 8 bits and look for an acknowledgement.
- */
-static int write_byte(uchar data)
-{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
- int j;
- int nack;
-
- I2C_ACTIVE;
- for(j = 0; j < 8; j++) {
- I2C_SCL(0);
- I2C_DELAY;
- I2C_SDA(data & 0x80);
- I2C_DELAY;
- I2C_SCL(1);
- I2C_DELAY;
- I2C_DELAY;
-
- data <<= 1;
- }
-
- /*
- * Look for an <ACK>(negative logic) and return it.
- */
- I2C_SCL(0);
- I2C_DELAY;
- I2C_SDA(1);
- I2C_TRISTATE;
- I2C_DELAY;
- I2C_SCL(1);
- I2C_DELAY;
- I2C_DELAY;
- nack = I2C_READ;
- I2C_SCL(0);
- I2C_DELAY;
- I2C_ACTIVE;
-
- return(nack); /* not a nack is an ack */
-}
-
-/*-----------------------------------------------------------------------
- * if ack == I2C_ACK, ACK the byte so can continue reading, else
- * send I2C_NOACK to end the read.
- */
-static uchar read_byte(int ack)
-{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
- int data;
- int j;
-
- /*
- * Read 8 bits, MSB first.
- */
- I2C_TRISTATE;
- I2C_SDA(1);
- data = 0;
- for(j = 0; j < 8; j++) {
- I2C_SCL(0);
- I2C_DELAY;
- I2C_SCL(1);
- I2C_DELAY;
- data <<= 1;
- data |= I2C_READ;
- I2C_DELAY;
- }
- send_ack(ack);
-
- return(data);
-}
-
-/*-----------------------------------------------------------------------
- * Initialization
- */
-static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
-{
- /*
- * WARNING: Do NOT save speed in a static variable: if the
- * I2C routines are called before RAM is initialized (to read
- * the DIMM SPD, for instance), RAM won't be usable and your
- * system will crash.
- */
- send_reset ();
-}
-
-/*-----------------------------------------------------------------------
- * Probe to see if a chip is present. Also good for checking for the
- * completion of EEPROM writes since the chip stops responding until
- * the write completes (typically 10mSec).
- */
-static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr)
-{
- int rc;
-
- /*
- * perform 1 byte write transaction with just address byte
- * (fake write)
- */
- send_start();
- rc = write_byte ((addr << 1) | 0);
- send_stop();
-
- return (rc ? 1 : 0);
-}
-
-/*-----------------------------------------------------------------------
- * Read bytes
- */
-static int soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
- int alen, uchar *buffer, int len)
-{
- int shift;
- PRINTD("i2c_read: chip %02X addr %02X alen %d buffer %p len %d\n",
- chip, addr, alen, buffer, len);
-
-#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
- /*
- * EEPROM chips that implement "address overflow" are ones
- * like Catalyst 24WC04/08/16 which has 9/10/11 bits of
- * address and the extra bits end up in the "chip address"
- * bit slots. This makes a 24WC08 (1Kbyte) chip look like
- * four 256 byte chips.
- *
- * Note that we consider the length of the address field to
- * still be one byte because the extra address bits are
- * hidden in the chip address.
- */
- chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
-
- PRINTD("i2c_read: fix addr_overflow: chip %02X addr %02X\n",
- chip, addr);
-#endif
-
- /*
- * Do the addressing portion of a write cycle to set the
- * chip's address pointer. If the address length is zero,
- * don't do the normal write cycle to set the address pointer,
- * there is no address pointer in this chip.
- */
- send_start();
- if(alen > 0) {
- if(write_byte(chip << 1)) { /* write cycle */
- send_stop();
- PRINTD("i2c_read, no chip responded %02X\n", chip);
- return(1);
- }
- shift = (alen-1) * 8;
- while(alen-- > 0) {
- if(write_byte(addr >> shift)) {
- PRINTD("i2c_read, address not <ACK>ed\n");
- return(1);
- }
- shift -= 8;
- }
-
- /* Some I2C chips need a stop/start sequence here,
- * other chips don't work with a full stop and need
- * only a start. Default behaviour is to send the
- * stop/start sequence.
- */
-#ifdef CONFIG_SOFT_I2C_READ_REPEATED_START
- send_start();
-#else
- send_stop();
- send_start();
-#endif
- }
- /*
- * Send the chip address again, this time for a read cycle.
- * Then read the data. On the last byte, we do a NACK instead
- * of an ACK(len == 0) to terminate the read.
- */
- write_byte((chip << 1) | 1); /* read cycle */
- while(len-- > 0) {
- *buffer++ = read_byte(len == 0);
- }
- send_stop();
- return(0);
-}
-
-/*-----------------------------------------------------------------------
- * Write bytes
- */
-static int soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
- int alen, uchar *buffer, int len)
-{
- int shift, failures = 0;
-
- PRINTD("i2c_write: chip %02X addr %02X alen %d buffer %p len %d\n",
- chip, addr, alen, buffer, len);
-
- send_start();
- if(write_byte(chip << 1)) { /* write cycle */
- send_stop();
- PRINTD("i2c_write, no chip responded %02X\n", chip);
- return(1);
- }
- shift = (alen-1) * 8;
- while(alen-- > 0) {
- if(write_byte(addr >> shift)) {
- PRINTD("i2c_write, address not <ACK>ed\n");
- return(1);
- }
- shift -= 8;
- }
-
- while(len-- > 0) {
- if(write_byte(*buffer++)) {
- failures++;
- }
- }
- send_stop();
- return(failures);
-}
-
-/*
- * Register soft i2c adapters
- */
-U_BOOT_I2C_ADAP_COMPLETE(soft00, soft_i2c_init, soft_i2c_probe,
- soft_i2c_read, soft_i2c_write, NULL,
- CONFIG_SYS_I2C_SOFT_SPEED, CONFIG_SYS_I2C_SOFT_SLAVE,
- 0)
diff --git a/drivers/iommu/apple_dart.c b/drivers/iommu/apple_dart.c
index bfd4ad20105..ebef28d0b9d 100644
--- a/drivers/iommu/apple_dart.c
+++ b/drivers/iommu/apple_dart.c
@@ -6,6 +6,7 @@
#include <cpu_func.h>
#include <dm.h>
#include <iommu.h>
+#include <linux/sizes.h>
#include <lmb.h>
#include <memalign.h>
#include <asm/io.h>
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index f45e611c966..1d9e284cfd1 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -32,6 +32,8 @@ config MPFS_MBOX
bool "Enable MPFS system controller support"
depends on DM_MAILBOX && ARCH_RV64I
select DEVRES
+ depends on SYSCON
+ depends on REGMAP
help
Enable support for the mailboxes that provide a communication
channel with the system controller integrated on PolarFire SoC.
diff --git a/drivers/mailbox/mpfs-mbox.c b/drivers/mailbox/mpfs-mbox.c
index 55238847ecd..b1ce377525e 100644
--- a/drivers/mailbox/mpfs-mbox.c
+++ b/drivers/mailbox/mpfs-mbox.c
@@ -13,19 +13,21 @@
#include <dm/device-internal.h>
#include <dm/device.h>
#include <dm/device_compat.h>
-#include <dm/devres.h>
#include <dm/ofnode.h>
#include <linux/bitops.h>
#include <linux/compat.h>
-#include <linux/io.h>
-#include <linux/ioport.h>
+#include <linux/err.h>
+#include <linux/errno.h>
#include <log.h>
#include <mailbox-uclass.h>
-#include <malloc.h>
#include <mpfs-mailbox.h>
+#include <regmap.h>
+#include <syscon.h>
#define SERVICES_CR_OFFSET 0x50u
#define SERVICES_SR_OFFSET 0x54u
+#define MESSAGE_INT_OFFSET 0x18cu
+#define MAILBOX_REG_OFFSET 0x800u
#define SERVICE_CR_REQ_MASK 0x1u
#define SERVICE_SR_BUSY_MASK 0x2u
@@ -35,17 +37,18 @@
struct mpfs_mbox {
struct udevice *dev;
- void __iomem *ctrl_base;
void __iomem *mbox_base;
- struct mbox_chan *chan;
+ void __iomem *int_reg;
+ struct regmap *control_scb;
+ struct regmap *sysreg_scb;
};
static bool mpfs_mbox_busy(struct mbox_chan *chan)
{
struct mpfs_mbox *mbox = dev_get_priv(chan->dev);
- uint16_t status;
+ u32 status;
- status = readl(mbox->ctrl_base + SERVICES_SR_OFFSET);
+ regmap_read(mbox->control_scb, SERVICES_SR_OFFSET, &status);
return status & SERVICE_SR_BUSY_MASK;
}
@@ -80,14 +83,15 @@ static int mpfs_mbox_send(struct mbox_chan *chan, const void *data)
cmd_shifted = msg->cmd_opcode << SERVICE_CR_COMMAND_SHIFT;
cmd_shifted |= SERVICE_CR_REQ_MASK;
- writel(cmd_shifted, mbox->ctrl_base + SERVICES_CR_OFFSET);
+
+ regmap_write(mbox->control_scb, SERVICES_CR_OFFSET, cmd_shifted);
do {
- value = readl(mbox->ctrl_base + SERVICES_CR_OFFSET);
+ regmap_read(mbox->control_scb, SERVICES_CR_OFFSET, &value);
} while (SERVICE_CR_REQ_MASK == (value & SERVICE_CR_REQ_MASK));
do {
- value = readl(mbox->ctrl_base + SERVICES_SR_OFFSET);
+ regmap_read(mbox->control_scb, SERVICES_SR_OFFSET, &value);
} while (SERVICE_SR_BUSY_MASK == (value & SERVICE_SR_BUSY_MASK));
msg->response->resp_status = (value >> SERVICE_SR_STATUS_SHIFT);
@@ -118,6 +122,11 @@ static int mpfs_mbox_recv(struct mbox_chan *chan, void *data)
for (idx = 0; idx < response->resp_size; idx++)
*((u8 *)(response->resp_msg) + idx) = readb(mbox->mbox_base + msg->resp_offset + idx);
+ if (mbox->sysreg_scb)
+ regmap_write(mbox->sysreg_scb, MESSAGE_INT_OFFSET, 0);
+ else
+ writel_relaxed(0, mbox->int_reg);
+
return 0;
}
@@ -126,42 +135,71 @@ static const struct mbox_ops mpfs_mbox_ops = {
.recv = mpfs_mbox_recv,
};
-static int mpfs_mbox_probe(struct udevice *dev)
+/*
+ * Use global compatible lookup instead of phandles, as U-Boot may run
+ * with a reduced or firmware-provided device tree where mailbox syscon
+ * phandle properties are not guaranteed to be present.
+ */
+static int mpfs_mbox_syscon_probe(struct udevice *dev, struct mpfs_mbox *mbox)
{
- struct mpfs_mbox *mbox;
- struct resource regs;
ofnode node;
- int ret;
- node = dev_ofnode(dev);
+ node = ofnode_by_compatible(ofnode_null(), "microchip,mpfs-control-scb");
+ if (!ofnode_valid(node))
+ return -ENODEV;
- mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
- if (!mbox)
- return -ENOMEM;
+ mbox->control_scb = syscon_node_to_regmap(node);
+ if (IS_ERR(mbox->control_scb))
+ return PTR_ERR(mbox->control_scb);
- ret = ofnode_read_resource(node, 0, &regs);
- if (ret) {
- dev_err(dev, "No reg property for controller base\n");
- return ret;
- };
+ node = ofnode_by_compatible(ofnode_null(), "microchip,mpfs-sysreg-scb");
+ if (!ofnode_valid(node))
+ return -ENODEV;
- mbox->ctrl_base = devm_ioremap(dev, regs.start, regs.start - regs.end);
+ mbox->sysreg_scb = syscon_node_to_regmap(node);
+ if (IS_ERR(mbox->sysreg_scb))
+ return PTR_ERR(mbox->sysreg_scb);
- ret = ofnode_read_resource(node, 2, &regs);
- if (ret) {
- dev_err(dev, "No reg property for mailbox base\n");
+ mbox->mbox_base = dev_read_addr_ptr(dev);
+ if (!mbox->mbox_base)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int mpfs_mbox_legacy_probe(struct udevice *dev, struct mpfs_mbox *mbox)
+{
+ int ret;
+
+ ret = regmap_init_mem_index(dev_ofnode(dev), &mbox->control_scb, 0);
+ if (ret)
return ret;
- };
- mbox->mbox_base = devm_ioremap(dev, regs.start, regs.start - regs.end);
+ mbox->mbox_base = dev_read_addr_index_ptr(dev, 2);
+ if (!mbox->mbox_base)
+ mbox->mbox_base = dev_read_addr_index_ptr(dev, 0) + MAILBOX_REG_OFFSET;
- mbox->dev = dev;
- dev_set_priv(dev, mbox);
- mbox->chan->con_priv = mbox;
+ mbox->int_reg = dev_read_addr_index_ptr(dev, 1);
+ if (!mbox->int_reg)
+ return -EINVAL;
return 0;
}
+static int mpfs_mbox_probe(struct udevice *dev)
+{
+ struct mpfs_mbox *mbox = dev_get_priv(dev);
+ int ret;
+
+ mbox->dev = dev;
+
+ ret = mpfs_mbox_syscon_probe(dev, mbox);
+ if (!ret)
+ return 0;
+
+ return mpfs_mbox_legacy_probe(dev, mbox);
+}
+
static const struct udevice_id mpfs_mbox_ids[] = {
{.compatible = "microchip,mpfs-mailbox"},
{ }
@@ -174,4 +212,4 @@ U_BOOT_DRIVER(mpfs_mbox) = {
.probe = mpfs_mbox_probe,
.priv_auto = sizeof(struct mpfs_mbox),
.ops = &mpfs_mbox_ops,
-};
+}; \ No newline at end of file
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 22bd3a972bd..24bd16ad5f3 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -924,7 +924,7 @@ config ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE
config FSL_ESDHC_IMX
bool "Freescale/NXP i.MX eSDHC controller support"
- depends on MACH_IMX
+ depends on MACH_IMX || M68K
help
This selects support for the i.MX eSDHC (Enhanced Secure Digital Host
Controller) found on numerous Freescale/NXP SoCs.
diff --git a/drivers/mmc/bcm2835_sdhci.c b/drivers/mmc/bcm2835_sdhci.c
index 655d9902dfa..efa4d14f5da 100644
--- a/drivers/mmc/bcm2835_sdhci.c
+++ b/drivers/mmc/bcm2835_sdhci.c
@@ -219,6 +219,10 @@ static int bcm2835_sdhci_probe(struct udevice *dev)
host->mmc = &plat->mmc;
host->mmc->dev = dev;
+ ret = mmc_of_parse(dev, &plat->cfg);
+ if (ret)
+ return ret;
+
ret = sdhci_setup_cfg(&plat->cfg, host, emmc_freq, MIN_FREQ);
if (ret) {
debug("%s: Failed to setup SDHCI (err=%d)\n", __func__, ret);
diff --git a/drivers/mmc/bcmstb_sdhci.c b/drivers/mmc/bcmstb_sdhci.c
index 7bddbebb162..f27b84a6ee4 100644
--- a/drivers/mmc/bcmstb_sdhci.c
+++ b/drivers/mmc/bcmstb_sdhci.c
@@ -56,7 +56,7 @@ struct sdhci_brcmstb_dev_priv {
static int sdhci_brcmstb_init_2712(struct udevice *dev)
{
- struct sdhci_host *host = dev_get_priv(dev);
+ struct sdhci_bcmstb_plat *plat = dev_get_plat(dev);
void *cfg_regs;
u32 reg;
@@ -65,8 +65,8 @@ static int sdhci_brcmstb_init_2712(struct udevice *dev)
if (!cfg_regs)
return -ENOENT;
- if ((host->mmc->host_caps & MMC_CAP_NONREMOVABLE) ||
- (host->mmc->host_caps & MMC_CAP_NEEDS_POLL)) {
+ if ((plat->cfg.host_caps & MMC_CAP_NONREMOVABLE) ||
+ (plat->cfg.host_caps & MMC_CAP_NEEDS_POLL)) {
/* Force presence */
reg = readl(cfg_regs + SDIO_CFG_CTRL);
reg &= ~SDIO_CFG_CTRL_SDCD_N_TEST_LEV;
diff --git a/drivers/mmc/msm_sdhci.c b/drivers/mmc/msm_sdhci.c
index 66f3cf2de4f..aaa87923604 100644
--- a/drivers/mmc/msm_sdhci.c
+++ b/drivers/mmc/msm_sdhci.c
@@ -71,8 +71,7 @@ static int msm_sdc_clk_init(struct udevice *dev)
var_info = (void *)dev_get_driver_data(dev);
- ret = ofnode_read_u32(node, "clock-frequency", (uint *)(&clk_rate));
- if (ret)
+ if (ofnode_read_u32(node, "max-frequency", (uint *)(&clk_rate)))
clk_rate = 201500000;
ret = clk_get_bulk(dev, &prv->clks);
diff --git a/drivers/mmc/mtk-sd.c b/drivers/mmc/mtk-sd.c
index 7a4bdee7496..51c9c0a3fad 100644
--- a/drivers/mmc/mtk-sd.c
+++ b/drivers/mmc/mtk-sd.c
@@ -539,7 +539,7 @@ static bool msdc_cmd_is_ready(struct msdc_host *host)
return false;
}
- if (host->last_resp_type == MMC_RSP_R1b && host->last_data_write) {
+ if (host->last_resp_type == MMC_RSP_R1b || host->last_data_write) {
ret = readl_poll_timeout(&host->base->msdc_ps, reg,
reg & MSDC_PS_DAT0, 1000000);
@@ -1983,10 +1983,12 @@ static const struct msdc_compatible mt8189_compat = {
.clk_div_bits = 12,
.pad_tune0 = true,
.async_fifo = true,
+ .async_fifo_crcsts = true,
.data_tune = true,
.busy_check = true,
.stop_clk_fix = true,
.enhance_rx = true,
+ .use_dma_mode = true,
};
static const struct udevice_id msdc_ids[] = {
diff --git a/drivers/mmc/sdhci-cadence.c b/drivers/mmc/sdhci-cadence.c
index 5bbc18dfa51..a76f9e8d6bd 100644
--- a/drivers/mmc/sdhci-cadence.c
+++ b/drivers/mmc/sdhci-cadence.c
@@ -39,6 +39,9 @@ static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = {
{ "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE, },
};
+static int __maybe_unused sdhci_cdns_execute_tuning(struct udevice *dev,
+ unsigned int opcode);
+
static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_plat *plat,
u8 addr, u8 data)
{
@@ -155,8 +158,93 @@ static void sdhci_cdns_set_control_reg(struct sdhci_host *host)
sdhci_cdns6_phy_adj(mmc->dev, plat, mmc->selected_mode);
}
+static __maybe_unused bool sdhci_cdns_sd_needs_tuning(struct mmc *mmc)
+{
+ struct sdhci_cdns_plat *plat = dev_get_plat(mmc->dev);
+
+ if (!IS_SD(mmc))
+ return false;
+
+ if (!dev_read_bool(mmc->dev, "cdns,sd-hs-tuning"))
+ return false;
+
+ /* Already tuned for this mode */
+ if (plat->tuned_mode == mmc->selected_mode)
+ return false;
+
+ switch (mmc->selected_mode) {
+ case SD_HS:
+ return mmc->bus_width == 4;
+ /* Add future modes here, e.g.:
+ * case UHS_SDR50:
+ * return true;
+ */
+ default:
+ return false;
+ }
+}
+
+static int sdhci_cdns_set_ios_post(struct sdhci_host *host)
+{
+ struct mmc *mmc = host->mmc;
+ struct sdhci_cdns_plat *plat = dev_get_plat(mmc->dev);
+ int ret __maybe_unused;
+ /*
+ * The SD6HC soft PHY requires runtime DLL delay calibration
+ * for SD High Speed mode. The default PHY_DLL_SLAVE_CTRL_REG
+ * values (READ_DQS_CMD_DELAY and READ_DQS_DELAY = 0) do not
+ * provide sufficient timing margin due to PVT and board trace
+ * variations.
+ *
+ * Tuning is performed once per entry into SD_HS mode
+ * (tracked by plat->tuned_mode state). The calibrated PHY delay
+ * values remain valid while the card stays in SD_HS mode, and
+ * leaving that tuned mode clears the state so re-entering SD_HS
+ * triggers tuning again.
+ *
+ * This must be done in set_ios_post (not set_control_reg)
+ * because the SDHCI controller must already be operating at
+ * the target bus width, clock, and speed mode before CMD19
+ * tuning commands can succeed.
+ */
+
+ if (IS_ENABLED(CONFIG_MMC_SUPPORTS_TUNING)) {
+ if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_420 &&
+ sdhci_cdns_sd_needs_tuning(mmc)) {
+ ret = sdhci_cdns_execute_tuning(mmc->dev,
+ MMC_CMD_SEND_TUNING_BLOCK);
+ if (ret) {
+ dev_err(mmc->dev,
+ "SD_HS tuning failed (ret=%d), using default PHY\n",
+ ret);
+ /* Restore default PHY settings and avoid retrying in this mode */
+ sdhci_cdns6_phy_adj(mmc->dev, plat,
+ mmc->selected_mode);
+ plat->tuned_mode = mmc->selected_mode;
+ plat->tuned_dll_slave_ctrl = sdhci_cdns6_phy_get_dll_slave(plat);
+ return 0;
+ }
+ /*
+ * Tuning succeeded. The tuned_mode is already set by
+ * execute_tuning(), so the tuned value will be preserved
+ * across subsequent PHY reconfigurations.
+ */
+ dev_dbg(mmc->dev, "SD_HS tuning successful\n");
+ }
+
+ /* Reset when mode changes away from a tuned mode */
+ if (mmc->selected_mode != plat->tuned_mode) {
+ plat->tuned_mode = MMC_MODES_END;
+ plat->tuned_dll_slave_ctrl = 0;
+ }
+ }
+
+ return 0;
+}
+
static const struct sdhci_ops sdhci_cdns_ops = {
.set_control_reg = sdhci_cdns_set_control_reg,
+ .set_ios_post = sdhci_cdns_set_ios_post,
};
static int sdhci_cdns_set_tune_val(struct sdhci_cdns_plat *plat,
@@ -204,6 +292,7 @@ static int __maybe_unused sdhci_cdns_execute_tuning(struct udevice *dev,
int cur_streak = 0;
int max_streak = 0;
int end_of_streak = 0;
+ int ret;
int i;
/*
@@ -229,7 +318,24 @@ static int __maybe_unused sdhci_cdns_execute_tuning(struct udevice *dev,
return -EIO;
}
- return sdhci_cdns_set_tune_val(plat, end_of_streak - max_streak / 2);
+ ret = sdhci_cdns_set_tune_val(plat, end_of_streak - max_streak / 2);
+ if (ret)
+ return ret;
+
+ /*
+ * Mark this mode as tuned. This is critical for both driver tuning
+ * (SD_HS via set_ios_post) and framework tuning (UHS_SDR104, MMC_HS_200,
+ * MMC_HS_400) so that subsequent PHY reconfigurations restore the
+ * calibrated DLL value instead of overwriting with DT defaults.
+ *
+ * For HS400, tuning is performed while the controller is in HS200 mode
+ * (mmc->selected_mode == MMC_HS_200 and mmc->hs400_tuning == true).
+ * Record the tuned mode as MMC_HS_400 so the calibrated DLL value is
+ * preserved across the HS200→HS400 transition.
+ */
+ plat->tuned_mode = mmc->hs400_tuning ? MMC_HS_400 : mmc->selected_mode;
+
+ return 0;
}
static struct dm_mmc_ops sdhci_cdns_mmc_ops;
diff --git a/drivers/mmc/sdhci-cadence.h b/drivers/mmc/sdhci-cadence.h
index 7101f00b75b..ea517491860 100644
--- a/drivers/mmc/sdhci-cadence.h
+++ b/drivers/mmc/sdhci-cadence.h
@@ -7,6 +7,8 @@
#ifndef SDHCI_CADENCE_H_
#define SDHCI_CADENCE_H_
+#include <mmc.h>
+
/* HRS - Host Register Set (specific to Cadence) */
/* PHY access port */
#define SDHCI_CDNS_HRS04 0x10
@@ -60,10 +62,13 @@ struct sdhci_cdns_plat {
struct mmc_config cfg;
struct mmc mmc;
void __iomem *hrs_addr;
+ enum bus_mode tuned_mode;
+ u32 tuned_dll_slave_ctrl;
};
int sdhci_cdns6_phy_adj(struct udevice *dev, struct sdhci_cdns_plat *plat, u32 mode);
int sdhci_cdns6_phy_init(struct udevice *dev, struct sdhci_cdns_plat *plat);
int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val);
+u32 sdhci_cdns6_phy_get_dll_slave(struct sdhci_cdns_plat *plat);
#endif
diff --git a/drivers/mmc/sdhci-cadence6.c b/drivers/mmc/sdhci-cadence6.c
index ca1086e2359..c8b42532e17 100644
--- a/drivers/mmc/sdhci-cadence6.c
+++ b/drivers/mmc/sdhci-cadence6.c
@@ -173,6 +173,30 @@ static void sdhci_cdns6_write_phy_reg(struct sdhci_cdns_plat *plat, u32 addr, u3
writel(val, plat->hrs_addr + SDHCI_CDNS_HRS05);
}
+static bool sdhci_cdns6_mode_is_tuned(struct sdhci_cdns_plat *plat, u32 mode)
+{
+ /*
+ * Check if the given mode has a valid tuned DLL value.
+ * Only modes that support tuning (driver or framework) can have
+ * valid tuned values. This prevents the initial state (tuned_mode=0)
+ * from falsely matching MMC_LEGACY.
+ */
+ if (plat->tuned_mode != mode)
+ return false;
+
+ switch (mode) {
+ case SD_HS: /* Driver tuning via set_ios_post */
+ case UHS_SDR50: /* Future driver tuning support */
+ case UHS_SDR104: /* Framework tuning */
+ case MMC_HS_200: /* Framework tuning */
+ case MMC_HS_400: /* Framework tuning */
+ case MMC_HS_400_ES: /* Framework tuning */
+ return true;
+ default:
+ return false;
+ }
+}
+
static int sdhci_cdns6_reset_phy_dll(struct sdhci_cdns_plat *plat, bool reset)
{
void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS09;
@@ -259,7 +283,18 @@ int sdhci_cdns6_phy_adj(struct udevice *dev, struct sdhci_cdns_plat *plat, u32 m
sdhci_cdns6_write_phy_reg(plat, PHY_DQS_TIMING_REG_ADDR, sdhci_cdns6_phy_cfgs[0].val);
sdhci_cdns6_write_phy_reg(plat, PHY_GATE_LPBK_CTRL_REG_ADDR, sdhci_cdns6_phy_cfgs[1].val);
sdhci_cdns6_write_phy_reg(plat, PHY_DLL_MASTER_CTRL_REG_ADDR, sdhci_cdns6_phy_cfgs[4].val);
- sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR, sdhci_cdns6_phy_cfgs[2].val);
+ if (sdhci_cdns6_mode_is_tuned(plat, mode)) {
+ /*
+ * Use previously saved tuned DLL slave control value.
+ * Note: 0 is a valid tuned value (e.g., optimal tap at position 0),
+ * so we check both mode match AND that it's a tunable mode.
+ */
+ sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR,
+ plat->tuned_dll_slave_ctrl);
+ } else {
+ sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR,
+ sdhci_cdns6_phy_cfgs[2].val);
+ }
/* Switch Off the DLL Reset */
ret = sdhci_cdns6_reset_phy_dll(plat, false);
@@ -318,6 +353,9 @@ int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val)
sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR, tmp);
+ /* Store tuned DLL slave control value which will be reapplied via set_ios(). */
+ plat->tuned_dll_slave_ctrl = tmp;
+
/* Switch Off the DLL Reset */
ret = sdhci_cdns6_reset_phy_dll(plat, false);
if (ret) {
@@ -327,3 +365,8 @@ int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val)
return 0;
}
+
+u32 sdhci_cdns6_phy_get_dll_slave(struct sdhci_cdns_plat *plat)
+{
+ return sdhci_cdns6_read_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR);
+}
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index ef27a4b7a36..49748fddf80 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -114,6 +114,7 @@ struct sunxi_nand_hw_ecc {
* @clk_rate: clk_rate required for this NAND chip
* @timing_cfg TIMING_CFG register value for this NAND chip
* @selected: current active CS
+ * @user_data_bytes array of user data lengths for all ECC steps
* @nsels: number of CS lines required by the NAND chip
* @sels: array of CS lines descriptions
*/
@@ -128,6 +129,7 @@ struct sunxi_nand_chip {
u32 addr[2];
int cmd_cycles;
u8 cmd[2];
+ u8 *user_data_bytes;
int nsels;
struct sunxi_nand_chip_sel sels[0];
};
@@ -744,20 +746,76 @@ static void sunxi_nfc_set_user_data_len(struct sunxi_nfc *nfc,
writel(val, nfc->regs + NFC_REG_USER_DATA_LEN(nfc, step));
}
+static u8 sunxi_nfc_user_data_sz(const struct sunxi_nand_chip *sunxi_nand, int step)
+{
+ if (!sunxi_nand->user_data_bytes)
+ return USER_DATA_SZ;
+
+ return sunxi_nand->user_data_bytes[step];
+}
+
+static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob,
+ int step, bool bbm, int page,
+ unsigned int user_data_sz)
+{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ u32 user_data;
+
+ if (!nfc->caps->reg_user_data_len) {
+ /*
+ * For A10, the user data for step n is in the nth
+ * REG_USER_DATA
+ */
+ user_data = readl(nfc->regs + NFC_REG_USER_DATA(nfc, step));
+ sunxi_nfc_user_data_to_buf(user_data, oob);
+
+ } else {
+ /*
+ * For H6 NAND controller, the user data for all steps is
+ * contained in 32 user data registers, but not at a specific
+ * offset for each step, they are just concatenated.
+ */
+ unsigned int user_data_off = 0;
+ unsigned int reg_off;
+ u8 *ptr = oob;
+ unsigned int i;
+
+ for (i = 0; i < step; i++)
+ user_data_off += sunxi_nfc_user_data_sz(sunxi_nand, i);
+
+ user_data_off /= 4;
+ for (i = 0; i < user_data_sz / 4; i++, ptr += 4) {
+ reg_off = NFC_REG_USER_DATA(nfc, user_data_off + i);
+ user_data = readl(nfc->regs + reg_off);
+ sunxi_nfc_user_data_to_buf(user_data, ptr);
+ }
+ }
+
+ /* De-randomize the Bad Block Marker. */
+ if (bbm && nand->options & NAND_NEED_SCRAMBLING)
+ sunxi_nfc_randomize_bbm(&nand->mtd, page, oob);
+}
+
static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
u8 *data, int data_off,
u8 *oob, int oob_off,
int *cur_off,
unsigned int *max_bitflips,
- bool bbm, int page)
+ int step, int page)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step);
struct nand_ecc_ctrl *ecc = &nand->ecc;
int raw_mode = 0;
u32 status;
u32 pattern_found;
+ bool bbm = !step;
int ret;
+ /* From the controller point of view, we are at step 0 */
+ const int nfc_step = 0;
if (*cur_off != data_off)
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1);
@@ -771,8 +829,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
if (ret)
return ret;
- sunxi_nfc_reset_user_data_len(nfc);
- sunxi_nfc_set_user_data_len(nfc, 4, 0);
+ sunxi_nfc_set_user_data_len(nfc, user_data_sz, nfc_step);
sunxi_nfc_randomizer_enable(mtd);
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
@@ -783,31 +840,31 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
if (ret)
return ret;
- *cur_off = oob_off + ecc->bytes + 4;
+ *cur_off = oob_off + ecc->bytes + user_data_sz;
pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found);
pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found);
- if (pattern_found & NFC_ECC_PAT_FOUND(0)) {
+ if (pattern_found & NFC_ECC_PAT_FOUND(nfc_step)) {
u8 pattern = 0xff;
if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID(nfc)) & 0x1)))
pattern = 0x0;
memset(data, pattern, ecc->size);
- memset(oob, pattern, ecc->bytes + 4);
+ memset(oob, pattern, ecc->bytes + user_data_sz);
return 1;
}
- ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(nfc, 0)));
+ ret = NFC_ECC_ERR_CNT(nfc_step, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(nfc, nfc_step)));
memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
- sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, true, page);
+ sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + user_data_sz, true, page);
status = readl(nfc->regs + NFC_REG_ECC_ST);
- if (status & NFC_ECC_ERR(0)) {
+ if (status & NFC_ECC_ERR(nfc_step)) {
/*
* Re-read the data with the randomizer disabled to identify
* bitflips in erased pages.
@@ -816,26 +873,21 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1);
nand->read_buf(mtd, data, ecc->size);
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
- nand->read_buf(mtd, oob, ecc->bytes + 4);
+ nand->read_buf(mtd, oob, ecc->bytes + user_data_sz);
}
ret = nand_check_erased_ecc_chunk(data, ecc->size,
- oob, ecc->bytes + 4,
+ oob, ecc->bytes + user_data_sz,
NULL, 0, ecc->strength);
if (ret >= 0)
raw_mode = 1;
} else {
/*
- * The engine protects 4 bytes of OOB data per chunk.
+ * The engine protects user_data_sz bytes of OOB data per chunk.
* Retrieve the corrected OOB bytes.
*/
- sunxi_nfc_user_data_to_buf(readl(nfc->regs +
- NFC_REG_USER_DATA(nfc, 0)),
- oob);
-
- /* De-randomize the Bad Block Marker. */
- if (bbm && nand->options & NAND_NEED_SCRAMBLING)
- sunxi_nfc_randomize_bbm(mtd, page, oob);
+ sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, nfc_step,
+ bbm, page, user_data_sz);
}
if (ret < 0) {
@@ -848,13 +900,30 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
return raw_mode;
}
+/*
+ * Returns the offset of the OOB for each step.
+ * (it includes the user data before the ECC data.)
+ */
+static int sunxi_get_oob_offset(struct sunxi_nand_chip *sunxi_nand,
+ struct nand_ecc_ctrl *ecc, int step)
+{
+ int ecc_off = step * ecc->bytes;
+ int i;
+
+ for (i = 0; i < step; i++)
+ ecc_off += sunxi_nfc_user_data_sz(sunxi_nand, i);
+
+ return ecc_off;
+}
+
static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,
u8 *oob, int *cur_off,
bool randomize, int page)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &nand->ecc;
- int offset = ((ecc->bytes + 4) * ecc->steps);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ int offset = sunxi_get_oob_offset(sunxi_nand, ecc, ecc->steps);
int len = mtd->oobsize - offset;
if (len <= 0)
@@ -881,13 +950,18 @@ static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
const u8 *data, int data_off,
const u8 *oob, int oob_off,
- int *cur_off, bool bbm,
+ int *cur_off, int step,
int page)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step);
struct nand_ecc_ctrl *ecc = &nand->ecc;
+ bool bbm = !step;
int ret;
+ /* From the controller point of view, we are at step 0 */
+ const int nfc_step = 0;
if (data_off != *cur_off)
nand->cmdfunc(mtd, NAND_CMD_RNDIN, data_off, -1);
@@ -896,15 +970,20 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
/* Fill OOB data in */
if ((nand->options & NAND_NEED_SCRAMBLING) && bbm) {
- u8 user_data[4];
+ u8 *user_data;
- memcpy(user_data, oob, 4);
+ user_data = kzalloc(user_data_sz, GFP_KERNEL);
+ if (!user_data)
+ return -ENOMEM;
+
+ memcpy(user_data, oob, user_data_sz);
sunxi_nfc_randomize_bbm(mtd, page, user_data);
writel(sunxi_nfc_buf_to_user_data(user_data),
- nfc->regs + NFC_REG_USER_DATA(nfc, 0));
+ nfc->regs + NFC_REG_USER_DATA(nfc, nfc_step));
+ kfree(user_data);
} else {
writel(sunxi_nfc_buf_to_user_data(oob),
- nfc->regs + NFC_REG_USER_DATA(nfc, 0));
+ nfc->regs + NFC_REG_USER_DATA(nfc, nfc_step));
}
if (data_off + ecc->size != oob_off)
@@ -914,8 +993,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
if (ret)
return ret;
- sunxi_nfc_reset_user_data_len(nfc);
- sunxi_nfc_set_user_data_len(nfc, 4, 0);
+ sunxi_nfc_set_user_data_len(nfc, user_data_sz, nfc_step);
sunxi_nfc_randomizer_enable(mtd);
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
@@ -927,7 +1005,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
if (ret)
return ret;
- *cur_off = oob_off + ecc->bytes + 4;
+ *cur_off = oob_off + ecc->bytes + user_data_sz;
return 0;
}
@@ -938,7 +1016,8 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd,
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &nand->ecc;
- int offset = ((ecc->bytes + 4) * ecc->steps);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ int offset = sunxi_get_oob_offset(sunxi_nand, ecc, ecc->steps);
int len = mtd->oobsize - offset;
if (len <= 0)
@@ -957,6 +1036,8 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf,
int oob_required, int page)
{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
struct nand_ecc_ctrl *ecc = &chip->ecc;
unsigned int max_bitflips = 0;
int ret, i, cur_off = 0;
@@ -964,16 +1045,17 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
sunxi_nfc_hw_ecc_enable(mtd);
+ sunxi_nfc_reset_user_data_len(nfc);
for (i = 0; i < ecc->steps; i++) {
int data_off = i * ecc->size;
- int oob_off = i * (ecc->bytes + 4);
+ int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i);
u8 *data = buf + data_off;
u8 *oob = chip->oob_poi + oob_off;
ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
oob_off + mtd->writesize,
&cur_off, &max_bitflips,
- !i, page);
+ i, page);
if (ret < 0)
return ret;
else if (ret)
@@ -994,23 +1076,26 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd,
uint32_t data_offs, uint32_t readlen,
uint8_t *bufpoi, int page)
{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
struct nand_ecc_ctrl *ecc = &chip->ecc;
int ret, i, cur_off = 0;
unsigned int max_bitflips = 0;
sunxi_nfc_hw_ecc_enable(mtd);
+ sunxi_nfc_reset_user_data_len(nfc);
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
for (i = data_offs / ecc->size;
i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) {
int data_off = i * ecc->size;
- int oob_off = i * (ecc->bytes + 4);
+ int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i);
u8 *data = bufpoi + data_off;
u8 *oob = chip->oob_poi + oob_off;
ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off,
oob, oob_off + mtd->writesize,
- &cur_off, &max_bitflips, !i, page);
+ &cur_off, &max_bitflips, i, page);
if (ret < 0)
return ret;
}
@@ -1025,20 +1110,23 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
const uint8_t *buf, int oob_required,
int page)
{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
struct nand_ecc_ctrl *ecc = &chip->ecc;
int ret, i, cur_off = 0;
sunxi_nfc_hw_ecc_enable(mtd);
+ sunxi_nfc_reset_user_data_len(nfc);
for (i = 0; i < ecc->steps; i++) {
int data_off = i * ecc->size;
- int oob_off = i * (ecc->bytes + 4);
+ int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i);
const u8 *data = buf + data_off;
const u8 *oob = chip->oob_poi + oob_off;
ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob,
oob_off + mtd->writesize,
- &cur_off, !i, page);
+ &cur_off, i, page);
if (ret)
return ret;
}
@@ -1058,21 +1146,24 @@ static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd,
const u8 *buf, int oob_required,
int page)
{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
struct nand_ecc_ctrl *ecc = &chip->ecc;
int ret, i, cur_off = 0;
sunxi_nfc_hw_ecc_enable(mtd);
+ sunxi_nfc_reset_user_data_len(nfc);
for (i = data_offs / ecc->size;
i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) {
int data_off = i * ecc->size;
- int oob_off = i * (ecc->bytes + 4);
+ int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i);
const u8 *data = buf + data_off;
const u8 *oob = chip->oob_poi + oob_off;
ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob,
oob_off + mtd->writesize,
- &cur_off, !i, page);
+ &cur_off, i, page);
if (ret)
return ret;
}
@@ -1087,18 +1178,23 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
uint8_t *buf, int oob_required,
int page)
{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
struct nand_ecc_ctrl *ecc = &chip->ecc;
unsigned int max_bitflips = 0;
int ret, i, cur_off = 0;
bool raw_mode = false;
+ /* With hw_syndrome, user data length is fixed */
+ unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, 0);
sunxi_nfc_hw_ecc_enable(mtd);
+ sunxi_nfc_reset_user_data_len(nfc);
for (i = 0; i < ecc->steps; i++) {
- int data_off = i * (ecc->size + ecc->bytes + 4);
+ int data_off = i * (ecc->size + ecc->bytes + user_data_sz);
int oob_off = data_off + ecc->size;
u8 *data = buf + (i * ecc->size);
- u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4));
+ u8 *oob = chip->oob_poi + (i * (ecc->bytes + user_data_sz));
ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
oob_off, &cur_off,
@@ -1123,16 +1219,19 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
const uint8_t *buf,
int oob_required, int page)
{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);
struct nand_ecc_ctrl *ecc = &chip->ecc;
int ret, i, cur_off = 0;
+ /* With hw_syndrome, user data length is fixed */
+ unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, 0);
sunxi_nfc_hw_ecc_enable(mtd);
for (i = 0; i < ecc->steps; i++) {
- int data_off = i * (ecc->size + ecc->bytes + 4);
+ int data_off = i * (ecc->size + ecc->bytes + user_data_sz);
int oob_off = data_off + ecc->size;
const u8 *data = buf + (i * ecc->size);
- const u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4));
+ const u8 *oob = chip->oob_poi + (i * (ecc->bytes + user_data_sz));
ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off,
oob, oob_off, &cur_off,
@@ -1334,6 +1433,34 @@ static int sunxi_nand_chip_init_timings(struct sunxi_nfc *nfc,
return sunxi_nand_chip_set_timings(nfc, chip, timings);
}
+static int sunxi_nfc_maximize_user_data(struct nand_chip *nand, uint32_t oobsize,
+ int ecc_bytes, int nsectors)
+{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ const struct sunxi_nfc_caps *c = nfc->caps;
+ int remaining_bytes = oobsize - (ecc_bytes * nsectors);
+ int i, step;
+
+ sunxi_nand->user_data_bytes = devm_kzalloc(nfc->dev, nsectors,
+ GFP_KERNEL);
+ if (!sunxi_nand->user_data_bytes)
+ return -ENOMEM;
+
+ for (step = 0; (step < nsectors) && (remaining_bytes > 0); step++) {
+ for (i = 0; i < c->nuser_data_tab; i++) {
+ if (c->user_data_len_tab[i] > remaining_bytes)
+ break;
+ sunxi_nand->user_data_bytes[step] = c->user_data_len_tab[i];
+ }
+ remaining_bytes -= sunxi_nand->user_data_bytes[step];
+ if (sunxi_nand->user_data_bytes[step] == 0)
+ break;
+ }
+
+ return 0;
+}
+
static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
struct nand_ecc_ctrl *ecc)
{
@@ -1342,6 +1469,7 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
struct sunxi_nand_hw_ecc *data;
struct nand_ecclayout *layout;
+ unsigned int total_user_data_sz = 0;
int nsectors;
int ret;
int i;
@@ -1390,7 +1518,15 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
layout = &data->layout;
nsectors = mtd->writesize / ecc->size;
- if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) {
+ /* Use the remaining OOB space for user data */
+ if (nfc->caps->reg_user_data_len)
+ sunxi_nfc_maximize_user_data(nand, mtd->oobsize, ecc->bytes,
+ nsectors);
+
+ for (i = 0; i < nsectors; i++)
+ total_user_data_sz += sunxi_nfc_user_data_sz(sunxi_nand, i);
+
+ if (mtd->oobsize < ecc->bytes * nsectors + total_user_data_sz) {
ret = -EINVAL;
goto err;
}
@@ -1404,6 +1540,8 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
err:
kfree(data);
+ devm_kfree(nfc->dev, sunxi_nand->user_data_bytes);
+ sunxi_nand->user_data_bytes = NULL;
return ret;
}
@@ -1418,7 +1556,10 @@ static void sunxi_nand_hw_common_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc)
static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
struct nand_ecc_ctrl *ecc)
{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct nand_ecclayout *layout;
+ unsigned int total_user_data_sz = 0;
int nsectors;
int i, j;
int ret;
@@ -1440,14 +1581,14 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
layout->oobfree[i - 1].offset +
layout->oobfree[i - 1].length +
ecc->bytes;
- layout->oobfree[i].length = 4;
+ layout->oobfree[i].length = sunxi_nfc_user_data_sz(sunxi_nand, i);
} else {
/*
* The first 2 bytes are used for BB markers, hence we
- * only have 2 bytes available in the first user data
- * section.
+ * only have user_data_len(0) - 2 bytes available in the
+ * first user data section.
*/
- layout->oobfree[i].length = 2;
+ layout->oobfree[i].length = sunxi_nfc_user_data_sz(sunxi_nand, i) - 2;
layout->oobfree[i].offset = 2;
}
@@ -1457,13 +1598,16 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
layout->oobfree[i].length + j;
}
- if (mtd->oobsize > (ecc->bytes + 4) * nsectors) {
+ for (i = 0; i < nsectors; i++)
+ total_user_data_sz += sunxi_nfc_user_data_sz(sunxi_nand, i);
+
+ if (mtd->oobsize > ecc->bytes * nsectors + total_user_data_sz) {
layout->oobfree[nsectors].offset =
layout->oobfree[nsectors - 1].offset +
layout->oobfree[nsectors - 1].length +
ecc->bytes;
layout->oobfree[nsectors].length = mtd->oobsize -
- ((ecc->bytes + 4) * nsectors);
+ (ecc->bytes * nsectors + total_user_data_sz);
}
return 0;
@@ -1472,6 +1616,8 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd,
struct nand_ecc_ctrl *ecc)
{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct nand_ecclayout *layout;
int nsectors;
int i;
@@ -1481,7 +1627,13 @@ static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd,
if (ret)
return ret;
- ecc->prepad = 4;
+ for (i = 0; i < nsectors; i++)
+ if (sunxi_nfc_user_data_sz(sunxi_nand, i) !=
+ sunxi_nfc_user_data_sz(sunxi_nand, 0)) {
+ dev_err(mtd->dev, "Variable user data length not upported with NAND_ECC_HW_SYNDROME\n");
+ return -EOPNOTSUPP;
+ }
+ ecc->prepad = sunxi_nfc_user_data_sz(sunxi_nand, 0);
ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page;
ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page;
@@ -1596,21 +1748,20 @@ static int sunxi_nand_chip_init(struct udevice *dev, struct sunxi_nfc *nfc,
if (ret) {
dev_err(dev, "could not retrieve reg property: %d\n",
ret);
- kfree(chip);
- return ret;
+ goto err_free;
}
if (tmp > NFC_MAX_CS) {
dev_err(dev,
"invalid reg value: %u (max CS = 7)\n", tmp);
- kfree(chip);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_free;
}
if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
dev_err(dev, "CS %d already assigned\n", tmp);
- kfree(chip);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_free;
}
chip->sels[i].cs = tmp;
@@ -1636,15 +1787,13 @@ static int sunxi_nand_chip_init(struct udevice *dev, struct sunxi_nfc *nfc,
dev_err(dev,
"could not retrieve timings for ONFI mode 0: %d\n",
ret);
- kfree(chip);
- return ret;
+ goto err_free;
}
ret = sunxi_nand_chip_set_timings(nfc, chip, timings);
if (ret) {
dev_err(dev, "could not configure chip timings: %d\n", ret);
- kfree(chip);
- return ret;
+ goto err_free;
}
nand = &chip->nand;
@@ -1665,10 +1814,8 @@ static int sunxi_nand_chip_init(struct udevice *dev, struct sunxi_nfc *nfc,
mtd = nand_to_mtd(nand);
ret = nand_scan_ident(mtd, nsels, NULL);
- if (ret) {
- kfree(chip);
- return ret;
- }
+ if (ret)
+ goto err_free;
if (nand->bbt_options & NAND_BBT_USE_FLASH)
nand->bbt_options |= NAND_BBT_NO_OOB;
@@ -1681,34 +1828,35 @@ static int sunxi_nand_chip_init(struct udevice *dev, struct sunxi_nfc *nfc,
ret = sunxi_nand_chip_init_timings(nfc, chip);
if (ret) {
dev_err(dev, "could not configure chip timings: %d\n", ret);
- kfree(chip);
- return ret;
+ goto err_free;
}
ret = sunxi_nand_ecc_init(mtd, &nand->ecc);
if (ret) {
dev_err(dev, "ECC init failed: %d\n", ret);
- kfree(chip);
- return ret;
+ goto err_free;
}
ret = nand_scan_tail(mtd);
if (ret) {
dev_err(dev, "nand_scan_tail failed: %d\n", ret);
- kfree(chip);
- return ret;
+ goto err_free;
}
ret = nand_register(devnum, mtd);
if (ret) {
dev_err(dev, "failed to register mtd device: %d\n", ret);
- kfree(chip);
- return ret;
+ goto err_free;
}
list_add_tail(&chip->node, &nfc->chips);
return 0;
+
+err_free:
+ kfree(chip);
+
+ return ret;
}
static int sunxi_nand_chips_init(struct udevice *dev, struct sunxi_nfc *nfc)
@@ -1736,6 +1884,7 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
nand_release(&chip->mtd);
sunxi_nand_ecc_cleanup(&chip->nand.ecc);
list_del(&chip->node);
+ devm_kfree(nfc->dev, chip->user_data_bytes);
kfree(chip);
}
}
diff --git a/drivers/mtd/nand/raw/sunxi_nand.h b/drivers/mtd/nand/raw/sunxi_nand.h
index 6ee3ea14ee1..d7a8b3dd40c 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.h
+++ b/drivers/mtd/nand/raw/sunxi_nand.h
@@ -181,6 +181,9 @@
#define NFC_MAX_CS 7
+/* On A10, the user data length register is 4 bytes */
+#define USER_DATA_SZ 4
+
/*
* NAND Controller capabilities structure: stores NAND controller capabilities
* for distinction between compatible strings.
diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c
index 67f7d22ed2c..cf351de4e8d 100644
--- a/drivers/mtd/nand/raw/sunxi_nand_spl.c
+++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c
@@ -28,6 +28,7 @@ struct nfc_config {
bool randomize;
bool valid;
const struct sunxi_nfc_caps *caps;
+ u8 *user_data_bytes;
};
/* minimal "boot0" style NAND support for Allwinner A20 */
@@ -223,22 +224,6 @@ static int nand_change_column(u16 column)
return 0;
}
-/*
- * On H6/H616 the user_data length has to be set in specific registers
- * before writing.
- */
-static void sunxi_nfc_reset_user_data_len(const struct nfc_config *nfc)
-{
- int loop_step = NFC_REG_USER_DATA_LEN_CAPACITY;
-
- /* not all SoCs have this register */
- if (!NFC_REG_USER_DATA_LEN(nfc, 0))
- return;
-
- for (int i = 0; i < nfc->caps->max_ecc_steps; i += loop_step)
- writel_nfc(0, NFC_REG_USER_DATA_LEN(nfc, i));
-}
-
static void sunxi_nfc_set_user_data_len(const struct nfc_config *nfc,
int len, int step)
{
@@ -269,13 +254,19 @@ static void sunxi_nfc_set_user_data_len(const struct nfc_config *nfc,
writel_nfc(val, NFC_REG_USER_DATA_LEN(nfc, step));
}
+/*
+ * Values in this table are obtained by doing:
+ * DIV_ROUND_UP(info->ecc_strength * 14, 8)
+ * So it's the number of bytes needed for ECC one step
+ * (not counting the user data length)
+ */
#if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_H6)
static const int ecc_bytes[] = {
- 32, 46, 54, 60, 74, 82, 88, 96, 102, 110, 116, 124, 130, 138, 144
+ 28, 42, 50, 56, 70, 78, 84, 92, 98, 106, 112, 120, 126, 134, 140
};
#else
static const int ecc_bytes[] = {
- 32, 46, 54, 60, 74, 88, 102, 110, 116
+ 28, 42, 50, 56, 70, 84, 98, 106, 112
};
#endif
@@ -288,6 +279,14 @@ static void nand_readlcpy(u32 *dest, u32 * __iomem src, size_t len)
*dest++ = readl(src++);
}
+static u8 nand_user_data_sz(const struct nfc_config *conf, int step)
+{
+ if (!conf->user_data_bytes)
+ return USER_DATA_SZ;
+
+ return conf->user_data_bytes[step];
+}
+
static int nand_read_page(const struct nfc_config *conf, u32 offs,
void *dest, int len)
{
@@ -295,8 +294,11 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs,
u16 rand_seed = 0;
int oob_chunk_sz = ecc_bytes[conf->ecc_strength];
int page = offs / conf->page_size;
+ int oob_off = conf->page_size;
u32 ecc_st, pattern_found;
int i;
+ /* From the controller point of view, we are at step 0 */
+ const int nfc_step = 0;
if (offs % conf->page_size || len % conf->ecc_size ||
len > conf->page_size || len < 0)
@@ -309,9 +311,9 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs,
/* Retrieve data from SRAM (PIO) */
for (i = 0; i < nsectors; i++) {
int data_off = i * conf->ecc_size;
- int oob_off = conf->page_size + (i * oob_chunk_sz);
u8 *data = dest + data_off;
u32 ecc512_bit = 0;
+ unsigned int user_data_sz = nand_user_data_sz(conf, i);
if (conf->caps->has_ecc_block_512 && conf->ecc_size == 512)
ecc512_bit = NFC_ECC_BLOCK_512;
@@ -337,8 +339,7 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs,
*/
nand_change_column(oob_off);
- sunxi_nfc_reset_user_data_len(conf);
- sunxi_nfc_set_user_data_len(conf, 4, 0);
+ sunxi_nfc_set_user_data_len(conf, user_data_sz, nfc_step);
nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_OP);
/* Get the ECC status */
@@ -356,7 +357,7 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs,
pattern_found = readl_nfc(conf->caps->reg_pat_found);
pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(conf),
pattern_found);
- if (pattern_found & NFC_ECC_PAT_FOUND(0))
+ if (pattern_found & NFC_ECC_PAT_FOUND(nfc_step))
return 1;
}
@@ -364,13 +365,61 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs,
nand_readlcpy((u32 *)data,
(void *)(uintptr_t)SUNXI_NFC_BASE + NFC_RAM0_BASE,
conf->ecc_size);
-
/* Stop the ECC engine */
writel_nfc(readl_nfc(NFC_REG_ECC_CTL) & ~NFC_ECC_EN,
NFC_REG_ECC_CTL);
if (data_off + conf->ecc_size >= len)
break;
+
+ oob_off += oob_chunk_sz + user_data_sz;
+ }
+
+ return 0;
+}
+
+static int nand_min_user_data_sz(struct nfc_config *conf, int nsectors)
+{
+ const struct sunxi_nfc_caps *c = conf->caps;
+ int min_user_data_sz = 0;
+ int i;
+
+ if (!c->reg_user_data_len) {
+ for (i = 0; i < nsectors; i++)
+ min_user_data_sz += nand_user_data_sz(conf, i);
+ } else {
+ for (i = 0; i < c->nuser_data_tab; i++)
+ /* We want at least enough size for the BBM */
+ if (c->user_data_len_tab[i] >= 2)
+ break;
+ min_user_data_sz = c->user_data_len_tab[i];
+ }
+
+ return min_user_data_sz;
+}
+
+static int nand_maximize_user_data(struct nfc_config *conf, uint32_t oobsize,
+ int ecc_len, int nsectors)
+{
+ const struct sunxi_nfc_caps *c = conf->caps;
+ int remaining_bytes = oobsize - (ecc_len * nsectors);
+ int i, step;
+
+ kfree(conf->user_data_bytes);
+
+ conf->user_data_bytes = kzalloc(nsectors, GFP_KERNEL);
+ if (!conf->user_data_bytes)
+ return -ENOMEM;
+
+ for (step = 0; (step < nsectors) && (remaining_bytes > 0); step++) {
+ for (i = 0; i < c->nuser_data_tab; i++) {
+ if (c->user_data_len_tab[i] > remaining_bytes)
+ break;
+ conf->user_data_bytes[step] = c->user_data_len_tab[i];
+ }
+ remaining_bytes -= conf->user_data_bytes[step];
+ if (conf->user_data_bytes[step] == 0)
+ break;
}
return 0;
@@ -380,7 +429,8 @@ static int nand_max_ecc_strength(struct nfc_config *conf)
{
int max_oobsize, max_ecc_bytes;
int nsectors = conf->page_size / conf->ecc_size;
- int i;
+ unsigned int total_user_data_sz = 0;
+ int ecc_idx, i;
/*
* ECC strength is limited by the size of the OOB area which is
@@ -405,15 +455,38 @@ static int nand_max_ecc_strength(struct nfc_config *conf)
max_ecc_bytes = max_oobsize / nsectors;
- for (i = 0; i < ARRAY_SIZE(ecc_bytes); i++) {
- if (ecc_bytes[i] > max_ecc_bytes)
+ /*
+ * nand_min_user_data_sz() will return the total_user_data_sz in case
+ * of a fixed user data length, or the minimal usable user data size
+ * in case of variable data length (with at least enough space for the
+ * BBM.
+ */
+ total_user_data_sz = nand_min_user_data_sz(conf, nsectors);
+
+ for (ecc_idx = 0; ecc_idx < ARRAY_SIZE(ecc_bytes); ecc_idx++) {
+ if (ecc_bytes[ecc_idx] + total_user_data_sz > max_ecc_bytes)
break;
}
- if (!i)
+ if (!ecc_idx)
return -EINVAL;
- return i - 1;
+ ecc_idx--;
+
+ /*
+ * The rationale for variable data length is to prioritize maximum ECC
+ * strength, and then use the remaining space for user data.
+ */
+ if (conf->caps->reg_user_data_len) {
+ nand_maximize_user_data(conf, max_oobsize,
+ ecc_bytes[ecc_idx], nsectors);
+
+ total_user_data_sz = 0;
+ for (i = 0; i < nsectors; i++)
+ total_user_data_sz += nand_user_data_sz(conf, i);
+ }
+
+ return ecc_idx;
}
static int nand_detect_ecc_config(struct nfc_config *conf, u32 offs,
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index ed07e286676..666618681df 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -339,7 +339,7 @@ config ESSEDMA
config ETH_SANDBOX
depends on SANDBOX
- depends on NET || NET_LWIP
+ depends on NET
default y
bool "Sandbox: Mocked Ethernet driver"
help
@@ -350,7 +350,7 @@ config ETH_SANDBOX
config ETH_SANDBOX_RAW
depends on SANDBOX
- depends on NET
+ depends on NET_LEGACY
default y
bool "Sandbox: Bridge to Linux Raw Sockets"
help
@@ -476,7 +476,7 @@ config FTMAC100
config FTGMAC100
bool "Ftgmac100 Ethernet Support"
select PHYLIB
- depends on NET
+ depends on NET_LEGACY
help
This driver supports the Faraday's FTGMAC100 Gigabit SoC
Ethernet controller that can be found on Aspeed SoCs (which
diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c
index a4ba27904bc..206f1a381bb 100644
--- a/drivers/net/fsl_enetc.c
+++ b/drivers/net/fsl_enetc.c
@@ -8,6 +8,7 @@
#include <clk.h>
#include <cpu_func.h>
#include <dm.h>
+#include <dm/device_compat.h>
#include <errno.h>
#include <fdt_support.h>
#include <malloc.h>
@@ -20,14 +21,21 @@
#include <linux/bug.h>
#include <linux/delay.h>
#include <linux/build_bug.h>
+#include <linux/bitfield.h>
+#include <power/regulator.h>
+#include "fsl_enetc.h"
#ifdef CONFIG_ARCH_IMX9
#include <asm/mach-imx/sys_proto.h>
#include <cpu_func.h>
+#include "fsl_enetc_xpcs_phy.c"
+#else
+static inline int xpcs_phy_usxgmii_pma_config(struct udevice *dev)
+{
+ return 0;
+}
#endif
-#include "fsl_enetc.h"
-
#define ENETC_DRIVER_NAME "enetc_eth"
/*
@@ -454,19 +462,23 @@ static void enetc_setup_mac_iface(struct udevice *dev,
/* set up serdes for SXGMII */
static int enetc_init_sxgmii(struct udevice *dev)
{
- struct enetc_priv *priv = dev_get_priv(dev);
-
if (!enetc_has_imdio(dev))
return 0;
- /* Dev ability - SXGMII */
- enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, ENETC_PCS_DEVAD_REPL,
- ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SXGMII);
+ if (enetc_is_imx95(dev)) {
+ xpcs_phy_usxgmii_pma_config(dev);
+ } else {
+ struct enetc_priv *priv = dev_get_priv(dev);
+
+ /* Dev ability - SXGMII */
+ enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, ENETC_PCS_DEVAD_REPL,
+ ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SXGMII);
- /* Restart PCS AN */
- enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, ENETC_PCS_DEVAD_REPL,
- ENETC_PCS_CR,
- ENETC_PCS_CR_RST | ENETC_PCS_CR_RESET_AN);
+ /* Restart PCS AN */
+ enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, ENETC_PCS_DEVAD_REPL,
+ ENETC_PCS_CR,
+ ENETC_PCS_CR_RST | ENETC_PCS_CR_RESET_AN);
+ }
return 0;
}
@@ -523,6 +535,10 @@ static int enetc_config_phy(struct udevice *dev)
return -ENODEV;
supported = PHY_GBIT_FEATURES | SUPPORTED_2500baseX_Full;
+
+ if (enetc_is_imx95(dev))
+ supported |= PHY_10G_FEATURES;
+
priv->phy->supported &= supported;
priv->phy->advertising &= supported;
@@ -537,12 +553,31 @@ static int enetc_probe(struct udevice *dev)
{
struct enetc_priv *priv = dev_get_priv(dev);
int res;
+ struct udevice *supply = NULL;
if (ofnode_valid(dev_ofnode(dev)) && !ofnode_is_enabled(dev_ofnode(dev))) {
enetc_dbg(dev, "interface disabled\n");
return -ENODEV;
}
+ if (CONFIG_IS_ENABLED(DM_REGULATOR)) {
+ res = device_get_supply_regulator(dev, "serdes-supply",
+ &supply);
+ if (res && res != -ENOENT) {
+ printf("%s: device_get_supply_regulator failed: %d\n",
+ __func__, res);
+ return res;
+ }
+
+ if (supply) {
+ res = regulator_set_enable_if_allowed(supply, true);
+ if (res) {
+ printf("%s: Error enabling phy supply\n", dev->name);
+ return res;
+ }
+ }
+ }
+
priv->enetc_txbd = memalign(ENETC_BD_ALIGN,
sizeof(struct enetc_tx_bd) * ENETC_BD_CNT);
priv->enetc_rxbd = memalign(ENETC_BD_ALIGN,
diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h
index 804df853bf5..6d868e82f8c 100644
--- a/drivers/net/fsl_enetc.h
+++ b/drivers/net/fsl_enetc.h
@@ -205,6 +205,7 @@ struct enetc_data {
/* PCS / internal SoC PHY ID, it defaults to 0 on all interfaces */
#define ENETC_PCS_PHY_ADDR 0
+#define ENETC_NON_PCS_PHY_ADDR 16
/* PCS registers */
#define ENETC_PCS_CR 0x00
diff --git a/drivers/net/fsl_enetc_xpcs_phy.c b/drivers/net/fsl_enetc_xpcs_phy.c
new file mode 100644
index 00000000000..4039690223d
--- /dev/null
+++ b/drivers/net/fsl_enetc_xpcs_phy.c
@@ -0,0 +1,970 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2024 NXP
+ */
+
+#define XPCS_PHY_GLOBAL 0x0
+#define XPCS_PHY_MPLLA 0x1
+#define XPCS_PHY_MPLLB 0x2
+#define XPCS_PHY_LANE 0x3
+#define XPCS_PHY_MAC_ADAPTER 0x1f
+
+#define XPCS_PHY_REG(x) (((x) & 0x1fffe) >> 1)
+
+/* MAC ADAPTER */
+#define MAC_ADAPTER_LOCK_PHY 0x200
+#define MAC_ADAPTER_LOCK_MPLLA 0x204
+#define MAC_ADAPTER_LOCK_MPLLB 0x208
+#define MAC_ADAPTER_LOCK_ROM 0x20c
+#define MAC_ADAPTER_LOCK_RAM 0x210
+#define MAC_ADAPTER_LOCK_EVENT 0x214
+
+#define MAC_ADAPTER_LOCK_LOCK BIT(7)
+
+/* PMA */
+#define PMA_RX_LSTS 0x10040
+#define PMA_RX_LSTS_RX_VALID_0 BIT(12)
+#define PMA_MP_12G_16G_25G_TX_GENCTRL0 0x10060
+#define PMA_TX_GENCTRL0_TX_RST_0 BIT(8)
+#define PMA_TX_GENCTRL0_TX_DT_EN_0 BIT(12)
+#define PMA_MP_12G_16G_25G_TX_GENCTRL1 0x10062
+#define PMA_TX_GENCTRL1_VBOOST_EN_0 BIT(4)
+#define PMA_TX_GENCTRL1_VBOOST_LVL_MASK GENMASK(10, 8)
+#define PMA_TX_GENCTRL1_VBOOST_LVL(x) (((x) << 8) & GENMASK(10, 8))
+#define PMA_TX_GENCTRL1_TX_CLK_RDY_0 BIT(12)
+#define PMA_MP_12G_16G_TX_GENCTRL2 0x10064
+#define PMA_TX_GENCTRL2_TX_REQ_0 BIT(0)
+#define PMA_TX_GENCTRL2_TX0_WIDTH_MASK GENMASK(9, 8)
+#define PMA_TX_GENCTRL2_TX0_WIDTH(x) (((x) << 8) & GENMASK(9, 8))
+#define PMA_MP_12G_16G_25G_TX_BOOST_CTRL 0x10066
+#define PMA_TX_BOOST_CTRL_TX0_IBOOST_MASK GENMASK(3, 0)
+#define PMA_TX_BOOST_CTRL_TX0_IBOOST(x) ((x) & GENMASK(3, 0))
+#define PMA_MP_12G_16G_25G_TX_RATE_CTRL 0x10068
+#define PMA_TX_RATE_CTRL_TX0_RATE_MASK GENMASK(2, 0)
+#define PMA_TX_RATE_CTRL_TX0_RATE(x) ((x) & GENMASK(2, 0))
+#define PMA_MP_12G_16G_25G_TX_POWER_STATE_CTRL 0x1006A
+#define PMA_POWER_STATE_CTRL_TX0_PSTATE_MASK GENMASK(1, 0)
+#define PMA_POWER_STATE_CTRL_TX0_PSTATE(x) ((x) & GENMASK(1, 0))
+#define PMA_POWER_STATE_CTRL_TX_DISABLE_0 BIT(8)
+#define PMA_MP_12G_16G_25G_TX_EQ_CTRL0 0x1006C
+#define PMA_TX_EQ_CTRL0_TX_EQ_PRE_MASK GENMASK(5, 0)
+#define PMA_TX_EQ_CTRL0_TX_EQ_PRE(x) ((x) & GENMASK(5, 0))
+#define PMA_TX_EQ_CTRL0_TX_EQ_MAIN_MASK GENMASK(13, 8)
+#define PMA_TX_EQ_CTRL0_TX_EQ_MAIN(x) (((x) << 8) & GENMASK(13, 8))
+#define PMA_MP_12G_16G_25G_TX_EQ_CTRL1 0x1006E
+#define PMA_TX_EQ_CTRL1_TX_EQ_POST_MASK GENMASK(5, 0)
+#define PMA_TX_EQ_CTRL1_TX_EQ_POST(x) ((x) & GENMASK(5, 0))
+#define PMA_MP_16G_25G_TX_MISC_CTRL0 0x1007C
+#define PMA_TX_MISC_CTRL0_TX0_MISC_MASK GENMASK(7, 0)
+#define PMA_TX_MISC_CTRL0_TX0_MISC(x) ((x) & GENMASK(7, 0))
+#define PMA_MP_12G_16G_25G_RX_GENCTRL0 0x100A0
+#define PMA_RX_GENCTRL0_RX_DT_EN_0 BIT(8)
+#define PMA_MP_12G_16G_25G_RX_GENCTRL1 0x100A2
+#define PMA_RX_GENCTRL1_RX_RST_0 BIT(4)
+#define PMA_RX_GENCTRL1_RX_TERM_ACDC_0 BIT(8)
+#define PMA_RX_GENCTRL1_RX_DIV16P5_CLK_EN_0 BIT(12)
+#define PMA_MP_12G_16G_RX_GENCTRL2 0x100A4
+#define PMA_RX_GENCTRL2_RX_REQ_0 BIT(0)
+#define PMA_RX_GENCTRL2_RX0_WIDTH_MASK GENMASK(9, 8)
+#define PMA_RX_GENCTRL2_RX0_WIDTH(x) (((x) << 8) & GENMASK(9, 8))
+#define PMA_MP_12G_16G_RX_GENCTRL3 0x100A6
+#define PMA_RX_GENCTRL3_LOS_TRSHLD_0_MASK GENMASK(2, 0)
+#define PMA_RX_GENCTRL3_LOS_TRSHLD_0(x) ((x) & GENMASK(2, 0))
+#define PMA_RX_GENCTRL3_LOS_LFPS_EN_0 BIT(12)
+#define PMA_MP_12G_16G_25G_RX_RATE_CTRL 0x100A8
+#define PMA_RX_RATE_CTRL_RX0_RATE_MASK GENMASK(1, 0)
+#define PMA_RX_RATE_CTRL_RX0_RATE(x) ((x) & GENMASK(1, 0))
+#define PMA_MP_12G_16G_25G_RX_POWER_STATE_CTRL 0x100AA
+#define PMA_RX_POWER_STATE_CTRL_RX0_PSTATE_MASK GENMASK(1, 0)
+#define PMA_RX_POWER_STATE_CTRL_RX0_PSTATE(x) ((x) & GENMASK(1, 0))
+#define PMA_RX_POWER_STATE_CTRL_RX_DISABLE_0 BIT(8)
+#define PMA_MP_12G_16G_25G_RX_CDR_CTRL 0x100AC
+#define PMA_RX_CDR_CTRL_CDR_SSC_EN_0 BIT(4)
+#define PMA_MP_12G_16G_25G_RX_ATTN_CTRL 0x100AE
+#define PMA_RX_ATTN_CTRL_RX0_EQ_ATT_LVL_MASK GENMASK(2, 0)
+#define PMA_RX_ATTN_CTRL_RX0_EQ_ATT_LVL(x) ((x) & GENMASK(2, 0))
+#define PMA_MP_16G_25G_RX_EQ_CTRL0 0x100B0
+#define PMA_RX_EQ_CTRL0_CTLE_BOOST_0_MASK GENMASK(4, 0)
+#define PMA_RX_EQ_CTRL0_CTLE_BOOST_0(x) ((x) & GENMASK(4, 0))
+#define PMA_RX_EQ_CTRL0_CTLE_POLE_0_MASK GENMASK(6, 5)
+#define PMA_RX_EQ_CTRL0_CTLE_POLE_0(x) (((x) << 5) & GENMASK(6, 5))
+#define PMA_RX_EQ_CTRL0_VGA2_GAIN_0_MASK GENMASK(10, 8)
+#define PMA_RX_EQ_CTRL0_VGA2_GAIN_0(x) (((x) << 8) & GENMASK(10, 8))
+#define PMA_RX_EQ_CTRL0_VGA1_GAIN_0_MASK GENMASK(14, 12)
+#define PMA_RX_EQ_CTRL0_VGA1_GAIN_0(x) (((x) << 12) & GENMASK(14, 12))
+#define PMA_MP_12G_16G_25G_RX_EQ_CTRL4 0x100B8
+#define PMA_RX_EQ_CTRL4_CONT_ADAPT_0 BIT(0)
+#define PMA_RX_EQ_CTRL4_RX_AD_REQ BIT(12)
+#define PMA_MP_16G_25G_RX_EQ_CTRL5 0x100BA
+#define PMA_RX_EQ_CTRL5_RX_ADPT_SEL_0 BIT(0)
+#define PMA_RX_EQ_CTRL5_RX0_ADPT_MODE_MASK GENMASK(5, 4)
+#define PMA_RX_EQ_CTRL5_RX0_ADPT_MODE(x) (((x) << 4) & GENMASK(5, 4))
+#define PMA_MP_12G_16G_25G_DFE_TAP_CTRL0 0x100BC
+#define PMA_DFE_TAP_CTRL0_DFE_TAP1_0_MASK GENMASK(7, 0)
+#define PMA_DFE_TAP_CTRL0_DFE_TAP1_0(x) ((x) & GENMASK(7, 0))
+#define PMA_MP_16G_RX_CDR_CTRL1 0x100C8
+#define PMA_RX_CDR_CTRL1_VCO_TEMP_COMP_EN_0 BIT(0)
+#define PMA_RX_CDR_CTRL1_VCO_STEP_CTRL_0 BIT(4)
+#define PMA_RX_CDR_CTRL1_VCO_FRQBAND_0_MASK GENMASK(9, 8)
+#define PMA_RX_CDR_CTRL1_VCO_FRQBAND_0(x) (((x) << 8) & GENMASK(9, 8))
+#define PMA_MP_16G_25G_RX_PPM_CTRL0 0x100CA
+#define PMA_RX_PPM_CTRL0_RX0_CDR_PPM_MAX_MASK GENMASK(4, 0)
+#define PMA_RX_PPM_CTRL0_RX0_CDR_PPM_MAX(x) ((x) & GENMASK(4, 0))
+#define PMA_MP_16G_25G_RX_GENCTRL4 0x100D0
+#define PMA_RX_GENCTRL4_RX_DFE_BYP_0 BIT(8)
+#define PMA_MP_16G_25G_RX_MISC_CTRL0 0x100D2
+#define PMA_RX_MISC_CTRL0_RX0_MISC_MASK GENMASK(7, 0)
+#define PMA_RX_MISC_CTRL0_RX0_MISC(x) ((x) & GENMASK(7, 0))
+#define PMA_MP_16G_25G_RX_IQ_CTRL0 0x100D6
+#define PMA_RX_IQ_CTRL0_RX0_MARGIN_IQ_MASK GENMASK(6, 0)
+#define PMA_RX_IQ_CTRL0_RX0_MARGIN_IQ(x) ((x) & GENMASK(6, 0))
+#define PMA_RX_IQ_CTRL0_RX0_DELTA_IQ_MASK GENMASK(11, 8)
+#define PMA_RX_IQ_CTRL0_RX0_DELTA_IQ(x) (((x) << 8) & GENMASK(11, 8))
+#define PMA_MP_12G_16G_25G_MPLL_CMN_CTRL 0x100E0
+#define PMA_MPLL_CMN_CTRL_MPLL_EN_0 BIT(0)
+#define PMA_MPLL_CMN_CTRL_MPLLB_SEL_0 BIT(4)
+#define PMA_MP_12G_16G_MPLLA_CTRL0 0x100E2
+#define PMA_MPLLA_CTRL0_MPLLA_MULTIPLIER_MASK GENMASK(7, 0)
+#define PMA_MPLLA_CTRL0_MPLLA_MULTIPLIER(x) ((x) & GENMASK(7, 0))
+#define PMA_MP_16G_MPLLA_CTRL1 0x100E4
+#define PMA_MPLLA_CTRL1_MPLLA_SSC_EN BIT(0)
+#define PMA_MPLLA_CTRL1_MPLLA_SSC_CLK_SEL BIT(4)
+#define PMA_MPLLA_CTRL1_MPLLA_FRACN_CTRL_MASK GENMASK(15, 5)
+#define PMA_MPLLA_CTRL1_MPLLA_FRACN_CTRL(x) (((x) << 5) & GENMASK(15, 5))
+#define PMA_MP_12G_16G_MPLLA_CTRL2 0x100E6
+#define PMA_MPLLA_CTRL2_MPLLA_DIV_MULT_MASK GENMASK(6, 0)
+#define PMA_MPLLA_CTRL2_MPLLA_DIV_MULT(x) ((x) & GENMASK(6, 0))
+#define PMA_MPLLA_CTRL2_MPLLA_DIV_CLK_EN BIT(7)
+#define PMA_MPLLA_CTRL2_MPLLA_DIV8_CLK_EN BIT(8)
+#define PMA_MPLLA_CTRL2_MPLLA_DIV10_CLK_EN BIT(9)
+#define PMA_MPLLA_CTRL2_MPLLA_DIV16P5_CLK_EN BIT(10)
+#define PMA_MPLLA_CTRL2_MPLLA_TX_CLK_DIV_MASK GENMASK(12, 11)
+#define PMA_MPLLA_CTRL2_MPLLA_TX_CLK_DIV(x) (((x) << 11) & GENMASK(12, 11))
+#define PMA_MP_16G_MPLLA_CTRL3 0x100EE
+#define PMA_MPLLA_CTRL3_MPLLA_BANDWIDTH_MASK GENMASK(15, 0)
+#define PMA_MPLLA_CTRL3_MPLLA_BANDWIDTH(x) ((x) & GENMASK(15, 0))
+#define PMA_MP_16G_MPLLA_CTRL4 0x100F2
+#define PMA_MPLLA_CTRL4_MPLLA_SSC_FRQ_CNT_INT_MASK GENMASK(11, 0)
+#define PMA_MPLLA_CTRL4_MPLLA_SSC_FRQ_CNT_INT(x) ((x) & GENMASK(11, 0))
+#define PMA_MP_16G_MPLLA_CTRL5 0x100F4
+#define PMA_MPLLA_CTRL5_MPLLA_SSC_FRQ_CNT_PK_MASK GENMASK(7, 0)
+#define PMA_MPLLA_CTRL5_MPLLA_SSC_FRQ_CNT_PK(x) ((x) & GENMASK(7, 0))
+#define PMA_MPLLA_CTRL5_MPLLA_SSC_SPD_EN BIT(8)
+#define PMA_MP_12G_16G_25G_MISC_CTRL0 0x10120
+#define PMA_MISC_CTRL0_RX_VREF_CTRL_MASK GENMASK(12, 8)
+#define PMA_MISC_CTRL0_RX_VREF_CTRL(x) (((x) << 8) & GENMASK(12, 8))
+#define PMA_MP_12G_16G_25G_REF_CLK_CTRL 0x10122
+#define PMA_REF_CLK_CTRL_REF_CLK_DIV2 BIT(2)
+#define PMA_REF_CLK_CTRL_REF_RANGE_MASK GENMASK(5, 3)
+#define PMA_REF_CLK_CTRL_REF_RANGE(x) (((x) << 3) & GENMASK(5, 3))
+#define PMA_REF_CLK_CTRL_REF_MPLLA_DIV2 BIT(6)
+#define PMA_MP_12G_16G_25G_VCO_CAL_LD0 0x10124
+#define PMA_VCO_CAL_LD0_VCO_LD_VAL_0_MASK GENMASK(12, 0)
+#define PMA_VCO_CAL_LD0_VCO_LD_VAL_0(x) ((x) & GENMASK(12, 0))
+#define PMA_MP_16G_25G_VCO_CAL_REF0 0x1012C
+#define PMA_VCO_CAL_REF0_VCO_REF_LD_0_MASK GENMASK(6, 0)
+#define PMA_VCO_CAL_REF0_VCO_REF_LD_0(x) ((x) & GENMASK(6, 0))
+#define PMA_MP_12G_16G_25G_MISC_STS 0x10130
+#define PMA_MISC_STS_RX_ADPT_ACK BIT(12)
+#define PMA_MP_12G_16G_25G_SRAM 0x10136
+#define PMA_SRAM_INIT_DN BIT(0)
+#define PMA_SRAM_EXT_LD_DN BIT(1)
+#define PMA_MP_16G_25G_MISC_CTRL2 0x10138
+#define PMA_MISC_CTRL2_SUP_MISC_MASK GENMASK(7, 0)
+#define PMA_MISC_CTRL2_SUP_MISC(x) ((x) & GENMASK(7, 0))
+
+/* PCS */
+#define PCS_CTRL1 0x0
+#define PCS_CTRL1_RESET BIT(15)
+#define PCS_CTRL2 0xE
+#define PCS_CTRL2_PCS_TYPE_SEL_MASK GENMASK(3, 0)
+#define PCS_CTRL2_PCS_TYPE_SEL(x) ((x) & GENMASK(3, 0))
+#define PCS_DIG_CTRL1 0x10000
+#define PCS_DIG_CTRL1_USXG_EN BIT(9)
+#define PCS_DIG_CTRL1_USRA_RST BIT(10)
+#define PCS_DIG_CTRL1_VR_RST BIT(15)
+#define PCS_DEBUG_CTRL 0x1000A
+#define PCS_DEBUG_CTRL_SUPRESS_LOS_DET BIT(4)
+#define PCS_DEBUG_CTRL_RX_DT_EN_CTL BIT(6)
+#define PCS_DEBUG_CTRL_TX_PMBL_CTL BIT(8)
+#define PCS_KR_CTRL1 0x1000E
+#define PCS_KR_CTRL1_USXG_MODE_MASK GENMASK(12, 10)
+#define PCS_KR_CTRL1_USXG_MODE(x) (((x) << 10) & GENMASK(12, 10))
+
+/* VS MII MMD */
+#define MII_CTRL 0x0
+#define MII_CTRL_SS5 BIT(5)
+#define MII_CTRL_SS6 BIT(6)
+#define MII_CTRL_AN_ENABLE BIT(12)
+#define MII_CTRL_SS13 BIT(13)
+#define MII_DIG_CTRL1 0x10000
+#define MII_DIG_CTRL1_CL37_TMR_OVR_RIDE BIT(3)
+#define MII_AN_CTRL 0x10002
+#define MII_AN_CTRL_MII_AN_INTR_EN BIT(0)
+#define MII_AN_CTRL_TX_CONFIG BIT(3)
+#define MII_AN_INTR_STS 0x10004
+#define MII_AN_INTR_STS_CL37_ANCMPLT_INTR BIT(0)
+#define MII_LINK_TIMER_CTRL 0x10014
+#define MII_LINK_TIMER_CTRL_CL37_LINK_TIME_MASK GENMASK(15, 0)
+#define MII_LINK_TIMER_CTRL_CL37_LINK_TIME(x) ((x) & GENMASK(15, 0))
+
+/* E16 MEM MAP */
+#define IDCODE_LO 0x0
+#define IDCODE_HI 0x4
+#define GLOBAL_CTRL_EX_0 0x114
+#define GLOBAL_CTRL_EX_0_PHY_SRAM_BYPASS BIT(0)
+#define L0_RX_VCO_OVRD_OUT_0 0x20c
+#define L0_RX_VCO_OVRD_OUT_0_RX_ANA_CDR_FREQ_TUNE_MASK GENMASK(12, 3)
+#define L0_RX_VCO_OVRD_OUT_0_RX_ANA_CDR_FREQ_TUNE(x) (((x) << 3) & GENMASK(12, 3))
+#define L0_RX_VCO_OVRD_OUT_0_RX_CDR_FREQ_TUNE_OVRD_EN BIT(15)
+#define L0_RX_VCO_OVRD_OUT_2 0x214
+#define L0_RX_VCO_OVRD_OUT_2_RX_ANA_CDR_FREQ_TUNE_CLK BIT(0)
+
+static int enetc_mdio_read(struct mii_dev *bus, int addr, int devad, int reg);
+static int enetc_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, u16 val);
+
+int xpcs_read(struct udevice *dev, int devaddr, u32 reg)
+{
+ struct enetc_priv *priv = dev_get_priv(dev);
+
+ return enetc_mdio_read(&priv->imdio, ENETC_PCS_PHY_ADDR, devaddr, reg);
+}
+
+int xpcs_write(struct udevice *dev, int devaddr, u32 reg, u16 val)
+{
+ struct enetc_priv *priv = dev_get_priv(dev);
+
+ return enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, devaddr, reg, val);
+}
+
+int xpcs_phy_read(struct udevice *dev, int devaddr, u32 reg)
+{
+ struct enetc_priv *priv = dev_get_priv(dev);
+
+ return enetc_mdio_read(&priv->imdio, ENETC_NON_PCS_PHY_ADDR, devaddr, reg);
+}
+
+int xpcs_phy_write(struct udevice *dev, int devaddr, u32 reg, u16 val)
+{
+ struct enetc_priv *priv = dev_get_priv(dev);
+
+ return enetc_mdio_write(&priv->imdio, ENETC_NON_PCS_PHY_ADDR, devaddr, reg, val);
+}
+
+int xpcs_phy_read_pma(struct udevice *dev, u32 reg)
+{
+ return xpcs_read(dev, MDIO_MMD_PMAPMD, XPCS_PHY_REG(reg));
+}
+
+int xpcs_phy_write_pma(struct udevice *dev, int reg, u16 val)
+{
+ return xpcs_write(dev, MDIO_MMD_PMAPMD, XPCS_PHY_REG(reg), val);
+}
+
+int xpcs_phy_usxgmii_init_seq_2(struct udevice *dev)
+{
+ ulong begin;
+ u16 val;
+
+ /* Seq 2.1 Keep preamble data */
+ val = xpcs_read(dev, MDIO_MMD_PCS, XPCS_PHY_REG(PCS_DEBUG_CTRL));
+ val |= PCS_DEBUG_CTRL_TX_PMBL_CTL;
+ xpcs_write(dev, MDIO_MMD_PCS, XPCS_PHY_REG(PCS_DEBUG_CTRL), val);
+
+ /* Seq 2.2 Power up MPLLA to P1 state */
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_TX_POWER_STATE_CTRL);
+ val = u16_replace_bits(val, 2, PMA_POWER_STATE_CTRL_TX0_PSTATE_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_POWER_STATE_CTRL, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_MPLL_CMN_CTRL);
+ val |= PMA_MPLL_CMN_CTRL_MPLL_EN_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_MPLL_CMN_CTRL, val);
+
+ /* Seq 2.3 Assert request of transmitand receive */
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_TX_GENCTRL2);
+ val |= PMA_TX_GENCTRL2_TX_REQ_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_TX_GENCTRL2, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_RX_GENCTRL2);
+ val |= PMA_RX_GENCTRL2_RX_REQ_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_RX_GENCTRL2, val);
+
+ /* Seq 2.4 Poll for acknowledge */
+ begin = get_timer(0);
+ do {
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_TX_GENCTRL2);
+ if (get_timer(begin) > 500) {
+ dev_err(dev, "Polling timeout, line: %d\n", __LINE__);
+ goto timeout;
+ }
+ mdelay(10);
+ } while (val & PMA_TX_GENCTRL2_TX_REQ_0);
+
+ begin = get_timer(0);
+ do {
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_RX_GENCTRL2);
+ if (get_timer(begin) > 500) {
+ dev_err(dev, "Polling timeout, line: %d\n", __LINE__);
+ goto timeout;
+ }
+ mdelay(10);
+ } while (val & PMA_RX_GENCTRL2_RX_REQ_0);
+
+ /* Seq 2.5 Turn transmit to P0 state */
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_TX_POWER_STATE_CTRL);
+ val = u16_replace_bits(val, 0, PMA_POWER_STATE_CTRL_TX0_PSTATE_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_POWER_STATE_CTRL, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL0);
+ val &= ~PMA_TX_GENCTRL0_TX_RST_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL0, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_TX_POWER_STATE_CTRL);
+ val &= ~PMA_POWER_STATE_CTRL_TX_DISABLE_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_POWER_STATE_CTRL, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_MPLL_CMN_CTRL);
+ val |= PMA_MPLL_CMN_CTRL_MPLL_EN_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_MPLL_CMN_CTRL, val);
+
+ /* Seq 2.6 Turn receive to P0 state */
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_RX_GENCTRL1);
+ val &= ~PMA_RX_GENCTRL1_RX_RST_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_GENCTRL1, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_RX_POWER_STATE_CTRL);
+ val &= ~PMA_RX_POWER_STATE_CTRL_RX0_PSTATE_MASK;
+ val &= ~PMA_RX_POWER_STATE_CTRL_RX_DISABLE_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_POWER_STATE_CTRL, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_RX_POWER_STATE_CTRL);
+ val &= ~PMA_RX_POWER_STATE_CTRL_RX0_PSTATE_MASK;
+ val &= ~PMA_RX_POWER_STATE_CTRL_RX_DISABLE_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_POWER_STATE_CTRL, val);
+
+ /* Seq 2.7 Enable transmitter output driver in the PHY */
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL0);
+ val |= PMA_TX_GENCTRL0_TX_DT_EN_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL0, val);
+
+ /* Seq 2.8 Enable receiver data output from PHY */
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_RX_GENCTRL0);
+ val |= PMA_RX_GENCTRL0_RX_DT_EN_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_GENCTRL0, val);
+
+ /* Seq 2.9 Assert request of transmit and receive */
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_TX_GENCTRL2);
+ val |= PMA_TX_GENCTRL2_TX_REQ_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_TX_GENCTRL2, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_RX_GENCTRL2);
+ val |= PMA_RX_GENCTRL2_RX_REQ_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_RX_GENCTRL2, val);
+
+ /* Seq 2.10 Poll for acknowledge */
+ begin = get_timer(0);
+ do {
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_TX_GENCTRL2);
+ if (get_timer(begin) > 500) {
+ dev_err(dev, "Polling timeout, line: %d\n", __LINE__);
+ goto timeout;
+ }
+ mdelay(10);
+ schedule();
+ } while (val & PMA_TX_GENCTRL2_TX_REQ_0);
+
+ begin = get_timer(0);
+ do {
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_RX_GENCTRL2);
+ if (get_timer(begin) > 500) {
+ dev_err(dev, "Polling timeout, line: %d\n", __LINE__);
+ goto timeout;
+ }
+ mdelay(10);
+ schedule();
+ } while (val & PMA_RX_GENCTRL2_RX_REQ_0);
+
+ return 0;
+
+timeout:
+ return -ETIMEDOUT;
+}
+
+void xpcs_phy_reg_lock(struct udevice *dev)
+{
+ u16 val;
+ ulong begin;
+
+ if (xpcs_phy_read(dev, XPCS_PHY_MAC_ADAPTER, XPCS_PHY_REG(MAC_ADAPTER_LOCK_PHY)) & MAC_ADAPTER_LOCK_LOCK)
+ return;
+
+ xpcs_phy_write(dev, XPCS_PHY_MAC_ADAPTER, XPCS_PHY_REG(MAC_ADAPTER_LOCK_PHY), MAC_ADAPTER_LOCK_LOCK);
+ xpcs_phy_write(dev, XPCS_PHY_MAC_ADAPTER, XPCS_PHY_REG(MAC_ADAPTER_LOCK_MPLLA), MAC_ADAPTER_LOCK_LOCK);
+ xpcs_phy_write(dev, XPCS_PHY_MAC_ADAPTER, XPCS_PHY_REG(MAC_ADAPTER_LOCK_MPLLB), MAC_ADAPTER_LOCK_LOCK);
+ xpcs_phy_write(dev, XPCS_PHY_MAC_ADAPTER, XPCS_PHY_REG(MAC_ADAPTER_LOCK_ROM), MAC_ADAPTER_LOCK_LOCK);
+ xpcs_phy_write(dev, XPCS_PHY_MAC_ADAPTER, XPCS_PHY_REG(MAC_ADAPTER_LOCK_RAM), MAC_ADAPTER_LOCK_LOCK);
+
+ begin = get_timer(0);
+ do {
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_SRAM);
+ if (get_timer(begin) > 500) {
+ dev_err(dev, "Polling timeout, line: %d\n", __LINE__);
+ goto timeout;
+ }
+ mdelay(10);
+ } while (!(val & PMA_SRAM_INIT_DN));
+
+ /* Work around */
+ // xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_SRAM, PMA_SRAM_EXT_LD_DN);
+ xpcs_phy_write(dev, XPCS_PHY_GLOBAL, XPCS_PHY_REG(GLOBAL_CTRL_EX_0), GLOBAL_CTRL_EX_0_PHY_SRAM_BYPASS);
+
+ begin = get_timer(0);
+ do {
+ val = xpcs_read(dev, MDIO_MMD_PCS, XPCS_PHY_REG(PCS_CTRL1));
+ if (get_timer(begin) > 500) {
+ dev_err(dev, "Polling timeout, line: %d\n", __LINE__);
+ goto timeout;
+ }
+ mdelay(10);
+ } while (val & PCS_CTRL1_RESET);
+
+ mdelay(1);
+
+timeout:
+ return;
+}
+
+int xpcs_phy_usxgmii_pma_config(struct udevice *dev)
+{
+ ulong begin;
+ u16 val;
+
+ xpcs_phy_reg_lock(dev);
+
+ /* 1.6 Turn off C37 auto-negotiation */
+ val = xpcs_read(dev, MDIO_MMD_VEND2, XPCS_PHY_REG(MII_CTRL));
+ val &= ~MII_CTRL_AN_ENABLE;
+ xpcs_write(dev, MDIO_MMD_VEND2, XPCS_PHY_REG(MII_CTRL), val);
+
+ /* 1.7 Assert tx_reset and rx_reset*/
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL0);
+ val |= PMA_TX_GENCTRL0_TX_RST_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL0, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_RX_GENCTRL1);
+ val |= PMA_RX_GENCTRL1_RX_RST_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_GENCTRL1, val);
+
+ /* 1.8 Wait for more than 1us */
+ udelay(5);
+
+ /* 1.9 Deassert tx_reset and rx_reset*/
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL0);
+ val &= ~PMA_TX_GENCTRL0_TX_RST_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL0, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_RX_GENCTRL1);
+ val &= ~PMA_RX_GENCTRL1_RX_RST_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_GENCTRL1, val);
+
+ /* 1.10 Power down MPLLA */
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_TX_POWER_STATE_CTRL);
+ val = u16_replace_bits(val, 3, PMA_POWER_STATE_CTRL_TX0_PSTATE_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_POWER_STATE_CTRL, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_MPLL_CMN_CTRL);
+ val &= ~PMA_MPLL_CMN_CTRL_MPLL_EN_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_MPLL_CMN_CTRL, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL0);
+ val &= ~PMA_TX_GENCTRL0_TX_DT_EN_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL0, val);
+
+ /* 1.11 Change RX0 power state to P2 */
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_RX_GENCTRL0);
+ val &= ~PMA_RX_GENCTRL0_RX_DT_EN_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_GENCTRL0, val);
+
+ /* TODO: check if it is needed */
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_RX_POWER_STATE_CTRL);
+ val = u16_replace_bits(val, 1, PMA_RX_POWER_STATE_CTRL_RX0_PSTATE_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_POWER_STATE_CTRL, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_RX_POWER_STATE_CTRL);
+ val = u16_replace_bits(val, 3, PMA_RX_POWER_STATE_CTRL_RX0_PSTATE_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_POWER_STATE_CTRL, val);
+
+ /* 1.12 Assert request of transmit and receive */
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_TX_GENCTRL2);
+ val |= PMA_TX_GENCTRL2_TX_REQ_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_TX_GENCTRL2, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_RX_GENCTRL2);
+ val |= PMA_RX_GENCTRL2_RX_REQ_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_RX_GENCTRL2, val);
+
+ /* 1.13 Poll for acknlowledge */
+ begin = get_timer(0);
+ do {
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_TX_GENCTRL2);
+ if (get_timer(begin) > 500) {
+ dev_err(dev, "Polling timeout, line: %d\n", __LINE__);
+ goto timeout;
+ }
+ mdelay(10);
+ } while (val & PMA_TX_GENCTRL2_TX_REQ_0);
+
+ begin = get_timer(0);
+ do {
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_RX_GENCTRL2);
+ if (get_timer(begin) > 500) {
+ dev_err(dev, "Polling timeout, line: %d\n", __LINE__);
+ goto timeout;
+ }
+ mdelay(10);
+ } while (val & PMA_RX_GENCTRL2_RX_REQ_0);
+
+ /* 2 Config MPLL for 10G XGMII */
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_REF_CLK_CTRL);
+ val = u16_replace_bits(val, 6, PMA_REF_CLK_CTRL_REF_RANGE_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_REF_CLK_CTRL, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_REF_CLK_CTRL);
+ val &= ~PMA_REF_CLK_CTRL_REF_CLK_DIV2;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_REF_CLK_CTRL, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_REF_CLK_CTRL);
+ val |= PMA_REF_CLK_CTRL_REF_MPLLA_DIV2;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_REF_CLK_CTRL, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL2);
+ val &= ~PMA_MPLLA_CTRL2_MPLLA_DIV8_CLK_EN;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL2, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL2);
+ val |= PMA_MPLLA_CTRL2_MPLLA_DIV10_CLK_EN;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL2, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL2);
+ val |= PMA_MPLLA_CTRL2_MPLLA_DIV16P5_CLK_EN;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL2, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL2);
+ val &= ~PMA_MPLLA_CTRL2_MPLLA_TX_CLK_DIV_MASK;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL2, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL2);
+ val |= PMA_MPLLA_CTRL2_MPLLA_DIV_CLK_EN;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL2, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL2);
+ val = u16_replace_bits(val, 5, PMA_MPLLA_CTRL2_MPLLA_DIV_MULT_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL2, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_MPLLA_CTRL1);
+ val &= ~PMA_MPLLA_CTRL1_MPLLA_SSC_EN;
+ xpcs_phy_write_pma(dev, PMA_MP_16G_MPLLA_CTRL1, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_MPLLA_CTRL1);
+ val &= ~PMA_MPLLA_CTRL1_MPLLA_SSC_CLK_SEL;
+ xpcs_phy_write_pma(dev, PMA_MP_16G_MPLLA_CTRL1, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_MPLLA_CTRL5);
+ val &= ~PMA_MPLLA_CTRL5_MPLLA_SSC_FRQ_CNT_PK_MASK;
+ xpcs_phy_write_pma(dev, PMA_MP_16G_MPLLA_CTRL5, val);
+
+ xpcs_phy_write_pma(dev, PMA_MP_16G_MPLLA_CTRL4, 0);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_MPLLA_CTRL5);
+ val &= ~PMA_MPLLA_CTRL5_MPLLA_SSC_SPD_EN;
+ xpcs_phy_write_pma(dev, PMA_MP_16G_MPLLA_CTRL5, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_MPLLA_CTRL1);
+ val &= ~PMA_MPLLA_CTRL1_MPLLA_FRACN_CTRL_MASK;
+ xpcs_phy_write_pma(dev, PMA_MP_16G_MPLLA_CTRL1, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL0);
+ val = u16_replace_bits(val, 33, PMA_MPLLA_CTRL0_MPLLA_MULTIPLIER_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL0, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL1);
+ val = u16_replace_bits(val, 5, PMA_TX_GENCTRL1_VBOOST_LVL_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL1, val);
+
+ val = PMA_MPLLA_CTRL3_MPLLA_BANDWIDTH(0xA016);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_MPLLA_CTRL3, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_MISC_CTRL0);
+ val = u16_replace_bits(val, 0x11, PMA_MISC_CTRL0_RX_VREF_CTRL_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_MISC_CTRL0, val);
+
+ val = PMA_MISC_CTRL2_SUP_MISC(1);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_MISC_CTRL2, val);
+
+ val = PMA_VCO_CAL_REF0_VCO_REF_LD_0(0x29);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_VCO_CAL_REF0, val);
+
+ val = PMA_VCO_CAL_LD0_VCO_LD_VAL_0(0x549);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_VCO_CAL_LD0, val);
+
+ val = PMA_RX_PPM_CTRL0_RX0_CDR_PPM_MAX(0x12);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_RX_PPM_CTRL0, val);
+
+ /* 3 Configure LANE0 for 10G XGMII */
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_TX_MISC_CTRL0, 0x0);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_RATE_CTRL, 0x0);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_MPLL_CMN_CTRL);
+ val &= ~PMA_MPLL_CMN_CTRL_MPLLB_SEL_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_MPLL_CMN_CTRL, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_TX_GENCTRL2);
+ val = u16_replace_bits(val, 3, PMA_TX_GENCTRL2_TX0_WIDTH_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_TX_GENCTRL2, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL1);
+ val |= PMA_TX_GENCTRL1_VBOOST_EN_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL1, val);
+
+ val = PMA_TX_BOOST_CTRL_TX0_IBOOST(0xf);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_BOOST_CTRL, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_TX_EQ_CTRL0);
+ val = u16_replace_bits(val, 0, PMA_TX_EQ_CTRL0_TX_EQ_PRE_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_EQ_CTRL0, val);
+
+ val = PMA_TX_EQ_CTRL1_TX_EQ_POST(0x20);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_EQ_CTRL1, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_TX_EQ_CTRL0);
+ val = u16_replace_bits(val, 0x20, PMA_TX_EQ_CTRL0_TX_EQ_MAIN_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_EQ_CTRL0, val);
+
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_RATE_CTRL, 0x0);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_25G_RX_EQ_CTRL0);
+ val = u16_replace_bits(val, 0x2, PMA_RX_EQ_CTRL0_CTLE_POLE_0_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_RX_EQ_CTRL0, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_25G_RX_EQ_CTRL0);
+ val = u16_replace_bits(val, 0x10, PMA_RX_EQ_CTRL0_CTLE_BOOST_0_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_RX_EQ_CTRL0, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_RX_GENCTRL3);
+ val = u16_replace_bits(val, 0x7, PMA_RX_GENCTRL3_LOS_TRSHLD_0_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_RX_GENCTRL3, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_RX_CDR_CTRL1);
+ val |= PMA_RX_CDR_CTRL1_VCO_STEP_CTRL_0;
+ xpcs_phy_write_pma(dev, PMA_MP_16G_RX_CDR_CTRL1, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_RX_CDR_CTRL1);
+ val |= PMA_RX_CDR_CTRL1_VCO_TEMP_COMP_EN_0;
+ xpcs_phy_write_pma(dev, PMA_MP_16G_RX_CDR_CTRL1, val);
+
+ val = PMA_RX_MISC_CTRL0_RX0_MISC(0x12);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_RX_MISC_CTRL0, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_RX_GENCTRL2);
+ val = u16_replace_bits(val, 0x3, PMA_RX_GENCTRL2_RX0_WIDTH_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_RX_GENCTRL2, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_RX_GENCTRL1);
+ val |= PMA_RX_GENCTRL1_RX_DIV16P5_CLK_EN_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_GENCTRL1, val);
+
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_CDR_CTRL, 0x0);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_RX_GENCTRL3);
+ val &= ~PMA_RX_GENCTRL3_LOS_LFPS_EN_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_RX_GENCTRL3, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_25G_RX_GENCTRL4);
+ val &= ~PMA_RX_GENCTRL4_RX_DFE_BYP_0;
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_RX_GENCTRL4, val);
+
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_ATTN_CTRL, 0x0);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_25G_RX_EQ_CTRL0);
+ val = u16_replace_bits(val, 0x5, PMA_RX_EQ_CTRL0_VGA1_GAIN_0_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_RX_EQ_CTRL0, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_25G_RX_EQ_CTRL0);
+ val = u16_replace_bits(val, 0x5, PMA_RX_EQ_CTRL0_VGA2_GAIN_0_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_RX_EQ_CTRL0, val);
+
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_DFE_TAP_CTRL0, 0x0);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_RX_CDR_CTRL1);
+ val = u16_replace_bits(val, 0x1, PMA_RX_CDR_CTRL1_VCO_FRQBAND_0_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_RX_CDR_CTRL1, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_RX_GENCTRL1);
+ val |= PMA_RX_GENCTRL1_RX_TERM_ACDC_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_GENCTRL1, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_25G_RX_IQ_CTRL0);
+ val = u16_replace_bits(val, 0x0, PMA_RX_IQ_CTRL0_RX0_DELTA_IQ_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_RX_IQ_CTRL0, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_25G_RX_EQ_CTRL5);
+ val &= ~PMA_RX_EQ_CTRL5_RX_ADPT_SEL_0;
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_RX_EQ_CTRL5, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_25G_RX_EQ_CTRL5);
+ val = u16_replace_bits(val, 0x3, PMA_RX_EQ_CTRL5_RX0_ADPT_MODE_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_RX_EQ_CTRL5, val);
+
+ /* 4 Configure XPCS for 10G XGMII */
+ xpcs_write(dev, MDIO_MMD_PCS, XPCS_PHY_REG(PCS_CTRL2), 0x0);
+
+ val = xpcs_read(dev, MDIO_MMD_PCS, XPCS_PHY_REG(PCS_DIG_CTRL1));
+ val |= PCS_DIG_CTRL1_USXG_EN;
+ xpcs_write(dev, MDIO_MMD_PCS, XPCS_PHY_REG(PCS_DIG_CTRL1), val);
+
+ val = xpcs_read(dev, MDIO_MMD_PCS, XPCS_PHY_REG(PCS_KR_CTRL1));
+ val = u16_replace_bits(val, 0x0, PCS_KR_CTRL1_USXG_MODE_MASK);
+ xpcs_write(dev, MDIO_MMD_PCS, XPCS_PHY_REG(PCS_KR_CTRL1), val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL0);
+ val = u16_replace_bits(val, 0x21, PMA_MPLLA_CTRL0_MPLLA_MULTIPLIER_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL0, val);
+
+ val = PMA_MPLLA_CTRL3_MPLLA_BANDWIDTH(0xA016);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_MPLLA_CTRL3, val);
+
+ val = PMA_VCO_CAL_LD0_VCO_LD_VAL_0(0x549);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_VCO_CAL_LD0, val);
+
+ val = PMA_VCO_CAL_REF0_VCO_REF_LD_0(0x29);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_VCO_CAL_REF0, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_RX_EQ_CTRL4);
+ val |= PMA_RX_EQ_CTRL4_CONT_ADAPT_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_EQ_CTRL4, val);
+
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_RATE_CTRL, 0x0);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_RATE_CTRL, 0x0);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_TX_GENCTRL2);
+ val = u16_replace_bits(val, 0x3, PMA_TX_GENCTRL2_TX0_WIDTH_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_TX_GENCTRL2, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_RX_GENCTRL2);
+ val = u16_replace_bits(val, 0x3, PMA_RX_GENCTRL2_RX0_WIDTH_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_RX_GENCTRL2, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL2);
+ val |= PMA_MPLLA_CTRL2_MPLLA_DIV16P5_CLK_EN;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL2, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL2);
+ val |= PMA_MPLLA_CTRL2_MPLLA_DIV10_CLK_EN;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL2, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL2);
+ val &= ~PMA_MPLLA_CTRL2_MPLLA_DIV8_CLK_EN;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_MPLLA_CTRL2, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL1);
+ val |= PMA_TX_GENCTRL1_VBOOST_EN_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL1, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_25G_RX_EQ_CTRL0);
+ val = u16_replace_bits(val, 0x10, PMA_RX_EQ_CTRL0_CTLE_BOOST_0_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_RX_EQ_CTRL0, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_RX_CDR_CTRL1);
+ val |= PMA_RX_CDR_CTRL1_VCO_STEP_CTRL_0;
+ xpcs_phy_write_pma(dev, PMA_MP_16G_RX_CDR_CTRL1, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_RX_CDR_CTRL1);
+ val |= PMA_RX_CDR_CTRL1_VCO_TEMP_COMP_EN_0;
+ xpcs_phy_write_pma(dev, PMA_MP_16G_RX_CDR_CTRL1, val);
+
+ val = PMA_RX_MISC_CTRL0_RX0_MISC(0x12);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_RX_MISC_CTRL0, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_25G_RX_GENCTRL4);
+ val &= ~PMA_RX_GENCTRL4_RX_DFE_BYP_0;
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_RX_GENCTRL4, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_RX_CDR_CTRL1);
+ val = u16_replace_bits(val, 0x1, PMA_RX_CDR_CTRL1_VCO_FRQBAND_0_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_RX_CDR_CTRL1, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_25G_RX_IQ_CTRL0);
+ val = u16_replace_bits(val, 0x0, PMA_RX_IQ_CTRL0_RX0_DELTA_IQ_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_RX_IQ_CTRL0, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_16G_25G_RX_EQ_CTRL5);
+ val = u16_replace_bits(val, 0x3, PMA_RX_EQ_CTRL5_RX0_ADPT_MODE_MASK);
+ xpcs_phy_write_pma(dev, PMA_MP_16G_25G_RX_EQ_CTRL5, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL1);
+ val &= ~PMA_TX_GENCTRL1_TX_CLK_RDY_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL1, val);
+
+ /* 5 Assert soft reset */
+ val = xpcs_read(dev, MDIO_MMD_PCS, XPCS_PHY_REG(PCS_DIG_CTRL1));
+ val |= PCS_DIG_CTRL1_VR_RST;
+ xpcs_write(dev, MDIO_MMD_PCS, XPCS_PHY_REG(PCS_DIG_CTRL1), val);
+
+ /* 6 Poll for SRAM initialization done */
+ begin = get_timer(0);
+ do {
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_SRAM);
+ if (get_timer(begin) > 500) {
+ dev_err(dev, "Polling timeout, line: %d\n", __LINE__);
+ goto timeout;
+ }
+ mdelay(10);
+ } while (!(val & PMA_SRAM_INIT_DN));
+
+ /* 7 Assert SRAM external loading done */
+ /* Workaround */
+ // xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_SRAM, PMA_SRAM_EXT_LD_DN);
+ xpcs_phy_write(dev, XPCS_PHY_GLOBAL, XPCS_PHY_REG(GLOBAL_CTRL_EX_0), GLOBAL_CTRL_EX_0_PHY_SRAM_BYPASS);
+
+ /* 8 Poll for vendor-specific soft reset */
+ begin = get_timer(0);
+ do {
+ val = xpcs_read(dev, MDIO_MMD_PCS, XPCS_PHY_REG(PCS_DIG_CTRL1));
+ if (get_timer(begin) > 500) {
+ dev_err(dev, "Polling timeout, line: %d\n", __LINE__);
+ goto timeout;
+ }
+ mdelay(10);
+ } while (val & PCS_DIG_CTRL1_VR_RST);
+
+ /* 9 Turn receive to P0 state */
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_RX_GENCTRL1);
+ val &= ~PMA_RX_GENCTRL1_RX_RST_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_GENCTRL1, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_RX_POWER_STATE_CTRL);
+ val &= ~PMA_RX_POWER_STATE_CTRL_RX_DISABLE_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_POWER_STATE_CTRL, val);
+
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_RX_POWER_STATE_CTRL);
+ val &= ~PMA_RX_POWER_STATE_CTRL_RX0_PSTATE_MASK;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_POWER_STATE_CTRL, val);
+
+ /* 10 Enable receiver data output from PHY */
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_RX_GENCTRL0);
+ val |= PMA_RX_GENCTRL0_RX_DT_EN_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_GENCTRL0, val);
+
+ /* 11 Assert request of receive */
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_RX_GENCTRL2);
+ val |= PMA_RX_GENCTRL2_RX_REQ_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_RX_GENCTRL2, val);
+
+ /* 11.1 Poll for acknowledge */
+ begin = get_timer(0);
+ do {
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_RX_GENCTRL2);
+ if (get_timer(begin) > 500) {
+ dev_err(dev, "Polling timeout, line: %d\n", __LINE__);
+ goto timeout;
+ }
+ mdelay(10);
+ } while (val & PMA_RX_GENCTRL2_RX_REQ_0);
+
+ /* 12 Assert TX0 clock is active and stable */
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL1);
+ val |= PMA_TX_GENCTRL1_TX_CLK_RDY_0;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_TX_GENCTRL1, val);
+
+ /*
+ * 13.1 Configure XPCS to consider Loss-of-Signal indicated by the
+ * PHY while evaluating the receive link status
+ */
+ val = xpcs_read(dev, MDIO_MMD_PCS, XPCS_PHY_REG(PCS_DEBUG_CTRL));
+ val |= PCS_DEBUG_CTRL_SUPRESS_LOS_DET;
+ xpcs_write(dev, MDIO_MMD_PCS, XPCS_PHY_REG(PCS_DEBUG_CTRL), val);
+ /*
+ * 13.2 Configure XPCS to deassert "receiver data enable" on
+ * detecting of Loss-of-Signal
+ */
+ val = xpcs_read(dev, MDIO_MMD_PCS, XPCS_PHY_REG(PCS_DEBUG_CTRL));
+ val |= PCS_DEBUG_CTRL_RX_DT_EN_CTL;
+ xpcs_write(dev, MDIO_MMD_PCS, XPCS_PHY_REG(PCS_DEBUG_CTRL), val);
+
+ /* 14 Poll for DPLL lock status for Lane 0 */
+ begin = get_timer(0);
+ do {
+ val = xpcs_phy_read_pma(dev, PMA_RX_LSTS);
+ if (get_timer(begin) > 500) {
+ dev_err(dev, "Polling timeout, line: %d\n", __LINE__);
+ goto timeout;
+ }
+ mdelay(10);
+ } while (!(val & PMA_RX_LSTS_RX_VALID_0));
+
+ /* 15 Assert request of receive adaptation */
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_RX_EQ_CTRL4);
+ val |= PMA_RX_EQ_CTRL4_RX_AD_REQ;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_EQ_CTRL4, val);
+
+ /* 16 Poll for acknowledge */
+ begin = get_timer(0);
+ do {
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_MISC_STS);
+ if (get_timer(begin) > 500) {
+ dev_err(dev, "Polling timeout, line: %d\n", __LINE__);
+ goto timeout;
+ }
+ mdelay(10);
+ } while (!(val & PMA_MISC_STS_RX_ADPT_ACK));
+
+ /* 17 Deassert request of receive adaptation */
+ val = xpcs_phy_read_pma(dev, PMA_MP_12G_16G_25G_RX_EQ_CTRL4);
+ val &= ~PMA_RX_EQ_CTRL4_RX_AD_REQ;
+ xpcs_phy_write_pma(dev, PMA_MP_12G_16G_25G_RX_EQ_CTRL4, val);
+
+ /* 18 Set the value of Config_Reg to 0 for Clause 37 autonegotiation. */
+ val = xpcs_read(dev, MDIO_MMD_VEND2, XPCS_PHY_REG(MII_AN_CTRL));
+ val &= ~MII_AN_CTRL_TX_CONFIG;
+ xpcs_write(dev, MDIO_MMD_VEND2, XPCS_PHY_REG(MII_AN_CTRL), val);
+
+ /* 19 Select XGMII speed */
+ val = xpcs_read(dev, MDIO_MMD_VEND2, XPCS_PHY_REG(MII_CTRL));
+ val &= ~MII_CTRL_SS5;
+ val |= MII_CTRL_SS6 | MII_CTRL_SS13;
+ xpcs_write(dev, MDIO_MMD_VEND2, XPCS_PHY_REG(MII_CTRL), val);
+
+ val = xpcs_phy_usxgmii_init_seq_2(dev);
+ if (val)
+ return val;
+
+ return 0;
+
+timeout:
+ return -ETIMEDOUT;
+}
+
+u32 xpcs_phy_get_id(struct udevice *dev)
+{
+ int ret;
+ u32 id;
+
+ /* First, search C73 PCS using PCS MMD */
+ ret = xpcs_phy_read(dev, XPCS_PHY_GLOBAL, XPCS_PHY_REG(IDCODE_HI));
+ if (ret < 0)
+ return 0xffffffff;
+
+ id = ret << 16;
+
+ ret = xpcs_phy_read(dev, XPCS_PHY_GLOBAL, XPCS_PHY_REG(IDCODE_LO));
+ if (ret < 0)
+ return 0xffffffff;
+
+ /* If Device IDs are not all zeros or all ones,
+ * we found C73 AN-type device
+ */
+ if ((id | ret) && (id | ret) != 0xffffffff)
+ return id | ret;
+
+ return 0xffffffff;
+}
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index cbf5f605518..bea1dfed892 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -38,9 +38,13 @@
#include <linux/mii.h>
#include <asm/io.h>
#include <linux/dma-mapping.h>
-#include <asm/arch/clk.h>
#include <linux/errno.h>
+/* Without CLK, we rely on the arch definition */
+#if !defined(CONFIG_CLK)
+#include <asm/arch/clk.h>
+#endif
+
#include "macb.h"
DECLARE_GLOBAL_DATA_PTR;
@@ -768,18 +772,40 @@ static int macb_phy_init(struct udevice *dev, const char *name)
lpa);
} else {
/* if macb port is a fixed link */
- /* TODO : manage gigabit capable processors */
+ const char *human_readable_speed;
+
speed = macb->speed;
duplex = macb->duplex;
+ switch (speed) {
+ case 2:
+ human_readable_speed = "1000";
+ break;
+ case 1:
+ human_readable_speed = "100";
+ break;
+ case 0:
+ human_readable_speed = "10";
+ break;
+ default:
+ printf("%s: speed %d not supported\n", name, speed);
+ return -EINVAL;
+ }
printf("%s: link up, %sMbps %s-duplex\n",
name,
- speed ? "100" : "10",
+ human_readable_speed,
duplex ? "full" : "half");
}
ncfgr = macb_readl(macb, NCFGR);
ncfgr &= ~(MACB_BIT(SPD) | MACB_BIT(FD) | GEM_BIT(GBE));
- if (speed) {
+ if (speed == 2) {
+ if (!gem_is_gigabit_capable(macb)) {
+ printf("%s: is not gigabit Ethernet capable\n", name);
+ return -EINVAL;
+ }
+ ncfgr |= GEM_BIT(GBE);
+ ret = macb_linkspd_cb(dev, _1000BASET);
+ } else if (speed == 1) {
ncfgr |= MACB_BIT(SPD);
ret = macb_linkspd_cb(dev, _100BASET);
} else {
@@ -923,26 +949,39 @@ static int _macb_init(struct udevice *dev, const char *name)
/* Check the multi queue and initialize the queue for tx */
gmac_init_multi_queues(macb);
- /*
- * When the GMAC IP with GE feature, this bit is used to
- * select interface between RGMII and GMII.
- * When the GMAC IP without GE feature, this bit is used
- * to select interface between RMII and MII.
+ /* This driver uses the user I/O to select the PHY features,
+ * but some GEM instances come with a fixed configuration and
+ * no USERIO.
*/
- if (macb->phy_interface == PHY_INTERFACE_MODE_RGMII ||
- macb->phy_interface == PHY_INTERFACE_MODE_RGMII_ID ||
- macb->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID ||
- macb->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID)
- val = macb->config->usrio->rgmii;
- else if (macb->phy_interface == PHY_INTERFACE_MODE_RMII)
- val = macb->config->usrio->rmii;
- else if (macb->phy_interface == PHY_INTERFACE_MODE_MII)
- val = macb->config->usrio->mii;
+ if (gem_readl(macb, DCFG1) & GEM_BIT(USERIO)) {
+ /*
+ * When the GMAC IP with GE feature, this bit is used to
+ * select interface between RGMII and GMII.
+ * When he GMAC IP without GE feature, this bit is used
+ * to select interface between RMII and MII.
+ */
+ switch (macb->phy_interface) {
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ val = macb->config->usrio->rgmii;
+ break;
+ case PHY_INTERFACE_MODE_RMII:
+ val = macb->config->usrio->rmii;
+ break;
+ case PHY_INTERFACE_MODE_MII:
+ val = macb->config->usrio->mii;
+ break;
+ default:
+ break;
+ }
- if (macb->config->caps & MACB_CAPS_USRIO_HAS_CLKEN)
- val |= macb->config->usrio->clken;
+ if (macb->config->caps & MACB_CAPS_USRIO_HAS_CLKEN)
+ val |= macb->config->usrio->clken;
- gem_writel(macb, USRIO, val);
+ gem_writel(macb, USRIO, val);
+ }
if (macb->phy_interface == PHY_INTERFACE_MODE_SGMII) {
unsigned int ncfgr = macb_readl(macb, NCFGR);
@@ -1003,9 +1042,14 @@ static int _macb_write_hwaddr(struct macb_device *macb, unsigned char *enetaddr)
/* set hardware address */
hwaddr_bottom = enetaddr[0] | enetaddr[1] << 8 |
enetaddr[2] << 16 | enetaddr[3] << 24;
- macb_writel(macb, SA1B, hwaddr_bottom);
hwaddr_top = enetaddr[4] | enetaddr[5] << 8;
- macb_writel(macb, SA1T, hwaddr_top);
+ if (macb_is_gem(macb)) {
+ gem_writel(macb, SA1B, hwaddr_bottom);
+ gem_writel(macb, SA1T, hwaddr_top);
+ } else {
+ macb_writel(macb, SA1B, hwaddr_bottom);
+ macb_writel(macb, SA1T, hwaddr_top);
+ }
return 0;
}
@@ -1307,7 +1351,9 @@ static int macb_eth_of_to_plat(struct udevice *dev)
macb->phy_addr = PHY_MAX_ADDR + 1;
macb->duplex = fdtdec_get_bool(blob, fl_node, "full-duplex");
speed_fdt = fdtdec_get_int(blob, fl_node, "speed", 0);
- if (speed_fdt == 100) {
+ if (speed_fdt == 1000) {
+ macb->speed = 2;
+ } else if (speed_fdt == 100) {
macb->speed = 1;
} else if (speed_fdt == 10) {
macb->speed = 0;
diff --git a/drivers/net/macb.h b/drivers/net/macb.h
index 0eb90574618..002d5bd31b2 100644
--- a/drivers/net/macb.h
+++ b/drivers/net/macb.h
@@ -443,6 +443,8 @@
#define MACB_REV_SIZE 16
/* Bitfields in DCFG1. */
+#define GEM_USERIO_OFFSET 9
+#define GEM_USERIO_SIZE 1
#define GEM_IRQCOR_OFFSET 23
#define GEM_IRQCOR_SIZE 1
#define GEM_DBWDEF_OFFSET 25
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 5d2277a4602..0025c895f12 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -7,7 +7,7 @@ config MV88E6352_SWITCH
menuconfig PHYLIB
bool "Ethernet PHY (physical media interface) support"
- depends on NET || NET_LWIP
+ depends on NET
help
Enable Ethernet PHY (physical media interface) support.
@@ -381,7 +381,7 @@ config PHY_FIXED
config PHY_NCSI
bool "NC-SI based PHY"
- depends on NET
+ depends on NET_LEGACY
endif #PHYLIB
diff --git a/drivers/net/phy/adin.c b/drivers/net/phy/adin.c
index ce448810ff6..4d42e56dada 100644
--- a/drivers/net/phy/adin.c
+++ b/drivers/net/phy/adin.c
@@ -10,6 +10,7 @@
#include <linux/bitops.h>
#include <linux/bitfield.h>
+#define PHY_ID_ADIN1200 0x0283bc20
#define PHY_ID_ADIN1300 0x0283bc30
#define ADIN1300_EXT_REG_PTR 0x10
#define ADIN1300_EXT_REG_DATA 0x11
@@ -263,6 +264,18 @@ static int adin1300_config(struct phy_device *phydev)
return genphy_config(phydev);
}
+U_BOOT_PHY_DRIVER(ADIN1200) = {
+ .name = "ADIN1200",
+ .uid = PHY_ID_ADIN1200,
+ .mask = 0xffffffff,
+ .features = PHY_BASIC_FEATURES,
+ .config = adin1300_config,
+ .startup = genphy_startup,
+ .shutdown = genphy_shutdown,
+ .readext = adin_extread,
+ .writeext = adin_extwrite,
+};
+
U_BOOT_PHY_DRIVER(ADIN1300) = {
.name = "ADIN1300",
.uid = PHY_ID_ADIN1300,
diff --git a/drivers/net/phy/airoha/Kconfig b/drivers/net/phy/airoha/Kconfig
index da8747939e3..4139df343ad 100644
--- a/drivers/net/phy/airoha/Kconfig
+++ b/drivers/net/phy/airoha/Kconfig
@@ -7,6 +7,7 @@ config PHY_AIROHA_EN8811
depends on PHY_AIROHA
depends on SUPPORTS_FW_LOADER
select FW_LOADER
+ select PHY_COMMON_PROPS
help
AIROHA EN8811H supported.
AIROHA AN8811HB supported.
diff --git a/drivers/net/phy/airoha/air_en8811.c b/drivers/net/phy/airoha/air_en8811.c
index 0b974472732..32f06dd6dfa 100644
--- a/drivers/net/phy/airoha/air_en8811.c
+++ b/drivers/net/phy/airoha/air_en8811.c
@@ -23,6 +23,7 @@
#include <linux/compat.h>
#include <dm/device_compat.h>
#include <u-boot/crc.h>
+#include <linux/phy/phy-common-props.h>
/* MII Registers */
#define AIR_AUX_CTRL_STATUS 0x1d
@@ -1046,11 +1047,50 @@ static int air_leds_init(struct phy_device *phydev, int num, u16 dur, int mode)
return 0;
}
-static int en8811h_config(struct phy_device *phydev)
+static int en8811h_config_serdes_polarity(struct phy_device *phydev)
{
- struct en8811h_priv *priv = phydev->priv;
ofnode node = phy_get_ofnode(phydev);
+ unsigned int pol, default_pol;
u32 pbus_value = 0;
+ int ret;
+
+ if (!ofnode_valid(node))
+ return 0;
+
+ default_pol = PHY_POL_NORMAL;
+ if (ofnode_read_bool(node, "airoha,pnswap-rx"))
+ default_pol = PHY_POL_INVERT;
+
+ ret = phy_get_rx_polarity(node,
+ phy_string_for_interface(phydev->interface),
+ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT),
+ default_pol, &pol);
+ if (ret)
+ return ret;
+ if (pol == PHY_POL_INVERT)
+ pbus_value |= EN8811H_POLARITY_RX_REVERSE;
+
+ default_pol = PHY_POL_NORMAL;
+ if (ofnode_read_bool(node, "airoha,pnswap-tx"))
+ default_pol = PHY_POL_INVERT;
+
+ ret = phy_get_tx_polarity(node,
+ phy_string_for_interface(phydev->interface),
+ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT),
+ default_pol, &pol);
+ if (ret)
+ return ret;
+ if (pol == PHY_POL_NORMAL)
+ pbus_value |= EN8811H_POLARITY_TX_NORMAL;
+
+ return air_buckpbus_reg_modify(phydev, EN8811H_POLARITY,
+ EN8811H_POLARITY_RX_REVERSE |
+ EN8811H_POLARITY_TX_NORMAL, pbus_value);
+}
+
+static int en8811h_config(struct phy_device *phydev)
+{
+ struct en8811h_priv *priv = phydev->priv;
int ret = 0;
/* If restart happened in .probe(), no need to restart now */
@@ -1081,20 +1121,8 @@ static int en8811h_config(struct phy_device *phydev)
if (ret < 0)
return ret;
- /* Serdes polarity */
- pbus_value = 0;
- if (ofnode_read_bool(node, "airoha,pnswap-rx"))
- pbus_value |= EN8811H_POLARITY_RX_REVERSE;
- else
- pbus_value &= ~EN8811H_POLARITY_RX_REVERSE;
- if (ofnode_read_bool(node, "airoha,pnswap-tx"))
- pbus_value &= ~EN8811H_POLARITY_TX_NORMAL;
- else
- pbus_value |= EN8811H_POLARITY_TX_NORMAL;
- ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY,
- EN8811H_POLARITY_RX_REVERSE |
- EN8811H_POLARITY_TX_NORMAL,
- pbus_value);
+ /* Configure Serdes polarity from device tree */
+ ret = en8811h_config_serdes_polarity(phydev);
if (ret < 0)
return ret;
diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c
index 10b87dfb8ab..83da3e1cc77 100644
--- a/drivers/net/phy/aquantia.c
+++ b/drivers/net/phy/aquantia.c
@@ -400,7 +400,7 @@ int aquantia_config(struct phy_device *phydev)
int interface = phydev->interface;
u32 val, id, rstatus, fault;
u32 reg_val1 = 0;
- int num_retries = 5;
+ int num_retries = 200;
int usx_an = 0;
/*
diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c
index 7ce03b59b6a..ebed61de133 100644
--- a/drivers/net/phy/dp83867.c
+++ b/drivers/net/phy/dp83867.c
@@ -203,32 +203,24 @@ static int dp83867_of_init(struct phy_device *phydev)
"Should be 'rgmii-id' to use internal delays\n");
}
- /* RX delay *must* be specified if internal delay of RX is used. */
+ dp83867->rx_id_delay = DP83867_RGMIIDCTL_2_00_NS;
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
ret = ofnode_read_u32(node, "ti,rx-internal-delay",
&dp83867->rx_id_delay);
- if (ret) {
- pr_debug("ti,rx-internal-delay must be specified\n");
- return ret;
- }
- if (dp83867->rx_id_delay > DP83867_RGMII_RX_CLK_DELAY_MAX) {
+ if (!ret && dp83867->rx_id_delay > DP83867_RGMII_RX_CLK_DELAY_MAX) {
pr_debug("ti,rx-internal-delay value of %u out of range\n",
dp83867->rx_id_delay);
return -EINVAL;
}
}
- /* TX delay *must* be specified if internal delay of RX is used. */
+ dp83867->tx_id_delay = DP83867_RGMIIDCTL_2_00_NS;
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
ret = ofnode_read_u32(node, "ti,tx-internal-delay",
&dp83867->tx_id_delay);
- if (ret) {
- debug("ti,tx-internal-delay must be specified\n");
- return ret;
- }
- if (dp83867->tx_id_delay > DP83867_RGMII_TX_CLK_DELAY_MAX) {
+ if (!ret && dp83867->tx_id_delay > DP83867_RGMII_TX_CLK_DELAY_MAX) {
pr_debug("ti,tx-internal-delay value of %u out of range\n",
dp83867->tx_id_delay);
return -EINVAL;
diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c
index a65e81dff0c..d96970949bc 100644
--- a/drivers/net/phy/mscc.c
+++ b/drivers/net/phy/mscc.c
@@ -23,6 +23,7 @@
#define PHY_ID_VSC8502 0x00070630
#define PHY_ID_VSC8540 0x00070760
#define PHY_ID_VSC8541 0x00070770
+#define PHY_ID_VSC8572 0x000704d0
#define PHY_ID_VSC8574 0x000704a0
#define PHY_ID_VSC8584 0x000707c0
@@ -1612,6 +1613,16 @@ U_BOOT_PHY_DRIVER(vsc8541) = {
.shutdown = &genphy_shutdown,
};
+U_BOOT_PHY_DRIVER(vsc8572) = {
+ .name = "Microsemi VSC8572",
+ .uid = PHY_ID_VSC8572,
+ .mask = 0x000ffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &vsc8574_config,
+ .startup = &mscc_startup,
+ .shutdown = &genphy_shutdown,
+};
+
U_BOOT_PHY_DRIVER(vsc8574) = {
.name = "Microsemi VSC8574",
.uid = PHY_ID_VSC8574,
diff --git a/drivers/net/sandbox.c b/drivers/net/sandbox.c
index 0ea50c484c0..e1daeb6c1e6 100644
--- a/drivers/net/sandbox.c
+++ b/drivers/net/sandbox.c
@@ -15,7 +15,7 @@
/*
* Structure definitions for network protocols. Since this file is used for
- * both NET and NET_LWIP, and given that the two network stacks do have
+ * both NET_LEGACY and NET_LWIP, and given that the two network stacks do have
* conflicting types (for instance struct icmp_hdr), it is on purpose that the
* structures are defined locally with minimal dependencies -- <asm/types.h> is
* included for the bit types and that's it.
diff --git a/drivers/net/ti/cpsw.c b/drivers/net/ti/cpsw.c
index d7746f454ba..7a7cb83bd98 100644
--- a/drivers/net/ti/cpsw.c
+++ b/drivers/net/ti/cpsw.c
@@ -33,6 +33,7 @@
#define PKT_MAX (1500 + 14 + 4 + 4)
#define CLEAR_BIT 1
#define GIGABITEN BIT(7)
+#define GMII_EN BIT(5)
#define FULLDUPLEXEN BIT(0)
#define MIIEN BIT(15)
#define CTL_EXT_EN BIT(18)
@@ -216,6 +217,10 @@ struct cpsw_priv {
u32 phy_mask;
};
+struct cpsw_driver_data {
+ void (*gmii_sel)(struct cpsw_priv *priv, phy_interface_t phy_mode);
+};
+
static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits)
{
int idx;
@@ -1064,9 +1069,17 @@ static void cpsw_gmii_sel_dra7xx(struct cpsw_priv *priv,
writel(reg, priv->data->gmii_sel);
}
-static void cpsw_phy_sel(struct cpsw_priv *priv, const char *compat,
- phy_interface_t phy_mode)
+static void cpsw_phy_sel(struct cpsw_priv *priv, phy_interface_t phy_mode)
{
+ const char *compat = priv->data->phy_sel_compat;
+ const struct cpsw_driver_data *drv_data =
+ (const struct cpsw_driver_data *)dev_get_driver_data(priv->dev);
+
+ if (drv_data && drv_data->gmii_sel) {
+ drv_data->gmii_sel(priv, phy_mode);
+ return;
+ }
+
if (!strcmp(compat, "ti,am3352-cpsw-phy-sel"))
cpsw_gmii_sel_am3352(priv, phy_mode);
if (!strcmp(compat, "ti,am43xx-cpsw-phy-sel"))
@@ -1084,8 +1097,7 @@ static int cpsw_eth_probe(struct udevice *dev)
priv->data = pdata->priv_pdata;
ti_cm_get_macid(dev, priv->data, pdata->enetaddr);
/* Select phy interface in control module */
- cpsw_phy_sel(priv, priv->data->phy_sel_compat,
- pdata->phy_interface);
+ cpsw_phy_sel(priv, pdata->phy_interface);
return _cpsw_register(priv);
}
@@ -1122,33 +1134,13 @@ static void cpsw_eth_of_parse_slave(struct cpsw_platform_data *data,
"max-speed", 0);
}
-static int cpsw_eth_of_to_plat(struct udevice *dev)
+static int cpsw_eth_of_to_plat_legacy(struct udevice *dev,
+ struct cpsw_platform_data *data)
{
- struct eth_pdata *pdata = dev_get_plat(dev);
- struct cpsw_platform_data *data;
- struct gpio_desc *mode_gpios;
int slave_index = 0;
- int num_mode_gpios;
ofnode subnode;
int ret;
- data = calloc(1, sizeof(struct cpsw_platform_data));
- if (!data)
- return -ENOMEM;
-
- pdata->priv_pdata = data;
- pdata->iobase = dev_read_addr(dev);
- data->version = CPSW_CTRL_VERSION_2;
- data->bd_ram_ofs = CPSW_BD_OFFSET;
- data->ale_reg_ofs = CPSW_ALE_OFFSET;
- data->cpdma_reg_ofs = CPSW_CPDMA_OFFSET;
- data->mdio_div = CPSW_MDIO_DIV;
- data->host_port_reg_ofs = CPSW_HOST_PORT_OFFSET,
-
- pdata->phy_interface = -1;
-
- data->cpsw_base = pdata->iobase;
-
ret = dev_read_s32(dev, "cpdma_channels", &data->channels);
if (ret) {
printf("error: cpdma_channels not found in dt\n");
@@ -1177,21 +1169,10 @@ static int cpsw_eth_of_to_plat(struct udevice *dev)
ret = dev_read_u32(dev, "mac_control", &data->mac_control);
if (ret) {
- printf("error: ale_entries not found in dt\n");
+ printf("error: mac_control not found in dt\n");
return ret;
}
- num_mode_gpios = gpio_get_list_count(dev, "mode-gpios");
- if (num_mode_gpios > 0) {
- mode_gpios = malloc(sizeof(struct gpio_desc) *
- num_mode_gpios);
- gpio_request_list_by_name(dev, "mode-gpios", mode_gpios,
- num_mode_gpios, GPIOD_IS_OUT);
- free(mode_gpios);
- }
-
- data->active_slave = dev_read_u32_default(dev, "active_slave", 0);
-
ofnode_for_each_subnode(subnode, dev_ofnode(dev)) {
const char *name;
@@ -1222,16 +1203,118 @@ static int cpsw_eth_of_to_plat(struct udevice *dev)
if (ofnode_read_bool(subnode, "rmii-clock-ext"))
data->rmii_clock_external = true;
+ }
+ }
- data->phy_sel_compat = ofnode_read_string(subnode,
- "compatible");
- if (!data->phy_sel_compat) {
- pr_err("Not able to get gmii_sel compatible\n");
- return -ENOENT;
- }
+ return 0;
+}
+
+static int cpsw_eth_of_to_plat_switch(struct udevice *dev,
+ struct cpsw_platform_data *data)
+{
+ ofnode eth_ports_node, subnode;
+ int ret;
+
+ data->channels = 8;
+ data->ale_entries = 1024;
+ data->mac_control = GMII_EN;
+
+ eth_ports_node = ofnode_find_subnode(dev_ofnode(dev), "ethernet-ports");
+ data->slaves = ofnode_get_child_count(eth_ports_node);
+ if (!data->slaves) {
+ pr_err("cpsw: No ethernet-ports defined\n");
+ return -EINVAL;
+ }
+
+ data->slave_data = malloc(sizeof(struct cpsw_slave_data) * data->slaves);
+ if (!data->slave_data)
+ return -ENOMEM;
+
+ ofnode_for_each_subnode(subnode, eth_ports_node) {
+ struct ofnode_phandle_args args;
+ u32 port_id;
+
+ ret = ofnode_read_u32(subnode, "reg", &port_id);
+ if (ret || !port_id || port_id > data->slaves) {
+ pr_err("cpsw: invalid or missing reg in port node\n");
+ return -EINVAL;
+ }
+
+ cpsw_eth_of_parse_slave(data, port_id - 1, subnode);
+
+ if (!data->gmii_sel) {
+ ret = ofnode_parse_phandle_with_args(subnode, "phys", "#phy-cells",
+ 0, 0, &args);
+ if (!ret)
+ data->gmii_sel = ofnode_get_addr(args.node);
+ }
+ }
+
+ if (!data->gmii_sel) {
+ pr_err("No port specified phys correctly\n");
+ return -ENOENT;
+ }
+
+ ofnode_for_each_subnode(subnode, dev_ofnode(dev)) {
+ const char *name = ofnode_get_name(subnode);
+
+ if (strncmp(name, "mdio", 4))
+ continue;
+
+ data->mdio_base = ofnode_get_addr(subnode);
+ if (data->mdio_base == FDT_ADDR_T_NONE) {
+ pr_err("Not able to get MDIO address space\n");
+ return -ENOENT;
}
}
+ return 0;
+}
+
+static int cpsw_eth_of_to_plat(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_plat(dev);
+ struct cpsw_platform_data *data;
+ struct gpio_desc *mode_gpios;
+ int num_mode_gpios;
+ int ret;
+ bool switch_dt_bindings =
+ ofnode_valid(ofnode_find_subnode(dev_ofnode(dev), "ethernet-ports"));
+
+ data = calloc(1, sizeof(struct cpsw_platform_data));
+ if (!data)
+ return -ENOMEM;
+
+ pdata->priv_pdata = data;
+ pdata->iobase = dev_read_addr(dev);
+ data->version = CPSW_CTRL_VERSION_2;
+ data->bd_ram_ofs = CPSW_BD_OFFSET;
+ data->ale_reg_ofs = CPSW_ALE_OFFSET;
+ data->cpdma_reg_ofs = CPSW_CPDMA_OFFSET;
+ data->mdio_div = CPSW_MDIO_DIV;
+ data->host_port_reg_ofs = CPSW_HOST_PORT_OFFSET;
+
+ pdata->phy_interface = -1;
+
+ data->cpsw_base = pdata->iobase;
+
+ num_mode_gpios = gpio_get_list_count(dev, "mode-gpios");
+ if (num_mode_gpios > 0) {
+ mode_gpios = malloc(sizeof(struct gpio_desc) * num_mode_gpios);
+ gpio_request_list_by_name(dev, "mode-gpios", mode_gpios,
+ num_mode_gpios, GPIOD_IS_OUT);
+ free(mode_gpios);
+ }
+
+ data->active_slave = dev_read_u32_default(dev, "active_slave", 0);
+
+ if (switch_dt_bindings)
+ ret = cpsw_eth_of_to_plat_switch(dev, data);
+ else
+ ret = cpsw_eth_of_to_plat_legacy(dev, data);
+ if (ret)
+ return ret;
+
data->slave_data[0].slave_reg_ofs = CPSW_SLAVE0_OFFSET;
data->slave_data[0].sliver_reg_ofs = CPSW_SLIVER0_OFFSET;
@@ -1253,9 +1336,22 @@ static int cpsw_eth_of_to_plat(struct udevice *dev)
return 0;
}
+static const struct cpsw_driver_data cpsw_data_am3352 = {
+ .gmii_sel = cpsw_gmii_sel_am3352,
+};
+
+static const struct cpsw_driver_data cpsw_data_dra7xx = {
+ .gmii_sel = cpsw_gmii_sel_dra7xx,
+};
+
static const struct udevice_id cpsw_eth_ids[] = {
- { .compatible = "ti,cpsw" },
- { .compatible = "ti,am335x-cpsw" },
+ { .compatible = "ti,cpsw", .data = (ulong)&cpsw_data_am3352 },
+ { .compatible = "ti,am335x-cpsw", .data = (ulong)&cpsw_data_am3352 },
+ { .compatible = "ti,am4372-cpsw", .data = (ulong)&cpsw_data_am3352 },
+ { .compatible = "ti,dra7-cpsw", .data = (ulong)&cpsw_data_dra7xx },
+ { .compatible = "ti,am335x-cpsw-switch", .data = (ulong)&cpsw_data_am3352 },
+ { .compatible = "ti,am4372-cpsw-switch", .data = (ulong)&cpsw_data_am3352 },
+ { .compatible = "ti,dra7-cpsw-switch", .data = (ulong)&cpsw_data_dra7xx },
{ }
};
#endif
diff --git a/drivers/net/ti/icssg_prueth.c b/drivers/net/ti/icssg_prueth.c
index 12a162b9d68..4796d0d67cd 100644
--- a/drivers/net/ti/icssg_prueth.c
+++ b/drivers/net/ti/icssg_prueth.c
@@ -496,14 +496,15 @@ static int prueth_port_probe(struct udevice *dev)
{
struct prueth_priv *priv = dev_get_priv(dev);
struct prueth *prueth;
- char portname[15];
+ char portname[64];
int ret;
priv->dev = dev;
prueth = dev_get_priv(dev->parent);
priv->prueth = prueth;
- sprintf(portname, "%s-%s", dev->parent->name, dev->name);
+ snprintf(portname, sizeof(portname), "%s-%s", dev->parent->name, dev->name);
+ portname[sizeof(portname) - 1] = '\0';
device_set_name(dev, portname);
diff --git a/drivers/net/xilinx_axi_emac.c b/drivers/net/xilinx_axi_emac.c
index e9cc5db52d2..1ea81fe1830 100644
--- a/drivers/net/xilinx_axi_emac.c
+++ b/drivers/net/xilinx_axi_emac.c
@@ -28,6 +28,10 @@
#define XAE_EMMC_LINKSPD_100 0x40000000 /* Link Speed mask for 100 Mbit */
#define XAE_EMMC_LINKSPD_1000 0x80000000 /* Link Speed mask for 1000 Mbit */
+/* Reset and Address Filter (RAF) Register bit definitions */
+#define XAE_RAF_MCSTREJ_MASK 0x00000002 /* Reject rx multicast dst addr */
+#define XAE_RAF_BCSTREJ_MASK 0x00000004 /* Reject rx broadcast dst addr */
+
/* Interrupt Status/Enable/Mask Registers bit definitions */
#define XAE_INT_RXRJECT_MASK 0x00000008 /* Rx frame rejected */
#define XAE_INT_MGTRDY_MASK 0x00000080 /* MGT clock Lock */
@@ -153,7 +157,8 @@ static struct axidma_bd tx_bd __attribute((aligned(DMAALIGN)));
static struct axidma_bd rx_bd __attribute((aligned(DMAALIGN)));
struct axi_regs {
- u32 reserved[3];
+ u32 raf; /* 0x0: Reset and Address Filter */
+ u32 reserved[2];
u32 is; /* 0xC: Interrupt status */
u32 reserved2;
u32 ie; /* 0x14: Interrupt enable */
@@ -528,6 +533,19 @@ static int axi_ethernet_init(struct axidma_priv *priv)
/* Set default MDIO divisor */
writel(XAE_MDIO_DIV_DFT | XAE_MDIO_MC_MDIOEN_MASK, &regs->mdio_mc);
+ /*
+ * Reject broadcast and multicast frames at MAC level to reduce
+ * unnecessary traffic processing. Multicast rejection is only
+ * enabled when IPv6 is not configured because IPv6 Neighbor
+ * Discovery and DHCPv6 rely on multicast.
+ */
+ if (!IS_ENABLED(CONFIG_IPV6))
+ writel(readl(&regs->raf) | XAE_RAF_MCSTREJ_MASK |
+ XAE_RAF_BCSTREJ_MASK, &regs->raf);
+ else
+ writel(readl(&regs->raf) | XAE_RAF_BCSTREJ_MASK,
+ &regs->raf);
+
debug("axiemac: InitHw done\n");
return 0;
}
diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c
index a50d5aee03f..f570ae9ee73 100644
--- a/drivers/net/zynq_gem.c
+++ b/drivers/net/zynq_gem.c
@@ -69,10 +69,13 @@
#define ZYNQ_GEM_NWCFG_SGMII_ENBL 0x08000000 /* SGMII Enable */
#define ZYNQ_GEM_NWCFG_PCS_SEL 0x00000800 /* PCS select */
+#define ZYNQ_GEM_DBUS_WIDTH_MASK (3 << 21) /* bits 22:21 */
#ifdef CONFIG_ARM64
# define ZYNQ_GEM_DBUS_WIDTH (1 << 21) /* 64 bit bus */
+# define ZYNQ_GEM_DBUS_WIDTH_128 (2 << 21) /* 128 bit bus */
#else
# define ZYNQ_GEM_DBUS_WIDTH (0 << 21) /* 32 bit bus */
+# define ZYNQ_GEM_DBUS_WIDTH_128 (0 << 21) /* 32 bit bus */
#endif
#define ZYNQ_GEM_NWCFG_INIT (ZYNQ_GEM_DBUS_WIDTH | \
@@ -134,6 +137,7 @@
#define ZYNQ_GEM_FREQUENCY_10 2500000UL
#define ZYNQ_GEM_FREQUENCY_100 25000000UL
#define ZYNQ_GEM_FREQUENCY_1000 125000000UL
+#define ZYNQ_GEM_FREQUENCY_10000 150000000UL
#define RXCLK_EN BIT(0)
@@ -470,28 +474,6 @@ static int zynq_gem_init(struct udevice *dev)
for (i = 0; i < STAT_SIZE; i++)
readl(&regs->stat[i]);
- /* Setup RxBD space */
- memset(priv->rx_bd, 0, RX_BUF * sizeof(struct emac_bd));
-
- for (i = 0; i < RX_BUF; i++) {
- priv->rx_bd[i].status = 0xF0000000;
- priv->rx_bd[i].addr =
- (lower_32_bits((ulong)(priv->rxbuffers)
- + (i * PKTSIZE_ALIGN)));
-#if defined(CONFIG_PHYS_64BIT)
- priv->rx_bd[i].addr_hi =
- (upper_32_bits((ulong)(priv->rxbuffers)
- + (i * PKTSIZE_ALIGN)));
-#endif
- }
- /* WRAP bit to last BD */
- priv->rx_bd[--i].addr |= ZYNQ_GEM_RXBUF_WRAP_MASK;
- /* Write RxBDs to IP */
- writel(lower_32_bits((ulong)priv->rx_bd), &regs->rxqbase);
-#if defined(CONFIG_PHYS_64BIT)
- writel(upper_32_bits((ulong)priv->rx_bd), &regs->upper_rxqbase);
-#endif
-
/* Setup for DMA Configuration register */
writel(ZYNQ_GEM_DMACR_INIT, &regs->dmacr);
@@ -520,6 +502,35 @@ static int zynq_gem_init(struct udevice *dev)
priv->init++;
}
+ /*
+ * Reinitialize RX BDs on every init. The 10GBE USX block asserts
+ * RX_SYNC_RESET during setup which resets the GEM RX DMA pointer
+ * back to rxqbase, so BDs and rxqbase must be refreshed each time
+ * to keep the hardware and driver ring indices in sync.
+ */
+ priv->rxbd_current = 0;
+ priv->rx_first_buf = 0;
+ memset(priv->rx_bd, 0, RX_BUF * sizeof(struct emac_bd));
+ for (i = 0; i < RX_BUF; i++) {
+ priv->rx_bd[i].status = 0xF0000000;
+ priv->rx_bd[i].addr =
+ (lower_32_bits((ulong)(priv->rxbuffers)
+ + (i * PKTSIZE_ALIGN)));
+#if defined(CONFIG_PHYS_64BIT)
+ priv->rx_bd[i].addr_hi =
+ (upper_32_bits((ulong)(priv->rxbuffers)
+ + (i * PKTSIZE_ALIGN)));
+#endif
+ }
+ /* WRAP bit to last BD */
+ priv->rx_bd[--i].addr |= ZYNQ_GEM_RXBUF_WRAP_MASK;
+
+ /* Write RxBDs to IP */
+ writel(lower_32_bits((ulong)priv->rx_bd), &regs->rxqbase);
+#if defined(CONFIG_PHYS_64BIT)
+ writel(upper_32_bits((ulong)priv->rx_bd), &regs->upper_rxqbase);
+#endif
+
ret = phy_startup(priv->phydev);
if (ret)
return ret;
@@ -532,6 +543,8 @@ static int zynq_gem_init(struct udevice *dev)
nwconfig = ZYNQ_GEM_NWCFG_INIT;
if (device_is_compatible(dev, "amd,versal2-10gbe")) {
+ nwconfig &= ~ZYNQ_GEM_DBUS_WIDTH_MASK;
+ nwconfig |= ZYNQ_GEM_DBUS_WIDTH_128;
if (priv->interface == PHY_INTERFACE_MODE_10GBASER) {
ctrl = readl(&regs->nwcfg);
ctrl |= PCSSEL;
@@ -602,6 +615,9 @@ static int zynq_gem_init(struct udevice *dev)
}
switch (priv->phydev->speed) {
+ case SPEED_10000:
+ clk_rate = ZYNQ_GEM_FREQUENCY_10000;
+ break;
case SPEED_1000:
nwconfig |= ZYNQ_GEM_NWCFG_SPEED1000;
clk_rate = ZYNQ_GEM_FREQUENCY_1000;
@@ -615,6 +631,7 @@ static int zynq_gem_init(struct udevice *dev)
break;
}
nwcfg = readl(&regs->nwcfg);
+ nwcfg &= ~(ZYNQ_GEM_NWCFG_SPEED100 | ZYNQ_GEM_NWCFG_SPEED1000);
nwcfg |= nwconfig;
if (nwcfg)
writel(nwcfg, &regs->nwcfg);
diff --git a/drivers/nvme/nvme-uclass.c b/drivers/nvme/nvme-uclass.c
index 44c88ad27f3..4ab9567450f 100644
--- a/drivers/nvme/nvme-uclass.c
+++ b/drivers/nvme/nvme-uclass.c
@@ -44,7 +44,7 @@ UCLASS_DRIVER(nvme) = {
.id = UCLASS_NVME,
};
-struct bootdev_ops nvme_bootdev_ops = {
+static const struct bootdev_ops nvme_bootdev_ops = {
};
static const struct udevice_id nvme_bootdev_ids[] = {
diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c
index 2b14437f69c..147a104149e 100644
--- a/drivers/nvme/nvme.c
+++ b/drivers/nvme/nvme.c
@@ -27,6 +27,23 @@
#define IO_TIMEOUT 30
#define MAX_PRP_POOL 512
+/**
+ * nvme_invalidate_cache_aligned() - invalidate cache with proper alignment
+ *
+ * Aligns cache invalidation to cacheline boundaries to ensure correct
+ * behavior even when the DMA buffer is not aligned to page boundaries.
+ *
+ * @addr: The start address of the buffer
+ * @length: The length of the buffer in bytes
+ */
+static inline void nvme_invalidate_cache_aligned(uintptr_t addr, int length)
+{
+ uintptr_t start_addr = addr & ~(ARCH_DMA_MINALIGN - 1);
+ uintptr_t end_addr = ALIGN(addr + length, ARCH_DMA_MINALIGN);
+
+ invalidate_dcache_range(start_addr, end_addr);
+}
+
static int nvme_wait_csts(struct nvme_dev *dev, u32 mask, u32 val)
{
int timeout;
@@ -94,7 +111,7 @@ static int nvme_setup_prps(struct nvme_dev *dev, u64 *prp2,
*(prp_pool + i) = cpu_to_le64((ulong)prp_pool +
page_size);
i = 0;
- prp_pool += page_size;
+ prp_pool = (u64 *)((uintptr_t)prp_pool + page_size);
}
*(prp_pool + i++) = cpu_to_le64(dma_addr);
dma_addr += page_size;
@@ -112,7 +129,10 @@ static __le16 nvme_get_cmd_id(void)
{
static unsigned short cmdid;
- return cpu_to_le16((cmdid < USHRT_MAX) ? cmdid++ : 0);
+ if (cmdid >= USHRT_MAX)
+ cmdid = 0;
+
+ return cpu_to_le16(cmdid++);
}
static u16 nvme_read_completion_status(struct nvme_queue *nvmeq, u16 index)
@@ -179,8 +199,10 @@ static int nvme_submit_sync_cmd(struct nvme_queue *nvmeq,
if ((status & 0x01) == phase)
break;
if (timeout_us > 0 && (timer_get_us() - start_time)
- >= timeout_us)
+ >= timeout_us) {
+ pr_warn("nvme: cmd %#x timed out\n", cmd->common.command_id);
return -ETIMEDOUT;
+ }
}
ops = (struct nvme_ops *)nvmeq->dev->udev->driver->ops;
@@ -278,11 +300,6 @@ static int nvme_delete_queue(struct nvme_dev *dev, u8 opcode, u16 id)
return nvme_submit_admin_cmd(dev, &c, NULL);
}
-static int nvme_delete_sq(struct nvme_dev *dev, u16 sqid)
-{
- return nvme_delete_queue(dev, nvme_admin_delete_sq, sqid);
-}
-
static int nvme_delete_cq(struct nvme_dev *dev, u16 cqid)
{
return nvme_delete_queue(dev, nvme_admin_delete_cq, cqid);
@@ -453,6 +470,7 @@ int nvme_identify(struct nvme_dev *dev, unsigned nsid,
u32 page_size = dev->page_size;
int offset = dma_addr & (page_size - 1);
int length = sizeof(struct nvme_id_ctrl);
+ dma_addr_t orig_dma_addr = dma_addr;
int ret;
memset(&c, 0, sizeof(c));
@@ -470,13 +488,13 @@ int nvme_identify(struct nvme_dev *dev, unsigned nsid,
c.identify.cns = cpu_to_le32(cns);
- invalidate_dcache_range(dma_addr,
- dma_addr + sizeof(struct nvme_id_ctrl));
+ nvme_invalidate_cache_aligned((uintptr_t)orig_dma_addr,
+ sizeof(struct nvme_id_ctrl));
ret = nvme_submit_admin_cmd(dev, &c, NULL);
if (!ret)
- invalidate_dcache_range(dma_addr,
- dma_addr + sizeof(struct nvme_id_ctrl));
+ nvme_invalidate_cache_aligned((uintptr_t)orig_dma_addr,
+ sizeof(struct nvme_id_ctrl));
return ret;
}
@@ -542,20 +560,19 @@ static int nvme_create_queue(struct nvme_queue *nvmeq, int qid)
nvmeq->cq_vector = qid - 1;
result = nvme_alloc_cq(dev, qid, nvmeq);
if (result < 0)
- goto release_cq;
+ goto release_ret;
result = nvme_alloc_sq(dev, qid, nvmeq);
if (result < 0)
- goto release_sq;
+ goto release_cq;
nvme_init_queue(nvmeq, qid);
return result;
- release_sq:
- nvme_delete_sq(dev, qid);
release_cq:
nvme_delete_cq(dev, qid);
+ release_ret:
return result;
}
@@ -865,14 +882,14 @@ int nvme_init(struct udevice *udev)
if (!ndev->prp_pool) {
ret = -ENOMEM;
printf("Error: %s: Out of memory!\n", udev->name);
- goto free_nvme;
+ goto free_queue;
}
ndev->prp_entry_num = MAX_PRP_POOL >> 3;
ret = nvme_setup_io_queues(ndev);
if (ret) {
log_debug("Unable to setup I/O queues(err=%dE)\n", ret);
- goto free_queue;
+ goto free_prp_pool;
}
nvme_get_info_from_identify(ndev);
@@ -882,7 +899,7 @@ int nvme_init(struct udevice *udev)
id = memalign(ndev->page_size, sizeof(struct nvme_id_ns));
if (!id) {
ret = -ENOMEM;
- goto free_queue;
+ goto free_prp_pool;
}
for (int i = 1; i <= ndev->nn; i++) {
@@ -927,6 +944,8 @@ int nvme_init(struct udevice *udev)
free_id:
free(id);
+free_prp_pool:
+ free((void *)ndev->prp_pool);
free_queue:
free((void *)ndev->queues);
free_nvme:
diff --git a/drivers/nvme/nvme_apple.c b/drivers/nvme/nvme_apple.c
index 7e7538553e3..e674eda8344 100644
--- a/drivers/nvme/nvme_apple.c
+++ b/drivers/nvme/nvme_apple.c
@@ -13,6 +13,7 @@
#include <asm/arch/rtkit.h>
#include <asm/arch/sart.h>
#include <linux/iopoll.h>
+#include <linux/sizes.h>
/* ASC registers */
#define REG_CPU_CTRL 0x0044
@@ -87,6 +88,9 @@ static int apple_nvme_setup_queue(struct nvme_queue *nvmeq)
}
priv->tcbs[nvmeq->qid] = (void *)memalign(4096, ANS_NVMMU_TCB_SIZE);
+ if (!priv->tcbs[nvmeq->qid])
+ return -ENOMEM;
+
memset((void *)priv->tcbs[nvmeq->qid], 0, ANS_NVMMU_TCB_SIZE);
switch (nvmeq->qid) {
@@ -287,6 +291,7 @@ static const struct nvme_ops apple_nvme_ops = {
};
static const struct udevice_id apple_nvme_ids[] = {
+ { .compatible = "apple,t8103-nvme-ans2" },
{ .compatible = "apple,nvme-ans2" },
{ /* sentinel */ }
};
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 8fc57895a78..39df0e776df 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -456,6 +456,17 @@ config PCIE_STARFIVE_JH7110
Say Y here if you want to enable PLDA XpressRich PCIe controller
support on StarFive JH7110 SoC.
+config PCIE_DW_AMD
+ bool "AMD Versal2 DW PCIe host controller"
+ depends on ARCH_VERSAL2
+ depends on DM_GPIO
+ select PCIE_DW_COMMON
+ select SYS_PCI_64BIT
+ help
+ Say Y here to enable support for the AMD Versal Gen 2 PCIe
+ host controller. This is a DesignWare-based PCIe controller
+ used in AMD Versal Gen 2 SoCs.
+
config PCIE_DW_IMX
bool "i.MX DW PCIe controller support"
depends on ARCH_IMX8M || ARCH_IMX9
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 98f3c226f63..e6d71fd172b 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -56,4 +56,5 @@ obj-$(CONFIG_PCIE_UNIPHIER) += pcie_uniphier.o
obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o
obj-$(CONFIG_PCIE_PLDA_COMMON) += pcie_plda_common.o
obj-$(CONFIG_PCIE_STARFIVE_JH7110) += pcie_starfive_jh7110.o
+obj-$(CONFIG_PCIE_DW_AMD) += pcie_dw_amd.o
obj-$(CONFIG_PCIE_DW_IMX) += pcie_dw_imx.o
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c
index 83b76d01f24..f58d542ef75 100644
--- a/drivers/pci/pci-uclass.c
+++ b/drivers/pci/pci-uclass.c
@@ -927,7 +927,7 @@ int pci_bind_bus_devices(struct udevice *bus)
if (PCI_FUNC(bdf) && !found_multi)
continue;
- if (only_one_child(bus) && (PCI_MASK_BUS(bdf) > 0))
+ if (only_one_child(bus) && (PCI_DEV(bdf) > 0))
continue;
/* Check only the first access, we don't expect problems */
diff --git a/drivers/pci/pcie_brcmstb.c b/drivers/pci/pcie_brcmstb.c
index f089c48f028..1b03b0a7b05 100644
--- a/drivers/pci/pcie_brcmstb.c
+++ b/drivers/pci/pcie_brcmstb.c
@@ -21,6 +21,7 @@
#include <linux/bitfield.h>
#include <linux/log2.h>
#include <linux/iopoll.h>
+#include <reset.h>
/* PCIe parameters */
#define BRCM_NUM_PCIE_OUT_WINS 4
@@ -49,6 +50,47 @@
#define SSC_STATUS_PLL_LOCK_MASK 0x800
#define SSC_STATUS_PLL_LOCK_SHIFT 11
+#define PCIE_RC_PL_PHY_CTL_15 0x184c
+#define PCIE_RC_PL_PHY_CTL_15_DIS_PLL_PD_MASK 0x400000
+#define PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK 0xff
+
+#define PCIE_MISC_UBUS_CTRL 0x40a4
+#define PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_ERR_DIS_MASK BIT(13)
+#define PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_DECERR_DIS_MASK BIT(19)
+#define PCIE_MISC_AXI_READ_ERROR_DATA 0x4170
+#define PCIE_MISC_UBUS_TIMEOUT 0x40A8
+#define PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT 0x405c
+#define PCIE_MISC_RC_BAR4_CONFIG_LO 0x40d4
+#define PCIE_MISC_RC_BAR4_CONFIG_HI 0x40d8
+#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_HI_MASK 0xff
+#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_HI 0x4110
+#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_ENABLE 0x1
+#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_LO_MASK 0xfffff000
+#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_LO 0x410c
+
+#define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP 0x40ac
+#define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP 0x40b4
+#define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP_ACCESS_ENABLE_MASK BIT(0)
+#define MISC_CTRL_PCIE_RCB_MPS_MODE_MASK 0x400
+
+enum {
+ RGR1_SW_INIT_1,
+ PCIE_HARD_DEBUG,
+};
+
+enum brcm_pcie_type {
+ BCM2711,
+ BCM2712
+};
+
+struct brcm_pcie;
+
+struct brcm_pcie_cfg_data {
+ const int *offsets;
+ const enum brcm_pcie_type type;
+ void (*perst_set)(struct brcm_pcie *pcie, u32 val);
+};
+
/**
* struct brcm_pcie - the PCIe controller state
* @base: Base address of memory mapped IO registers of the controller
@@ -61,6 +103,9 @@ struct brcm_pcie {
int gen;
bool ssc;
+ struct reset_ctl rescal;
+ struct reset_ctl bridge_reset;
+ const struct brcm_pcie_cfg_data *pcie_cfg;
};
/**
@@ -79,8 +124,8 @@ static int brcm_pcie_encode_ibar_size(u64 size)
if (log2_in >= 12 && log2_in <= 15)
/* Covers 4KB to 32KB (inclusive) */
return (log2_in - 12) + 0x1c;
- else if (log2_in >= 16 && log2_in <= 37)
- /* Covers 64KB to 32GB, (inclusive) */
+ else if (log2_in >= 16 && log2_in <= 36)
+ /* Covers 64KB to 64GB, (inclusive) */
return log2_in - 15;
/* Something is awry so disable */
@@ -104,6 +149,80 @@ static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie)
return (val & STATUS_PCIE_PORT_MASK) >> STATUS_PCIE_PORT_SHIFT;
}
+static void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val)
+{
+ if (val)
+ setbits_le32(pcie->base + pcie->pcie_cfg->offsets[RGR1_SW_INIT_1],
+ RGR1_SW_INIT_1_PERST_MASK);
+ else
+ clrbits_le32(pcie->base + pcie->pcie_cfg->offsets[RGR1_SW_INIT_1],
+ RGR1_SW_INIT_1_PERST_MASK);
+}
+
+static void brcm_pcie_perst_set_2712(struct brcm_pcie *pcie, u32 val)
+{
+ u32 tmp;
+
+ /* Perst bit has moved and assert value is 0 */
+ tmp = readl(pcie->base + PCIE_MISC_PCIE_CTRL);
+ u32p_replace_bits(&tmp, !val, RGR1_SW_INIT_1_PERSTB_MASK);
+ writel(tmp, pcie->base + PCIE_MISC_PCIE_CTRL);
+}
+
+static int brcm_pcie_get_resets_dt(struct udevice *dev)
+{
+ struct brcm_pcie *pcie = dev_get_priv(dev);
+ int ret;
+
+ ret = reset_get_by_name(dev, "rescal", &pcie->rescal);
+ if (ret) {
+ printf("Unable to get rescal reset\n");
+ return ret;
+ }
+
+ ret = reset_get_by_name(dev, "bridge", &pcie->bridge_reset);
+ if (ret)
+ printf("Unable to get bridge reset\n");
+
+ return ret;
+}
+
+static int brcm_pcie_do_reset(struct udevice *dev)
+{
+ struct brcm_pcie *pcie = dev_get_priv(dev);
+ int ret;
+
+ ret = reset_deassert(&pcie->rescal);
+ if (ret)
+ printf("failed to deassert 'rescal'\n");
+ return ret;
+}
+
+static int brcm_pcie_bridge_sw_init_set(struct brcm_pcie *pcie, u32 val)
+{
+ int ret = 0;
+
+ if (reset_valid(&pcie->bridge_reset))
+ {
+ if (val)
+ ret = reset_assert(&pcie->bridge_reset);
+ else
+ ret = reset_deassert(&pcie->bridge_reset);
+ if (ret)
+ log_err("failed to %sassert bridge reset, err=%d\n",
+ val ? "" : "de", ret);
+ return ret;
+ }
+
+ if (val)
+ setbits_le32(pcie->base + pcie->pcie_cfg->offsets[RGR1_SW_INIT_1],
+ RGR1_SW_INIT_1_INIT_MASK);
+ else
+ clrbits_le32(pcie->base + pcie->pcie_cfg->offsets[RGR1_SW_INIT_1],
+ RGR1_SW_INIT_1_INIT_MASK);
+ return 0;
+}
+
/**
* brcm_pcie_link_up() - Check whether the PCIe link is up
* @pcie: Pointer to the PCIe controller state
@@ -125,7 +244,7 @@ static int brcm_pcie_config_address(const struct udevice *dev, pci_dev_t bdf,
uint offset, void **paddress)
{
struct brcm_pcie *pcie = dev_get_priv(dev);
- unsigned int pci_bus = PCI_BUS(bdf);
+ unsigned int pci_bus = PCI_BUS(bdf) - dev_seq(dev);
unsigned int pci_dev = PCI_DEV(bdf);
unsigned int pci_func = PCI_FUNC(bdf);
int idx;
@@ -345,28 +464,150 @@ static void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie,
writel(tmp, base + PCIE_MEM_WIN0_LIMIT_HI(win));
}
+static u32 brcm_bar_reg_offset(int bar)
+{
+ if (bar <= 3)
+ return PCIE_MISC_RC_BAR1_CONFIG_LO + 8 * (bar - 1);
+ else
+ return PCIE_MISC_RC_BAR4_CONFIG_LO + 8 * (bar - 4);
+}
+
+static u32 brcm_ubus_reg_offset(int bar)
+{
+ if (bar <= 3)
+ return PCIE_MISC_UBUS_BAR1_CONFIG_REMAP + 8 * (bar - 1);
+ else
+ return PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_LO + 8 * (bar - 4);
+}
+
+/*
+ * Round size up to the next power of two, as required by
+ * brcm_pcie_encode_ibar_size(). If size is already a power of two
+ * fls64(size - 1) still gives the correct result because the hardware
+ * encodes the exponent, not the raw value.
+ */
+static u64 brcm_ibar_round_size(u64 size)
+{
+ return 1ULL << fls64(size - 1);
+}
+
+static void brcm_pcie_set_inbound_windows(struct udevice *dev)
+{
+ struct brcm_pcie *pcie = dev_get_priv(dev);
+ void __iomem *base = pcie->base;
+ bool is_2712 = (pcie->pcie_cfg->type == BCM2712);
+ int i, ibar_no, ret;
+ u32 tmp;
+
+ ibar_no = 0;
+ /* pre-2712 chips leave the first entry empty */
+ if (pcie->pcie_cfg->type != BCM2712)
+ ibar_no++;
+
+ /* program inbound windows from OF property "dma-regions" */
+ for (i = 0; i < 7; i++, ibar_no++) {
+ u64 bar_cpu, bar_size, bar_pci;
+ struct pci_region region;
+ int ubus_bar_offset, rc_bar_offset;
+
+ ret = pci_get_dma_regions(dev, &region, i);
+ if (ret) /* no region #i? Then we're done. */
+ break;
+ ubus_bar_offset = brcm_ubus_reg_offset(ibar_no + 1);
+ rc_bar_offset = brcm_bar_reg_offset(ibar_no + 1);
+
+ bar_pci = region.bus_start;
+ bar_cpu = region.phys_start;
+ bar_size = region.size;
+
+ if (is_2712) {
+ /* BCM2712: BAR holds raw PCI address; UBUS remap
+ * registers supply the CPU-side translation. */
+ tmp = lower_32_bits(bar_pci);
+ u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(bar_size),
+ RC_BAR2_CONFIG_LO_SIZE_MASK);
+ writel(tmp, base + rc_bar_offset);
+ writel(upper_32_bits(bar_pci), base + rc_bar_offset + 4);
+
+ tmp = lower_32_bits(bar_cpu) &
+ PCIE_MISC_UBUS_BAR_CONFIG_REMAP_LO_MASK;
+ tmp |= PCIE_MISC_UBUS_BAR_CONFIG_REMAP_ENABLE;
+ writel(tmp, base + ubus_bar_offset);
+
+ tmp = upper_32_bits(bar_cpu) &
+ PCIE_MISC_UBUS_BAR_CONFIG_REMAP_HI_MASK;
+ writel(tmp, base + ubus_bar_offset + 4);
+ } else {
+ /* Pre-BCM2712 (e.g. BCM2711 / RPi4): the BAR config
+ * register holds the offset (bus_start - phys_start),
+ * not the raw PCI address. The size must be rounded
+ * up to the next power of two before encoding. */
+ u64 bar_offset = bar_pci - bar_cpu;
+ u64 bar_size_po2 = brcm_ibar_round_size(bar_size);
+
+ tmp = lower_32_bits(bar_offset);
+ u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(bar_size_po2),
+ RC_BAR2_CONFIG_LO_SIZE_MASK);
+ writel(tmp, base + rc_bar_offset);
+ writel(upper_32_bits(bar_offset), base + rc_bar_offset + 4);
+ /* UBUS remap registers are not used on pre-2712 hardware. */
+ }
+ }
+}
+
+static void brcm_pcie_munge_pll(struct brcm_pcie *pcie)
+{
+ u32 tmp;
+ int ret, i;
+ u8 regs[] = { 0x16, 0x17, 0x18, 0x19, 0x1b, 0x1c, 0x1e };
+ u16 data[] = { 0x50b9, 0xbda1, 0x0094, 0x97b4, 0x5030, 0x5030, 0x0007 };
+
+ ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET,
+ 0x1600);
+ for (i = 0; i < ARRAY_SIZE(regs); i++) {
+ brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, regs[i], &tmp);
+ debug("PCIE MDIO pre_refclk 0x%02x = 0x%04x\n",
+ regs[i], tmp);
+ }
+ for (i = 0; i < ARRAY_SIZE(regs); i++) {
+ brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, regs[i], data[i]);
+ brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, regs[i], &tmp);
+ debug("PCIE MDIO post_refclk 0x%02x = 0x%04x\n",
+ regs[i], tmp);
+ }
+
+ udelay(200);
+}
+
static int brcm_pcie_probe(struct udevice *dev)
{
struct udevice *ctlr = pci_get_controller(dev);
struct pci_controller *hose = dev_get_uclass_priv(ctlr);
struct brcm_pcie *pcie = dev_get_priv(dev);
void __iomem *base = pcie->base;
- struct pci_region region;
bool ssc_good = false;
int num_out_wins = 0;
- u64 rc_bar2_offset, rc_bar2_size;
- unsigned int scb_size_val;
- int i, ret;
+ int i, ret = 0;
u16 nlw, cls, lnksta;
u32 tmp;
/*
+ * Ensure rescal reset for BCM2712 is really disabled.
+ */
+ if (pcie->pcie_cfg->type == BCM2712)
+ ret = brcm_pcie_do_reset(dev);
+ if (ret)
+ return ret;
+ /*
* Reset the bridge, assert the fundamental reset. Note for some SoCs,
* e.g. BCM7278, the fundamental reset should not be asserted here.
* This will need to be changed when support for other SoCs is added.
*/
- setbits_le32(base + PCIE_RGR1_SW_INIT_1,
- PCIE_RGR1_SW_INIT_1_INIT_MASK | PCIE_RGR1_SW_INIT_1_PERST_MASK);
+ ret = brcm_pcie_bridge_sw_init_set(pcie, 1);
+ if (ret)
+ return ret;
+ if (pcie->pcie_cfg->type != BCM2712)
+ pcie->pcie_cfg->perst_set(pcie, 1);
/*
* The delay is a safety precaution to preclude the reset signal
* from looking like a glitch.
@@ -374,39 +615,77 @@ static int brcm_pcie_probe(struct udevice *dev)
udelay(100);
/* Take the bridge out of reset */
- clrbits_le32(base + PCIE_RGR1_SW_INIT_1, PCIE_RGR1_SW_INIT_1_INIT_MASK);
-
- clrbits_le32(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG,
+ ret = brcm_pcie_bridge_sw_init_set(pcie, 0);
+ if (ret)
+ return ret;
+ clrbits_le32(base + pcie->pcie_cfg->offsets[PCIE_HARD_DEBUG],
PCIE_HARD_DEBUG_SERDES_IDDQ_MASK);
/* Wait for SerDes to be stable */
udelay(100);
+ if (pcie->pcie_cfg->type == BCM2712) {
+ /* Allow a 54MHz (xosc) refclk source */
+ brcm_pcie_munge_pll(pcie);
+ /* Fix for L1SS errata */
+ tmp = readl(base + PCIE_RC_PL_PHY_CTL_15);
+ tmp &= ~PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK;
+ /* PM clock period is 18.52ns (round down) */
+ tmp |= 0x12;
+ writel(tmp, base + PCIE_RC_PL_PHY_CTL_15);
+ }
+
+ tmp = (pcie->pcie_cfg->type == BCM2712) ?
+ MISC_CTRL_MAX_BURST_SIZE_128_2712 :
+ MISC_CTRL_MAX_BURST_SIZE_128;
/* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN */
clrsetbits_le32(base + PCIE_MISC_MISC_CTRL,
MISC_CTRL_MAX_BURST_SIZE_MASK,
MISC_CTRL_SCB_ACCESS_EN_MASK |
MISC_CTRL_CFG_READ_UR_MODE_MASK |
- MISC_CTRL_MAX_BURST_SIZE_128);
+ MISC_CTRL_PCIE_RCB_MPS_MODE_MASK |
+ tmp);
- pci_get_dma_regions(dev, &region, 0);
- rc_bar2_offset = region.bus_start - region.phys_start;
- rc_bar2_size = 1ULL << fls64(region.size - 1);
+ tmp = readl(base + PCIE_MISC_MISC_CTRL);
+ if (pcie->pcie_cfg->type == BCM2712) {
+ /* BCM2712: fixed 32GB SCB0 window */
+ u32p_replace_bits(&tmp, 20, MISC_CTRL_SCB0_SIZE_MASK);
+ } else {
+ /* Pre-BCM2712: size SCB0 to match the actual DMA region.
+ * rc_bar2_size must be a power of two; ilog2(size) - 15
+ * gives the hardware encoding (e.g. 1GB -> 15). */
+ struct pci_region region;
+ u64 rc_bar2_size;
- tmp = lower_32_bits(rc_bar2_offset);
- u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(rc_bar2_size),
- RC_BAR2_CONFIG_LO_SIZE_MASK);
- writel(tmp, base + PCIE_MISC_RC_BAR2_CONFIG_LO);
- writel(upper_32_bits(rc_bar2_offset),
- base + PCIE_MISC_RC_BAR2_CONFIG_HI);
+ pci_get_dma_regions(dev, &region, 0);
+ rc_bar2_size = brcm_ibar_round_size(region.size);
+ u32p_replace_bits(&tmp, rc_bar2_size ? ilog2(rc_bar2_size) - 15 : 0xf,
+ MISC_CTRL_SCB0_SIZE_MASK);
+ }
+ writel(tmp, base + PCIE_MISC_MISC_CTRL);
- scb_size_val = rc_bar2_size ?
- ilog2(rc_bar2_size) - 15 : 0xf; /* 0xf is 1GB */
+ if (pcie->pcie_cfg->type == BCM2712) {
+ /* Suppress AXI error responses and return 1s for read failures */
+ tmp = readl(base + PCIE_MISC_UBUS_CTRL);
+ u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_ERR_DIS_MASK);
+ u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_DECERR_DIS_MASK);
+ writel(tmp, base + PCIE_MISC_UBUS_CTRL);
+ writel(0xffffffff, base + PCIE_MISC_AXI_READ_ERROR_DATA);
- tmp = readl(base + PCIE_MISC_MISC_CTRL);
- u32p_replace_bits(&tmp, scb_size_val,
- MISC_CTRL_SCB0_SIZE_MASK);
- writel(tmp, base + PCIE_MISC_MISC_CTRL);
+ /*
+ * Adjust timeouts. The UBUS timeout also affects CRS
+ * completion retries, as the request will get terminated if
+ * either timeout expires, so both have to be a large value
+ * (in clocks of 750MHz).
+ * Set UBUS timeout to 250ms, then set RC config retry timeout
+ * to be ~240ms.
+ *
+ * Setting CRSVis=1 will stop the core from blocking on a CRS
+ * response, but does require the device to be well-behaved...
+ */
+ writel(0xB2D0000, base + PCIE_MISC_UBUS_TIMEOUT);
+ writel(0xABA0000, base + PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT);
+ }
/* Disable the PCIe->GISB memory window (RC_BAR1) */
clrbits_le32(base + PCIE_MISC_RC_BAR1_CONFIG_LO,
@@ -422,12 +701,13 @@ static int brcm_pcie_probe(struct udevice *dev)
/* Clear any interrupts we find on boot */
writel(0xffffffff, base + PCIE_MSI_INTR2_CLR);
+ brcm_pcie_set_inbound_windows(dev);
+
if (pcie->gen)
brcm_pcie_set_gen(pcie, pcie->gen);
/* Unassert the fundamental reset */
- clrbits_le32(pcie->base + PCIE_RGR1_SW_INIT_1,
- PCIE_RGR1_SW_INIT_1_PERST_MASK);
+ pcie->pcie_cfg->perst_set(pcie, 0);
/*
* Wait for 100ms after PERST# deassertion; see PCIe CEM specification
@@ -514,14 +794,25 @@ static int brcm_pcie_remove(struct udevice *dev)
void __iomem *base = pcie->base;
/* Assert fundamental reset */
- setbits_le32(base + PCIE_RGR1_SW_INIT_1, PCIE_RGR1_SW_INIT_1_PERST_MASK);
+ setbits_le32(base + pcie->pcie_cfg->offsets[RGR1_SW_INIT_1],
+ PCIE_RGR1_SW_INIT_1_PERST_MASK);
/* Turn off SerDes */
- setbits_le32(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG,
+ setbits_le32(base + pcie->pcie_cfg->offsets[PCIE_HARD_DEBUG],
PCIE_HARD_DEBUG_SERDES_IDDQ_MASK);
/* Shutdown bridge */
- setbits_le32(base + PCIE_RGR1_SW_INIT_1, PCIE_RGR1_SW_INIT_1_INIT_MASK);
+ brcm_pcie_bridge_sw_init_set(pcie, 1);
+
+ /*
+ * For the controllers that are utilizing reset for bridge Sw init,
+ * such as BCM2712, reset should be deasserted after assertion.
+ * Leaving it in asserted state may lead to unexpected hangs in
+ * the Linux Kernel driver because it do not perform reset initialization
+ * and start accessing device memory.
+ */
+ if (pcie->pcie_cfg->type == BCM2712)
+ brcm_pcie_bridge_sw_init_set(pcie, 0);
return 0;
}
@@ -546,6 +837,11 @@ static int brcm_pcie_of_to_plat(struct udevice *dev)
else
pcie->gen = max_link_speed;
+ pcie->pcie_cfg = (const struct brcm_pcie_cfg_data *)dev_get_driver_data(dev);
+
+ if (pcie->pcie_cfg->type == BCM2712)
+ return brcm_pcie_get_resets_dt(dev);
+
return 0;
}
@@ -554,8 +850,31 @@ static const struct dm_pci_ops brcm_pcie_ops = {
.write_config = brcm_pcie_write_config,
};
+static const int pcie_offsets[] = {
+ [RGR1_SW_INIT_1] = 0x9210,
+ [PCIE_HARD_DEBUG] = 0x4204,
+};
+
+static const struct brcm_pcie_cfg_data bcm2711_cfg = {
+ .offsets = pcie_offsets,
+ .type = BCM2711,
+ .perst_set = brcm_pcie_perst_set_generic,
+};
+
+static const int pcie_offsets_bcm2712[] = {
+ [RGR1_SW_INIT_1] = 0x0,
+ [PCIE_HARD_DEBUG] = 0x4304,
+};
+
+static const struct brcm_pcie_cfg_data bcm2712_cfg = {
+ .offsets = pcie_offsets_bcm2712,
+ .type = BCM2712,
+ .perst_set = brcm_pcie_perst_set_2712,
+};
+
static const struct udevice_id brcm_pcie_ids[] = {
- { .compatible = "brcm,bcm2711-pcie" },
+ { .compatible = "brcm,bcm2711-pcie", .data = (ulong)&bcm2711_cfg },
+ { .compatible = "brcm,bcm2712-pcie", .data = (ulong)&bcm2712_cfg },
{ }
};
diff --git a/drivers/pci/pcie_dw_amd.c b/drivers/pci/pcie_dw_amd.c
new file mode 100644
index 00000000000..81c6d8f2817
--- /dev/null
+++ b/drivers/pci/pcie_dw_amd.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Versal2 DesignWare PCIe host controller driver
+ *
+ * Copyright (C) 2025 - 2026, Advanced Micro Devices, Inc.
+ * Author: Pranav Sanwal <[email protected]>
+ */
+
+#include <dm.h>
+#include <log.h>
+#include <pci.h>
+#include <wait_bit.h>
+
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <dm/device_compat.h>
+#include <dm/ofnode.h>
+#include <linux/delay.h>
+
+#include "pcie_dw_common.h"
+
+/*
+ * SLCR (System Level Control Register) Interrupt Register Offsets
+ * These are relative to the SLCR base address from device tree
+ */
+#define AMD_DW_TLP_IR_STATUS_MISC 0x4c0
+#define AMD_DW_TLP_IR_DISABLE_MISC 0x4cc
+
+/* Interrupt bit definitions */
+#define AMD_DW_PCIE_INTR_CMPL_TIMEOUT 15
+#define AMD_DW_PCIE_INTR_PM_PME_RCVD 24
+#define AMD_DW_PCIE_INTR_PME_TO_ACK_RCVD 25
+#define AMD_DW_PCIE_INTR_MISC_CORRECTABLE 26
+#define AMD_DW_PCIE_INTR_NONFATAL 27
+#define AMD_DW_PCIE_INTR_FATAL 28
+
+#define AMD_DW_PCIE_INTR_INTX_MASK GENMASK(23, 16)
+
+#define AMD_DW_PCIE_IMR_ALL_MASK \
+ (BIT(AMD_DW_PCIE_INTR_CMPL_TIMEOUT) | \
+ BIT(AMD_DW_PCIE_INTR_PM_PME_RCVD) | \
+ BIT(AMD_DW_PCIE_INTR_PME_TO_ACK_RCVD) | \
+ BIT(AMD_DW_PCIE_INTR_MISC_CORRECTABLE) | \
+ BIT(AMD_DW_PCIE_INTR_NONFATAL) | \
+ BIT(AMD_DW_PCIE_INTR_FATAL) | \
+ AMD_DW_PCIE_INTR_INTX_MASK)
+
+/* DW PCIe Debug Registers (in DBI space) */
+#define AMD_DW_PCIE_PORT_DEBUG1 0x72c
+#define AMD_DW_PCIE_PORT_DEBUG1_LINK_UP BIT(4)
+#define AMD_DW_PCIE_PORT_DEBUG1_LINK_IN_TRAINING BIT(29)
+#define AMD_DW_PCIE_DBI_64BIT_MEM_DECODE BIT(0)
+
+/* Link training timeout */
+#define LINK_WAIT_MSLEEP_MAX 1000
+
+/* PCIe spec timing requirements */
+#define PCIE_RESET_CONFIG_WAIT_MS 100
+#define PCIE_T_PERST_WAIT_MS 1
+
+/**
+ * struct amd_dw_pcie - AMD DesignWare PCIe controller private data
+ * @dw: DesignWare PCIe common structure
+ * @slcr_base: System Level Control Register base (for interrupts)
+ */
+struct amd_dw_pcie {
+ struct pcie_dw dw;
+ void __iomem *slcr_base;
+};
+
+static void amd_dw_pcie_init_port(struct amd_dw_pcie *pcie)
+{
+ u32 val;
+
+ if (!pcie->slcr_base)
+ return;
+
+ /* Disable all TLP interrupts */
+ writel(AMD_DW_PCIE_IMR_ALL_MASK,
+ pcie->slcr_base + AMD_DW_TLP_IR_DISABLE_MISC);
+
+ /* Clear any pending TLP interrupts */
+ val = readl(pcie->slcr_base + AMD_DW_TLP_IR_STATUS_MISC);
+ val &= AMD_DW_PCIE_IMR_ALL_MASK;
+ writel(val, pcie->slcr_base + AMD_DW_TLP_IR_STATUS_MISC);
+}
+
+static void amd_dw_pcie_start_link(struct amd_dw_pcie *pcie)
+{
+ void __iomem *reg = pcie->dw.dbi_base + AMD_DW_PCIE_PORT_DEBUG1;
+ struct udevice *dev = pcie->dw.dev;
+ struct pcie_dw *pci = &pcie->dw;
+ int ret;
+
+ ret = wait_for_bit_le32(reg, AMD_DW_PCIE_PORT_DEBUG1_LINK_UP,
+ true, LINK_WAIT_MSLEEP_MAX,
+ false);
+ if (!ret)
+ ret = wait_for_bit_le32(reg,
+ AMD_DW_PCIE_PORT_DEBUG1_LINK_IN_TRAINING,
+ false, LINK_WAIT_MSLEEP_MAX, false);
+ if (ret)
+ dev_warn(dev, "PCIE-%d: Link down\n", dev_seq(dev));
+ else
+ dev_dbg(dev, "PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n",
+ dev_seq(dev), pcie_dw_get_link_speed(pci),
+ pcie_dw_get_link_width(pci), pci->first_busno);
+}
+
+static void amd_dw_pcie_host_init(struct amd_dw_pcie *pcie)
+{
+ struct pcie_dw *pci = &pcie->dw;
+
+ /*
+ * Set 64-bit prefetchable memory decode capability. U-Boot's pci_auto.c
+ * reads this bit before assigning prefetchable BARs. If cleared, it skips
+ * PCI_PREF_BASE_UPPER32 programming, causing 64-bit BAR assignment to fail.
+ */
+ dw_pcie_dbi_write_enable(pci, true);
+ setbits_le32(pci->dbi_base + PCI_PREF_MEMORY_BASE,
+ AMD_DW_PCIE_DBI_64BIT_MEM_DECODE);
+ dw_pcie_dbi_write_enable(pci, false);
+
+ amd_dw_pcie_init_port(pcie);
+ pcie_dw_setup_host(pci);
+}
+
+static void amd_dw_pcie_request_gpio(struct udevice *dev)
+{
+ struct gpio_desc perst_gpio;
+ ofnode child_node;
+ int ret;
+
+ /*
+ * PERST# reset GPIO is optional. Child PCI endpoint nodes may carry a
+ * 'reset-gpios' property to toggle the endpoint reset signal during
+ * initialization. If absent, the endpoint is assumed to be already
+ * released from reset.
+ */
+ ofnode_for_each_subnode(child_node, dev_ofnode(dev)) {
+ ret = gpio_request_by_name_nodev(child_node, "reset-gpios", 0,
+ &perst_gpio, GPIOD_IS_OUT);
+ if (!ret) {
+ dev_dbg(dev, "Found reset-gpios in child node %s\n",
+ ofnode_get_name(child_node));
+ dm_gpio_set_value(&perst_gpio, 1);
+ mdelay(PCIE_T_PERST_WAIT_MS);
+ dm_gpio_set_value(&perst_gpio, 0);
+ mdelay(PCIE_RESET_CONFIG_WAIT_MS);
+ dm_gpio_free(dev, &perst_gpio);
+ }
+ }
+}
+
+static int amd_dw_pcie_of_to_plat(struct udevice *dev)
+{
+ struct pci_region *io_region, *mem_region, *pref_region;
+ struct amd_dw_pcie *pcie = dev_get_priv(dev);
+ struct pcie_dw *pci = &pcie->dw;
+ int ret;
+
+ pci->dev = dev;
+
+ pci->dbi_base = dev_read_addr_name_ptr(dev, "dbi");
+ if (!pci->dbi_base) {
+ dev_err(dev, "Missing 'dbi' register region\n");
+ return -EINVAL;
+ }
+
+ pci->cfg_base = dev_read_addr_size_name_ptr(dev, "config", &pci->cfg_size);
+ if (!pci->cfg_base) {
+ dev_err(dev, "Missing 'config' register region\n");
+ return -EINVAL;
+ }
+
+ pci->atu_base = dev_read_addr_name_ptr(dev, "atu");
+ if (!pci->atu_base) {
+ dev_dbg(dev, "No 'atu' region, using default offset from DBI\n");
+ pci->atu_base = pci->dbi_base + DEFAULT_DBI_ATU_OFFSET;
+ }
+
+ pcie->slcr_base = dev_read_addr_name_ptr(dev, "slcr");
+ if (!pcie->slcr_base)
+ dev_dbg(dev, "No 'slcr' region, interrupt features disabled\n");
+
+ ret = pci_get_regions(dev, &io_region, &mem_region, &pref_region);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get PCI regions: %d\n", ret);
+ return ret;
+ }
+
+ if (mem_region)
+ pci->mem = *mem_region;
+
+ return 0;
+}
+
+static int amd_dw_pcie_probe(struct udevice *dev)
+{
+ struct amd_dw_pcie *pcie = dev_get_priv(dev);
+ struct pcie_dw *pci = &pcie->dw;
+
+ /* Set first bus number */
+ pci->first_busno = dev_seq(dev);
+
+ amd_dw_pcie_request_gpio(dev);
+ amd_dw_pcie_host_init(pcie);
+ amd_dw_pcie_start_link(pcie);
+
+ if (pci->mem.size) {
+ dev_dbg(dev, "Programming ATU region 0 for MEM: phys=0x%llx bus=0x%llx size=0x%llx\n",
+ (unsigned long long)pci->mem.phys_start,
+ (unsigned long long)pci->mem.bus_start,
+ (unsigned long long)pci->mem.size);
+ pcie_dw_prog_outbound_atu_unroll(pci,
+ PCIE_ATU_REGION_INDEX0,
+ PCIE_ATU_TYPE_MEM,
+ pci->mem.phys_start,
+ pci->mem.bus_start,
+ pci->mem.size);
+ } else {
+ dev_warn(dev, "No MEM region configured!\n");
+ }
+
+ dev_dbg(dev, "dbi: 0x%lx | config: 0x%lx | atu: 0x%lx | slcr: 0x%lx\n",
+ (long)pci->dbi_base, (long)pci->cfg_base,
+ (long)pci->atu_base, (long)pcie->slcr_base);
+
+ return 0;
+}
+
+static const struct dm_pci_ops amd_dw_pcie_ops = {
+ .read_config = pcie_dw_read_config,
+ .write_config = pcie_dw_write_config,
+};
+
+static const struct udevice_id amd_dw_pcie_ids[] = {
+ { .compatible = "amd,versal2-mdb-host" },
+ { }
+};
+
+U_BOOT_DRIVER(pcie_dw_amd) = {
+ .name = "pcie_dw_amd",
+ .id = UCLASS_PCI,
+ .of_match = amd_dw_pcie_ids,
+ .ops = &amd_dw_pcie_ops,
+ .of_to_plat = amd_dw_pcie_of_to_plat,
+ .probe = amd_dw_pcie_probe,
+ .priv_auto = sizeof(struct amd_dw_pcie),
+};
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 09810b62b51..5c8ec2b146f 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -292,6 +292,17 @@ config PHY_MTK_UFS
initialization, power on and power off flow of
specified M-PHYs.
+config PHY_MTK_XSPHY
+ bool "MediaTek XS-PHY Driver"
+ depends on PHY
+ depends on ARCH_MEDIATEK
+ select REGMAP
+ select SYSCON
+ help
+ Enable this to support the SuperSpeedPlus XS-PHY transceiver for
+ USB3.1 GEN2 controllers on MediaTek chips. The driver supports
+ multiple USB2.0, USB3.1 GEN2 ports.
+
config PHY_NPCM_USB
bool "Nuvoton NPCM USB PHY support"
depends on PHY
@@ -339,4 +350,12 @@ source "drivers/phy/qcom/Kconfig"
source "drivers/phy/renesas/Kconfig"
source "drivers/phy/starfive/Kconfig"
+config PHY_COMMON_PROPS
+ bool "Common PHY properties support"
+ help
+ Enable support for common PHY properties defined in the device tree,
+ such as rx-polarity and tx-polarity. This provides helpers for PHY
+ drivers to read polarity and other standard PHY properties from the
+ device tree node.
+
endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 83102349669..e46c362878d 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -38,10 +38,12 @@ obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o
obj-$(CONFIG_PHY_EXYNOS_USBDRD) += phy-exynos-usbdrd.o
obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o
obj-$(CONFIG_PHY_MTK_UFS) += phy-mtk-ufs.o
+obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o
obj-$(CONFIG_PHY_NPCM_USB) += phy-npcm-usb.o
obj-$(CONFIG_$(PHASE_)PHY_IMX8MQ_USB) += phy-imx8mq-usb.o
obj-$(CONFIG_PHY_IMX8M_PCIE) += phy-imx8m-pcie.o
obj-$(CONFIG_PHY_XILINX_ZYNQMP) += phy-zynqmp.o
+obj-$(CONFIG_PHY_COMMON_PROPS) += phy-common-props.o
obj-y += cadence/
obj-y += ti/
obj-y += qcom/
diff --git a/drivers/phy/phy-common-props.c b/drivers/phy/phy-common-props.c
new file mode 100644
index 00000000000..0a0b4439430
--- /dev/null
+++ b/drivers/phy/phy-common-props.c
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * phy-common-props.c -- Common PHY properties
+ *
+ * Copyright 2025-2026 NXP
+ */
+#include <dm/ofnode.h>
+#include <log.h>
+#include <malloc.h>
+#include <linux/phy/phy-common-props.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+
+/**
+ * ofnode_count_u32_prop - Count number of u32 elements in a property
+ * @node: Device tree node
+ * @propname: Property name
+ *
+ * Return: number of u32 elements, or negative error code
+ */
+static int ofnode_count_u32_prop(ofnode node, const char *propname)
+{
+ int size;
+
+ size = ofnode_read_size(node, propname);
+ if (size < 0) {
+ pr_debug("%s: property '%s' not found (err=%d)\n",
+ __func__, propname, size);
+ return size;
+ }
+
+ pr_debug("%s: property '%s' has %zu bytes (%zu elements)\n",
+ __func__, propname, (size_t)size, (size_t)(size / sizeof(u32)));
+
+ return size / sizeof(u32);
+}
+
+/**
+ * ofnode_get_u32_prop_for_name - Find u32 property by name, or default value
+ * @node: Device tree node; if invalid or @props_title is absent, @default_val is used
+ * @name: Property name used as lookup key in @names_title (must not be NULL)
+ * @props_title: Name of u32 array property holding values
+ * @names_title: Name of string array property holding lookup keys
+ * @default_val: Default value if @node is invalid, @props_title is absent, or empty
+ * @val: Pointer to store the returned value
+ *
+ * This function retrieves a u32 value from @props_title based on a name lookup
+ * in @names_title. The value stored in @val is determined as follows:
+ *
+ * - If @node is invalid or @props_title is absent: @default_val is used
+ * - If @props_title exists but is empty: @default_val is used
+ * - If @props_title has exactly one element and @names_title is empty:
+ * that element is used
+ * - Otherwise: @val is set to the element at the same index where @name is
+ * found in @names_title.
+ * - If @name is not found, the function looks for a "default" entry in
+ * @names_title and uses the corresponding value from @props_title
+ *
+ * When both @props_title and @names_title are present, they must have the
+ * same number of elements (except when @props_title has exactly one element).
+ *
+ * Return: zero on success, negative error on failure.
+ */
+static int ofnode_get_u32_prop_for_name(ofnode node, const char *name,
+ const char *props_title,
+ const char *names_title,
+ unsigned int default_val,
+ unsigned int *val)
+{
+ int err, n_props, n_names, idx;
+ u32 *props;
+
+ if (!name) {
+ pr_err("Error: Lookup key inside \"%s\" is mandatory\n",
+ names_title);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: looking up '%s' in props='%s' names='%s' default=%u\n",
+ __func__, name, props_title, names_title, default_val);
+
+ n_props = ofnode_count_u32_prop(node, props_title);
+ if (n_props < 0) {
+ /* property is absent */
+ pr_debug("%s: '%s' is absent, using default value %u\n",
+ __func__, props_title, default_val);
+ *val = default_val;
+ return 0;
+ }
+ if (n_props == 0) {
+ /* property exists but is empty, use default */
+ pr_debug("%s: '%s' is empty, using default value %u\n",
+ __func__, props_title, default_val);
+ *val = default_val;
+ return 0;
+ }
+
+ n_names = ofnode_read_string_count(node, names_title);
+
+ pr_debug("%s: '%s' has %d elements, '%s' has %d entries\n",
+ __func__, props_title, n_props, names_title, n_names);
+ if (n_names >= 0 && n_props != n_names) {
+ pr_err("Error: mismatch between \"%s\" and \"%s\" property count (%d vs %d)\n",
+ props_title, names_title, n_props, n_names);
+ return -EINVAL;
+ }
+
+ idx = ofnode_stringlist_search(node, names_title, name);
+ if (idx >= 0) {
+ pr_debug("%s: found '%s' at index %d in '%s'\n",
+ __func__, name, idx, names_title);
+ } else {
+ pr_debug("%s: '%s' not found in '%s', trying 'default'\n",
+ __func__, name, names_title);
+ idx = ofnode_stringlist_search(node, names_title, "default");
+ if (idx >= 0)
+ pr_debug("%s: 'default' entry found at index %d\n",
+ __func__, idx);
+ else
+ pr_debug("%s: 'default' entry not found in '%s'\n",
+ __func__, names_title);
+ }
+ /*
+ * If the mode name is missing, it can only mean the specified property
+ * is the default one for all modes, so reject any other property count
+ * than 1.
+ */
+ if (idx < 0 && n_props != 1) {
+ pr_err("Error: \"%s\" property has %d elements, but cannot find \"%s\" in \"%s\" and there is no default value\n",
+ props_title, n_props, name, names_title);
+ return -EINVAL;
+ }
+
+ if (n_props == 1) {
+ pr_debug("%s: single-element '%s', reading directly\n",
+ __func__, props_title);
+ err = ofnode_read_u32(node, props_title, val);
+ if (err) {
+ pr_debug("%s: failed to read '%s' (err=%d)\n",
+ __func__, props_title, err);
+ return err;
+ }
+ pr_debug("%s: resolved value %u for name '%s' from '%s'\n",
+ __func__, *val, name, props_title);
+ return 0;
+ }
+
+ /* We implicitly know idx >= 0 here */
+ props = calloc(n_props, sizeof(*props));
+ if (!props)
+ return -ENOMEM;
+
+ err = ofnode_read_u32_array(node, props_title, props, n_props);
+ if (err >= 0) {
+ *val = props[idx];
+ pr_debug("%s: resolved value %u at index %d for name '%s' from '%s'\n",
+ __func__, *val, idx, name, props_title);
+ } else {
+ pr_debug("%s: failed to read u32 array '%s' (err=%d)\n",
+ __func__, props_title, err);
+ }
+
+ free(props);
+
+ return err;
+}
+
+/**
+ * phy_get_polarity_for_mode - Get polarity for a specific PHY mode
+ * @node: Device tree node
+ * @mode_name: The name of the PHY mode to look up
+ * @supported: Bit mask of supported polarity values
+ * @default_val: Default polarity value if property is missing
+ * @polarity_prop: Name of the polarity property
+ * @names_prop: Name of the names property
+ * @val: Pointer to returned polarity
+ *
+ * Return: zero on success, negative error on failure.
+ */
+static int phy_get_polarity_for_mode(ofnode node, const char *mode_name,
+ unsigned int supported,
+ unsigned int default_val,
+ const char *polarity_prop,
+ const char *names_prop,
+ unsigned int *val)
+{
+ int err;
+
+ pr_debug("%s: querying '%s' for mode '%s' (supported=0x%x, default=%u)\n",
+ __func__, polarity_prop, mode_name, supported, default_val);
+
+ err = ofnode_get_u32_prop_for_name(node, mode_name, polarity_prop,
+ names_prop, default_val, val);
+ if (err) {
+ pr_debug("%s: '%s' lookup failed for mode '%s' (err=%d)\n",
+ __func__, polarity_prop, mode_name, err);
+ return err;
+ }
+
+ pr_debug("%s: '%s' for mode '%s' = %u\n",
+ __func__, polarity_prop, mode_name, *val);
+
+ if (!(supported & BIT(*val))) {
+ pr_err("Error: %d is not a supported value for '%s' element '%s'\n",
+ *val, polarity_prop, mode_name);
+ err = -EOPNOTSUPP;
+ }
+
+ return err;
+}
+
+/**
+ * phy_get_rx_polarity - Get RX polarity for PHY differential lane
+ * @node: Pointer to the PHY's device tree node.
+ * @mode_name: The name of the PHY mode to look up.
+ * @supported: Bit mask of PHY_POL_NORMAL, PHY_POL_INVERT and PHY_POL_AUTO
+ * @default_val: Default polarity value if property is missing
+ * @val: Pointer to returned polarity.
+ *
+ * Return: zero on success, negative error on failure.
+ */
+int phy_get_rx_polarity(ofnode node, const char *mode_name,
+ unsigned int supported, unsigned int default_val,
+ unsigned int *val)
+{
+ return phy_get_polarity_for_mode(node, mode_name, supported,
+ default_val, "rx-polarity",
+ "rx-polarity-names", val);
+}
+
+/**
+ * phy_get_tx_polarity - Get TX polarity for PHY differential lane
+ * @node: Pointer to the PHY's device tree node.
+ * @mode_name: The name of the PHY mode to look up.
+ * @supported: Bit mask of PHY_POL_NORMAL, PHY_POL_INVERT and PHY_POL_AUTO
+ * @default_val: Default polarity value if property is missing
+ * @val: Pointer to returned polarity.
+ *
+ * Return: zero on success, negative error on failure.
+ */
+int phy_get_tx_polarity(ofnode node, const char *mode_name,
+ unsigned int supported, unsigned int default_val,
+ unsigned int *val)
+{
+ return phy_get_polarity_for_mode(node, mode_name, supported,
+ default_val, "tx-polarity",
+ "tx-polarity-names", val);
+}
+
+/**
+ * phy_get_manual_rx_polarity - Get manual RX polarity for PHY differential lane
+ * @node: Pointer to the PHY's device tree node.
+ * @mode_name: The name of the PHY mode to look up.
+ * @val: Pointer to returned polarity.
+ *
+ * Helper for PHYs which do not support protocols with automatic RX polarity
+ * detection and correction.
+ *
+ * Return: zero on success, negative error on failure.
+ */
+int phy_get_manual_rx_polarity(ofnode node, const char *mode_name,
+ unsigned int *val)
+{
+ return phy_get_rx_polarity(node, mode_name,
+ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT),
+ PHY_POL_NORMAL, val);
+}
+
+/**
+ * phy_get_manual_tx_polarity - Get manual TX polarity for PHY differential lane
+ * @node: Pointer to the PHY's device tree node.
+ * @mode_name: The name of the PHY mode to look up.
+ * @val: Pointer to returned polarity.
+ *
+ * Helper for PHYs without any custom default value for the TX polarity.
+ *
+ * Return: zero on success, negative error on failure.
+ */
+int phy_get_manual_tx_polarity(ofnode node, const char *mode_name,
+ unsigned int *val)
+{
+ return phy_get_tx_polarity(node, mode_name,
+ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT),
+ PHY_POL_NORMAL, val);
+}
diff --git a/drivers/phy/phy-mtk-ufs.c b/drivers/phy/phy-mtk-ufs.c
index 1eda3df858d..c4c214dcde0 100644
--- a/drivers/phy/phy-mtk-ufs.c
+++ b/drivers/phy/phy-mtk-ufs.c
@@ -6,52 +6,41 @@
* Copyright (c) 2025, Igor Belwon <[email protected]>
*/
-#include "dm/ofnode.h"
-#include "dm/read.h"
-#include <clk.h>
-#include <dm.h>
-#include <generic-phy.h>
-#include <malloc.h>
-#include <mapmem.h>
-#include <regmap.h>
-#include <syscon.h>
#include <asm/io.h>
+#include <clk.h>
+#include <dm/device.h>
#include <dm/device_compat.h>
-#include <dm/devres.h>
-#include <linux/bitfield.h>
+#include <dm/read.h>
+#include <generic-phy.h>
#include <linux/bitops.h>
#include <linux/delay.h>
-
-#include <dt-bindings/phy/phy.h>
+#include <mapmem.h>
/* mphy register and offsets */
-#define MP_GLB_DIG_8C 0x008C
-#define FRC_PLL_ISO_EN BIT(8)
-#define PLL_ISO_EN BIT(9)
-#define FRC_FRC_PWR_ON BIT(10)
-#define PLL_PWR_ON BIT(11)
-
-#define MP_LN_DIG_RX_9C 0xA09C
-#define FSM_DIFZ_FRC BIT(18)
+#define MP_GLB_DIG_8C 0x008C
+#define FRC_PLL_ISO_EN BIT(8)
+#define PLL_ISO_EN BIT(9)
+#define FRC_FRC_PWR_ON BIT(10)
+#define PLL_PWR_ON BIT(11)
-#define MP_LN_DIG_RX_AC 0xA0AC
-#define FRC_RX_SQ_EN BIT(0)
-#define RX_SQ_EN BIT(1)
+#define MP_LN_DIG_RX_9C 0xA09C
+#define FSM_DIFZ_FRC BIT(18)
-#define MP_LN_RX_44 0xB044
-#define FRC_CDR_PWR_ON BIT(17)
-#define CDR_PWR_ON BIT(18)
-#define FRC_CDR_ISO_EN BIT(19)
-#define CDR_ISO_EN BIT(20)
+#define MP_LN_DIG_RX_AC 0xA0AC
+#define FRC_RX_SQ_EN BIT(0)
+#define RX_SQ_EN BIT(1)
-#define UFSPHY_CLKS_CNT 2
+#define MP_LN_RX_44 0xB044
+#define FRC_CDR_PWR_ON BIT(17)
+#define CDR_PWR_ON BIT(18)
+#define FRC_CDR_ISO_EN BIT(19)
+#define CDR_ISO_EN BIT(20)
struct mtk_ufs_phy {
struct udevice *dev;
void __iomem *mmio;
- struct clk *unipro_clk;
- struct clk *mp_clk;
+ struct clk_bulk clk_bulk;
};
static void ufs_mtk_phy_set_active(struct mtk_ufs_phy *phy)
@@ -88,16 +77,9 @@ static int mtk_phy_power_on(struct phy *phy)
struct mtk_ufs_phy *ufs_phy = dev_get_priv(phy->dev);
int ret;
- ret = clk_enable(ufs_phy->mp_clk);
- if (ret < 0) {
- dev_err(phy->dev, "failed to enable mp_clk\n");
- return ret;
- }
-
- ret = clk_enable(ufs_phy->unipro_clk);
- if (ret < 0) {
- dev_err(phy->dev, "failed to enable unipro_clk %d\n", ret);
- clk_disable(ufs_phy->unipro_clk);
+ ret = clk_enable_bulk(&ufs_phy->clk_bulk);
+ if (ret) {
+ dev_err(phy->dev, "failed to enable clocks (ret=%d)\n", ret);
return ret;
}
@@ -106,34 +88,44 @@ static int mtk_phy_power_on(struct phy *phy)
return 0;
}
-static int mtk_phy_power_off(struct phy *phy)
+static void ufs_mtk_phy_set_inactive(struct mtk_ufs_phy *phy)
{
- struct mtk_ufs_phy *ufs_phy = dev_get_priv(phy->dev);
-
/* Set PHY to Deep Hibernate mode */
- setbits_le32(ufs_phy->mmio + MP_LN_DIG_RX_9C, FSM_DIFZ_FRC);
+ setbits_le32(phy->mmio + MP_LN_DIG_RX_9C, FSM_DIFZ_FRC);
/* force DA_MP_RX0_SQ_EN */
- setbits_le32(ufs_phy->mmio + MP_LN_DIG_RX_AC, FRC_RX_SQ_EN);
- clrbits_le32(ufs_phy->mmio + MP_LN_DIG_RX_AC, RX_SQ_EN);
+ setbits_le32(phy->mmio + MP_LN_DIG_RX_AC, FRC_RX_SQ_EN);
+ clrbits_le32(phy->mmio + MP_LN_DIG_RX_AC, RX_SQ_EN);
/* force DA_MP_CDR_ISO_EN */
- setbits_le32(ufs_phy->mmio + MP_LN_RX_44, FRC_CDR_ISO_EN);
- setbits_le32(ufs_phy->mmio + MP_LN_RX_44, CDR_ISO_EN);
+ setbits_le32(phy->mmio + MP_LN_RX_44, FRC_CDR_ISO_EN);
+ setbits_le32(phy->mmio + MP_LN_RX_44, CDR_ISO_EN);
/* force DA_MP_CDR_PWR_ON */
- setbits_le32(ufs_phy->mmio + MP_LN_RX_44, FRC_CDR_PWR_ON);
- clrbits_le32(ufs_phy->mmio + MP_LN_RX_44, CDR_PWR_ON);
+ setbits_le32(phy->mmio + MP_LN_RX_44, FRC_CDR_PWR_ON);
+ clrbits_le32(phy->mmio + MP_LN_RX_44, CDR_PWR_ON);
/* force DA_MP_PLL_ISO_EN */
- setbits_le32(ufs_phy->mmio + MP_GLB_DIG_8C, FRC_PLL_ISO_EN);
- setbits_le32(ufs_phy->mmio + MP_GLB_DIG_8C, PLL_ISO_EN);
+ setbits_le32(phy->mmio + MP_GLB_DIG_8C, FRC_PLL_ISO_EN);
+ setbits_le32(phy->mmio + MP_GLB_DIG_8C, PLL_ISO_EN);
/* force DA_MP_PLL_PWR_ON */
- setbits_le32(ufs_phy->mmio + MP_GLB_DIG_8C, FRC_FRC_PWR_ON);
- clrbits_le32(ufs_phy->mmio + MP_GLB_DIG_8C, PLL_PWR_ON);
+ setbits_le32(phy->mmio + MP_GLB_DIG_8C, FRC_FRC_PWR_ON);
+ clrbits_le32(phy->mmio + MP_GLB_DIG_8C, PLL_PWR_ON);
+}
- return 0;
+static int mtk_phy_power_off(struct phy *phy)
+{
+ struct mtk_ufs_phy *ufs_phy = dev_get_priv(phy->dev);
+ int ret;
+
+ ufs_mtk_phy_set_inactive(ufs_phy);
+
+ ret = clk_disable_bulk(&ufs_phy->clk_bulk);
+ if (ret)
+ dev_err(phy->dev, "failed to disable clocks (ret=%d)\n", ret);
+
+ return ret;
}
static const struct phy_ops mtk_ufs_phy_ops = {
@@ -147,10 +139,6 @@ static int mtk_ufs_phy_probe(struct udevice *dev)
fdt_addr_t addr;
int ret;
- phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
- if (!phy)
- return -ENOMEM;
-
addr = dev_read_addr(dev);
if (addr == FDT_ADDR_T_NONE)
return -ENOMEM;
@@ -158,21 +146,11 @@ static int mtk_ufs_phy_probe(struct udevice *dev)
phy->dev = dev;
phy->mmio = map_sysmem(addr, 0);
- phy->mp_clk = devm_clk_get(dev, "mp");
- if (IS_ERR(phy->mp_clk)) {
- ret = PTR_ERR(phy->mp_clk);
- dev_err(dev, "Failed to get mp clock (ret=%d)\n", ret);
- return ret;
- }
+ ret = clk_get_bulk(dev, &phy->clk_bulk);
+ if (ret)
+ dev_err(dev, "Failed to get clocks (ret=%d)\n", ret);
- phy->unipro_clk = devm_clk_get(dev, "unipro");
- if (IS_ERR(phy->unipro_clk)) {
- ret = PTR_ERR(phy->unipro_clk);
- dev_err(dev, "Failed to get unipro clock (ret=%d)\n", ret);
- return ret;
- }
-
- return 0;
+ return ret;
}
static const struct udevice_id mtk_ufs_phy_id_table[] = {
diff --git a/drivers/phy/phy-mtk-xsphy.c b/drivers/phy/phy-mtk-xsphy.c
new file mode 100644
index 00000000000..d3418ffb101
--- /dev/null
+++ b/drivers/phy/phy-mtk-xsphy.c
@@ -0,0 +1,600 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MediaTek USB3.1 gen2 xsphy Driver
+ *
+ * Copyright (c) 2026 MediaTek Inc.
+ * Copyright (c) 2026 BayLibre, SAS
+ *
+ * Based on Linux mtk-xsphy driver:
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Chunfeng Yun <[email protected]>
+ *
+ * And U-Boot mtk-tphy driver:
+ * Copyright (c) 2015 - 2019 MediaTek Inc.
+ * Author: Chunfeng Yun <[email protected]>
+ * Ryder Lee <[email protected]>
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <generic-phy.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <regmap.h>
+#include <syscon.h>
+
+#include <asm/io.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+
+#include <dt-bindings/phy/phy.h>
+
+/* u2 phy banks */
+#define SSUSB_SIFSLV_MISC 0x000
+#define SSUSB_SIFSLV_U2FREQ 0x100
+#define SSUSB_SIFSLV_U2PHY_COM 0x300
+
+/* u3 phy shared banks */
+#define SSPXTP_SIFSLV_DIG_GLB 0x000
+#define SSPXTP_SIFSLV_PHYA_GLB 0x100
+
+/* u3 phy banks */
+#define SSPXTP_SIFSLV_DIG_LN_TOP 0x000
+#define SSPXTP_SIFSLV_DIG_LN_TX0 0x100
+#define SSPXTP_SIFSLV_DIG_LN_RX0 0x200
+#define SSPXTP_SIFSLV_DIG_LN_DAIF 0x300
+#define SSPXTP_SIFSLV_PHYA_LN 0x400
+
+#define XSP_U2FREQ_FMCR0 ((SSUSB_SIFSLV_U2FREQ) + 0x00)
+#define P2F_RG_FREQDET_EN BIT(24)
+#define P2F_RG_CYCLECNT GENMASK(23, 0)
+
+#define XSP_U2FREQ_MMONR0 ((SSUSB_SIFSLV_U2FREQ) + 0x0c)
+
+#define XSP_U2FREQ_FMMONR1 ((SSUSB_SIFSLV_U2FREQ) + 0x10)
+#define P2F_RG_FRCK_EN BIT(8)
+#define P2F_USB_FM_VALID BIT(0)
+
+#define XSP_USBPHYACR0 ((SSUSB_SIFSLV_U2PHY_COM) + 0x00)
+#define P2A0_RG_INTR_EN BIT(5)
+
+#define XSP_USBPHYACR1 ((SSUSB_SIFSLV_U2PHY_COM) + 0x04)
+#define P2A1_RG_INTR_CAL GENMASK(23, 19)
+#define P2A1_RG_VRT_SEL GENMASK(14, 12)
+#define P2A1_RG_TERM_SEL GENMASK(10, 8)
+
+#define XSP_USBPHYACR5 ((SSUSB_SIFSLV_U2PHY_COM) + 0x014)
+#define P2A5_RG_HSTX_SRCAL_EN BIT(15)
+#define P2A5_RG_HSTX_SRCTRL GENMASK(14, 12)
+
+#define XSP_USBPHYACR6 ((SSUSB_SIFSLV_U2PHY_COM) + 0x018)
+#define P2A6_RG_BC11_SW_EN BIT(23)
+#define P2A6_RG_OTG_VBUSCMP_EN BIT(20)
+
+#define XSP_U2PHYDTM1 ((SSUSB_SIFSLV_U2PHY_COM) + 0x06C)
+#define P2D_FORCE_IDDIG BIT(9)
+#define P2D_RG_VBUSVALID BIT(5)
+#define P2D_RG_SESSEND BIT(4)
+#define P2D_RG_AVALID BIT(2)
+#define P2D_RG_IDDIG BIT(1)
+
+#define SSPXTP_PHYA_GLB_00 ((SSPXTP_SIFSLV_PHYA_GLB) + 0x00)
+#define RG_XTP_GLB_BIAS_INTR_CTRL GENMASK(21, 16)
+
+#define SSPXTP_PHYA_LN_04 ((SSPXTP_SIFSLV_PHYA_LN) + 0x04)
+#define RG_XTP_LN0_TX_IMPSEL GENMASK(4, 0)
+
+#define SSPXTP_PHYA_LN_14 ((SSPXTP_SIFSLV_PHYA_LN) + 0x014)
+#define RG_XTP_LN0_RX_IMPSEL GENMASK(4, 0)
+
+#define XSP_REF_CLK_MHZ 26
+#define XSP_SLEW_RATE_COEF 17
+#define XSP_SR_COEF_DIVISOR 1000
+#define XSP_FM_DET_CYCLE_CNT 1024
+
+/* PHY switch between pcie/usb3/sgmii */
+#define USB_PHY_SWITCH_CTRL 0x0
+#define RG_PHY_SW_TYPE GENMASK(3, 0)
+#define RG_PHY_SW_PCIE 0x0
+#define RG_PHY_SW_USB3 0x1
+#define RG_PHY_SW_SGMII 0x2
+
+struct mtk_xsphy_instance {
+ void __iomem *port_base;
+ struct device_node *np;
+ struct clk ref_clk; /* reference clock of analog phy */
+ u32 index;
+ u32 type;
+ struct regmap *type_sw;
+ u32 type_sw_reg;
+ u32 type_sw_index;
+ /* only for HQA test */
+ u32 efuse_intr;
+ u32 efuse_tx_imp;
+ u32 efuse_rx_imp;
+ /* u2 eye diagram */
+ u32 eye_src;
+ u32 eye_vrt;
+ u32 eye_term;
+};
+
+struct mtk_xsphy {
+ struct udevice *dev;
+ void __iomem *sif_base;
+ struct mtk_xsphy_instance **phys;
+ u32 nphys;
+ u32 src_ref_clk_mhz; /* reference clock for slew rate calibrate */
+ u32 src_coef; /* coefficient for slew rate calibrate */
+};
+
+static void mtk_xsphy_u2_slew_rate_calibrate(struct mtk_xsphy *xsphy,
+ struct mtk_xsphy_instance *instance)
+{
+ void __iomem *pbase = instance->port_base;
+ u32 calib_val;
+ u32 fm_out;
+ u32 tmp;
+
+ /* use force value */
+ if (instance->eye_src)
+ return;
+
+ /* enable USB ring oscillator */
+ setbits_le32(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCAL_EN);
+ /* wait for clock to become stable */
+ udelay(1);
+
+ /* enable free run clock */
+ setbits_le32(pbase + XSP_U2FREQ_FMMONR1, P2F_RG_FRCK_EN);
+
+ /* set cycle count as 1024 */
+ clrsetbits_le32(pbase + XSP_U2FREQ_FMCR0, P2F_RG_CYCLECNT,
+ FIELD_PREP(P2F_RG_CYCLECNT, XSP_FM_DET_CYCLE_CNT));
+
+ /* enable frequency meter */
+ setbits_le32(pbase + XSP_U2FREQ_FMCR0, P2F_RG_FREQDET_EN);
+
+ /* ignore return value */
+ readl_poll_sleep_timeout(pbase + XSP_U2FREQ_FMMONR1, tmp,
+ (tmp & P2F_USB_FM_VALID), 10, 200);
+
+ fm_out = readl(pbase + XSP_U2FREQ_MMONR0);
+
+ /* disable frequency meter */
+ clrbits_le32(pbase + XSP_U2FREQ_FMCR0, P2F_RG_FREQDET_EN);
+
+ /* disable free run clock */
+ clrbits_le32(pbase + XSP_U2FREQ_FMMONR1, P2F_RG_FRCK_EN);
+
+ if (fm_out) {
+ /* (1024 / FM_OUT) x reference clock frequency x coefficient */
+ tmp = xsphy->src_ref_clk_mhz * xsphy->src_coef;
+ tmp = (tmp * XSP_FM_DET_CYCLE_CNT) / fm_out;
+ calib_val = DIV_ROUND_CLOSEST(tmp, XSP_SR_COEF_DIVISOR);
+ } else {
+ /* if FM detection fail, set default value */
+ calib_val = 3;
+ }
+ dev_dbg(xsphy->dev, "phy.%u, fm_out:%u, calib:%u (clk:%u, coef:%u)\n",
+ instance->index, fm_out, calib_val, xsphy->src_ref_clk_mhz,
+ xsphy->src_coef);
+
+ /* set HS slew rate */
+ clrsetbits_le32(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCTRL,
+ FIELD_PREP(P2A5_RG_HSTX_SRCTRL, calib_val));
+
+ /* disable USB ring oscillator */
+ clrbits_le32(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCAL_EN);
+}
+
+static void mtk_xsphy_u2_instance_init(struct mtk_xsphy *xsphy,
+ struct mtk_xsphy_instance *instance)
+{
+ void __iomem *pbase = instance->port_base;
+
+ /* DP/DM BC1.1 path Disable */
+ clrbits_le32(pbase + XSP_USBPHYACR6, P2A6_RG_BC11_SW_EN);
+
+ setbits_le32(pbase + XSP_USBPHYACR0, P2A0_RG_INTR_EN);
+}
+
+static void mtk_xsphy_u2_instance_power_on(struct mtk_xsphy *xsphy,
+ struct mtk_xsphy_instance *instance)
+{
+ void __iomem *pbase = instance->port_base;
+
+ setbits_le32(pbase + XSP_USBPHYACR6, P2A6_RG_OTG_VBUSCMP_EN);
+
+ clrsetbits_le32(pbase + XSP_U2PHYDTM1,
+ P2D_RG_VBUSVALID | P2D_RG_AVALID | P2D_RG_SESSEND,
+ P2D_RG_VBUSVALID | P2D_RG_AVALID);
+
+ dev_dbg(xsphy->dev, "%s(%u)\n", __func__, instance->index);
+}
+
+static void mtk_xsphy_u2_instance_power_off(struct mtk_xsphy *xsphy,
+ struct mtk_xsphy_instance *instance)
+{
+ void __iomem *pbase = instance->port_base;
+
+ clrbits_le32(pbase + XSP_USBPHYACR6, P2A6_RG_OTG_VBUSCMP_EN);
+
+ clrsetbits_le32(pbase + XSP_U2PHYDTM1,
+ P2D_RG_VBUSVALID | P2D_RG_AVALID | P2D_RG_SESSEND,
+ P2D_RG_SESSEND);
+
+ dev_dbg(xsphy->dev, "%s(%u)\n", __func__, instance->index);
+}
+
+static void mtk_xsphy_u2_instance_set_mode(struct mtk_xsphy *xsphy,
+ struct mtk_xsphy_instance *instance,
+ enum phy_mode mode)
+{
+ u32 tmp;
+
+ tmp = readl(instance->port_base + XSP_U2PHYDTM1);
+
+ switch (mode) {
+ case PHY_MODE_USB_DEVICE:
+ tmp |= P2D_FORCE_IDDIG | P2D_RG_IDDIG;
+ break;
+ case PHY_MODE_USB_HOST:
+ tmp |= P2D_FORCE_IDDIG;
+ tmp &= ~P2D_RG_IDDIG;
+ break;
+ case PHY_MODE_USB_OTG:
+ tmp &= ~(P2D_FORCE_IDDIG | P2D_RG_IDDIG);
+ break;
+ default:
+ return;
+ }
+
+ writel(tmp, instance->port_base + XSP_U2PHYDTM1);
+}
+
+static void mtk_xsphy_parse_property(struct mtk_xsphy *xsphy,
+ struct mtk_xsphy_instance *instance)
+{
+ ofnode node = np_to_ofnode(instance->np);
+
+ switch (instance->type) {
+ case PHY_TYPE_USB2:
+ ofnode_read_u32(node, "mediatek,efuse-intr", &instance->efuse_intr);
+ ofnode_read_u32(node, "mediatek,eye-src", &instance->eye_src);
+ ofnode_read_u32(node, "mediatek,eye-vrt", &instance->eye_vrt);
+ ofnode_read_u32(node, "mediatek,eye-term", &instance->eye_term);
+
+ dev_dbg(xsphy->dev, "intr:%u, src:%u, vrt:%u, term:%u\n",
+ instance->efuse_intr, instance->eye_src,
+ instance->eye_vrt, instance->eye_term);
+ return;
+ case PHY_TYPE_USB3:
+ ofnode_read_u32(node, "mediatek,efuse-intr", &instance->efuse_intr);
+ ofnode_read_u32(node, "mediatek,efuse-tx-imp", &instance->efuse_tx_imp);
+ ofnode_read_u32(node, "mediatek,efuse-rx-imp", &instance->efuse_rx_imp);
+
+ dev_dbg(xsphy->dev, "intr:%u, tx-imp:%u, rx-imp:%u\n",
+ instance->efuse_intr, instance->efuse_tx_imp,
+ instance->efuse_rx_imp);
+ return;
+ case PHY_TYPE_PCIE:
+ case PHY_TYPE_SGMII:
+ /* nothing to do */
+ return;
+ default:
+ dev_err(xsphy->dev, "incompatible PHY type\n");
+ return;
+ }
+}
+
+static void mtk_xsphy_u2_props_set(struct mtk_xsphy *xsphy,
+ struct mtk_xsphy_instance *instance)
+{
+ void __iomem *pbase = instance->port_base;
+
+ if (instance->efuse_intr)
+ clrsetbits_le32(pbase + XSP_USBPHYACR1, P2A1_RG_INTR_CAL,
+ FIELD_PREP(P2A1_RG_INTR_CAL, instance->efuse_intr));
+
+ if (instance->eye_src)
+ clrsetbits_le32(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCTRL,
+ FIELD_PREP(P2A5_RG_HSTX_SRCTRL, instance->eye_src));
+
+ if (instance->eye_vrt)
+ clrsetbits_le32(pbase + XSP_USBPHYACR1, P2A1_RG_VRT_SEL,
+ FIELD_PREP(P2A1_RG_VRT_SEL, instance->eye_vrt));
+
+ if (instance->eye_term)
+ clrsetbits_le32(pbase + XSP_USBPHYACR1, P2A1_RG_TERM_SEL,
+ FIELD_PREP(P2A1_RG_TERM_SEL, instance->eye_term));
+}
+
+static void mtk_xsphy_u3_props_set(struct mtk_xsphy *xsphy,
+ struct mtk_xsphy_instance *instance)
+{
+ void __iomem *pbase = instance->port_base;
+
+ if (instance->efuse_intr)
+ clrsetbits_le32(xsphy->sif_base + SSPXTP_PHYA_GLB_00,
+ RG_XTP_GLB_BIAS_INTR_CTRL,
+ FIELD_PREP(RG_XTP_GLB_BIAS_INTR_CTRL, instance->efuse_intr));
+
+ if (instance->efuse_tx_imp)
+ clrsetbits_le32(pbase + SSPXTP_PHYA_LN_04, RG_XTP_LN0_TX_IMPSEL,
+ FIELD_PREP(RG_XTP_LN0_TX_IMPSEL, instance->efuse_tx_imp));
+
+ if (instance->efuse_rx_imp)
+ clrsetbits_le32(pbase + SSPXTP_PHYA_LN_14, RG_XTP_LN0_RX_IMPSEL,
+ FIELD_PREP(RG_XTP_LN0_RX_IMPSEL, instance->efuse_rx_imp));
+}
+
+/* type switch for usb3/pcie/sgmii */
+static int mtk_xsphy_type_syscon_get(struct udevice *dev,
+ struct mtk_xsphy_instance *instance,
+ ofnode dn)
+{
+ struct ofnode_phandle_args args;
+ int ret;
+
+ if (!ofnode_read_bool(dn, "mediatek,syscon-type"))
+ return 0;
+
+ ret = ofnode_parse_phandle_with_args(dn, "mediatek,syscon-type",
+ NULL, 2, 0, &args);
+ if (ret)
+ return ret;
+
+ instance->type_sw_reg = args.args[0];
+ instance->type_sw_index = args.args[1] & 0x3; /* <=3 */
+ instance->type_sw = syscon_node_to_regmap(args.node);
+ if (IS_ERR(instance->type_sw))
+ return PTR_ERR(instance->type_sw);
+
+ dev_dbg(dev, "phy-%s.%d: type_sw - reg %#x, index %d\n",
+ dev->name, instance->index, instance->type_sw_reg,
+ instance->type_sw_index);
+
+ return 0;
+}
+
+static int mtk_xsphy_type_set(struct mtk_xsphy_instance *instance)
+{
+ int type;
+ u32 offset;
+
+ if (!instance->type_sw)
+ return 0;
+
+ switch (instance->type) {
+ case PHY_TYPE_USB3:
+ type = RG_PHY_SW_USB3;
+ break;
+ case PHY_TYPE_PCIE:
+ type = RG_PHY_SW_PCIE;
+ break;
+ case PHY_TYPE_SGMII:
+ type = RG_PHY_SW_SGMII;
+ break;
+ case PHY_TYPE_USB2:
+ default:
+ return 0;
+ }
+
+ offset = instance->type_sw_index * BITS_PER_BYTE;
+ regmap_update_bits(instance->type_sw, instance->type_sw_reg,
+ RG_PHY_SW_TYPE << offset, type << offset);
+
+ return 0;
+}
+
+static int mtk_xsphy_init(struct phy *phy)
+{
+ struct mtk_xsphy *xsphy = dev_get_priv(phy->dev);
+ struct mtk_xsphy_instance *instance = xsphy->phys[phy->id];
+ int ret;
+
+ ret = clk_enable(&instance->ref_clk);
+ if (ret) {
+ dev_err(xsphy->dev, "failed to enable ref_clk\n");
+ return ret;
+ }
+
+ switch (instance->type) {
+ case PHY_TYPE_USB2:
+ mtk_xsphy_u2_instance_init(xsphy, instance);
+ mtk_xsphy_u2_props_set(xsphy, instance);
+ break;
+ case PHY_TYPE_USB3:
+ mtk_xsphy_u3_props_set(xsphy, instance);
+ break;
+ case PHY_TYPE_PCIE:
+ case PHY_TYPE_SGMII:
+ /* nothing to do, only used to set type */
+ break;
+ default:
+ dev_err(xsphy->dev, "incompatible PHY type\n");
+ clk_disable(&instance->ref_clk);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mtk_xsphy_power_on(struct phy *phy)
+{
+ struct mtk_xsphy *xsphy = dev_get_priv(phy->dev);
+ struct mtk_xsphy_instance *instance = xsphy->phys[phy->id];
+
+ if (instance->type == PHY_TYPE_USB2) {
+ mtk_xsphy_u2_instance_power_on(xsphy, instance);
+ mtk_xsphy_u2_slew_rate_calibrate(xsphy, instance);
+ }
+
+ return 0;
+}
+
+static int mtk_xsphy_power_off(struct phy *phy)
+{
+ struct mtk_xsphy *xsphy = dev_get_priv(phy->dev);
+ struct mtk_xsphy_instance *instance = xsphy->phys[phy->id];
+
+ if (instance->type == PHY_TYPE_USB2)
+ mtk_xsphy_u2_instance_power_off(xsphy, instance);
+
+ return 0;
+}
+
+static int mtk_xsphy_exit(struct phy *phy)
+{
+ struct mtk_xsphy *xsphy = dev_get_priv(phy->dev);
+ struct mtk_xsphy_instance *instance = xsphy->phys[phy->id];
+
+ clk_disable(&instance->ref_clk);
+
+ return 0;
+}
+
+static int mtk_xsphy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+ struct mtk_xsphy *xsphy = dev_get_priv(phy->dev);
+ struct mtk_xsphy_instance *instance = xsphy->phys[phy->id];
+
+ if (instance->type == PHY_TYPE_USB2)
+ mtk_xsphy_u2_instance_set_mode(xsphy, instance, mode);
+
+ return 0;
+}
+
+static int mtk_xsphy_xlate(struct phy *phy, struct ofnode_phandle_args *args)
+{
+ struct mtk_xsphy *xsphy = dev_get_priv(phy->dev);
+ struct mtk_xsphy_instance *instance = NULL;
+ const struct device_node *phy_np = ofnode_to_np(args->node);
+ u32 index;
+
+ if (!phy_np) {
+ dev_err(phy->dev, "null pointer phy node\n");
+ return -EINVAL;
+ }
+
+ if (args->args_count != 2) {
+ dev_err(phy->dev, "invalid number of cells in 'phy' property\n");
+ return -EINVAL;
+ }
+
+ for (index = 0; index < xsphy->nphys; index++)
+ if (phy_np == xsphy->phys[index]->np) {
+ instance = xsphy->phys[index];
+ break;
+ }
+
+ if (!instance) {
+ dev_err(phy->dev, "failed to find appropriate phy\n");
+ return -EINVAL;
+ }
+
+ phy->id = index;
+ instance->type = args->args[1];
+ if (!(instance->type == PHY_TYPE_USB2 ||
+ instance->type == PHY_TYPE_USB3 ||
+ instance->type == PHY_TYPE_PCIE ||
+ instance->type == PHY_TYPE_SGMII)) {
+ dev_err(phy->dev, "unsupported PHY type\n");
+ return -EINVAL;
+ }
+
+ mtk_xsphy_parse_property(xsphy, instance);
+ mtk_xsphy_type_set(instance);
+
+ return 0;
+}
+
+static const struct phy_ops mtk_xsphy_ops = {
+ .init = mtk_xsphy_init,
+ .exit = mtk_xsphy_exit,
+ .power_on = mtk_xsphy_power_on,
+ .power_off = mtk_xsphy_power_off,
+ .set_mode = mtk_xsphy_set_mode,
+ .of_xlate = mtk_xsphy_xlate,
+};
+
+static int mtk_xsphy_probe(struct udevice *dev)
+{
+ struct mtk_xsphy *xsphy = dev_get_priv(dev);
+ fdt_addr_t sif_addr;
+ ofnode subnode;
+ int index = 0;
+
+ xsphy->nphys = dev_get_child_count(dev);
+
+ xsphy->phys = devm_kcalloc(dev, xsphy->nphys, sizeof(*xsphy->phys),
+ GFP_KERNEL);
+ if (!xsphy->phys)
+ return -ENOMEM;
+
+ xsphy->dev = dev;
+
+ sif_addr = ofnode_get_addr(dev_ofnode(dev));
+ /* optional, may not exist if no u3 phys */
+ if (sif_addr != FDT_ADDR_T_NONE)
+ xsphy->sif_base = map_sysmem(sif_addr, 0);
+
+ xsphy->src_ref_clk_mhz = XSP_REF_CLK_MHZ;
+ xsphy->src_coef = XSP_SLEW_RATE_COEF;
+ /* update parameters of slew rate calibrate if exist */
+ ofnode_read_u32(dev_ofnode(dev), "mediatek,src-ref-clk-mhz",
+ &xsphy->src_ref_clk_mhz);
+ ofnode_read_u32(dev_ofnode(dev), "mediatek,src-coef", &xsphy->src_coef);
+
+ dev_for_each_subnode(subnode, dev) {
+ struct mtk_xsphy_instance *inst;
+ fdt_addr_t addr;
+ int ret;
+
+ inst = devm_kzalloc(dev, sizeof(*inst), GFP_KERNEL);
+ if (!inst)
+ return -ENOMEM;
+
+ xsphy->phys[index] = inst;
+
+ addr = ofnode_get_addr(subnode);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EADDRNOTAVAIL;
+
+ inst->port_base = map_sysmem(addr, 0);
+ inst->index = index;
+ inst->np = ofnode_to_np(subnode);
+
+ ret = clk_get_by_name_nodev(subnode, "ref", &inst->ref_clk);
+ if (ret) {
+ dev_err(dev, "failed to get ref_clk(id-%d)\n", index);
+ return ret;
+ }
+
+ ret = mtk_xsphy_type_syscon_get(dev, inst, subnode);
+ if (ret)
+ return ret;
+
+ index++;
+ }
+
+ return 0;
+}
+
+static const struct udevice_id mtk_xsphy_id_table[] = {
+ { .compatible = "mediatek,xsphy" },
+ { }
+};
+
+U_BOOT_DRIVER(mtk_xsphy) = {
+ .name = "mtk-xsphy",
+ .id = UCLASS_PHY,
+ .of_match = mtk_xsphy_id_table,
+ .ops = &mtk_xsphy_ops,
+ .probe = mtk_xsphy_probe,
+ .priv_auto = sizeof(struct mtk_xsphy),
+};
diff --git a/drivers/phy/qcom/Kconfig b/drivers/phy/qcom/Kconfig
index 0dd69f7ffd0..49f830abf01 100644
--- a/drivers/phy/qcom/Kconfig
+++ b/drivers/phy/qcom/Kconfig
@@ -12,6 +12,14 @@ config PHY_QCOM_IPQ4019_USB
help
Support for the USB PHY-s on Qualcomm IPQ40xx SoC-s.
+config PHY_QCOM_QMP_COMBO
+ bool "Qualcomm QMP USB3-DP Combo PHY driver"
+ depends on PHY && ARCH_SNAPDRAGON
+ help
+ Enable this to support the USB3-DP Combo QMP PHY on various Qualcomm
+ chipsets. This driver supports the USB3 PHY functionality of the combo
+ PHY (USB3 + DisplayPort). Currently only USB3 mode is supported.
+
config PHY_QCOM_QMP_PCIE
tristate "Qualcomm QMP PCIe PHY driver"
depends on PHY && ARCH_SNAPDRAGON
diff --git a/drivers/phy/qcom/Makefile b/drivers/phy/qcom/Makefile
index 1c4e7d8d391..714013dc572 100644
--- a/drivers/phy/qcom/Makefile
+++ b/drivers/phy/qcom/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_PHY_QCOM_IPQ4019_USB) += phy-qcom-ipq4019-usb.o
obj-$(CONFIG_MSM8916_USB_PHY) += msm8916-usbh-phy.o
+obj-$(CONFIG_PHY_QCOM_QMP_COMBO) += phy-qcom-qmp-combo.o
obj-$(CONFIG_PHY_QCOM_QMP_PCIE) += phy-qcom-qmp-pcie.o
obj-$(CONFIG_PHY_QCOM_QMP_UFS) += phy-qcom-qmp-ufs.o
obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o
diff --git a/drivers/phy/qcom/phy-qcom-qmp-combo.c b/drivers/phy/qcom/phy-qcom-qmp-combo.c
new file mode 100644
index 00000000000..0d63e482ee4
--- /dev/null
+++ b/drivers/phy/qcom/phy-qcom-qmp-combo.c
@@ -0,0 +1,643 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <generic-phy.h>
+#include <reset.h>
+#include <power/regulator.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/compat.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/err.h>
+
+#include "phy-qcom-qmp-common.h"
+
+#include "phy-qcom-qmp.h"
+#include "phy-qcom-qmp-pcs-misc-v3.h"
+#include "phy-qcom-qmp-pcs-usb-v4.h"
+#include "phy-qcom-qmp-dp-com-v3.h"
+
+/* QPHY_V3_DP_COM_RESET_OVRD_CTRL register bits */
+/* DP PHY soft reset */
+#define SW_DPPHY_RESET BIT(0)
+/* mux to select DP PHY reset control, 0:HW control, 1: software reset */
+#define SW_DPPHY_RESET_MUX BIT(1)
+/* USB3 PHY soft reset */
+#define SW_USB3PHY_RESET BIT(2)
+/* mux to select USB3 PHY reset control, 0:HW control, 1: software reset */
+#define SW_USB3PHY_RESET_MUX BIT(3)
+
+/* QPHY_V3_DP_COM_PHY_MODE_CTRL register bits */
+#define USB3_MODE BIT(0) /* enables USB3 mode */
+#define DP_MODE BIT(1) /* enables DP mode */
+
+/* QPHY_V3_DP_COM_TYPEC_CTRL register bits */
+#define SW_PORTSELECT_MUX BIT(1)
+
+/* PHY slot identifiers for device tree phandle arguments */
+#define QMP_USB43DP_USB3_PHY 0
+#define QMP_USB43DP_DP_PHY 1
+
+#define PHY_INIT_COMPLETE_TIMEOUT 10000
+
+struct qmp_combo_offsets {
+ u16 com;
+ u16 txa;
+ u16 rxa;
+ u16 txb;
+ u16 rxb;
+ u16 usb3_serdes;
+ u16 usb3_pcs_misc;
+ u16 usb3_pcs;
+ u16 usb3_pcs_usb;
+};
+
+/*
+ * Initialisation tables
+ */
+
+static const struct qmp_combo_offsets qmp_combo_offsets_v3 = {
+ .com = 0x0000,
+ .txa = 0x1200,
+ .rxa = 0x1400,
+ .txb = 0x1600,
+ .rxb = 0x1800,
+ .usb3_serdes = 0x1000,
+ .usb3_pcs_misc = 0x1a00,
+ .usb3_pcs = 0x1c00,
+ .usb3_pcs_usb = 0x1f00,
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0xde),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_IPTRIM, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0xea),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xea),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xca),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11),
+};
+
+static const struct qmp_phy_init_tbl sm8250_usb3_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x60),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x60),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x40, 1),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x54, 2),
+};
+
+static const struct qmp_phy_init_tbl sm8250_usb3_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0xff, 1),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f, 2),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x7f, 1),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xff, 2),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x97),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x7b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb4),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10),
+};
+
+static const struct qmp_phy_init_tbl sm8250_usb3_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xa9),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10),
+};
+
+static const struct qmp_phy_init_tbl sm8250_usb3_pcs_usb_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07),
+};
+
+struct qmp_phy_cfg {
+ const struct qmp_combo_offsets *offsets;
+ const struct qmp_phy_init_tbl *serdes_tbl;
+ int serdes_tbl_num;
+ const struct qmp_phy_init_tbl *tx_tbl;
+ int tx_tbl_num;
+ const struct qmp_phy_init_tbl *rx_tbl;
+ int rx_tbl_num;
+ const struct qmp_phy_init_tbl *pcs_tbl;
+ int pcs_tbl_num;
+ const struct qmp_phy_init_tbl *pcs_usb_tbl;
+ int pcs_usb_tbl_num;
+ const char * const *vreg_list;
+ int num_vregs;
+ /* true, if PHY needs delay after POWER_DOWN */
+ bool has_pwrdn_delay;
+};
+
+/* list of clocks required by phy */
+static const char * const qmp_combo_phy_clk_l[] = {
+ "aux", "com_aux",
+};
+
+/* list of regulators */
+static const char * const qmp_phy_vreg_l[] = {
+ "vdda-phy-supply",
+ "vdda-pll-supply",
+};
+
+struct qmp_combo {
+ struct udevice *dev;
+ void __iomem *com;
+ void __iomem *serdes;
+ void __iomem *tx;
+ void __iomem *rx;
+ void __iomem *tx2;
+ void __iomem *rx2;
+ void __iomem *pcs;
+ void __iomem *pcs_usb;
+ void __iomem *pcs_misc;
+ struct clk *clks;
+ struct clk *pipe_clk;
+ int num_clks;
+ struct reset_ctl_bulk resets;
+ int num_resets;
+ struct udevice **vregs;
+ int num_vregs;
+ const struct qmp_phy_cfg *cfg;
+};
+
+static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
+{
+ u32 reg;
+
+ reg = readl(base + offset);
+ reg |= val;
+ writel(reg, base + offset);
+
+ /* ensure that above write is through */
+ readl(base + offset);
+}
+
+static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
+{
+ u32 reg;
+
+ reg = readl(base + offset);
+ reg &= ~val;
+ writel(reg, base + offset);
+
+ /* ensure that above write is through */
+ readl(base + offset);
+}
+
+static int qmp_combo_com_exit(struct qmp_combo *qmp)
+{
+ int i, ret;
+
+ for (i = 0; i < qmp->num_clks; i++)
+ clk_disable(&qmp->clks[i]);
+
+ reset_assert_bulk(&qmp->resets);
+
+ for (i = qmp->num_vregs - 1; i >= 0; i--) {
+ ret = regulator_set_enable(qmp->vregs[i], false);
+ if (ret)
+ dev_warn(qmp->dev, "failed to disable %s: %d\n",
+ qmp->cfg->vreg_list[i], ret);
+ }
+
+ return 0;
+}
+
+static int qmp_combo_com_init(struct qmp_combo *qmp)
+{
+ void __iomem *com = qmp->com;
+ void __iomem *pcs = qmp->pcs;
+ u32 val;
+ int ret, i;
+
+ ret = reset_assert_bulk(&qmp->resets);
+ if (ret) {
+ printf("Failed to assert resets: %d\n", ret);
+ return ret;
+ }
+
+ ret = reset_deassert_bulk(&qmp->resets);
+ if (ret) {
+ printf("Failed to deassert resets: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < qmp->num_vregs; i++) {
+ ret = regulator_set_enable(qmp->vregs[i], true);
+ if (ret) {
+ dev_err(qmp->dev, "Failed to enable regulator %d: %d\n", i, ret);
+ while (--i >= 0)
+ regulator_set_enable(qmp->vregs[i], false);
+ reset_assert_bulk(&qmp->resets);
+ return ret;
+ }
+ }
+
+ for (i = 0; i < qmp->num_clks; i++) {
+ ret = clk_enable(&qmp->clks[i]);
+ if (ret) {
+ printf("Failed to enable clock %d: %d\n", i, ret);
+ while (--i >= 0)
+ clk_disable(&qmp->clks[i]);
+ for (i = qmp->num_vregs - 1; i >= 0; i--)
+ regulator_set_enable(qmp->vregs[i], false);
+ reset_assert_bulk(&qmp->resets);
+ return ret;
+ }
+ }
+
+ /* Common block register writes */
+ qphy_setbits(com, QPHY_V3_DP_COM_POWER_DOWN_CTRL, SW_PWRDN);
+ qphy_setbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
+ SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
+ SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
+
+ val = SW_PORTSELECT_MUX;
+ writel(val, com + QPHY_V3_DP_COM_TYPEC_CTRL);
+
+ writel(USB3_MODE | DP_MODE, com + QPHY_V3_DP_COM_PHY_MODE_CTRL);
+
+ qphy_clrbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
+ SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
+ SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
+
+ qphy_clrbits(com, QPHY_V3_DP_COM_SWI_CTRL, 0x03);
+
+ qphy_clrbits(com, QPHY_V3_DP_COM_SW_RESET, SW_RESET);
+
+ qphy_setbits(pcs, QPHY_V4_PCS_POWER_DOWN_CONTROL, SW_PWRDN);
+
+ return 0;
+}
+
+static int qmp_combo_usb_power_on(struct qmp_combo *qmp)
+{
+ const struct qmp_phy_cfg *cfg = qmp->cfg;
+ void __iomem *serdes = qmp->serdes;
+ void __iomem *tx = qmp->tx;
+ void __iomem *rx = qmp->rx;
+ void __iomem *tx2 = qmp->tx2;
+ void __iomem *rx2 = qmp->rx2;
+ void __iomem *pcs = qmp->pcs;
+ void __iomem *pcs_usb = qmp->pcs_usb;
+ u32 val;
+ int ret;
+
+ /* Serdes configuration */
+ qmp_configure(qmp->dev, serdes, cfg->serdes_tbl, cfg->serdes_tbl_num);
+
+ ret = clk_prepare_enable(qmp->pipe_clk);
+ if (ret) {
+ dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret);
+ return ret;
+ }
+
+ /* Tx, Rx configurations */
+ qmp_configure_lane(qmp->dev, tx, cfg->tx_tbl, cfg->tx_tbl_num, 1);
+ qmp_configure_lane(qmp->dev, tx2, cfg->tx_tbl, cfg->tx_tbl_num, 2);
+
+ qmp_configure_lane(qmp->dev, rx, cfg->rx_tbl, cfg->rx_tbl_num, 1);
+ qmp_configure_lane(qmp->dev, rx2, cfg->rx_tbl, cfg->rx_tbl_num, 2);
+
+ /* PCS configuration */
+ qmp_configure(qmp->dev, pcs, cfg->pcs_tbl, cfg->pcs_tbl_num);
+
+ if (pcs_usb) {
+ qmp_configure(qmp->dev, pcs_usb,
+ cfg->pcs_usb_tbl,
+ cfg->pcs_usb_tbl_num);
+ }
+
+ if (cfg->has_pwrdn_delay)
+ udelay(20);
+
+ /* Pull PHY out of reset */
+ qphy_clrbits(pcs, QPHY_V4_PCS_SW_RESET, SW_RESET);
+
+ /* Start SerDes and Phy-Coding-Sublayer */
+ qphy_setbits(pcs, QPHY_V4_PCS_START_CONTROL,
+ SERDES_START | PCS_START);
+
+ /* Wait for PHY initialization */
+ ret = readl_poll_timeout(pcs + QPHY_V4_PCS_PCS_STATUS1, val,
+ !(val & PHYSTATUS), PHY_INIT_COMPLETE_TIMEOUT);
+
+ if (ret) {
+ printf("QMP USB3 PHY initialization timeout\n");
+ clk_disable(qmp->pipe_clk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int qmp_combo_power_on(struct phy *phy)
+{
+ struct qmp_combo *qmp = dev_get_priv(phy->dev);
+ int ret;
+
+ /* Initialize common block */
+ ret = qmp_combo_com_init(qmp);
+ if (ret)
+ return ret;
+
+ /* Initialize USB3-specific configuration */
+ ret = qmp_combo_usb_power_on(qmp);
+ if (ret) {
+ qmp_combo_com_exit(qmp);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int qmp_combo_power_off(struct phy *phy)
+{
+ struct qmp_combo *qmp = dev_get_priv(phy->dev);
+ void __iomem *com = qmp->com;
+
+ clk_disable(qmp->pipe_clk);
+
+ /* PHY reset */
+ qphy_setbits(qmp->pcs, QPHY_V4_PCS_SW_RESET, SW_RESET);
+
+ /* Stop SerDes and Phy-Coding-Sublayer */
+ qphy_clrbits(qmp->pcs, QPHY_V4_PCS_START_CONTROL,
+ SERDES_START | PCS_START);
+
+ /* Put PHY into POWER DOWN state: active low */
+ qphy_clrbits(qmp->pcs, QPHY_V4_PCS_POWER_DOWN_CONTROL, SW_PWRDN);
+
+ /* Power down common block */
+ qphy_clrbits(com, QPHY_V3_DP_COM_POWER_DOWN_CTRL, SW_PWRDN);
+
+ return qmp_combo_com_exit(qmp);
+}
+
+static int qmp_combo_reset_init(struct qmp_combo *qmp)
+{
+ struct udevice *dev = qmp->dev;
+ int ret;
+
+ ret = reset_get_bulk(dev, &qmp->resets);
+ if (ret) {
+ printf("Failed to get resets: %d\n", ret);
+ return ret;
+ }
+
+ qmp->num_resets = qmp->resets.count;
+
+ return 0;
+}
+
+static int qmp_combo_clk_init(struct qmp_combo *qmp)
+{
+ struct udevice *dev = qmp->dev;
+ int num = ARRAY_SIZE(qmp_combo_phy_clk_l);
+ int i, ret;
+
+ qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL);
+ if (!qmp->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < num; i++) {
+ ret = clk_get_by_name(dev, qmp_combo_phy_clk_l[i], &qmp->clks[i]);
+ if (ret) {
+ dev_err(dev, "failed to get %s clock: %d\n",
+ qmp_combo_phy_clk_l[i], ret);
+ return ret;
+ }
+ }
+
+ qmp->num_clks = num;
+ return 0;
+}
+
+static int qmp_combo_vreg_init(struct qmp_combo *qmp)
+{
+ const struct qmp_phy_cfg *cfg = qmp->cfg;
+ struct udevice *dev = qmp->dev;
+ int num = cfg->num_vregs;
+ int i, ret;
+
+ if (!num)
+ return 0;
+
+ qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL);
+ if (!qmp->vregs)
+ return -ENOMEM;
+
+ for (i = 0; i < num; i++) {
+ ret = device_get_supply_regulator(dev, cfg->vreg_list[i],
+ &qmp->vregs[i]);
+ if (ret) {
+ dev_err(dev, "failed to get regulator %s: %d\n",
+ cfg->vreg_list[i], ret);
+ return ret;
+ }
+ }
+
+ qmp->num_vregs = num;
+ return 0;
+}
+
+static int qmp_combo_parse_dt(struct qmp_combo *qmp)
+{
+ const struct qmp_phy_cfg *cfg = qmp->cfg;
+ const struct qmp_combo_offsets *offs = cfg->offsets;
+ struct udevice *dev = qmp->dev;
+ void __iomem *base;
+ int ret;
+
+ if (!offs)
+ return -EINVAL;
+
+ base = (void __iomem *)dev_read_addr(dev);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ qmp->com = base + offs->com;
+ qmp->serdes = base + offs->usb3_serdes;
+ qmp->tx = base + offs->txa;
+ qmp->rx = base + offs->rxa;
+ qmp->tx2 = base + offs->txb;
+ qmp->rx2 = base + offs->rxb;
+ qmp->pcs = base + offs->usb3_pcs;
+ qmp->pcs_usb = base + offs->usb3_pcs_usb;
+ qmp->pcs_misc = base + offs->usb3_pcs_misc;
+
+ ret = qmp_combo_clk_init(qmp);
+ if (ret)
+ return ret;
+
+ qmp->pipe_clk = devm_clk_get(dev, "usb3_pipe");
+ if (IS_ERR(qmp->pipe_clk)) {
+ dev_err(dev, "failed to get pipe clock (%ld)\n",
+ PTR_ERR(qmp->pipe_clk));
+ return ret;
+ }
+
+ ret = qmp_combo_reset_init(qmp);
+ if (ret)
+ return ret;
+
+ ret = qmp_combo_vreg_init(qmp);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int qmp_combo_probe(struct udevice *dev)
+{
+ struct qmp_combo *qmp = dev_get_priv(dev);
+ int ret;
+
+ qmp->dev = dev;
+ qmp->cfg = (const struct qmp_phy_cfg *)dev_get_driver_data(dev);
+ if (!qmp->cfg) {
+ printf("Failed to get PHY configuration\n");
+ return -EINVAL;
+ }
+
+ ret = qmp_combo_parse_dt(qmp);
+
+ return ret;
+}
+
+static const struct qmp_phy_cfg sc7280_usb3dpphy_cfg = {
+ .offsets = &qmp_combo_offsets_v3,
+ .serdes_tbl = sm8150_usb3_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl),
+ .tx_tbl = sm8250_usb3_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sm8250_usb3_tx_tbl),
+ .rx_tbl = sm8250_usb3_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sm8250_usb3_rx_tbl),
+ .pcs_tbl = sm8250_usb3_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_tbl),
+ .pcs_usb_tbl = sm8250_usb3_pcs_usb_tbl,
+ .pcs_usb_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_usb_tbl),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+
+ .has_pwrdn_delay = true,
+};
+
+static int qmp_combo_xlate(struct phy *phy, struct ofnode_phandle_args *args)
+{
+ if (args->args_count != 1) {
+ debug("Invalid args_count: %d\n", args->args_count);
+ return -EINVAL;
+ }
+
+ /* We only support the USB3 phy at slot 0 */
+ if (args->args[0] == QMP_USB43DP_DP_PHY)
+ return -EINVAL;
+
+ phy->id = QMP_USB43DP_USB3_PHY;
+
+ return 0;
+}
+
+static struct phy_ops qmp_combo_ops = {
+ .init = qmp_combo_power_on,
+ .exit = qmp_combo_power_off,
+ .of_xlate = qmp_combo_xlate,
+};
+
+static const struct udevice_id qmp_combo_ids[] = {
+ {
+ .compatible = "qcom,sc7280-qmp-usb3-dp-phy",
+ .data = (ulong)&sc7280_usb3dpphy_cfg,
+ },
+ { }
+};
+
+U_BOOT_DRIVER(qmp_combo) = {
+ .name = "qcom-qmp-usb3-dp-phy",
+ .id = UCLASS_PHY,
+ .of_match = qmp_combo_ids,
+ .ops = &qmp_combo_ops,
+ .probe = qmp_combo_probe,
+ .priv_auto = sizeof(struct qmp_combo),
+};
diff --git a/drivers/phy/qcom/phy-qcom-qmp-common.h b/drivers/phy/qcom/phy-qcom-qmp-common.h
new file mode 100644
index 00000000000..71356fb7dd0
--- /dev/null
+++ b/drivers/phy/qcom/phy-qcom-qmp-common.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_COMMON_H_
+#define QCOM_PHY_QMP_COMMON_H_
+
+struct qmp_phy_init_tbl {
+ unsigned int offset;
+ unsigned int val;
+ char *name;
+ /*
+ * mask of lanes for which this register is written
+ * for cases when second lane needs different values
+ */
+ u8 lane_mask;
+};
+
+#define QMP_PHY_INIT_CFG(o, v) \
+ { \
+ .offset = o, \
+ .val = v, \
+ .name = #o, \
+ .lane_mask = 0xff, \
+ }
+
+#define QMP_PHY_INIT_CFG_LANE(o, v, l) \
+ { \
+ .offset = o, \
+ .val = v, \
+ .name = #o, \
+ .lane_mask = l, \
+ }
+
+static inline void qmp_configure_lane(struct udevice *dev, void __iomem *base,
+ const struct qmp_phy_init_tbl tbl[],
+ int num, u8 lane_mask)
+{
+ int i;
+ const struct qmp_phy_init_tbl *t = tbl;
+
+ if (!t)
+ return;
+
+ for (i = 0; i < num; i++, t++) {
+ if (!(t->lane_mask & lane_mask))
+ continue;
+
+ dev_dbg(dev, "Writing Reg: %s Offset: 0x%04x Val: 0x%02x\n",
+ t->name, t->offset, t->val);
+ writel(t->val, base + t->offset);
+ }
+}
+
+static inline void qmp_configure(struct udevice *dev, void __iomem *base,
+ const struct qmp_phy_init_tbl tbl[], int num)
+{
+ qmp_configure_lane(dev, base, tbl, num, 0xff);
+}
+
+#endif
diff --git a/drivers/phy/qcom/phy-qcom-qmp-dp-com-v3.h b/drivers/phy/qcom/phy-qcom-qmp-dp-com-v3.h
new file mode 100644
index 00000000000..396179ef38b
--- /dev/null
+++ b/drivers/phy/qcom/phy-qcom-qmp-dp-com-v3.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_DP_COM_V3_H_
+#define QCOM_PHY_QMP_DP_COM_V3_H_
+
+/* Only for QMP V3 & V4 PHY - DP COM registers */
+#define QPHY_V3_DP_COM_PHY_MODE_CTRL 0x00
+#define QPHY_V3_DP_COM_SW_RESET 0x04
+#define QPHY_V3_DP_COM_POWER_DOWN_CTRL 0x08
+#define QPHY_V3_DP_COM_SWI_CTRL 0x0c
+#define QPHY_V3_DP_COM_TYPEC_CTRL 0x10
+#define QPHY_V3_DP_COM_TYPEC_PWRDN_CTRL 0x14
+#define QPHY_V3_DP_COM_RESET_OVRD_CTRL 0x1c
+
+#endif
diff --git a/drivers/phy/qcom/phy-qcom-qmp-pcs-usb-v4.h b/drivers/phy/qcom/phy-qcom-qmp-pcs-usb-v4.h
new file mode 100644
index 00000000000..d7fd4ac0fc5
--- /dev/null
+++ b/drivers/phy/qcom/phy-qcom-qmp-pcs-usb-v4.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_USB_V4_H_
+#define QCOM_PHY_QMP_PCS_USB_V4_H_
+
+/* Only for QMP V4 PHY - USB3 PCS registers */
+#define QPHY_V4_PCS_USB3_POWER_STATE_CONFIG1 0x000
+#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_STATUS 0x004
+#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_CTRL 0x008
+#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_CTRL2 0x00c
+#define QPHY_V4_PCS_USB3_LFPS_RXTERM_IRQ_SOURCE_STATUS 0x010
+#define QPHY_V4_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR 0x014
+#define QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL 0x018
+#define QPHY_V4_PCS_USB3_LFPS_TX_ECSTART 0x01c
+#define QPHY_V4_PCS_USB3_LFPS_PER_TIMER_VAL 0x020
+#define QPHY_V4_PCS_USB3_LFPS_TX_END_CNT_U3_START 0x024
+#define QPHY_V4_PCS_USB3_RXEQTRAINING_LOCK_TIME 0x028
+#define QPHY_V4_PCS_USB3_RXEQTRAINING_WAIT_TIME 0x02c
+#define QPHY_V4_PCS_USB3_RXEQTRAINING_CTLE_TIME 0x030
+#define QPHY_V4_PCS_USB3_RXEQTRAINING_WAIT_TIME_S2 0x034
+#define QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2 0x038
+#define QPHY_V4_PCS_USB3_RCVR_DTCT_DLY_U3_L 0x03c
+#define QPHY_V4_PCS_USB3_RCVR_DTCT_DLY_U3_H 0x040
+#define QPHY_V4_PCS_USB3_ARCVR_DTCT_EN_PERIOD 0x044
+#define QPHY_V4_PCS_USB3_ARCVR_DTCT_CM_DLY 0x048
+#define QPHY_V4_PCS_USB3_TXONESZEROS_RUN_LENGTH 0x04c
+#define QPHY_V4_PCS_USB3_ALFPS_DEGLITCH_VAL 0x050
+#define QPHY_V4_PCS_USB3_SIGDET_STARTUP_TIMER_VAL 0x054
+#define QPHY_V4_PCS_USB3_TEST_CONTROL 0x058
+
+#endif
diff --git a/drivers/phy/qcom/phy-qcom-qmp-ufs.c b/drivers/phy/qcom/phy-qcom-qmp-ufs.c
index 907f34744eb..80eba734a63 100644
--- a/drivers/phy/qcom/phy-qcom-qmp-ufs.c
+++ b/drivers/phy/qcom/phy-qcom-qmp-ufs.c
@@ -119,6 +119,68 @@ static const unsigned int ufsphy_v6_regs_layout[QPHY_LAYOUT_SIZE] = {
[QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V6_PCS_UFS_POWER_DOWN_CONTROL,
};
+static const struct qmp_ufs_init_tbl milos_ufsphy_serdes[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0xd9),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_HS_SWITCH_SEL_1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_IETRIM, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_IPTRIM, 0x17),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_BG_TIMER, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_INITVAL2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE1, 0x98),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE1, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE1, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE1, 0x32),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE1, 0x0f),
+};
+
+static const struct qmp_ufs_init_tbl milos_ufsphy_tx[] = {
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_LANE_MODE_1, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_RES_CODE_LANE_OFFSET_TX, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_RES_CODE_LANE_OFFSET_RX, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_FR_DCC_CTRL, 0xcc),
+};
+
+static const struct qmp_ufs_init_tbl milos_ufsphy_rx[] = {
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FO_GAIN_RATE2, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_VGA_CAL_MAN_VAL, 0x3e),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B0, 0xce),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B1, 0xce),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B2, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B3, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B4, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B6, 0x60),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE2_B3, 0x9e),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE2_B6, 0x60),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE3_B3, 0x9e),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE3_B4, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE3_B5, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE3_B8, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_PI_CTRL1, 0x94),
+};
+
+static const struct qmp_ufs_init_tbl milos_ufsphy_pcs[] = {
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_MULTI_LANE_CTRL1, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_MID_TERM_CTRL1, 0x43),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PLL_CNTL, 0x0b),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0f),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_SIGDET_CTRL2, 0x68),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_HSGEAR_CAPABILITY, 0x04),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_HSGEAR_CAPABILITY, 0x04),
+};
+
static const struct qmp_ufs_init_tbl sdm845_ufsphy_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04),
@@ -982,6 +1044,31 @@ static const struct qmp_ufs_offsets qmp_ufs_offsets_v6 = {
.rx2 = 0x1a00,
};
+static const struct qmp_ufs_cfg milos_ufsphy_cfg = {
+ .lanes = 2,
+
+ .offsets = &qmp_ufs_offsets_v6,
+
+ .tbls = {
+ .serdes = milos_ufsphy_serdes,
+ .serdes_num = ARRAY_SIZE(milos_ufsphy_serdes),
+ .tx = milos_ufsphy_tx,
+ .tx_num = ARRAY_SIZE(milos_ufsphy_tx),
+ .rx = milos_ufsphy_rx,
+ .rx_num = ARRAY_SIZE(milos_ufsphy_rx),
+ .pcs = milos_ufsphy_pcs,
+ .pcs_num = ARRAY_SIZE(milos_ufsphy_pcs),
+ },
+ .tbls_hs_b = {
+ .serdes = sm8550_ufsphy_hs_b_serdes,
+ .serdes_num = ARRAY_SIZE(sm8550_ufsphy_hs_b_serdes),
+ },
+
+ .vreg_list = qmp_ufs_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_ufs_vreg_l),
+ .regs = ufsphy_v6_regs_layout,
+};
+
static const struct qmp_ufs_cfg sdm845_ufsphy_cfg = {
.lanes = 2,
@@ -1651,6 +1738,7 @@ static struct phy_ops qmp_ufs_ops = {
};
static const struct udevice_id qmp_ufs_ids[] = {
+ { .compatible = "qcom,milos-qmp-ufs-phy", .data = (ulong)&milos_ufsphy_cfg, },
{ .compatible = "qcom,sa8775p-qmp-ufs-phy", .data = (ulong)&sa8775p_ufsphy_cfg, },
{ .compatible = "qcom,sdm845-qmp-ufs-phy", .data = (ulong)&sdm845_ufsphy_cfg },
{ .compatible = "qcom,sm6350-qmp-ufs-phy", .data = (ulong)&sdm845_ufsphy_cfg },
diff --git a/drivers/phy/qcom/phy-qcom-qmp.h b/drivers/phy/qcom/phy-qcom-qmp.h
index 99f4d447caf..06dac21ddc4 100644
--- a/drivers/phy/qcom/phy-qcom-qmp.h
+++ b/drivers/phy/qcom/phy-qcom-qmp.h
@@ -12,12 +12,17 @@
#include "phy-qcom-qmp-qserdes-com-v3.h"
#include "phy-qcom-qmp-qserdes-txrx-v3.h"
+#include "phy-qcom-qmp-qserdes-com-v4.h"
+#include "phy-qcom-qmp-qserdes-txrx-v4.h"
+
#include "phy-qcom-qmp-qserdes-pll.h"
#include "phy-qcom-qmp-pcs-v2.h"
#include "phy-qcom-qmp-pcs-v3.h"
+#include "phy-qcom-qmp-pcs-v4.h"
+
/* Only for QMP V3 & V4 PHY - DP COM registers */
#define QPHY_V3_DP_COM_PHY_MODE_CTRL 0x00
#define QPHY_V3_DP_COM_SW_RESET 0x04
@@ -112,4 +117,16 @@
#define QSERDES_V6_DP_PHY_AUX_INTERRUPT_STATUS 0x0e0
#define QSERDES_V6_DP_PHY_STATUS 0x0e4
+/* QPHY_SW_RESET bit */
+#define SW_RESET BIT(0)
+/* QPHY_POWER_DOWN_CONTROL */
+#define SW_PWRDN BIT(0)
+
+/* QPHY_START_CONTROL bits */
+#define SERDES_START BIT(0)
+#define PCS_START BIT(1)
+
+/* QPHY_PCS_STATUS bit */
+#define PHYSTATUS BIT(6)
+
#endif
diff --git a/drivers/pinctrl/mediatek/pinctrl-mt7981.c b/drivers/pinctrl/mediatek/pinctrl-mt7981.c
index 8875c276f36..5219b147797 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mt7981.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mt7981.c
@@ -106,7 +106,7 @@ static const struct mtk_pin_field_calc mt7981_pin_ies_range[] = {
PIN_FIELD_BASE(9, 9, 5, 0x20, 0x10, 9, 1),
PIN_FIELD_BASE(10, 10, 5, 0x20, 0x10, 8, 1),
- PIN_FIELD_BASE(11, 11, 5, 0x40, 0x10, 10, 1),
+ PIN_FIELD_BASE(11, 11, 5, 0x20, 0x10, 10, 1),
PIN_FIELD_BASE(12, 12, 5, 0x20, 0x10, 7, 1),
PIN_FIELD_BASE(13, 13, 5, 0x20, 0x10, 11, 1),
@@ -215,7 +215,7 @@ static const struct mtk_pin_field_calc mt7981_pin_smt_range[] = {
PIN_FIELD_BASE(41, 41, 7, 0x70, 0x10, 0, 1),
PIN_FIELD_BASE(42, 42, 7, 0x70, 0x10, 9, 1),
PIN_FIELD_BASE(43, 43, 7, 0x70, 0x10, 7, 1),
- PIN_FIELD_BASE(44, 44, 7, 0x30, 0x10, 8, 1),
+ PIN_FIELD_BASE(44, 44, 7, 0x70, 0x10, 8, 1),
PIN_FIELD_BASE(45, 45, 7, 0x70, 0x10, 3, 1),
PIN_FIELD_BASE(46, 46, 7, 0x70, 0x10, 4, 1),
PIN_FIELD_BASE(47, 47, 7, 0x70, 0x10, 5, 1),
@@ -279,8 +279,8 @@ static const struct mtk_pin_field_calc mt7981_pin_drv_range[] = {
PIN_FIELD_BASE(2, 2, 5, 0x00, 0x10, 18, 3),
- PIN_FIELD_BASE(3, 3, 4, 0x00, 0x10, 18, 1),
- PIN_FIELD_BASE(4, 4, 4, 0x00, 0x10, 6, 1),
+ PIN_FIELD_BASE(3, 3, 4, 0x00, 0x10, 18, 3),
+ PIN_FIELD_BASE(4, 4, 4, 0x00, 0x10, 6, 3),
PIN_FIELD_BASE(5, 5, 4, 0x00, 0x10, 3, 3),
PIN_FIELD_BASE(6, 6, 4, 0x00, 0x10, 9, 3),
PIN_FIELD_BASE(7, 7, 4, 0x00, 0x10, 0, 3),
@@ -288,9 +288,9 @@ static const struct mtk_pin_field_calc mt7981_pin_drv_range[] = {
PIN_FIELD_BASE(9, 9, 5, 0x00, 0x10, 27, 3),
PIN_FIELD_BASE(10, 10, 5, 0x00, 0x10, 24, 3),
- PIN_FIELD_BASE(11, 11, 5, 0x00, 0x10, 0, 3),
+ PIN_FIELD_BASE(11, 11, 5, 0x10, 0x10, 0, 3),
PIN_FIELD_BASE(12, 12, 5, 0x00, 0x10, 21, 3),
- PIN_FIELD_BASE(13, 13, 5, 0x00, 0x10, 3, 3),
+ PIN_FIELD_BASE(13, 13, 5, 0x10, 0x10, 3, 3),
PIN_FIELD_BASE(14, 14, 4, 0x00, 0x10, 27, 3),
@@ -302,7 +302,7 @@ static const struct mtk_pin_field_calc mt7981_pin_drv_range[] = {
PIN_FIELD_BASE(20, 20, 2, 0x00, 0x10, 9, 3),
PIN_FIELD_BASE(21, 21, 2, 0x00, 0x10, 18, 3),
PIN_FIELD_BASE(22, 22, 2, 0x00, 0x10, 21, 3),
- PIN_FIELD_BASE(23, 23, 2, 0x00, 0x10, 0, 3),
+ PIN_FIELD_BASE(23, 23, 2, 0x10, 0x10, 0, 3),
PIN_FIELD_BASE(24, 24, 2, 0x00, 0x10, 27, 3),
PIN_FIELD_BASE(25, 25, 2, 0x00, 0x10, 24, 3),
@@ -368,7 +368,7 @@ static const struct mtk_pin_field_calc mt7981_pin_pupd_range[] = {
PIN_FIELD_BASE(17, 17, 2, 0x30, 0x10, 5, 1),
PIN_FIELD_BASE(18, 18, 2, 0x30, 0x10, 4, 1),
PIN_FIELD_BASE(19, 19, 2, 0x30, 0x10, 2, 1),
- PIN_FIELD_BASE(20, 20, 2, 0x90, 0x10, 3, 1),
+ PIN_FIELD_BASE(20, 20, 2, 0x30, 0x10, 3, 1),
PIN_FIELD_BASE(21, 21, 2, 0x30, 0x10, 6, 1),
PIN_FIELD_BASE(22, 22, 2, 0x30, 0x10, 7, 1),
PIN_FIELD_BASE(23, 23, 2, 0x30, 0x10, 10, 1),
diff --git a/drivers/pinctrl/pinctrl-apple.c b/drivers/pinctrl/pinctrl-apple.c
index f373afde58e..083ea6d6cd5 100644
--- a/drivers/pinctrl/pinctrl-apple.c
+++ b/drivers/pinctrl/pinctrl-apple.c
@@ -192,6 +192,7 @@ static struct pinctrl_ops apple_pinctrl_ops = {
};
static const struct udevice_id apple_pinctrl_ids[] = {
+ { .compatible = "apple,t8103-pinctrl" },
{ .compatible = "apple,pinctrl" },
{ /* sentinel */ }
};
diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index 580308621b1..11e6763b5f3 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -54,6 +54,14 @@ config PINCTRL_QCOM_IPQ9574
Say Y here to enable support for pinctrl on the IPQ9574 SoC,
as well as the associated GPIO driver.
+config PINCTRL_QCOM_MILOS
+ bool "Qualcomm Milos Pinctrl"
+ default y if PINCTRL_QCOM_GENERIC
+ select PINCTRL_QCOM
+ help
+ Say Y here to enable support for pinctrl on the Snapdragon Milos SoC,
+ as well as the associated GPIO driver.
+
config PINCTRL_QCOM_QCM2290
bool "Qualcomm QCM2290 Pinctrl"
default y if PINCTRL_QCOM_GENERIC
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index b5a111605ed..4096c1aa491 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_PINCTRL_QCOM_IPQ4019) += pinctrl-ipq4019.o
obj-$(CONFIG_PINCTRL_QCOM_IPQ5424) += pinctrl-ipq5424.o
obj-$(CONFIG_PINCTRL_QCOM_IPQ9574) += pinctrl-ipq9574.o
obj-$(CONFIG_PINCTRL_QCOM_APQ8096) += pinctrl-apq8096.o
+obj-$(CONFIG_PINCTRL_QCOM_MILOS) += pinctrl-milos.o
obj-$(CONFIG_PINCTRL_QCOM_QCM2290) += pinctrl-qcm2290.o
obj-$(CONFIG_PINCTRL_QCOM_QCS404) += pinctrl-qcs404.o
obj-$(CONFIG_PINCTRL_QCOM_QCS615) += pinctrl-qcs615.o
diff --git a/drivers/pinctrl/qcom/pinctrl-milos.c b/drivers/pinctrl/qcom/pinctrl-milos.c
new file mode 100644
index 00000000000..4f958fbfbf3
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-milos.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Qualcomm Milos pinctrl
+ *
+ * (C) Copyright 2024 Linaro Ltd.
+ * (C) Copyright 2026 Luca Weiss <[email protected]>
+ *
+ */
+
+#include <dm.h>
+
+#include "pinctrl-qcom.h"
+
+#define MAX_PIN_NAME_LEN 32
+static char pin_name[MAX_PIN_NAME_LEN] __section(".data");
+
+static const struct pinctrl_function msm_pinctrl_functions[] = {
+ {"qup0_se5", 1},
+ {"sdc2_clk", 1},
+ {"sdc2_cmd", 1},
+ {"sdc2_data", 1},
+ {"gpio", 0},
+};
+
+#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \
+ { \
+ .name = pg_name, \
+ .ctl_reg = ctl, \
+ .io_reg = 0, \
+ .pull_bit = pull, \
+ .drv_bit = drv, \
+ .oe_bit = -1, \
+ .in_bit = -1, \
+ .out_bit = -1, \
+ }
+
+#define UFS_RESET(pg_name, ctl, io) \
+ { \
+ .name = pg_name, \
+ .ctl_reg = ctl, \
+ .io_reg = io, \
+ .pull_bit = 3, \
+ .drv_bit = 0, \
+ .oe_bit = -1, \
+ .in_bit = -1, \
+ .out_bit = 0, \
+ }
+
+static const struct msm_special_pin_data msm_special_pins_data[] = {
+ [0] = UFS_RESET("ufs_reset", 0xb4004, 0xb5000),
+ [1] = SDC_QDSD_PINGROUP("sdc2_clk", 0xab000, 0, 6),
+ [2] = SDC_QDSD_PINGROUP("sdc2_cmd", 0xab000, 12, 3),
+ [3] = SDC_QDSD_PINGROUP("sdc2_data", 0xab000, 9, 0),
+};
+
+static const char *milos_get_function_name(struct udevice *dev,
+ unsigned int selector)
+{
+ return msm_pinctrl_functions[selector].name;
+}
+
+static const char *milos_get_pin_name(struct udevice *dev,
+ unsigned int selector)
+{
+ if (selector >= 167 && selector <= 170)
+ snprintf(pin_name, MAX_PIN_NAME_LEN,
+ msm_special_pins_data[selector - 167].name);
+ else
+ snprintf(pin_name, MAX_PIN_NAME_LEN, "gpio%u", selector);
+
+ return pin_name;
+}
+
+static int milos_get_function_mux(__maybe_unused unsigned int pin,
+ unsigned int selector)
+{
+ return msm_pinctrl_functions[selector].val;
+}
+
+static struct msm_pinctrl_data milos_data = {
+ .pin_data = {
+ .pin_count = 171,
+ .special_pins_start = 167,
+ .special_pins_data = msm_special_pins_data,
+ },
+ .functions_count = ARRAY_SIZE(msm_pinctrl_functions),
+ .get_function_name = milos_get_function_name,
+ .get_function_mux = milos_get_function_mux,
+ .get_pin_name = milos_get_pin_name,
+};
+
+static const struct udevice_id msm_pinctrl_ids[] = {
+ { .compatible = "qcom,milos-tlmm", .data = (ulong)&milos_data },
+ { /* Sentinel */ }
+};
+
+U_BOOT_DRIVER(pinctrl_milos) = {
+ .name = "pinctrl_milos",
+ .id = UCLASS_NOP,
+ .of_match = msm_pinctrl_ids,
+ .ops = &msm_pinctrl_ops,
+ .bind = msm_pinctrl_bind,
+};
diff --git a/drivers/pinctrl/qcom/pinctrl-sc7280.c b/drivers/pinctrl/qcom/pinctrl-sc7280.c
index d62b2cc6fb6..d0242fc1c17 100644
--- a/drivers/pinctrl/qcom/pinctrl-sc7280.c
+++ b/drivers/pinctrl/qcom/pinctrl-sc7280.c
@@ -13,11 +13,22 @@
#define MAX_PIN_NAME_LEN 32
static char pin_name[MAX_PIN_NAME_LEN] __section(".data");
-static const struct pinctrl_function msm_pinctrl_functions[] = {
- { "qup05", 1 },
- { "gpio", 0 },
- { "pcie1_clkreqn", 3},
-};
+typedef unsigned int msm_pin_function[10];
+
+#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9)\
+ { \
+ msm_mux_gpio, /* gpio mode */ \
+ msm_mux_##f1, \
+ msm_mux_##f2, \
+ msm_mux_##f3, \
+ msm_mux_##f4, \
+ msm_mux_##f5, \
+ msm_mux_##f6, \
+ msm_mux_##f7, \
+ msm_mux_##f8, \
+ msm_mux_##f9 \
+ }
+
#define SDC_PINGROUP(pg_name, ctl, pull, drv) \
{ \
.name = pg_name, \
@@ -42,6 +53,492 @@ static const struct pinctrl_function msm_pinctrl_functions[] = {
.out_bit = 0, \
}
+enum sc7280_functions {
+ msm_mux_atest_char,
+ msm_mux_atest_char0,
+ msm_mux_atest_char1,
+ msm_mux_atest_char2,
+ msm_mux_atest_char3,
+ msm_mux_atest_usb0,
+ msm_mux_atest_usb00,
+ msm_mux_atest_usb01,
+ msm_mux_atest_usb02,
+ msm_mux_atest_usb03,
+ msm_mux_atest_usb1,
+ msm_mux_atest_usb10,
+ msm_mux_atest_usb11,
+ msm_mux_atest_usb12,
+ msm_mux_atest_usb13,
+ msm_mux_audio_ref,
+ msm_mux_cam_mclk,
+ msm_mux_cci_async,
+ msm_mux_cci_i2c,
+ msm_mux_cci_timer0,
+ msm_mux_cci_timer1,
+ msm_mux_cci_timer2,
+ msm_mux_cci_timer3,
+ msm_mux_cci_timer4,
+ msm_mux_cmu_rng0,
+ msm_mux_cmu_rng1,
+ msm_mux_cmu_rng2,
+ msm_mux_cmu_rng3,
+ msm_mux_coex_uart1,
+ msm_mux_cri_trng,
+ msm_mux_cri_trng0,
+ msm_mux_cri_trng1,
+ msm_mux_dbg_out,
+ msm_mux_ddr_bist,
+ msm_mux_ddr_pxi0,
+ msm_mux_ddr_pxi1,
+ msm_mux_dp_hot,
+ msm_mux_dp_lcd,
+ msm_mux_edp_hot,
+ msm_mux_edp_lcd,
+ msm_mux_egpio,
+ msm_mux_gcc_gp1,
+ msm_mux_gcc_gp2,
+ msm_mux_gcc_gp3,
+ msm_mux_gpio,
+ msm_mux_host2wlan_sol,
+ msm_mux_ibi_i3c,
+ msm_mux_jitter_bist,
+ msm_mux_lpass_slimbus,
+ msm_mux_mdp_vsync,
+ msm_mux_mdp_vsync0,
+ msm_mux_mdp_vsync1,
+ msm_mux_mdp_vsync2,
+ msm_mux_mdp_vsync3,
+ msm_mux_mdp_vsync4,
+ msm_mux_mdp_vsync5,
+ msm_mux_mi2s0_data0,
+ msm_mux_mi2s0_data1,
+ msm_mux_mi2s0_sck,
+ msm_mux_mi2s0_ws,
+ msm_mux_mi2s1_data0,
+ msm_mux_mi2s1_data1,
+ msm_mux_mi2s1_sck,
+ msm_mux_mi2s1_ws,
+ msm_mux_mi2s2_data0,
+ msm_mux_mi2s2_data1,
+ msm_mux_mi2s2_sck,
+ msm_mux_mi2s2_ws,
+ msm_mux_mss_grfc0,
+ msm_mux_mss_grfc1,
+ msm_mux_mss_grfc10,
+ msm_mux_mss_grfc11,
+ msm_mux_mss_grfc12,
+ msm_mux_mss_grfc2,
+ msm_mux_mss_grfc3,
+ msm_mux_mss_grfc4,
+ msm_mux_mss_grfc5,
+ msm_mux_mss_grfc6,
+ msm_mux_mss_grfc7,
+ msm_mux_mss_grfc8,
+ msm_mux_mss_grfc9,
+ msm_mux_nav_gpio0,
+ msm_mux_nav_gpio1,
+ msm_mux_nav_gpio2,
+ msm_mux_pa_indicator,
+ msm_mux_pcie0_clkreqn,
+ msm_mux_pcie1_clkreqn,
+ msm_mux_phase_flag,
+ msm_mux_pll_bist,
+ msm_mux_pll_bypassnl,
+ msm_mux_pll_clk,
+ msm_mux_pll_reset,
+ msm_mux_pri_mi2s,
+ msm_mux_prng_rosc,
+ msm_mux_qdss,
+ msm_mux_qdss_cti,
+ msm_mux_qlink0_enable,
+ msm_mux_qlink0_request,
+ msm_mux_qlink0_wmss,
+ msm_mux_qlink1_enable,
+ msm_mux_qlink1_request,
+ msm_mux_qlink1_wmss,
+ msm_mux_qspi_clk,
+ msm_mux_qspi_cs,
+ msm_mux_qspi_data,
+ msm_mux_qup00,
+ msm_mux_qup01,
+ msm_mux_qup02,
+ msm_mux_qup03,
+ msm_mux_qup04,
+ msm_mux_qup05,
+ msm_mux_qup06,
+ msm_mux_qup07,
+ msm_mux_qup10,
+ msm_mux_qup11,
+ msm_mux_qup12,
+ msm_mux_qup13,
+ msm_mux_qup14,
+ msm_mux_qup15,
+ msm_mux_qup16,
+ msm_mux_qup17,
+ msm_mux_sd_write,
+ msm_mux_sdc40,
+ msm_mux_sdc41,
+ msm_mux_sdc42,
+ msm_mux_sdc43,
+ msm_mux_sdc4_clk,
+ msm_mux_sdc4_cmd,
+ msm_mux_sec_mi2s,
+ msm_mux_tb_trig,
+ msm_mux_tgu_ch0,
+ msm_mux_tgu_ch1,
+ msm_mux_tsense_pwm1,
+ msm_mux_tsense_pwm2,
+ msm_mux_uim0_clk,
+ msm_mux_uim0_data,
+ msm_mux_uim0_present,
+ msm_mux_uim0_reset,
+ msm_mux_uim1_clk,
+ msm_mux_uim1_data,
+ msm_mux_uim1_present,
+ msm_mux_uim1_reset,
+ msm_mux_usb2phy_ac,
+ msm_mux_usb_phy,
+ msm_mux_vfr_0,
+ msm_mux_vfr_1,
+ msm_mux_vsense_trigger,
+ msm_mux__,
+};
+
+#define MSM_PIN_FUNCTION(fname) \
+ [msm_mux_##fname] = {#fname, msm_mux_##fname}
+
+static const struct pinctrl_function msm_pinctrl_functions[] = {
+ MSM_PIN_FUNCTION(atest_char),
+ MSM_PIN_FUNCTION(atest_char0),
+ MSM_PIN_FUNCTION(atest_char1),
+ MSM_PIN_FUNCTION(atest_char2),
+ MSM_PIN_FUNCTION(atest_char3),
+ MSM_PIN_FUNCTION(atest_usb0),
+ MSM_PIN_FUNCTION(atest_usb00),
+ MSM_PIN_FUNCTION(atest_usb01),
+ MSM_PIN_FUNCTION(atest_usb02),
+ MSM_PIN_FUNCTION(atest_usb03),
+ MSM_PIN_FUNCTION(atest_usb1),
+ MSM_PIN_FUNCTION(atest_usb10),
+ MSM_PIN_FUNCTION(atest_usb11),
+ MSM_PIN_FUNCTION(atest_usb12),
+ MSM_PIN_FUNCTION(atest_usb13),
+ MSM_PIN_FUNCTION(audio_ref),
+ MSM_PIN_FUNCTION(cam_mclk),
+ MSM_PIN_FUNCTION(cci_async),
+ MSM_PIN_FUNCTION(cci_i2c),
+ MSM_PIN_FUNCTION(cci_timer0),
+ MSM_PIN_FUNCTION(cci_timer1),
+ MSM_PIN_FUNCTION(cci_timer2),
+ MSM_PIN_FUNCTION(cci_timer3),
+ MSM_PIN_FUNCTION(cci_timer4),
+ MSM_PIN_FUNCTION(cmu_rng0),
+ MSM_PIN_FUNCTION(cmu_rng1),
+ MSM_PIN_FUNCTION(cmu_rng2),
+ MSM_PIN_FUNCTION(cmu_rng3),
+ MSM_PIN_FUNCTION(coex_uart1),
+ MSM_PIN_FUNCTION(cri_trng),
+ MSM_PIN_FUNCTION(cri_trng0),
+ MSM_PIN_FUNCTION(cri_trng1),
+ MSM_PIN_FUNCTION(dbg_out),
+ MSM_PIN_FUNCTION(ddr_bist),
+ MSM_PIN_FUNCTION(ddr_pxi0),
+ MSM_PIN_FUNCTION(ddr_pxi1),
+ MSM_PIN_FUNCTION(dp_hot),
+ MSM_PIN_FUNCTION(dp_lcd),
+ MSM_PIN_FUNCTION(edp_hot),
+ MSM_PIN_FUNCTION(edp_lcd),
+ MSM_PIN_FUNCTION(egpio),
+ MSM_PIN_FUNCTION(gcc_gp1),
+ MSM_PIN_FUNCTION(gcc_gp2),
+ MSM_PIN_FUNCTION(gcc_gp3),
+ MSM_PIN_FUNCTION(gpio),
+ MSM_PIN_FUNCTION(host2wlan_sol),
+ MSM_PIN_FUNCTION(ibi_i3c),
+ MSM_PIN_FUNCTION(jitter_bist),
+ MSM_PIN_FUNCTION(lpass_slimbus),
+ MSM_PIN_FUNCTION(mdp_vsync),
+ MSM_PIN_FUNCTION(mdp_vsync0),
+ MSM_PIN_FUNCTION(mdp_vsync1),
+ MSM_PIN_FUNCTION(mdp_vsync2),
+ MSM_PIN_FUNCTION(mdp_vsync3),
+ MSM_PIN_FUNCTION(mdp_vsync4),
+ MSM_PIN_FUNCTION(mdp_vsync5),
+ MSM_PIN_FUNCTION(mi2s0_data0),
+ MSM_PIN_FUNCTION(mi2s0_data1),
+ MSM_PIN_FUNCTION(mi2s0_sck),
+ MSM_PIN_FUNCTION(mi2s0_ws),
+ MSM_PIN_FUNCTION(mi2s1_data0),
+ MSM_PIN_FUNCTION(mi2s1_data1),
+ MSM_PIN_FUNCTION(mi2s1_sck),
+ MSM_PIN_FUNCTION(mi2s1_ws),
+ MSM_PIN_FUNCTION(mi2s2_data0),
+ MSM_PIN_FUNCTION(mi2s2_data1),
+ MSM_PIN_FUNCTION(mi2s2_sck),
+ MSM_PIN_FUNCTION(mi2s2_ws),
+ MSM_PIN_FUNCTION(mss_grfc0),
+ MSM_PIN_FUNCTION(mss_grfc1),
+ MSM_PIN_FUNCTION(mss_grfc10),
+ MSM_PIN_FUNCTION(mss_grfc11),
+ MSM_PIN_FUNCTION(mss_grfc12),
+ MSM_PIN_FUNCTION(mss_grfc2),
+ MSM_PIN_FUNCTION(mss_grfc3),
+ MSM_PIN_FUNCTION(mss_grfc4),
+ MSM_PIN_FUNCTION(mss_grfc5),
+ MSM_PIN_FUNCTION(mss_grfc6),
+ MSM_PIN_FUNCTION(mss_grfc7),
+ MSM_PIN_FUNCTION(mss_grfc8),
+ MSM_PIN_FUNCTION(mss_grfc9),
+ MSM_PIN_FUNCTION(nav_gpio0),
+ MSM_PIN_FUNCTION(nav_gpio1),
+ MSM_PIN_FUNCTION(nav_gpio2),
+ MSM_PIN_FUNCTION(pa_indicator),
+ MSM_PIN_FUNCTION(pcie0_clkreqn),
+ MSM_PIN_FUNCTION(pcie1_clkreqn),
+ MSM_PIN_FUNCTION(phase_flag),
+ MSM_PIN_FUNCTION(pll_bist),
+ MSM_PIN_FUNCTION(pll_bypassnl),
+ MSM_PIN_FUNCTION(pll_clk),
+ MSM_PIN_FUNCTION(pll_reset),
+ MSM_PIN_FUNCTION(pri_mi2s),
+ MSM_PIN_FUNCTION(prng_rosc),
+ MSM_PIN_FUNCTION(qdss),
+ MSM_PIN_FUNCTION(qdss_cti),
+ MSM_PIN_FUNCTION(qlink0_enable),
+ MSM_PIN_FUNCTION(qlink0_request),
+ MSM_PIN_FUNCTION(qlink0_wmss),
+ MSM_PIN_FUNCTION(qlink1_enable),
+ MSM_PIN_FUNCTION(qlink1_request),
+ MSM_PIN_FUNCTION(qlink1_wmss),
+ MSM_PIN_FUNCTION(qspi_clk),
+ MSM_PIN_FUNCTION(qspi_cs),
+ MSM_PIN_FUNCTION(qspi_data),
+ MSM_PIN_FUNCTION(qup00),
+ MSM_PIN_FUNCTION(qup01),
+ MSM_PIN_FUNCTION(qup02),
+ MSM_PIN_FUNCTION(qup03),
+ MSM_PIN_FUNCTION(qup04),
+ MSM_PIN_FUNCTION(qup05),
+ MSM_PIN_FUNCTION(qup06),
+ MSM_PIN_FUNCTION(qup07),
+ MSM_PIN_FUNCTION(qup10),
+ MSM_PIN_FUNCTION(qup11),
+ MSM_PIN_FUNCTION(qup12),
+ MSM_PIN_FUNCTION(qup13),
+ MSM_PIN_FUNCTION(qup14),
+ MSM_PIN_FUNCTION(qup15),
+ MSM_PIN_FUNCTION(qup16),
+ MSM_PIN_FUNCTION(qup17),
+ MSM_PIN_FUNCTION(sd_write),
+ MSM_PIN_FUNCTION(sdc40),
+ MSM_PIN_FUNCTION(sdc41),
+ MSM_PIN_FUNCTION(sdc42),
+ MSM_PIN_FUNCTION(sdc43),
+ MSM_PIN_FUNCTION(sdc4_clk),
+ MSM_PIN_FUNCTION(sdc4_cmd),
+ MSM_PIN_FUNCTION(sec_mi2s),
+ MSM_PIN_FUNCTION(tb_trig),
+ MSM_PIN_FUNCTION(tgu_ch0),
+ MSM_PIN_FUNCTION(tgu_ch1),
+ MSM_PIN_FUNCTION(tsense_pwm1),
+ MSM_PIN_FUNCTION(tsense_pwm2),
+ MSM_PIN_FUNCTION(uim0_clk),
+ MSM_PIN_FUNCTION(uim0_data),
+ MSM_PIN_FUNCTION(uim0_present),
+ MSM_PIN_FUNCTION(uim0_reset),
+ MSM_PIN_FUNCTION(uim1_clk),
+ MSM_PIN_FUNCTION(uim1_data),
+ MSM_PIN_FUNCTION(uim1_present),
+ MSM_PIN_FUNCTION(uim1_reset),
+ MSM_PIN_FUNCTION(usb2phy_ac),
+ MSM_PIN_FUNCTION(usb_phy),
+ MSM_PIN_FUNCTION(vfr_0),
+ MSM_PIN_FUNCTION(vfr_1),
+ MSM_PIN_FUNCTION(vsense_trigger),
+};
+
+static const msm_pin_function sc7280_pin_functions[] = {
+ [0] = PINGROUP(0, qup00, ibi_i3c, _, _, _, _, _, _, _),
+ [1] = PINGROUP(1, qup00, ibi_i3c, _, _, _, _, _, _, _),
+ [2] = PINGROUP(2, qup00, qup07, _, qdss, _, _, _, _, _),
+ [3] = PINGROUP(3, qup00, qup07, _, qdss, _, _, _, _, _),
+ [4] = PINGROUP(4, qup01, ibi_i3c, _, _, _, _, _, _, _),
+ [5] = PINGROUP(5, qup01, ibi_i3c, _, _, _, _, _, _, _),
+ [6] = PINGROUP(6, qup01, qup07, _, _, _, _, _, _, _),
+ [7] = PINGROUP(7, qup01, _, _, _, _, _, _, _, _),
+ [8] = PINGROUP(8, qup02, _, qdss, _, _, _, _, _, _),
+ [9] = PINGROUP(9, qup02, _, qdss, _, _, _, _, _, _),
+ [10] = PINGROUP(10, qup02, _, qdss, _, _, _, _, _, _),
+ [11] = PINGROUP(11, qup02, _, qdss, _, _, _, _, _, _),
+ [12] = PINGROUP(12, qup03, qspi_data, sdc40, tb_trig, phase_flag, qdss, ddr_pxi1, _, _),
+ [13] = PINGROUP(13, qup03, qspi_data, sdc41, tb_trig, phase_flag, qdss, ddr_pxi1, _, _),
+ [14] = PINGROUP(14, qup03, qspi_clk, sdc4_clk, mdp_vsync, phase_flag, ddr_pxi0, _, _, _),
+ [15] = PINGROUP(15, qup03, qspi_cs, tb_trig, phase_flag, qdss_cti, ddr_pxi0, _, _, _),
+ [16] = PINGROUP(16, qup04, qspi_data, sdc42, mdp_vsync, phase_flag, qdss_cti, _, _, _),
+ [17] = PINGROUP(17, qup04, qspi_data, sdc43, _, phase_flag, _, _, _, _),
+ [18] = PINGROUP(18, qup04, _, phase_flag, qdss_cti, _, _, _, _, _),
+ [19] = PINGROUP(19, qup04, qspi_cs, sdc4_cmd, _, phase_flag, qdss_cti, _, _, _),
+ [20] = PINGROUP(20, qup05, cci_timer0, _, qdss, _, _, _, _, _),
+ [21] = PINGROUP(21, qup05, cci_timer1, _, qdss, _, _, _, _, _),
+ [22] = PINGROUP(22, qup05, _, qdss, _, _, _, _, _, _),
+ [23] = PINGROUP(23, qup05, _, qdss, _, _, _, _, _, _),
+ [24] = PINGROUP(24, qup06, _, qdss, _, _, _, _, _, _),
+ [25] = PINGROUP(25, qup06, _, qdss, _, _, _, _, _, _),
+ [26] = PINGROUP(26, qup06, host2wlan_sol, _, qdss, _, _, _, _, _),
+ [27] = PINGROUP(27, qup06, _, qdss, _, _, _, _, _, _),
+ [28] = PINGROUP(28, qup07, _, qdss, _, _, _, _, _, _),
+ [29] = PINGROUP(29, qup07, qdss, _, _, _, _, _, _, _),
+ [30] = PINGROUP(30, qup07, _, _, _, _, _, _, _, _),
+ [31] = PINGROUP(31, qup07, _, _, _, _, _, _, _, _),
+ [32] = PINGROUP(32, qup10, _, _, _, _, _, _, _, _),
+ [33] = PINGROUP(33, qup10, _, _, _, _, _, _, _, _),
+ [34] = PINGROUP(34, qup10, _, _, _, _, _, _, _, _),
+ [35] = PINGROUP(35, qup10, _, _, _, _, _, _, _, _),
+ [36] = PINGROUP(36, qup11, ibi_i3c, _, _, _, _, _, _, _),
+ [37] = PINGROUP(37, qup11, ibi_i3c, _, _, _, _, _, _, _),
+ [38] = PINGROUP(38, qup11, qup14, dbg_out, _, _, _, _, _, _),
+ [39] = PINGROUP(39, qup11, _, _, _, _, _, _, _, _),
+ [40] = PINGROUP(40, qup12, _, _, _, _, _, _, _, _),
+ [41] = PINGROUP(41, qup12, _, _, _, _, _, _, _, _),
+ [42] = PINGROUP(42, qup12, _, _, _, _, _, _, _, _),
+ [43] = PINGROUP(43, qup12, _, _, _, _, _, _, _, _),
+ [44] = PINGROUP(44, qup13, _, _, _, _, _, _, _, _),
+ [45] = PINGROUP(45, qup13, _, _, _, _, _, _, _, _),
+ [46] = PINGROUP(46, qup13, edp_lcd, _, _, _, _, _, _, _),
+ [47] = PINGROUP(47, qup13, dp_hot, _, _, _, _, _, _, _),
+ [48] = PINGROUP(48, qup14, _, _, _, _, _, _, _, _),
+ [49] = PINGROUP(49, qup14, _, _, _, _, _, _, _, _),
+ [50] = PINGROUP(50, qup14, qup16, _, _, _, _, _, _, _),
+ [51] = PINGROUP(51, qup14, _, _, _, _, _, _, _, _),
+ [52] = PINGROUP(52, qup15, _, _, _, _, _, _, _, _),
+ [53] = PINGROUP(53, qup15, _, _, _, _, _, _, _, _),
+ [54] = PINGROUP(54, qup15, qup14, _, _, _, _, _, _, _),
+ [55] = PINGROUP(55, qup15, qup14, _, _, _, _, _, _, _),
+ [56] = PINGROUP(56, qup16, ddr_bist, phase_flag, _, _, _, _, _, _),
+ [57] = PINGROUP(57, qup16, ddr_bist, phase_flag, _, _, _, _, _, _),
+ [58] = PINGROUP(58, qup16, ddr_bist, phase_flag, qdss, _, _, _, _, _),
+ [59] = PINGROUP(59, qup16, ddr_bist, phase_flag, qdss, _, _, _, _, _),
+ [60] = PINGROUP(60, qup17, edp_hot, _, phase_flag, _, _, _, _, _),
+ [61] = PINGROUP(61, qup17, sd_write, phase_flag, tsense_pwm1, tsense_pwm2, _, _, _, _),
+ [62] = PINGROUP(62, qup17, qup16, phase_flag, _, _, _, _, _, _),
+ [63] = PINGROUP(63, qup17, qup16, phase_flag, _, _, _, _, _, _),
+ [64] = PINGROUP(64, cam_mclk, _, _, _, _, _, _, _, _),
+ [65] = PINGROUP(65, cam_mclk, tgu_ch0, _, _, _, _, _, _, _),
+ [66] = PINGROUP(66, cam_mclk, pll_bypassnl, tgu_ch1, _, _, _, _, _, _),
+ [67] = PINGROUP(67, cam_mclk, pll_reset, _, _, _, _, _, _, _),
+ [68] = PINGROUP(68, cam_mclk, _, _, _, _, _, _, _, _),
+ [69] = PINGROUP(69, cci_i2c, _, _, _, _, _, _, _, _),
+ [70] = PINGROUP(70, cci_i2c, _, _, _, _, _, _, _, _),
+ [71] = PINGROUP(71, cci_i2c, _, _, _, _, _, _, _, _),
+ [72] = PINGROUP(72, cci_i2c, _, _, _, _, _, _, _, _),
+ [73] = PINGROUP(73, cci_i2c, _, _, _, _, _, _, _, _),
+ [74] = PINGROUP(74, cci_i2c, _, _, _, _, _, _, _, _),
+ [75] = PINGROUP(75, cci_i2c, _, _, _, _, _, _, _, _),
+ [76] = PINGROUP(76, cci_i2c, gcc_gp1, _, _, _, _, _, _, _),
+ [77] = PINGROUP(77, cci_timer2, gcc_gp2, _, atest_usb13, atest_char0, _, _, _, _),
+ [78] = PINGROUP(78, cci_timer3, cci_async, gcc_gp3, _, atest_usb12, atest_char1, _, _, _),
+ [79] = PINGROUP(79, cci_timer4, cci_async, pcie1_clkreqn, mdp_vsync, jitter_bist,
+ atest_usb11, atest_char2, _, _),
+ [80] = PINGROUP(80, mdp_vsync, vfr_0, mdp_vsync0, mdp_vsync1, mdp_vsync4, pll_bist,
+ atest_usb10, atest_char3, _),
+ [81] = PINGROUP(81, mdp_vsync, dp_lcd, mdp_vsync2, mdp_vsync3, mdp_vsync5, atest_usb1,
+ atest_char, _, _),
+ [82] = PINGROUP(82, _, _, _, _, _, _, _, _, _),
+ [83] = PINGROUP(83, _, _, _, _, _, _, _, _, _),
+ [84] = PINGROUP(84, usb2phy_ac, _, _, _, _, _, _, _, _),
+ [85] = PINGROUP(85, usb2phy_ac, _, _, _, _, _, _, _, _),
+ [86] = PINGROUP(86, _, _, _, _, _, _, _, _, _),
+ [87] = PINGROUP(87, _, _, _, _, _, _, _, _, _),
+ [88] = PINGROUP(88, pcie0_clkreqn, _, _, _, _, _, _, _, _),
+ [89] = PINGROUP(89, _, _, _, _, _, _, _, _, _),
+ [90] = PINGROUP(90, _, _, _, _, _, _, _, _, _),
+ [91] = PINGROUP(91, _, _, _, _, _, _, _, _, _),
+ [92] = PINGROUP(92, _, _, _, _, _, _, _, _, _),
+ [93] = PINGROUP(93, cam_mclk, cci_async, _, _, _, _, _, _, _),
+ [94] = PINGROUP(94, lpass_slimbus, _, _, _, _, _, _, _, _),
+ [95] = PINGROUP(95, lpass_slimbus, _, _, _, _, _, _, _, _),
+ [96] = PINGROUP(96, pri_mi2s, _, _, _, _, _, _, _, _),
+ [97] = PINGROUP(97, mi2s0_sck, _, _, _, _, _, _, _, _),
+ [98] = PINGROUP(98, mi2s0_data0, _, _, _, _, _, _, _, _),
+ [99] = PINGROUP(99, mi2s0_data1, _, _, _, _, _, _, _, _),
+ [100] = PINGROUP(100, mi2s0_ws, _, vsense_trigger, _, _, _, _, _, _),
+ [101] = PINGROUP(101, mi2s2_sck, _, qdss, _, _, _, _, _, _),
+ [102] = PINGROUP(102, mi2s2_data0, _, _, qdss, _, _, _, _, _),
+ [103] = PINGROUP(103, mi2s2_ws, vfr_1, _, _, qdss, _, atest_usb03, _, _),
+ [104] = PINGROUP(104, mi2s2_data1, _, _, qdss, _, atest_usb02, _, _, _),
+ [105] = PINGROUP(105, sec_mi2s, mi2s1_data1, audio_ref, gcc_gp1, _, qdss,
+ atest_usb01, _, _),
+ [106] = PINGROUP(106, mi2s1_sck, gcc_gp2, _, qdss, atest_usb00, _, _, _, _),
+ [107] = PINGROUP(107, mi2s1_data0, gcc_gp3, _, qdss, atest_usb0, _, _, _, _),
+ [108] = PINGROUP(108, mi2s1_ws, _, qdss, _, _, _, _, _, _),
+ [109] = PINGROUP(109, uim1_data, _, _, _, _, _, _, _, _),
+ [110] = PINGROUP(110, uim1_clk, _, _, _, _, _, _, _, _),
+ [111] = PINGROUP(111, uim1_reset, _, _, _, _, _, _, _, _),
+ [112] = PINGROUP(112, uim1_present, _, _, _, _, _, _, _, _),
+ [113] = PINGROUP(113, uim0_data, _, _, _, _, _, _, _, _),
+ [114] = PINGROUP(114, uim0_clk, _, _, _, _, _, _, _, _),
+ [115] = PINGROUP(115, uim0_reset, _, _, _, _, _, _, _, _),
+ [116] = PINGROUP(116, uim0_present, _, _, _, _, _, _, _, _),
+ [117] = PINGROUP(117, _, mss_grfc0, cmu_rng3, phase_flag, _, _, _, _, _),
+ [118] = PINGROUP(118, _, mss_grfc1, cmu_rng2, phase_flag, _, _, _, _, _),
+ [119] = PINGROUP(119, _, mss_grfc2, cmu_rng1, phase_flag, _, _, _, _, _),
+ [120] = PINGROUP(120, _, mss_grfc3, cmu_rng0, phase_flag, _, _, _, _, _),
+ [121] = PINGROUP(121, _, mss_grfc4, cri_trng0, phase_flag, _, _, _, _, _),
+ [122] = PINGROUP(122, _, mss_grfc5, cri_trng1, phase_flag, _, _, _, _, _),
+ [123] = PINGROUP(123, _, mss_grfc6, prng_rosc, phase_flag, _, _, _, _, _),
+ [124] = PINGROUP(124, _, mss_grfc7, cri_trng, phase_flag, _, _, _, _, _),
+ [125] = PINGROUP(125, _, mss_grfc8, phase_flag, _, _, _, _, _, _),
+ [126] = PINGROUP(126, _, mss_grfc9, phase_flag, _, _, _, _, _, _),
+ [127] = PINGROUP(127, coex_uart1, mss_grfc10, phase_flag, _, _, _, _, _, _),
+ [128] = PINGROUP(128, coex_uart1, mss_grfc11, phase_flag, _, _, _, _, _, _),
+ [129] = PINGROUP(129, nav_gpio0, phase_flag, _, _, _, _, _, _, _),
+ [130] = PINGROUP(130, nav_gpio1, phase_flag, _, _, _, _, _, _, _),
+ [131] = PINGROUP(131, mss_grfc12, nav_gpio2, pa_indicator, phase_flag, _, _, _, _, _),
+ [132] = PINGROUP(132, mss_grfc0, phase_flag, _, _, _, _, _, _, _),
+ [133] = PINGROUP(133, qlink0_request, _, _, _, _, _, _, _, _),
+ [134] = PINGROUP(134, qlink0_enable, _, _, _, _, _, _, _, _),
+ [135] = PINGROUP(135, qlink0_wmss, _, _, _, _, _, _, _, _),
+ [136] = PINGROUP(136, qlink1_request, _, _, _, _, _, _, _, _),
+ [137] = PINGROUP(137, qlink1_enable, _, _, _, _, _, _, _, _),
+ [138] = PINGROUP(138, qlink1_wmss, _, _, _, _, _, _, _, _),
+ [139] = PINGROUP(139, _, _, _, _, _, _, _, _, _),
+ [140] = PINGROUP(140, usb_phy, pll_clk, _, _, _, _, _, _, _),
+ [141] = PINGROUP(141, _, _, _, _, _, _, _, _, _),
+ [142] = PINGROUP(142, _, _, _, _, _, _, _, _, _),
+ [143] = PINGROUP(143, _, _, _, _, _, _, _, _, _),
+ [144] = PINGROUP(144, _, _, _, _, _, _, _, _, egpio),
+ [145] = PINGROUP(145, _, _, _, _, _, _, _, _, egpio),
+ [146] = PINGROUP(146, _, _, _, _, _, _, _, _, egpio),
+ [147] = PINGROUP(147, _, _, _, _, _, _, _, _, egpio),
+ [148] = PINGROUP(148, _, _, _, _, _, _, _, _, egpio),
+ [149] = PINGROUP(149, _, _, _, _, _, _, _, _, egpio),
+ [150] = PINGROUP(150, qdss, _, _, _, _, _, _, _, egpio),
+ [151] = PINGROUP(151, qdss, _, _, _, _, _, _, _, egpio),
+ [152] = PINGROUP(152, qdss, _, _, _, _, _, _, _, egpio),
+ [153] = PINGROUP(153, qdss, _, _, _, _, _, _, _, egpio),
+ [154] = PINGROUP(154, _, _, _, _, _, _, _, _, egpio),
+ [155] = PINGROUP(155, _, _, _, _, _, _, _, _, egpio),
+ [156] = PINGROUP(156, qdss_cti, _, _, _, _, _, _, _, egpio),
+ [157] = PINGROUP(157, qdss_cti, _, _, _, _, _, _, _, egpio),
+ [158] = PINGROUP(158, _, _, _, _, _, _, _, _, egpio),
+ [159] = PINGROUP(159, _, _, _, _, _, _, _, _, egpio),
+ [160] = PINGROUP(160, _, _, _, _, _, _, _, _, egpio),
+ [161] = PINGROUP(161, _, _, _, _, _, _, _, _, egpio),
+ [162] = PINGROUP(162, _, _, _, _, _, _, _, _, egpio),
+ [163] = PINGROUP(163, _, _, _, _, _, _, _, _, egpio),
+ [164] = PINGROUP(164, _, _, _, _, _, _, _, _, egpio),
+ [165] = PINGROUP(165, qdss_cti, _, _, _, _, _, _, _, egpio),
+ [166] = PINGROUP(166, qdss_cti, _, _, _, _, _, _, _, egpio),
+ [167] = PINGROUP(167, _, _, _, _, _, _, _, _, egpio),
+ [168] = PINGROUP(168, _, _, _, _, _, _, _, _, egpio),
+ [169] = PINGROUP(169, _, _, _, _, _, _, _, _, egpio),
+ [170] = PINGROUP(170, _, _, _, _, _, _, _, _, egpio),
+ [171] = PINGROUP(171, qdss, _, _, _, _, _, _, _, egpio),
+ [172] = PINGROUP(172, qdss, _, _, _, _, _, _, _, egpio),
+ [173] = PINGROUP(173, qdss, _, _, _, _, _, _, _, egpio),
+ [174] = PINGROUP(174, qdss, _, _, _, _, _, _, _, egpio),
+};
+
static const struct msm_special_pin_data sc7280_special_pins_data[] = {
[0] = UFS_RESET("ufs_reset", 0xbe000),
[1] = SDC_PINGROUP("sdc1_rclk", 0xb3004, 0, 6),
@@ -71,7 +568,20 @@ static const char *sc7280_get_pin_name(struct udevice *dev, unsigned int selecto
static int sc7280_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector)
{
- return msm_pinctrl_functions[selector].val;
+ unsigned int i;
+ const msm_pin_function *func = NULL;
+
+ if (pin >= ARRAY_SIZE(sc7280_pin_functions))
+ return -EINVAL;
+
+ func = sc7280_pin_functions + pin;
+ for (i = 0; i < 10; i++)
+ if ((*func)[i] == selector)
+ return i;
+
+ pr_err("Can't find requested function for pin %u pin\n", pin);
+
+ return -EINVAL;
}
static struct msm_pinctrl_data sc7280_data = {
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
index fd357ab0d4e..a8632aeaf8f 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -603,6 +603,7 @@ static const struct sunxi_pinctrl_function sun20i_d1_pinctrl_functions[] = {
{ "gpio_in", 0 },
{ "gpio_out", 1 },
{ "i2c0", 4 }, /* PB10-PB11 */
+ { "i2c3", 3 }, /* PG10-PG11 */
{ "mmc0", 2 }, /* PF0-PF5 */
{ "mmc1", 2 }, /* PG0-PG5 */
{ "mmc2", 3 }, /* PC2-PC7 */
diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig
index 2f63a8e54e5..4112b777371 100644
--- a/drivers/power/domain/Kconfig
+++ b/drivers/power/domain/Kconfig
@@ -90,6 +90,22 @@ config MESON_SECURE_POWER_DOMAIN
Enable support for manipulating Amlogic Meson Secure power domains.
Support for Amlogic A1 series.
+config QCOM_RPMH_POWER_DOMAIN
+ bool "Enable the QCOM RPMH Power domain driver"
+ depends on POWER_DOMAIN && ARCH_SNAPDRAGON
+ help
+ Generic RPMH power domain implementation for QCOM devices.
+ The RPMH power domain driver is responsible for managing power
+ domains on Qualcomm SoCs.
+
+config RENESAS_R8A78000_POWER_DOMAIN
+ bool "Enable the Renesas R-Car MDLC Power domain and reset driver"
+ depends on POWER_DOMAIN && ARCH_RENESAS
+ help
+ Enable support for Renesas R-Car R8A78000 X5H MDLC Power domain
+ and reset driver. The MDLC is responsible for managing both
+ power domains and resets on R-Car R8A78000 X5H SoC.
+
config SANDBOX_POWER_DOMAIN
bool "Enable the sandbox power domain test driver"
depends on POWER_DOMAIN && SANDBOX
diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile
index b2c0bd8a61a..110153d5cf8 100644
--- a/drivers/power/domain/Makefile
+++ b/drivers/power/domain/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_MTK_POWER_DOMAIN) += mtk-power-domain.o
obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o
obj-$(CONFIG_MESON_EE_POWER_DOMAIN) += meson-ee-pwrc.o
obj-$(CONFIG_MESON_SECURE_POWER_DOMAIN) += meson-secure-pwrc.o
+obj-$(CONFIG_RENESAS_R8A78000_POWER_DOMAIN) += renesas-r8a78000-power-domain.o
obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain.o
obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain-test.o
obj-$(CONFIG_SCMI_POWER_DOMAIN) += scmi-power-domain.o
@@ -23,3 +24,4 @@ obj-$(CONFIG_TI_SCI_POWER_DOMAIN) += ti-sci-power-domain.o
obj-$(CONFIG_TI_POWER_DOMAIN) += ti-power-domain.o
obj-$(CONFIG_TI_OMAP_PRM_POWER_DOMAIN) += ti-omap-prm.o
obj-$(CONFIG_ZYNQMP_POWER_DOMAIN) += zynqmp-power-domain.o
+obj-$(CONFIG_QCOM_RPMH_POWER_DOMAIN) += qcom-rpmhpd.o
diff --git a/drivers/power/domain/apple-pmgr.c b/drivers/power/domain/apple-pmgr.c
index bf9940621ee..71fedb405da 100644
--- a/drivers/power/domain/apple-pmgr.c
+++ b/drivers/power/domain/apple-pmgr.c
@@ -110,6 +110,7 @@ static int apple_pmgr_of_xlate(struct power_domain *power_domain,
}
static const struct udevice_id apple_pmgr_ids[] = {
+ { .compatible = "apple,t8103-pmgr-pwrstate" },
{ .compatible = "apple,pmgr-pwrstate" },
{ /* sentinel */ }
};
diff --git a/drivers/power/domain/power-domain-uclass.c b/drivers/power/domain/power-domain-uclass.c
index cea68945cbd..b4cda5f6c16 100644
--- a/drivers/power/domain/power-domain-uclass.c
+++ b/drivers/power/domain/power-domain-uclass.c
@@ -10,6 +10,7 @@
#include <malloc.h>
#include <power-domain.h>
#include <power-domain-uclass.h>
+#include <dm/device_compat.h>
#include <dm/device-internal.h>
struct power_domain_priv {
@@ -187,6 +188,12 @@ static int dev_power_domain_ctrl(struct udevice *dev, bool on)
"#power-domain-cells", 0);
for (i = 0; i < count; i++) {
ret = power_domain_get_by_index(dev, &pd, i);
+
+ if (ret == -ENODEV) {
+ dev_warn(dev, "power-domain driver not found\n");
+ return 0;
+ }
+
if (ret)
return ret;
if (on)
diff --git a/drivers/power/domain/qcom-rpmhpd.c b/drivers/power/domain/qcom-rpmhpd.c
new file mode 100644
index 00000000000..f51bc9a4bbb
--- /dev/null
+++ b/drivers/power/domain/qcom-rpmhpd.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018, The Linux Foundation. All rights reserved.
+// Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
+
+#include <dm.h>
+#include <dm/lists.h>
+#include <power-domain.h>
+#include <asm/io.h>
+#include <linux/errno.h>
+
+#include <power-domain-uclass.h>
+#include <soc/qcom/cmd-db.h>
+#include <soc/qcom/rpmh.h>
+#include <dt-bindings/power/qcom-rpmpd.h>
+#include <dm/device_compat.h>
+
+#define RPMH_ARC_MAX_LEVELS 16
+
+/**
+ * struct rpmhpd - top level RPMh power domain resource data structure
+ * @dev: rpmh power domain controller device
+ * @pd: generic_pm_domain corresponding to the power domain
+ * @parent: generic_pm_domain corresponding to the parent's power domain
+ * @enable_corner: lowest non-zero corner
+ * @level: An array of level (vlvl) to corner (hlvl) mappings
+ * derived from cmd-db
+ * @level_count: Number of levels supported by the power domain. max
+ * being 16 (0 - 15)
+ * @enabled: true if the power domain is enabled
+ * @res_name: Resource name used for cmd-db lookup
+ * @addr: Resource address as looped up using resource name from
+ * @skip_retention_level: Indicate that retention level should not be used for the power domain
+ */
+struct rpmhpd {
+ struct udevice *dev;
+ struct power_domain pd;
+ struct power_domain *parent;
+ unsigned int enable_corner;
+ u32 level[RPMH_ARC_MAX_LEVELS];
+ size_t level_count;
+ bool enabled;
+ const char *res_name;
+ u32 addr;
+ bool skip_retention_level;
+};
+
+struct rpmhpd_desc {
+ struct rpmhpd **rpmhpds;
+ size_t num_pds;
+};
+
+/* RPMH powerdomains */
+static struct rpmhpd mmcx_ao;
+static struct rpmhpd mmcx = {
+ .res_name = "mmcx.lvl",
+};
+
+static struct rpmhpd mmcx_ao = {
+ .res_name = "mmcx.lvl",
+};
+
+/* SA8775P RPMH power domains */
+static struct rpmhpd *sa8775p_rpmhpds[] = {
+ [SA8775P_MMCX] = &mmcx,
+ [SA8775P_MMCX_AO] = &mmcx_ao,
+};
+
+static const struct rpmhpd_desc sa8775p_desc = {
+ .rpmhpds = sa8775p_rpmhpds,
+ .num_pds = ARRAY_SIZE(sa8775p_rpmhpds),
+};
+
+/* stub RPMH power domains mapped for unsupported platforms */
+static struct rpmhpd *stub_rpmhpds[] = {};
+
+static const struct rpmhpd_desc stub_desc = {
+ .rpmhpds = stub_rpmhpds,
+ .num_pds = ARRAY_SIZE(stub_rpmhpds),
+};
+
+static const struct udevice_id rpmhpd_match_table[] = {
+ { .compatible = "qcom,sa8775p-rpmhpd", .data = (ulong)&sa8775p_desc },
+ { .compatible = "qcom,qcs615-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,qcs8300-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,qdu1000-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sa8155p-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sa8540p-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sar2130p-rpmhpd", .data = (ulong)&stub_desc},
+ { .compatible = "qcom,sc7180-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sc7280-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sc8180x-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sc8280xp-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sdm670-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sdm845-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sdx55-rpmhpd", .data = (ulong)&stub_desc},
+ { .compatible = "qcom,sdx65-rpmhpd", .data = (ulong)&stub_desc},
+ { .compatible = "qcom,sdx75-rpmhpd", .data = (ulong)&stub_desc},
+ { .compatible = "qcom,sm4450-rpmhpd", .data = (ulong)&stub_desc},
+ { .compatible = "qcom,sm6350-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm7150-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8150-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8250-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8350-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8450-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8550-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8650-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8750-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,x1e80100-rpmhpd", .data = (ulong)&stub_desc },
+ { }
+};
+
+static int rpmhpd_send_corner(struct rpmhpd *pd, int state,
+ unsigned int corner, bool sync)
+{
+ struct tcs_cmd cmd = {
+ .addr = pd->addr,
+ .data = corner,
+ };
+
+ return rpmh_write(pd->dev, state, &cmd, 1);
+}
+
+static int rpmhpd_power_on(struct power_domain *pd)
+{
+ int ret;
+ unsigned int corner;
+ struct rpmhpd **rpmhpds;
+ const struct rpmhpd_desc *desc;
+ struct rpmhpd *curr_rpmhpd;
+
+ desc = (const struct rpmhpd_desc *)dev_get_driver_data(pd->dev);
+ if (!desc)
+ return -EINVAL;
+
+ rpmhpds = desc->rpmhpds;
+ curr_rpmhpd = rpmhpds[pd->id];
+
+ /* Do nothing for undefined power domains */
+ if (!curr_rpmhpd) {
+ log_warning("Power domain id (%ld) not supported\n",
+ pd->id);
+ return 0;
+ }
+
+ corner = curr_rpmhpd->enable_corner;
+
+ ret = rpmhpd_send_corner(curr_rpmhpd, RPMH_ACTIVE_ONLY_STATE, corner,
+ false);
+ if (!ret)
+ curr_rpmhpd->enabled = true;
+
+ return ret;
+}
+
+static int rpmhpd_power_off(struct power_domain *pd)
+{
+ int ret;
+ unsigned int corner;
+ struct rpmhpd **rpmhpds;
+ const struct rpmhpd_desc *desc;
+ struct rpmhpd *curr_rpmhpd;
+
+ desc = (const struct rpmhpd_desc *)dev_get_driver_data(pd->dev);
+ if (!desc)
+ return -EINVAL;
+
+ rpmhpds = desc->rpmhpds;
+ curr_rpmhpd = rpmhpds[pd->id];
+
+ /* Do nothing for undefined power domains */
+ if (!curr_rpmhpd) {
+ log_warning("Power domain id (%ld) not supported\n",
+ pd->id);
+ return 0;
+ }
+
+ corner = 0;
+
+ ret = rpmhpd_send_corner(curr_rpmhpd, RPMH_ACTIVE_ONLY_STATE, corner,
+ false);
+ if (!ret)
+ curr_rpmhpd->enabled = false;
+
+ return ret;
+}
+
+static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd)
+{
+ int i;
+ const u16 *buf;
+
+ buf = cmd_db_read_aux_data(rpmhpd->res_name, &rpmhpd->level_count);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ /* 2 bytes used for each command DB aux data entry */
+ rpmhpd->level_count >>= 1;
+
+ if (rpmhpd->level_count > RPMH_ARC_MAX_LEVELS)
+ return -EINVAL;
+
+ for (i = 0; i < rpmhpd->level_count; i++) {
+ if (rpmhpd->skip_retention_level && buf[i] == RPMH_REGULATOR_LEVEL_RETENTION)
+ continue;
+
+ rpmhpd->level[i] = buf[i];
+
+ /* Remember the first corner with non-zero level */
+ if (!rpmhpd->level[rpmhpd->enable_corner] && rpmhpd->level[i])
+ rpmhpd->enable_corner = i;
+
+ /*
+ * The AUX data may be zero padded. These 0 valued entries at
+ * the end of the map must be ignored.
+ */
+ if (i > 0 && rpmhpd->level[i] == 0) {
+ rpmhpd->level_count = i;
+ break;
+ }
+ debug("%s: ARC hlvl=%2d --> vlvl=%4u\n", rpmhpd->res_name, i,
+ rpmhpd->level[i]);
+ }
+
+ return 0;
+}
+
+static int rpmhpd_probe(struct udevice *dev)
+{
+ int i, ret = 0;
+ struct rpmhpd **rpmhpds;
+ struct rpmhpd *priv;
+ const struct rpmhpd_desc *desc;
+
+ desc = (const struct rpmhpd_desc *)dev_get_driver_data(dev);
+ if (!desc)
+ return -EINVAL;
+
+ rpmhpds = desc->rpmhpds;
+
+ for (i = 0; i < desc->num_pds; i++) {
+ if (!rpmhpds[i])
+ continue;
+
+ priv = rpmhpds[i];
+ priv->dev = dev;
+ priv->addr = cmd_db_read_addr(priv->res_name);
+ if (!priv->addr) {
+ dev_err(dev, "Could not find RPMh address for resource %s\n",
+ priv->res_name);
+ return -ENODEV;
+ }
+
+ ret = cmd_db_read_slave_id(priv->res_name);
+ if (ret != CMD_DB_HW_ARC) {
+ dev_err(dev, "RPMh slave ID mismatch\n");
+ return -EINVAL;
+ }
+
+ ret = rpmhpd_update_level_mapping(priv);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+static const struct power_domain_ops qcom_rpmhpd_power_ops = {
+ .on = rpmhpd_power_on,
+ .off = rpmhpd_power_off,
+};
+
+U_BOOT_DRIVER(qcom_rpmhpd_drv) = {
+ .name = "qcom_rpmhpd_drv",
+ .id = UCLASS_POWER_DOMAIN,
+ .of_match = rpmhpd_match_table,
+ .probe = rpmhpd_probe,
+ .ops = &qcom_rpmhpd_power_ops,
+};
diff --git a/drivers/power/domain/renesas-r8a78000-power-domain.c b/drivers/power/domain/renesas-r8a78000-power-domain.c
new file mode 100644
index 00000000000..d621373f90d
--- /dev/null
+++ b/drivers/power/domain/renesas-r8a78000-power-domain.c
@@ -0,0 +1,427 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas R-Car Gen5 MDLC driver
+ *
+ * Copyright (C) 2026 Marek Vasut <[email protected]>
+ */
+
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <power-domain-uclass.h>
+#include <reset-uclass.h>
+
+#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
+#include <scmi_protocols.h>
+
+#include <dt-bindings/power/r8a78000-power-scmi.h>
+#include <dt-bindings/reset/r8a78000-reset-scmi.h>
+
+#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
+#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
+
+#define PKC_PROT_LOCK 0xa5a5a500
+#define PKC_PROT_UNLOCK 0xa5a5a501
+
+#define MDLC_MSRESS_STANDBY 0
+#define MDLC_MSRESS_RESET 1
+#define MDLC_MSRESS_STOP 2
+#define MDLC_MSRESS_RUN 3
+
+#define MDLC_MSRES00 0x900
+#define MDLC_MSRESS00 0x960
+#define MDLC_PKCPROT1 0xcf4
+
+struct gen5_mdlc_priv {
+#if IS_ENABLED(CONFIG_SCMI_POWER_DOMAIN)
+ struct udevice *pd;
+#endif
+#if IS_ENABLED(CONFIG_RESET_SCMI)
+ struct udevice *rst;
+#endif
+#if IS_ENABLED(CONFIG_SCMI_POWER_DOMAIN) || IS_ENABLED(CONFIG_RESET_SCMI)
+ u32 basever;
+#endif
+#if !IS_ENABLED(CONFIG_SCMI_POWER_DOMAIN) && !IS_ENABLED(CONFIG_RESET_SCMI)
+ void __iomem *base;
+#endif
+};
+
+static int gen5_pd_of_xlate(struct power_domain *power_domain,
+ struct ofnode_phandle_args *args)
+{
+ /* Perform direct remap until the bindings stabilize. */
+ power_domain->id = args->args[0];
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_SCMI_POWER_DOMAIN)
+static int gen5_pd_on(struct power_domain *power_domain)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(power_domain->dev->parent);
+ struct power_domain_ops *ops = (struct power_domain_ops *)priv->pd->driver->ops;
+ struct power_domain scmi = {
+ .dev = priv->pd,
+ .id = power_domain->id
+ };
+
+ return ops->on(&scmi);
+}
+
+static int gen5_pd_off(struct power_domain *power_domain)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(power_domain->dev->parent);
+ struct power_domain_ops *ops = (struct power_domain_ops *)priv->pd->driver->ops;
+ struct power_domain scmi = {
+ .dev = priv->pd,
+ .id = power_domain->id
+ };
+
+ return ops->off(&scmi);
+}
+
+static const struct power_domain_ops pd_gen5_ops = {
+ .on = gen5_pd_on,
+ .off = gen5_pd_off,
+ .of_xlate = gen5_pd_of_xlate,
+};
+
+static int gen5_pd_probe(struct udevice *dev)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(dev->parent);
+ struct udevice *agent;
+ int ret;
+
+ if (!priv->basever) {
+ ret = uclass_get_device(UCLASS_SCMI_AGENT, 0, &agent);
+ if (ret)
+ return ret;
+
+ if (!agent)
+ return -ENODEV;
+
+ priv->basever = scmi_impl_version(agent);
+ }
+
+ return uclass_get_device_by_driver(UCLASS_POWER_DOMAIN,
+ DM_DRIVER_GET(scmi_power_domain),
+ &priv->pd);
+}
+
+U_BOOT_DRIVER(pd_gen5) = {
+ .name = "pd_gen5",
+ .id = UCLASS_POWER_DOMAIN,
+ .ops = &pd_gen5_ops,
+ .probe = gen5_pd_probe,
+ .flags = DM_FLAG_OS_PREPARE | DM_FLAG_VITAL,
+};
+#else
+static const struct power_domain_ops pd_gen5_ops = {
+ .of_xlate = gen5_pd_of_xlate,
+};
+
+U_BOOT_DRIVER(pd_gen5) = {
+ .name = "pd_gen5",
+ .id = UCLASS_POWER_DOMAIN,
+ .ops = &pd_gen5_ops,
+ .flags = DM_FLAG_OS_PREPARE | DM_FLAG_VITAL,
+};
+#endif
+
+#if IS_ENABLED(CONFIG_RESET_SCMI)
+static int gen5_reset_assert(struct reset_ctl *reset_ctl)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(reset_ctl->dev->parent);
+ struct reset_ops *ops = (struct reset_ops *)priv->rst->driver->ops;
+ struct reset_ctl scmi = {
+ .dev = priv->rst,
+ .id = reset_ctl->id
+ };
+
+ return ops->rst_assert(&scmi);
+}
+
+static int gen5_reset_deassert(struct reset_ctl *reset_ctl)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(reset_ctl->dev->parent);
+ struct reset_ops *ops = (struct reset_ops *)priv->rst->driver->ops;
+ struct reset_ctl scmi = {
+ .dev = priv->rst,
+ .id = reset_ctl->id
+ };
+
+ return ops->rst_deassert(&scmi);
+}
+
+struct rst_map_in {
+ u16 dt_id; /* DT binding clock ID */
+ u16 fw_id; /* SCMI firmware clock ID */
+};
+
+#define GEN5_SCMI_SDK_4_28 0x010a0000
+#define GEN5_SCMI_SDK_4_29 0x010b0000
+#define GEN5_SCMI_SDK_4_30 0x010c0000
+#define GEN5_SCMI_SDK_4_31 0x010d0000
+#define GEN5_SCMI_SDK_4_32 0x010e0000
+
+static const struct rst_map_in gen5_rst_map_dt_sdk_4_28[] = {
+ { SCP_RESET_DOMAIN_ID_UFS0, 202 },
+ { SCP_RESET_DOMAIN_ID_UFS1, 203 },
+ { SCP_RESET_DOMAIN_ID_XPCS0, 316 },
+ { SCP_RESET_DOMAIN_ID_XPCS1, 317 },
+ { SCP_RESET_DOMAIN_ID_XPCS2, 318 },
+ { SCP_RESET_DOMAIN_ID_XPCS3, 319 },
+ { SCP_RESET_DOMAIN_ID_XPCS4, 320 },
+ { SCP_RESET_DOMAIN_ID_XPCS5, 321 },
+ { SCP_RESET_DOMAIN_ID_XPCS6, 322 },
+ { SCP_RESET_DOMAIN_ID_XPCS7, 323 },
+ { SCP_RESET_DOMAIN_ID_MPPHY01, 344 },
+ { SCP_RESET_DOMAIN_ID_MPPHY11, 345 },
+ { SCP_RESET_DOMAIN_ID_MPPHY21, 346 },
+ { SCP_RESET_DOMAIN_ID_MPPHY31, 347 },
+ { SCP_RESET_DOMAIN_ID_MPPHY02, 348 },
+};
+
+static const struct rst_map_in gen5_rst_map_dt_sdk_4_31[] = {
+ { SCP_RESET_DOMAIN_ID_UFS0, 198 },
+ { SCP_RESET_DOMAIN_ID_UFS1, 199 },
+ { SCP_RESET_DOMAIN_ID_XPCS0, 312 },
+ { SCP_RESET_DOMAIN_ID_XPCS1, 313 },
+ { SCP_RESET_DOMAIN_ID_XPCS2, 314 },
+ { SCP_RESET_DOMAIN_ID_XPCS3, 315 },
+ { SCP_RESET_DOMAIN_ID_XPCS4, 316 },
+ { SCP_RESET_DOMAIN_ID_XPCS5, 317 },
+ { SCP_RESET_DOMAIN_ID_XPCS6, 318 },
+ { SCP_RESET_DOMAIN_ID_XPCS7, 319 },
+ { SCP_RESET_DOMAIN_ID_MPPHY01, 340 },
+ { SCP_RESET_DOMAIN_ID_MPPHY11, 341 },
+ { SCP_RESET_DOMAIN_ID_MPPHY21, 342 },
+ { SCP_RESET_DOMAIN_ID_MPPHY31, 343 },
+ { SCP_RESET_DOMAIN_ID_MPPHY02, 344 },
+};
+
+static int gen5_reset_of_xlate(struct reset_ctl *reset_ctl,
+ struct ofnode_phandle_args *args)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(reset_ctl->dev->parent);
+ const struct rst_map_in *map;
+ unsigned int map_size;
+ int i;
+
+ if (args->args_count != 1) {
+ debug("Invalid args_count: %d\n", args->args_count);
+ return -EINVAL;
+ }
+
+ if (priv->basever == GEN5_SCMI_SDK_4_28) {
+ map = gen5_rst_map_dt_sdk_4_28;
+ map_size = ARRAY_SIZE(gen5_rst_map_dt_sdk_4_28);
+ } else if (priv->basever == GEN5_SCMI_SDK_4_31 ||
+ priv->basever == GEN5_SCMI_SDK_4_32) {
+ map = gen5_rst_map_dt_sdk_4_31;
+ map_size = ARRAY_SIZE(gen5_rst_map_dt_sdk_4_31);
+ } else {
+ printf("Unsupported SCMI base protocol version %x\n", priv->basever);
+ return -EINVAL;
+ }
+
+ reset_ctl->id = -1;
+ for (i = 0; i < map_size; i++) {
+ if (map[i].dt_id != args->args[0])
+ continue;
+ reset_ctl->id = map[i].fw_id;
+ break;
+ }
+
+ return 0;
+}
+
+static const struct reset_ops rst_gen5_ops = {
+ .rst_assert = gen5_reset_assert,
+ .rst_deassert = gen5_reset_deassert,
+ .of_xlate = gen5_reset_of_xlate,
+};
+
+static int gen5_rst_probe(struct udevice *dev)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(dev->parent);
+ struct udevice *agent;
+ int ret = 0;
+
+ if (!priv->basever) {
+ ret = uclass_get_device(UCLASS_SCMI_AGENT, 0, &agent);
+ if (ret)
+ return ret;
+
+ if (!agent)
+ return -ENODEV;
+
+ priv->basever = scmi_impl_version(agent);
+ }
+
+ return uclass_get_device_by_driver(UCLASS_RESET,
+ DM_DRIVER_GET(scmi_reset_domain),
+ &priv->rst);
+}
+#else
+static int mdlc_wait_for_reset(struct reset_ctl *reset_ctl)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(reset_ctl->dev->parent);
+ const u32 offset = (reset_ctl->id / 16) * 4;
+ void __iomem *res = priv->base + MDLC_MSRES00 + offset;
+ void __iomem *stat = priv->base + MDLC_MSRESS00 + offset;
+ u32 val;
+ int ret;
+
+ /* Wait 100ms for reset controller to synchronize. */
+ ret = readl_poll_timeout(res, val, val == readl(stat), 100000);
+ if (ret < 0)
+ dev_err(reset_ctl->dev, "Reset controller out of sync!\n");
+
+ return ret;
+}
+
+static void mdlc_rmw_msres(struct reset_ctl *reset_ctl, const int val)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(reset_ctl->dev->parent);
+ const u32 offset = (reset_ctl->id / 16) * 4;
+ const u32 mask = 3 << ((reset_ctl->id % 16) * 2);
+ void __iomem *prot = priv->base + MDLC_PKCPROT1;
+ void __iomem *res = priv->base + MDLC_MSRES00 + offset;
+ u32 reg;
+
+ reg = readl(res);
+ reg &= ~mask;
+ reg |= field_prep(mask, val);
+
+ writel(PKC_PROT_UNLOCK, prot);
+ writel(reg, res);
+ writel(PKC_PROT_LOCK, prot);
+}
+
+static int gen5_reset_toggle(struct reset_ctl *reset_ctl, const u8 step1,
+ const u8 step2, const u8 step3, const u8 step4)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(reset_ctl->dev->parent);
+ const u32 offset = (reset_ctl->id / 16) * 4;
+ const u32 mask = 3 << ((reset_ctl->id % 16) * 2);
+ void __iomem *stat = priv->base + MDLC_MSRESS00 + offset;
+ u32 status;
+ int ret;
+
+ ret = mdlc_wait_for_reset(reset_ctl);
+ if (ret)
+ return ret;
+
+ status = field_get(mask, readl(stat));
+ if (status == step1) {
+ mdlc_rmw_msres(reset_ctl, step2);
+ ret = mdlc_wait_for_reset(reset_ctl);
+ if (ret)
+ return ret;
+ status = field_get(mask, readl(stat));
+ }
+
+ if (status == step2 || status == step3) {
+ mdlc_rmw_msres(reset_ctl, step4);
+ ret = mdlc_wait_for_reset(reset_ctl);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int gen5_reset_assert(struct reset_ctl *reset_ctl)
+{
+ return gen5_reset_toggle(reset_ctl,
+ MDLC_MSRESS_STOP, MDLC_MSRESS_STANDBY,
+ MDLC_MSRESS_RUN, MDLC_MSRESS_RESET);
+}
+
+static int gen5_reset_deassert(struct reset_ctl *reset_ctl)
+{
+ return gen5_reset_toggle(reset_ctl,
+ MDLC_MSRESS_STANDBY, MDLC_MSRESS_RESET,
+ MDLC_MSRESS_STOP, MDLC_MSRESS_RUN);
+}
+
+static int gen5_reset_of_xlate(struct reset_ctl *reset_ctl,
+ struct ofnode_phandle_args *args)
+{
+ /* Perform direct remap until the bindings stabilize. */
+ reset_ctl->id = args->args[0];
+
+ return 0;
+}
+
+static const struct reset_ops rst_gen5_ops = {
+ .rst_assert = gen5_reset_assert,
+ .rst_deassert = gen5_reset_deassert,
+ .of_xlate = gen5_reset_of_xlate,
+};
+
+static int gen5_rst_probe(struct udevice *dev)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(dev->parent);
+
+ priv->base = dev_read_addr_ptr(dev);
+ if (!priv->base)
+ return -EINVAL;
+
+ return 0;
+}
+#endif
+
+U_BOOT_DRIVER(rst_gen5) = {
+ .name = "rst_gen5",
+ .id = UCLASS_RESET,
+ .ops = &rst_gen5_ops,
+ .probe = gen5_rst_probe,
+ .flags = DM_FLAG_OS_PREPARE | DM_FLAG_VITAL,
+};
+
+int gen5_mdlc_bind(struct udevice *parent)
+{
+ struct udevice *pdev, *rdev;
+ struct driver *pdrv, *rdrv;
+ int ret;
+
+ pdrv = lists_driver_lookup_name("pd_gen5");
+ if (!pdrv)
+ return -ENOENT;
+
+ rdrv = lists_driver_lookup_name("rst_gen5");
+ if (!rdrv)
+ return -ENOENT;
+
+ ret = device_bind_with_driver_data(parent, pdrv, "pd_gen5", 0,
+ dev_ofnode(parent), &pdev);
+ if (ret)
+ return ret;
+
+ ret = device_bind_with_driver_data(parent, rdrv, "rst_gen5", (ulong)pdev,
+ dev_ofnode(parent), &rdev);
+ if (ret)
+ device_unbind(pdev);
+
+ return ret;
+}
+
+static const struct udevice_id r8a78000_mdlc_ids[] = {
+ { .compatible = "renesas,r8a78000-mdlc", },
+ { }
+};
+
+U_BOOT_DRIVER(mdlc_gen5) = {
+ .name = "mdlc_gen5",
+ .id = UCLASS_NOP,
+ .of_match = r8a78000_mdlc_ids,
+ .bind = gen5_mdlc_bind,
+ .priv_auto = sizeof(struct gen5_mdlc_priv),
+};
diff --git a/drivers/power/pmic/mtk-pwrap.c b/drivers/power/pmic/mtk-pwrap.c
index 3e3a691d9e8..47347785519 100644
--- a/drivers/power/pmic/mtk-pwrap.c
+++ b/drivers/power/pmic/mtk-pwrap.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * MT6357 regulator driver
+ * MediaTek PMIC Wrapper driver
*
* Copyright (c) 2026 BayLibre, SAS.
* Author: Julien Masson <[email protected]>
@@ -59,9 +59,11 @@ static const struct pmic_child_info mt6359_pmic_children_info[] = {
#define HAS_CAP(_c, _x_val) (((_c) & (_x_val)) == (_x_val))
/* Group of bits used for shown pwrap capability */
-#define PWRAP_CAP_INT1_EN BIT(3)
-#define PWRAP_CAP_WDT_SRC1 BIT(4)
-#define PWRAP_CAP_ARB BIT(5)
+#define PWRAP_CAP_WDT_SRC BIT(4)
+#define PWRAP_CAP_WDT_SRC1 BIT(5)
+#define PWRAP_CAP_ARB BIT(6)
+/* To implement this capability, the registers used in pwrap_init() must be defined. */
+#define PWRAP_CAP_INIT BIT(7)
/* defines for slave device wrapper registers */
enum dew_regs {
@@ -249,20 +251,20 @@ enum pwrap_regs {
};
static int mt8188_regs[] = {
- [PWRAP_INIT_DONE2] = 0x0,
- [PWRAP_STAUPD_CTRL] = 0x4C,
- [PWRAP_TIMER_EN] = 0x3E4,
- [PWRAP_INT_EN] = 0x420,
- [PWRAP_INT_FLG] = 0x428,
- [PWRAP_INT_CLR] = 0x42C,
- [PWRAP_INT1_EN] = 0x450,
- [PWRAP_INT1_FLG] = 0x458,
- [PWRAP_INT1_CLR] = 0x45C,
- [PWRAP_WACS2_CMD] = 0x880,
- [PWRAP_SWINF_2_WDATA_31_0] = 0x884,
- [PWRAP_SWINF_2_RDATA_31_0] = 0x894,
- [PWRAP_WACS2_VLDCLR] = 0x8A4,
- [PWRAP_WACS2_RDATA] = 0x8A8,
+ [PWRAP_INIT_DONE2] = 0x0,
+ [PWRAP_STAUPD_CTRL] = 0x4C,
+ [PWRAP_TIMER_EN] = 0x3E4,
+ [PWRAP_INT_EN] = 0x420,
+ [PWRAP_INT_FLG] = 0x428,
+ [PWRAP_INT_CLR] = 0x42C,
+ [PWRAP_INT1_EN] = 0x450,
+ [PWRAP_INT1_FLG] = 0x458,
+ [PWRAP_INT1_CLR] = 0x45C,
+ [PWRAP_WACS2_CMD] = 0x880,
+ [PWRAP_SWINF_2_WDATA_31_0] = 0x884,
+ [PWRAP_SWINF_2_RDATA_31_0] = 0x894,
+ [PWRAP_WACS2_VLDCLR] = 0x8A4,
+ [PWRAP_WACS2_RDATA] = 0x8A8,
};
static int mt8189_regs[] = {
@@ -276,6 +278,23 @@ static int mt8189_regs[] = {
[PWRAP_WACS2_RDATA] = 0x8A8,
};
+static int mt8195_regs[] = {
+ [PWRAP_INIT_DONE2] = 0x0,
+ [PWRAP_STAUPD_CTRL] = 0x4C,
+ [PWRAP_TIMER_EN] = 0x3E4,
+ [PWRAP_INT_EN] = 0x420,
+ [PWRAP_INT_FLG] = 0x428,
+ [PWRAP_INT_CLR] = 0x42C,
+ [PWRAP_INT1_EN] = 0x450,
+ [PWRAP_INT1_FLG] = 0x458,
+ [PWRAP_INT1_CLR] = 0x45C,
+ [PWRAP_WACS2_CMD] = 0x880,
+ [PWRAP_SWINF_2_WDATA_31_0] = 0x884,
+ [PWRAP_SWINF_2_RDATA_31_0] = 0x894,
+ [PWRAP_WACS2_VLDCLR] = 0x8A4,
+ [PWRAP_WACS2_RDATA] = 0x8A8,
+};
+
static int mt8365_regs[] = {
[PWRAP_MUX_SEL] = 0x0,
[PWRAP_WRAP_EN] = 0x4,
@@ -338,12 +357,6 @@ static int mt8365_regs[] = {
[PWRAP_WDT_SRC_EN_1] = 0xf8,
};
-enum pwrap_type {
- PWRAP_MT8188,
- PWRAP_MT8189,
- PWRAP_MT8365,
-};
-
struct pwrap_slv_type {
const u32 *dew_regs;
u32 caps;
@@ -362,10 +375,7 @@ struct pmic_wrapper {
struct pmic_wrapper_type {
int *regs;
- enum pwrap_type type;
u32 arb_en_all;
- u32 int_en_all;
- u32 int1_en_all;
u32 spi_w;
u32 wdt_src;
/* Flags indicating the capability for the target pwrap */
@@ -644,7 +654,7 @@ static const struct pwrap_slv_type pmic_mt6357 = {
static const struct pwrap_slv_type pmic_mt6359 = {
.dew_regs = mt6359_regs,
- .caps = PWRAP_SLV_CAP_DUALIO,
+ .caps = 0,
};
static const struct udevice_id mtk_pmic_ids[] = {
@@ -734,6 +744,11 @@ static int mtk_pwrap_probe(struct udevice *dev)
* Skip initialization here in this case.
*/
if (!pwrap_readl(wrp, PWRAP_INIT_DONE2)) {
+ if (!HAS_CAP(wrp->master->caps, PWRAP_CAP_INIT)) {
+ dev_err(dev, "initialization is required but not supported\n");
+ return -EOPNOTSUPP;
+ }
+
ret = pwrap_init(wrp);
if (ret) {
dev_err(dev, "init failed with %d\n", ret);
@@ -755,7 +770,8 @@ static int mtk_pwrap_probe(struct udevice *dev)
* Since STAUPD was not used on mt8173 platform,
* so STAUPD of WDT_SRC which should be turned off
*/
- pwrap_writel(wrp, wrp->master->wdt_src, PWRAP_WDT_SRC_EN);
+ if (HAS_CAP(wrp->master->caps, PWRAP_CAP_WDT_SRC))
+ pwrap_writel(wrp, wrp->master->wdt_src, PWRAP_WDT_SRC_EN);
if (HAS_CAP(wrp->master->caps, PWRAP_CAP_WDT_SRC1))
pwrap_writel(wrp, wrp->master->wdt_src, PWRAP_WDT_SRC_EN_1);
@@ -765,15 +781,6 @@ static int mtk_pwrap_probe(struct udevice *dev)
else
pwrap_writel(wrp, 0x1, PWRAP_TIMER_EN);
- pwrap_writel(wrp, wrp->master->int_en_all, PWRAP_INT_EN);
-
- /*
- * We add INT1 interrupt to handle starvation and request exception
- * If we support it, we should enable it here.
- */
- if (HAS_CAP(wrp->master->caps, PWRAP_CAP_INT1_EN))
- pwrap_writel(wrp, wrp->master->int1_en_all, PWRAP_INT1_EN);
-
return 0;
}
@@ -782,7 +789,6 @@ static int mtk_pwrap_bind(struct udevice *dev)
ofnode pmic_node, regulators_node;
int children;
const struct pmic_child_info *pmic_children_info;
- struct pmic_wrapper_type *pw_type = (void *)dev_get_driver_data(dev);
pmic_node = dev_read_first_subnode(dev);
if (!ofnode_valid(pmic_node)) {
@@ -790,16 +796,13 @@ static int mtk_pwrap_bind(struct udevice *dev)
return -ENXIO;
}
- switch (pw_type->type) {
- case PWRAP_MT8365:
+ if (ofnode_device_is_compatible(pmic_node, "mediatek,mt6357")) {
pmic_children_info = mt6357_pmic_children_info;
- break;
- case PWRAP_MT8188:
- case PWRAP_MT8189:
+ } else if (ofnode_device_is_compatible(pmic_node, "mediatek,mt6359")) {
pmic_children_info = mt6359_pmic_children_info;
- break;
- default:
- dev_err(dev, "pwrap type %d not supported\n", pw_type->type);
+ } else {
+ dev_err(dev, "pmic type %s not supported\n",
+ ofnode_read_string(pmic_node, "compatible"));
return -ENXIO;
}
@@ -848,20 +851,23 @@ static struct dm_pmic_ops mtk_pwrap_ops = {
static struct pmic_wrapper_type pwrap_mt8188 = {
.regs = mt8188_regs,
- .type = PWRAP_MT8188,
.arb_en_all = 0x777f,
- .int_en_all = 0x180000,
- .int1_en_all = 0x0,
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.wdt_src = PWRAP_WDT_SRC_MASK_ALL,
- .caps = PWRAP_CAP_INT1_EN | PWRAP_CAP_ARB,
+ .caps = PWRAP_CAP_ARB,
};
static struct pmic_wrapper_type pwrap_mt8189 = {
.regs = mt8189_regs,
- .type = PWRAP_MT8189,
.arb_en_all = 0x777f,
- .int_en_all = 0x180000,
+ .spi_w = PWRAP_MAN_CMD_SPI_WRITE,
+ .wdt_src = PWRAP_WDT_SRC_MASK_ALL,
+ .caps = PWRAP_CAP_ARB,
+};
+
+static const struct pmic_wrapper_type pwrap_mt8195 = {
+ .regs = mt8195_regs,
+ .arb_en_all = 0x777f, /* NEED CONFIRM */
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.wdt_src = PWRAP_WDT_SRC_MASK_ALL,
.caps = PWRAP_CAP_ARB,
@@ -869,18 +875,16 @@ static struct pmic_wrapper_type pwrap_mt8189 = {
static const struct pmic_wrapper_type pwrap_mt8365 = {
.regs = mt8365_regs,
- .type = PWRAP_MT8365,
.arb_en_all = 0x3ffff,
- .int_en_all = 0x7f1fffff,
- .int1_en_all = 0x0,
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.wdt_src = PWRAP_WDT_SRC_MASK_ALL,
- .caps = PWRAP_CAP_INT1_EN | PWRAP_CAP_WDT_SRC1,
+ .caps = PWRAP_CAP_WDT_SRC1 | PWRAP_CAP_INIT,
};
static const struct udevice_id mtk_pwrap_ids[] = {
{ .compatible = "mediatek,mt8188-pwrap", .data = (ulong)&pwrap_mt8188 },
{ .compatible = "mediatek,mt8189-pwrap", .data = (ulong)&pwrap_mt8189 },
+ { .compatible = "mediatek,mt8195-pwrap", .data = (ulong)&pwrap_mt8195 },
{ .compatible = "mediatek,mt8365-pwrap", .data = (ulong)&pwrap_mt8365 },
{ }
};
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
index a4ee5f1335a..ca5de5b8726 100644
--- a/drivers/power/regulator/Kconfig
+++ b/drivers/power/regulator/Kconfig
@@ -126,6 +126,16 @@ config SPL_DM_REGULATOR_DA9063
config DM_REGULATOR_PFUZE100
bool "Enable Driver Model for REGULATOR PFUZE100"
depends on DM_REGULATOR && DM_PMIC_PFUZE100
+ default DM_PMIC_PFUZE100
+ ---help---
+ This config enables implementation of driver-model regulator uclass
+ features for REGULATOR PFUZE100. The driver implements get/set api for:
+ value, enable and mode.
+
+config SPL_DM_REGULATOR_PFUZE100
+ bool "Enable Driver Model for REGULATOR PFUZE100 in SPL"
+ depends on SPL_DM_REGULATOR && SPL_DM_PMIC_PFUZE100
+ default SPL_DM_PMIC_PFUZE100
---help---
This config enables implementation of driver-model regulator uclass
features for REGULATOR PFUZE100. The driver implements get/set api for:
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
index 9e303d4f7f8..36a84e7cd71 100644
--- a/drivers/power/regulator/Makefile
+++ b/drivers/power/regulator/Makefile
@@ -15,7 +15,7 @@ obj-$(CONFIG_$(PHASE_)DM_REGULATOR_MAX77663) += max77663_regulator.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_MAX8907) += max8907_regulator.o
obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
obj-$(CONFIG_DM_REGULATOR_NPCM8XX) += npcm8xx_regulator.o
-obj-$(CONFIG_$(PHASE_)DM_PMIC_PFUZE100) += pfuze100.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PFUZE100) += pfuze100.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_BD71837) += bd71837.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PCA9450) += pca9450.o
obj-$(CONFIG_$(PHASE_)REGULATOR_PWM) += pwm_regulator.o
diff --git a/drivers/power/regulator/pfuze100.c b/drivers/power/regulator/pfuze100.c
index f864b1d8834..77c82a00b65 100644
--- a/drivers/power/regulator/pfuze100.c
+++ b/drivers/power/regulator/pfuze100.c
@@ -13,29 +13,43 @@
#include <power/regulator.h>
#include <power/pfuze100_pmic.h>
+struct pfuze100_high_volt_desc {
+ unsigned int uV_step;
+ unsigned int min_uV;
+};
+
/**
* struct pfuze100_regulator_desc - regulator descriptor
*
* @name: Identify name for the regulator.
* @type: Indicates the regulator type.
* @uV_step: Voltage increase for each selector.
+ * @min_uV: Indicates the minimal voltage supported.
* @vsel_reg: Register for adjust regulator voltage for normal.
* @vsel_mask: Mask bit for setting regulator voltage for normal.
* @stby_reg: Register for adjust regulator voltage for standby.
* @stby_mask: Mask bit for setting regulator voltage for standby.
* @volt_table: Voltage mapping table (if table based mapping).
* @voltage: Current voltage for REGULATOR_TYPE_FIXED type regulator.
+ * @high_volt_mask: Low or High Output voltage mode mask
+ * @high_volt_desc: High Output voltage description
+ * @b: Some regulators could work together to provide one output when working
+ * in single phase or dual phase mode.
*/
struct pfuze100_regulator_desc {
char *name;
enum regulator_type type;
unsigned int uV_step;
+ unsigned int min_uV;
unsigned int vsel_reg;
unsigned int vsel_mask;
unsigned int stby_reg;
unsigned int stby_mask;
unsigned int *volt_table;
unsigned int voltage;
+ unsigned int high_volt_mask;
+ struct pfuze100_high_volt_desc *high_volt_desc;
+ struct pfuze100_regulator_desc *b;
};
/**
@@ -54,15 +68,33 @@ struct pfuze100_regulator_plat {
.voltage = (vol), \
}
-#define PFUZE100_SW_REG(_name, base, step) \
+#define PFUZE100_SW_REG(_name, base, step, min, desc, mask) \
+ { \
+ .name = #_name, \
+ .type = REGULATOR_TYPE_BUCK, \
+ .uV_step = (step), \
+ .min_uV = (min), \
+ .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \
+ .vsel_mask = 0x3F, \
+ .stby_reg = (base) + PFUZE100_STBY_OFFSET, \
+ .stby_mask = 0x3F, \
+ .high_volt_desc = (desc), \
+ .high_volt_mask = (mask), \
+ }
+
+#define PFUZE100_SWAB_REG(_name, base, step, min, volt_desc, mask, desc) \
{ \
.name = #_name, \
.type = REGULATOR_TYPE_BUCK, \
.uV_step = (step), \
+ .min_uV = (min), \
.vsel_reg = (base) + PFUZE100_VOL_OFFSET, \
.vsel_mask = 0x3F, \
.stby_reg = (base) + PFUZE100_STBY_OFFSET, \
.stby_mask = 0x3F, \
+ .high_volt_desc = (volt_desc), \
+ .high_volt_mask = (mask), \
+ .b = (desc), \
}
#define PFUZE100_SWB_REG(_name, base, mask, step, voltages) \
@@ -84,32 +116,35 @@ struct pfuze100_regulator_plat {
.volt_table = (voltages), \
}
-#define PFUZE100_VGEN_REG(_name, base, step) \
+#define PFUZE100_VGEN_REG(_name, base, step, min) \
{ \
.name = #_name, \
.type = REGULATOR_TYPE_LDO, \
.uV_step = (step), \
+ .min_uV = (min), \
.vsel_reg = (base), \
.vsel_mask = 0xF, \
.stby_reg = (base), \
.stby_mask = 0x20, \
}
-#define PFUZE3000_VCC_REG(_name, base, step) \
+#define PFUZE3000_VCC_REG(_name, base, step, min) \
{ \
.name = #_name, \
.type = REGULATOR_TYPE_LDO, \
.uV_step = (step), \
+ .min_uV = (min), \
.vsel_reg = (base), \
.vsel_mask = 0x3, \
.stby_reg = (base), \
.stby_mask = 0x20, \
}
-#define PFUZE3000_SW1_REG(_name, base, step) \
+#define PFUZE3000_SW1_REG(_name, base, step, min) \
{ \
.name = #_name, \
.type = REGULATOR_TYPE_BUCK, \
+ .min_uV = (min), \
.uV_step = (step), \
.vsel_reg = (base) + PFUZE100_VOL_OFFSET, \
.vsel_mask = 0x1F, \
@@ -128,11 +163,12 @@ struct pfuze100_regulator_plat {
.stby_mask = 0x7, \
}
-#define PFUZE3000_SW3_REG(_name, base, step) \
+#define PFUZE3000_SW3_REG(_name, base, step, min) \
{ \
.name = #_name, \
.type = REGULATOR_TYPE_BUCK, \
.uV_step = (step), \
+ .min_uV = (min), \
.vsel_reg = (base) + PFUZE100_VOL_OFFSET, \
.vsel_mask = 0xF, \
.stby_reg = (base) + PFUZE100_STBY_OFFSET, \
@@ -155,57 +191,66 @@ static unsigned int pfuze3000_sw2lo[] = {
1500000, 1550000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000
};
+static struct pfuze100_high_volt_desc high_desc = {
+ .min_uV = 800000,
+ .uV_step = 50000,
+};
+
/* PFUZE100 */
static struct pfuze100_regulator_desc pfuze100_regulators[] = {
- PFUZE100_SW_REG(sw1ab, PFUZE100_SW1ABVOL, 25000),
- PFUZE100_SW_REG(sw1c, PFUZE100_SW1CVOL, 25000),
- PFUZE100_SW_REG(sw2, PFUZE100_SW2VOL, 25000),
- PFUZE100_SW_REG(sw3a, PFUZE100_SW3AVOL, 25000),
- PFUZE100_SW_REG(sw3b, PFUZE100_SW3BVOL, 25000),
- PFUZE100_SW_REG(sw4, PFUZE100_SW4VOL, 25000),
+ PFUZE100_SW_REG(sw1ab, PFUZE100_SW1ABVOL, 25000, 300000, NULL, 0),
+ PFUZE100_SW_REG(sw1c, PFUZE100_SW1CVOL, 25000, 300000, NULL, 0),
+ PFUZE100_SW_REG(sw2, PFUZE100_SW2VOL, 25000, 400000, &high_desc, BIT(6)),
+ PFUZE100_SW_REG(sw3a, PFUZE100_SW3AVOL, 25000, 400000, &high_desc, BIT(6)),
+ PFUZE100_SWAB_REG(sw3ab, PFUZE100_SW3AVOL, 25000, 400000, &high_desc, BIT(6),
+ &pfuze100_regulators[5]),
+ PFUZE100_SW_REG(sw3b, PFUZE100_SW3BVOL, 25000, 400000, &high_desc, BIT(6)),
+ PFUZE100_SW_REG(sw4, PFUZE100_SW4VOL, 25000, 400000, &high_desc, BIT(6)),
PFUZE100_SWB_REG(swbst, PFUZE100_SWBSTCON1, 0x3, 50000, pfuze100_swbst),
PFUZE100_SNVS_REG(vsnvs, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs),
PFUZE100_FIXED_REG(vrefddr, PFUZE100_VREFDDRCON, 750000),
- PFUZE100_VGEN_REG(vgen1, PFUZE100_VGEN1VOL, 50000),
- PFUZE100_VGEN_REG(vgen2, PFUZE100_VGEN2VOL, 50000),
- PFUZE100_VGEN_REG(vgen3, PFUZE100_VGEN3VOL, 100000),
- PFUZE100_VGEN_REG(vgen4, PFUZE100_VGEN4VOL, 100000),
- PFUZE100_VGEN_REG(vgen5, PFUZE100_VGEN5VOL, 100000),
- PFUZE100_VGEN_REG(vgen6, PFUZE100_VGEN6VOL, 100000),
+ PFUZE100_VGEN_REG(vgen1, PFUZE100_VGEN1VOL, 50000, 800000),
+ PFUZE100_VGEN_REG(vgen2, PFUZE100_VGEN2VOL, 50000, 1800000),
+ PFUZE100_VGEN_REG(vgen3, PFUZE100_VGEN3VOL, 100000, 1800000),
+ PFUZE100_VGEN_REG(vgen4, PFUZE100_VGEN4VOL, 100000, 1800000),
+ PFUZE100_VGEN_REG(vgen5, PFUZE100_VGEN5VOL, 100000, 1800000),
+ PFUZE100_VGEN_REG(vgen6, PFUZE100_VGEN6VOL, 100000, 1800000),
};
/* PFUZE200 */
static struct pfuze100_regulator_desc pfuze200_regulators[] = {
- PFUZE100_SW_REG(sw1ab, PFUZE100_SW1ABVOL, 25000),
- PFUZE100_SW_REG(sw2, PFUZE100_SW2VOL, 25000),
- PFUZE100_SW_REG(sw3a, PFUZE100_SW3AVOL, 25000),
- PFUZE100_SW_REG(sw3b, PFUZE100_SW3BVOL, 25000),
+ PFUZE100_SW_REG(sw1ab, PFUZE100_SW1ABVOL, 25000, 300000, NULL, 0),
+ PFUZE100_SW_REG(sw2, PFUZE100_SW2VOL, 25000, 400000, &high_desc, BIT(6)),
+ PFUZE100_SW_REG(sw3a, PFUZE100_SW3AVOL, 25000, 400000, &high_desc, BIT(6)),
+ PFUZE100_SWAB_REG(sw3ab, PFUZE100_SW3AVOL, 25000, 400000, &high_desc, BIT(6),
+ &pfuze200_regulators[4]),
+ PFUZE100_SW_REG(sw3b, PFUZE100_SW3BVOL, 25000, 400000, &high_desc, BIT(6)),
PFUZE100_SWB_REG(swbst, PFUZE100_SWBSTCON1, 0x3, 50000, pfuze100_swbst),
PFUZE100_SNVS_REG(vsnvs, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs),
PFUZE100_FIXED_REG(vrefddr, PFUZE100_VREFDDRCON, 750000),
- PFUZE100_VGEN_REG(vgen1, PFUZE100_VGEN1VOL, 50000),
- PFUZE100_VGEN_REG(vgen2, PFUZE100_VGEN2VOL, 50000),
- PFUZE100_VGEN_REG(vgen3, PFUZE100_VGEN3VOL, 100000),
- PFUZE100_VGEN_REG(vgen4, PFUZE100_VGEN4VOL, 100000),
- PFUZE100_VGEN_REG(vgen5, PFUZE100_VGEN5VOL, 100000),
- PFUZE100_VGEN_REG(vgen6, PFUZE100_VGEN6VOL, 100000),
+ PFUZE100_VGEN_REG(vgen1, PFUZE100_VGEN1VOL, 50000, 800000),
+ PFUZE100_VGEN_REG(vgen2, PFUZE100_VGEN2VOL, 50000, 800000),
+ PFUZE100_VGEN_REG(vgen3, PFUZE100_VGEN3VOL, 100000, 1800000),
+ PFUZE100_VGEN_REG(vgen4, PFUZE100_VGEN4VOL, 100000, 1800000),
+ PFUZE100_VGEN_REG(vgen5, PFUZE100_VGEN5VOL, 100000, 1800000),
+ PFUZE100_VGEN_REG(vgen6, PFUZE100_VGEN6VOL, 100000, 1800000),
};
/* PFUZE3000 */
static struct pfuze100_regulator_desc pfuze3000_regulators[] = {
- PFUZE3000_SW1_REG(sw1a, PFUZE100_SW1ABVOL, 25000),
- PFUZE3000_SW1_REG(sw1b, PFUZE100_SW1CVOL, 25000),
+ PFUZE3000_SW1_REG(sw1a, PFUZE100_SW1ABVOL, 25000, 700000),
+ PFUZE3000_SW1_REG(sw1b, PFUZE100_SW1CVOL, 25000, 700000),
PFUZE100_SWB_REG(sw2, PFUZE100_SW2VOL, 0x7, 50000, pfuze3000_sw2lo),
- PFUZE3000_SW3_REG(sw3, PFUZE100_SW3AVOL, 50000),
+ PFUZE3000_SW3_REG(sw3, PFUZE100_SW3AVOL, 50000, 900000),
PFUZE100_SWB_REG(swbst, PFUZE100_SWBSTCON1, 0x3, 50000, pfuze100_swbst),
PFUZE100_SNVS_REG(vsnvs, PFUZE100_VSNVSVOL, 0x7, pfuze3000_vsnvs),
PFUZE100_FIXED_REG(vrefddr, PFUZE100_VREFDDRCON, 750000),
- PFUZE100_VGEN_REG(vldo1, PFUZE100_VGEN1VOL, 100000),
- PFUZE100_VGEN_REG(vldo2, PFUZE100_VGEN2VOL, 50000),
- PFUZE3000_VCC_REG(vccsd, PFUZE100_VGEN3VOL, 150000),
- PFUZE3000_VCC_REG(v33, PFUZE100_VGEN4VOL, 150000),
- PFUZE100_VGEN_REG(vldo3, PFUZE100_VGEN5VOL, 100000),
- PFUZE100_VGEN_REG(vldo4, PFUZE100_VGEN6VOL, 100000),
+ PFUZE100_VGEN_REG(vldo1, PFUZE100_VGEN1VOL, 100000, 1800000),
+ PFUZE100_VGEN_REG(vldo2, PFUZE100_VGEN2VOL, 50000, 800000),
+ PFUZE3000_VCC_REG(vccsd, PFUZE100_VGEN3VOL, 150000, 2850000),
+ PFUZE3000_VCC_REG(v33, PFUZE100_VGEN4VOL, 150000, 2850000),
+ PFUZE100_VGEN_REG(vldo3, PFUZE100_VGEN5VOL, 100000, 1800000),
+ PFUZE100_VGEN_REG(vldo4, PFUZE100_VGEN6VOL, 100000, 1800000),
};
#define MODE(_id, _val, _name) { \
@@ -357,6 +402,16 @@ static int pfuze100_regulator_mode(struct udevice *dev, int op, int *opmode)
desc->vsel_reg + PFUZE100_MODE_OFFSET,
SW_MODE_MASK,
*opmode << SW_MODE_SHIFT);
+ if (val)
+ return val;
+
+ if (desc->b) {
+ desc = desc->b;
+ val = pmic_clrsetbits(dev->parent,
+ desc->vsel_reg + PFUZE100_MODE_OFFSET,
+ SW_MODE_MASK,
+ *opmode << SW_MODE_SHIFT);
+ }
} else if (desc->type == REGULATOR_TYPE_LDO) {
val = pmic_clrsetbits(dev->parent, desc->vsel_reg,
@@ -435,8 +490,8 @@ static int pfuze100_regulator_enable(struct udevice *dev, int op, bool *enable)
static int pfuze100_regulator_val(struct udevice *dev, int op, int *uV)
{
- int i;
- int val;
+ int i, ret;
+ int val, min_uV, uV_step;
struct pfuze100_regulator_plat *plat = dev_get_plat(dev);
struct pfuze100_regulator_desc *desc = plat->desc;
struct dm_regulator_uclass_plat *uc_pdata =
@@ -460,8 +515,15 @@ static int pfuze100_regulator_val(struct udevice *dev, int op, int *uV)
val = pmic_reg_read(dev->parent, desc->vsel_reg);
if (val < 0)
return val;
+ if (desc->high_volt_mask && (val & desc->high_volt_mask)) {
+ min_uV = desc->high_volt_desc->min_uV;
+ uV_step = desc->high_volt_desc->uV_step;
+ } else {
+ min_uV = desc->min_uV ?: uc_pdata->min_uV;
+ uV_step = desc->uV_step;
+ }
val &= desc->vsel_mask;
- *uV = uc_pdata->min_uV + (int)val * desc->uV_step;
+ *uV = min_uV + (int)val * uV_step;
}
return 0;
@@ -487,9 +549,27 @@ static int pfuze100_regulator_val(struct udevice *dev, int op, int *uV)
debug("Need to provide min_uV in dts.\n");
return -EINVAL;
}
- return pmic_clrsetbits(dev->parent, desc->vsel_reg,
- desc->vsel_mask,
- (*uV - uc_pdata->min_uV) / desc->uV_step);
+ val = pmic_reg_read(dev->parent, desc->vsel_reg);
+ if (desc->high_volt_mask && (val & desc->high_volt_mask)) {
+ min_uV = desc->high_volt_desc->min_uV;
+ uV_step = desc->high_volt_desc->uV_step;
+ } else {
+ min_uV = desc->min_uV ?: uc_pdata->min_uV;
+ uV_step = desc->uV_step;
+ }
+
+ ret = pmic_clrsetbits(dev->parent, desc->vsel_reg,
+ desc->vsel_mask, (*uV - min_uV) / uV_step);
+ if (ret)
+ return ret;
+
+ if (desc->b) {
+ desc = desc->b;
+ ret = pmic_clrsetbits(dev->parent, desc->vsel_reg,
+ desc->vsel_mask, (*uV - min_uV) / uV_step);
+ if (ret)
+ return ret;
+ }
}
return 0;
diff --git a/drivers/power/regulator/qcom-rpmh-regulator.c b/drivers/power/regulator/qcom-rpmh-regulator.c
index 3f0f1845469..f789b5b6f86 100644
--- a/drivers/power/regulator/qcom-rpmh-regulator.c
+++ b/drivers/power/regulator/qcom-rpmh-regulator.c
@@ -295,6 +295,56 @@ static int rpmh_regulator_vrm_get_value(struct udevice *rdev)
return vreg->uv;
}
+static int rpmh_regulator_vrm_set_mode_bypass(struct rpmh_vreg *vreg,
+ unsigned int mode, bool bypassed)
+{
+ struct tcs_cmd cmd = {
+ .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_MODE,
+ };
+ struct dm_regulator_mode *pmic_mode;
+ int i;
+
+ if (mode > REGULATOR_MODE_HPM)
+ return -EINVAL;
+
+ for (i = 0; i < vreg->hw_data->n_modes; i++) {
+ pmic_mode = &vreg->hw_data->pmic_mode_map[i];
+ if (pmic_mode->id == mode)
+ break;
+ }
+ if (pmic_mode->id != mode) {
+ printf("Invalid mode %d\n", mode);
+ return -EINVAL;
+ }
+
+ if (bypassed)
+ // XXX: should have a version check for PMIC4 but we don't have any yet
+ // and we don't use bypass mode
+ cmd.data = PMIC5_BOB_MODE_PASS;
+ else
+ cmd.data = pmic_mode->register_value;
+
+ return rpmh_regulator_send_request(vreg, &cmd, true);
+}
+
+static int rpmh_regulator_vrm_set_mode(struct udevice *rdev,
+ int mode)
+{
+ struct rpmh_vreg *vreg = dev_get_priv(rdev);
+ int ret;
+
+ debug("%s: set_mode %d (current %d)\n", rdev->name, mode, vreg->mode);
+
+ if (mode == vreg->mode)
+ return 0;
+
+ ret = rpmh_regulator_vrm_set_mode_bypass(vreg, mode, vreg->bypassed);
+ if (!ret)
+ vreg->mode = mode;
+
+ return ret;
+}
+
static int rpmh_regulator_is_enabled(struct udevice *rdev)
{
struct rpmh_vreg *vreg = dev_get_priv(rdev);
@@ -331,6 +381,12 @@ static int rpmh_regulator_set_enable_state(struct udevice *rdev,
debug("%s: set_enable %d (current %d)\n", rdev->name, enable,
vreg->enabled);
+ if (vreg->mode != -EINVAL) {
+ ret = rpmh_regulator_vrm_set_mode_bypass(vreg, vreg->mode, vreg->bypassed);
+ if (ret < 0)
+ return ret;
+ }
+
if (vreg->enabled == -EINVAL &&
vreg->uv != -ENOTRECOVERABLE) {
ret = _rpmh_regulator_vrm_set_value(rdev,
@@ -346,56 +402,6 @@ static int rpmh_regulator_set_enable_state(struct udevice *rdev,
return ret;
}
-static int rpmh_regulator_vrm_set_mode_bypass(struct rpmh_vreg *vreg,
- unsigned int mode, bool bypassed)
-{
- struct tcs_cmd cmd = {
- .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_MODE,
- };
- struct dm_regulator_mode *pmic_mode;
- int i;
-
- if (mode > REGULATOR_MODE_HPM)
- return -EINVAL;
-
- for (i = 0; i < vreg->hw_data->n_modes; i++) {
- pmic_mode = &vreg->hw_data->pmic_mode_map[i];
- if (pmic_mode->id == mode)
- break;
- }
- if (pmic_mode->id != mode) {
- printf("Invalid mode %d\n", mode);
- return -EINVAL;
- }
-
- if (bypassed)
- // XXX: should have a version check for PMIC4 but we don't have any yet
- // and we don't use bypass mode
- cmd.data = PMIC5_BOB_MODE_PASS;
- else
- cmd.data = pmic_mode->register_value;
-
- return rpmh_regulator_send_request(vreg, &cmd, true);
-}
-
-static int rpmh_regulator_vrm_set_mode(struct udevice *rdev,
- int mode)
-{
- struct rpmh_vreg *vreg = dev_get_priv(rdev);
- int ret;
-
- debug("%s: set_mode %d (current %d)\n", rdev->name, mode, vreg->mode);
-
- if (mode == vreg->mode)
- return 0;
-
- ret = rpmh_regulator_vrm_set_mode_bypass(vreg, mode, vreg->bypassed);
- if (!ret)
- vreg->mode = mode;
-
- return ret;
-}
-
static int rpmh_regulator_vrm_get_pmic_mode(struct rpmh_vreg *vreg, int *pmic_mode)
{
struct tcs_cmd cmd = {
@@ -640,6 +646,35 @@ static const struct rpmh_vreg_init_data pm6150l_vreg_data[] = {
{}
};
+static const struct rpmh_vreg_init_data pm7550_vreg_data[] = {
+ /* smps1 - smps6 are not added to u-boot yet */
+ RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1"),
+ RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2-l3"),
+ RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l2-l3"),
+ RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo515, "vdd-l4-l5"),
+ RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo515, "vdd-l4-l5"),
+ RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo515, "vdd-l6"),
+ RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo515, "vdd-l7"),
+ RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo515, "vdd-l8"),
+ RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo515, "vdd-l9-l10"),
+ RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo515, "vdd-l9-l10"),
+ RPMH_VREG("ldo11", "ldo%s11", &pmic5_nldo515, "vdd-l11"),
+ RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo515_mv, "vdd-l12-l14"),
+ RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo515_mv, "vdd-l13-l16"),
+ RPMH_VREG("ldo14", "ldo%s14", &pmic5_pldo, "vdd-l12-l14"),
+ RPMH_VREG("ldo15", "ldo%s15", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"),
+ RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l13-l16"),
+ RPMH_VREG("ldo17", "ldo%s17", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"),
+ RPMH_VREG("ldo18", "ldo%s18", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"),
+ RPMH_VREG("ldo19", "ldo%s19", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"),
+ RPMH_VREG("ldo20", "ldo%s20", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"),
+ RPMH_VREG("ldo21", "ldo%s21", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"),
+ RPMH_VREG("ldo22", "ldo%s22", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"),
+ RPMH_VREG("ldo23", "ldo%s23", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"),
+ RPMH_VREG("bob", "bob%s1", &pmic5_bob, "vdd-bob"),
+ {}
+};
+
static const struct rpmh_vreg_init_data pm8150_vreg_data[] = {
RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"),
RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"),
@@ -947,6 +982,10 @@ static const struct udevice_id rpmh_regulator_ids[] = {
.data = (ulong)pm7325_vreg_data,
},
{
+ .compatible = "qcom,pm7550-rpmh-regulators",
+ .data = (ulong)pm7550_vreg_data,
+ },
+ {
.compatible = "qcom,pm8150-rpmh-regulators",
.data = (ulong)pm8150_vreg_data,
},
diff --git a/drivers/power/regulator/tps65941_regulator.c b/drivers/power/regulator/tps65941_regulator.c
index 2561d6f4c6c..209968b5718 100644
--- a/drivers/power/regulator/tps65941_regulator.c
+++ b/drivers/power/regulator/tps65941_regulator.c
@@ -63,13 +63,14 @@ static inline int tps65941_get_chip_id(struct udevice *dev)
static int tps65941_buck_enable(struct udevice *dev, int op, bool *enable)
{
- int ret;
+ int ret, idx;
unsigned int adr;
struct dm_regulator_uclass_plat *uc_pdata;
uc_pdata = dev_get_uclass_plat(dev);
adr = uc_pdata->ctrl_reg;
+ idx = dev->driver_data;
ret = pmic_reg_read(dev->parent, adr);
if (ret < 0)
return ret;
@@ -84,10 +85,18 @@ static int tps65941_buck_enable(struct udevice *dev, int op, bool *enable)
return 0;
} else if (op == PMIC_OP_SET) {
- if (*enable)
+ if (*enable) {
ret |= TPS65941_BUCK_MODE_MASK;
- else
+ /* Enable FPWM */
+ ret |= TPS65941_BUCK_FPWM_MASK;
+ /* If Multiphase enable FPWM_MP */
+ if (idx == TPS65941_BUCK_ID_12 ||
+ idx == TPS65941_BUCK_ID_123 ||
+ idx == TPS65941_BUCK_ID_1234)
+ ret |= TPS65941_BUCK_FPWM_MP_MASK;
+ } else {
ret &= ~TPS65941_BUCK_MODE_MASK;
+ }
ret = pmic_reg_write(dev->parent, adr, ret);
if (ret)
return ret;
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index f07ae903a28..7f9e35283f9 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -48,6 +48,15 @@ config REMOTEPROC_RENESAS_APMU
Say 'y' here to add support for Renesas R-Car Gen4 Cortex-R52
processor via the remoteproc framework.
+config REMOTEPROC_RENESAS_RSIP
+ bool "Support for Renesas R-Car Gen5 RSIP start of SCP/CR52/CA720 processors"
+ select REMOTEPROC
+ depends on ARCH_RENESAS && RCAR_GEN5 && DM && OF_CONTROL
+ help
+ Say 'y' here to add support for Renesas R-Car Gen5 RSIP
+ start of SCP, Cortex-R52 and Cortex-A720 processors via
+ the remoteproc framework.
+
config REMOTEPROC_SANDBOX
bool "Support for Test processor for Sandbox"
select REMOTEPROC
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 7ea8023c50b..250915d0de4 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_K3_SYSTEM_CONTROLLER) += k3_system_controller.o
obj-$(CONFIG_REMOTEPROC_ADI_SC5XX) += adi_sc5xx_rproc.o
obj-$(CONFIG_REMOTEPROC_IMX) += imx_rproc.o
obj-$(CONFIG_REMOTEPROC_RENESAS_APMU) += renesas_apmu.o
+obj-$(CONFIG_REMOTEPROC_RENESAS_RSIP) += renesas_rsip.o
obj-$(CONFIG_REMOTEPROC_SANDBOX) += sandbox_testproc.o
obj-$(CONFIG_REMOTEPROC_STM32_COPRO) += stm32_copro.o
obj-$(CONFIG_REMOTEPROC_TI_K3_ARM64) += ti_k3_arm64_rproc.o
diff --git a/drivers/remoteproc/renesas_rsip.c b/drivers/remoteproc/renesas_rsip.c
new file mode 100644
index 00000000000..76ccaf93b1a
--- /dev/null
+++ b/drivers/remoteproc/renesas_rsip.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ */
+
+#include <asm/io.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <errno.h>
+#include <hang.h>
+#include <linux/iopoll.h>
+#include <linux/sizes.h>
+#include <malloc.h>
+#include <remoteproc.h>
+
+/* R-Car X5H contains 1 SCP core, 6 lockstep Cortex-R52 and 32 Cortex-A720AE cores. */
+#define RCAR5_SCP_CORES 1
+#define RCAR5_CR52_CORES 12
+#define RCAR5_CA720_CORES 32
+
+#define SCP_BASE 0xc1340000
+#define SCP_CPUWAIT (SCP_BASE + 0x30)
+#define SCP_CPUWAIT_WAIT BIT(0)
+#define SCP_STCM 0xc1000000
+
+struct scp_scmi_shmem {
+ u32 reserved0;
+ u32 status;
+ u64 reserved1;
+ u32 flags;
+ u32 length;
+ u32 message_header;
+ u8 payload[];
+};
+
+struct scp_scmi_pd_power_state_set_a2p {
+ u32 flags;
+ u32 domain_id;
+ u32 power_state;
+ u32 boot_addr;
+};
+
+/* The addresses in range 0x08000000..0x1fffffff are incremented by 0xa0000000 */
+#define MFIS_COMMON_BASE 0xb89e1000
+#define MFIS_SCP_WACNTR (MFIS_COMMON_BASE + 0x904)
+
+/* The addresses in range 0x08000000..0x1fffffff are incremented by 0xa0000000 */
+#define MFIS_SCP_BASE 0xb8840000
+#define MFIS_SCP_REICR8 (MFIS_SCP_BASE + 0x28004)
+#define MFIS_SCP_CODEVALUE 0xacc00000
+#define MFIS_SCP_REG_MASK GENMASK(19, 0)
+
+/**
+ * mfis_scp_unlock() - Release MFIS-SCP lock
+ * @lck: MFIS lock register
+ */
+static void mfis_scp_unlock(const u32 lck)
+{
+ writel(MFIS_SCP_CODEVALUE | (lck & MFIS_SCP_REG_MASK), MFIS_SCP_WACNTR);
+}
+
+#define SCP_SCMI_SHMEM_AREA09 0xc1060800
+#define SCP_SCMI_STATUS_MASK 0x3
+#define SCP_SCMI_STATUS_FREE 0x1
+
+/**
+ * scp_wait_fw_free() - Wait for SCP channel to be free for communication
+ */
+static void scp_wait_fw_free(void)
+{
+ struct scp_scmi_shmem *shmem = (struct scp_scmi_shmem *)SCP_SCMI_SHMEM_AREA09;
+
+ while ((shmem->status & SCP_SCMI_STATUS_MASK) != SCP_SCMI_STATUS_FREE)
+ mdelay(1);
+}
+
+/**
+ * scp_send_interrupt() - Raise interrupt on SCP side
+ */
+static void scp_send_interrupt(void)
+{
+ mfis_scp_unlock(MFIS_SCP_REICR8);
+ /* Send SCP IRQ */
+ writel(1, MFIS_SCP_REICR8);
+}
+
+/* SCMI power domain IDs */
+#define SCMI_PD_CORE_RT_CORE00 117
+#define SCMI_PD_CORE_AP_CORE00 129
+
+/*
+ * FIXME: This is custom extension to the SCMI PD protocol:
+ * - Protocol 0x11 (PD)
+ * - Command 0x11 (POWER_STATE_SET_BOOTADDR - custom)
+ * This must be removed when proper upstream SCP port exists
+ */
+#define SCMI_PD_POWER_STATE_SET_BOOTADDR 0x4411
+
+/**
+ * scp_cpu_core_start() - Boot CPU core by invoking SCP via SCMI
+ * @core: CPU core to boot
+ * @ep: Entry point
+ */
+static void scp_cpu_core_start(const u32 core, const u32 ep)
+{
+ struct scp_scmi_shmem *shmem = (struct scp_scmi_shmem *)SCP_SCMI_SHMEM_AREA09;
+ struct scp_scmi_pd_power_state_set_a2p scmi_parameter = {
+ .flags = 1, /* Asynchronous power transition using APMU */
+ .domain_id = core,
+ .power_state = 0, /* Power on */
+ .boot_addr = ep,
+ };
+ u32 status;
+
+ /* Wait for SCP to be free, then set it busy */
+ scp_wait_fw_free();
+ shmem->status &= ~SCP_SCMI_STATUS_FREE;
+
+ /* Set up the message and copy it to SHMEM */
+ shmem->message_header = SCMI_PD_POWER_STATE_SET_BOOTADDR;
+ memcpy(shmem->payload, &scmi_parameter, sizeof(scmi_parameter));
+ shmem->length = sizeof(shmem->message_header) + sizeof(scmi_parameter);
+
+ /* Send message to SCP and wait for completion */
+ scp_send_interrupt();
+ scp_wait_fw_free();
+
+ /* Read back the result */
+ status = readl((uintptr_t)shmem->payload);
+ if (status)
+ printf("SCP POWER_STATE_SET failed, status=0x%x\n", status);
+}
+
+/**
+ * struct renesas_rsip_rproc_privdata - remote processor private data
+ * @core_id: CPU core id
+ * @ep: Entry point
+ */
+struct renesas_rsip_rproc_privdata {
+ ulong core_id;
+ ulong ep;
+};
+
+/**
+ * renesas_rsip_rproc_load() - Load the remote processor
+ * @dev: corresponding remote processor device
+ * @addr: Address in memory where image is stored
+ * @size: Size in bytes of the image
+ *
+ * Return: 0 if all went ok, else corresponding -ve error
+ */
+static int renesas_rsip_rproc_load(struct udevice *dev, ulong addr, ulong size)
+{
+ struct renesas_rsip_rproc_privdata *priv = dev_get_priv(dev);
+
+ priv->ep = addr;
+
+ if (priv->core_id == 0) /* SCP */
+ memcpy((void *)SCP_STCM, (void *)addr, size);
+
+ return 0;
+}
+
+/**
+ * renesas_rsip_rproc_start() - Start the remote processor
+ * @dev: corresponding remote processor device
+ *
+ * Return: 0 if all went ok, else corresponding -ve error
+ */
+static int renesas_rsip_rproc_start(struct udevice *dev)
+{
+ struct renesas_rsip_rproc_privdata *priv = dev_get_priv(dev);
+ int scmi_core;
+
+ if (priv->core_id == 0) {
+ /* SCP */
+ clrbits_le32(SCP_CPUWAIT, SCP_CPUWAIT_WAIT);
+ return 0;
+ } else if (priv->core_id >= RCAR5_SCP_CORES &&
+ priv->core_id < RCAR5_SCP_CORES + RCAR5_CR52_CORES) {
+ /* CR52 */
+ scmi_core = priv->core_id - RCAR5_SCP_CORES +
+ SCMI_PD_CORE_RT_CORE00;
+ } else if (priv->core_id >= RCAR5_SCP_CORES + RCAR5_CR52_CORES &&
+ priv->core_id < RCAR5_SCP_CORES + RCAR5_CR52_CORES + RCAR5_CA720_CORES) {
+ /* CA720 */
+ scmi_core = priv->core_id - RCAR5_SCP_CORES - RCAR5_CR52_CORES +
+ SCMI_PD_CORE_AP_CORE00;
+ }
+
+ scp_cpu_core_start(scmi_core, priv->ep);
+
+ return 0;
+}
+
+/**
+ * renesas_rsip_rproc_stop() - Stop the remote processor
+ * @dev: corresponding remote processor device
+ *
+ * Return: 0 if all went ok, else corresponding -ve error
+ */
+static int renesas_rsip_rproc_stop(struct udevice *dev)
+{
+ struct renesas_rsip_rproc_privdata *priv = dev_get_priv(dev);
+
+ if (priv->core_id == 0) { /* SCP */
+ setbits_le32(SCP_CPUWAIT, SCP_CPUWAIT_WAIT);
+ return 0;
+ }
+
+ return 0;
+}
+
+/**
+ * renesas_rsip_rproc_reset() - Reset the remote processor
+ * @dev: corresponding remote processor device
+ *
+ * Return: 0 if all went ok, else corresponding -ve error
+ */
+static int renesas_rsip_rproc_reset(struct udevice *dev)
+{
+ renesas_rsip_rproc_stop(dev);
+ renesas_rsip_rproc_start(dev);
+ return 0;
+}
+
+/**
+ * renesas_rsip_rproc_is_running() - Is the remote processor running
+ * @dev: corresponding remote processor device
+ *
+ * Return: 0 if the remote processor is running, 1 otherwise
+ */
+static int renesas_rsip_rproc_is_running(struct udevice *dev)
+{
+ /* We assume the core is stopped. */
+ return 1;
+}
+
+/**
+ * renesas_rsip_rproc_init() - Initialize the remote processor
+ * @dev: corresponding remote processor device
+ *
+ * Return: 0 if all went ok, else corresponding -ve error
+ */
+static int renesas_rsip_rproc_init(struct udevice *dev)
+{
+ return 0;
+}
+
+/**
+ * renesas_rsip_rproc_device_to_virt() - Convert device address to virtual address
+ * @dev: corresponding remote processor device
+ * @da: device address
+ * @size: Size of the memory region @da is pointing to
+ * @is_iomem: optional pointer filled in to indicate if @da is iomapped memory
+ *
+ * Return: converted virtual address
+ */
+static void *renesas_rsip_rproc_device_to_virt(struct udevice *dev, ulong da,
+ ulong size, bool *is_iomem)
+{
+ return (void *)da;
+}
+
+static const struct dm_rproc_ops renesas_rsip_rproc_ops = {
+ .init = renesas_rsip_rproc_init,
+ .load = renesas_rsip_rproc_load,
+ .start = renesas_rsip_rproc_start,
+ .stop = renesas_rsip_rproc_stop,
+ .reset = renesas_rsip_rproc_reset,
+ .is_running = renesas_rsip_rproc_is_running,
+ .device_to_virt = renesas_rsip_rproc_device_to_virt,
+};
+
+/**
+ * renesas_rsip_rproc_of_to_plat() - Convert OF data to platform data
+ * @dev: corresponding remote processor device
+ *
+ * Return: 0 if all went ok, else corresponding -ve error
+ */
+static int renesas_rsip_rproc_of_to_plat(struct udevice *dev)
+{
+ struct renesas_rsip_rproc_privdata *priv = dev_get_priv(dev);
+
+ priv->core_id = dev_get_driver_data(dev);
+
+ return 0;
+}
+
+U_BOOT_DRIVER(renesas_rsip_core) = {
+ .name = "rcar-rsip-core",
+ .id = UCLASS_REMOTEPROC,
+ .ops = &renesas_rsip_rproc_ops,
+ .of_to_plat = renesas_rsip_rproc_of_to_plat,
+ .priv_auto = sizeof(struct renesas_rsip_rproc_privdata),
+};
+
+/**
+ * renesas_rsip_rproc_bind() - Bind rproc driver to each core control
+ * @dev: corresponding remote processor parent device
+ *
+ * Return: 0 if all went ok, else corresponding -ve error
+ */
+static int renesas_rsip_rproc_bind(struct udevice *parent)
+{
+ ofnode pnode = dev_ofnode(parent);
+ struct udevice *cdev;
+ struct driver *cdrv;
+ char name[32];
+ ulong i;
+ int ret;
+
+ cdrv = lists_driver_lookup_name("rcar-rsip-core");
+ if (!cdrv)
+ return -ENOENT;
+
+ /* Singleton SCP core is core 0 */
+ ret = device_bind_with_driver_data(parent, cdrv,
+ strdup("rcar-rsip-scp"),
+ 0, pnode, &cdev);
+ if (ret)
+ return ret;
+
+ /* Cores 1..13 are Cortex-R52 */
+ for (i = 0; i < RCAR5_CR52_CORES; i++) {
+ snprintf(name, sizeof(name), "rcar-rsip-cr.%ld", i);
+ ret = device_bind_with_driver_data(parent, cdrv, strdup(name),
+ i + RCAR5_SCP_CORES, pnode,
+ &cdev);
+ if (ret)
+ return ret;
+ }
+
+ /* Cores 14..46 are Cortex-A720 */
+ for (i = 0; i < RCAR5_CA720_CORES; i++) {
+ snprintf(name, sizeof(name), "rcar-rsip-ca.%ld", i);
+ ret = device_bind_with_driver_data(parent, cdrv, strdup(name),
+ i + RCAR5_SCP_CORES + RCAR5_CR52_CORES,
+ pnode, &cdev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct udevice_id renesas_rsip_rproc_ids[] = {
+ { .compatible = "renesas,r8a78000-rproc" },
+ { }
+};
+
+U_BOOT_DRIVER(renesas_rsip_rproc) = {
+ .name = "rcar-rsip-rproc",
+ .of_match = renesas_rsip_rproc_ids,
+ .id = UCLASS_NOP,
+ .bind = renesas_rsip_rproc_bind,
+};
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 66911199c8b..e7c0870c918 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -64,6 +64,22 @@ config RESET_BCM6345
help
Support reset controller on BCM6345.
+config RESET_BRCMSTB
+ depends on ARCH_BCM283X
+ bool "Generic Reset controller driver for Broadcom"
+ help
+ This enables reset controller for Broadcom devices.
+ If you wish to use reset resources managed by the Broadcom
+ Reset Controller, say Y here. Otherwise, say N.
+
+config RESET_BRCMSTB_RESCAL
+ depends on ARCH_BCM283X
+ bool "Generic Rescal Reset controller driver for Broadcom"
+ help
+ Support rescal reset controller on Broadcom.
+ If you wish to use reset resources managed by the Broadcom
+ Reset Controller, say Y here. Otherwise, say N.
+
config RESET_UNIPHIER
bool "Reset controller driver for UniPhier SoCs"
depends on ARCH_UNIPHIER
@@ -100,6 +116,15 @@ config RESET_ROCKCHIP
though is that some reset signals, like I2C or MISC reset multiple
devices.
+config SPL_RESET_ROCKCHIP
+ bool "SPL reset controller driver for Rockchip SoCs"
+ depends on SPL_DM_RESET && ARCH_ROCKCHIP && SPL_CLK
+ default y
+ help
+ Support for the reset controller on Rockchip SoCs in SPL. Select this
+ if you observe any reset-related warnings or errors when booting SPL,
+ such as when using UFS storage
+
config RESET_HSDK
bool "Synopsys HSDK Reset Driver"
depends on DM_RESET && TARGET_HSDK
@@ -239,8 +264,7 @@ config RESET_AT91
config RESET_RZG2L_USBPHY_CTRL
bool "Enable support for Renesas RZ/G2L USB 2.0 PHY control"
- depends on DM_RESET
- select REGULATOR_RZG2L_USBPHY
+ depends on DM_RESET && REGULATOR_RZG2L_USBPHY
help
Enable support for controlling USB 2.0 PHY resets on the Renesas
RZ/G2L SoC. This is required for USB 2.0 functionality to work on this
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 088545c6473..2c83f858895 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -13,10 +13,12 @@ obj-$(CONFIG_RESET_AIROHA) += reset-airoha.o
obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o
obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o
obj-$(CONFIG_RESET_BCM6345) += reset-bcm6345.o
+obj-$(CONFIG_RESET_BRCMSTB) += reset-brcmstb.o
+obj-$(CONFIG_RESET_BRCMSTB_RESCAL) += reset-brcmstb-rescal.o
obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o
obj-$(CONFIG_RESET_AST2500) += reset-ast2500.o
obj-$(CONFIG_RESET_AST2600) += reset-ast2600.o
-obj-$(CONFIG_RESET_ROCKCHIP) += reset-rockchip.o rst-rk3506.o rst-rk3528.o rst-rk3576.o rst-rk3588.o
+obj-$(CONFIG_$(PHASE_)RESET_ROCKCHIP) += reset-rockchip.o rst-rk3506.o rst-rk3528.o rst-rk3576.o rst-rk3588.o
obj-$(CONFIG_RESET_MESON) += reset-meson.o
obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
obj-$(CONFIG_RESET_MEDIATEK) += reset-mediatek.o
diff --git a/drivers/reset/reset-brcmstb-rescal.c b/drivers/reset/reset-brcmstb-rescal.c
new file mode 100644
index 00000000000..fc8fcfa8b3f
--- /dev/null
+++ b/drivers/reset/reset-brcmstb-rescal.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Broadcom STB generic reset controller
+ *
+ * Copyright (C) 2024 EPAM Systems
+ * Moved from linux kernel:
+ * Copyright (C) 2018-2020 Broadcom
+ */
+
+#include <asm/io.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <errno.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <log.h>
+#include <malloc.h>
+#include <reset-uclass.h>
+
+#define BRCM_RESCAL_START 0x0
+#define BRCM_RESCAL_START_BIT BIT(0)
+#define BRCM_RESCAL_CTRL 0x4
+#define BRCM_RESCAL_STATUS 0x8
+#define BRCM_RESCAL_STATUS_BIT BIT(0)
+
+struct brcm_rescal_reset {
+ void __iomem *base;
+};
+
+/* Also doubles a deassert */
+static int brcm_rescal_reset_set(struct reset_ctl *rst)
+{
+ struct brcm_rescal_reset *data = dev_get_priv(rst->dev);
+ void __iomem *base = data->base;
+ u32 reg;
+ int ret;
+
+ reg = readl(base + BRCM_RESCAL_START);
+ writel(reg | BRCM_RESCAL_START_BIT, base + BRCM_RESCAL_START);
+ reg = readl(base + BRCM_RESCAL_START);
+ if (!(reg & BRCM_RESCAL_START_BIT)) {
+ dev_err(rst->dev, "failed to start SATA/PCIe rescal\n");
+ return -EIO;
+ }
+
+ ret = readl_poll_timeout(base + BRCM_RESCAL_STATUS, reg,
+ (reg & BRCM_RESCAL_STATUS_BIT), 100);
+ if (ret) {
+ dev_err(rst->dev, "time out on SATA/PCIe rescal\n");
+ return ret;
+ }
+
+ reg = readl(base + BRCM_RESCAL_START);
+ writel(reg & ~BRCM_RESCAL_START_BIT, base + BRCM_RESCAL_START);
+
+ dev_dbg(rst->dev, "SATA/PCIe rescal success\n");
+ return 0;
+}
+
+/* A dummy function - deassert/reset does all the work */
+static int brcm_rescal_reset_assert(struct reset_ctl *rst)
+{
+ return 0;
+}
+
+static int brcm_rescal_reset_xlate(struct reset_ctl *reset_ctl,
+ struct ofnode_phandle_args *args)
+{
+ /* This is needed if #reset-cells == 0. */
+ return 0;
+}
+
+static const struct reset_ops brcm_rescal_reset_ops = {
+ .rst_deassert = brcm_rescal_reset_set,
+ .rst_assert = brcm_rescal_reset_assert,
+ .of_xlate = brcm_rescal_reset_xlate,
+};
+
+static int brcm_rescal_reset_probe(struct udevice *dev)
+{
+ struct brcm_rescal_reset *data = dev_get_priv(dev);
+
+ data->base = dev_remap_addr(dev);
+ if (!data->base)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct udevice_id brcm_rescal_reset_of_match[] = {
+ {.compatible = "brcm,bcm7216-pcie-sata-rescal"},
+ {},
+};
+
+U_BOOT_DRIVER(brcmstb_reset_rescal) = {
+ .name = "brcmstb-reset-rescal",
+ .id = UCLASS_RESET,
+ .of_match = brcm_rescal_reset_of_match,
+ .ops = &brcm_rescal_reset_ops,
+ .probe = brcm_rescal_reset_probe,
+ .priv_auto = sizeof(struct brcm_rescal_reset),
+};
diff --git a/drivers/reset/reset-brcmstb.c b/drivers/reset/reset-brcmstb.c
new file mode 100644
index 00000000000..7861f7c9baf
--- /dev/null
+++ b/drivers/reset/reset-brcmstb.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Broadcom STB generic reset controller
+ *
+ * Copyright (C) 2024 EPAM Systems
+ *
+ * Moved from linux kernel:
+ * Author: Florian Fainelli <[email protected]>
+ * Copyright (C) 2018 Broadcom
+ */
+
+#include <asm/io.h>
+#include <dm.h>
+#include <errno.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <log.h>
+#include <malloc.h>
+#include <reset-uclass.h>
+
+struct brcmstb_reset {
+ void __iomem *base;
+};
+
+#define SW_INIT_SET 0x00
+#define SW_INIT_CLEAR 0x04
+#define SW_INIT_STATUS 0x08
+
+#define SW_INIT_BIT(id) BIT((id) & 0x1f)
+#define SW_INIT_BANK(id) ((id) >> 5)
+
+#define usleep_range(a, b) udelay((b))
+
+/* A full bank contains extra registers that we are not utilizing but still
+ * qualify as a single bank.
+ */
+#define SW_INIT_BANK_SIZE 0x18
+
+static int brcmstb_reset_assert(struct reset_ctl *rst)
+{
+ unsigned int off = SW_INIT_BANK(rst->id) * SW_INIT_BANK_SIZE;
+ struct brcmstb_reset *priv = dev_get_priv(rst->dev);
+
+ writel_relaxed(SW_INIT_BIT(rst->id), priv->base + off + SW_INIT_SET);
+ return 0;
+}
+
+static int brcmstb_reset_deassert(struct reset_ctl *rst)
+{
+ unsigned int off = SW_INIT_BANK(rst->id) * SW_INIT_BANK_SIZE;
+ struct brcmstb_reset *priv = dev_get_priv(rst->dev);
+
+ writel_relaxed(SW_INIT_BIT(rst->id), priv->base + off + SW_INIT_CLEAR);
+ /* Maximum reset delay after de-asserting a line and seeing block
+ * operation is typically 14us for the worst case, build some slack
+ * here.
+ */
+ usleep_range(100, 200);
+ return 0;
+}
+
+static int brcmstb_reset_status(struct reset_ctl *rst)
+{
+ unsigned int off = SW_INIT_BANK(rst->id) * SW_INIT_BANK_SIZE;
+ struct brcmstb_reset *priv = dev_get_priv(rst->dev);
+
+ return readl_relaxed(priv->base + off + SW_INIT_STATUS) &
+ SW_INIT_BIT(rst->id);
+}
+
+struct reset_ops brcmstb_reset_reset_ops = {
+ .rst_assert = brcmstb_reset_assert,
+ .rst_deassert = brcmstb_reset_deassert,
+ .rst_status = brcmstb_reset_status};
+
+static int brcmstb_reset_probe(struct udevice *dev)
+{
+ struct brcmstb_reset *priv = dev_get_priv(dev);
+
+ priv->base = dev_remap_addr(dev);
+ if (!priv->base)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct udevice_id brcmstb_reset_ids[] = {
+ {.compatible = "brcm,brcmstb-reset"}, {/* sentinel */}};
+
+U_BOOT_DRIVER(brcmstb_reset) = {
+ .name = "brcmstb-reset",
+ .id = UCLASS_RESET,
+ .of_match = brcmstb_reset_ids,
+ .ops = &brcmstb_reset_reset_ops,
+ .probe = brcmstb_reset_probe,
+ .priv_auto = sizeof(struct brcmstb_reset),
+};
diff --git a/drivers/reset/stm32/stm32-reset-mp21.c b/drivers/reset/stm32/stm32-reset-mp21.c
index 7d169d7582f..0e92b0f5d5d 100644
--- a/drivers/reset/stm32/stm32-reset-mp21.c
+++ b/drivers/reset/stm32/stm32-reset-mp21.c
@@ -5,7 +5,7 @@
*/
#include <dm.h>
-#include <stm32-reset-core.h>
+#include "stm32-reset-core.h"
#include <stm32mp21_rcc.h>
#include <dt-bindings/reset/st,stm32mp21-rcc.h>
diff --git a/drivers/rtc/ds1307.c b/drivers/rtc/ds1307.c
index 04921099101..34d8f8c5276 100644
--- a/drivers/rtc/ds1307.c
+++ b/drivers/rtc/ds1307.c
@@ -68,157 +68,6 @@ enum ds_type {
#define MCP7941X_BIT_ST 0x80
#define MCP7941X_BIT_VBATEN 0x08
-#ifndef CONFIG_DM_RTC
-
-/*---------------------------------------------------------------------*/
-#undef DEBUG_RTC
-
-#ifdef DEBUG_RTC
-#define DEBUGR(fmt, args...) printf(fmt, ##args)
-#else
-#define DEBUGR(fmt, args...)
-#endif
-/*---------------------------------------------------------------------*/
-
-#ifndef CFG_SYS_I2C_RTC_ADDR
-# define CFG_SYS_I2C_RTC_ADDR 0x68
-#endif
-
-#if defined(CONFIG_RTC_DS1307) && (CONFIG_SYS_I2C_SPEED > 100000)
-# error The DS1307 is specified only up to 100kHz!
-#endif
-
-static uchar rtc_read (uchar reg);
-static void rtc_write (uchar reg, uchar val);
-
-/*
- * Get the current time from the RTC
- */
-int rtc_get (struct rtc_time *tmp)
-{
- int rel = 0;
- uchar sec, min, hour, mday, wday, mon, year;
-
-#ifdef CONFIG_RTC_MCP79411
-read_rtc:
-#endif
- sec = rtc_read (RTC_SEC_REG_ADDR);
- min = rtc_read (RTC_MIN_REG_ADDR);
- hour = rtc_read (RTC_HR_REG_ADDR);
- wday = rtc_read (RTC_DAY_REG_ADDR);
- mday = rtc_read (RTC_DATE_REG_ADDR);
- mon = rtc_read (RTC_MON_REG_ADDR);
- year = rtc_read (RTC_YR_REG_ADDR);
-
- DEBUGR ("Get RTC year: %02x mon: %02x mday: %02x wday: %02x "
- "hr: %02x min: %02x sec: %02x\n",
- year, mon, mday, wday, hour, min, sec);
-
-#ifdef CONFIG_RTC_DS1307
- if (sec & RTC_SEC_BIT_CH) {
- printf ("### Warning: RTC oscillator has stopped\n");
- /* clear the CH flag */
- rtc_write (RTC_SEC_REG_ADDR,
- rtc_read (RTC_SEC_REG_ADDR) & ~RTC_SEC_BIT_CH);
- rel = -1;
- }
-#endif
-
-#ifdef CONFIG_RTC_MCP79411
- /* make sure that the backup battery is enabled */
- if (!(wday & MCP7941X_BIT_VBATEN)) {
- rtc_write(RTC_DAY_REG_ADDR,
- wday | MCP7941X_BIT_VBATEN);
- }
-
- /* clock halted? turn it on, so clock can tick. */
- if (!(sec & MCP7941X_BIT_ST)) {
- rtc_write(RTC_SEC_REG_ADDR, MCP7941X_BIT_ST);
- printf("Started RTC\n");
- goto read_rtc;
- }
-#endif
-
- tmp->tm_sec = bcd2bin (sec & 0x7F);
- tmp->tm_min = bcd2bin (min & 0x7F);
- tmp->tm_hour = bcd2bin (hour & 0x3F);
- tmp->tm_mday = bcd2bin (mday & 0x3F);
- tmp->tm_mon = bcd2bin (mon & 0x1F);
- tmp->tm_year = bcd2bin (year) + ( bcd2bin (year) >= 70 ? 1900 : 2000);
- tmp->tm_wday = bcd2bin ((wday - 1) & 0x07);
- tmp->tm_yday = 0;
- tmp->tm_isdst= 0;
-
- DEBUGR ("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
- tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday,
- tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
-
- return rel;
-}
-
-/*
- * Set the RTC
- */
-int rtc_set (struct rtc_time *tmp)
-{
- DEBUGR ("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
- tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday,
- tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
-
- if (tmp->tm_year < 1970 || tmp->tm_year > 2069)
- printf("WARNING: year should be between 1970 and 2069!\n");
-
- rtc_write (RTC_YR_REG_ADDR, bin2bcd (tmp->tm_year % 100));
- rtc_write (RTC_MON_REG_ADDR, bin2bcd (tmp->tm_mon));
-#ifdef CONFIG_RTC_MCP79411
- rtc_write (RTC_DAY_REG_ADDR,
- bin2bcd (tmp->tm_wday + 1) | MCP7941X_BIT_VBATEN);
-#else
- rtc_write (RTC_DAY_REG_ADDR, bin2bcd (tmp->tm_wday + 1));
-#endif
- rtc_write (RTC_DATE_REG_ADDR, bin2bcd (tmp->tm_mday));
- rtc_write (RTC_HR_REG_ADDR, bin2bcd (tmp->tm_hour));
- rtc_write (RTC_MIN_REG_ADDR, bin2bcd (tmp->tm_min));
-#ifdef CONFIG_RTC_MCP79411
- rtc_write (RTC_SEC_REG_ADDR, bin2bcd (tmp->tm_sec) | MCP7941X_BIT_ST);
-#else
- rtc_write (RTC_SEC_REG_ADDR, bin2bcd (tmp->tm_sec));
-#endif
-
- return 0;
-}
-
-/*
- * Reset the RTC. We setting the date back to 1970-01-01.
- * We also enable the oscillator output on the SQW/OUT pin and program
- * it for 32,768 Hz output. Note that according to the datasheet, turning
- * on the square wave output increases the current drain on the backup
- * battery to something between 480nA and 800nA.
- */
-void rtc_reset (void)
-{
- rtc_write (RTC_SEC_REG_ADDR, 0x00); /* clearing Clock Halt */
- rtc_write (RTC_CTL_REG_ADDR, RTC_CTL_BIT_SQWE | RTC_CTL_BIT_RS1 | RTC_CTL_BIT_RS0);
-}
-
-/*
- * Helper functions
- */
-
-static
-uchar rtc_read (uchar reg)
-{
- return (i2c_reg_read (CFG_SYS_I2C_RTC_ADDR, reg));
-}
-
-static void rtc_write (uchar reg, uchar val)
-{
- i2c_reg_write (CFG_SYS_I2C_RTC_ADDR, reg, val);
-}
-
-#endif /* !CONFIG_DM_RTC */
-
-#ifdef CONFIG_DM_RTC
static int ds1307_rtc_set(struct udevice *dev, const struct rtc_time *tm)
{
int ret;
@@ -366,4 +215,3 @@ U_BOOT_DRIVER(rtc_ds1307) = {
.of_match = ds1307_rtc_ids,
.ops = &ds1307_rtc_ops,
};
-#endif /* CONFIG_DM_RTC */
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index 116b696b08d..50e7d749921 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -82,23 +82,6 @@ static void scsi_setup_inquiry(struct scsi_cmd *pccb)
pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */
}
-static void scsi_setup_sync_cache(struct scsi_cmd *pccb, lbaint_t start,
- lbaint_t blocks)
-{
- pccb->cmd[0] = SCSI_SYNC_CACHE;
- pccb->cmd[1] = 0;
- pccb->cmd[2] = (unsigned char)(start >> 24) & 0xff;
- pccb->cmd[3] = (unsigned char)(start >> 16) & 0xff;
- pccb->cmd[4] = (unsigned char)(start >> 8) & 0xff;
- pccb->cmd[5] = (unsigned char)start & 0xff;
- pccb->cmd[6] = 0;
- pccb->cmd[7] = (unsigned char)(blocks >> 8) & 0xff;
- pccb->cmd[8] = (unsigned char)blocks & 0xff;
- pccb->cmd[9] = 0;
- pccb->cmdlen = 10;
- pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */
-}
-
static void scsi_setup_read_ext(struct scsi_cmd *pccb, lbaint_t start,
lbaint_t blocks)
{
@@ -124,7 +107,7 @@ static void scsi_setup_write_ext(struct scsi_cmd *pccb, lbaint_t start,
lbaint_t blocks)
{
pccb->cmd[0] = SCSI_WRITE10;
- pccb->cmd[1] = 0;
+ pccb->cmd[1] = 0x08; /* Set FUA bit to bypass write cache */
pccb->cmd[2] = (unsigned char)(start >> 24) & 0xff;
pccb->cmd[3] = (unsigned char)(start >> 16) & 0xff;
pccb->cmd[4] = (unsigned char)(start >> 8) & 0xff;
@@ -301,11 +284,6 @@ static ulong scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
buf_addr += pccb->datalen;
} while (blks != 0);
- /* Flush the SCSI cache so we don't lose data on board reset. */
- scsi_setup_sync_cache(pccb, 0, 0);
- if (scsi_exec(bdev, pccb))
- scsi_print_error(pccb);
-
debug("%s: end startblk " LBAF ", blccnt " LBAF " buffer %lX\n",
__func__, start, blocks, buf_addr);
return blkcnt;
diff --git a/drivers/soc/soc_xilinx_zynqmp.c b/drivers/soc/soc_xilinx_zynqmp.c
index b97cd443c60..4abc73013eb 100644
--- a/drivers/soc/soc_xilinx_zynqmp.c
+++ b/drivers/soc/soc_xilinx_zynqmp.c
@@ -71,6 +71,11 @@ static const struct zynqmp_device zynqmp_devices[] = {
.variants = ZYNQMP_VARIANT_EG_LR,
},
{
+ .id = 0x0468A093,
+ .device = 1,
+ .variants = ZYNQMP_VARIANT_EG_LR,
+ },
+ {
.id = 0x04711093,
.device = 2,
.variants = ZYNQMP_VARIANT_EG | ZYNQMP_VARIANT_CG,
@@ -209,6 +214,16 @@ static const struct zynqmp_device zynqmp_devices[] = {
.variants = ZYNQMP_VARIANT_DR,
},
{
+ .id = 0x047F9093,
+ .device = 58,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x047FC093,
+ .device = 59,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
.id = 0x046d0093,
.device = 67,
.variants = ZYNQMP_VARIANT_DR,
@@ -219,6 +234,36 @@ static const struct zynqmp_device zynqmp_devices[] = {
.variants = ZYNQMP_VARIANT_DR_SE,
},
{
+ .id = 0x046D1093,
+ .device = 65,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x046D2093,
+ .device = 55,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x046D3093,
+ .device = 57,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x046D4093,
+ .device = 42,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x046D5093,
+ .device = 63,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x046D6093,
+ .device = 64,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
.id = 0x04712093,
.device = 24,
.variants = 0,
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 4ff17617d99..cfbedd64c4c 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -565,7 +565,7 @@ config SPI_SUNXI
config STM32_OSPI
bool "STM32MP2 OSPI driver"
- depends on STM32MP25X && STM32_OMM
+ depends on (STM32MP23X || 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
diff --git a/drivers/spi/apple_spi.c b/drivers/spi/apple_spi.c
index 5f94e9f7a74..088d02e3b90 100644
--- a/drivers/spi/apple_spi.c
+++ b/drivers/spi/apple_spi.c
@@ -270,6 +270,7 @@ static int apple_spi_probe(struct udevice *dev)
}
static const struct udevice_id apple_spi_of_match[] = {
+ { .compatible = "apple,t8103-spi" },
{ .compatible = "apple,spi" },
{ /* sentinel */ }
};
diff --git a/drivers/spi/fsl_espi.c b/drivers/spi/fsl_espi.c
index 117e36376b7..c5bc603b5c0 100644
--- a/drivers/spi/fsl_espi.c
+++ b/drivers/spi/fsl_espi.c
@@ -216,13 +216,13 @@ int espi_xfer(struct fsl_spi_slave *fsl, uint cs, unsigned int bitlen,
break;
case SPI_XFER_BEGIN | SPI_XFER_END:
len = data_len;
- buffer = (unsigned char *)malloc(len * 2);
+ buffer = (unsigned char *)malloc(len);
if (!buffer) {
debug("SF: Failed to malloc memory.\n");
return 1;
}
memcpy(buffer, data_out, len);
- rx_offset = len;
+ rx_offset = 0;
cmd_len = 0;
break;
}
@@ -275,7 +275,7 @@ int espi_xfer(struct fsl_spi_slave *fsl, uint cs, unsigned int bitlen,
}
}
if (data_in) {
- memcpy(data_in, buffer + rx_offset, tran_len);
+ memcpy(data_in, buffer + 2 * cmd_len, tran_len);
if (*buffer == 0x0b) {
data_in += tran_len;
data_len -= tran_len;
diff --git a/drivers/spi/mtk_snor.c b/drivers/spi/mtk_snor.c
index f202b2f49f5..40fc1826db6 100644
--- a/drivers/spi/mtk_snor.c
+++ b/drivers/spi/mtk_snor.c
@@ -1,10 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
-//
-// Mediatek SPI-NOR controller driver
-//
-// Copyright (C) 2020 SkyLake Huang <[email protected]>
-//
-// Some parts are based on drivers/spi/spi-mtk-nor.c of linux version
+/*
+ * Mediatek SPI-NOR controller driver
+ *
+ * Copyright (C) 2020 SkyLake Huang <[email protected]>
+ *
+ * Some parts are based on drivers/spi/spi-mtk-nor.c of linux version
+ */
#include <clk.h>
#include <cpu_func.h>
@@ -89,22 +90,33 @@
#define MTK_NOR_REG_DMA_END_DADR 0x724
#define MTK_NOR_PRG_MAX_SIZE 6
-// Reading DMA src/dst addresses have to be 16-byte aligned
+#define MTK_NOR_PRG_CNT_MAX 56
+/* Reading DMA src/dst addresses have to be 16-byte aligned */
#define MTK_NOR_DMA_ALIGN 16
#define MTK_NOR_DMA_ALIGN_MASK (MTK_NOR_DMA_ALIGN - 1)
-// and we allocate a bounce buffer if destination address isn't aligned.
+/* and we allocate a bounce buffer if destination address isn't aligned. */
#define MTK_NOR_BOUNCE_BUF_SIZE PAGE_SIZE
-// Buffered page program can do one 128-byte transfer
+/* Buffered page program can do one 128-byte transfer */
#define MTK_NOR_PP_SIZE 128
#define CLK_TO_US(priv, clkcnt) DIV_ROUND_UP(clkcnt, (priv)->spi_freq / 1000000)
#define MTK_NOR_UNLOCK_ALL 0x0
+struct mtk_snor_caps {
+ /*
+ * Some new SoCs modify the timing of fetching registers' values and IDs
+ * of NOR flash, they need a extra_bit which can add more clock cycles
+ * for fetching data.
+ */
+ u8 extra_bit;
+};
+
struct mtk_snor_priv {
struct device *dev;
void __iomem *base;
+ const struct mtk_snor_caps *caps;
u8 *buffer;
struct clk spi_clk;
struct clk ctlr_clk;
@@ -168,8 +180,8 @@ static int mtk_snor_adjust_op_size(struct spi_slave *slave,
return 0;
if (op->addr.nbytes == 3 || op->addr.nbytes == 4) {
- if (op->data.dir == SPI_MEM_DATA_IN) { //&&
- // limit size to prevent timeout calculation overflow
+ if (op->data.dir == SPI_MEM_DATA_IN) {
+ /* limit size to prevent timeout calculation overflow */
if (op->data.nbytes > 0x400000)
op->data.nbytes = 0x400000;
if (op->addr.val & MTK_NOR_DMA_ALIGN_MASK ||
@@ -262,6 +274,7 @@ static int mtk_snor_read_bounce(struct mtk_snor_priv *priv,
{
unsigned int rdlen;
int ret;
+ dma_addr_t bounce_dma;
if (op->data.nbytes & MTK_NOR_DMA_ALIGN_MASK)
rdlen = (op->data.nbytes + MTK_NOR_DMA_ALIGN) &
@@ -269,11 +282,21 @@ static int mtk_snor_read_bounce(struct mtk_snor_priv *priv,
else
rdlen = op->data.nbytes;
- ret = mtk_snor_dma_exec(priv, op->addr.val, rdlen,
- (dma_addr_t)priv->buffer);
+ /* Map bounce buffer for DMA */
+ bounce_dma = dma_map_single(priv->buffer, rdlen, DMA_FROM_DEVICE);
+ if (dma_mapping_error(priv->dev, bounce_dma)) {
+ dev_err(priv->dev, "bounce buffer dma map failed\n");
+ return -EINVAL;
+ }
- if (!ret)
+ ret = mtk_snor_dma_exec(priv, op->addr.val, rdlen, bounce_dma);
+ /* Ensure DMA writes are visible to CPU and copy the requested bytes */
+ if (!ret) {
+ /* Synchronize cached data to CPU visible memory if needed */
memcpy(op->data.buf.in, priv->buffer, op->data.nbytes);
+ }
+ /* Unmap bounce buffer regardless of success/failure */
+ dma_unmap_single(bounce_dma, rdlen, DMA_FROM_DEVICE);
return ret;
}
@@ -361,8 +384,12 @@ static int mtk_snor_pp_buffered(struct mtk_snor_priv *priv,
buf[i];
writel(val, priv->base + MTK_NOR_REG_PP_DATA);
}
- mtk_snor_cmd_exec(priv, MTK_NOR_CMD_WRITE,
- (op->data.nbytes + 5) * BITS_PER_BYTE);
+
+ ret = mtk_snor_cmd_exec(priv, MTK_NOR_CMD_WRITE,
+ (op->data.nbytes + 5) * BITS_PER_BYTE);
+ if (ret)
+ return ret;
+
return mtk_snor_write_buffer_disable(priv);
}
@@ -382,50 +409,83 @@ static int mtk_snor_pp_unbuffered(struct mtk_snor_priv *priv,
static int mtk_snor_cmd_program(struct mtk_snor_priv *priv,
const struct spi_mem_op *op)
{
- u32 tx_len = 0;
- u32 trx_len = 0;
+ int rx_len = 0;
int reg_offset = MTK_NOR_REG_PRGDATA_MAX;
+ int tx_len, prg_len;
+ int i, ret;
void __iomem *reg;
- u8 *txbuf;
- int tx_cnt = 0;
- u8 *rxbuf = op->data.buf.in;
- int i = 0;
+ u8 val;
+
+ tx_len = op->cmd.nbytes + op->addr.nbytes;
- tx_len = 1 + op->addr.nbytes + op->dummy.nbytes;
- trx_len = tx_len + op->data.nbytes;
+ /* count dummy bytes only if we need to write data after it */
if (op->data.dir == SPI_MEM_DATA_OUT)
- tx_len += op->data.nbytes;
+ tx_len += op->dummy.nbytes + op->data.nbytes;
+ else if (op->data.dir == SPI_MEM_DATA_IN)
+ rx_len = op->data.nbytes;
- txbuf = kmalloc_array(tx_len, sizeof(u8), GFP_KERNEL);
- memset(txbuf, 0x0, tx_len * sizeof(u8));
+ prg_len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes +
+ op->data.nbytes;
- /* Join all bytes to be transferred */
- txbuf[tx_cnt] = op->cmd.opcode;
- tx_cnt++;
- for (i = op->addr.nbytes; i > 0; i--, tx_cnt++)
- txbuf[tx_cnt] = ((u8 *)&op->addr.val)[i - 1];
- for (i = op->dummy.nbytes; i > 0; i--, tx_cnt++)
- txbuf[tx_cnt] = 0x0;
- if (op->data.dir == SPI_MEM_DATA_OUT)
- for (i = op->data.nbytes; i > 0; i--, tx_cnt++)
- txbuf[tx_cnt] = ((u8 *)op->data.buf.out)[i - 1];
+ /*
+ * An invalid op may reach here if the caller calls exec_op without
+ * adjust_op_size. return -EINVAL instead of -ENOTSUPP so that
+ * spi-mem won't try this op again with generic spi transfers.
+ */
+ if ((tx_len > MTK_NOR_REG_PRGDATA_MAX + 1) ||
+ (rx_len > MTK_NOR_REG_SHIFT_MAX + 1) ||
+ (prg_len > MTK_NOR_PRG_CNT_MAX / 8))
+ return -EINVAL;
- for (i = MTK_NOR_REG_PRGDATA_MAX; i >= 0; i--)
- writeb(0, priv->base + MTK_NOR_REG_PRGDATA(i));
+ /* fill tx data */
- for (i = 0; i < tx_len; i++, reg_offset--)
- writeb(txbuf[i], priv->base + MTK_NOR_REG_PRGDATA(reg_offset));
+ for (i = op->cmd.nbytes; i > 0; i--, reg_offset--) {
+ reg = priv->base + MTK_NOR_REG_PRGDATA(reg_offset);
+ val = (op->cmd.opcode >> ((i - 1) * BITS_PER_BYTE)) & 0xff;
+ writeb(val, reg);
+ }
- kfree(txbuf);
+ for (i = op->addr.nbytes; i > 0; i--, reg_offset--) {
+ reg = priv->base + MTK_NOR_REG_PRGDATA(reg_offset);
+ val = (op->addr.val >> ((i - 1) * BITS_PER_BYTE)) & 0xff;
+ writeb(val, reg);
+ }
- writel(trx_len * BITS_PER_BYTE, priv->base + MTK_NOR_REG_PRG_CNT);
+ if (op->data.dir == SPI_MEM_DATA_OUT) {
+ for (i = 0; i < op->dummy.nbytes; i++, reg_offset--) {
+ reg = priv->base + MTK_NOR_REG_PRGDATA(reg_offset);
+ writeb(0, reg);
+ }
- mtk_snor_cmd_exec(priv, MTK_NOR_CMD_PROGRAM, trx_len * BITS_PER_BYTE);
+ for (i = 0; i < op->data.nbytes; i++, reg_offset--) {
+ reg = priv->base + MTK_NOR_REG_PRGDATA(reg_offset);
+ writeb(((const u8 *)(op->data.buf.out))[i], reg);
+ }
+ }
- reg_offset = op->data.nbytes - 1;
- for (i = 0; i < op->data.nbytes; i++, reg_offset--) {
- reg = priv->base + MTK_NOR_REG_SHIFT(reg_offset);
- rxbuf[i] = readb(reg);
+ for (; reg_offset >= 0; reg_offset--) {
+ reg = priv->base + MTK_NOR_REG_PRGDATA(reg_offset);
+ writeb(0, reg);
+ }
+
+ /* trigger op */
+ if (rx_len)
+ writel(prg_len * BITS_PER_BYTE + priv->caps->extra_bit,
+ priv->base + MTK_NOR_REG_PRG_CNT);
+ else
+ writel(prg_len * BITS_PER_BYTE, priv->base + MTK_NOR_REG_PRG_CNT);
+
+ ret = mtk_snor_cmd_exec(priv, MTK_NOR_CMD_PROGRAM, prg_len * BITS_PER_BYTE);
+ if (ret)
+ return ret;
+
+ /* fetch read data */
+ reg_offset = 0;
+ if (op->data.dir == SPI_MEM_DATA_IN) {
+ for (i = op->data.nbytes - 1; i >= 0; i--, reg_offset++) {
+ reg = priv->base + MTK_NOR_REG_SHIFT(reg_offset);
+ ((u8 *)(op->data.buf.in))[i] = readb(reg);
+ }
}
return 0;
@@ -467,12 +527,13 @@ static int mtk_snor_probe(struct udevice *bus)
struct mtk_snor_priv *priv = dev_get_priv(bus);
u8 *buffer;
int ret;
- u32 reg;
priv->base = devfdt_get_addr_ptr(bus);
if (!priv->base)
return -EINVAL;
+ priv->caps = (const void *)dev_get_driver_data(bus);
+
ret = clk_get_by_name(bus, "spi", &priv->spi_clk);
if (ret < 0)
return ret;
@@ -496,7 +557,8 @@ static int mtk_snor_probe(struct udevice *bus)
priv->spi_freq = clk_get_rate(&priv->spi_clk);
printf("spi frequency: %d Hz\n", priv->spi_freq);
- /* With this setting, we issue one command at a time to
+ /*
+ * With this setting, we issue one command at a time to
* accommodate to SPI-mem framework.
*/
writel(MTK_NOR_ENABLE_SF_CMD, priv->base + MTK_NOR_REG_WP);
@@ -504,24 +566,13 @@ static int mtk_snor_probe(struct udevice *bus)
mtk_snor_rmw(priv, MTK_NOR_REG_CFG3,
MTK_NOR_DISABLE_WREN | MTK_NOR_DISABLE_SR_POLL, 0);
- /* Unlock all blocks using write status command.
- * SPI-MEM hasn't implemented unlock procedure on MXIC devices.
- * We may remove this later.
- */
- writel(2 * BITS_PER_BYTE, priv->base + MTK_NOR_REG_PRG_CNT);
- writel(MTK_NOR_UNLOCK_ALL, priv->base + MTK_NOR_REG_PRGDATA(5));
- writel(MTK_NOR_IRQ_WRSR, priv->base + MTK_NOR_REG_IRQ_EN);
- writel(MTK_NOR_CMD_WRSR, priv->base + MTK_NOR_REG_CMD);
- ret = readl_poll_timeout(priv->base + MTK_NOR_REG_IRQ_STAT, reg,
- !(reg & MTK_NOR_IRQ_WRSR),
- ((3 * BITS_PER_BYTE) + 1) * 200);
-
return 0;
}
static int mtk_snor_set_speed(struct udevice *bus, uint speed)
{
- /* MTK's SNOR controller does not have a bus clock divider.
+ /*
+ * MTK's SNOR controller does not have a bus clock divider.
* We setup maximum bus clock in dts.
*/
@@ -530,8 +581,7 @@ static int mtk_snor_set_speed(struct udevice *bus, uint speed)
static int mtk_snor_set_mode(struct udevice *bus, uint mode)
{
- /* We set up mode later for each transmission.
- */
+ /* We set up mode later for each transmission. */
return 0;
}
@@ -547,8 +597,19 @@ static const struct dm_spi_ops mtk_snor_ops = {
.set_mode = mtk_snor_set_mode,
};
+static const struct mtk_snor_caps mtk_snor_caps_default = {
+ .extra_bit = 0,
+};
+
+static const struct mtk_snor_caps mtk_snor_caps_extra_bit = {
+ .extra_bit = 1,
+};
+
static const struct udevice_id mtk_snor_ids[] = {
- { .compatible = "mediatek,mtk-snor" },
+ { .compatible = "mediatek,mtk-snor", .data = (ulong)&mtk_snor_caps_default },
+ { .compatible = "mediatek,mt8188-nor", .data = (ulong)&mtk_snor_caps_extra_bit },
+ { .compatible = "mediatek,mt8189-nor", .data = (ulong)&mtk_snor_caps_extra_bit },
+ { .compatible = "mediatek,mt8195-nor", .data = (ulong)&mtk_snor_caps_default },
{}
};
diff --git a/drivers/spi/sandbox_spi.c b/drivers/spi/sandbox_spi.c
index 4cc016138b1..3ee97d67f4a 100644
--- a/drivers/spi/sandbox_spi.c
+++ b/drivers/spi/sandbox_spi.c
@@ -61,6 +61,22 @@ uint sandbox_spi_get_mode(struct udevice *dev)
return priv->mode;
}
+uint sandbox_spi_get_wordlen(struct udevice *dev)
+{
+ struct spi_slave *slave = dev_get_parent_priv(dev);
+
+ return slave->wordlen;
+}
+
+static int sandbox_spi_set_wordlen(struct udevice *dev, unsigned int wordlen)
+{
+ struct spi_slave *slave = dev_get_parent_priv(dev);
+
+ slave->wordlen = wordlen;
+
+ return 0;
+}
+
static int sandbox_spi_xfer(struct udevice *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
@@ -158,6 +174,7 @@ static const struct dm_spi_ops sandbox_spi_ops = {
.set_mode = sandbox_spi_set_mode,
.cs_info = sandbox_cs_info,
.get_mmap = sandbox_spi_get_mmap,
+ .set_wordlen = sandbox_spi_set_wordlen,
};
static const struct udevice_id sandbox_spi_ids[] = {
diff --git a/drivers/spi/spi-sunxi.c b/drivers/spi/spi-sunxi.c
index 0bdc112d249..08b603f04a2 100644
--- a/drivers/spi/spi-sunxi.c
+++ b/drivers/spi/spi-sunxi.c
@@ -344,7 +344,7 @@ static int sun4i_spi_xfer(struct udevice *dev, unsigned int bitlen,
struct sun4i_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
- u32 len = bitlen / 8;
+ u32 rst, val, len = bitlen / 8;
u8 nbytes;
int ret;
@@ -360,8 +360,11 @@ static int sun4i_spi_xfer(struct udevice *dev, unsigned int bitlen,
sun4i_spi_set_cs(bus, slave_plat->cs[0], true);
/* Reset FIFOs */
- setbits_le32(SPI_REG(priv, SPI_FCR), SPI_BIT(priv, SPI_FCR_RF_RST) |
- SPI_BIT(priv, SPI_FCR_TF_RST));
+ rst = SPI_BIT(priv, SPI_FCR_RF_RST) | SPI_BIT(priv, SPI_FCR_TF_RST);
+ setbits_le32(SPI_REG(priv, SPI_FCR), rst);
+ ret = readl_poll_timeout(SPI_REG(priv, SPI_FCR), val, !(rst & val), 20);
+ if (ret)
+ return -EBUSY;
while (len) {
/* Setup the transfer now... */
diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c
index 6b7ad47c22d..120565df149 100644
--- a/drivers/spi/spi-uclass.c
+++ b/drivers/spi/spi-uclass.c
@@ -88,6 +88,20 @@ void dm_spi_release_bus(struct udevice *dev)
ops->release_bus(dev);
}
+int dm_spi_set_wordlen(struct udevice *dev, unsigned int wordlen)
+{
+ struct udevice *bus = dev->parent;
+ struct dm_spi_ops *ops = spi_get_ops(bus);
+
+ if (bus->uclass->uc_drv->id != UCLASS_SPI)
+ return -EOPNOTSUPP;
+
+ if (!ops->set_wordlen)
+ return -ENOSYS;
+
+ return ops->set_wordlen(dev, wordlen);
+}
+
int dm_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
@@ -141,6 +155,11 @@ int spi_set_speed(struct spi_slave *slave, uint hz)
return ret;
}
+int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen)
+{
+ return dm_spi_set_wordlen(slave->dev, wordlen);
+}
+
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
@@ -245,6 +264,7 @@ static int spi_child_post_bind(struct udevice *dev)
}
plat->mode = mode;
+ plat->wordlen = SPI_DEFAULT_WORDLEN;
return 0;
}
@@ -277,7 +297,7 @@ static int spi_child_pre_probe(struct udevice *dev)
slave->max_hz = plat->max_hz;
slave->mode = plat->mode;
- slave->wordlen = SPI_DEFAULT_WORDLEN;
+ slave->wordlen = plat->wordlen;
return 0;
}
diff --git a/drivers/spi/stm32_spi.c b/drivers/spi/stm32_spi.c
index a1f31cf653c..c93a749cd5b 100644
--- a/drivers/spi/stm32_spi.c
+++ b/drivers/spi/stm32_spi.c
@@ -192,6 +192,11 @@ static void stm32_spi_read_rxfifo(struct udevice *bus)
log_debug("%d bytes left\n", priv->rx_len);
}
+static bool stm32_spi_is_enabled(void __iomem *base)
+{
+ return !!(readl(base + STM32_SPI_CR1) & SPI_CR1_SPE);
+}
+
static int stm32_spi_enable(void __iomem *base)
{
log_debug("\n");
@@ -245,9 +250,7 @@ static void stm32_spi_stopxfer(struct udevice *dev)
dev_dbg(dev, "\n");
- cr1 = readl(base + STM32_SPI_CR1);
-
- if (!(cr1 & SPI_CR1_SPE))
+ if (!stm32_spi_is_enabled(base))
return;
/* Wait on EOT or suspend the flow */
@@ -381,6 +384,44 @@ static int stm32_spi_set_speed(struct udevice *bus, uint hz)
return 0;
}
+static int _stm32_spi_set_wordlen(struct udevice *bus, unsigned int wordlen)
+{
+ struct stm32_spi_priv *priv = dev_get_priv(bus);
+ struct stm32_spi_plat *plat = dev_get_plat(bus);
+ void __iomem *base = plat->base;
+ bool spi_enabled;
+
+ if ((wordlen - 1) < SPI_CFG1_DSIZE_MIN ||
+ (wordlen - 1) > SPI_CFG1_DSIZE) {
+ dev_err(bus, "Cannot set wordlen to %u [%d - %ld]\n",
+ wordlen, SPI_CFG1_DSIZE_MIN + 1,
+ SPI_CFG1_DSIZE + 1);
+ return -EINVAL;
+ }
+
+ spi_enabled = stm32_spi_is_enabled(plat->base);
+ if (spi_enabled)
+ stm32_spi_disable(plat->base);
+
+ dev_dbg(bus, "bits_per_word=%d\n", wordlen);
+
+ priv->cur_bpw = wordlen;
+ clrsetbits_le32(base + STM32_SPI_CFG1, SPI_CFG1_DSIZE,
+ priv->cur_bpw - 1);
+
+ if (spi_enabled)
+ stm32_spi_enable(plat->base);
+
+ return 0;
+}
+
+static int stm32_spi_set_wordlen(struct udevice *slave, unsigned int wordlen)
+{
+ struct udevice *bus = dev_get_parent(slave);
+
+ return _stm32_spi_set_wordlen(bus, wordlen);
+}
+
static int stm32_spi_xfer(struct udevice *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
@@ -394,18 +435,28 @@ static int stm32_spi_xfer(struct udevice *slave, unsigned int bitlen,
u32 xferlen;
u32 mode;
int xfer_status = 0;
+ int nb_words;
xferlen = bitlen / 8;
- if (xferlen <= SPI_CR2_TSIZE)
- writel(xferlen, base + STM32_SPI_CR2);
+ if (priv->cur_bpw <= 8)
+ nb_words = xferlen;
+ else if (priv->cur_bpw <= 16)
+ nb_words = DIV_ROUND_UP(xferlen * 8, 16);
+ else
+ nb_words = DIV_ROUND_UP(xferlen * 8, 32);
+
+ if (nb_words <= SPI_CR2_TSIZE)
+ writel(nb_words, base + STM32_SPI_CR2);
else
return -EMSGSIZE;
priv->tx_buf = dout;
priv->rx_buf = din;
- priv->tx_len = priv->tx_buf ? bitlen / 8 : 0;
- priv->rx_len = priv->rx_buf ? bitlen / 8 : 0;
+ priv->tx_len = priv->tx_buf ? xferlen : 0;
+ priv->rx_len = priv->rx_buf ? xferlen : 0;
+ dev_dbg(bus, "bitlen: %d, xferlen: %d, nb_words: %d\n",
+ bitlen, xferlen, nb_words);
mode = SPI_FULL_DUPLEX;
if (!priv->tx_buf)
@@ -567,9 +618,7 @@ static int stm32_spi_probe(struct udevice *dev)
priv->fifo_size = stm32_spi_get_fifo_size(dev);
priv->cur_mode = SPI_FULL_DUPLEX;
priv->cur_xferlen = 0;
- priv->cur_bpw = SPI_DEFAULT_WORDLEN;
- clrsetbits_le32(base + STM32_SPI_CFG1, SPI_CFG1_DSIZE,
- priv->cur_bpw - 1);
+ _stm32_spi_set_wordlen(dev, SPI_DEFAULT_WORDLEN);
for (i = 0; i < ARRAY_SIZE(plat->cs_gpios); i++) {
if (!dm_gpio_is_valid(&plat->cs_gpios[i]))
@@ -630,10 +679,12 @@ static const struct dm_spi_ops stm32_spi_ops = {
.release_bus = stm32_spi_release_bus,
.set_mode = stm32_spi_set_mode,
.set_speed = stm32_spi_set_speed,
+ .set_wordlen = stm32_spi_set_wordlen,
.xfer = stm32_spi_xfer,
};
static const struct udevice_id stm32_spi_ids[] = {
+ { .compatible = "st,stm32mp25-spi", },
{ .compatible = "st,stm32h7-spi", },
{ }
};
diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig
index 16ef434a8d9..90f740f51d4 100644
--- a/drivers/sysreset/Kconfig
+++ b/drivers/sysreset/Kconfig
@@ -49,6 +49,14 @@ config SYSRESET_CMD_RESET
help
Enable sysreset implementation of the reset command.
+config SYSRESET_CMD_RESET_ARGS
+ bool "Enable reset command to take arguments"
+ help
+ Pass on the arguments received by the 'reset' command to the
+ sysreset driver(s). The sysreset driver(s) may make use of the
+ additional arguments for implementing arch/board specific
+ functionality.
+
if CMD_POWEROFF
config SYSRESET_CMD_POWEROFF
@@ -293,6 +301,13 @@ config SYSRESET_RAA215300
help
Add support for the system reboot via the Renesas RAA215300 PMIC.
+config SYSRESET_QCOM_PSCI
+ bool "Support reset to EDL for Qualcomm SoCs via PSCI"
+ depends on ARM_SMCCC
+ help
+ Add support for the reset to EDL (Emergency Download) on Qualcomm
+ SoCs via PSCI.
+
config SYSRESET_QCOM_PSHOLD
bool "Support sysreset for Qualcomm SoCs via PSHOLD"
help
diff --git a/drivers/sysreset/Makefile b/drivers/sysreset/Makefile
index d18a5d52360..b5b99235b6e 100644
--- a/drivers/sysreset/Makefile
+++ b/drivers/sysreset/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_SYSRESET_RESETCTL) += sysreset_resetctl.o
obj-$(CONFIG_$(PHASE_)SYSRESET_AT91) += sysreset_at91.o
obj-$(CONFIG_$(PHASE_)SYSRESET_X86) += sysreset_x86.o
obj-$(CONFIG_SYSRESET_RAA215300) += sysreset_raa215300.o
+obj-$(CONFIG_SYSRESET_QCOM_PSCI) += sysreset_qcom-psci.o
obj-$(CONFIG_SYSRESET_QCOM_PSHOLD) += sysreset_qcom-pshold.o
obj-$(CONFIG_TARGET_XTFPGA) += sysreset_xtfpga.o
obj-$(CONFIG_SYSRESET_QEMU_VIRT_CTRL) += sysreset_qemu_virt_ctrl.o
diff --git a/drivers/sysreset/sysreset-uclass.c b/drivers/sysreset/sysreset-uclass.c
index 536ac727142..f25e09e9cd0 100644
--- a/drivers/sysreset/sysreset-uclass.c
+++ b/drivers/sysreset/sysreset-uclass.c
@@ -32,6 +32,18 @@ int sysreset_request(struct udevice *dev, enum sysreset_t type)
return ops->request(dev, type);
}
+#if IS_ENABLED(CONFIG_SYSRESET_CMD_RESET_ARGS)
+int sysreset_request_arg(struct udevice *dev, int argc, char * const argv[])
+{
+ struct sysreset_ops *ops = sysreset_get_ops(dev);
+
+ if (!ops->request_arg)
+ return -ENOSYS;
+
+ return ops->request_arg(dev, argc, argv);
+}
+#endif /* CONFIG_SYSRESET_CMD_RESET_ARGS */
+
int sysreset_get_status(struct udevice *dev, char *buf, int size)
{
struct sysreset_ops *ops = sysreset_get_ops(dev);
@@ -71,6 +83,26 @@ int sysreset_walk(enum sysreset_t type)
return ret;
}
+#if IS_ENABLED(CONFIG_SYSRESET_CMD_RESET_ARGS)
+int sysreset_walk_arg(int argc, char * const argv[])
+{
+ struct udevice *dev;
+ int ret = -ENOSYS;
+
+ while (ret != -EINPROGRESS && ret != -EPROTONOSUPPORT) {
+ for (uclass_first_device(UCLASS_SYSRESET, &dev);
+ dev;
+ uclass_next_device(&dev)) {
+ ret = sysreset_request_arg(dev, argc, argv);
+ if (ret == -EINPROGRESS || ret == -EPROTONOSUPPORT)
+ break;
+ }
+ }
+
+ return ret;
+}
+#endif /* CONFIG_SYSRESET_CMD_RESET_ARGS */
+
int sysreset_get_last_walk(void)
{
struct udevice *dev;
@@ -132,6 +164,11 @@ int do_reset(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
printf("resetting ...\n");
mdelay(100);
+#if IS_ENABLED(CONFIG_SYSRESET_CMD_RESET_ARGS)
+ if (argc > 1 && sysreset_walk_arg(argc, argv) == -EINPROGRESS)
+ return 0;
+#endif
+
sysreset_walk_halt(reset_type);
return 0;
diff --git a/drivers/sysreset/sysreset_qcom-psci.c b/drivers/sysreset/sysreset_qcom-psci.c
new file mode 100644
index 00000000000..3627bbf5c82
--- /dev/null
+++ b/drivers/sysreset/sysreset_qcom-psci.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Masahiro Yamada <[email protected]>
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <dm.h>
+#include <sysreset.h>
+#include <asm/system.h>
+#include <linux/errno.h>
+#include <linux/psci.h>
+#include <asm/psci.h>
+
+static int qcom_psci_sysreset_get_status(struct udevice *dev, char *buf, int size)
+{
+ return -EOPNOTSUPP;
+}
+
+static int qcom_psci_sysreset_request_arg(struct udevice *dev, int argc,
+ char * const argv[])
+{
+ if (!strncasecmp(argv[1], "-edl", 4)) {
+ /* Supported in qcs9100, qcs8300, sc7280, qcs615 */
+ if (psci_features(ARM_PSCI_1_1_FN64_SYSTEM_RESET2) ==
+ ARM_PSCI_RET_SUCCESS) {
+ psci_system_reset2(0, 1);
+ return -EINPROGRESS;
+ }
+ printf("PSCI SYSTEM_RESET2 not supported\n");
+ }
+
+ return -EPROTONOSUPPORT;
+}
+
+static struct sysreset_ops qcom_psci_sysreset_ops = {
+ .request_arg = qcom_psci_sysreset_request_arg,
+ .get_status = qcom_psci_sysreset_get_status,
+};
+
+U_BOOT_DRIVER(qcom_psci_sysreset) = {
+ .name = "qcom_psci-sysreset",
+ .id = UCLASS_SYSRESET,
+ .ops = &qcom_psci_sysreset_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 5fc0505c788..4d67c948ec1 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -795,6 +795,11 @@ static optee_invoke_fn *get_invoke_func(struct udevice *dev)
return ERR_PTR(-EINVAL);
}
+bool is_optee_smc_api(void)
+{
+ return is_optee_api(optee_smccc_smc);
+}
+
static int optee_of_to_plat(struct udevice *dev)
{
struct optee_pdata *pdata = dev_get_plat(dev);
diff --git a/drivers/ufs/ufs-mediatek.c b/drivers/ufs/ufs-mediatek.c
index e860d765eea..268627d5863 100644
--- a/drivers/ufs/ufs-mediatek.c
+++ b/drivers/ufs/ufs-mediatek.c
@@ -182,19 +182,15 @@ static int ufs_mtk_bind_mphy(struct ufs_hba *hba)
struct ufs_mtk_host *host = dev_get_priv(hba->dev);
int err = 0;
- err = generic_phy_get_by_index(hba->dev, 0, host->mphy);
+ err = generic_phy_get_by_index(hba->dev, 0, &host->mphy);
- if (IS_ERR(host->mphy)) {
- err = PTR_ERR(host->mphy);
- if (err != -ENODEV) {
- dev_info(hba->dev, "%s: Could NOT get a valid PHY %d\n", __func__,
- err);
- }
+ if (err) {
+ if (err == -ENOENT)
+ return 0; /* no PHY, nothing to do */
+ dev_err(hba->dev, "Failed to get PHY: %d.\n", err);
+ return err;
}
- if (err)
- host->mphy = NULL;
-
return err;
}
@@ -321,19 +317,35 @@ static int ufs_mtk_init(struct ufs_hba *hba)
ufs_mtk_init_reset(hba);
- // TODO: Clocking
+ err = clk_get_bulk(hba->dev, &priv->clks);
+ if (err) {
+ dev_err(hba->dev, "failed to initialize clocks, err:%d\n", err);
+ return err;
+ }
+
+ err = clk_enable_bulk(&priv->clks);
+ if (err) {
+ dev_err(hba->dev, "failed to enable clocks, err:%d\n", err);
+ goto err_clk_enable;
+ }
- err = generic_phy_power_on(priv->mphy);
+ err = generic_phy_power_on(&priv->mphy);
if (err) {
dev_err(hba->dev, "%s: phy init failed, err = %d\n",
__func__, err);
- return err;
+ goto err_phy_power_on;
}
ufs_mtk_setup_ref_clk(hba, true);
ufs_mtk_get_hw_ip_version(hba);
return 0;
+
+err_phy_power_on:
+ clk_disable_bulk(&priv->clks);
+err_clk_enable:
+ clk_release_bulk(&priv->clks);
+ return err;
}
static int ufs_mtk_device_reset(struct ufs_hba *hba)
@@ -383,7 +395,9 @@ static int ufs_mtk_probe(struct udevice *dev)
static const struct udevice_id ufs_mtk_ids[] = {
{ .compatible = "mediatek,mt6878-ufshci" },
- {},
+ { .compatible = "mediatek,mt8183-ufshci" },
+ { .compatible = "mediatek,mt8195-ufshci" },
+ { }
};
U_BOOT_DRIVER(mediatek_ufshci) = {
diff --git a/drivers/ufs/ufs-mediatek.h b/drivers/ufs/ufs-mediatek.h
index 11a83d34c5b..0ffd0483eff 100644
--- a/drivers/ufs/ufs-mediatek.h
+++ b/drivers/ufs/ufs-mediatek.h
@@ -154,7 +154,7 @@ struct ufs_mtk_mcq_intr_info {
};
struct ufs_mtk_host {
- struct phy *mphy;
+ struct phy mphy;
struct reset_ctl *unipro_reset;
struct reset_ctl *crypto_reset;
struct reset_ctl *hci_reset;
diff --git a/drivers/ufs/ufs-renesas-rcar-gen5.c b/drivers/ufs/ufs-renesas-rcar-gen5.c
index a21ae3f390e..a9473cd60b1 100644
--- a/drivers/ufs/ufs-renesas-rcar-gen5.c
+++ b/drivers/ufs/ufs-renesas-rcar-gen5.c
@@ -92,14 +92,14 @@ static int ufs_renesas_pre_init(struct ufs_hba *hba)
ufs_dme_command(hba, 0x00000002, 0x81010000, 0x00000000, 0x00000005);
ufs_dme_command(hba, 0x00000002, 0x81150000, 0x00000000, 0x00000001);
ufs_dme_command(hba, 0x00000002, 0x81180000, 0x00000000, 0x00000001);
- ufs_dme_command(hba, 0x00000002, 0x80090000, 0x00000000, 0x00000000);
- ufs_dme_command(hba, 0x00000002, 0x800a0000, 0x00000000, 0x000000c8);
- ufs_dme_command(hba, 0x00000002, 0x80090001, 0x00000000, 0x00000000);
- ufs_dme_command(hba, 0x00000002, 0x800a0001, 0x00000000, 0x000000c8);
- ufs_dme_command(hba, 0x00000002, 0x800a0004, 0x00000000, 0x00000000);
- ufs_dme_command(hba, 0x00000002, 0x800b0004, 0x00000000, 0x00000064);
- ufs_dme_command(hba, 0x00000002, 0x800a0005, 0x00000000, 0x00000000);
- ufs_dme_command(hba, 0x00000002, 0x800b0005, 0x00000000, 0x00000064);
+ ufs_dme_command(hba, 0x00000002, 0x80090000, 0x00000000, 0x0000000c);
+ ufs_dme_command(hba, 0x00000002, 0x800a0000, 0x00000000, 0x00000080);
+ ufs_dme_command(hba, 0x00000002, 0x80090001, 0x00000000, 0x0000000c);
+ ufs_dme_command(hba, 0x00000002, 0x800a0001, 0x00000000, 0x00000080);
+ ufs_dme_command(hba, 0x00000002, 0x800a0004, 0x00000000, 0x00000003);
+ ufs_dme_command(hba, 0x00000002, 0x800b0004, 0x00000000, 0x000000ea);
+ ufs_dme_command(hba, 0x00000002, 0x800a0005, 0x00000000, 0x00000003);
+ ufs_dme_command(hba, 0x00000002, 0x800b0005, 0x00000000, 0x000000ea);
ufs_dme_command(hba, 0x00000002, 0xd0850000, 0x00000000, 0x00000001);
writew(0x0001, priv->phy_base + 0x20000);
diff --git a/drivers/ufs/ufs-rockchip.c b/drivers/ufs/ufs-rockchip.c
index a13236c7f76..dc4b9b5c86d 100644
--- a/drivers/ufs/ufs-rockchip.c
+++ b/drivers/ufs/ufs-rockchip.c
@@ -19,13 +19,22 @@
#include "unipro.h"
#include "ufs-rockchip.h"
+static void ufs_rockchip_controller_reset(struct ufs_rockchip_host *host)
+{
+ reset_assert_bulk(&host->rsts);
+ udelay(1);
+ reset_deassert_bulk(&host->rsts);
+}
+
static int ufs_rockchip_hce_enable_notify(struct ufs_hba *hba,
enum ufs_notify_change_status status)
{
int err = 0;
- if (status != POST_CHANGE)
+ if (status != POST_CHANGE) {
+ ufs_rockchip_controller_reset(dev_get_priv(hba->dev));
return 0;
+ }
ufshcd_dme_reset(hba);
ufshcd_dme_enable(hba);
@@ -150,6 +159,8 @@ static int ufs_rockchip_common_init(struct ufs_hba *hba)
return err;
}
+ ufs_rockchip_controller_reset(host);
+
err = gpio_request_by_name(dev, "reset-gpios", 0, &host->device_reset,
GPIOD_IS_OUT | GPIOD_ACTIVE_LOW);
if (err) {
diff --git a/drivers/ufs/ufs-uclass.c b/drivers/ufs/ufs-uclass.c
index 81fd431f951..6a51f337e47 100644
--- a/drivers/ufs/ufs-uclass.c
+++ b/drivers/ufs/ufs-uclass.c
@@ -1751,7 +1751,15 @@ static int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index,
goto out;
}
- buff_ascii = kmalloc(ascii_len, GFP_KERNEL);
+ /*
+ * utf-8 is encoded using up to 4-Bytes per character,
+ * however, we only allocate such a buffer because the
+ * utf16_to_utf8() converts the entire $ascii_len worth
+ * of input characters into up to 4-Byte long utf-8
+ * characters. The rest of the function uses only up to
+ * $ascii_len bytes of that utf-8 string.
+ */
+ buff_ascii = kmalloc(ascii_len * 4, GFP_KERNEL);
if (!buff_ascii) {
err = -ENOMEM;
goto out;
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 65c4d1a4e6f..be198041f08 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -106,6 +106,8 @@ done:
if (DWC3_VER_IS_WITHIN(DWC31, ANY, 180A))
mdelay(50);
+ mdelay(100);
+
return 0;
}
@@ -206,7 +208,6 @@ static void dwc3_free_one_event_buffer(struct dwc3 *dwc,
struct dwc3_event_buffer *evt)
{
dma_free_coherent(evt->buf);
- free(evt);
}
/**
diff --git a/drivers/usb/emul/Kconfig b/drivers/usb/emul/Kconfig
index 279f6c6d740..6305f2496c3 100644
--- a/drivers/usb/emul/Kconfig
+++ b/drivers/usb/emul/Kconfig
@@ -2,6 +2,7 @@ config USB_EMUL
bool "Support for USB device emulation"
depends on SANDBOX
select DM_USB
+ select SCSI
select USB_HOST
help
Since sandbox does not have access to a real USB bus, it is possible
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index baa2eb61ea3..5390878254a 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -119,6 +119,7 @@ config USB_GADGET_DWC2_OTG
config USB_RENESAS_USBHS
bool "Renesas R-Car USB2.0 HS controller (gadget mode)"
+ depends on DM_USB_GADGET
select USB_GADGET_DUALSPEED
help
The Renesas R-Car USB 2.0 high-speed gadget controller
@@ -232,7 +233,7 @@ endif # USB_GADGET_DOWNLOAD
config USB_ETHER
bool "USB Ethernet Gadget"
- depends on NET || NET_LWIP
+ depends on NET
default y if ARCH_SUNXI && USB_MUSB_GADGET
help
Creates an Ethernet network device through a USB peripheral
diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c
index f7a92ded6da..a2eee2bca2c 100644
--- a/drivers/usb/gadget/atmel_usba_udc.c
+++ b/drivers/usb/gadget/atmel_usba_udc.c
@@ -289,10 +289,6 @@ static int usba_ep_disable(struct usb_ep *_ep)
if (!ep->desc) {
spin_unlock_irqrestore(&udc->lock, flags);
- /* REVISIT because this driver disables endpoints in
- * reset_all_endpoints() before calling disconnect(),
- * most gadget drivers would trigger this non-error ...
- */
if (udc->gadget.speed != USB_SPEED_UNKNOWN)
DBG(DBG_ERR, "ep_disable: %s not enabled\n",
ep->ep.name);
@@ -571,20 +567,6 @@ static void reset_all_endpoints(struct usba_udc *udc)
list_del_init(&req->queue);
request_complete(ep, req, -ECONNRESET);
}
-
- /* NOTE: normally, the next call to the gadget driver is in
- * charge of disabling endpoints... usually disconnect().
- * The exception would be entering a high speed test mode.
- *
- * FIXME remove this code ... and retest thoroughly.
- */
- list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
- if (ep->desc) {
- spin_unlock(&udc->lock);
- usba_ep_disable(&ep->ep);
- spin_lock(&udc->lock);
- }
- }
}
static struct usba_ep *get_ep_by_addr(struct usba_udc *udc, u16 wIndex)
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
index 8f7256069f5..b6c11d97a62 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/f_acm.c
@@ -548,13 +548,11 @@ static int acm_add(struct usb_configuration *c)
status = udc_device_get_by_index(0, &f_acm->udc);
if (status)
- return status;
+ goto err;
status = usb_add_function(c, &f_acm->usb_function);
- if (status) {
- free(f_acm);
- return status;
- }
+ if (status)
+ goto err;
buf_init(&f_acm->rx_buf, 2048);
buf_init(&f_acm->tx_buf, 2048);
@@ -562,6 +560,10 @@ static int acm_add(struct usb_configuration *c)
if (!default_acm_function)
default_acm_function = f_acm;
+ return 0;
+
+err:
+ free(f_acm);
return status;
}
diff --git a/drivers/usb/gadget/rcar/common.c b/drivers/usb/gadget/rcar/common.c
index 2ba022a3f2c..10548f7a20c 100644
--- a/drivers/usb/gadget/rcar/common.c
+++ b/drivers/usb/gadget/rcar/common.c
@@ -15,6 +15,7 @@
#include <linux/err.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <reset.h>
#include <usb.h>
#include "common.h"
@@ -91,6 +92,12 @@ void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable)
u16 mask = DCFM | DRPD | DPRPU | HSE | USBE;
u16 val = HSE | USBE;
+ /* CNEN bit is required for function operation */
+ if (usbhs_get_dparam(priv, has_cnen)) {
+ mask |= CNEN;
+ val |= CNEN;
+ }
+
/*
* if enable
*
@@ -290,6 +297,9 @@ struct usbhs_priv_otg_data {
void __iomem *base;
void __iomem *phybase;
+ struct clk_bulk clk_bulk;
+ struct reset_ctl_bulk reset_bulk;
+
struct platform_device usbhs_dev;
struct usbhs_priv usbhs_priv;
@@ -355,15 +365,25 @@ static int usbhs_udc_otg_gadget_handle_interrupts(struct udevice *dev)
return 0;
}
-static int usbhs_probe(struct usbhs_priv *priv)
+static int usbhs_probe(struct udevice *dev)
{
+ struct usbhs_priv_otg_data *otg_priv = dev_get_priv(dev);
+ struct usbhs_priv *priv = &otg_priv->usbhs_priv;
+ struct renesas_usbhs_driver_param *plat_param;
int ret;
+ plat_param = (struct renesas_usbhs_driver_param *)dev_get_driver_data(dev);
+
priv->dparam.type = USBHS_TYPE_RCAR_GEN3;
priv->dparam.pio_dma_border = 64;
priv->dparam.pipe_configs = usbhsc_new_pipe;
priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_new_pipe);
+ if (plat_param) {
+ priv->dparam.has_cnen = plat_param->has_cnen;
+ priv->dparam.cfifo_byte_addr = plat_param->cfifo_byte_addr;
+ }
+
/* call pipe and module init */
ret = usbhs_pipe_probe(priv);
if (ret < 0)
@@ -396,34 +416,41 @@ static int usbhs_udc_otg_probe(struct udevice *dev)
{
struct usbhs_priv_otg_data *priv = dev_get_priv(dev);
struct usb_gadget *gadget;
- struct clk_bulk clk_bulk;
int ret = -EINVAL;
priv->base = dev_read_addr_ptr(dev);
if (!priv->base)
return -EINVAL;
- ret = clk_get_bulk(dev, &clk_bulk);
+ ret = clk_get_bulk(dev, &priv->clk_bulk);
if (ret)
return ret;
- ret = clk_enable_bulk(&clk_bulk);
+ ret = clk_enable_bulk(&priv->clk_bulk);
if (ret)
- return ret;
+ goto err_clk_enable;
+
+ ret = reset_get_bulk(dev, &priv->reset_bulk);
+ if (ret)
+ goto err_clk;
+
+ ret = reset_deassert_bulk(&priv->reset_bulk);
+ if (ret)
+ goto err_reset_deassert;
clrsetbits_le32(priv->base + UGCTRL2, UGCTRL2_USB0SEL_MASK, UGCTRL2_USB0SEL_EHCI);
clrsetbits_le16(priv->base + LPSTS, LPSTS_SUSPM, LPSTS_SUSPM);
ret = generic_setup_phy(dev, &priv->phy, 0, PHY_MODE_USB_OTG, 1);
if (ret)
- goto err_clk;
+ goto err_reset;
priv->phybase = dev_read_addr_ptr(priv->phy.dev);
priv->usbhs_priv.pdev = &priv->usbhs_dev;
priv->usbhs_priv.base = priv->base;
priv->usbhs_dev.dev.driver_data = &priv->usbhs_priv;
- ret = usbhs_probe(&priv->usbhs_priv);
+ ret = usbhs_probe(dev);
if (ret < 0)
goto err_phy;
@@ -439,27 +466,49 @@ static int usbhs_udc_otg_probe(struct udevice *dev)
err_phy:
generic_shutdown_phy(&priv->phy);
+err_reset:
+ reset_assert_bulk(&priv->reset_bulk);
+err_reset_deassert:
+ reset_release_bulk(&priv->reset_bulk);
err_clk:
- clk_disable_bulk(&clk_bulk);
+ clk_disable_bulk(&priv->clk_bulk);
+err_clk_enable:
+ clk_release_bulk(&priv->clk_bulk);
+
return ret;
}
static int usbhs_udc_otg_remove(struct udevice *dev)
{
struct usbhs_priv_otg_data *priv = dev_get_priv(dev);
+ struct usb_gadget *gadget;
usbhs_rcar3_power_ctrl(&priv->usbhs_priv, false);
+ gadget = usbhsg_get_gadget(&priv->usbhs_priv);
+ usb_del_gadget_udc(gadget);
usbhs_mod_remove(&priv->usbhs_priv);
usbhs_fifo_remove(&priv->usbhs_priv);
usbhs_pipe_remove(&priv->usbhs_priv);
generic_shutdown_phy(&priv->phy);
+ reset_assert_bulk(&priv->reset_bulk);
+ reset_release_bulk(&priv->reset_bulk);
+
+ clk_disable_bulk(&priv->clk_bulk);
+ clk_release_bulk(&priv->clk_bulk);
+
return dm_scan_fdt_dev(dev);
}
+static struct renesas_usbhs_driver_param rzg2l_param = {
+ .has_cnen = 1,
+ .cfifo_byte_addr = 1,
+};
+
static const struct udevice_id usbhs_udc_otg_ids[] = {
{ .compatible = "renesas,rcar-gen3-usbhs" },
+ { .compatible = "renesas,rzg2l-usbhs", .data = (unsigned long)&rzg2l_param },
{},
};
diff --git a/drivers/usb/gadget/rcar/renesas_usb.h b/drivers/usb/gadget/rcar/renesas_usb.h
index 8155e3dcaf6..140a70251cf 100644
--- a/drivers/usb/gadget/rcar/renesas_usb.h
+++ b/drivers/usb/gadget/rcar/renesas_usb.h
@@ -111,6 +111,7 @@ struct renesas_usbhs_driver_param {
u32 has_otg:1; /* for controlling PWEN/EXTLP */
u32 has_sudmac:1; /* for SUDMAC */
u32 has_usb_dmac:1; /* for USB-DMAC */
+ u32 has_cnen:1;
u32 cfifo_byte_addr:1; /* CFIFO is byte addressable */
#define USBHS_USB_DMAC_XFER_SIZE 32 /* hardcode the xfer size */
u32 multi_clks:1;
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index ef4ce62a680..9cac53f07c7 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -5,7 +5,7 @@
ifdef CONFIG_$(PHASE_)DM_USB
obj-y += usb-uclass.o
-obj-$(CONFIG_SANDBOX) += usb-sandbox.o
+obj-$(CONFIG_USB_EMUL) += usb-sandbox.o
endif
ifdef CONFIG_$(PHASE_)USB_STORAGE
diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c
index 7247245a702..1c74d6fd39a 100644
--- a/drivers/usb/host/usb-uclass.c
+++ b/drivers/usb/host/usb-uclass.c
@@ -134,7 +134,7 @@ int usb_alloc_device(struct usb_device *udev)
struct udevice *bus = udev->controller_dev;
struct dm_usb_ops *ops = usb_get_ops(bus);
- /* This is only requird by some controllers - current XHCI */
+ /* This is only required by some controllers - currently XHCI */
if (!ops->alloc_device)
return 0;
diff --git a/drivers/usb/host/xhci-mvebu.c b/drivers/usb/host/xhci-mvebu.c
index 12dc61aee9d..c294a56b3c9 100644
--- a/drivers/usb/host/xhci-mvebu.c
+++ b/drivers/usb/host/xhci-mvebu.c
@@ -82,6 +82,7 @@ static int xhci_usb_of_to_plat(struct udevice *dev)
static const struct udevice_id xhci_usb_ids[] = {
{ .compatible = "marvell,armada3700-xhci" },
+ { .compatible = "marvell,armada-375-xhci" },
{ .compatible = "marvell,armada-380-xhci" },
{ .compatible = "marvell,armada-8k-xhci" },
{ }
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 08c9b020788..c2acc13139c 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -552,6 +552,14 @@ config VIDEO_LCD_HIMAX_HX8394
Say Y here if you want to enable support for Himax HX8394
dsi 4dl panel.
+config VIDEO_LCD_ILITEK_ILI9806E
+ bool "Ilitek ILI9806E-based panels"
+ depends on PANEL && BACKLIGHT
+ help
+ Say Y here if you want to enable support for panels base on
+ the Ilitek ILI9806E controller. Currently only the DBI panel
+ is implemented.
+
config VIDEO_LCD_MOT
tristate "Atrix 4G and Droid X2 540x960 DSI video mode panel"
depends on PANEL && BACKLIGHT
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 984768ea156..082b8967982 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -24,7 +24,7 @@ obj-$(CONFIG_$(PHASE_)PANEL) += panel-uclass.o
obj-$(CONFIG_PANEL_HX8238D) += hx8238d.o
obj-$(CONFIG_$(PHASE_)SIMPLE_PANEL) += simple_panel.o
-obj-$(CONFIG_VIDEO_LOGO) += u_boot_logo.o
+obj-$(CONFIG_VIDEO_LOGO) += u_boot_logo.bmp.o
obj-$(CONFIG_$(PHASE_)BMP) += bmp.o
endif
@@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_LCD_ENDEAVORU) += endeavoru-panel.o
obj-$(CONFIG_VIDEO_LCD_HIMAX_HX8394) += himax-hx8394.o
obj-$(CONFIG_VIDEO_LCD_HITACHI_TX10D07VM0BAA) += hitachi-tx10d07vm0baa.o
obj-$(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM) += hitachi_tx18d42vm_lcd.o
+obj-$(CONFIG_VIDEO_LCD_ILITEK_ILI9806E) += ilitek-ili9806e.o
obj-$(CONFIG_VIDEO_LCD_LG_LD070WX3) += lg-ld070wx3.o
obj-$(CONFIG_VIDEO_LCD_LG_LH400WV3) += lg-lh400wv3-sd04.o
obj-$(CONFIG_VIDEO_LCD_MOT) += mot-panel.o
diff --git a/drivers/video/bcm2835.c b/drivers/video/bcm2835.c
index 0c81e606622..0e0cc1979eb 100644
--- a/drivers/video/bcm2835.c
+++ b/drivers/video/bcm2835.c
@@ -66,6 +66,7 @@ static int bcm2835_video_probe(struct udevice *dev)
static const struct udevice_id bcm2835_video_ids[] = {
{ .compatible = "brcm,bcm2835-hdmi" },
{ .compatible = "brcm,bcm2711-hdmi0" },
+ { .compatible = "brcm,bcm2712-hdmi0" },
{ .compatible = "brcm,bcm2708-fb" },
#if !IS_ENABLED(CONFIG_VIDEO_DT_SIMPLEFB)
{ .compatible = "simple-framebuffer" },
diff --git a/drivers/video/fonts/Makefile b/drivers/video/fonts/Makefile
index 1111f92a2c6..4d32fa43994 100644
--- a/drivers/video/fonts/Makefile
+++ b/drivers/video/fonts/Makefile
@@ -3,8 +3,8 @@
# (C) Copyright 2000-2007
# Wolfgang Denk, DENX Software Engineering, [email protected].
-obj-$(CONFIG_CONSOLE_TRUETYPE_NIMBUS) += nimbus_sans_l_regular.o
-obj-$(CONFIG_CONSOLE_TRUETYPE_ANKACODER) += ankacoder_c75_r.o
-obj-$(CONFIG_CONSOLE_TRUETYPE_RUFSCRIPT) += rufscript010.o
-obj-$(CONFIG_CONSOLE_TRUETYPE_CANTORAONE) += cantoraone_regular.o
-obj-$(CONFIG_CONSOLE_TRUETYPE_DEJAVU) += dejavu_mono.o
+obj-$(CONFIG_CONSOLE_TRUETYPE_NIMBUS) += nimbus_sans_l_regular.ttf.o
+obj-$(CONFIG_CONSOLE_TRUETYPE_ANKACODER) += ankacoder_c75_r.ttf.o
+obj-$(CONFIG_CONSOLE_TRUETYPE_RUFSCRIPT) += rufscript010.ttf.o
+obj-$(CONFIG_CONSOLE_TRUETYPE_CANTORAONE) += cantoraone_regular.ttf.o
+obj-$(CONFIG_CONSOLE_TRUETYPE_DEJAVU) += dejavu_mono.ttf.o
diff --git a/drivers/video/ilitek-ili9806e.c b/drivers/video/ilitek-ili9806e.c
new file mode 100644
index 00000000000..9ba580639c9
--- /dev/null
+++ b/drivers/video/ilitek-ili9806e.c
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 Amarula Solutions, Dario Binacchi <[email protected]>
+ */
+
+#include <backlight.h>
+#include <dm.h>
+#include <mipi_display.h>
+#include <panel.h>
+#include <spi.h>
+#include <asm/gpio.h>
+#include <dm/device_compat.h>
+#include <linux/delay.h>
+#include <power/regulator.h>
+
+struct ilitek_ili9806e_priv {
+ struct udevice *vdd;
+ struct udevice *backlight;
+ struct gpio_desc reset_gpio;
+ const struct ilitek_ili9806e_desc *desc;
+};
+
+struct ilitek_ili9806e_desc {
+ const struct display_timing timing;
+ void (*init_sequence)(struct udevice *dev);
+};
+
+static int ilitek_ili9806e_dcs_write(struct udevice *dev, u8 cmd, const u8 *seq, int len)
+{
+ u16 data[16];
+ int i, ret;
+
+ if ((len + 1) > ARRAY_SIZE(data)) {
+ dev_err(dev, "Command length (%d) exceeds buffer size (%lu)\n",
+ len + 1, ARRAY_SIZE(data));
+ return -EMSGSIZE;
+ }
+
+ data[0] = cmd;
+ if (len) {
+ for (i = 0; i < len; i++)
+ data[i + 1] = seq[i] | 0x0100;
+ }
+
+ ret = dm_spi_xfer(dev, (len + 1) * 8 * sizeof(u16), data, NULL,
+ SPI_XFER_ONCE);
+ return 0;
+}
+
+#define ilitek_ili9806e_dcs_write_seq(dev, cmd, seq...) \
+({ \
+ static const u8 b[] = { seq }; \
+ ilitek_ili9806e_dcs_write(dev, cmd, b, ARRAY_SIZE(b)); \
+})
+
+static int ilitek_ili9806e_enable_backlight(struct udevice *dev)
+{
+ struct ilitek_ili9806e_priv *priv = dev_get_priv(dev);
+ const struct ilitek_ili9806e_desc *desc = priv->desc;
+
+ desc->init_sequence(dev);
+
+ return panel_set_backlight(dev, BACKLIGHT_DEFAULT);
+}
+
+static int ilitek_ili9806e_set_backlight(struct udevice *dev, int percent)
+{
+ struct ilitek_ili9806e_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = backlight_enable(priv->backlight);
+ if (ret) {
+ dev_err(dev, "Cannot enable backlight\n");
+ return ret;
+ }
+
+ ret = backlight_set_brightness(priv->backlight, percent);
+ if (ret)
+ dev_err(dev, "Cannot set backlight brightness\n");
+
+ return ret;
+}
+
+static int ilitek_ili9806e_get_display_timing(struct udevice *dev,
+ struct display_timing *timing)
+{
+ struct ilitek_ili9806e_priv *priv = dev_get_priv(dev);
+
+ memcpy(timing, &priv->desc->timing, sizeof(*timing));
+
+ return 0;
+}
+
+static int ilitek_ili9806e_of_to_plat(struct udevice *dev)
+{
+ struct ilitek_ili9806e_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ if (CONFIG_IS_ENABLED(DM_REGULATOR)) {
+ ret = device_get_supply_regulator(dev, "vdd-supply", &priv->vdd);
+ if (ret) {
+ dev_err(dev, "Cannot get vdd supply\n");
+ return ret;
+ }
+ }
+
+ ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
+ "backlight", &priv->backlight);
+ if (ret) {
+ dev_err(dev, "Cannot get backlight\n");
+ return ret;
+ }
+
+ ret = gpio_request_by_name(dev, "reset-gpios", 0,
+ &priv->reset_gpio, GPIOD_IS_OUT);
+ if (ret) {
+ dev_err(dev, "Cannot get reset GPIO\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ilitek_ili9806e_hw_init(struct udevice *dev)
+{
+ struct ilitek_ili9806e_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 1);
+ if (ret) {
+ dev_err(dev, "Cannot enter reset\n");
+ return ret;
+ }
+
+ ret = regulator_set_enable_if_allowed(priv->vdd, 1);
+ if (ret) {
+ dev_err(dev, "Cannot enable vdd-supply\n");
+ return ret;
+ }
+
+ mdelay(20);
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+ if (ret) {
+ dev_err(dev, "Cannot exit reset\n");
+ return ret;
+ }
+
+ mdelay(20);
+
+ return 0;
+}
+
+static int ilitek_ili9806e_probe(struct udevice *dev)
+{
+ struct ilitek_ili9806e_priv *priv = dev_get_priv(dev);
+ struct spi_slave *slave = dev_get_parent_priv(dev);
+ int ret;
+
+ ret = spi_set_wordlen(slave, 9);
+ if (ret) {
+ dev_err(dev, "Cannot set SPI.bits_per_word\n");
+ return ret;
+ }
+
+ ret = spi_claim_bus(slave);
+ if (ret) {
+ dev_err(dev, "Cannot get SPI bus\n");
+ return ret;
+ }
+
+ priv->desc = (struct ilitek_ili9806e_desc *)dev_get_driver_data(dev);
+
+ return ilitek_ili9806e_hw_init(dev);
+}
+
+static const struct panel_ops ilitek_ili9806e_ops = {
+ .enable_backlight = ilitek_ili9806e_enable_backlight,
+ .set_backlight = ilitek_ili9806e_set_backlight,
+ .get_display_timing = ilitek_ili9806e_get_display_timing,
+};
+
+static void rk050hr345_ct106a_init(struct udevice *dev)
+{
+ /* Switch to page 1 */
+ ilitek_ili9806e_dcs_write_seq(dev, 0xff, 0xff, 0x98, 0x06, 0x04, 0x01);
+ /* Interface Settings */
+ ilitek_ili9806e_dcs_write_seq(dev, 0x08, 0x10);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x21, 0x01);
+ /* Panel Settings */
+ ilitek_ili9806e_dcs_write_seq(dev, 0x30, 0x01);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x31, 0x00);
+ /* Power Control */
+ ilitek_ili9806e_dcs_write_seq(dev, 0x40, 0x15);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x41, 0x44);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x42, 0x03);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x43, 0x09);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x44, 0x09);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x50, 0x78);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x51, 0x78);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x52, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x53, 0x3a);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x57, 0x50);
+ /* Timing Control */
+ ilitek_ili9806e_dcs_write_seq(dev, 0x60, 0x07);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x61, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x62, 0x08);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x63, 0x00);
+ /* Gamma Settings */
+ ilitek_ili9806e_dcs_write_seq(dev, 0xa0, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xa1, 0x03);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xa2, 0x0b);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xa3, 0x0f);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xa4, 0x0b);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xa5, 0x1b);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xa6, 0x0a);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xa7, 0x0a);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xa8, 0x02);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xa9, 0x07);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xaa, 0x05);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xab, 0x03);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xac, 0x0e);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xad, 0x32);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xae, 0x2d);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xaf, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xc0, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xc1, 0x03);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xc2, 0x0e);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xc3, 0x10);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xc4, 0x09);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xc5, 0x17);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xc6, 0x09);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xc7, 0x07);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xc8, 0x04);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xc9, 0x09);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xca, 0x06);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xcb, 0x06);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xcc, 0x0c);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xcd, 0x25);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xce, 0x20);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xcf, 0x00);
+
+ /* Switch to page 6 */
+ ilitek_ili9806e_dcs_write_seq(dev, 0xff, 0xff, 0x98, 0x06, 0x04, 0x06);
+ /* GIP settings */
+ ilitek_ili9806e_dcs_write_seq(dev, 0x00, 0x21);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x01, 0x09);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x02, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x03, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x04, 0x01);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x05, 0x01);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x06, 0x80);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x07, 0x05);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x08, 0x02);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x09, 0x80);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x0a, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x0b, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x0c, 0x0a);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x0d, 0x0a);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x0e, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x0f, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x10, 0xe0);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x11, 0xe4);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x12, 0x04);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x13, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x14, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x15, 0xc0);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x16, 0x08);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x17, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x18, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x19, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x1a, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x1b, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x1c, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x1d, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x20, 0x01);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x21, 0x23);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x22, 0x45);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x23, 0x67);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x24, 0x01);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x25, 0x23);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x26, 0x45);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x27, 0x67);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x30, 0x01);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x31, 0x11);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x32, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x33, 0xee);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x34, 0xff);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x35, 0xbb);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x36, 0xca);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x37, 0xdd);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x38, 0xac);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x39, 0x76);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x3a, 0x67);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x3b, 0x22);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x3c, 0x22);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x3d, 0x22);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x3e, 0x22);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x3f, 0x22);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x40, 0x22);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x52, 0x10);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x53, 0x10);
+
+ /* Switch to page 7 */
+ ilitek_ili9806e_dcs_write_seq(dev, 0xff, 0xff, 0x98, 0x06, 0x04, 0x07);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x17, 0x22);
+ ilitek_ili9806e_dcs_write_seq(dev, 0x02, 0x77);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xe1, 0x79);
+ ilitek_ili9806e_dcs_write_seq(dev, 0xb3, 0x10);
+
+ /* Switch to page 0 */
+ ilitek_ili9806e_dcs_write_seq(dev, 0xff, 0xff, 0x98, 0x06, 0x04, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
+ ilitek_ili9806e_dcs_write_seq(dev, MIPI_DCS_EXIT_SLEEP_MODE);
+
+ mdelay(120);
+
+ ilitek_ili9806e_dcs_write_seq(dev, MIPI_DCS_SET_DISPLAY_ON);
+
+ mdelay(120);
+}
+
+static const struct ilitek_ili9806e_desc rk050hr345_ct106a_desc = {
+ .timing = {
+ .pixelclock.typ = 27000000,
+ .hactive.typ = 480,
+ .hfront_porch.typ = 10,
+ .hback_porch.typ = 10,
+ .hsync_len.typ = 10,
+ .vactive.typ = 854,
+ .vfront_porch.typ = 10,
+ .vback_porch.typ = 10,
+ .vsync_len.typ = 10,
+ .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW,
+ },
+ .init_sequence = rk050hr345_ct106a_init,
+};
+
+static const struct udevice_id ilitek_ili9806e_ids[] = {
+ {
+ .compatible = "rocktech,rk050hr345-ct106a",
+ .data = (ulong)&rk050hr345_ct106a_desc,
+ },
+ { }
+};
+
+U_BOOT_DRIVER(ilitek_ili9806e) = {
+ .name = "ilitek_ili9806e",
+ .id = UCLASS_PANEL,
+ .of_match = ilitek_ili9806e_ids,
+ .ops = &ilitek_ili9806e_ops,
+ .of_to_plat = ilitek_ili9806e_of_to_plat,
+ .probe = ilitek_ili9806e_probe,
+ .priv_auto = sizeof(struct ilitek_ili9806e_priv),
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/video/stm32/stm32_dsi.c b/drivers/video/stm32/stm32_dsi.c
index 65a91f5cff7..5c4d8d2aab5 100644
--- a/drivers/video/stm32/stm32_dsi.c
+++ b/drivers/video/stm32/stm32_dsi.c
@@ -493,8 +493,11 @@ static int stm32_dsi_probe(struct udevice *dev)
priv->hw_version != HWVER_131) {
dev_err(dev, "DSI version 0x%x not supported\n", priv->hw_version);
dev_dbg(dev, "remove and unbind all DSI child\n");
- device_chld_remove(dev, NULL, DM_REMOVE_NORMAL);
- device_chld_unbind(dev, NULL);
+ ret = device_chld_remove(dev, NULL, DM_REMOVE_NORMAL);
+ if (!ret)
+ ret = device_chld_unbind(dev, NULL);
+ if (ret)
+ dev_err(dev, "Unbinding from %s failed %d\n", dev->name, ret);
ret = -ENODEV;
goto err_clk;
}
diff --git a/drivers/virtio/virtio_blk.c b/drivers/virtio/virtio_blk.c
index 3dd0cf36268..7b1d891cdcb 100644
--- a/drivers/virtio/virtio_blk.c
+++ b/drivers/virtio/virtio_blk.c
@@ -13,7 +13,9 @@
#include <virtio.h>
#include <virtio_ring.h>
#include <linux/log2.h>
+#include <linux/err.h>
#include "virtio_blk.h"
+#include <malloc.h>
/**
* struct virtio_blk_priv - private data for virtio block device
@@ -23,10 +25,16 @@ struct virtio_blk_priv {
struct virtqueue *vq;
/** @blksz_shift - log2 of block size divided by 512 */
u32 blksz_shift;
+ /** @size_max - maximum segment size */
+ u32 size_max;
+ /** @seg_max - maximum segment count */
+ u32 seg_max;
};
static const u32 feature[] = {
VIRTIO_BLK_F_BLK_SIZE,
+ VIRTIO_BLK_F_SIZE_MAX,
+ VIRTIO_BLK_F_SEG_MAX,
VIRTIO_BLK_F_WRITE_ZEROES
};
@@ -67,50 +75,77 @@ static void virtio_blk_init_data_sg(void *buffer, lbaint_t blkcnt, struct virtio
sg->length = blkcnt * 512;
}
-static ulong virtio_blk_do_req(struct udevice *dev, u64 sector,
- lbaint_t blkcnt, void *buffer, u32 type)
+/*
+ * Create, execute and wait for one single virtio request. On success the
+ * transferred block count is returned and in the error case -EIO.
+ */
+static ulong virtio_blk_do_single_req(struct udevice *dev, u64 sector,
+ lbaint_t blkcnt, char *buffer, u32 type)
{
struct virtio_blk_priv *priv = dev_get_priv(dev);
+ /*
+ * The virtio device may have constrains on the maximum segment size.
+ * Calculate how many segments we need.
+ */
+ u32 seg_cnt = (blkcnt * 512) / priv->size_max + 1;
+ lbaint_t seg_sec_cnt = priv->size_max / 512;
struct virtio_blk_outhdr out_hdr;
struct virtio_blk_discard_write_zeroes wz_hdr;
unsigned int num_out = 0, num_in = 0;
- struct virtio_sg hdr_sg, wz_sg, data_sg, status_sg;
- struct virtio_sg *sgs[3];
- u8 status;
+ struct virtio_sg **sgs;
+ u8 status = VIRTIO_BLK_S_IOERR;
int ret;
+ u32 i;
- 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;
+ /*
+ * +2 is header and status descriptor; seg_cnt is the number of data segments
+ * required. Needs to be dynamically allocated.
+ */
+ sgs = calloc(seg_cnt + 2, sizeof(struct virtio_sg *));
+ if (!sgs)
+ return -ENOMEM;
+
+ for (i = 0; i < seg_cnt + 2; ++i) {
+ sgs[i] = malloc(sizeof(struct virtio_sg));
+ if (!sgs[i])
+ goto err_free;
+ }
+
+ virtio_blk_init_header_sg(dev, sector, type, &out_hdr, sgs[num_out++]);
switch (type) {
case VIRTIO_BLK_T_IN:
- case VIRTIO_BLK_T_OUT:
- virtio_blk_init_data_sg(buffer, blkcnt, &data_sg);
- if (type & VIRTIO_BLK_T_OUT)
- sgs[num_out++] = &data_sg;
- else
- sgs[num_out + num_in++] = &data_sg;
- break;
+ case VIRTIO_BLK_T_OUT: {
+ i = 0;
+ while (i < blkcnt) {
+ u32 blk_per_seg = min(blkcnt - i, seg_sec_cnt);
+ if (type & VIRTIO_BLK_T_OUT)
+ virtio_blk_init_data_sg(buffer + i * 512, blk_per_seg,
+ sgs[num_out++]);
+ else
+ virtio_blk_init_data_sg(buffer + i * 512, blk_per_seg,
+ sgs[num_out + num_in++]);
+ i += blk_per_seg;
+ }
+ break;
+ }
case VIRTIO_BLK_T_WRITE_ZEROES:
- virtio_blk_init_write_zeroes_sg(dev, sector, blkcnt, &wz_hdr, &wz_sg);
- sgs[num_out++] = &wz_sg;
+ virtio_blk_init_write_zeroes_sg(dev, sector, blkcnt, &wz_hdr,
+ sgs[num_out++]);
break;
default:
- return -EINVAL;
+ goto err_free;
}
- virtio_blk_init_status_sg(&status, &status_sg);
- sgs[num_out + num_in++] = &status_sg;
+ virtio_blk_init_status_sg(&status, sgs[num_out + num_in++]);
log_debug("dev=%s, active=%d, priv=%p, priv->vq=%p\n", dev->name,
device_active(dev), priv, priv->vq);
ret = virtqueue_add(priv->vq, sgs, num_out, num_in);
if (ret)
- return ret;
+ goto err_free;
virtqueue_kick(priv->vq);
@@ -119,7 +154,40 @@ static ulong virtio_blk_do_req(struct udevice *dev, u64 sector,
;
log_debug("done\n");
- return status == VIRTIO_BLK_S_OK ? blkcnt >> priv->blksz_shift : -EIO;
+err_free:
+ for (i = 0; i < seg_cnt + 2; ++i)
+ free(sgs[i]);
+ free(sgs);
+
+ return status == VIRTIO_BLK_S_OK ? blkcnt : -EIO;
+}
+
+static ulong virtio_blk_do_req(struct udevice *dev, u64 sector,
+ lbaint_t blkcnt, char *buffer, u32 type)
+{
+ struct virtio_blk_priv *priv = dev_get_priv(dev);
+ lbaint_t seg_sec_cnt = priv->size_max / 512;
+ u32 i = 0;
+ ulong ret;
+
+ sector <<= priv->blksz_shift;
+ blkcnt <<= priv->blksz_shift;
+
+ /*
+ * The virtio device may have constrains on the maximum segment count. So
+ * send multiple virtio requests one after each other, if so.
+ */
+ while (i < blkcnt) {
+ u32 blk_per_sg = min(blkcnt - i, seg_sec_cnt * priv->seg_max);
+
+ ret = virtio_blk_do_single_req(dev, sector + i, blk_per_sg,
+ buffer + i * 512, type);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ i += blk_per_sg;
+ }
+
+ return blkcnt >> priv->blksz_shift;
}
static ulong virtio_blk_read(struct udevice *dev, lbaint_t start,
@@ -207,6 +275,15 @@ static int virtio_blk_probe(struct udevice *dev)
priv->blksz_shift = desc->log2blksz - 9;
desc->lba >>= priv->blksz_shift;
+ if (virtio_has_feature(dev, VIRTIO_BLK_F_SIZE_MAX))
+ virtio_cread(dev, struct virtio_blk_config, size_max, &priv->size_max);
+ else
+ priv->size_max = -1U;
+ if (virtio_has_feature(dev, VIRTIO_BLK_F_SEG_MAX))
+ virtio_cread(dev, struct virtio_blk_config, seg_max, &priv->seg_max);
+ else
+ priv->seg_max = -1U;
+
return 0;
}
diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
index 1cd737aca24..d90d8309f99 100644
--- a/drivers/virtio/virtio_mmio.c
+++ b/drivers/virtio/virtio_mmio.c
@@ -354,7 +354,7 @@ static int virtio_mmio_probe(struct udevice *udev)
magic = readl(priv->base + VIRTIO_MMIO_MAGIC_VALUE);
if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) {
debug("(%s): wrong magic value 0x%08x!\n", udev->name, magic);
- return 0;
+ return -ENODEV;
}
/* Check device version */
@@ -362,7 +362,7 @@ static int virtio_mmio_probe(struct udevice *udev)
if (priv->version < 1 || priv->version > 2) {
debug("(%s): version %d not supported!\n",
udev->name, priv->version);
- return 0;
+ return -ENXIO;
}
/* Check device ID */
@@ -372,7 +372,7 @@ static int virtio_mmio_probe(struct udevice *udev)
* virtio-mmio device with an ID 0 is a (dummy) placeholder
* with no function. End probing now with no error reported.
*/
- return 0;
+ return -ENOENT;
}
uc_priv->vendor = readl(priv->base + VIRTIO_MMIO_VENDOR_ID);
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 416d29d256a..9ea617f1e43 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -356,6 +356,13 @@ config WDT_RENESAS
help
Enables Renesas SoC R8A779F0 watchdog timer support.
+config WDT_RENESAS_WWDT
+ bool "Renesas window watchdog timer support"
+ depends on WDT
+ select SYSCON
+ help
+ Enables Renesas window watchdog timer support.
+
config WDT_SANDBOX
bool "Enable Watchdog Timer support for Sandbox"
depends on SANDBOX && WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 02e2674f8af..84faefdb4c2 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_WDT_NPCM) += npcm_wdt.o
obj-$(CONFIG_WDT_OCTEONTX) += octeontx_wdt.o
obj-$(CONFIG_WDT_OMAP3) += omap_wdt.o
obj-$(CONFIG_WDT_RENESAS) += renesas_wdt.o
+obj-$(CONFIG_WDT_RENESAS_WWDT) += renesas_wwdt.o
obj-$(CONFIG_WDT_SBSA) += sbsa_gwdt.o
obj-$(CONFIG_WDT_K3_RTI) += rti_wdt.o
obj-$(CONFIG_WDT_SIEMENS_PMIC) += siemens_pmic_wdt.o
diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c
index c7307f41cb7..9e998994216 100644
--- a/drivers/watchdog/apple_wdt.c
+++ b/drivers/watchdog/apple_wdt.c
@@ -78,6 +78,7 @@ static const struct wdt_ops apple_wdt_ops = {
};
static const struct udevice_id apple_wdt_ids[] = {
+ { .compatible = "apple,t8103-wdt" },
{ .compatible = "apple,wdt" },
{ /* sentinel */ }
};
diff --git a/drivers/watchdog/renesas_wwdt.c b/drivers/watchdog/renesas_wwdt.c
new file mode 100644
index 00000000000..f6f508c95c2
--- /dev/null
+++ b/drivers/watchdog/renesas_wwdt.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 Marek Vasut <[email protected]>
+ */
+
+#include <asm/io.h>
+#include <clk.h>
+#include <dm/device_compat.h>
+#include <dm.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <wdt.h>
+
+#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
+
+#define RSIP_CTL_CFG4 0xc0
+#define RSIP_CTL_CFG4_OPWDEN BIT(3)
+#define RSIP_CTL_CFG4_OPWDVAC BIT(5)
+
+#define WDTA0_ACT_CODE 0xac
+#define WDTA0WDTE 0x0
+#define WDTA0EVAC 0x4
+#define WDTA0REF 0x8
+#define WDTA0MD 0xc
+#define WDTA0MD_OVF_MASK GENMASK(6, 4)
+#define WDTA0MD_OVF(n) field_prep(WDTA0MD_OVF_MASK, (n))
+#define WDTA0MD_NWIE BIT(3)
+#define WDTA0MD_NERM BIT(2)
+#define WDTA0MD_NVS_MASK GENMASK(1, 0)
+#define WDTA0MD_NVS_75P FIELD_PREP(WDTA0MD_NVS_MASK, 3)
+
+struct wwdt_priv {
+ void __iomem *base;
+ struct regmap *ctl;
+ unsigned int timeout;
+};
+
+/**
+ * wwdt_reset() - Reset or ping Window WDT
+ * @dev: Watchdog device
+ */
+static int wwdt_reset(struct udevice *dev)
+{
+ struct wwdt_priv *priv = dev_get_priv(dev);
+ const u32 cfg = readl(priv->ctl->ranges[0].start + RSIP_CTL_CFG4);
+ u32 rv;
+
+ /* WDT disabled, do nothing. */
+ if (!(cfg & RSIP_CTL_CFG4_OPWDEN))
+ return 0;
+
+ /* WDT with variable activation code */
+ if (cfg & RSIP_CTL_CFG4_OPWDVAC) {
+ rv = readb(priv->base + WDTA0REF);
+ rv = WDTA0_ACT_CODE - rv;
+ writeb(rv, priv->base + WDTA0EVAC);
+ } else {
+ writeb(WDTA0_ACT_CODE, priv->base + WDTA0WDTE);
+ }
+
+ return 0;
+}
+
+/**
+ * wwdt_start() - Start Window WDT
+ * @dev: Watchdog device
+ * @timeout: Watchdog timeout (not used)
+ * @flags: Flags (not used)
+ */
+static int wwdt_start(struct udevice *dev, u64 timeout, ulong flags)
+{
+ struct wwdt_priv *priv = dev_get_priv(dev);
+
+ clrsetbits_8(priv->base + WDTA0MD,
+ WDTA0MD_OVF_MASK | WDTA0MD_NWIE |
+ WDTA0MD_NERM | WDTA0MD_NVS_MASK,
+ WDTA0MD_OVF(priv->timeout) | WDTA0MD_NWIE |
+ WDTA0MD_NVS_75P);
+
+ wwdt_reset(dev);
+
+ return 0;
+}
+
+/**
+ * wwdt_probe() - Initialize Window WDT hardware
+ * @dev: Watchdog device
+ */
+static int wwdt_probe(struct udevice *dev)
+{
+ struct wwdt_priv *priv = dev_get_priv(dev);
+ struct udevice *syscon;
+ unsigned long rate;
+ struct clk *clk;
+ int ret;
+
+ priv->base = dev_remap_addr(dev);
+ if (!priv->base)
+ return -EINVAL;
+
+ ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, "syscon", &syscon);
+ if (ret) {
+ dev_err(dev, "Failed to get syscon\n");
+ return ret;
+ }
+
+ priv->ctl = syscon_get_regmap(syscon);
+ if (!priv->ctl) {
+ dev_err(dev, "Failed to get regmap\n");
+ return -ENODEV;
+ }
+
+ clk = devm_clk_get(dev, "cnt");
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ dev_err(dev, "Failed to get counter clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_enable(clk);
+ if (ret)
+ return ret;
+
+ rate = clk_get_rate(clk);
+ if (!rate) {
+ clk_disable(clk);
+ return -ENOENT;
+ }
+
+ /*
+ * Interval time is in "2^9..2^16 / clk_wdt" range. WDTA0OVFx is
+ * in 0..7 range. The code below does the WDTA0OVFx calculation
+ * from "interval_time = (1 << N) / clk_wdt" by caculating the N.
+ * The N rounded down is MSbit of (interval_time * clk_wdt). The
+ * result is then clamped to fit into the N in 9..16 range, and
+ * decremented by 9 to fit into WDTA0OVFx in 0..7 range .
+ */
+ priv->timeout = clamp(fls(CONFIG_WATCHDOG_TIMEOUT_MSECS * rate) - 1, 9, 16) - 9;
+
+ return 0;
+}
+
+static const struct wdt_ops wwdt_ops = {
+ .start = wwdt_start,
+ .reset = wwdt_reset,
+};
+
+static const struct udevice_id wwdt_ids[] = {
+ { .compatible = "renesas,rcar-gen5-wwdt" },
+ {}
+};
+
+U_BOOT_DRIVER(wwdt_renesas) = {
+ .name = "wwdt_renesas",
+ .id = UCLASS_WDT,
+ .of_match = wwdt_ids,
+ .ops = &wwdt_ops,
+ .probe = wwdt_probe,
+ .priv_auto = sizeof(struct wwdt_priv),
+};