diff options
Diffstat (limited to 'drivers')
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, ®s); - 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, ®s); - 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, ®s->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(®s->raf) | XAE_RAF_MCSTREJ_MASK | + XAE_RAF_BCSTREJ_MASK, ®s->raf); + else + writel(readl(®s->raf) | XAE_RAF_BCSTREJ_MASK, + ®s->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(®s->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), ®s->rxqbase); -#if defined(CONFIG_PHYS_64BIT) - writel(upper_32_bits((ulong)priv->rx_bd), ®s->upper_rxqbase); -#endif - /* Setup for DMA Configuration register */ writel(ZYNQ_GEM_DMACR_INIT, ®s->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), ®s->rxqbase); +#if defined(CONFIG_PHYS_64BIT) + writel(upper_32_bits((ulong)priv->rx_bd), ®s->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(®s->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(®s->nwcfg); + nwcfg &= ~(ZYNQ_GEM_NWCFG_SPEED100 | ZYNQ_GEM_NWCFG_SPEED1000); nwcfg |= nwconfig; if (nwcfg) writel(nwcfg, ®s->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, ®ion, 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, ®ion, 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, ®ion, 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), +}; |
