summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/Kconfig1
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk-uclass.c2
-rw-r--r--drivers/clk/clk_scmi.c204
-rw-r--r--drivers/clk/imx/clk-imxrt1170.c9
-rw-r--r--drivers/clk/mediatek/clk-mt7981.c2
-rw-r--r--drivers/clk/mediatek/clk-mt7986.c2
-rw-r--r--drivers/clk/mediatek/clk-mt7987.c2
-rw-r--r--drivers/clk/mediatek/clk-mt7988.c2
-rw-r--r--drivers/clk/mediatek/clk-mtk.c11
-rw-r--r--drivers/clk/mediatek/clk-mtk.h1
-rw-r--r--drivers/clk/thead/Kconfig19
-rw-r--r--drivers/clk/thead/Makefile5
-rw-r--r--drivers/clk/thead/clk-th1520-ap.c1031
-rw-r--r--drivers/cpu/imx8_cpu.c2
-rw-r--r--drivers/crypto/fsl/jr.c25
-rw-r--r--drivers/crypto/fsl/jr.h4
-rw-r--r--drivers/firmware/scmi/base.c24
-rw-r--r--drivers/firmware/scmi/sandbox-scmi_agent.c56
-rw-r--r--drivers/firmware/scmi/scmi_agent-uclass.c50
-rw-r--r--drivers/firmware/ti_sci.c6
-rw-r--r--drivers/gpio/pca953x.c1
-rw-r--r--drivers/gpio/tegra_gpio.c11
-rw-r--r--drivers/misc/Kconfig6
-rw-r--r--drivers/mmc/cv1800b_sdhci.c4
-rw-r--r--drivers/mtd/spi/spi-nor-core.c29
-rw-r--r--drivers/mtd/ubi/build.c2
-rw-r--r--drivers/net/Kconfig2
-rw-r--r--drivers/net/dc2114x.c1
-rw-r--r--drivers/net/dwc_eth_qos.c4
-rw-r--r--drivers/net/dwc_eth_qos.h1
-rw-r--r--drivers/net/dwc_eth_qos_stm32.c11
-rw-r--r--drivers/net/dwc_eth_xgmac.c35
-rw-r--r--drivers/net/e1000.c5
-rw-r--r--drivers/net/e1000.h1
-rw-r--r--drivers/net/fsl_enetc.c4
-rw-r--r--drivers/net/gmac_rockchip.c69
-rw-r--r--drivers/net/netconsole.c11
-rw-r--r--drivers/net/pfe_eth/pfe_firmware.c1
-rw-r--r--drivers/net/phy/phy.c2
-rw-r--r--drivers/net/ravb.c173
-rw-r--r--drivers/pinctrl/nxp/Kconfig13
-rw-r--r--drivers/pinctrl/nxp/Makefile1
-rw-r--r--drivers/pinctrl/nxp/pinctrl-imx-scmi.c165
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-px30.c2
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rk3036.c2
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rk3066.c2
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rk3128.c2
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rk3188.c2
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rk322x.c2
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rk3288.c2
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rk3308.c2
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rk3328.c2
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rk3368.c2
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rk3399.c2
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rk3568.c1
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rk3588.c1
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rockchip-core.c5
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rockchip.h1
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rv1108.c2
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rv1126.c1
-rw-r--r--drivers/power/acpi_pmc/acpi-pmc-uclass.c2
-rw-r--r--drivers/power/axp221.c1
-rw-r--r--drivers/power/domain/scmi-power-domain.c8
-rw-r--r--drivers/power/pmic/Kconfig9
-rw-r--r--drivers/power/pmic/Makefile1
-rw-r--r--drivers/power/pmic/max8907.c94
-rw-r--r--drivers/power/regulator/Kconfig9
-rw-r--r--drivers/power/regulator/Makefile1
-rw-r--r--drivers/power/regulator/max8907_regulator.c249
-rw-r--r--drivers/power/regulator/scmi_regulator.c8
-rw-r--r--drivers/ram/Kconfig1
-rw-r--r--drivers/ram/Makefile4
-rw-r--r--drivers/ram/thead/Kconfig5
-rw-r--r--drivers/ram/thead/Makefile1
-rw-r--r--drivers/ram/thead/th1520_ddr.c787
-rw-r--r--drivers/reset/reset-scmi.c8
-rw-r--r--drivers/scsi/scsi-uclass.c30
-rw-r--r--drivers/spi/nxp_fspi.c55
-rw-r--r--drivers/sysreset/Kconfig7
-rw-r--r--drivers/sysreset/Makefile1
-rw-r--r--drivers/sysreset/sysreset_max8907.c37
-rw-r--r--drivers/tpm/sandbox_common.c1
-rw-r--r--drivers/usb/dwc3/core.c14
-rw-r--r--drivers/usb/host/Kconfig7
-rw-r--r--drivers/usb/host/ehci-tegra.c7
-rw-r--r--drivers/usb/ulpi/Kconfig28
-rw-r--r--drivers/usb/ulpi/Makefile1
-rw-r--r--drivers/usb/ulpi/omap-ulpi-viewport.c71
-rw-r--r--drivers/video/Kconfig33
-rw-r--r--drivers/video/Makefile4
-rw-r--r--drivers/video/aat2870_backlight.c132
-rw-r--r--drivers/video/console_truetype.c90
-rw-r--r--drivers/video/hitachi-tx10d07vm0baa.c304
-rw-r--r--drivers/video/lg-lh400wv3-sd04.c230
-rw-r--r--drivers/video/nexell_display.c1
-rw-r--r--drivers/video/novatek-nt35510.c1253
-rw-r--r--drivers/video/tegra/Kconfig10
-rw-r--r--drivers/video/tegra/Makefile1
-rw-r--r--drivers/video/tegra/cpu-bridge.c325
-rw-r--r--drivers/video/vidconsole-uclass.c36
-rw-r--r--drivers/video/video-uclass.c52
102 files changed, 5667 insertions, 295 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 18bd640a68b..19aa2ffa539 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -271,6 +271,7 @@ source "drivers/clk/starfive/Kconfig"
source "drivers/clk/stm32/Kconfig"
source "drivers/clk/tegra/Kconfig"
source "drivers/clk/ti/Kconfig"
+source "drivers/clk/thead/Kconfig"
source "drivers/clk/uniphier/Kconfig"
endmenu
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 8411205ee04..5f0c0d8a5c2 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -20,6 +20,7 @@ obj-y += imx/
obj-$(CONFIG_CLK_JH7110) += starfive/
obj-y += tegra/
obj-y += ti/
+obj-$(CONFIG_CLK_THEAD) += thead/
obj-$(CONFIG_$(PHASE_)CLK_INTEL) += intel/
obj-$(CONFIG_ARCH_ASPEED) += aspeed/
obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index bc4d76277cd..2167cd5ad0f 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -627,7 +627,7 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
return -ENOSYS;
ret = clk_enable(parent);
- if (ret) {
+ if (ret && ret != -ENOSYS) {
printf("Cannot enable parent %s\n", parent->dev->name);
return ret;
}
diff --git a/drivers/clk/clk_scmi.c b/drivers/clk/clk_scmi.c
index e42d2032d45..af69850cdd8 100644
--- a/drivers/clk/clk_scmi.c
+++ b/drivers/clk/clk_scmi.c
@@ -8,10 +8,59 @@
#include <clk-uclass.h>
#include <dm.h>
#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
#include <scmi_protocols.h>
#include <asm/types.h>
#include <linux/clk-provider.h>
+struct clk_scmi {
+ struct clk clk;
+ u32 ctrl_flags;
+};
+
+struct scmi_clock_priv {
+ u32 version;
+};
+
+static int scmi_clk_get_permissions(struct udevice *dev, int clkid, u32 *perm)
+{
+ struct scmi_clock_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ struct scmi_clk_get_permissions_in in = {
+ .clock_id = clkid,
+ };
+ struct scmi_clk_get_permissions_out out;
+ struct scmi_msg msg = {
+ .protocol_id = SCMI_PROTOCOL_ID_CLOCK,
+ .message_id = SCMI_CLOCK_GET_PERMISSIONS,
+ .in_msg = (u8 *)&in,
+ .in_msg_sz = sizeof(in),
+ .out_msg = (u8 *)&out,
+ .out_msg_sz = sizeof(out),
+ };
+
+ if (priv->version < CLOCK_PROTOCOL_VERSION_3_0) {
+ log_debug("%s: SCMI clock management protocol version is less than 3.0.\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret) {
+ log_debug("%s: get SCMI clock management protocol permissions failed\n", __func__);
+ return ret;
+ }
+
+ ret = scmi_to_linux_errno(out.status);
+ if (ret < 0) {
+ log_debug("%s: the status code of getting permissions: %d\n", __func__, ret);
+ return ret;
+ }
+
+ *perm = out.permissions;
+ return 0;
+}
+
static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks)
{
struct scmi_clk_protocol_attr_out out;
@@ -32,7 +81,8 @@ static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks)
return 0;
}
-static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name)
+static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name,
+ u32 *attr)
{
struct scmi_clk_attribute_in in = {
.clock_id = clkid,
@@ -53,6 +103,7 @@ static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name)
return ret;
*name = strdup(out.clock_name);
+ *attr = out.attributes;
return 0;
}
@@ -78,12 +129,48 @@ static int scmi_clk_gate(struct clk *clk, int enable)
static int scmi_clk_enable(struct clk *clk)
{
- return scmi_clk_gate(clk, 1);
+ struct clk_scmi *clkscmi;
+ struct clk *c;
+ int ret;
+
+ if (!CONFIG_IS_ENABLED(CLK_CCF))
+ return scmi_clk_gate(clk, 1);
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ clkscmi = container_of(c, struct clk_scmi, clk);
+
+ if (clkscmi->ctrl_flags & SUPPORT_CLK_STAT_CONTROL)
+ return scmi_clk_gate(clk, 1);
+
+ /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
+ log_debug("%s: SCMI CLOCK: the clock cannot be enabled by the agent.\n", __func__);
+ return 0;
}
static int scmi_clk_disable(struct clk *clk)
{
- return scmi_clk_gate(clk, 0);
+ struct clk_scmi *clkscmi;
+ struct clk *c;
+ int ret;
+
+ if (!CONFIG_IS_ENABLED(CLK_CCF))
+ return scmi_clk_gate(clk, 0);
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ clkscmi = container_of(c, struct clk_scmi, clk);
+
+ if (clkscmi->ctrl_flags & SUPPORT_CLK_STAT_CONTROL)
+ return scmi_clk_gate(clk, 0);
+
+ /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
+ log_debug("%s: SCMI CLOCK: the clock cannot be disabled by the agent.\n", __func__);
+ return 0;
}
static ulong scmi_clk_get_rate(struct clk *clk)
@@ -108,7 +195,7 @@ static ulong scmi_clk_get_rate(struct clk *clk)
return (ulong)(((u64)out.rate_msb << 32) | out.rate_lsb);
}
-static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
+static ulong __scmi_clk_set_rate(struct clk *clk, ulong rate)
{
struct scmi_clk_rate_set_in in = {
.clock_id = clk->id,
@@ -133,9 +220,33 @@ static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
return scmi_clk_get_rate(clk);
}
+static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
+{
+ struct clk_scmi *clkscmi;
+ struct clk *c;
+ int ret;
+
+ if (!CONFIG_IS_ENABLED(CLK_CCF))
+ return __scmi_clk_set_rate(clk, rate);
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ clkscmi = container_of(c, struct clk_scmi, clk);
+
+ if (clkscmi->ctrl_flags & SUPPORT_CLK_RATE_CONTROL)
+ return __scmi_clk_set_rate(clk, rate);
+
+ /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
+ log_debug("%s: SCMI CLOCK: the clock rate cannot be changed by the agent.\n", __func__);
+ return 0;
+}
+
static int scmi_clk_probe(struct udevice *dev)
{
- struct clk *clk;
+ struct clk_scmi *clk_scmi;
+ struct scmi_clock_priv *priv = dev_get_priv(dev);
size_t num_clocks, i;
int ret;
@@ -154,35 +265,98 @@ static int scmi_clk_probe(struct udevice *dev)
if (ret)
return ret;
+ ret = scmi_generic_protocol_version(dev, SCMI_PROTOCOL_ID_CLOCK, &priv->version);
+ if (ret) {
+ log_debug("%s: get SCMI clock management protocol version failed\n", __func__);
+ return ret;
+ }
+
for (i = 0; i < num_clocks; i++) {
char *clock_name;
+ u32 attributes;
- if (!scmi_clk_get_attibute(dev, i, &clock_name)) {
- clk = kzalloc(sizeof(*clk), GFP_KERNEL);
- if (!clk || !clock_name)
+ if (!scmi_clk_get_attibute(dev, i, &clock_name, &attributes)) {
+ clk_scmi = kzalloc(sizeof(*clk_scmi), GFP_KERNEL);
+ if (!clk_scmi || !clock_name)
ret = -ENOMEM;
else
- ret = clk_register(clk, dev->driver->name,
+ ret = clk_register(&clk_scmi->clk, dev->driver->name,
clock_name, dev->name);
if (ret) {
- free(clk);
+ free(clk_scmi);
free(clock_name);
return ret;
}
- clk_dm(i, clk);
+ clk_dm(i, &clk_scmi->clk);
+
+ if (CLK_HAS_RESTRICTIONS(attributes)) {
+ u32 perm;
+
+ ret = scmi_clk_get_permissions(dev, i, &perm);
+ if (ret < 0)
+ clk_scmi->ctrl_flags = 0;
+ else
+ clk_scmi->ctrl_flags = perm;
+ } else {
+ clk_scmi->ctrl_flags = SUPPORT_CLK_STAT_CONTROL | SUPPORT_CLK_PARENT_CONTROL |
+ SUPPORT_CLK_RATE_CONTROL;
+ }
}
}
return 0;
}
+static int __scmi_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct scmi_clk_parent_set_in in = {
+ .clock_id = clk->id,
+ .parent_clk = parent->id,
+ };
+ struct scmi_clk_parent_set_out out;
+ struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
+ SCMI_CLOCK_PARENT_SET,
+ in, out);
+ int ret;
+
+ ret = devm_scmi_process_msg(clk->dev, &msg);
+ if (ret < 0)
+ return ret;
+
+ return scmi_to_linux_errno(out.status);
+}
+
+static int scmi_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct clk_scmi *clkscmi;
+ struct clk *c;
+ int ret;
+
+ if (!CONFIG_IS_ENABLED(CLK_CCF))
+ return -ENOTSUPP;
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ clkscmi = container_of(c, struct clk_scmi, clk);
+
+ if (clkscmi->ctrl_flags & SUPPORT_CLK_PARENT_CONTROL)
+ return __scmi_clk_set_parent(clk, parent);
+
+ /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
+ log_debug("%s: SCMI CLOCK: the clock's parent cannot be changed by the agent.\n", __func__);
+ return 0;
+}
+
static const struct clk_ops scmi_clk_ops = {
.enable = scmi_clk_enable,
.disable = scmi_clk_disable,
.get_rate = scmi_clk_get_rate,
.set_rate = scmi_clk_set_rate,
+ .set_parent = scmi_clk_set_parent,
};
U_BOOT_DRIVER(scmi_clock) = {
@@ -190,4 +364,12 @@ U_BOOT_DRIVER(scmi_clock) = {
.id = UCLASS_CLK,
.ops = &scmi_clk_ops,
.probe = scmi_clk_probe,
+ .priv_auto = sizeof(struct scmi_clock_priv),
};
+
+static struct scmi_proto_match match[] = {
+ { .proto_id = SCMI_PROTOCOL_ID_CLOCK },
+ { /* Sentinel */ }
+};
+
+U_BOOT_SCMI_PROTO_DRIVER(scmi_clock, match);
diff --git a/drivers/clk/imx/clk-imxrt1170.c b/drivers/clk/imx/clk-imxrt1170.c
index 3f55d0d0127..bfd5dd6c464 100644
--- a/drivers/clk/imx/clk-imxrt1170.c
+++ b/drivers/clk/imx/clk-imxrt1170.c
@@ -105,6 +105,8 @@ static const char * const usdhc1_sels[] = {"rcosc48M_div2", "osc", "rcosc400M",
"pll2_pfd2", "pll2_pfd0", "pll1_div5", "pll_arm"};
static const char * const semc_sels[] = {"rcosc48M_div2", "osc", "rcosc400M", "rcosc16M",
"pll1_div5", "pll2_sys", "pll2_pfd2", "pll3_pfd0"};
+static const char * const flexspi1_sels[] = {"rcosc48M_div2", "osc", "rcosc400M", "rcosc16M",
+"pll3_pdf0", "pll2_clk", "pll2_pfd2", "pll3_clk"};
static int imxrt1170_clk_probe(struct udevice *dev)
{
@@ -163,6 +165,13 @@ static int imxrt1170_clk_probe(struct udevice *dev)
imx_clk_divider(dev, "lpuart1", "lpuart1_sel",
base + (25 * 0x80), 0, 8));
+ clk_dm(IMXRT1170_CLK_FLEXSPI1_SEL,
+ imx_clk_mux(dev, "flexspi1_sel", base + (20 * 0x80), 8, 3,
+ flexspi1_sels, ARRAY_SIZE(flexspi1_sels)));
+ clk_dm(IMXRT1170_CLK_FLEXSPI1,
+ imx_clk_divider(dev, "flexspi1", "flexspi1_sel",
+ base + (20 * 0x80), 0, 8));
+
clk_dm(IMXRT1170_CLK_USDHC1_SEL,
imx_clk_mux(dev, "usdhc1_sel", base + (58 * 0x80), 8, 3,
usdhc1_sels, ARRAY_SIZE(usdhc1_sels)));
diff --git a/drivers/clk/mediatek/clk-mt7981.c b/drivers/clk/mediatek/clk-mt7981.c
index 60814652322..6130c93d5e6 100644
--- a/drivers/clk/mediatek/clk-mt7981.c
+++ b/drivers/clk/mediatek/clk-mt7981.c
@@ -566,7 +566,7 @@ U_BOOT_DRIVER(mtk_clk_apmixedsys) = {
.of_match = mt7981_fixed_pll_compat,
.probe = mt7981_fixed_pll_probe,
.priv_auto = sizeof(struct mtk_clk_priv),
- .ops = &mtk_clk_topckgen_ops,
+ .ops = &mtk_clk_fixed_pll_ops,
.flags = DM_FLAG_PRE_RELOC,
};
diff --git a/drivers/clk/mediatek/clk-mt7986.c b/drivers/clk/mediatek/clk-mt7986.c
index f9d6f9c1749..cf298af644c 100644
--- a/drivers/clk/mediatek/clk-mt7986.c
+++ b/drivers/clk/mediatek/clk-mt7986.c
@@ -573,7 +573,7 @@ U_BOOT_DRIVER(mtk_clk_apmixedsys) = {
.of_match = mt7986_fixed_pll_compat,
.probe = mt7986_fixed_pll_probe,
.priv_auto = sizeof(struct mtk_clk_priv),
- .ops = &mtk_clk_topckgen_ops,
+ .ops = &mtk_clk_fixed_pll_ops,
.flags = DM_FLAG_PRE_RELOC,
};
diff --git a/drivers/clk/mediatek/clk-mt7987.c b/drivers/clk/mediatek/clk-mt7987.c
index 173686a38e8..b662d680b15 100644
--- a/drivers/clk/mediatek/clk-mt7987.c
+++ b/drivers/clk/mediatek/clk-mt7987.c
@@ -67,7 +67,7 @@ U_BOOT_DRIVER(mtk_clk_apmixedsys) = {
.of_match = mt7987_fixed_pll_compat,
.probe = mt7987_fixed_pll_probe,
.priv_auto = sizeof(struct mtk_clk_priv),
- .ops = &mtk_clk_topckgen_ops,
+ .ops = &mtk_clk_fixed_pll_ops,
.flags = DM_FLAG_PRE_RELOC,
};
diff --git a/drivers/clk/mediatek/clk-mt7988.c b/drivers/clk/mediatek/clk-mt7988.c
index 73fd9c6bea6..c6da42f970b 100644
--- a/drivers/clk/mediatek/clk-mt7988.c
+++ b/drivers/clk/mediatek/clk-mt7988.c
@@ -830,7 +830,7 @@ U_BOOT_DRIVER(mtk_clk_apmixedsys) = {
.of_match = mt7988_fixed_pll_compat,
.probe = mt7988_fixed_pll_probe,
.priv_auto = sizeof(struct mtk_clk_priv),
- .ops = &mtk_clk_topckgen_ops,
+ .ops = &mtk_clk_fixed_pll_ops,
.flags = DM_FLAG_PRE_RELOC,
};
diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c
index 66683aeb2d7..f91777e968a 100644
--- a/drivers/clk/mediatek/clk-mtk.c
+++ b/drivers/clk/mediatek/clk-mtk.c
@@ -47,6 +47,11 @@ static int mtk_clk_get_id(struct clk *clk)
return id;
}
+static int mtk_dummy_enable(struct clk *clk)
+{
+ return 0;
+}
+
static int mtk_gate_enable(void __iomem *base, const struct mtk_gate *gate)
{
u32 bit = BIT(gate->shift);
@@ -752,6 +757,12 @@ const struct clk_ops mtk_clk_apmixedsys_ops = {
.get_rate = mtk_apmixedsys_get_rate,
};
+const struct clk_ops mtk_clk_fixed_pll_ops = {
+ .enable = mtk_dummy_enable,
+ .disable = mtk_dummy_enable,
+ .get_rate = mtk_topckgen_get_rate,
+};
+
const struct clk_ops mtk_clk_topckgen_ops = {
.enable = mtk_clk_mux_enable,
.disable = mtk_clk_mux_disable,
diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h
index c1d9901c10b..4ef1341aea6 100644
--- a/drivers/clk/mediatek/clk-mtk.h
+++ b/drivers/clk/mediatek/clk-mtk.h
@@ -283,6 +283,7 @@ struct mtk_cg_priv {
};
extern const struct clk_ops mtk_clk_apmixedsys_ops;
+extern const struct clk_ops mtk_clk_fixed_pll_ops;
extern const struct clk_ops mtk_clk_topckgen_ops;
extern const struct clk_ops mtk_clk_infrasys_ops;
extern const struct clk_ops mtk_clk_gate_ops;
diff --git a/drivers/clk/thead/Kconfig b/drivers/clk/thead/Kconfig
new file mode 100644
index 00000000000..e815286b085
--- /dev/null
+++ b/drivers/clk/thead/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (c) 2025, Yao Zi <ziyao@disroot.org>
+
+config CLK_THEAD
+ bool "Clock support for T-Head SoCs"
+ depends on CLK
+
+if CLK_THEAD
+
+config CLK_THEAD_TH1520_AP
+ bool "T-Head TH1520 AP clock support"
+ select CLK_CCF
+ default THEAD_TH1520
+ help
+ This enables support clock driver for T-Head TH1520 Application
+ processor.
+
+endif
diff --git a/drivers/clk/thead/Makefile b/drivers/clk/thead/Makefile
new file mode 100644
index 00000000000..8cc05ed7914
--- /dev/null
+++ b/drivers/clk/thead/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2025 Yao Zi <ziyao@disroot.org>
+
+obj-$(CONFIG_CLK_THEAD_TH1520_AP) += clk-th1520-ap.o
diff --git a/drivers/clk/thead/clk-th1520-ap.c b/drivers/clk/thead/clk-th1520-ap.c
new file mode 100644
index 00000000000..b80ad05b8ad
--- /dev/null
+++ b/drivers/clk/thead/clk-th1520-ap.c
@@ -0,0 +1,1031 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Jisheng Zhang <jszhang@kernel.org>
+ * Copyright (C) 2023 Vivo Communication Technology Co. Ltd.
+ * Copyright (C) 2025 Yao Zi <ziyao@disroot.org>
+ * Authors: Yangtao Li <frank.li@vivo.com>
+ */
+
+#include <asm/io.h>
+#include <dm.h>
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+
+#include <dt-bindings/clock/thead,th1520-clk-ap.h>
+
+#define TH1520_PLL_POSTDIV2 GENMASK(26, 24)
+#define TH1520_PLL_POSTDIV1 GENMASK(22, 20)
+#define TH1520_PLL_FBDIV GENMASK(19, 8)
+#define TH1520_PLL_REFDIV GENMASK(5, 0)
+#define TH1520_PLL_BYPASS BIT(30)
+#define TH1520_PLL_DSMPD BIT(24)
+#define TH1520_PLL_FRAC GENMASK(23, 0)
+#define TH1520_PLL_FRAC_BITS 24
+
+static const char ccu_osc_name_to_be_filled[] = "TO BE FILLED";
+
+struct ccu_internal {
+ u8 shift;
+ u8 width;
+};
+
+struct ccu_div_internal {
+ u8 shift;
+ u8 width;
+};
+
+struct ccu_common {
+ void __iomem *reg;
+ const char *name;
+ struct clk clk;
+ int clkid;
+ u16 cfg0;
+ u16 cfg1;
+};
+
+struct ccu_mux {
+ struct ccu_common common;
+ struct ccu_internal mux;
+ const char **parents;
+ size_t num_parents;
+};
+
+struct ccu_gate {
+ struct ccu_common common;
+ const char *parent;
+ u32 enable;
+};
+
+struct ccu_div {
+ struct ccu_div_internal div;
+ struct ccu_common common;
+ struct ccu_internal mux;
+ const char **parents;
+ size_t num_parents;
+ u32 enable;
+};
+
+struct ccu_pll {
+ struct ccu_common common;
+};
+
+#define TH_CCU_ARG(_shift, _width) \
+ { \
+ .shift = _shift, \
+ .width = _width, \
+ }
+
+#define TH_CCU_DIV_FLAGS(_shift, _width, _flags) \
+ { \
+ .shift = _shift, \
+ .width = _width, \
+ }
+
+#define CCU_GATE(_clkid, _struct, _name, _parent, _reg, _gate, _flags) \
+ struct ccu_gate _struct = { \
+ .parent = _parent, \
+ .enable = _gate, \
+ .common = { \
+ .clkid = _clkid, \
+ .cfg0 = _reg, \
+ .name = _name, \
+ } \
+ }
+
+static inline struct ccu_common *clk_to_ccu_common(struct clk *clk)
+{
+ return container_of(clk, struct ccu_common, clk);
+}
+
+static inline struct ccu_mux *clk_to_ccu_mux(struct clk *clk)
+{
+ struct ccu_common *common = clk_to_ccu_common(clk);
+
+ return container_of(common, struct ccu_mux, common);
+}
+
+static inline struct ccu_pll *clk_to_ccu_pll(struct clk *clk)
+{
+ struct ccu_common *common = clk_to_ccu_common(clk);
+
+ return container_of(common, struct ccu_pll, common);
+}
+
+static inline struct ccu_div *clk_to_ccu_div(struct clk *clk)
+{
+ struct ccu_common *common = clk_to_ccu_common(clk);
+
+ return container_of(common, struct ccu_div, common);
+}
+
+static inline struct ccu_gate *clk_to_ccu_gate(struct clk *clk)
+{
+ struct ccu_common *common = clk_to_ccu_common(clk);
+
+ return container_of(common, struct ccu_gate, common);
+}
+
+static int ccu_set_parent_helper(struct ccu_common *common,
+ struct ccu_internal *mux,
+ u8 index)
+{
+ clrsetbits_le32(common->reg + common->cfg0,
+ GENMASK(mux->width - 1, 0) << mux->shift,
+ index << mux->shift);
+
+ return 0;
+}
+
+static void ccu_disable_helper(struct ccu_common *common, u32 gate)
+{
+ if (!gate)
+ return;
+
+ clrsetbits_le32(common->reg + common->cfg0,
+ gate, ~gate);
+}
+
+static int ccu_enable_helper(struct ccu_common *common, u32 gate)
+{
+ u32 val;
+
+ if (!gate)
+ return 0;
+
+ clrsetbits_le32(common->reg + common->cfg0, gate, gate);
+ val = readl(common->reg + common->cfg0);
+
+ return 0;
+}
+
+static int ccu_get_parent_index_helper(const char * const *parents,
+ int num_parents, struct clk *parent)
+{
+ const char *parent_name = parent->dev->name;
+ unsigned int index;
+
+ for (index = 0; index < num_parents; index++) {
+ if (!strcmp(parents[index], parent_name))
+ return index;
+ }
+
+ return -ENOENT;
+}
+
+static unsigned long ccu_div_get_rate(struct clk *clk)
+{
+ struct ccu_div *cd = clk_to_ccu_div(clk);
+ unsigned long rate;
+ unsigned int val;
+
+ val = readl(cd->common.reg + cd->common.cfg0);
+ val = val >> cd->div.shift;
+ val &= GENMASK(cd->div.width - 1, 0);
+ rate = divider_recalc_rate(clk, clk_get_parent_rate(clk), val, NULL,
+ 0, cd->div.width);
+
+ return rate;
+}
+
+static int ccu_div_get_parent(struct ccu_div *cd)
+{
+ u32 val = readl(cd->common.reg + cd->common.cfg0);
+
+ return (val >> cd->mux.shift) & GENMASK(cd->mux.width - 1, 0);
+}
+
+static int ccu_div_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct ccu_div *cd = clk_to_ccu_div(clk);
+ u8 id;
+
+ id = ccu_get_parent_index_helper(cd->parents, cd->num_parents, parent);
+ if (id < 0)
+ return id;
+
+ return ccu_set_parent_helper(&cd->common, &cd->mux, id);
+}
+
+static int ccu_div_disable(struct clk *clk)
+{
+ struct ccu_div *cd = clk_to_ccu_div(clk);
+
+ ccu_disable_helper(&cd->common, cd->enable);
+
+ return 0;
+}
+
+static int ccu_div_enable(struct clk *clk)
+{
+ struct ccu_div *cd = clk_to_ccu_div(clk);
+
+ return ccu_enable_helper(&cd->common, cd->enable);
+}
+
+static const struct clk_ops ccu_div_ops = {
+ .disable = ccu_div_disable,
+ .enable = ccu_div_enable,
+ .set_parent = ccu_div_set_parent,
+ .get_rate = ccu_div_get_rate,
+};
+
+U_BOOT_DRIVER(th1520_clk_div) = {
+ .name = "th1520_clk_div",
+ .id = UCLASS_CLK,
+ .ops = &ccu_div_ops,
+};
+
+static unsigned long th1520_pll_vco_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ struct ccu_pll *pll = clk_to_ccu_pll(clk);
+ unsigned long div, mul, frac;
+ unsigned int cfg0, cfg1;
+ u64 rate = parent_rate;
+
+ cfg0 = readl(pll->common.reg + pll->common.cfg0);
+ cfg1 = readl(pll->common.reg + pll->common.cfg1);
+
+ mul = FIELD_GET(TH1520_PLL_FBDIV, cfg0);
+ div = FIELD_GET(TH1520_PLL_REFDIV, cfg0);
+ if (!(cfg1 & TH1520_PLL_DSMPD)) {
+ mul <<= TH1520_PLL_FRAC_BITS;
+ frac = FIELD_GET(TH1520_PLL_FRAC, cfg1);
+ mul += frac;
+ div <<= TH1520_PLL_FRAC_BITS;
+ }
+
+ rate = parent_rate * mul;
+ rate = rate / div;
+
+ return rate;
+}
+
+static unsigned long th1520_pll_postdiv_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ struct ccu_pll *pll = clk_to_ccu_pll(clk);
+ unsigned long div, rate = parent_rate;
+ unsigned int cfg0, cfg1;
+
+ cfg0 = readl(pll->common.reg + pll->common.cfg0);
+ cfg1 = readl(pll->common.reg + pll->common.cfg1);
+
+ if (cfg1 & TH1520_PLL_BYPASS)
+ return rate;
+
+ div = FIELD_GET(TH1520_PLL_POSTDIV1, cfg0) *
+ FIELD_GET(TH1520_PLL_POSTDIV2, cfg0);
+
+ rate = rate / div;
+
+ return rate;
+}
+
+static unsigned long ccu_pll_get_rate(struct clk *clk)
+{
+ unsigned long rate = clk_get_parent_rate(clk);
+
+ rate = th1520_pll_vco_recalc_rate(clk, rate);
+ rate = th1520_pll_postdiv_recalc_rate(clk, rate);
+
+ return rate;
+}
+
+static const struct clk_ops clk_pll_ops = {
+ .get_rate = ccu_pll_get_rate,
+};
+
+U_BOOT_DRIVER(th1520_clk_pll) = {
+ .name = "th1520_clk_pll",
+ .id = UCLASS_CLK,
+ .ops = &clk_pll_ops,
+};
+
+static struct ccu_pll cpu_pll0_clk = {
+ .common = {
+ .clkid = CLK_CPU_PLL0,
+ .cfg0 = 0x000,
+ .cfg1 = 0x004,
+ .name = "cpu-pll0",
+ },
+};
+
+static struct ccu_pll cpu_pll1_clk = {
+ .common = {
+ .clkid = CLK_CPU_PLL1,
+ .cfg0 = 0x010,
+ .cfg1 = 0x014,
+ .name = "cpu-pll1",
+ },
+};
+
+static struct ccu_pll gmac_pll_clk = {
+ .common = {
+ .clkid = CLK_GMAC_PLL,
+ .cfg0 = 0x020,
+ .cfg1 = 0x024,
+ .name = "gmac-pll",
+ },
+};
+
+static const char *gmac_pll_clk_parent[] = {
+ "gmac-pll",
+};
+
+static struct ccu_pll video_pll_clk = {
+ .common = {
+ .clkid = CLK_VIDEO_PLL,
+ .cfg0 = 0x030,
+ .cfg1 = 0x034,
+ .name = "video-pll",
+ },
+};
+
+static const char *video_pll_clk_parent[] = {
+ "video-pll",
+};
+
+static struct ccu_pll dpu0_pll_clk = {
+ .common = {
+ .clkid = CLK_DPU0_PLL,
+ .cfg0 = 0x040,
+ .cfg1 = 0x044,
+ .name = "dpu0-pll",
+ },
+};
+
+static const char *dpu0_pll_clk_parent[] = {
+ "dpu0-pll",
+};
+
+static struct ccu_pll dpu1_pll_clk = {
+ .common = {
+ .clkid = CLK_DPU1_PLL,
+ .cfg0 = 0x050,
+ .cfg1 = 0x054,
+ .name = "dpu1-pll",
+ },
+};
+
+static const char *dpu1_pll_clk_parent[] = {
+ "dpu1-pll",
+};
+
+static struct ccu_pll tee_pll_clk = {
+ .common = {
+ .clkid = CLK_TEE_PLL,
+ .cfg0 = 0x060,
+ .cfg1 = 0x064,
+ .name = "tee-pll",
+ },
+};
+
+static const char *c910_i0_parents[] = {
+ "cpu-pll0", ccu_osc_name_to_be_filled,
+};
+
+static struct ccu_mux c910_i0_clk = {
+ .parents = c910_i0_parents,
+ .num_parents = ARRAY_SIZE(c910_i0_parents),
+ .mux = TH_CCU_ARG(1, 1),
+ .common = {
+ .clkid = CLK_C910_I0,
+ .cfg0 = 0x100,
+ .name = "c910-i0",
+ }
+};
+
+static const char *c910_parents[] = {
+ "c910-i0", "cpu-pll1",
+};
+
+static struct ccu_mux c910_clk = {
+ .parents = c910_parents,
+ .num_parents = ARRAY_SIZE(c910_parents),
+ .mux = TH_CCU_ARG(0, 1),
+ .common = {
+ .clkid = CLK_C910,
+ .cfg0 = 0x100,
+ .name = "c910",
+ }
+};
+
+static const char *ahb2_cpusys_parents[] = {
+ "gmac-pll", ccu_osc_name_to_be_filled,
+};
+
+static struct ccu_div ahb2_cpusys_hclk = {
+ .parents = ahb2_cpusys_parents,
+ .num_parents = ARRAY_SIZE(ahb2_cpusys_parents),
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(5, 1),
+ .common = {
+ .clkid = CLK_AHB2_CPUSYS_HCLK,
+ .cfg0 = 0x120,
+ .name = "ahb2-cpusys-hclk",
+ },
+};
+
+static const char *ahb2_cpusys_hclk_parents[] = {
+ "ahb2-cpusys-hclk",
+};
+
+static struct ccu_div apb3_cpusys_pclk = {
+ .parents = ahb2_cpusys_hclk_parents,
+ .num_parents = ARRAY_SIZE(ahb2_cpusys_hclk_parents),
+ .div = TH_CCU_ARG(0, 3),
+ .common = {
+ .clkid = CLK_APB3_CPUSYS_PCLK,
+ .cfg0 = 0x130,
+ .name = "apb3-cpusys-pclk",
+ },
+};
+
+static struct ccu_div axi4_cpusys2_aclk = {
+ .parents = gmac_pll_clk_parent,
+ .num_parents = ARRAY_SIZE(gmac_pll_clk_parent),
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_AXI4_CPUSYS2_ACLK,
+ .cfg0 = 0x134,
+ .name = "axi4-cpusys2-aclk",
+ },
+};
+
+static const char *axi_parents[] = {
+ "video-pll", ccu_osc_name_to_be_filled,
+};
+
+static struct ccu_div axi_aclk = {
+ .parents = axi_parents,
+ .num_parents = ARRAY_SIZE(axi_parents),
+ .div = TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(5, 1),
+ .common = {
+ .clkid = CLK_AXI_ACLK,
+ .cfg0 = 0x138,
+ .name = "axi-aclk",
+ },
+};
+
+static const char *perisys_ahb_hclk_parents[] = {
+ "gmac-pll", ccu_osc_name_to_be_filled,
+};
+
+static struct ccu_div perisys_ahb_hclk = {
+ .parents = perisys_ahb_hclk_parents,
+ .num_parents = ARRAY_SIZE(perisys_ahb_hclk_parents),
+ .enable = BIT(6),
+ .div = TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(5, 1),
+ .common = {
+ .clkid = CLK_PERI_AHB_HCLK,
+ .cfg0 = 0x140,
+ .name = "perisys-ahb-hclk",
+ },
+};
+
+static const char *perisys_ahb_hclk_parent[] = {
+ "perisys-ahb-hclk",
+};
+
+static struct ccu_div perisys_apb_pclk = {
+ .parents = perisys_ahb_hclk_parent,
+ .num_parents = ARRAY_SIZE(perisys_ahb_hclk_parent),
+ .div = TH_CCU_ARG(0, 3),
+ .common = {
+ .clkid = CLK_PERI_APB_PCLK,
+ .cfg0 = 0x150,
+ .name = "perisys-apb-pclk",
+ },
+};
+
+static struct ccu_div peri2sys_apb_pclk = {
+ .parents = gmac_pll_clk_parent,
+ .num_parents = ARRAY_SIZE(gmac_pll_clk_parent),
+ .div = TH_CCU_DIV_FLAGS(4, 3, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_PERI2APB_PCLK,
+ .cfg0 = 0x150,
+ .name = "peri2sys-apb-pclk",
+ },
+};
+
+static const char *apb_parents[] = {
+ "gmac-pll", ccu_osc_name_to_be_filled,
+};
+
+static struct ccu_div apb_pclk = {
+ .parents = apb_parents,
+ .num_parents = ARRAY_SIZE(apb_parents),
+ .enable = BIT(5),
+ .div = TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(7, 1),
+ .common = {
+ .clkid = CLK_APB_PCLK,
+ .cfg0 = 0x1c4,
+ .name = "apb-pclk",
+ },
+};
+
+static const char *npu_parents[] = {
+ "gmac-pll", "video-pll",
+};
+
+static struct ccu_div npu_clk = {
+ .parents = npu_parents,
+ .num_parents = ARRAY_SIZE(npu_parents),
+ .enable = BIT(4),
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(6, 1),
+ .common = {
+ .clkid = CLK_NPU,
+ .cfg0 = 0x1c8,
+ .name = "npu",
+ },
+};
+
+static struct ccu_div vi_clk = {
+ .parents = video_pll_clk_parent,
+ .num_parents = ARRAY_SIZE(video_pll_clk_parent),
+ .div = TH_CCU_DIV_FLAGS(16, 4, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_VI,
+ .cfg0 = 0x1d0,
+ .name = "vi",
+ },
+};
+
+static struct ccu_div vi_ahb_clk = {
+ .parents = video_pll_clk_parent,
+ .num_parents = ARRAY_SIZE(video_pll_clk_parent),
+ .div = TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_VI_AHB,
+ .cfg0 = 0x1d0,
+ .name = "vi-ahb",
+ },
+};
+
+static struct ccu_div vo_axi_clk = {
+ .parents = video_pll_clk_parent,
+ .num_parents = ARRAY_SIZE(video_pll_clk_parent),
+ .enable = BIT(5),
+ .div = TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_VO_AXI,
+ .cfg0 = 0x1dc,
+ .name = "vo-axi",
+ },
+};
+
+static struct ccu_div vp_apb_clk = {
+ .parents = gmac_pll_clk_parent,
+ .num_parents = ARRAY_SIZE(gmac_pll_clk_parent),
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_VP_APB,
+ .cfg0 = 0x1e0,
+ .name = "vp-apb",
+ },
+};
+
+static struct ccu_div vp_axi_clk = {
+ .parents = video_pll_clk_parent,
+ .num_parents = ARRAY_SIZE(video_pll_clk_parent),
+ .enable = BIT(15),
+ .div = TH_CCU_DIV_FLAGS(8, 4, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_VP_AXI,
+ .cfg0 = 0x1e0,
+ .name = "vp-axi",
+ },
+};
+
+static struct ccu_div venc_clk = {
+ .parents = gmac_pll_clk_parent,
+ .num_parents = ARRAY_SIZE(gmac_pll_clk_parent),
+ .enable = BIT(5),
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_VENC,
+ .cfg0 = 0x1e4,
+ .name = "venc",
+ },
+};
+
+static struct ccu_div dpu0_clk = {
+ .parents = dpu0_pll_clk_parent,
+ .num_parents = ARRAY_SIZE(dpu0_pll_clk_parent),
+ .div = TH_CCU_DIV_FLAGS(0, 8, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_DPU0,
+ .cfg0 = 0x1e8,
+ .name = "dpu0",
+ },
+};
+
+static struct ccu_div dpu1_clk = {
+ .parents = dpu1_pll_clk_parent,
+ .num_parents = ARRAY_SIZE(dpu1_pll_clk_parent),
+ .div = TH_CCU_DIV_FLAGS(0, 8, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .clkid = CLK_DPU1,
+ .cfg0 = 0x1ec,
+ .name = "dpu1",
+ },
+};
+
+static CCU_GATE(CLK_BROM, brom_clk, "brom", "ahb2-cpusys-hclk", 0x100, BIT(4), 0);
+static CCU_GATE(CLK_BMU, bmu_clk, "bmu", "axi4-cpusys2-aclk", 0x100, BIT(5), 0);
+static CCU_GATE(CLK_AON2CPU_A2X, aon2cpu_a2x_clk, "aon2cpu-a2x", "axi4-cpusys2-aclk",
+ 0x134, BIT(8), 0);
+static CCU_GATE(CLK_X2X_CPUSYS, x2x_cpusys_clk, "x2x-cpusys", "axi4-cpusys2-aclk",
+ 0x134, BIT(7), 0);
+static CCU_GATE(CLK_CPU2AON_X2H, cpu2aon_x2h_clk, "cpu2aon-x2h", "axi-aclk", 0x138, BIT(8), 0);
+static CCU_GATE(CLK_CPU2PERI_X2H, cpu2peri_x2h_clk, "cpu2peri-x2h", "axi4-cpusys2-aclk",
+ 0x140, BIT(9), CLK_IGNORE_UNUSED);
+static CCU_GATE(CLK_PERISYS_APB1_HCLK, perisys_apb1_hclk, "perisys-apb1-hclk", "perisys-ahb-hclk",
+ 0x150, BIT(9), 0);
+static CCU_GATE(CLK_PERISYS_APB2_HCLK, perisys_apb2_hclk, "perisys-apb2-hclk", "perisys-ahb-hclk",
+ 0x150, BIT(10), CLK_IGNORE_UNUSED);
+static CCU_GATE(CLK_PERISYS_APB3_HCLK, perisys_apb3_hclk, "perisys-apb3-hclk", "perisys-ahb-hclk",
+ 0x150, BIT(11), CLK_IGNORE_UNUSED);
+static CCU_GATE(CLK_PERISYS_APB4_HCLK, perisys_apb4_hclk, "perisys-apb4-hclk", "perisys-ahb-hclk",
+ 0x150, BIT(12), 0);
+static CCU_GATE(CLK_NPU_AXI, npu_axi_clk, "npu-axi", "axi-aclk", 0x1c8, BIT(5), 0);
+static CCU_GATE(CLK_CPU2VP, cpu2vp_clk, "cpu2vp", "axi-aclk", 0x1e0, BIT(13), 0);
+static CCU_GATE(CLK_EMMC_SDIO, emmc_sdio_clk, "emmc-sdio", "emmc-sdio-ref", 0x204, BIT(30), 0);
+static CCU_GATE(CLK_GMAC1, gmac1_clk, "gmac1", "gmac-pll", 0x204, BIT(26), 0);
+static CCU_GATE(CLK_PADCTRL1, padctrl1_clk, "padctrl1", "perisys-apb-pclk", 0x204, BIT(24), 0);
+static CCU_GATE(CLK_DSMART, dsmart_clk, "dsmart", "perisys-apb-pclk", 0x204, BIT(23), 0);
+static CCU_GATE(CLK_PADCTRL0, padctrl0_clk, "padctrl0", "perisys-apb-pclk", 0x204, BIT(22), 0);
+static CCU_GATE(CLK_GMAC_AXI, gmac_axi_clk, "gmac-axi", "axi4-cpusys2-aclk", 0x204, BIT(21), 0);
+static CCU_GATE(CLK_GPIO3, gpio3_clk, "gpio3-clk", "peri2sys-apb-pclk", 0x204, BIT(20), 0);
+static CCU_GATE(CLK_GMAC0, gmac0_clk, "gmac0", "gmac-pll", 0x204, BIT(19), 0);
+static CCU_GATE(CLK_PWM, pwm_clk, "pwm", "perisys-apb-pclk", 0x204, BIT(18), 0);
+static CCU_GATE(CLK_QSPI0, qspi0_clk, "qspi0", "video-pll", 0x204, BIT(17), 0);
+static CCU_GATE(CLK_QSPI1, qspi1_clk, "qspi1", "video-pll", 0x204, BIT(16), 0);
+static CCU_GATE(CLK_SPI, spi_clk, "spi", "video-pll", 0x204, BIT(15), 0);
+static CCU_GATE(CLK_UART0_PCLK, uart0_pclk, "uart0-pclk", "perisys-apb-pclk", 0x204, BIT(14), 0);
+static CCU_GATE(CLK_UART1_PCLK, uart1_pclk, "uart1-pclk", "perisys-apb-pclk", 0x204, BIT(13), 0);
+static CCU_GATE(CLK_UART2_PCLK, uart2_pclk, "uart2-pclk", "perisys-apb-pclk", 0x204, BIT(12), 0);
+static CCU_GATE(CLK_UART3_PCLK, uart3_pclk, "uart3-pclk", "perisys-apb-pclk", 0x204, BIT(11), 0);
+static CCU_GATE(CLK_UART4_PCLK, uart4_pclk, "uart4-pclk", "perisys-apb-pclk", 0x204, BIT(10), 0);
+static CCU_GATE(CLK_UART5_PCLK, uart5_pclk, "uart5-pclk", "perisys-apb-pclk", 0x204, BIT(9), 0);
+static CCU_GATE(CLK_GPIO0, gpio0_clk, "gpio0-clk", "perisys-apb-pclk", 0x204, BIT(8), 0);
+static CCU_GATE(CLK_GPIO1, gpio1_clk, "gpio1-clk", "perisys-apb-pclk", 0x204, BIT(7), 0);
+static CCU_GATE(CLK_GPIO2, gpio2_clk, "gpio2-clk", "peri2sys-apb-pclk", 0x204, BIT(6), 0);
+static CCU_GATE(CLK_I2C0, i2c0_clk, "i2c0", "perisys-apb-pclk", 0x204, BIT(5), 0);
+static CCU_GATE(CLK_I2C1, i2c1_clk, "i2c1", "perisys-apb-pclk", 0x204, BIT(4), 0);
+static CCU_GATE(CLK_I2C2, i2c2_clk, "i2c2", "perisys-apb-pclk", 0x204, BIT(3), 0);
+static CCU_GATE(CLK_I2C3, i2c3_clk, "i2c3", "perisys-apb-pclk", 0x204, BIT(2), 0);
+static CCU_GATE(CLK_I2C4, i2c4_clk, "i2c4", "perisys-apb-pclk", 0x204, BIT(1), 0);
+static CCU_GATE(CLK_I2C5, i2c5_clk, "i2c5", "perisys-apb-pclk", 0x204, BIT(0), 0);
+static CCU_GATE(CLK_SPINLOCK, spinlock_clk, "spinlock", "ahb2-cpusys-hclk", 0x208, BIT(10), 0);
+static CCU_GATE(CLK_DMA, dma_clk, "dma", "axi4-cpusys2-aclk", 0x208, BIT(8), 0);
+static CCU_GATE(CLK_MBOX0, mbox0_clk, "mbox0", "apb3-cpusys-pclk", 0x208, BIT(7), 0);
+static CCU_GATE(CLK_MBOX1, mbox1_clk, "mbox1", "apb3-cpusys-pclk", 0x208, BIT(6), 0);
+static CCU_GATE(CLK_MBOX2, mbox2_clk, "mbox2", "apb3-cpusys-pclk", 0x208, BIT(5), 0);
+static CCU_GATE(CLK_MBOX3, mbox3_clk, "mbox3", "apb3-cpusys-pclk", 0x208, BIT(4), 0);
+static CCU_GATE(CLK_WDT0, wdt0_clk, "wdt0", "apb3-cpusys-pclk", 0x208, BIT(3), 0);
+static CCU_GATE(CLK_WDT1, wdt1_clk, "wdt1", "apb3-cpusys-pclk", 0x208, BIT(2), 0);
+static CCU_GATE(CLK_TIMER0, timer0_clk, "timer0", "apb3-cpusys-pclk", 0x208, BIT(1), 0);
+static CCU_GATE(CLK_TIMER1, timer1_clk, "timer1", "apb3-cpusys-pclk", 0x208, BIT(0), 0);
+static CCU_GATE(CLK_SRAM0, sram0_clk, "sram0", "axi-aclk", 0x20c, BIT(4), 0);
+static CCU_GATE(CLK_SRAM1, sram1_clk, "sram1", "axi-aclk", 0x20c, BIT(3), 0);
+static CCU_GATE(CLK_SRAM2, sram2_clk, "sram2", "axi-aclk", 0x20c, BIT(2), 0);
+static CCU_GATE(CLK_SRAM3, sram3_clk, "sram3", "axi-aclk", 0x20c, BIT(1), 0);
+
+static const char *uart_sclk_parents[] = {
+ "gmac-pll-clk-100m", ccu_osc_name_to_be_filled,
+};
+
+static struct ccu_mux uart_sclk = {
+ .parents = uart_sclk_parents,
+ .num_parents = ARRAY_SIZE(uart_sclk_parents),
+ .mux = TH_CCU_ARG(0, 1),
+ .common = {
+ .clkid = CLK_UART_SCLK,
+ .cfg0 = 0x210,
+ .name = "uart-sclk",
+ }
+};
+
+static struct ccu_common *th1520_pll_clks[] = {
+ &cpu_pll0_clk.common,
+ &cpu_pll1_clk.common,
+ &gmac_pll_clk.common,
+ &video_pll_clk.common,
+ &dpu0_pll_clk.common,
+ &dpu1_pll_clk.common,
+ &tee_pll_clk.common,
+};
+
+static struct ccu_common *th1520_div_clks[] = {
+ &ahb2_cpusys_hclk.common,
+ &apb3_cpusys_pclk.common,
+ &axi4_cpusys2_aclk.common,
+ &perisys_ahb_hclk.common,
+ &perisys_apb_pclk.common,
+ &axi_aclk.common,
+ &peri2sys_apb_pclk.common,
+ &apb_pclk.common,
+ &npu_clk.common,
+ &vi_clk.common,
+ &vi_ahb_clk.common,
+ &vo_axi_clk.common,
+ &vp_apb_clk.common,
+ &vp_axi_clk.common,
+ &venc_clk.common,
+ &dpu0_clk.common,
+ &dpu1_clk.common,
+};
+
+static struct ccu_common *th1520_mux_clks[] = {
+ &c910_i0_clk.common,
+ &c910_clk.common,
+ &uart_sclk.common,
+};
+
+static struct ccu_common *th1520_gate_clks[] = {
+ &emmc_sdio_clk.common,
+ &aon2cpu_a2x_clk.common,
+ &x2x_cpusys_clk.common,
+ &brom_clk.common,
+ &bmu_clk.common,
+ &cpu2aon_x2h_clk.common,
+ &cpu2peri_x2h_clk.common,
+ &cpu2vp_clk.common,
+ &perisys_apb1_hclk.common,
+ &perisys_apb2_hclk.common,
+ &perisys_apb3_hclk.common,
+ &perisys_apb4_hclk.common,
+ &npu_axi_clk.common,
+ &gmac1_clk.common,
+ &padctrl1_clk.common,
+ &dsmart_clk.common,
+ &padctrl0_clk.common,
+ &gmac_axi_clk.common,
+ &gpio3_clk.common,
+ &gmac0_clk.common,
+ &pwm_clk.common,
+ &qspi0_clk.common,
+ &qspi1_clk.common,
+ &spi_clk.common,
+ &uart0_pclk.common,
+ &uart1_pclk.common,
+ &uart2_pclk.common,
+ &uart3_pclk.common,
+ &uart4_pclk.common,
+ &uart5_pclk.common,
+ &gpio0_clk.common,
+ &gpio1_clk.common,
+ &gpio2_clk.common,
+ &i2c0_clk.common,
+ &i2c1_clk.common,
+ &i2c2_clk.common,
+ &i2c3_clk.common,
+ &i2c4_clk.common,
+ &i2c5_clk.common,
+ &spinlock_clk.common,
+ &dma_clk.common,
+ &mbox0_clk.common,
+ &mbox1_clk.common,
+ &mbox2_clk.common,
+ &mbox3_clk.common,
+ &wdt0_clk.common,
+ &wdt1_clk.common,
+ &timer0_clk.common,
+ &timer1_clk.common,
+ &sram0_clk.common,
+ &sram1_clk.common,
+ &sram2_clk.common,
+ &sram3_clk.common,
+};
+
+static void th1520_clk_fill_osc_name(const char **names, size_t name_num,
+ const char *osc_name)
+{
+ size_t i;
+
+ for (i = 0; i < name_num; i++) {
+ if (names[i] == ccu_osc_name_to_be_filled)
+ names[i] = osc_name;
+ }
+}
+
+static int th1520_clk_probe(struct udevice *dev)
+{
+ struct clk *clk, osc_clk;
+ const char *osc_name;
+ void __iomem *base;
+ fdt_addr_t addr;
+ int ret, i;
+
+ addr = dev_read_addr(dev);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ base = (void __iomem *)addr;
+
+ ret = clk_get_by_index(dev, 0, &osc_clk);
+ if (ret) {
+ pr_err("failed to get osc clock: %d\n", ret);
+ return ret;
+ }
+
+ osc_name = clk_hw_get_name(&osc_clk);
+
+ for (i = 0; i < ARRAY_SIZE(th1520_pll_clks); i++) {
+ struct ccu_common *common = th1520_pll_clks[i];
+
+ common->reg = base;
+
+ ret = clk_register(&common->clk, "th1520_clk_pll",
+ common->name, osc_name);
+ if (ret) {
+ pr_err("failed to register PLL %s: %d\n",
+ common->name, ret);
+ return ret;
+ }
+
+ common->clk.id = common->clkid;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(th1520_div_clks); i++) {
+ struct ccu_div *cd = container_of(th1520_div_clks[i],
+ struct ccu_div, common);
+ const char *current_parent;
+
+ cd->common.reg = base;
+ th1520_clk_fill_osc_name(cd->parents, cd->num_parents,
+ osc_name);
+
+ if (cd->num_parents > 1)
+ current_parent = cd->parents[ccu_div_get_parent(cd)];
+ else
+ current_parent = cd->parents[0];
+
+ ret = clk_register(&cd->common.clk, "th1520_clk_div",
+ cd->common.name,
+ current_parent);
+
+ if (ret) {
+ pr_err("failed to register div clock %s: %d\n",
+ cd->common.name, ret);
+ return ret;
+ }
+
+ cd->common.clk.id = cd->common.clkid;
+ }
+
+ clk = clk_register_fixed_factor(dev, "gmac-pll-clk-100m", "gmac-pll",
+ 0, 1, 10);
+ if (IS_ERR(clk)) {
+ pr_err("failed to register gmac-pll-clk-100m: %d\n",
+ (int)PTR_ERR(clk));
+ return PTR_ERR(clk);
+ }
+ clk->id = CLK_PLL_GMAC_100M;
+
+ clk = clk_register_fixed_factor(dev, "emmc-sdio-ref", "video-pll",
+ 0, 1, 4);
+ if (IS_ERR(clk)) {
+ pr_err("failed to register emmc-sdio-ref: %d\n",
+ (int)PTR_ERR(clk));
+ return PTR_ERR(clk);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(th1520_mux_clks); i++) {
+ struct ccu_mux *cm = container_of(th1520_mux_clks[i],
+ struct ccu_mux, common);
+
+ th1520_clk_fill_osc_name(cm->parents, cm->num_parents,
+ osc_name);
+
+ clk = clk_register_mux(dev, cm->common.name,
+ cm->parents, cm->num_parents,
+ 0,
+ base + cm->common.cfg0,
+ cm->mux.shift, cm->mux.width,
+ 0);
+ if (IS_ERR(clk)) {
+ pr_err("failed to register mux clock %s: %d\n",
+ cm->common.name, (int)PTR_ERR(clk));
+ return PTR_ERR(clk);
+ }
+
+ clk->id = cm->common.clkid;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(th1520_gate_clks); i++) {
+ struct ccu_gate *cg = container_of(th1520_gate_clks[i],
+ struct ccu_gate, common);
+
+ th1520_clk_fill_osc_name(&cg->parent, 1, osc_name);
+
+ clk = clk_register_gate(dev, cg->common.name,
+ cg->parent,
+ 0,
+ base + cg->common.cfg0,
+ ffs(cg->enable) - 1, 0, NULL);
+ if (IS_ERR(clk)) {
+ pr_err("failed to register gate clock %s: %d\n",
+ cg->common.name, (int)PTR_ERR(clk));
+ return PTR_ERR(clk);
+ }
+
+ clk->id = cg->common.clkid;
+ }
+
+ return 0;
+}
+
+static const struct udevice_id th1520_clk_match[] = {
+ {
+ .compatible = "thead,th1520-clk-ap",
+ },
+ { /* sentinel */ },
+};
+
+static int th1520_clk_enable(struct clk *clk)
+{
+ struct clk *c;
+ int ret;
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ return clk_enable(c);
+}
+
+static int th1520_clk_disable(struct clk *clk)
+{
+ struct clk *c;
+ int ret;
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ return clk_disable(c);
+}
+
+static ulong th1520_clk_get_rate(struct clk *clk)
+{
+ struct clk *c;
+ int ret;
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ return clk_get_rate(c);
+}
+
+static ulong th1520_clk_set_rate(struct clk *clk, ulong rate)
+{
+ struct clk *c;
+ int ret;
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ return clk_set_rate(c, rate);
+}
+
+static int th1520_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct clk *c, *p;
+ int ret;
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ ret = clk_get_by_id(parent->id, &p);
+ if (ret)
+ return ret;
+
+ return clk_set_parent(c, p);
+}
+
+static const struct clk_ops th1520_clk_ops = {
+ .enable = th1520_clk_enable,
+ .disable = th1520_clk_disable,
+ .get_rate = th1520_clk_get_rate,
+ .set_rate = th1520_clk_set_rate,
+ .set_parent = th1520_clk_set_parent,
+};
+
+U_BOOT_DRIVER(th1520_clk) = {
+ .name = "th1520-clk",
+ .id = UCLASS_CLK,
+ .of_match = th1520_clk_match,
+ .probe = th1520_clk_probe,
+ .ops = &th1520_clk_ops,
+};
diff --git a/drivers/cpu/imx8_cpu.c b/drivers/cpu/imx8_cpu.c
index 4e1eccaa5b0..4836bddd93b 100644
--- a/drivers/cpu/imx8_cpu.c
+++ b/drivers/cpu/imx8_cpu.c
@@ -111,6 +111,8 @@ static const char *get_imx_type_str(u32 imxtype)
return "91(11)";/* iMX91 9x9 Reduced feature */
case MXC_CPU_IMX9101:
return "91(01)";/* iMX91 9x9 Specific feature */
+ case MXC_CPU_IMX95:
+ return "95";
default:
return "??";
}
diff --git a/drivers/crypto/fsl/jr.c b/drivers/crypto/fsl/jr.c
index 8f7a821ebf3..1d95d2bff27 100644
--- a/drivers/crypto/fsl/jr.c
+++ b/drivers/crypto/fsl/jr.c
@@ -217,13 +217,6 @@ static int jr_enqueue(uint32_t *desc_addr,
jr->head = (head + 1) & (jr->size - 1);
- /* Invalidate output ring */
- start = (unsigned long)jr->output_ring &
- ~(ARCH_DMA_MINALIGN - 1);
- end = ALIGN((unsigned long)jr->output_ring + jr->op_size,
- ARCH_DMA_MINALIGN);
- invalidate_dcache_range(start, end);
-
sec_out32(&regs->irja, 1);
return 0;
@@ -243,6 +236,7 @@ static int jr_dequeue(int sec_idx, struct caam_regs *caam)
#else
uint32_t *addr;
#endif
+ unsigned long start, end;
while (sec_in32(&regs->orsf) && CIRC_CNT(jr->head, jr->tail,
jr->size)) {
@@ -250,6 +244,11 @@ static int jr_dequeue(int sec_idx, struct caam_regs *caam)
found = 0;
caam_dma_addr_t op_desc;
+
+ /* Invalidate output ring */
+ start = (unsigned long)jr->output_ring & ~(ARCH_DMA_MINALIGN - 1);
+ end = ALIGN((unsigned long)jr->output_ring + jr->op_size, ARCH_DMA_MINALIGN);
+ invalidate_dcache_range(start, end);
#ifdef CONFIG_CAAM_64BIT
/* Read the 64 bit Descriptor address from Output Ring.
* The 32 bit hign and low part of the address will
@@ -283,8 +282,13 @@ static int jr_dequeue(int sec_idx, struct caam_regs *caam)
}
/* Error condition if match not found */
- if (!found)
+ if (!found) {
+ int slots_full = sec_in32(&regs->orsf);
+
+ jr->tail = (jr->tail + slots_full) & (jr->size - 1);
+ sec_out32(&regs->orjr, slots_full);
return -1;
+ }
jr->info[idx].op_done = 1;
callback = (void *)jr->info[idx].callback;
@@ -296,14 +300,14 @@ static int jr_dequeue(int sec_idx, struct caam_regs *caam)
*/
if (idx == tail)
do {
+ jr->info[tail].op_done = 0;
tail = (tail + 1) & (jr->size - 1);
} while (jr->info[tail].op_done);
jr->tail = tail;
- jr->read_idx = (jr->read_idx + 1) & (jr->size - 1);
+
sec_out32(&regs->orjr, 1);
- jr->info[idx].op_done = 0;
callback(status, arg);
}
@@ -378,7 +382,6 @@ static int jr_sw_cleanup(uint8_t sec_idx, struct caam_regs *caam)
jr->head = 0;
jr->tail = 0;
- jr->read_idx = 0;
jr->write_idx = 0;
memset(jr->info, 0, sizeof(jr->info));
memset(jr->input_ring, 0, jr->size * sizeof(caam_dma_addr_t));
diff --git a/drivers/crypto/fsl/jr.h b/drivers/crypto/fsl/jr.h
index b136cd8d05a..8d5ca03e501 100644
--- a/drivers/crypto/fsl/jr.h
+++ b/drivers/crypto/fsl/jr.h
@@ -83,10 +83,6 @@ struct jobring {
* in-order job completion
*/
int tail;
- /* Read index of the output ring. It may not match with tail in case
- * of out of order completetion
- */
- int read_idx;
/* Write index to input ring. Would be always equal to head */
int write_idx;
/* Size of the rings. */
diff --git a/drivers/firmware/scmi/base.c b/drivers/firmware/scmi/base.c
index f4e3974ff5b..78ee2ffd2da 100644
--- a/drivers/firmware/scmi/base.c
+++ b/drivers/firmware/scmi/base.c
@@ -258,7 +258,7 @@ static int scmi_base_discover_impl_version_int(struct udevice *dev,
static int scmi_base_discover_list_protocols_int(struct udevice *dev,
u8 **protocols)
{
- struct scmi_base_discover_list_protocols_out out;
+ struct scmi_base_discover_list_protocols_out *out;
int cur;
struct scmi_msg msg = {
.protocol_id = SCMI_PROTOCOL_ID_BASE,
@@ -268,7 +268,7 @@ static int scmi_base_discover_list_protocols_int(struct udevice *dev,
.out_msg = (u8 *)&out,
.out_msg_sz = sizeof(out),
};
- u32 num_agents, num_protocols;
+ u32 num_agents, num_protocols, out_size;
u8 *buf;
int i, ret;
@@ -276,22 +276,31 @@ static int scmi_base_discover_list_protocols_int(struct udevice *dev,
if (ret)
return ret;
+ out_size = sizeof(*out) + sizeof(u32) * (1 + num_protocols / 4);
+ out = calloc(1, out_size);
+ if (!out)
+ return -ENOMEM;
+ msg.out_msg = (u8 *)out;
+ msg.out_msg_sz = out_size;
+
buf = calloc(sizeof(u8), num_protocols);
- if (!buf)
+ if (!buf) {
+ free(out);
return -ENOMEM;
+ }
cur = 0;
do {
ret = devm_scmi_process_msg(dev, &msg);
if (ret)
goto err;
- if (out.status) {
- ret = scmi_to_linux_errno(out.status);
+ if (out->status) {
+ ret = scmi_to_linux_errno(out->status);
goto err;
}
- for (i = 0; i < out.num_protocols; i++, cur++)
- buf[cur] = out.protocols[i / 4] >> ((i % 4) * 8);
+ for (i = 0; i < out->num_protocols; i++, cur++)
+ buf[cur] = out->protocols[i / 4] >> ((i % 4) * 8);
} while (cur < num_protocols);
*protocols = buf;
@@ -299,6 +308,7 @@ static int scmi_base_discover_list_protocols_int(struct udevice *dev,
return num_protocols;
err:
free(buf);
+ free(out);
return ret;
}
diff --git a/drivers/firmware/scmi/sandbox-scmi_agent.c b/drivers/firmware/scmi/sandbox-scmi_agent.c
index 19be280ec44..74a87832dcb 100644
--- a/drivers/firmware/scmi/sandbox-scmi_agent.c
+++ b/drivers/firmware/scmi/sandbox-scmi_agent.c
@@ -80,9 +80,9 @@ static struct sandbox_scmi_pwd scmi_pwdom[] = {
};
static struct sandbox_scmi_clk scmi_clk[] = {
- { .rate = 333 },
- { .rate = 200 },
- { .rate = 1000 },
+ { .rate = 333, .perm = 0xE0000000 },
+ { .rate = 200, .perm = 0xE0000000 },
+ { .rate = 1000, .perm = 0xE0000000 },
};
static struct sandbox_scmi_reset scmi_reset[] = {
@@ -700,6 +700,21 @@ static int sandbox_scmi_pwd_name_get(struct udevice *dev, struct scmi_msg *msg)
/* Clock Protocol */
+static int sandbox_scmi_clock_protocol_version(struct udevice *dev,
+ struct scmi_msg *msg)
+{
+ struct scmi_protocol_version_out *out = NULL;
+
+ if (!msg->out_msg || msg->out_msg_sz < sizeof(*out))
+ return -EINVAL;
+
+ out = (struct scmi_protocol_version_out *)msg->out_msg;
+ out->version = 0x30000;
+ out->status = SCMI_SUCCESS;
+
+ return 0;
+}
+
static int sandbox_scmi_clock_protocol_attribs(struct udevice *dev,
struct scmi_msg *msg)
{
@@ -740,6 +755,9 @@ static int sandbox_scmi_clock_attribs(struct udevice *dev, struct scmi_msg *msg)
if (clk_state->enabled)
out->attributes = 1;
+ /* Restricted clock */
+ out->attributes |= BIT(1);
+
ret = snprintf(out->clock_name, sizeof(out->clock_name),
"clk%u", in->clock_id);
assert(ret > 0 && ret < sizeof(out->clock_name));
@@ -837,6 +855,34 @@ static int sandbox_scmi_clock_gate(struct udevice *dev, struct scmi_msg *msg)
return 0;
}
+static int sandbox_scmi_clock_permissions_get(struct udevice *dev,
+ struct scmi_msg *msg)
+{
+ struct scmi_clk_get_permissions_in *in = NULL;
+ struct scmi_clk_get_permissions_out *out = NULL;
+ struct sandbox_scmi_clk *clk_state = NULL;
+
+ if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) ||
+ !msg->out_msg || msg->out_msg_sz < sizeof(*out))
+ return -EINVAL;
+
+ in = (struct scmi_clk_get_permissions_in *)msg->in_msg;
+ out = (struct scmi_clk_get_permissions_out *)msg->out_msg;
+
+ clk_state = get_scmi_clk_state(in->clock_id);
+ if (!clk_state) {
+ dev_err(dev, "Unexpected clock ID %u\n", in->clock_id);
+
+ out->status = SCMI_NOT_FOUND;
+ } else {
+ out->permissions = clk_state->perm;
+
+ out->status = SCMI_SUCCESS;
+ }
+
+ return 0;
+}
+
static int sandbox_scmi_rd_attribs(struct udevice *dev, struct scmi_msg *msg)
{
struct scmi_rd_attr_in *in = NULL;
@@ -1193,6 +1239,8 @@ static int sandbox_scmi_test_process_msg(struct udevice *dev,
return sandbox_proto_not_supported(msg);
switch (msg->message_id) {
+ case SCMI_PROTOCOL_VERSION:
+ return sandbox_scmi_clock_protocol_version(dev, msg);
case SCMI_PROTOCOL_ATTRIBUTES:
return sandbox_scmi_clock_protocol_attribs(dev, msg);
case SCMI_CLOCK_ATTRIBUTES:
@@ -1203,6 +1251,8 @@ static int sandbox_scmi_test_process_msg(struct udevice *dev,
return sandbox_scmi_clock_rate_get(dev, msg);
case SCMI_CLOCK_CONFIG_SET:
return sandbox_scmi_clock_gate(dev, msg);
+ case SCMI_CLOCK_GET_PERMISSIONS:
+ return sandbox_scmi_clock_permissions_get(dev, msg);
default:
break;
}
diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c
index e6e43ae936a..e7ec2c108e6 100644
--- a/drivers/firmware/scmi/scmi_agent-uclass.c
+++ b/drivers/firmware/scmi/scmi_agent-uclass.c
@@ -97,6 +97,9 @@ struct udevice *scmi_get_protocol(struct udevice *dev,
case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN:
proto = priv->voltagedom_dev;
break;
+ case SCMI_PROTOCOL_ID_PINCTRL:
+ proto = priv->pinctrl_dev;
+ break;
default:
dev_err(dev, "Protocol not supported\n");
proto = NULL;
@@ -147,6 +150,9 @@ static int scmi_add_protocol(struct udevice *dev,
case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN:
priv->voltagedom_dev = proto;
break;
+ case SCMI_PROTOCOL_ID_PINCTRL:
+ priv->pinctrl_dev = proto;
+ break;
default:
dev_err(dev, "Protocol not supported\n");
return -EPROTO;
@@ -352,6 +358,22 @@ static int scmi_fill_base_info(struct udevice *agent, struct udevice *dev)
return 0;
}
+static struct driver *scmi_proto_driver_get(unsigned int proto_id)
+{
+ struct scmi_proto_driver *start, *entry;
+ int n_ents;
+
+ start = ll_entry_start(struct scmi_proto_driver, scmi_proto_driver);
+ n_ents = ll_entry_count(struct scmi_proto_driver, scmi_proto_driver);
+
+ for (entry = start; entry != start + n_ents; entry++) {
+ if (entry->match->proto_id == proto_id)
+ return entry->driver;
+ }
+
+ return NULL;
+}
+
/*
* SCMI agent devices binds devices of various uclasses depending on
* the FDT description. scmi_bind_protocol() is a generic bind sequence
@@ -409,31 +431,11 @@ static int scmi_bind_protocols(struct udevice *dev)
drv = NULL;
name = ofnode_get_name(node);
- switch (protocol_id) {
- case SCMI_PROTOCOL_ID_POWER_DOMAIN:
- if (CONFIG_IS_ENABLED(SCMI_POWER_DOMAIN) &&
- scmi_protocol_is_supported(dev, protocol_id))
- drv = DM_DRIVER_GET(scmi_power_domain);
- break;
- case SCMI_PROTOCOL_ID_CLOCK:
- if (CONFIG_IS_ENABLED(CLK_SCMI) &&
- scmi_protocol_is_supported(dev, protocol_id))
- drv = DM_DRIVER_GET(scmi_clock);
- break;
- case SCMI_PROTOCOL_ID_RESET_DOMAIN:
- if (IS_ENABLED(CONFIG_RESET_SCMI) &&
- scmi_protocol_is_supported(dev, protocol_id))
- drv = DM_DRIVER_GET(scmi_reset_domain);
- break;
- case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN:
- if (IS_ENABLED(CONFIG_DM_REGULATOR_SCMI) &&
- scmi_protocol_is_supported(dev, protocol_id))
- drv = DM_DRIVER_GET(scmi_voltage_domain);
- break;
- default:
- break;
- }
+ if (!scmi_protocol_is_supported(dev, protocol_id))
+ continue;
+
+ drv = scmi_proto_driver_get(protocol_id);
if (!drv) {
dev_dbg(dev, "Ignore unsupported SCMI protocol %#x\n",
protocol_id);
diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c
index 54d6689ce78..344df9454b3 100644
--- a/drivers/firmware/ti_sci.c
+++ b/drivers/firmware/ti_sci.c
@@ -2845,11 +2845,11 @@ static int ti_sci_probe(struct udevice *dev)
info->dev = dev;
info->seq = 0xA;
+ INIT_LIST_HEAD(&info->dev_list);
+
list_add_tail(&info->list, &ti_sci_list);
ti_sci_setup_ops(info);
- INIT_LIST_HEAD(&info->dev_list);
-
if (IS_ENABLED(CONFIG_SYSRESET_TI_SCI)) {
ret = device_bind_driver(dev, "ti-sci-sysreset", "sysreset", NULL);
if (ret)
@@ -2889,6 +2889,8 @@ static __maybe_unused int ti_sci_dm_probe(struct udevice *dev)
info->dev = dev;
info->seq = 0xA;
+ INIT_LIST_HEAD(&info->dev_list);
+
list_add_tail(&info->list, &ti_sci_list);
ops = &info->handle.ops;
diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c
index 2fb14590c0f..f0a79b92b02 100644
--- a/drivers/gpio/pca953x.c
+++ b/drivers/gpio/pca953x.c
@@ -13,6 +13,7 @@
#include <i2c.h>
#include <pca953x.h>
#include <vsprintf.h>
+#include <asm/byteorder.h>
/* Default to an address that hopefully won't corrupt other i2c devices */
#ifndef CFG_SYS_I2C_PCA953X_ADDR
diff --git a/drivers/gpio/tegra_gpio.c b/drivers/gpio/tegra_gpio.c
index b83df351e74..3d1e18854f2 100644
--- a/drivers/gpio/tegra_gpio.c
+++ b/drivers/gpio/tegra_gpio.c
@@ -248,6 +248,16 @@ static int tegra_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
return 0;
}
+static int tegra_gpio_rfree(struct udevice *dev, unsigned int offset)
+{
+ struct tegra_port_info *state = dev_get_priv(dev);
+
+ /* Set the pin as a SFIO */
+ set_config(state->base_gpio + offset, CFG_SFIO);
+
+ return 0;
+}
+
static const struct dm_gpio_ops gpio_tegra_ops = {
.direction_input = tegra_gpio_direction_input,
.direction_output = tegra_gpio_direction_output,
@@ -255,6 +265,7 @@ static const struct dm_gpio_ops gpio_tegra_ops = {
.set_value = tegra_gpio_set_value,
.get_function = tegra_gpio_get_function,
.xlate = tegra_gpio_xlate,
+ .rfree = tegra_gpio_rfree,
};
/*
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 0911d2fc0cc..ffc5868c0dd 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -104,7 +104,11 @@ config ROCKCHIP_OTP
config ROCKCHIP_IODOMAIN
bool "Rockchip IO-domain driver support"
depends on DM_REGULATOR && ARCH_ROCKCHIP
- default y if ROCKCHIP_RK3328 || ROCKCHIP_RK3568
+ default y if ROCKCHIP_PX30
+ default y if ROCKCHIP_RK3308
+ default y if ROCKCHIP_RK3328
+ default y if ROCKCHIP_RK3399
+ default y if ROCKCHIP_RK3568
help
Enable support for IO-domains in Rockchip SoCs. It is necessary
for the IO-domain setting of the SoC to match the voltage supplied
diff --git a/drivers/mmc/cv1800b_sdhci.c b/drivers/mmc/cv1800b_sdhci.c
index 4e75051c317..377e6a887df 100644
--- a/drivers/mmc/cv1800b_sdhci.c
+++ b/drivers/mmc/cv1800b_sdhci.c
@@ -31,6 +31,7 @@ static void cv1800b_sdhci_reset(struct sdhci_host *host, u8 mask)
udelay(10);
}
+#if CONFIG_IS_ENABLED(MMC_SUPPORTS_TUNING)
static int cv1800b_execute_tuning(struct mmc *mmc, u8 opcode)
{
struct sdhci_host *host = dev_get_priv(mmc->dev);
@@ -61,9 +62,12 @@ static int cv1800b_execute_tuning(struct mmc *mmc, u8 opcode)
return 0;
}
+#endif
const struct sdhci_ops cv1800b_sdhci_sd_ops = {
+#if CONFIG_IS_ENABLED(MMC_SUPPORTS_TUNING)
.platform_execute_tuning = cv1800b_execute_tuning,
+#endif
};
static int cv1800b_sdhci_bind(struct udevice *dev)
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 87a3099eeaf..655bf3aaf81 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -944,6 +944,7 @@ static int spi_nor_erase_chip_wait_till_ready(struct spi_nor *nor, unsigned long
static int clean_bar(struct spi_nor *nor)
{
u8 cmd, bank_sel = 0;
+ int ret;
if (nor->bank_curr == 0)
return 0;
@@ -951,7 +952,11 @@ static int clean_bar(struct spi_nor *nor)
nor->bank_curr = 0;
write_enable(nor);
- return nor->write_reg(nor, cmd, &bank_sel, 1);
+ ret = nor->write_reg(nor, cmd, &bank_sel, 1);
+ if (ret)
+ return ret;
+
+ return write_disable(nor);
}
static int write_bar(struct spi_nor *nor, u32 offset)
@@ -1272,6 +1277,10 @@ static int write_sr_and_check(struct spi_nor *nor, u8 status_new, u8 mask)
if (ret)
return ret;
+ ret = write_disable(nor);
+ if (ret)
+ return ret;
+
ret = read_sr(nor);
if (ret < 0)
return ret;
@@ -1809,13 +1818,18 @@ static int sst26_lock_ctl(struct spi_nor *nor, loff_t ofs, uint64_t len, enum lo
if (ctl == SST26_CTL_CHECK)
return 0;
+ /* Write latch enable before write operation */
+ ret = write_enable(nor);
+ if (ret)
+ return ret;
+
ret = nor->write_reg(nor, SPINOR_OP_WRITE_BPR, bpr_buff, bpr_size);
if (ret < 0) {
dev_err(nor->dev, "fail to write block-protection register\n");
return ret;
}
- return 0;
+ return write_disable(nor);
}
static int sst26_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
@@ -2217,7 +2231,7 @@ static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr)
return ret;
}
- return 0;
+ return write_disable(nor);
}
/**
@@ -4136,6 +4150,12 @@ static void mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor,
params->rdsr_addr_nbytes = 0;
/*
+ * SCCR Map 22nd DWORD does not indicate DTR Octal Mode Enable
+ * for MT35XU512ABA but is actually supported by device.
+ */
+ nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE;
+
+ /*
* The BFPT quad enable field is set to a reserved value so the quad
* enable function is ignored by spi_nor_parse_bfpt(). Make sure we
* disable it.
@@ -4299,6 +4319,9 @@ static int spi_nor_init(struct spi_nor *nor)
write_disable(nor);
}
}
+ err = write_disable(nor);
+ if (err)
+ return err;
}
if (nor->quad_enable) {
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 50e43928af0..4881287dd6b 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -1152,10 +1152,10 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
ubi_wl_close(ubi);
ubi_free_internal_volumes(ubi);
vfree(ubi->vtbl);
- put_mtd_device(ubi->mtd);
vfree(ubi->peb_buf);
vfree(ubi->fm_buf);
ubi_msg(ubi, "mtd%d is detached", ubi->mtd->index);
+ put_mtd_device(ubi->mtd);
put_device(&ubi->dev);
return 0;
}
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 4434d364777..950ed0f25a9 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -860,7 +860,7 @@ config RENESAS_RAVB
select PHY_ETHERNET_ID
help
This driver implements support for the Ethernet AVB block in
- Renesas M3 and H3 SoCs.
+ several Renesas R-Car and RZ SoCs.
config MPC8XX_FEC
bool "Fast Ethernet Controller on MPC8XX"
diff --git a/drivers/net/dc2114x.c b/drivers/net/dc2114x.c
index 7c0665faa8e..8fa549280aa 100644
--- a/drivers/net/dc2114x.c
+++ b/drivers/net/dc2114x.c
@@ -3,6 +3,7 @@
#include <asm/io.h>
#include <cpu_func.h>
#include <dm.h>
+#include <env.h>
#include <malloc.h>
#include <net.h>
#include <netdev.h>
diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
index 0cfe09333f7..551ee0ea6a0 100644
--- a/drivers/net/dwc_eth_qos.c
+++ b/drivers/net/dwc_eth_qos.c
@@ -1599,6 +1599,10 @@ static const struct udevice_id eqos_ids[] = {
.compatible = "st,stm32mp1-dwmac",
.data = (ulong)&eqos_stm32mp15_config
},
+ {
+ .compatible = "st,stm32mp25-dwmac",
+ .data = (ulong)&eqos_stm32mp25_config
+ },
#endif
#if IS_ENABLED(CONFIG_DWC_ETH_QOS_IMX)
{
diff --git a/drivers/net/dwc_eth_qos.h b/drivers/net/dwc_eth_qos.h
index 403e8203974..c239a5c7aca 100644
--- a/drivers/net/dwc_eth_qos.h
+++ b/drivers/net/dwc_eth_qos.h
@@ -301,5 +301,6 @@ extern struct eqos_config eqos_rockchip_config;
extern struct eqos_config eqos_qcom_config;
extern struct eqos_config eqos_stm32mp13_config;
extern struct eqos_config eqos_stm32mp15_config;
+extern struct eqos_config eqos_stm32mp25_config;
extern struct eqos_config eqos_jh7110_config;
extern struct eqos_config eqos_adi_config;
diff --git a/drivers/net/dwc_eth_qos_stm32.c b/drivers/net/dwc_eth_qos_stm32.c
index f3a973f3774..7a28f2a3e83 100644
--- a/drivers/net/dwc_eth_qos_stm32.c
+++ b/drivers/net/dwc_eth_qos_stm32.c
@@ -350,3 +350,14 @@ struct eqos_config __maybe_unused eqos_stm32mp15_config = {
.interface = dev_read_phy_mode,
.ops = &eqos_stm32_ops
};
+
+struct eqos_config __maybe_unused eqos_stm32mp25_config = {
+ .reg_access_always_ok = false,
+ .mdio_wait = 10000,
+ .swr_wait = 50,
+ .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB,
+ .config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_250_300,
+ .axi_bus_width = EQOS_AXI_WIDTH_64,
+ .interface = dev_read_phy_mode,
+ .ops = &eqos_stm32_ops
+};
diff --git a/drivers/net/dwc_eth_xgmac.c b/drivers/net/dwc_eth_xgmac.c
index cf8227b1b4d..03959ea95a5 100644
--- a/drivers/net/dwc_eth_xgmac.c
+++ b/drivers/net/dwc_eth_xgmac.c
@@ -45,6 +45,7 @@
#include <asm/gpio.h>
#include <asm/io.h>
#include <linux/delay.h>
+#include <linux/kernel.h>
#include "dwc_eth_xgmac.h"
static void *xgmac_alloc_descs(struct xgmac_priv *xgmac, unsigned int num)
@@ -457,7 +458,7 @@ static int xgmac_start(struct udevice *dev)
int ret, i;
u32 val, tx_fifo_sz, rx_fifo_sz, tqs, rqs, pbl;
ulong last_rx_desc;
- ulong desc_pad;
+ ulong desc_pad, address;
struct xgmac_desc *tx_desc = NULL;
struct xgmac_desc *rx_desc = NULL;
@@ -702,8 +703,11 @@ static int xgmac_start(struct udevice *dev)
for (i = 0; i < XGMAC_DESCRIPTORS_RX; i++) {
rx_desc = (struct xgmac_desc *)xgmac_get_desc(xgmac, i, true);
- rx_desc->des0 = (uintptr_t)(xgmac->rx_dma_buf +
- (i * XGMAC_MAX_PACKET_SIZE));
+ address = (uintptr_t)(xgmac->rx_dma_buf +
+ (i * XGMAC_MAX_PACKET_SIZE));
+
+ rx_desc->des0 = lower_32_bits(address);
+ rx_desc->des1 = upper_32_bits(address);
rx_desc->des3 = XGMAC_DESC3_OWN;
/* Flush the cache to the memory */
mb();
@@ -713,13 +717,17 @@ static int xgmac_start(struct udevice *dev)
XGMAC_MAX_PACKET_SIZE);
}
- writel(0, &xgmac->dma_regs->ch0_txdesc_list_haddress);
- writel((ulong)xgmac_get_desc(xgmac, 0, false),
+ address = (ulong)xgmac_get_desc(xgmac, 0, false);
+ writel(upper_32_bits(address),
+ &xgmac->dma_regs->ch0_txdesc_list_haddress);
+ writel(lower_32_bits(address),
&xgmac->dma_regs->ch0_txdesc_list_address);
writel(XGMAC_DESCRIPTORS_TX - 1,
&xgmac->dma_regs->ch0_txdesc_ring_length);
- writel(0, &xgmac->dma_regs->ch0_rxdesc_list_haddress);
- writel((ulong)xgmac_get_desc(xgmac, 0, true),
+ address = (ulong)xgmac_get_desc(xgmac, 0, true);
+ writel(upper_32_bits(address),
+ &xgmac->dma_regs->ch0_rxdesc_list_haddress);
+ writel(lower_32_bits(address),
&xgmac->dma_regs->ch0_rxdesc_list_address);
writel(XGMAC_DESCRIPTORS_RX - 1,
&xgmac->dma_regs->ch0_rxdesc_ring_length);
@@ -844,8 +852,8 @@ static int xgmac_send(struct udevice *dev, void *packet, int length)
xgmac->tx_desc_idx++;
xgmac->tx_desc_idx %= XGMAC_DESCRIPTORS_TX;
- tx_desc->des0 = (ulong)xgmac->tx_dma_buf;
- tx_desc->des1 = 0;
+ tx_desc->des0 = lower_32_bits((ulong)xgmac->tx_dma_buf);
+ tx_desc->des1 = upper_32_bits((ulong)xgmac->tx_dma_buf);
tx_desc->des2 = length;
/*
* Make sure that if HW sees the _OWN write below, it will see all the
@@ -901,6 +909,7 @@ static int xgmac_free_pkt(struct udevice *dev, uchar *packet, int length)
u32 idx, idx_mask = xgmac->desc_per_cacheline - 1;
uchar *packet_expected;
struct xgmac_desc *rx_desc;
+ ulong address;
debug("%s(packet=%p, length=%d)\n", __func__, packet, length);
@@ -920,13 +929,15 @@ static int xgmac_free_pkt(struct udevice *dev, uchar *packet, int length)
idx++) {
rx_desc = xgmac_get_desc(xgmac, idx, true);
rx_desc->des0 = 0;
+ rx_desc->des1 = 0;
/* Flush the cache to the memory */
mb();
xgmac->config->ops->xgmac_flush_desc(rx_desc);
xgmac->config->ops->xgmac_inval_buffer(packet, length);
- rx_desc->des0 = (u32)(ulong)(xgmac->rx_dma_buf +
- (idx * XGMAC_MAX_PACKET_SIZE));
- rx_desc->des1 = 0;
+ address = (ulong)(xgmac->rx_dma_buf +
+ (idx * XGMAC_MAX_PACKET_SIZE));
+ rx_desc->des0 = lower_32_bits(address);
+ rx_desc->des1 = upper_32_bits(address);
rx_desc->des2 = 0;
/*
* Make sure that if HW sees the _OWN write below,
diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c
index b77298070f8..1c8767dbea2 100644
--- a/drivers/net/e1000.c
+++ b/drivers/net/e1000.c
@@ -121,6 +121,7 @@ static struct pci_device_id e1000_supported[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I210_1000BASEKX) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I225_UNPROGRAMMED) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I225_IT) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I225_V) },
{}
};
@@ -1588,6 +1589,7 @@ e1000_set_mac_type(struct e1000_hw *hw)
case PCI_DEVICE_ID_INTEL_I210_1000BASEKX:
case PCI_DEVICE_ID_INTEL_I225_UNPROGRAMMED:
case PCI_DEVICE_ID_INTEL_I225_IT:
+ case PCI_DEVICE_ID_INTEL_I225_V:
hw->mac_type = e1000_igb;
break;
default:
@@ -4852,6 +4854,7 @@ static int e1000_set_phy_type (struct e1000_hw *hw)
hw->phy_type = e1000_phy_igb;
break;
case I225_I_PHY_ID:
+ case I225_V_PHY_ID:
case I226_LM_PHY_ID:
case I226_I_PHY_ID:
hw->phy_type = e1000_phy_igc;
@@ -4965,6 +4968,8 @@ e1000_detect_gig_phy(struct e1000_hw *hw)
match = true;
if (hw->phy_id == I225_I_PHY_ID)
match = true;
+ if (hw->phy_id == I225_V_PHY_ID)
+ match = true;
if (hw->phy_id == I226_LM_PHY_ID)
match = true;
if (hw->phy_id == I226_I_PHY_ID)
diff --git a/drivers/net/e1000.h b/drivers/net/e1000.h
index 5ca720f4609..cf8bd750757 100644
--- a/drivers/net/e1000.h
+++ b/drivers/net/e1000.h
@@ -2420,6 +2420,7 @@ struct e1000_hw {
#define I226_LM_PHY_ID 0x67C9DC10
#define I225_I_PHY_ID 0x67C9DCC0
#define I226_I_PHY_ID 0x67C9DCD0
+#define I225_V_PHY_ID 0x67C9DC00
/* Miscellaneous PHY bit definitions. */
#define PHY_PREAMBLE 0xFFFFFFFF
diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c
index 52fa820f518..97cccda4519 100644
--- a/drivers/net/fsl_enetc.c
+++ b/drivers/net/fsl_enetc.c
@@ -473,13 +473,15 @@ static int enetc_init_sxgmii(struct udevice *dev)
/* Apply protocol specific configuration to MAC, serdes as needed */
static void enetc_start_pcs(struct udevice *dev)
{
+ struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
struct enetc_priv *priv = dev_get_priv(dev);
/* register internal MDIO for debug purposes */
if (enetc_read_pcapr_mdio(dev)) {
priv->imdio.read = enetc_mdio_read;
priv->imdio.write = enetc_mdio_write;
- priv->imdio.priv = priv->port_regs + ENETC_PM_IMDIO_BASE;
+ priv->imdio.priv = priv->port_regs + data->reg_offset_mac +
+ ENETC_PM_IMDIO_BASE;
strlcpy(priv->imdio.name, dev->name, MDIO_NAME_LEN);
if (!miiphy_get_dev_by_name(priv->imdio.name))
mdio_register(&priv->imdio);
diff --git a/drivers/net/gmac_rockchip.c b/drivers/net/gmac_rockchip.c
index 8cfeeffe95b..c8cfe7448d4 100644
--- a/drivers/net/gmac_rockchip.c
+++ b/drivers/net/gmac_rockchip.c
@@ -151,26 +151,51 @@ static int rk3228_gmac_fix_mac_speed(struct dw_eth_dev *priv)
static int rk3288_gmac_fix_mac_speed(struct dw_eth_dev *priv)
{
+ struct dw_eth_pdata *dw_pdata = dev_get_plat(priv->dev);
+ struct eth_pdata *eth_pdata = &dw_pdata->eth_pdata;
struct rk3288_grf *grf;
+ grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
int clk;
- switch (priv->phydev->speed) {
- case 10:
- clk = RK3288_GMAC_CLK_SEL_2_5M;
- break;
- case 100:
- clk = RK3288_GMAC_CLK_SEL_25M;
- break;
- case 1000:
- clk = RK3288_GMAC_CLK_SEL_125M;
- break;
- default:
- debug("Unknown phy speed: %d\n", priv->phydev->speed);
- return -EINVAL;
- }
+ if (eth_pdata->phy_interface == PHY_INTERFACE_MODE_RMII) {
+ switch (priv->phydev->speed) {
+ case 10:
+ rk_clrsetreg(&grf->soc_con1,
+ RK3288_RMII_CLK_SEL_MASK |
+ RK3288_GMAC_SPEED_MASK,
+ RK3288_RMII_CLK_SEL_2_5M |
+ RK3288_GMAC_SPEED_10M);
+ break;
+ case 100:
+ rk_clrsetreg(&grf->soc_con1,
+ RK3288_RMII_CLK_SEL_MASK |
+ RK3288_GMAC_SPEED_MASK,
+ RK3288_RMII_CLK_SEL_25M |
+ RK3288_GMAC_SPEED_100M);
+ break;
+ default:
+ debug("Unknown phy speed: %d\n", priv->phydev->speed);
+ return -EINVAL;
+ }
+ } else {
+ switch (priv->phydev->speed) {
+ case 10:
+ clk = RK3288_GMAC_CLK_SEL_2_5M;
+ break;
+ case 100:
+ clk = RK3288_GMAC_CLK_SEL_25M;
+ break;
+ case 1000:
+ clk = RK3288_GMAC_CLK_SEL_125M;
+ break;
+
+ default:
+ debug("Unknown phy speed: %d\n", priv->phydev->speed);
+ return -EINVAL;
+ }
- grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
- rk_clrsetreg(&grf->soc_con1, RK3288_GMAC_CLK_SEL_MASK, clk);
+ rk_clrsetreg(&grf->soc_con1, RK3288_GMAC_CLK_SEL_MASK, clk);
+ }
return 0;
}
@@ -401,6 +426,17 @@ static void rk3228_gmac_set_to_rgmii(struct gmac_rockchip_plat *pdata)
pdata->tx_delay << RK3228_CLK_TX_DL_CFG_GMAC_SHIFT);
}
+static void rk3288_gmac_set_to_rmii(struct gmac_rockchip_plat *pdata)
+{
+ struct rk3288_grf *grf;
+
+ grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
+
+ rk_clrsetreg(&grf->soc_con1,
+ RK3288_GMAC_PHY_INTF_SEL_MASK | RK3288_RMII_MODE_MASK,
+ RK3288_GMAC_PHY_INTF_SEL_RMII | RK3288_RMII_MODE);
+}
+
static void rk3288_gmac_set_to_rgmii(struct gmac_rockchip_plat *pdata)
{
struct rk3288_grf *grf;
@@ -703,6 +739,7 @@ const struct rk_gmac_ops rk3228_gmac_ops = {
const struct rk_gmac_ops rk3288_gmac_ops = {
.fix_mac_speed = rk3288_gmac_fix_mac_speed,
.set_to_rgmii = rk3288_gmac_set_to_rgmii,
+ .set_to_rmii = rk3288_gmac_set_to_rmii,
};
const struct rk_gmac_ops rk3308_gmac_ops = {
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 1943de8ba73..c2ce4a80d12 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -61,8 +61,8 @@ static int is_broadcast(struct in_addr ip)
/* update only when the environment has changed */
if (env_changed_id != env_id) {
- netmask = env_get_ip("netmask");
- our_ip = env_get_ip("ipaddr");
+ netmask = string_to_ip(env_get("netmask"));
+ our_ip = string_to_ip(env_get("ipaddr"));
env_changed_id = env_id;
}
@@ -81,11 +81,12 @@ static int refresh_settings_from_env(void)
/* update only when the environment has changed */
if (env_changed_id != env_id) {
- if (env_get("ncip")) {
- nc_ip = env_get_ip("ncip");
+ char *tmp = env_get("ncip");
+ if (tmp) {
+ nc_ip = string_to_ip(tmp);
if (!nc_ip.s_addr)
return -1; /* ncip is 0.0.0.0 */
- p = strchr(env_get("ncip"), ':');
+ p = strchr(tmp, ':');
if (p != NULL) {
nc_out_port = dectoul(p + 1, NULL);
nc_in_port = nc_out_port;
diff --git a/drivers/net/pfe_eth/pfe_firmware.c b/drivers/net/pfe_eth/pfe_firmware.c
index da4f2ca42a5..b821fb17a1d 100644
--- a/drivers/net/pfe_eth/pfe_firmware.c
+++ b/drivers/net/pfe_eth/pfe_firmware.c
@@ -12,6 +12,7 @@
#include <dm.h>
#include <dm/device-internal.h>
+#include <env.h>
#include <image.h>
#include <log.h>
#include <malloc.h>
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 716a1d46111..e6fed8c41d7 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -839,8 +839,6 @@ struct phy_device *phy_find_by_mask(struct mii_dev *bus, uint phy_mask)
static void phy_connect_dev(struct phy_device *phydev, struct udevice *dev,
phy_interface_t interface)
{
- /* Soft Reset the PHY */
- phy_reset(phydev);
if (phydev->dev && phydev->dev != dev) {
printf("%s:%d is connected to %s. Reconnecting to %s\n",
phydev->bus->name, phydev->addr,
diff --git a/drivers/net/ravb.c b/drivers/net/ravb.c
index 539fd37ee59..68528864ac6 100644
--- a/drivers/net/ravb.c
+++ b/drivers/net/ravb.c
@@ -23,6 +23,7 @@
#include <asm/io.h>
#include <asm/global_data.h>
#include <asm/gpio.h>
+#include <reset.h>
/* Registers */
#define RAVB_REG_CCC 0x000
@@ -30,6 +31,7 @@
#define RAVB_REG_CSR 0x00C
#define RAVB_REG_APSR 0x08C
#define RAVB_REG_RCR 0x090
+#define RAVB_REG_RTC 0x0B4
#define RAVB_REG_TGC 0x300
#define RAVB_REG_TCCR 0x304
#define RAVB_REG_RIC0 0x360
@@ -43,6 +45,7 @@
#define RAVB_REG_GECMR 0x5b0
#define RAVB_REG_MAHR 0x5c0
#define RAVB_REG_MALR 0x5c8
+#define RAVB_REG_CSR0 0x800
#define CCC_OPC_CONFIG BIT(0)
#define CCC_OPC_OPERATION BIT(1)
@@ -64,14 +67,24 @@
#define PIR_MDC BIT(0)
#define ECMR_TRCCM BIT(26)
+#define ECMR_RCPT BIT(25)
#define ECMR_RZPF BIT(20)
#define ECMR_PFR BIT(18)
#define ECMR_RXF BIT(17)
+#define ECMR_TXF BIT(16)
#define ECMR_RE BIT(6)
#define ECMR_TE BIT(5)
#define ECMR_DM BIT(1)
+#define ECMR_PRM BIT(0)
#define ECMR_CHG_DM (ECMR_TRCCM | ECMR_RZPF | ECMR_PFR | ECMR_RXF)
+#define CSR0_RPE BIT(5)
+#define CSR0_TPE BIT(4)
+
+#define GECMR_SPEED_10M (0 << 4)
+#define GECMR_SPEED_100M (1 << 4)
+#define GECMR_SPEED_1G (2 << 4)
+
/* DMA Descriptors */
#define RAVB_NUM_BASE_DESC 16
#define RAVB_NUM_TX_DESC 8
@@ -107,6 +120,13 @@
#define RAVB_TX_TIMEOUT_MS 1000
+struct ravb_device_ops {
+ void (*mac_init)(struct udevice *dev);
+ void (*dmac_init)(struct udevice *dev);
+ void (*config)(struct udevice *dev);
+ bool has_reset;
+};
+
struct ravb_desc {
u32 ctrl;
u32 dptr;
@@ -130,6 +150,7 @@ struct ravb_priv {
struct mii_dev *bus;
void __iomem *iobase;
struct clk_bulk clks;
+ struct reset_ctl rst;
};
static inline void ravb_flush_dcache(u32 addr, u32 len)
@@ -181,7 +202,7 @@ static int ravb_recv(struct udevice *dev, int flags, uchar **packetp)
{
struct ravb_priv *eth = dev_get_priv(dev);
struct ravb_rxdesc *desc = &eth->rx_desc[eth->rx_desc_idx];
- int len;
+ int len = 0;
u8 *packet;
/* Check if the rx descriptor is ready */
@@ -190,12 +211,11 @@ static int ravb_recv(struct udevice *dev, int flags, uchar **packetp)
return -EAGAIN;
/* Check for errors */
- if (desc->data.ctrl & RAVB_RX_DESC_MSC_RX_ERR_MASK) {
+ if (desc->data.ctrl & RAVB_RX_DESC_MSC_RX_ERR_MASK)
desc->data.ctrl &= ~RAVB_RX_DESC_MSC_MASK;
- return -EAGAIN;
- }
+ else
+ len = desc->data.ctrl & RAVB_DESC_DS_MASK;
- len = desc->data.ctrl & RAVB_DESC_DS_MASK;
packet = (u8 *)(uintptr_t)desc->data.dptr;
ravb_invalidate_dcache((uintptr_t)packet, len);
@@ -349,10 +369,13 @@ static int ravb_write_hwaddr(struct udevice *dev)
}
/* E-MAC init function */
-static int ravb_mac_init(struct ravb_priv *eth)
+static void ravb_mac_init(struct udevice *dev)
{
- /* Disable MAC Interrupt */
- writel(0, eth->iobase + RAVB_REG_ECSIPR);
+ struct ravb_device_ops *device_ops =
+ (struct ravb_device_ops *)dev_get_driver_data(dev);
+ struct ravb_priv *eth = dev_get_priv(dev);
+
+ device_ops->mac_init(dev);
/*
* Set receive frame length
@@ -363,19 +386,33 @@ static int ravb_mac_init(struct ravb_priv *eth)
* largest frames add the CRC length to the maximum Rx descriptor size.
*/
writel(RFLR_RFL_MIN + ETH_FCS_LEN, eth->iobase + RAVB_REG_RFLR);
+}
- return 0;
+static void ravb_mac_init_rcar(struct udevice *dev)
+{
+ struct ravb_priv *eth = dev_get_priv(dev);
+
+ /* Disable MAC Interrupt */
+ writel(0, eth->iobase + RAVB_REG_ECSIPR);
+}
+
+static void ravb_mac_init_rzg2l(struct udevice *dev)
+{
+ struct ravb_priv *eth = dev_get_priv(dev);
+
+ setbits_32(eth->iobase + RAVB_REG_ECMR,
+ ECMR_PRM | ECMR_RXF | ECMR_TXF | ECMR_RCPT |
+ ECMR_TE | ECMR_RE | ECMR_RZPF |
+ (eth->phydev->duplex ? ECMR_DM : 0));
}
/* AVB-DMAC init function */
static int ravb_dmac_init(struct udevice *dev)
{
+ struct ravb_device_ops *device_ops =
+ (struct ravb_device_ops *)dev_get_driver_data(dev);
struct ravb_priv *eth = dev_get_priv(dev);
- struct eth_pdata *pdata = dev_get_plat(dev);
- int ret = 0;
- int mode = 0;
- unsigned int delay;
- bool explicit_delay = false;
+ int ret;
/* Set CONFIG mode */
ret = ravb_reset(dev);
@@ -391,6 +428,18 @@ static int ravb_dmac_init(struct udevice *dev)
/* Set little endian */
clrbits_le32(eth->iobase + RAVB_REG_CCC, CCC_BOC);
+ device_ops->dmac_init(dev);
+ return 0;
+}
+
+static void ravb_dmac_init_rcar(struct udevice *dev)
+{
+ struct ravb_priv *eth = dev_get_priv(dev);
+ struct eth_pdata *pdata = dev_get_plat(dev);
+ int mode = 0;
+ unsigned int delay;
+ bool explicit_delay = false;
+
/* AVB rx set */
writel(0x18000001, eth->iobase + RAVB_REG_RCR);
@@ -400,7 +449,7 @@ static int ravb_dmac_init(struct udevice *dev)
/* Delay CLK: 2ns (not applicable on R-Car E3/D3) */
if ((renesas_get_cpu_type() == RENESAS_CPU_TYPE_R8A77990) ||
(renesas_get_cpu_type() == RENESAS_CPU_TYPE_R8A77995))
- return 0;
+ return;
if (!dev_read_u32(dev, "rx-internal-delay-ps", &delay)) {
/* Valid values are 0 and 1800, according to DT bindings */
@@ -429,28 +478,45 @@ static int ravb_dmac_init(struct udevice *dev)
}
writel(mode, eth->iobase + RAVB_REG_APSR);
+}
- return 0;
+static void ravb_dmac_init_rzg2l(struct udevice *dev)
+{
+ struct ravb_priv *eth = dev_get_priv(dev);
+
+ /* Set Max Frame Length (RTC) */
+ writel(0x7ffc0000 | RFLR_RFL_MIN, eth->iobase + RAVB_REG_RTC);
}
static int ravb_config(struct udevice *dev)
{
+ struct ravb_device_ops *device_ops =
+ (struct ravb_device_ops *)dev_get_driver_data(dev);
struct ravb_priv *eth = dev_get_priv(dev);
struct phy_device *phy = eth->phydev;
- u32 mask = ECMR_CHG_DM | ECMR_RE | ECMR_TE;
int ret;
/* Configure AVB-DMAC register */
ravb_dmac_init(dev);
/* Configure E-MAC registers */
- ravb_mac_init(eth);
+ ravb_mac_init(dev);
ravb_write_hwaddr(dev);
ret = phy_startup(phy);
if (ret)
return ret;
+ device_ops->config(dev);
+ return 0;
+}
+
+static void ravb_config_rcar(struct udevice *dev)
+{
+ struct ravb_priv *eth = dev_get_priv(dev);
+ struct phy_device *phy = eth->phydev;
+ u32 mask = ECMR_CHG_DM | ECMR_RE | ECMR_TE;
+
/* Set the transfer speed */
if (phy->speed == 100)
writel(0, eth->iobase + RAVB_REG_GECMR);
@@ -462,8 +528,22 @@ static int ravb_config(struct udevice *dev)
mask |= ECMR_DM;
writel(mask, eth->iobase + RAVB_REG_ECMR);
+}
- return 0;
+static void ravb_config_rzg2l(struct udevice *dev)
+{
+ struct ravb_priv *eth = dev_get_priv(dev);
+ struct phy_device *phy = eth->phydev;
+
+ writel(CSR0_TPE | CSR0_RPE, eth->iobase + RAVB_REG_CSR0);
+
+ /* Set the transfer speed */
+ if (phy->speed == 10)
+ writel(GECMR_SPEED_10M, eth->iobase + RAVB_REG_GECMR);
+ else if (phy->speed == 100)
+ writel(GECMR_SPEED_100M, eth->iobase + RAVB_REG_GECMR);
+ else if (phy->speed == 1000)
+ writel(GECMR_SPEED_1G, eth->iobase + RAVB_REG_GECMR);
}
static int ravb_start(struct udevice *dev)
@@ -581,6 +661,8 @@ static int ravb_bb_miiphy_write(struct mii_dev *miidev, int addr,
static int ravb_probe(struct udevice *dev)
{
+ struct ravb_device_ops *device_ops =
+ (struct ravb_device_ops *)dev_get_driver_data(dev);
struct eth_pdata *pdata = dev_get_plat(dev);
struct ravb_priv *eth = dev_get_priv(dev);
struct mii_dev *mdiodev;
@@ -616,16 +698,32 @@ static int ravb_probe(struct udevice *dev)
if (ret)
goto err_clk_enable;
+ if (device_ops->has_reset) {
+ ret = reset_get_by_index(dev, 0, &eth->rst);
+ if (ret < 0)
+ goto err_clk_enable;
+
+ ret = reset_deassert(&eth->rst);
+ if (ret < 0)
+ goto err_reset_deassert;
+ }
+
ret = ravb_reset(dev);
if (ret)
- goto err_clk_enable;
+ goto err_ravb_reset;
ret = ravb_phy_config(dev);
if (ret)
- goto err_clk_enable;
+ goto err_ravb_reset;
return 0;
+err_ravb_reset:
+ if (device_ops->has_reset)
+ reset_assert(&eth->rst);
+err_reset_deassert:
+ if (device_ops->has_reset)
+ reset_free(&eth->rst);
err_clk_enable:
mdio_unregister(mdiodev);
err_mdio_register:
@@ -639,8 +737,14 @@ err_clk_get:
static int ravb_remove(struct udevice *dev)
{
+ struct ravb_device_ops *device_ops =
+ (struct ravb_device_ops *)dev_get_driver_data(dev);
struct ravb_priv *eth = dev_get_priv(dev);
+ if (device_ops->has_reset) {
+ reset_assert(&eth->rst);
+ reset_free(&eth->rst);
+ }
clk_release_bulk(&eth->clks);
free(eth->phydev);
@@ -675,9 +779,32 @@ int ravb_of_to_plat(struct udevice *dev)
return 0;
}
+static const struct ravb_device_ops ravb_device_ops_rcar = {
+ .mac_init = ravb_mac_init_rcar,
+ .dmac_init = ravb_dmac_init_rcar,
+ .config = ravb_config_rcar,
+};
+
+static const struct ravb_device_ops ravb_device_ops_rzg2l = {
+ .mac_init = ravb_mac_init_rzg2l,
+ .dmac_init = ravb_dmac_init_rzg2l,
+ .config = ravb_config_rzg2l,
+ .has_reset = true,
+};
+
static const struct udevice_id ravb_ids[] = {
- { .compatible = "renesas,etheravb-rcar-gen3" },
- { .compatible = "renesas,etheravb-rcar-gen4" },
+ {
+ .compatible = "renesas,etheravb-rcar-gen3",
+ .data = (ulong)&ravb_device_ops_rcar,
+ },
+ {
+ .compatible = "renesas,etheravb-rcar-gen4",
+ .data = (ulong)&ravb_device_ops_rcar,
+ },
+ {
+ .compatible = "renesas,rzg2l-gbeth",
+ .data = (ulong)&ravb_device_ops_rzg2l,
+ },
{ }
};
diff --git a/drivers/pinctrl/nxp/Kconfig b/drivers/pinctrl/nxp/Kconfig
index d13c5f2a6d5..84d9a3641ff 100644
--- a/drivers/pinctrl/nxp/Kconfig
+++ b/drivers/pinctrl/nxp/Kconfig
@@ -139,6 +139,19 @@ config PINCTRL_IMXRT
only parses the 'fsl,pins' property and configure related
registers.
+config PINCTRL_IMX_SCMI
+ bool "IMX pinctrl SCMI driver"
+ depends on ARCH_IMX9 && PINCTRL_FULL
+ select PINCTRL_IMX
+ help
+ This provides a simple pinctrl driver for i.MX SoC which supports
+ SCMI. This feature depends on device tree configuration. This driver
+ is different from the linux one, this is a simple implementation,
+ only parses the 'fsl,pins' property and configure related
+ registers.
+
+ Say Y here to enable the imx pinctrl SCMI driver
+
config PINCTRL_VYBRID
bool "Vybrid (vf610) pinctrl driver"
depends on ARCH_VF610 && PINCTRL_FULL
diff --git a/drivers/pinctrl/nxp/Makefile b/drivers/pinctrl/nxp/Makefile
index 44e37c631e5..7d861ae52c1 100644
--- a/drivers/pinctrl/nxp/Makefile
+++ b/drivers/pinctrl/nxp/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_PINCTRL_IMX93) += pinctrl-imx93.o
obj-$(CONFIG_PINCTRL_MXS) += pinctrl-mxs.o
obj-$(CONFIG_PINCTRL_VYBRID) += pinctrl-vf610.o
obj-$(CONFIG_PINCTRL_IMXRT) += pinctrl-imxrt.o
+obj-$(CONFIG_PINCTRL_IMX_SCMI) += pinctrl-imx-scmi.o
diff --git a/drivers/pinctrl/nxp/pinctrl-imx-scmi.c b/drivers/pinctrl/nxp/pinctrl-imx-scmi.c
new file mode 100644
index 00000000000..aed47be337d
--- /dev/null
+++ b/drivers/pinctrl/nxp/pinctrl-imx-scmi.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <asm/io.h>
+#include <asm/mach-imx/sys_proto.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <dm/pinctrl.h>
+#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
+#include <scmi_protocols.h>
+
+#include "pinctrl-imx.h"
+
+#define DAISY_OFFSET_IMX95 0x408
+
+/* SCMI pin control types */
+#define PINCTRL_TYPE_MUX 192
+#define PINCTRL_TYPE_CONFIG 193
+#define PINCTRL_TYPE_DAISY_ID 194
+#define PINCTRL_TYPE_DAISY_CFG 195
+#define PINCTRL_NUM_CFGS_SHIFT 2
+
+struct imx_scmi_pinctrl_priv {
+ u16 daisy_offset;
+};
+
+static int imx_pinconf_scmi_set(struct udevice *dev, u32 mux_ofs, u32 mux, u32 config_val,
+ u32 input_ofs, u32 input_val)
+{
+ struct imx_scmi_pinctrl_priv *priv = dev_get_priv(dev);
+ int ret, num_cfgs = 0;
+ struct scmi_msg msg;
+
+ /* Call SCMI API to set the pin mux and configuration. */
+ struct scmi_pinctrl_config_set_out out;
+ struct scmi_pinctrl_config_set_in in = {
+ .identifier = mux_ofs / 4,
+ .function_id = 0xFFFFFFFF,
+ .attributes = 0,
+ };
+
+ if (mux_ofs) {
+ in.configs[num_cfgs].type = PINCTRL_TYPE_MUX;
+ in.configs[num_cfgs].val = mux;
+ num_cfgs++;
+ }
+
+ if (config_val) {
+ in.configs[num_cfgs].type = PINCTRL_TYPE_CONFIG;
+ in.configs[num_cfgs].val = config_val;
+ num_cfgs++;
+ }
+
+ if (input_ofs) {
+ in.configs[num_cfgs].type = PINCTRL_TYPE_DAISY_ID;
+ in.configs[num_cfgs].val = (input_ofs - priv->daisy_offset) / 4;
+ num_cfgs++;
+ in.configs[num_cfgs].type = PINCTRL_TYPE_DAISY_CFG;
+ in.configs[num_cfgs].val = input_val;
+ num_cfgs++;
+ }
+
+ /* Update the number of configs sent in this call. */
+ in.attributes = num_cfgs << PINCTRL_NUM_CFGS_SHIFT;
+
+ msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_PINCTRL,
+ SCMI_MSG_PINCTRL_CONFIG_SET, in, out);
+
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret || out.status) {
+ dev_err(dev, "Failed to set PAD = %d, daisy = %d, scmi_err = %d, ret = %d\n",
+ mux_ofs / 4, input_ofs / 4, out.status, ret);
+ }
+
+ return ret;
+}
+
+static int imx_pinctrl_set_state_scmi(struct udevice *dev, struct udevice *config)
+{
+ int mux_ofs, mux, config_val, input_reg, input_val;
+ u32 *pin_data;
+ int i, j = 0;
+ int npins;
+ int ret;
+
+ ret = imx_pinctrl_set_state_common(dev, config, FSL_PIN_SIZE,
+ &pin_data, &npins);
+ if (ret)
+ return ret;
+
+ /*
+ * Refer to linux documentation for details:
+ * Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt
+ */
+ for (i = 0; i < npins; i++) {
+ mux_ofs = pin_data[j++];
+ /* Skip config_reg */
+ j++;
+ input_reg = pin_data[j++];
+
+ mux = pin_data[j++];
+ input_val = pin_data[j++];
+ config_val = pin_data[j++];
+
+ if (config_val & IMX_PAD_SION)
+ mux |= IOMUXC_CONFIG_SION;
+
+ config_val &= ~IMX_PAD_SION;
+
+ ret = imx_pinconf_scmi_set(dev, mux_ofs, mux, config_val, input_reg, input_val);
+ if (ret && ret != -EPERM) {
+ dev_err(dev, "Set pin %d, mux %d, val %d, error\n",
+ mux_ofs, mux, config_val);
+ }
+ }
+
+ devm_kfree(dev, pin_data);
+
+ return ret;
+}
+
+static const struct pinctrl_ops imx_scmi_pinctrl_ops = {
+ .set_state = imx_pinctrl_set_state_scmi,
+};
+
+static int imx_scmi_pinctrl_probe(struct udevice *dev)
+{
+ struct imx_scmi_pinctrl_priv *priv = dev_get_priv(dev);
+
+ if (IS_ENABLED(CONFIG_IMX95))
+ priv->daisy_offset = DAISY_OFFSET_IMX95;
+ else
+ return -EINVAL;
+
+ return devm_scmi_of_get_channel(dev);
+}
+
+static int imx_scmi_pinctrl_bind(struct udevice *dev)
+{
+ if (IS_ENABLED(CONFIG_IMX95))
+ return 0;
+
+ return -ENODEV;
+}
+
+U_BOOT_DRIVER(scmi_pinctrl_imx) = {
+ .name = "scmi_pinctrl_imx",
+ .id = UCLASS_PINCTRL,
+ .bind = imx_scmi_pinctrl_bind,
+ .probe = imx_scmi_pinctrl_probe,
+ .priv_auto = sizeof(struct imx_scmi_pinctrl_priv),
+ .ops = &imx_scmi_pinctrl_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+static struct scmi_proto_match match[] = {
+ { .proto_id = SCMI_PROTOCOL_ID_PINCTRL },
+ { /* Sentinel */ }
+};
+
+U_BOOT_SCMI_PROTO_DRIVER(scmi_pinctrl_imx, match);
diff --git a/drivers/pinctrl/rockchip/pinctrl-px30.c b/drivers/pinctrl/rockchip/pinctrl-px30.c
index cc7885bae40..4595d8a4a23 100644
--- a/drivers/pinctrl/rockchip/pinctrl-px30.c
+++ b/drivers/pinctrl/rockchip/pinctrl-px30.c
@@ -324,7 +324,7 @@ static struct rockchip_pin_bank px30_pin_banks[] = {
),
};
-static struct rockchip_pin_ctrl px30_pin_ctrl = {
+static const struct rockchip_pin_ctrl px30_pin_ctrl = {
.pin_banks = px30_pin_banks,
.nr_banks = ARRAY_SIZE(px30_pin_banks),
.grf_mux_offset = 0x0,
diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3036.c b/drivers/pinctrl/rockchip/pinctrl-rk3036.c
index b14386ccd93..8d0c0e0b655 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rk3036.c
+++ b/drivers/pinctrl/rockchip/pinctrl-rk3036.c
@@ -80,7 +80,7 @@ static struct rockchip_pin_bank rk3036_pin_banks[] = {
PIN_BANK(2, 32, "gpio2"),
};
-static struct rockchip_pin_ctrl rk3036_pin_ctrl = {
+static const struct rockchip_pin_ctrl rk3036_pin_ctrl = {
.pin_banks = rk3036_pin_banks,
.nr_banks = ARRAY_SIZE(rk3036_pin_banks),
.grf_mux_offset = 0xa8,
diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3066.c b/drivers/pinctrl/rockchip/pinctrl-rk3066.c
index 60e088a9a6f..f773f2a3dab 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rk3066.c
+++ b/drivers/pinctrl/rockchip/pinctrl-rk3066.c
@@ -82,7 +82,7 @@ static struct rockchip_pin_bank rk3066_pin_banks[] = {
PIN_BANK(6, 16, "gpio6"),
};
-static struct rockchip_pin_ctrl rk3066_pin_ctrl = {
+static const struct rockchip_pin_ctrl rk3066_pin_ctrl = {
.pin_banks = rk3066_pin_banks,
.nr_banks = ARRAY_SIZE(rk3066_pin_banks),
.grf_mux_offset = 0xa8,
diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3128.c b/drivers/pinctrl/rockchip/pinctrl-rk3128.c
index d00fc3da8b2..9f9c358694a 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rk3128.c
+++ b/drivers/pinctrl/rockchip/pinctrl-rk3128.c
@@ -171,7 +171,7 @@ static struct rockchip_pin_bank rk3128_pin_banks[] = {
PIN_BANK(3, 32, "gpio3"),
};
-static struct rockchip_pin_ctrl rk3128_pin_ctrl = {
+static const struct rockchip_pin_ctrl rk3128_pin_ctrl = {
.pin_banks = rk3128_pin_banks,
.nr_banks = ARRAY_SIZE(rk3128_pin_banks),
.grf_mux_offset = 0xa8,
diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3188.c b/drivers/pinctrl/rockchip/pinctrl-rk3188.c
index 83db51f66ae..3a93db5622d 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rk3188.c
+++ b/drivers/pinctrl/rockchip/pinctrl-rk3188.c
@@ -105,7 +105,7 @@ static struct rockchip_pin_bank rk3188_pin_banks[] = {
PIN_BANK(3, 32, "gpio3"),
};
-static struct rockchip_pin_ctrl rk3188_pin_ctrl = {
+static const struct rockchip_pin_ctrl rk3188_pin_ctrl = {
.pin_banks = rk3188_pin_banks,
.nr_banks = ARRAY_SIZE(rk3188_pin_banks),
.grf_mux_offset = 0x60,
diff --git a/drivers/pinctrl/rockchip/pinctrl-rk322x.c b/drivers/pinctrl/rockchip/pinctrl-rk322x.c
index b804597c048..a80978685d4 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rk322x.c
+++ b/drivers/pinctrl/rockchip/pinctrl-rk322x.c
@@ -257,7 +257,7 @@ static struct rockchip_pin_bank rk3228_pin_banks[] = {
PIN_BANK(3, 32, "gpio3"),
};
-static struct rockchip_pin_ctrl rk3228_pin_ctrl = {
+static const struct rockchip_pin_ctrl rk3228_pin_ctrl = {
.pin_banks = rk3228_pin_banks,
.nr_banks = ARRAY_SIZE(rk3228_pin_banks),
.grf_mux_offset = 0x0,
diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3288.c b/drivers/pinctrl/rockchip/pinctrl-rk3288.c
index 3870c1b7a34..d3ad1f70e5d 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rk3288.c
+++ b/drivers/pinctrl/rockchip/pinctrl-rk3288.c
@@ -212,7 +212,7 @@ static struct rockchip_pin_bank rk3288_pin_banks[] = {
PIN_BANK(8, 16, "gpio8"),
};
-static struct rockchip_pin_ctrl rk3288_pin_ctrl = {
+static const struct rockchip_pin_ctrl rk3288_pin_ctrl = {
.pin_banks = rk3288_pin_banks,
.nr_banks = ARRAY_SIZE(rk3288_pin_banks),
.grf_mux_offset = 0x0,
diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3308.c b/drivers/pinctrl/rockchip/pinctrl-rk3308.c
index 2cd91b10a3b..5c0e34a7baa 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rk3308.c
+++ b/drivers/pinctrl/rockchip/pinctrl-rk3308.c
@@ -421,7 +421,7 @@ static struct rockchip_pin_bank rk3308_pin_banks[] = {
IOMUX_8WIDTH_2BIT),
};
-static struct rockchip_pin_ctrl rk3308_pin_ctrl = {
+static const struct rockchip_pin_ctrl rk3308_pin_ctrl = {
.pin_banks = rk3308_pin_banks,
.nr_banks = ARRAY_SIZE(rk3308_pin_banks),
.grf_mux_offset = 0x0,
diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3328.c b/drivers/pinctrl/rockchip/pinctrl-rk3328.c
index dd0dc2eff27..1834df6c3d1 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rk3328.c
+++ b/drivers/pinctrl/rockchip/pinctrl-rk3328.c
@@ -330,7 +330,7 @@ static struct rockchip_pin_bank rk3328_pin_banks[] = {
0),
};
-static struct rockchip_pin_ctrl rk3328_pin_ctrl = {
+static const struct rockchip_pin_ctrl rk3328_pin_ctrl = {
.pin_banks = rk3328_pin_banks,
.nr_banks = ARRAY_SIZE(rk3328_pin_banks),
.grf_mux_offset = 0x0,
diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3368.c b/drivers/pinctrl/rockchip/pinctrl-rk3368.c
index 9ae06ed19e9..aaf24719a16 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rk3368.c
+++ b/drivers/pinctrl/rockchip/pinctrl-rk3368.c
@@ -152,7 +152,7 @@ static struct rockchip_pin_bank rk3368_pin_banks[] = {
PIN_BANK(3, 32, "gpio3"),
};
-static struct rockchip_pin_ctrl rk3368_pin_ctrl = {
+static const struct rockchip_pin_ctrl rk3368_pin_ctrl = {
.pin_banks = rk3368_pin_banks,
.nr_banks = ARRAY_SIZE(rk3368_pin_banks),
.grf_mux_offset = 0x0,
diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3399.c b/drivers/pinctrl/rockchip/pinctrl-rk3399.c
index b7a5092c032..928ed59aec6 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rk3399.c
+++ b/drivers/pinctrl/rockchip/pinctrl-rk3399.c
@@ -279,7 +279,7 @@ static struct rockchip_pin_bank rk3399_pin_banks[] = {
),
};
-static struct rockchip_pin_ctrl rk3399_pin_ctrl = {
+static const struct rockchip_pin_ctrl rk3399_pin_ctrl = {
.pin_banks = rk3399_pin_banks,
.nr_banks = ARRAY_SIZE(rk3399_pin_banks),
.grf_mux_offset = 0xe000,
diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3568.c b/drivers/pinctrl/rockchip/pinctrl-rk3568.c
index 5deedc648a4..c8a91b8bb6e 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rk3568.c
+++ b/drivers/pinctrl/rockchip/pinctrl-rk3568.c
@@ -345,7 +345,6 @@ static struct rockchip_pin_bank rk3568_pin_banks[] = {
static const struct rockchip_pin_ctrl rk3568_pin_ctrl = {
.pin_banks = rk3568_pin_banks,
.nr_banks = ARRAY_SIZE(rk3568_pin_banks),
- .nr_pins = 160,
.grf_mux_offset = 0x0,
.pmu_mux_offset = 0x0,
.iomux_routes = rk3568_mux_route_data,
diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3588.c b/drivers/pinctrl/rockchip/pinctrl-rk3588.c
index 98ababc7c90..fd8e617b910 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rk3588.c
+++ b/drivers/pinctrl/rockchip/pinctrl-rk3588.c
@@ -324,7 +324,6 @@ static struct rockchip_pin_bank rk3588_pin_banks[] = {
static const struct rockchip_pin_ctrl rk3588_pin_ctrl = {
.pin_banks = rk3588_pin_banks,
.nr_banks = ARRAY_SIZE(rk3588_pin_banks),
- .nr_pins = 160,
.set_mux = rk3588_set_mux,
.set_pull = rk3588_set_pull,
.set_drive = rk3588_set_drive,
diff --git a/drivers/pinctrl/rockchip/pinctrl-rockchip-core.c b/drivers/pinctrl/rockchip/pinctrl-rockchip-core.c
index d449d07d32e..4de67aba1c3 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rockchip-core.c
+++ b/drivers/pinctrl/rockchip/pinctrl-rockchip-core.c
@@ -532,6 +532,7 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(struct udevice *d
(struct rockchip_pin_ctrl *)dev_get_driver_data(dev);
struct rockchip_pin_bank *bank;
int grf_offs, pmu_offs, drv_grf_offs, drv_pmu_offs, i, j;
+ u32 ctrl_nr_pins = 0;
grf_offs = ctrl->grf_mux_offset;
pmu_offs = ctrl->pmu_mux_offset;
@@ -543,8 +544,8 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(struct udevice *d
int bank_pins = 0;
bank->priv = priv;
- bank->pin_base = ctrl->nr_pins;
- ctrl->nr_pins += bank->nr_pins;
+ bank->pin_base = ctrl_nr_pins;
+ ctrl_nr_pins += bank->nr_pins;
/* calculate iomux and drv offsets */
for (j = 0; j < 4; j++) {
diff --git a/drivers/pinctrl/rockchip/pinctrl-rockchip.h b/drivers/pinctrl/rockchip/pinctrl-rockchip.h
index 5e3c9c90760..ba684baed24 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rockchip.h
+++ b/drivers/pinctrl/rockchip/pinctrl-rockchip.h
@@ -503,7 +503,6 @@ struct rockchip_mux_route_data {
struct rockchip_pin_ctrl {
struct rockchip_pin_bank *pin_banks;
u32 nr_banks;
- u32 nr_pins;
int grf_mux_offset;
int pmu_mux_offset;
int grf_drv_offset;
diff --git a/drivers/pinctrl/rockchip/pinctrl-rv1108.c b/drivers/pinctrl/rockchip/pinctrl-rv1108.c
index 3eff5f59598..780da1e946e 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rv1108.c
+++ b/drivers/pinctrl/rockchip/pinctrl-rv1108.c
@@ -263,7 +263,7 @@ static struct rockchip_pin_bank rv1108_pin_banks[] = {
PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", 0, 0, 0, 0),
};
-static struct rockchip_pin_ctrl rv1108_pin_ctrl = {
+static const struct rockchip_pin_ctrl rv1108_pin_ctrl = {
.pin_banks = rv1108_pin_banks,
.nr_banks = ARRAY_SIZE(rv1108_pin_banks),
.grf_mux_offset = 0x10,
diff --git a/drivers/pinctrl/rockchip/pinctrl-rv1126.c b/drivers/pinctrl/rockchip/pinctrl-rv1126.c
index efa2408b204..3878a5420dc 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rv1126.c
+++ b/drivers/pinctrl/rockchip/pinctrl-rv1126.c
@@ -381,7 +381,6 @@ static struct rockchip_pin_bank rv1126_pin_banks[] = {
static const struct rockchip_pin_ctrl rv1126_pin_ctrl = {
.pin_banks = rv1126_pin_banks,
.nr_banks = ARRAY_SIZE(rv1126_pin_banks),
- .nr_pins = 130,
.grf_mux_offset = 0x10004, /* mux offset from GPIO0_D0 */
.pmu_mux_offset = 0x0,
.iomux_routes = rv1126_mux_route_data,
diff --git a/drivers/power/acpi_pmc/acpi-pmc-uclass.c b/drivers/power/acpi_pmc/acpi-pmc-uclass.c
index 1e94104091e..40488402c32 100644
--- a/drivers/power/acpi_pmc/acpi-pmc-uclass.c
+++ b/drivers/power/acpi_pmc/acpi-pmc-uclass.c
@@ -141,7 +141,7 @@ int pmc_prev_sleep_state(struct udevice *dev)
if (upriv->pm1_sts & WAK_STS) {
switch (acpi_sleep_from_pm1(upriv->pm1_cnt)) {
case ACPI_S3:
- if (IS_ENABLED(HAVE_ACPI_RESUME))
+ if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME))
prev_sleep_state = ACPI_S3;
break;
case ACPI_S5:
diff --git a/drivers/power/axp221.c b/drivers/power/axp221.c
index c22ca03f469..f5daa243082 100644
--- a/drivers/power/axp221.c
+++ b/drivers/power/axp221.c
@@ -10,6 +10,7 @@
*/
#include <command.h>
+#include <env.h>
#include <errno.h>
#include <asm/arch/pmic_bus.h>
#include <axp_pmic.h>
diff --git a/drivers/power/domain/scmi-power-domain.c b/drivers/power/domain/scmi-power-domain.c
index 3cd0f075d95..e8c0ba8878e 100644
--- a/drivers/power/domain/scmi-power-domain.c
+++ b/drivers/power/domain/scmi-power-domain.c
@@ -11,6 +11,7 @@
#include <power-domain.h>
#include <power-domain-uclass.h>
#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
#include <scmi_protocols.h>
#include <dm/device_compat.h>
@@ -190,3 +191,10 @@ U_BOOT_DRIVER(scmi_power_domain) = {
.probe = scmi_power_domain_probe,
.priv_auto = sizeof(struct scmi_power_domain_priv),
};
+
+static struct scmi_proto_match match[] = {
+ { .proto_id = SCMI_PROTOCOL_ID_POWER_DOMAIN },
+ { /* Sentinel */ }
+};
+
+U_BOOT_SCMI_PROTO_DRIVER(scmi_power_domain, match);
diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig
index 5a61cd45b8c..ec7ccc3a63f 100644
--- a/drivers/power/pmic/Kconfig
+++ b/drivers/power/pmic/Kconfig
@@ -184,6 +184,15 @@ config SPL_DM_PMIC_PFUZE100
This config enables implementation of driver-model pmic uclass features
for PMIC PFUZE100 in SPL. The driver implements read/write operations.
+config DM_PMIC_MAX8907
+ bool "Enable Driver Model for PMIC MAX8907"
+ ---help---
+ This config enables implementation of driver-model pmic uclass features
+ for PMIC MAX8907. The driver implements read/write operations.
+ This is a Power Management IC with a decent set of peripherals from which
+ 3 DC-to-DC Step-Down (SD) Regulators, 20 Low-Dropout Linear (LDO) Regulators,
+ Real-Time Clock (RTC) and more with I2C Compatible Interface.
+
config DM_PMIC_MAX77663
bool "Enable Driver Model for PMIC MAX77663"
---help---
diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
index 2210b1a64ae..6bebffb05a6 100644
--- a/drivers/power/pmic/Makefile
+++ b/drivers/power/pmic/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_$(PHASE_)DM_PMIC) += pmic-uclass.o
obj-$(CONFIG_$(PHASE_)DM_PMIC_FAN53555) += fan53555.o
obj-$(CONFIG_$(PHASE_)DM_PMIC_DA9063) += da9063.o
obj-$(CONFIG_$(PHASE_)DM_PMIC_MAX77663) += max77663.o
+obj-$(CONFIG_$(PHASE_)DM_PMIC_MAX8907) += max8907.o
obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o
obj-$(CONFIG_DM_PMIC_MAX8998) += max8998.o
obj-$(CONFIG_DM_PMIC_MC34708) += mc34708.o
diff --git a/drivers/power/pmic/max8907.c b/drivers/power/pmic/max8907.c
new file mode 100644
index 00000000000..a7ef70177de
--- /dev/null
+++ b/drivers/power/pmic/max8907.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright(C) 2024 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <dm.h>
+#include <dm/lists.h>
+#include <power/pmic.h>
+#include <power/max8907.h>
+
+static const struct pmic_child_info pmic_children_info[] = {
+ { .prefix = "ldo", .driver = MAX8907_LDO_DRIVER },
+ { .prefix = "sd", .driver = MAX8907_SD_DRIVER },
+ { },
+};
+
+static int max8907_write(struct udevice *dev, uint reg, const uint8_t *buff, int len)
+{
+ int ret;
+
+ ret = dm_i2c_write(dev, reg, buff, len);
+ if (ret) {
+ log_debug("%s: write error to device: %p register: %#x!\n",
+ __func__, dev, reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max8907_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
+{
+ int ret;
+
+ ret = dm_i2c_read(dev, reg, buff, len);
+ if (ret) {
+ log_debug("%s: read error from device: %p register: %#x!\n",
+ __func__, dev, reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max8907_bind(struct udevice *dev)
+{
+ ofnode regulators_node;
+ int children, ret;
+
+ if (IS_ENABLED(CONFIG_SYSRESET_MAX8907) &&
+ dev_read_bool(dev, "maxim,system-power-controller")) {
+ ret = device_bind_driver_to_node(dev, MAX8907_RST_DRIVER,
+ "sysreset", dev_ofnode(dev),
+ NULL);
+ if (ret) {
+ log_debug("%s: cannot bind SYSRESET (ret = %d)\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ regulators_node = dev_read_subnode(dev, "regulators");
+ if (!ofnode_valid(regulators_node)) {
+ log_err("%s regulators subnode not found!\n", dev->name);
+ return -ENXIO;
+ }
+
+ log_debug("%s: '%s' - found regulators subnode\n", __func__, dev->name);
+
+ children = pmic_bind_children(dev, regulators_node, pmic_children_info);
+ if (!children)
+ log_err("%s - no child found\n", dev->name);
+
+ /* Always return success for this device */
+ return 0;
+}
+
+static struct dm_pmic_ops max8907_ops = {
+ .read = max8907_read,
+ .write = max8907_write,
+};
+
+static const struct udevice_id max8907_ids[] = {
+ { .compatible = "maxim,max8907" },
+ { }
+};
+
+U_BOOT_DRIVER(pmic_max8907) = {
+ .name = "max8907_pmic",
+ .id = UCLASS_PMIC,
+ .of_match = max8907_ids,
+ .bind = max8907_bind,
+ .ops = &max8907_ops,
+};
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
index 95912ef5633..7ed435f0202 100644
--- a/drivers/power/regulator/Kconfig
+++ b/drivers/power/regulator/Kconfig
@@ -148,6 +148,15 @@ config SPL_REGULATOR_PWM
This config enables implementation of driver-model regulator uclass
features for PWM regulators in SPL.
+config DM_REGULATOR_MAX8907
+ bool "Enable Driver Model for REGULATOR MAX8907"
+ depends on DM_REGULATOR && DM_PMIC_MAX8907
+ ---help---
+ This config enables implementation of driver-model regulator uclass
+ features for REGULATOR MAX8907. The driver supports both DC-to-DC
+ Step-Down (SD) Regulators and Low-Dropout Linear (LDO) Regulators
+ found in MAX8907 PMIC and implements get/set api for value and enable.
+
config DM_REGULATOR_MAX77663
bool "Enable Driver Model for REGULATOR MAX77663"
depends on DM_REGULATOR && DM_PMIC_MAX77663
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
index 0ee5d908a2a..ee8f56ea3b9 100644
--- a/drivers/power/regulator/Makefile
+++ b/drivers/power/regulator/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_$(PHASE_)REGULATOR_AXP_DRIVEVBUS) += axp_drivevbus.o
obj-$(CONFIG_$(PHASE_)REGULATOR_AXP_USB_POWER) += axp_usb_power.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_DA9063) += da9063.o
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
diff --git a/drivers/power/regulator/max8907_regulator.c b/drivers/power/regulator/max8907_regulator.c
new file mode 100644
index 00000000000..00ecd4b808b
--- /dev/null
+++ b/drivers/power/regulator/max8907_regulator.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright(C) 2024 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <dm.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <power/max8907.h>
+
+static const char max8907_regmap[] = {
+ 0x00, MAX8907_REG_SDCTL1, MAX8907_REG_SDCTL2, MAX8907_REG_SDCTL3,
+ MAX8907_REG_LDOCTL1, MAX8907_REG_LDOCTL2, MAX8907_REG_LDOCTL3,
+ MAX8907_REG_LDOCTL4, MAX8907_REG_LDOCTL5, MAX8907_REG_LDOCTL6,
+ MAX8907_REG_LDOCTL7, MAX8907_REG_LDOCTL8, MAX8907_REG_LDOCTL9,
+ MAX8907_REG_LDOCTL10, MAX8907_REG_LDOCTL11, MAX8907_REG_LDOCTL12,
+ MAX8907_REG_LDOCTL13, MAX8907_REG_LDOCTL14, MAX8907_REG_LDOCTL15,
+ MAX8907_REG_LDOCTL16, MAX8907_REG_LDOCTL17, MAX8907_REG_LDOCTL18,
+ MAX8907_REG_LDOCTL19, MAX8907_REG_LDOCTL20
+};
+
+static int max8907_enable(struct udevice *dev, int op, bool *enable)
+{
+ struct dm_regulator_uclass_plat *uc_pdata =
+ dev_get_uclass_plat(dev);
+ int val, ret = 0;
+
+ if (op == PMIC_OP_GET) {
+ val = pmic_reg_read(dev->parent, uc_pdata->ctrl_reg);
+ if (val < 0)
+ return val;
+
+ if (val & MAX8907_MASK_LDO_EN)
+ *enable = true;
+ else
+ *enable = false;
+ } else if (op == PMIC_OP_SET) {
+ if (*enable) {
+ ret = pmic_clrsetbits(dev->parent,
+ uc_pdata->ctrl_reg,
+ MAX8907_MASK_LDO_EN |
+ MAX8907_MASK_LDO_SEQ,
+ MAX8907_MASK_LDO_EN |
+ MAX8907_MASK_LDO_SEQ);
+ } else {
+ ret = pmic_clrsetbits(dev->parent,
+ uc_pdata->ctrl_reg,
+ MAX8907_MASK_LDO_EN |
+ MAX8907_MASK_LDO_SEQ,
+ MAX8907_MASK_LDO_SEQ);
+ }
+ }
+
+ return ret;
+}
+
+static int max8907_get_enable(struct udevice *dev)
+{
+ bool enable = false;
+ int ret;
+
+ ret = max8907_enable(dev, PMIC_OP_GET, &enable);
+ if (ret)
+ return ret;
+
+ return enable;
+}
+
+static int max8907_set_enable(struct udevice *dev, bool enable)
+{
+ return max8907_enable(dev, PMIC_OP_SET, &enable);
+}
+
+/**
+ * max8907_volt2hex() - convert voltage in uV into
+ * applicable to register hex value
+ *
+ * @idx: regulator index
+ * @uV: voltage in uV
+ *
+ * Return: voltage in hex on success, -ve on failure
+ */
+static int max8907_volt2hex(int idx, int uV)
+{
+ switch (idx) {
+ case 1: /* SD1 */
+ if (uV > SD1_VOLT_MAX || uV < SD1_VOLT_MIN)
+ break;
+
+ return (uV - SD1_VOLT_MIN) / SD1_VOLT_STEP;
+
+ case 2: /* SD2 */
+ if (uV > SD2_VOLT_MAX || uV < SD2_VOLT_MIN)
+ break;
+
+ return (uV - SD2_VOLT_MIN) / SD2_VOLT_STEP;
+
+ case 3: /* SD3 */
+ if (uV > SD2_VOLT_MAX || uV < SD2_VOLT_MIN)
+ break;
+
+ return (uV - SD2_VOLT_MIN) / SD2_VOLT_STEP;
+
+ case 5: /* LDO2 */
+ case 6: /* LDO3 */
+ case 20: /* LDO17 */
+ case 21: /* LDO18 */
+ if (uV > LDO_650_VOLT_MAX || uV < LDO_650_VOLT_MIN)
+ break;
+
+ return (uV - LDO_650_VOLT_MIN) / LDO_650_VOLT_STEP;
+
+ default: /* LDO1, 4..16, 19..20 */
+ if (uV > LDO_750_VOLT_MAX || uV < LDO_750_VOLT_MIN)
+ break;
+
+ return (uV - LDO_750_VOLT_MIN) / LDO_750_VOLT_STEP;
+ };
+
+ return -EINVAL;
+}
+
+/**
+ * max8907_hex2volt() - convert register hex value into
+ * actual voltage in uV
+ *
+ * @idx: regulator index
+ * @hex: hex value of register
+ *
+ * Return: voltage in uV on success, -ve on failure
+ */
+static int max8907_hex2volt(int idx, int hex)
+{
+ switch (idx) {
+ case 1:
+ return hex * SD1_VOLT_STEP + SD1_VOLT_MIN;
+
+ case 2:
+ return hex * SD2_VOLT_STEP + SD2_VOLT_MIN;
+
+ case 3:
+ return hex * SD3_VOLT_STEP + SD3_VOLT_MIN;
+
+ case 5: /* LDO2 */
+ case 6: /* LDO3 */
+ case 20: /* LDO17 */
+ case 21: /* LDO18 */
+ return hex * LDO_650_VOLT_STEP + LDO_650_VOLT_MIN;
+
+ default: /* LDO1, 4..16, 19..20 */
+ return hex * LDO_750_VOLT_STEP + LDO_750_VOLT_MIN;
+ };
+
+ return -EINVAL;
+}
+
+static int max8907_val(struct udevice *dev, int op, int *uV)
+{
+ struct dm_regulator_uclass_plat *uc_pdata =
+ dev_get_uclass_plat(dev);
+ int idx = dev->driver_data;
+ int hex, ret;
+
+ if (op == PMIC_OP_GET) {
+ hex = pmic_reg_read(dev->parent, uc_pdata->volt_reg);
+ if (hex < 0)
+ return hex;
+
+ *uV = 0;
+
+ ret = max8907_hex2volt(idx, hex);
+ if (ret < 0)
+ return ret;
+ *uV = ret;
+
+ return 0;
+ }
+
+ hex = max8907_volt2hex(idx, *uV);
+ if (hex < 0)
+ return hex;
+
+ return pmic_reg_write(dev->parent, uc_pdata->volt_reg, hex);
+}
+
+static int max8907_get_value(struct udevice *dev)
+{
+ int uV;
+ int ret;
+
+ ret = max8907_val(dev, PMIC_OP_GET, &uV);
+ if (ret)
+ return ret;
+
+ return uV;
+}
+
+static int max8907_set_value(struct udevice *dev, int uV)
+{
+ return max8907_val(dev, PMIC_OP_SET, &uV);
+}
+
+static const struct dm_regulator_ops max8907_regulator_ops = {
+ .get_value = max8907_get_value,
+ .set_value = max8907_set_value,
+ .get_enable = max8907_get_enable,
+ .set_enable = max8907_set_enable,
+};
+
+static int max8907_sd_probe(struct udevice *dev)
+{
+ struct dm_regulator_uclass_plat *uc_pdata =
+ dev_get_uclass_plat(dev);
+ int idx = dev->driver_data;
+
+ uc_pdata->type = REGULATOR_TYPE_BUCK;
+ uc_pdata->ctrl_reg = max8907_regmap[idx];
+ uc_pdata->volt_reg = uc_pdata->ctrl_reg + MAX8907_VOUT;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(max8907_sd) = {
+ .name = MAX8907_SD_DRIVER,
+ .id = UCLASS_REGULATOR,
+ .ops = &max8907_regulator_ops,
+ .probe = max8907_sd_probe,
+};
+
+static int max8907_ldo_probe(struct udevice *dev)
+{
+ struct dm_regulator_uclass_plat *uc_pdata =
+ dev_get_uclass_plat(dev);
+ /* LDO regulator id is shifted by number for SD regulators */
+ int idx = dev->driver_data + 3;
+
+ uc_pdata->type = REGULATOR_TYPE_LDO;
+ uc_pdata->ctrl_reg = max8907_regmap[idx];
+ uc_pdata->volt_reg = uc_pdata->ctrl_reg + MAX8907_VOUT;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(max8907_ldo) = {
+ .name = MAX8907_LDO_DRIVER,
+ .id = UCLASS_REGULATOR,
+ .ops = &max8907_regulator_ops,
+ .probe = max8907_ldo_probe,
+};
diff --git a/drivers/power/regulator/scmi_regulator.c b/drivers/power/regulator/scmi_regulator.c
index 79db1a6a8aa..7d2db1e2bee 100644
--- a/drivers/power/regulator/scmi_regulator.c
+++ b/drivers/power/regulator/scmi_regulator.c
@@ -8,6 +8,7 @@
#include <dm.h>
#include <errno.h>
#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
#include <scmi_protocols.h>
#include <asm/types.h>
#include <dm/device.h>
@@ -202,3 +203,10 @@ U_BOOT_DRIVER(scmi_voltage_domain) = {
.id = UCLASS_NOP,
.bind = scmi_regulator_bind,
};
+
+static struct scmi_proto_match match[] = {
+ { .proto_id = SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN },
+ { /* Sentinel */ }
+};
+
+U_BOOT_SCMI_PROTO_DRIVER(scmi_voltage_domain, match);
diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig
index 2a40b0c9f81..39d03e8d3d3 100644
--- a/drivers/ram/Kconfig
+++ b/drivers/ram/Kconfig
@@ -135,3 +135,4 @@ source "drivers/ram/sifive/Kconfig"
source "drivers/ram/stm32mp1/Kconfig"
source "drivers/ram/starfive/Kconfig"
source "drivers/ram/sunxi/Kconfig"
+source "drivers/ram/thead/Kconfig"
diff --git a/drivers/ram/Makefile b/drivers/ram/Makefile
index f92e86eaa3f..82afd5fcbcc 100644
--- a/drivers/ram/Makefile
+++ b/drivers/ram/Makefile
@@ -30,3 +30,7 @@ obj-$(CONFIG_ARCH_OCTEON) += octeon/
obj-$(CONFIG_ARCH_RENESAS) += renesas/
obj-$(CONFIG_CADENCE_DDR_CTRL) += cadence/
+
+ifdef CONFIG_XPL_BUILD
+obj-$(CONFIG_SPL_THEAD_TH1520_DDR) += thead/
+endif
diff --git a/drivers/ram/thead/Kconfig b/drivers/ram/thead/Kconfig
new file mode 100644
index 00000000000..7b05abb6986
--- /dev/null
+++ b/drivers/ram/thead/Kconfig
@@ -0,0 +1,5 @@
+config SPL_THEAD_TH1520_DDR
+ bool "T-Head TH1520 DDR driver in SPL"
+ depends on SPL_RAM && THEAD_TH1520
+ help
+ This enables DDR support for T-Head TH1520 platforms.
diff --git a/drivers/ram/thead/Makefile b/drivers/ram/thead/Makefile
new file mode 100644
index 00000000000..ad4d053cfc2
--- /dev/null
+++ b/drivers/ram/thead/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SPL_THEAD_TH1520_DDR) += th1520_ddr.o
diff --git a/drivers/ram/thead/th1520_ddr.c b/drivers/ram/thead/th1520_ddr.c
new file mode 100644
index 00000000000..bb4736b0236
--- /dev/null
+++ b/drivers/ram/thead/th1520_ddr.c
@@ -0,0 +1,787 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017-2024 Alibaba Group Holding Limited
+ * Copyright (C) 2025 Yao Zi <ziyao@disroot.org>
+ */
+
+#include <binman.h>
+#include <binman_sym.h>
+#include <dm.h>
+#include <init.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+#include <ram.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#pragma pack(push, 1)
+
+struct th1520_ddr_fw {
+ u64 magic;
+ u8 type, ranknum, bitwidth, freq;
+ u8 reserved[8];
+
+ u32 cfgnum;
+ union th1520_ddr_cfg {
+ u32 opaddr;
+
+ struct th1520_ddr_phy {
+ u32 opaddr;
+ u16 data;
+ } phy;
+
+ struct th1520_ddr_range {
+ u32 opaddr;
+ u32 num;
+ u16 data[];
+ } range;
+ } cfgs[];
+};
+
+#pragma pack(pop)
+
+/* Firmware constants */
+#define TH1520_DDR_MAGIC 0x4452444445415448
+
+#define TH1520_DDR_TYPE_LPDDR4 0
+#define TH1520_DDR_TYPE_LPDDR4X 1
+
+#define TH1520_DDR_FREQ_2133 0
+#define TH1520_DDR_FREQ_3200 1
+#define TH1520_DDR_FREQ_3733 2
+#define TH1520_DDR_FREQ_4266 3
+
+#define TH1520_DDR_CFG_OP GENMASK(31, 24)
+#define TH1520_DDR_CFG_ADDR GENMASK(23, 0)
+
+#define TH1520_DDR_CFG_PHY0 0
+#define TH1520_DDR_CFG_PHY1 1
+#define TH1520_DDR_CFG_PHY 2
+#define TH1520_DDR_CFG_RANGE 3
+#define TH1520_DDR_CFG_WAITFW0 4
+#define TH1520_DDR_CFG_WAITFW1 5
+
+/* Driver constants */
+#define TH1520_SYS_PLL_TIMEOUT_US 30
+#define TH1520_CTRL_INIT_TIMEOUT_US 1000000
+#define TH1520_PHY_MSG_TIMEOUT_US 1000000
+
+/* System configuration registers */
+#define TH1520_SYS_DDR_CFG0 0x00
+#define TH1520_SYS_DDR_CFG0_APB_RSTN BIT(4)
+#define TH1520_SYS_DDR_CFG0_CTRL_RSTN BIT(5)
+#define TH1520_SYS_DDR_CFG0_PHY_PWROK_RSTN BIT(6)
+#define TH1520_SYS_DDR_CFG0_PHY_CORE_RSTN BIT(7)
+#define TH1520_SYS_DDR_CFG0_APB_PORT_RSTN(n) BIT(n + 4 + 4)
+#define TH1520_SYS_DDR_CFG1 0x04
+#define TH1520_SYS_PLL_CFG0 0x08
+#define TH1520_SYS_PLL_CFG0_POSTDIV2 GENMASK(26, 24)
+#define TH1520_SYS_PLL_CFG0_POSTDIV1 GENMASK(22, 20)
+#define TH1520_SYS_PLL_CFG0_FBDIV GENMASK(19, 8)
+#define TH1520_SYS_PLL_CFG0_REFDIV GENMASK(5, 0)
+#define TH1520_SYS_PLL_CFG1 0x0c
+#define TH1520_SYS_PLL_CFG1_RST BIT(30)
+#define TH1520_SYS_PLL_CFG1_FOUTPOSTDIVPD BIT(27)
+#define TH1520_SYS_PLL_CFG1_FOUT4PHASEPD BIT(25)
+#define Th1520_SYS_PLL_CFG1_DACPD BIT(24)
+#define TH1520_SYS_PLL_CFG2 0x10
+#define TH1520_SYS_PLL_CFG3 0x14
+#define TH1520_SYS_PLL_STS 0x18
+#define TH1520_SYS_PLL_STS_EN BIT(16)
+#define TH1520_SYS_PLL_STS_LOCKED BIT(0)
+
+/* DDR Controller Registers */
+#define TH1520_CTRL_MSTR 0x0000
+#define TH1520_CTRL_STAT 0x0004
+#define TH1520_CTRL_MRCTRL0 0x0010
+#define TH1520_CTRL_MRCTRL1 0x0014
+#define TH1520_CTRL_MRSTAT 0x0018
+#define TH1520_CTRL_DERATEEN 0x0020
+#define TH1520_CTRL_DERATEINT 0x0024
+#define TH1520_CTRL_DERATECTL 0x002c
+#define TH1520_CTRL_PWRCTL 0x0030
+#define TH1520_CTRL_PWRTMG 0x0034
+#define TH1520_CTRL_HWLPCTL 0x0038
+#define TH1520_CTRL_RFSHCTL0 0x0050
+#define TH1520_CTRL_RFSHCTL1 0x0054
+#define TH1520_CTRL_RFSHCTL3 0x0060
+#define TH1520_CTRL_RFSHTMG 0x0064
+#define TH1520_CTRL_RFSHTMG1 0x0068
+#define TH1520_CTRL_CRCPARCTL0 0x00c0
+#define TH1520_CTRL_CRCPARSTAT 0x00cc
+#define TH1520_CTRL_INIT0 0x00d0
+#define TH1520_CTRL_INIT1 0x00d4
+#define TH1520_CTRL_INIT2 0x00d8
+#define TH1520_CTRL_INIT3 0x00dc
+#define TH1520_CTRL_INIT4 0x00e0
+#define TH1520_CTRL_INIT5 0x00e4
+#define TH1520_CTRL_INIT6 0x00e8
+#define TH1520_CTRL_INIT7 0x00ec
+#define TH1520_CTRL_DIMMCTL 0x00f0
+#define TH1520_CTRL_RANKCTL 0x00f4
+#define TH1520_CTRL_RANKCTL1 0x00f8
+#define TH1520_CTRL_DRAMTMG0 0x0100
+#define TH1520_CTRL_DRAMTMG1 0x0104
+#define TH1520_CTRL_DRAMTMG2 0x0108
+#define TH1520_CTRL_DRAMTMG3 0x010c
+#define TH1520_CTRL_DRAMTMG4 0x0110
+#define TH1520_CTRL_DRAMTMG5 0x0114
+#define TH1520_CTRL_DRAMTMG6 0x0118
+#define TH1520_CTRL_DRAMTMG7 0x011c
+#define TH1520_CTRL_DRAMTMG8 0x0120
+#define TH1520_CTRL_DRAMTMG12 0x0130
+#define TH1520_CTRL_DRAMTMG13 0x0134
+#define TH1520_CTRL_DRAMTMG14 0x0138
+#define TH1520_CTRL_DRAMTMG17 0x0144
+#define TH1520_CTRL_ZQCTL0 0x0180
+#define TH1520_CTRL_ZQCTL1 0x0184
+#define TH1520_CTRL_ZQCTL2 0x0188
+#define TH1520_CTRL_ZQSTAT 0x018c
+#define TH1520_CTRL_DFITMG0 0x0190
+#define TH1520_CTRL_DFITMG1 0x0194
+#define TH1520_CTRL_DFILPCFG0 0x0198
+#define TH1520_CTRL_DFIUPD0 0x01a0
+#define TH1520_CTRL_DFIUPD1 0x01a4
+#define TH1520_CTRL_DFIUPD2 0x01a8
+#define TH1520_CTRL_DFIMISC 0x01b0
+#define TH1520_CTRL_DFITMG2 0x01b4
+#define TH1520_CTRL_DFISTAT 0x01bc
+#define TH1520_CTRL_DBICTL 0x01c0
+#define TH1520_CTRL_DFIPHYMSTR 0x01c4
+#define TH1520_CTRL_ADDRMAP0 0x0200
+#define TH1520_CTRL_ADDRMAP1 0x0204
+#define TH1520_CTRL_ADDRMAP2 0x0208
+#define TH1520_CTRL_ADDRMAP3 0x020c
+#define TH1520_CTRL_ADDRMAP4 0x0210
+#define TH1520_CTRL_ADDRMAP5 0x0214
+#define TH1520_CTRL_ADDRMAP6 0x0218
+#define TH1520_CTRL_ADDRMAP7 0x021c
+#define TH1520_CTRL_ADDRMAP8 0x0220
+#define TH1520_CTRL_ADDRMAP9 0x0224
+#define TH1520_CTRL_ADDRMAP10 0x0228
+#define TH1520_CTRL_ADDRMAP11 0x022c
+#define TH1520_CTRL_ODTCFG 0x0240
+#define TH1520_CTRL_ODTMAP 0x0244
+#define TH1520_CTRL_SCHED 0x0250
+#define TH1520_CTRL_SCHED1 0x0254
+#define TH1520_CTRL_PERFHPR1 0x025c
+#define TH1520_CTRL_PERFLPR1 0x0264
+#define TH1520_CTRL_PERFWR1 0x026c
+#define TH1520_CTRL_SCHED3 0x0270
+#define TH1520_CTRL_SCHED4 0x0274
+#define TH1520_CTRL_DBG0 0x0300
+#define TH1520_CTRL_DBG1 0x0304
+#define TH1520_CTRL_DBGCAM 0x0308
+#define TH1520_CTRL_DBGCMD 0x030c
+#define TH1520_CTRL_DBGSTAT 0x0310
+#define TH1520_CTRL_SWCTL 0x0320
+#define TH1520_CTRL_SWSTAT 0x0324
+#define TH1520_CTRL_SWCTLSTATIC 0x0328
+#define TH1520_CTRL_POISONCFG 0x036c
+#define TH1520_CTRL_POISONSTAT 0x0370
+#define TH1520_CTRL_DERATESTAT 0x03f0
+#define TH1520_CTRL_PSTAT 0x03fc
+#define TH1520_CTRL_PCCFG 0x0400
+#define TH1520_CTRL_PCFGR_0 0x0404
+#define TH1520_CTRL_PCFGW_0 0x0408
+#define TH1520_CTRL_PCTRL_0 0x0490
+#define TH1520_CTRL_PCFGQOS0_0 0x0494
+#define TH1520_CTRL_PCFGQOS1_0 0x0498
+#define TH1520_CTRL_PCFGWQOS0_0 0x049c
+#define TH1520_CTRL_PCFGWQOS1_0 0x04a0
+#define TH1520_CTRL_PCFGR_1 0x04b4
+#define TH1520_CTRL_PCFGW_1 0x04b8
+#define TH1520_CTRL_PCTRL_1 0x0540
+#define TH1520_CTRL_PCFGQOS0_1 0x0544
+#define TH1520_CTRL_PCFGQOS1_1 0x0548
+#define TH1520_CTRL_PCFGWQOS0_1 0x054c
+#define TH1520_CTRL_PCFGWQOS1_1 0x0550
+#define TH1520_CTRL_PCFGR_2 0x0564
+#define TH1520_CTRL_PCFGW_2 0x0568
+#define TH1520_CTRL_PCTRL_2 0x05f0
+#define TH1520_CTRL_PCFGQOS0_2 0x05f4
+#define TH1520_CTRL_PCFGQOS1_2 0x05f8
+#define TH1520_CTRL_PCFGWQOS0_2 0x05fc
+#define TH1520_CTRL_PCFGWQOS1_2 0x0600
+#define TH1520_CTRL_PCFGR_3 0x0614
+#define TH1520_CTRL_PCFGW_3 0x0618
+#define TH1520_CTRL_PCTRL_3 0x06a0
+#define TH1520_CTRL_PCFGQOS0_3 0x06a4
+#define TH1520_CTRL_PCFGQOS1_3 0x06a8
+#define TH1520_CTRL_PCFGWQOS0_3 0x06ac
+#define TH1520_CTRL_PCFGWQOS1_3 0x06b0
+#define TH1520_CTRL_PCFGR_4 0x06c4
+#define TH1520_CTRL_PCFGW_4 0x06c8
+#define TH1520_CTRL_PCTRL_4 0x0750
+#define TH1520_CTRL_PCFGQOS0_4 0x0754
+#define TH1520_CTRL_PCFGQOS1_4 0x0758
+#define TH1520_CTRL_PCFGWQOS0_4 0x075c
+#define TH1520_CTRL_PCFGWQOS1_4 0x0760
+#define TH1520_CTRL_UMCTL2_VER_NUMBER 0x0ff0
+#define TH1520_CTRL_UMCTL2_VER_TYPE 0x0ff4
+#define TH1520_CTRL_DCH1_STAT 0x1b04
+#define TH1520_CTRL_DCH1_MRCTRL0 0x1b10
+#define TH1520_CTRL_DCH1_MRCTRL1 0x1b14
+#define TH1520_CTRL_DCH1_MRSTAT 0x1b18
+#define TH1520_CTRL_DCH1_DERATECTL 0x1b2c
+#define TH1520_CTRL_DCH1_PWRCTL 0x1b30
+#define TH1520_CTRL_DCH1_HWLPCTL 0x1b38
+#define TH1520_CTRL_DCH1_CRCPARCTL0 0x1bc0
+#define TH1520_CTRL_DCH1_ZQCTL2 0x1c88
+#define TH1520_CTRL_DCH1_DFISTAT 0x1cbc
+#define TH1520_CTRL_DCH1_ODTMAP 0x1d44
+#define TH1520_CTRL_DCH1_DBG1 0x1e04
+#define TH1520_CTRL_DCH1_DBGCMD 0x1e0c
+#define TH1520_CTRL_DCH1_DBGCAM 0x1e08
+
+/* PHY configuration registers */
+#define TH1520_DDR_PHY_REG(regid) ((regid) * 2)
+
+/* UctShadowRegs */
+#define TH1520_PHY_MSG_STATUS TH1520_DDR_PHY_REG(0xd0004)
+#define TH1520_PHY_MSG_STATUS_EMPTY BIT(0)
+/* DctWriteProt */
+#define TH1520_PHY_MSG_ACK TH1520_DDR_PHY_REG(0xd0031)
+#define TH1520_PHY_MSG_ACK_EN BIT(0)
+/* UctWriteOnlyShadow */
+#define TH1520_PHY_MSG_ID TH1520_DDR_PHY_REG(0xd0032)
+#define TH1520_PHY_MSG_ID_COMPLETION 0x7
+#define TH1520_PHY_MSG_ID_ERROR 0xff
+/* UctDatWriteOnlyShadow */
+#define TH1520_PHY_MSG_DATA TH1520_DDR_PHY_REG(0xd0034)
+
+struct th1520_ddr_priv {
+ void __iomem *phy0;
+ void __iomem *phy1;
+ void __iomem *ctrl;
+ void __iomem *sys;
+};
+
+binman_sym_declare(ulong, ddr_fw, image_pos);
+
+static int th1520_ddr_pll_config(void __iomem *sysreg, unsigned int frequency)
+{
+ u32 tmp;
+ int ret;
+
+ tmp = TH1520_SYS_PLL_CFG1_RST |
+ TH1520_SYS_PLL_CFG1_FOUTPOSTDIVPD |
+ TH1520_SYS_PLL_CFG1_FOUT4PHASEPD |
+ Th1520_SYS_PLL_CFG1_DACPD;
+ writel(tmp, sysreg + TH1520_SYS_PLL_CFG1);
+
+ switch (frequency) {
+ case TH1520_DDR_FREQ_3733:
+ writel(FIELD_PREP(TH1520_SYS_PLL_CFG0_REFDIV, 1) |
+ FIELD_PREP(TH1520_SYS_PLL_CFG0_FBDIV, 77) |
+ FIELD_PREP(TH1520_SYS_PLL_CFG0_POSTDIV1, 2) |
+ FIELD_PREP(TH1520_SYS_PLL_CFG0_POSTDIV2, 1),
+ sysreg + TH1520_SYS_PLL_CFG0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ udelay(2);
+ tmp &= ~TH1520_SYS_PLL_CFG1_RST;
+ writel(tmp, sysreg + TH1520_SYS_PLL_CFG1);
+
+ ret = readl_poll_timeout(sysreg + TH1520_SYS_PLL_STS, tmp,
+ tmp & TH1520_SYS_PLL_STS_LOCKED,
+ TH1520_SYS_PLL_TIMEOUT_US);
+
+ writel(TH1520_SYS_PLL_STS_EN, sysreg + TH1520_SYS_PLL_STS);
+
+ return ret;
+}
+
+static int th1520_ddr_ctrl_init(void __iomem *ctrlreg, struct th1520_ddr_fw *fw)
+{
+ int ret;
+ u32 tmp;
+
+ writel(0x00000001, ctrlreg + TH1520_CTRL_DBG1);
+ writel(0x00000001, ctrlreg + TH1520_CTRL_PWRCTL);
+
+ ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_STAT, tmp,
+ tmp == 0x00000000,
+ TH1520_CTRL_INIT_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ if (fw->ranknum == 2)
+ writel(0x03080020, ctrlreg + TH1520_CTRL_MSTR);
+ else
+ return -EINVAL;
+
+ writel(0x00003030, ctrlreg + TH1520_CTRL_MRCTRL0);
+ writel(0x0002d90f, ctrlreg + TH1520_CTRL_MRCTRL1);
+
+ switch (fw->freq) {
+ case TH1520_DDR_FREQ_3733:
+ writel(0x000013f3, ctrlreg + TH1520_CTRL_DERATEEN);
+ writel(0x40000000, ctrlreg + TH1520_CTRL_DERATEINT);
+ writel(0x00000001, ctrlreg + TH1520_CTRL_DERATECTL);
+ writel(0x00000020, ctrlreg + TH1520_CTRL_PWRCTL);
+ writel(0x0040ae04, ctrlreg + TH1520_CTRL_PWRTMG);
+ writel(0x00430000, ctrlreg + TH1520_CTRL_HWLPCTL);
+ writel(0x00210004, ctrlreg + TH1520_CTRL_RFSHCTL0);
+ writel(0x000d0021, ctrlreg + TH1520_CTRL_RFSHCTL1);
+ writel(0x00000001, ctrlreg + TH1520_CTRL_RFSHCTL3);
+ writel(0x81c00084, ctrlreg + TH1520_CTRL_RFSHTMG);
+ writel(0x00540000, ctrlreg + TH1520_CTRL_RFSHTMG1);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_CRCPARCTL0);
+ writel(0xc0020002, ctrlreg + TH1520_CTRL_INIT0);
+ writel(0x00010002, ctrlreg + TH1520_CTRL_INIT1);
+ writel(0x00001f00, ctrlreg + TH1520_CTRL_INIT2);
+ writel(0x00640036, ctrlreg + TH1520_CTRL_INIT3);
+ writel(0x00f20008, ctrlreg + TH1520_CTRL_INIT4);
+ writel(0x0004000b, ctrlreg + TH1520_CTRL_INIT5);
+ writel(0x00440012, ctrlreg + TH1520_CTRL_INIT6);
+ writel(0x0004001a, ctrlreg + TH1520_CTRL_INIT7);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_DIMMCTL);
+ writel(0x0000ab9f, ctrlreg + TH1520_CTRL_RANKCTL);
+ writel(0x00000017, ctrlreg + TH1520_CTRL_RANKCTL1);
+ writel(0x1f263f28, ctrlreg + TH1520_CTRL_DRAMTMG0);
+ writel(0x00080839, ctrlreg + TH1520_CTRL_DRAMTMG1);
+ writel(0x08121d17, ctrlreg + TH1520_CTRL_DRAMTMG2);
+ writel(0x00d0e000, ctrlreg + TH1520_CTRL_DRAMTMG3);
+ writel(0x11040a12, ctrlreg + TH1520_CTRL_DRAMTMG4);
+ writel(0x02050e0e, ctrlreg + TH1520_CTRL_DRAMTMG5);
+ writel(0x01010008, ctrlreg + TH1520_CTRL_DRAMTMG6);
+ writel(0x00000502, ctrlreg + TH1520_CTRL_DRAMTMG7);
+ writel(0x00000101, ctrlreg + TH1520_CTRL_DRAMTMG8);
+ writel(0x00020000, ctrlreg + TH1520_CTRL_DRAMTMG12);
+ writel(0x0d100002, ctrlreg + TH1520_CTRL_DRAMTMG13);
+ writel(0x0000010c, ctrlreg + TH1520_CTRL_DRAMTMG14);
+ writel(0x03a50021, ctrlreg + TH1520_CTRL_ZQCTL0);
+ writel(0x02f00800, ctrlreg + TH1520_CTRL_ZQCTL1);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_ZQCTL2);
+ writel(0x059f820c, ctrlreg + TH1520_CTRL_DFITMG0);
+ writel(0x000c0303, ctrlreg + TH1520_CTRL_DFITMG1);
+ writel(0x0351a101, ctrlreg + TH1520_CTRL_DFILPCFG0);
+ writel(0x00000011, ctrlreg + TH1520_CTRL_DFIMISC);
+ writel(0x00001f0c, ctrlreg + TH1520_CTRL_DFITMG2);
+ writel(0x00000007, ctrlreg + TH1520_CTRL_DBICTL);
+ writel(0x14000001, ctrlreg + TH1520_CTRL_DFIPHYMSTR);
+ writel(0x06090b40, ctrlreg + TH1520_CTRL_ODTCFG);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ writel(0x00400018, ctrlreg + TH1520_CTRL_DFIUPD0);
+ writel(0x00280032, ctrlreg + TH1520_CTRL_DFIUPD1);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_DFIUPD2);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_ODTMAP);
+ writel(0x1f829b1c, ctrlreg + TH1520_CTRL_SCHED);
+ writel(0x4400b00f, ctrlreg + TH1520_CTRL_SCHED1);
+ writel(0x0f000001, ctrlreg + TH1520_CTRL_PERFHPR1);
+ writel(0x0f00007f, ctrlreg + TH1520_CTRL_PERFLPR1);
+ writel(0x0f00007f, ctrlreg + TH1520_CTRL_PERFWR1);
+ writel(0x00000208, ctrlreg + TH1520_CTRL_SCHED3);
+ writel(0x08400810, ctrlreg + TH1520_CTRL_SCHED4);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_DBG0);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_DBG1);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_DBGCMD);
+ writel(0x00000001, ctrlreg + TH1520_CTRL_SWCTL);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_SWCTLSTATIC);
+ writel(0x00000001, ctrlreg + TH1520_CTRL_POISONCFG);
+ writel(0x00000001, ctrlreg + TH1520_CTRL_PCTRL_0);
+ writel(0x00000001, ctrlreg + TH1520_CTRL_PCTRL_1);
+ writel(0x00000001, ctrlreg + TH1520_CTRL_PCTRL_2);
+ writel(0x00000001, ctrlreg + TH1520_CTRL_PCTRL_3);
+ writel(0x00000001, ctrlreg + TH1520_CTRL_PCTRL_4);
+ writel(0x00003030, ctrlreg + TH1520_CTRL_DCH1_MRCTRL0);
+ writel(0x0002d90f, ctrlreg + TH1520_CTRL_DCH1_MRCTRL1);
+ writel(0x00000001, ctrlreg + TH1520_CTRL_DCH1_DERATECTL);
+ writel(0x00000020, ctrlreg + TH1520_CTRL_DCH1_PWRCTL);
+ writel(0x00430002, ctrlreg + TH1520_CTRL_DCH1_HWLPCTL);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_DCH1_CRCPARCTL0);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_DCH1_ZQCTL2);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_DCH1_ODTMAP);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_DCH1_DBG1);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_DCH1_DBGCMD);
+
+ ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_RFSHCTL3, tmp,
+ tmp == 0x00000001,
+ TH1520_CTRL_INIT_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ writel(0x00000010, ctrlreg + TH1520_CTRL_PCCFG);
+ writel(0x0000500f, ctrlreg + TH1520_CTRL_PCFGR_0);
+ writel(0x0000500f, ctrlreg + TH1520_CTRL_PCFGW_0);
+ writel(0x00005020, ctrlreg + TH1520_CTRL_PCFGR_1);
+ writel(0x0000501f, ctrlreg + TH1520_CTRL_PCFGW_1);
+ writel(0x0000501f, ctrlreg + TH1520_CTRL_PCFGR_2);
+ writel(0x0000503f, ctrlreg + TH1520_CTRL_PCFGW_2);
+ writel(0x000051ff, ctrlreg + TH1520_CTRL_PCFGR_3);
+ writel(0x000051ff, ctrlreg + TH1520_CTRL_PCFGW_3);
+ writel(0x0000503f, ctrlreg + TH1520_CTRL_PCFGR_4);
+ writel(0x0000503f, ctrlreg + TH1520_CTRL_PCFGW_4);
+
+ ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_PWRCTL, tmp,
+ tmp == 0x00000020,
+ TH1520_CTRL_INIT_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ writel(0x00000020, ctrlreg + TH1520_CTRL_PWRCTL);
+
+ ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_DCH1_PWRCTL, tmp,
+ tmp == 0x00000020,
+ TH1520_CTRL_INIT_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ writel(0x00000020, ctrlreg + TH1520_CTRL_DCH1_PWRCTL);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_DBG1);
+
+ ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_PWRCTL, tmp,
+ tmp == 0x00000020,
+ TH1520_CTRL_INIT_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ writel(0x00000020, ctrlreg + TH1520_CTRL_PWRCTL);
+
+ ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_PWRCTL, tmp,
+ tmp == 0x00000020,
+ TH1520_CTRL_INIT_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ writel(0x00000020, ctrlreg + TH1520_CTRL_PWRCTL);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_DCH1_DBG1);
+
+ ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_DCH1_PWRCTL, tmp,
+ tmp == 0x00000020,
+ TH1520_CTRL_INIT_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ writel(0x00000020, ctrlreg + TH1520_CTRL_DCH1_PWRCTL);
+
+ ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_DCH1_PWRCTL, tmp,
+ tmp == 0x00000020,
+ TH1520_CTRL_INIT_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ writel(0x00000020, ctrlreg + TH1520_CTRL_DCH1_PWRCTL);
+ writel(0x14000001, ctrlreg + TH1520_CTRL_DFIPHYMSTR);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_SWCTL);
+ writel(0x00000010, ctrlreg + TH1520_CTRL_DFIMISC);
+ writel(0x00000010, ctrlreg + TH1520_CTRL_DFIMISC);
+ writel(0x00000002, ctrlreg + TH1520_CTRL_DBG1);
+ writel(0x00000002, ctrlreg + TH1520_CTRL_DCH1_DBG1);
+
+ switch (fw->bitwidth) {
+ case 64:
+ writel(0x00040018, ctrlreg + TH1520_CTRL_ADDRMAP0);
+ writel(0x00090909, ctrlreg + TH1520_CTRL_ADDRMAP1);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_ADDRMAP2);
+ writel(0x01010101, ctrlreg + TH1520_CTRL_ADDRMAP3);
+ writel(0x00001f1f, ctrlreg + TH1520_CTRL_ADDRMAP4);
+ writel(0x080f0808, ctrlreg + TH1520_CTRL_ADDRMAP5);
+ writel(0x08080808, ctrlreg + TH1520_CTRL_ADDRMAP6);
+ writel(0x00000f0f, ctrlreg + TH1520_CTRL_ADDRMAP7);
+ writel(0x08080808, ctrlreg + TH1520_CTRL_ADDRMAP9);
+ writel(0x08080808, ctrlreg + TH1520_CTRL_ADDRMAP10);
+ writel(0x00000008, ctrlreg + TH1520_CTRL_ADDRMAP11);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int th1520_ddr_read_msg(void __iomem *phyreg, u16 *id, u16 *data)
+{
+ u32 tmp;
+ int ret;
+
+ ret = readw_poll_timeout(phyreg + TH1520_PHY_MSG_STATUS, tmp,
+ !(tmp & TH1520_PHY_MSG_STATUS_EMPTY),
+ TH1520_PHY_MSG_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ *id = readw(phyreg + TH1520_PHY_MSG_ID);
+ *data = readw(phyreg + TH1520_PHY_MSG_DATA);
+
+ writew(0, phyreg + TH1520_PHY_MSG_ACK);
+
+ ret = readw_poll_timeout(phyreg + TH1520_PHY_MSG_STATUS, tmp,
+ tmp & TH1520_PHY_MSG_STATUS_EMPTY,
+ TH1520_PHY_MSG_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ writew(TH1520_PHY_MSG_ACK_EN, phyreg + TH1520_PHY_MSG_ACK);
+
+ return 0;
+}
+
+static int th1520_phy_wait_pmu_completion(void __iomem *phyreg)
+{
+ u16 id, data;
+ int ret;
+
+ do {
+ ret = th1520_ddr_read_msg(phyreg, &id, &data);
+
+ if (ret)
+ return ret;
+ } while (id != TH1520_PHY_MSG_ID_COMPLETION &&
+ id != TH1520_PHY_MSG_ID_ERROR &&
+ !ret);
+
+ return id == TH1520_PHY_MSG_ID_COMPLETION ? ret : -EIO;
+}
+
+static int lpddr4_load_firmware(struct th1520_ddr_priv *priv,
+ struct th1520_ddr_fw *fw)
+{
+ union th1520_ddr_cfg *cfg;
+ size_t i, j;
+ int ret;
+
+ for (cfg = fw->cfgs, i = 0; i < fw->cfgnum; i++) {
+ u32 addr = FIELD_GET(TH1520_DDR_CFG_ADDR, cfg->opaddr) * 2;
+ u32 op = FIELD_GET(TH1520_DDR_CFG_OP, cfg->opaddr);
+
+ switch (op) {
+ case TH1520_DDR_CFG_PHY0:
+ writew(cfg->phy.data, priv->phy0 + addr);
+ break;
+ case TH1520_DDR_CFG_PHY1:
+ writew(cfg->phy.data, priv->phy1 + addr);
+ break;
+ case TH1520_DDR_CFG_PHY:
+ writew(cfg->phy.data, priv->phy0 + addr);
+ writew(cfg->phy.data, priv->phy1 + addr);
+ break;
+ case TH1520_DDR_CFG_RANGE:
+ for (j = 0; j < cfg->range.num; j++) {
+ writew(cfg->range.data[j],
+ priv->phy0 + addr + j * 2);
+ writew(cfg->range.data[j],
+ priv->phy1 + addr + j * 2);
+ }
+ break;
+ case TH1520_DDR_CFG_WAITFW0:
+ ret = th1520_phy_wait_pmu_completion(priv->phy0);
+
+ if (ret) {
+ pr_err("phy 0 training failed: %d\n", ret);
+ return ret;
+ }
+
+ break;
+ case TH1520_DDR_CFG_WAITFW1:
+ ret = th1520_phy_wait_pmu_completion(priv->phy1);
+
+ if (ret) {
+ pr_err("phy 1 training failed: %d\n", ret);
+ return ret;
+ }
+
+ break;
+ default:
+ pr_err("Unknown DRAM configuration %d\n", op);
+
+ return -EOPNOTSUPP;
+ }
+
+ if (op == TH1520_DDR_CFG_RANGE)
+ cfg = (void *)cfg + sizeof(cfg->range) +
+ cfg->range.num * sizeof(u16);
+ else
+ cfg = (union th1520_ddr_cfg *)(&cfg->phy + 1);
+ }
+
+ return 0;
+}
+
+static int th1520_ddr_ctrl_enable(void __iomem *ctrlreg,
+ struct th1520_ddr_fw *fw)
+{
+ u32 tmp;
+ int ret;
+
+ writel(0x00000030, ctrlreg + TH1520_CTRL_DFIMISC);
+
+ ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_DFISTAT, tmp,
+ tmp == 0x00000001,
+ TH1520_CTRL_INIT_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_DCH1_DFISTAT, tmp,
+ tmp == 0x00000001,
+ TH1520_CTRL_INIT_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ writel(0x00000010, ctrlreg + TH1520_CTRL_DFIMISC);
+ writel(0x00000011, ctrlreg + TH1520_CTRL_DFIMISC);
+ writel(0x0000000a, ctrlreg + TH1520_CTRL_PWRCTL);
+ writel(0x0000000a, ctrlreg + TH1520_CTRL_DCH1_PWRCTL);
+ writel(0x00000001, ctrlreg + TH1520_CTRL_SWCTL);
+
+ ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_SWSTAT, tmp,
+ tmp == 0x00000001,
+ TH1520_CTRL_INIT_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_STAT, tmp,
+ tmp == 0x00000001,
+ TH1520_CTRL_INIT_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_DCH1_STAT, tmp,
+ tmp == 0x00000001,
+ TH1520_CTRL_INIT_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ writel(0x14000001, ctrlreg + TH1520_CTRL_DFIPHYMSTR);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_SWCTL);
+ writel(0x00020002, ctrlreg + TH1520_CTRL_INIT0);
+ writel(0x00000001, ctrlreg + TH1520_CTRL_SWCTL);
+
+ ret = readl_poll_timeout(ctrlreg + TH1520_CTRL_SWSTAT, tmp,
+ tmp == 0x00000001,
+ TH1520_CTRL_INIT_TIMEOUT_US);
+
+ if (ret)
+ return ret;
+
+ writel(0x00000000, ctrlreg + TH1520_CTRL_DBG1);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_DCH1_DBG1);
+
+ return 0;
+}
+
+static void th1520_ddr_enable_self_refresh(void __iomem *ctrlreg,
+ void __iomem *sysreg)
+{
+ writel(0x00000000, ctrlreg + TH1520_CTRL_RFSHCTL3);
+
+ writel(0x000a0000, sysreg + TH1520_SYS_DDR_CFG1);
+
+ writel(0x00000000, ctrlreg + TH1520_CTRL_SWCTL);
+ writel(0x00000001, ctrlreg + TH1520_CTRL_SWCTLSTATIC);
+ writel(0x0040ae04, ctrlreg + TH1520_CTRL_PWRTMG);
+ writel(0x00430003, ctrlreg + TH1520_CTRL_HWLPCTL);
+ writel(0x00430003, ctrlreg + TH1520_CTRL_DCH1_HWLPCTL);
+ writel(0x00000001, ctrlreg + TH1520_CTRL_SWCTL);
+ writel(0x00000000, ctrlreg + TH1520_CTRL_SWCTLSTATIC);
+ writel(0x0000000b, ctrlreg + TH1520_CTRL_PWRCTL);
+ writel(0x0000000b, ctrlreg + TH1520_CTRL_DCH1_PWRCTL);
+}
+
+static int th1520_ddr_init(struct th1520_ddr_priv *priv)
+{
+ struct th1520_ddr_fw *fw = (void *)binman_sym(ulong, ddr_fw, image_pos);
+ u32 reset;
+ int ret;
+
+ ret = th1520_ddr_pll_config(priv->sys, fw->freq);
+ if (ret) {
+ pr_err("failed to configure PLL: %d\n", ret);
+ return ret;
+ }
+
+ reset = TH1520_SYS_DDR_CFG0_PHY_PWROK_RSTN;
+ writel(reset, priv->sys + TH1520_SYS_DDR_CFG0);
+ reset |= TH1520_SYS_DDR_CFG0_PHY_CORE_RSTN;
+ writel(reset, priv->sys + TH1520_SYS_DDR_CFG0);
+ reset |= TH1520_SYS_DDR_CFG0_APB_RSTN;
+ writel(reset, priv->sys + TH1520_SYS_DDR_CFG0);
+
+ ret = th1520_ddr_ctrl_init(priv->ctrl, fw);
+ if (ret) {
+ pr_err("failed to initialize DDR controller: %d\n", ret);
+ return ret;
+ }
+
+ reset |= TH1520_SYS_DDR_CFG0_APB_PORT_RSTN(0) |
+ TH1520_SYS_DDR_CFG0_APB_PORT_RSTN(1) |
+ TH1520_SYS_DDR_CFG0_APB_PORT_RSTN(2) |
+ TH1520_SYS_DDR_CFG0_APB_PORT_RSTN(3) |
+ TH1520_SYS_DDR_CFG0_APB_PORT_RSTN(4) |
+ TH1520_SYS_DDR_CFG0_CTRL_RSTN;
+ writel(reset, priv->sys + TH1520_SYS_DDR_CFG0);
+
+ lpddr4_load_firmware(priv, fw);
+
+ ret = th1520_ddr_ctrl_enable(priv->ctrl, fw);
+ if (ret) {
+ pr_err("failed to enable DDR controller: %d\n", ret);
+ return ret;
+ }
+
+ th1520_ddr_enable_self_refresh(priv->ctrl, priv->sys);
+
+ return 0;
+}
+
+static int th1520_ddr_probe(struct udevice *dev)
+{
+ struct th1520_ddr_priv *priv = dev_get_priv(dev);
+ fdt_addr_t addr;
+
+ addr = dev_read_addr_name(dev, "phy-0");
+ priv->phy0 = (void __iomem *)addr;
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ addr = dev_read_addr_name(dev, "phy-1");
+ priv->phy1 = (void __iomem *)addr;
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ addr = dev_read_addr_name(dev, "ctrl");
+ priv->ctrl = (void __iomem *)addr;
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ addr = dev_read_addr_name(dev, "sys");
+ priv->sys = (void __iomem *)addr;
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ return th1520_ddr_init(priv);
+}
+
+static int th1520_ddr_get_info(struct udevice *dev, struct ram_info *info)
+{
+ info->base = gd->ram_base;
+ info->size = gd->ram_size;
+
+ return 0;
+}
+
+static struct ram_ops th1520_ddr_ops = {
+ .get_info = th1520_ddr_get_info,
+};
+
+static const struct udevice_id th1520_ddr_ids[] = {
+ { .compatible = "thead,th1520-ddrc" },
+ { }
+};
+
+U_BOOT_DRIVER(th1520_ddr) = {
+ .name = "th1520_ddr",
+ .id = UCLASS_RAM,
+ .ops = &th1520_ddr_ops,
+ .of_match = th1520_ddr_ids,
+ .probe = th1520_ddr_probe,
+ .priv_auto = sizeof(struct th1520_ddr_priv),
+};
diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c
index 6dc1fcb3365..f92a9e35579 100644
--- a/drivers/reset/reset-scmi.c
+++ b/drivers/reset/reset-scmi.c
@@ -9,6 +9,7 @@
#include <errno.h>
#include <reset-uclass.h>
#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
#include <scmi_protocols.h>
#include <asm/types.h>
@@ -81,3 +82,10 @@ U_BOOT_DRIVER(scmi_reset_domain) = {
.ops = &scmi_reset_domain_ops,
.probe = scmi_reset_probe,
};
+
+static struct scmi_proto_match match[] = {
+ { .proto_id = SCMI_PROTOCOL_ID_RESET_DOMAIN },
+ { /* Sentinel */ }
+};
+
+U_BOOT_SCMI_PROTO_DRIVER(scmi_reset_domain, match);
diff --git a/drivers/scsi/scsi-uclass.c b/drivers/scsi/scsi-uclass.c
index 1ee8236c05c..3eb6069649f 100644
--- a/drivers/scsi/scsi-uclass.c
+++ b/drivers/scsi/scsi-uclass.c
@@ -10,7 +10,9 @@
#define LOG_CATEGORY UCLASS_SCSI
+#include <blk.h>
#include <dm.h>
+#include <part.h>
#include <scsi.h>
int scsi_exec(struct udevice *dev, struct scsi_cmd *pccb)
@@ -23,6 +25,34 @@ int scsi_exec(struct udevice *dev, struct scsi_cmd *pccb)
return ops->exec(dev, pccb);
}
+int scsi_get_blk_by_uuid(const char *uuid,
+ struct blk_desc **blk_desc_ptr,
+ struct disk_partition *part_info_ptr)
+{
+ static int is_scsi_scanned;
+ struct blk_desc *blk;
+ int i, ret;
+
+ if (!is_scsi_scanned) {
+ scsi_scan(false /* no verbose */);
+ is_scsi_scanned = 1;
+ }
+
+ for (i = 0; i < blk_find_max_devnum(UCLASS_SCSI) + 1; i++) {
+ ret = blk_get_desc(UCLASS_SCSI, i, &blk);
+ if (ret)
+ continue;
+
+ ret = part_get_info_by_uuid(blk, uuid, part_info_ptr);
+ if (ret > 0) {
+ *blk_desc_ptr = blk;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
int scsi_bus_reset(struct udevice *dev)
{
struct scsi_ops *ops = scsi_get_ops(dev);
diff --git a/drivers/spi/nxp_fspi.c b/drivers/spi/nxp_fspi.c
index 7489c896f9d..6d97b8eefc9 100644
--- a/drivers/spi/nxp_fspi.c
+++ b/drivers/spi/nxp_fspi.c
@@ -52,13 +52,6 @@
#include <linux/bug.h>
#include <linux/err.h>
-/*
- * The driver only uses one single LUT entry, that is updated on
- * each call of exec_op(). Index 0 is preset at boot with a basic
- * read operation, so let's use the last entry (31).
- */
-#define SEQID_LUT 31
-
/* Registers used by the driver */
#define FSPI_MCR0 0x00
#define FSPI_MCR0_AHB_TIMEOUT(x) ((x) << 24)
@@ -242,9 +235,6 @@
#define FSPI_TFDR 0x180
#define FSPI_LUT_BASE 0x200
-#define FSPI_LUT_OFFSET (SEQID_LUT * 4 * 4)
-#define FSPI_LUT_REG(idx) \
- (FSPI_LUT_BASE + FSPI_LUT_OFFSET + (idx) * 4)
/* register map end */
@@ -316,6 +306,7 @@ struct nxp_fspi_devtype_data {
unsigned int txfifo;
unsigned int ahb_buf_size;
unsigned int quirks;
+ unsigned int lut_num;
bool little_endian;
};
@@ -324,6 +315,7 @@ static struct nxp_fspi_devtype_data lx2160a_data = {
.txfifo = SZ_1K, /* (128 * 64 bits) */
.ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
.quirks = 0,
+ .lut_num = 32,
.little_endian = true, /* little-endian */
};
@@ -332,9 +324,19 @@ static struct nxp_fspi_devtype_data imx8mm_data = {
.txfifo = SZ_1K, /* (128 * 64 bits) */
.ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
.quirks = 0,
+ .lut_num = 32,
.little_endian = true, /* little-endian */
};
+static struct nxp_fspi_devtype_data imxrt1170_data = {
+ .rxfifo = SZ_256,
+ .txfifo = SZ_256,
+ .ahb_buf_size = SZ_4K,
+ .quirks = 0,
+ .lut_num = 16,
+ .little_endian = true,
+};
+
struct nxp_fspi {
struct udevice *dev;
void __iomem *iobase;
@@ -486,6 +488,8 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f,
void __iomem *base = f->iobase;
u32 lutval[4] = {};
int lutidx = 1, i;
+ u32 lut_offset = (f->devtype_data->lut_num - 1) * 4 * 4;
+ u32 target_lut_reg;
/* cmd */
lutval[0] |= LUT_DEF(0, LUT_CMD, LUT_PAD(op->cmd.buswidth),
@@ -530,8 +534,10 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f,
fspi_writel(f, FSPI_LCKER_UNLOCK, f->iobase + FSPI_LCKCR);
/* fill LUT */
- for (i = 0; i < ARRAY_SIZE(lutval); i++)
- fspi_writel(f, lutval[i], base + FSPI_LUT_REG(i));
+ for (i = 0; i < ARRAY_SIZE(lutval); i++) {
+ target_lut_reg = FSPI_LUT_BASE + lut_offset + i * 4;
+ fspi_writel(f, lutval[i], base + target_lut_reg);
+ }
dev_dbg(f->dev, "CMD[%x] lutval[0:%x \t 1:%x \t 2:%x \t 3:%x], size: 0x%08x\n",
op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3], op->data.nbytes);
@@ -731,7 +737,7 @@ static int nxp_fspi_do_op(struct nxp_fspi *f, const struct spi_mem_op *op)
void __iomem *base = f->iobase;
int seqnum = 0;
int err = 0;
- u32 reg;
+ u32 reg, seqid_lut;
reg = fspi_readl(f, base + FSPI_IPRXFCR);
/* invalid RXFIFO first */
@@ -745,8 +751,9 @@ static int nxp_fspi_do_op(struct nxp_fspi *f, const struct spi_mem_op *op)
* the LUT at each exec_op() call. And also specify the DATA
* length, since it's has not been specified in the LUT.
*/
+ seqid_lut = f->devtype_data->lut_num - 1;
fspi_writel(f, op->data.nbytes |
- (SEQID_LUT << FSPI_IPCR1_SEQID_SHIFT) |
+ (seqid_lut << FSPI_IPCR1_SEQID_SHIFT) |
(seqnum << FSPI_IPCR1_SEQNUM_SHIFT),
base + FSPI_IPCR1);
@@ -862,7 +869,7 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f)
{
void __iomem *base = f->iobase;
int ret, i;
- u32 reg;
+ u32 reg, seqid_lut;
#if CONFIG_IS_ENABLED(CLK)
/* the default frequency, we will change it later if necessary. */
@@ -933,11 +940,17 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f)
fspi_writel(f, reg, base + FSPI_FLSHB1CR1);
fspi_writel(f, reg, base + FSPI_FLSHB2CR1);
+ /*
+ * The driver only uses one single LUT entry, that is updated on
+ * each call of exec_op(). Index 0 is preset at boot with a basic
+ * read operation, so let's use the last entry.
+ */
+ seqid_lut = f->devtype_data->lut_num - 1;
/* AHB Read - Set lut sequence ID for all CS. */
- fspi_writel(f, SEQID_LUT, base + FSPI_FLSHA1CR2);
- fspi_writel(f, SEQID_LUT, base + FSPI_FLSHA2CR2);
- fspi_writel(f, SEQID_LUT, base + FSPI_FLSHB1CR2);
- fspi_writel(f, SEQID_LUT, base + FSPI_FLSHB2CR2);
+ fspi_writel(f, seqid_lut, base + FSPI_FLSHA1CR2);
+ fspi_writel(f, seqid_lut, base + FSPI_FLSHA2CR2);
+ fspi_writel(f, seqid_lut, base + FSPI_FLSHB1CR2);
+ fspi_writel(f, seqid_lut, base + FSPI_FLSHB2CR2);
return 0;
}
@@ -1035,7 +1048,8 @@ static int nxp_fspi_of_to_plat(struct udevice *bus)
}
#endif
- dev_dbg(bus, "iobase=<0x%llx>, ahb_addr=<0x%llx>\n", iobase, ahb_addr);
+ dev_dbg(bus, "iobase=<0x%llx>, ahb_addr=<0x%llx>\n",
+ (long long)iobase, (long long)ahb_addr);
return 0;
}
@@ -1057,6 +1071,7 @@ static const struct udevice_id nxp_fspi_ids[] = {
{ .compatible = "nxp,lx2160a-fspi", .data = (ulong)&lx2160a_data, },
{ .compatible = "nxp,imx8mm-fspi", .data = (ulong)&imx8mm_data, },
{ .compatible = "nxp,imx8mp-fspi", .data = (ulong)&imx8mm_data, },
+ { .compatible = "nxp,imxrt1170-fspi", .data = (ulong)&imxrt1170_data, },
{ }
};
diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig
index 4972905482a..aa83073c96a 100644
--- a/drivers/sysreset/Kconfig
+++ b/drivers/sysreset/Kconfig
@@ -131,6 +131,13 @@ config SYSRESET_MAX77663
help
Enable system power management functions found in MAX77663 PMIC.
+config SYSRESET_MAX8907
+ bool "Enable support for MAX8907 PMIC System Reset"
+ depends on DM_PMIC_MAX8907
+ select SYSRESET_CMD_POWEROFF if CMD_POWEROFF
+ help
+ Enable system power management functions found in MAX8907 PMIC.
+
config SYSRESET_MICROBLAZE
bool "Enable support for Microblaze soft reset"
depends on MICROBLAZE
diff --git a/drivers/sysreset/Makefile b/drivers/sysreset/Makefile
index ded91a4d325..f5c78b25896 100644
--- a/drivers/sysreset/Makefile
+++ b/drivers/sysreset/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_SYSRESET_CV1800B) += sysreset_cv1800b.o
obj-$(CONFIG_$(PHASE_)POWEROFF_GPIO) += poweroff_gpio.o
obj-$(CONFIG_$(PHASE_)SYSRESET_GPIO) += sysreset_gpio.o
obj-$(CONFIG_$(PHASE_)SYSRESET_MAX77663) += sysreset_max77663.o
+obj-$(CONFIG_$(PHASE_)SYSRESET_MAX8907) += sysreset_max8907.o
obj-$(CONFIG_SYSRESET_MPC83XX) += sysreset_mpc83xx.o
obj-$(CONFIG_SYSRESET_MICROBLAZE) += sysreset_microblaze.o
obj-$(CONFIG_SYSRESET_OCTEON) += sysreset_octeon.o
diff --git a/drivers/sysreset/sysreset_max8907.c b/drivers/sysreset/sysreset_max8907.c
new file mode 100644
index 00000000000..6f62af9bffe
--- /dev/null
+++ b/drivers/sysreset/sysreset_max8907.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright(C) 2024 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <dm.h>
+#include <i2c.h>
+#include <errno.h>
+#include <sysreset.h>
+#include <power/pmic.h>
+#include <power/max8907.h>
+
+static int max8907_sysreset_request(struct udevice *dev, enum sysreset_t type)
+{
+ switch (type) {
+ case SYSRESET_POWER:
+ case SYSRESET_POWER_OFF:
+ /* MAX8907: PWR_OFF > RESET_CNFG */
+ pmic_clrsetbits(dev->parent, MAX8907_REG_RESET_CNFG,
+ MASK_POWER_OFF, MASK_POWER_OFF);
+ break;
+ default:
+ return -EPROTONOSUPPORT;
+ }
+
+ return -EINPROGRESS;
+}
+
+static struct sysreset_ops max8907_sysreset = {
+ .request = max8907_sysreset_request,
+};
+
+U_BOOT_DRIVER(sysreset_max8907) = {
+ .id = UCLASS_SYSRESET,
+ .name = MAX8907_RST_DRIVER,
+ .ops = &max8907_sysreset,
+};
diff --git a/drivers/tpm/sandbox_common.c b/drivers/tpm/sandbox_common.c
index 596e0156389..9d386fc32e5 100644
--- a/drivers/tpm/sandbox_common.c
+++ b/drivers/tpm/sandbox_common.c
@@ -9,6 +9,7 @@
#include <tpm-v1.h>
#include <tpm-v2.h>
+#include <linux/string.h>
#include <asm/unaligned.h>
#include "sandbox_common.h"
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index a35b8c2f646..847fa1f82c3 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -23,6 +23,7 @@
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
+#include <linux/iopoll.h>
#include <linux/ioport.h>
#include <dm.h>
#include <generic-phy.h>
@@ -587,7 +588,6 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
*/
static int dwc3_core_init(struct dwc3 *dwc)
{
- unsigned long timeout;
u32 hwparams4 = dwc->hwparams.hwparams4;
u32 reg;
int ret;
@@ -610,15 +610,11 @@ static int dwc3_core_init(struct dwc3 *dwc)
}
/* issue device SoftReset too */
- timeout = 5000;
dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
- while (timeout--) {
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
- if (!(reg & DWC3_DCTL_CSFTRST))
- break;
- };
-
- if (!timeout) {
+ ret = read_poll_timeout(dwc3_readl, reg,
+ !(reg & DWC3_DCTL_CSFTRST),
+ 1, 5000, dwc->regs, DWC3_DCTL);
+ if (ret) {
dev_err(dwc->dev, "Reset Timed Out\n");
ret = -ETIMEDOUT;
goto err0;
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 3dc79770eeb..5c9e8fc9d15 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -262,7 +262,7 @@ endif
config USB_EHCI_MSM
bool "Support for Qualcomm on-chip EHCI USB controller"
depends on DM_USB
- select USB_ULPI_VIEWPORT
+ select USB_ULPI
select MSM8916_USB_PHY
---help---
Enables support for the on-chip EHCI controller on Qualcomm
@@ -279,7 +279,10 @@ config USB_EHCI_TEGRA
depends on ARCH_TEGRA
select USB_EHCI_IS_TDI
---help---
- Enable support for Tegra on-chip EHCI USB controller
+ Enable support for Tegra on-chip EHCI USB controller. If you enable
+ ULPI and your PHY needs a different reference clock than the standard
+ 24 MHz then you have to define CFG_ULPI_REF_CLK to the appropriate
+ value in Hz.
config USB_EHCI_ZYNQ
bool "Support for Xilinx Zynq on-chip EHCI USB controller"
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 7c73eb66b60..89b87886da1 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -25,13 +25,6 @@
#define HOSTPC1_DEVLC 0x84
#define HOSTPC1_PSPD(x) (((x) >> 25) & 0x3)
-#ifdef CONFIG_USB_ULPI
- #ifndef CONFIG_USB_ULPI_VIEWPORT
- #error "To use CONFIG_USB_ULPI on Tegra Boards you have to also \
- define CONFIG_USB_ULPI_VIEWPORT"
- #endif
-#endif
-
/* Parameters we need for USB */
enum {
PARAM_DIVN, /* PLL FEEDBACK DIVIDer */
diff --git a/drivers/usb/ulpi/Kconfig b/drivers/usb/ulpi/Kconfig
index 001564d40c6..d969360f7eb 100644
--- a/drivers/usb/ulpi/Kconfig
+++ b/drivers/usb/ulpi/Kconfig
@@ -1,8 +1,18 @@
comment "ULPI drivers"
+config USB_ULPI
+ bool "ULPI support"
+ depends on USB_HOST
+ help
+ Select to commnicate with USB PHY via ULPI interface.
+ The ULPI (UTMI Low Pin (count) Interface) is a wrapper on UTMI+ core
+ that is used as PHY Transreceiver for USB controllers.
+
+ This driver uses ULPI viewports that are specific for each SoC.
+
choice
prompt "ULPI Viewport type"
- optional
+ depends on USB_ULPI
help
Select ULPI viewport (SoC-side interface to ULPI) implementation
appropriate for the device if you want to communicate with
@@ -10,23 +20,9 @@ choice
config USB_ULPI_VIEWPORT
bool "Generic ULPI Viewport"
+ depends on USB_ULPI
help
Support generic ULPI Viewport implementation that is used on
some Tegra and Snapdragon devices.
-config USB_ULPI_VIEWPORT_OMAP
- bool "OMAP ULPI Viewport"
- help
- Support ULPI Viewport implementation that is used on OMAP devices.
-
endchoice
-
-config USB_ULPI
- bool "ULPI support"
- depends on (USB_ULPI_VIEWPORT || USB_ULPI_VIEWPORT_OMAP)
- help
- Select to commnicate with USB PHY via ULPI interface.
- ULPI is wrapper on UTMI+ core that is used as
- PHY Transreceiver for USB controllers.
-
- This driver uses ULPI viewports that are specific for each SoC.
diff --git a/drivers/usb/ulpi/Makefile b/drivers/usb/ulpi/Makefile
index f05b7743531..5565948bc12 100644
--- a/drivers/usb/ulpi/Makefile
+++ b/drivers/usb/ulpi/Makefile
@@ -4,4 +4,3 @@
obj-$(CONFIG_USB_ULPI) += ulpi.o
obj-$(CONFIG_USB_ULPI_VIEWPORT) += ulpi-viewport.o
-obj-$(CONFIG_USB_ULPI_VIEWPORT_OMAP) += omap-ulpi-viewport.o
diff --git a/drivers/usb/ulpi/omap-ulpi-viewport.c b/drivers/usb/ulpi/omap-ulpi-viewport.c
deleted file mode 100644
index 6f0c3eb154e..00000000000
--- a/drivers/usb/ulpi/omap-ulpi-viewport.c
+++ /dev/null
@@ -1,71 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * OMAP ulpi viewport support
- * Based on drivers/usb/ulpi/ulpi-viewport.c
- *
- * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com
- * Author: Govindraj R <govindraj.raja@ti.com>
- */
-
-#include <log.h>
-#include <asm/io.h>
-#include <linux/delay.h>
-#include <usb/ulpi.h>
-
-#define OMAP_ULPI_WR_OPSEL (2 << 22)
-#define OMAP_ULPI_RD_OPSEL (3 << 22)
-#define OMAP_ULPI_START (1 << 31)
-
-/*
- * Wait for having ulpi in done state
- */
-static int ulpi_wait(struct ulpi_viewport *ulpi_vp, u32 mask)
-{
- int timeout = CFG_USB_ULPI_TIMEOUT;
-
- while (--timeout) {
- if (!(readl(ulpi_vp->viewport_addr) & mask))
- return 0;
-
- udelay(1);
- }
-
- return ULPI_ERROR;
-}
-
-/*
- * Issue a ULPI read/write request
- */
-static int ulpi_request(struct ulpi_viewport *ulpi_vp, u32 value)
-{
- int err;
-
- writel(value, ulpi_vp->viewport_addr);
-
- err = ulpi_wait(ulpi_vp, OMAP_ULPI_START);
- if (err)
- debug("ULPI request timed out\n");
-
- return err;
-}
-
-int ulpi_write(struct ulpi_viewport *ulpi_vp, u8 *reg, u32 value)
-{
- u32 val = OMAP_ULPI_START | (((ulpi_vp->port_num + 1) & 0xf) << 24) |
- OMAP_ULPI_WR_OPSEL | ((u32)reg << 16) | (value & 0xff);
-
- return ulpi_request(ulpi_vp, val);
-}
-
-u32 ulpi_read(struct ulpi_viewport *ulpi_vp, u8 *reg)
-{
- int err;
- u32 val = OMAP_ULPI_START | (((ulpi_vp->port_num + 1) & 0xf) << 24) |
- OMAP_ULPI_RD_OPSEL | ((u32)reg << 16);
-
- err = ulpi_request(ulpi_vp, val);
- if (err)
- return err;
-
- return readl(ulpi_vp->viewport_addr) & 0xff;
-}
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 80508fb24df..dfe4b3b8a02 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -559,6 +559,13 @@ config VIDEO_LCD_MOT
unknown. The panel has a 540x960 resolution and uses 24 bit RGB per
pixel.
+config VIDEO_LCD_NOVATEK_NT35510
+ bool "Novatek NT35510 DSI LCD panel support"
+ select VIDEO_MIPI_DSI
+ help
+ Say Y here if you want to enable support for Novatek nt35510
+ dsi panel.
+
config VIDEO_LCD_ORISETECH_OTM8009A
bool "OTM8009A DSI LCD panel support"
select VIDEO_MIPI_DSI
@@ -574,6 +581,14 @@ config VIDEO_LCD_LG_LD070WX3
Say Y here if you want to enable support for LG LD070WX3
800x1280 DSI video mode panel.
+config VIDEO_LCD_LG_LH400WV3
+ bool "LH400WV3-SD04 DSI LCD panel support"
+ depends on PANEL && BACKLIGHT
+ select VIDEO_MIPI_DSI
+ help
+ Say Y here if you want to enable support for LG LH400WV3
+ 480x800 DSI video mode panel.
+
config VIDEO_LCD_RAYDIUM_RM68200
bool "RM68200 DSI LCD panel support"
select VIDEO_MIPI_DSI
@@ -661,6 +676,14 @@ config VIDEO_LCD_TDO_TL070WSH30
Say Y here if you want to enable support for TDO TL070WSH30
1024x600 DSI video mode panel.
+config VIDEO_LCD_HITACHI_TX10D07VM0BAA
+ tristate "Hitachi TX10D07VM0BAA 480x800 MIPI DSI video mode panel"
+ depends on PANEL && BACKLIGHT
+ select VIDEO_MIPI_DSI
+ help
+ Say Y here if you want to enable support for Hitachi TX10D07VM0BAA
+ TFT-LCD module. The panel has a 480x800 resolution.
+
config VIDEO_LCD_HITACHI_TX18D42VM
bool "Hitachi tx18d42vm LVDS LCD panel support"
---help---
@@ -760,6 +783,16 @@ config ATMEL_HLCD
help
HLCDC supports video output to an attached LCD panel.
+config BACKLIGHT_AAT2870
+ bool "Backlight Driver for AAT2870"
+ depends on BACKLIGHT
+ select DM_I2C
+ help
+ Say Y to enable the backlight driver for Skyworks AAT2870 LED
+ Backlight Driver and Multiple LDO Lighting Management Unit.
+ Only backlight is supported as for now. Supported backlight
+ level range is from 2 to 255 with step of 1.
+
config BACKLIGHT_LM3532
bool "Backlight Driver for LM3532"
depends on BACKLIGHT
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index fbdb058647a..ebe4a3961fc 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_$(PHASE_)BMP) += bmp.o
endif
+obj-$(CONFIG_BACKLIGHT_AAT2870) += aat2870_backlight.o
obj-$(CONFIG_BACKLIGHT_LM3532) += lm3532_backlight.o
obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_backlight.o
obj-$(CONFIG_BACKLIGHT_LP855x) += lp855x_backlight.o
@@ -58,9 +59,12 @@ obj-$(CONFIG_VIDEO_IVYBRIDGE_IGD) += ivybridge_igd.o
obj-$(CONFIG_VIDEO_LCD_ANX9804) += anx9804.o
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_LG_LD070WX3) += lg-ld070wx3.o
+obj-$(CONFIG_VIDEO_LCD_LG_LH400WV3) += lg-lh400wv3-sd04.o
obj-$(CONFIG_VIDEO_LCD_MOT) += mot-panel.o
+obj-$(CONFIG_VIDEO_LCD_NOVATEK_NT35510) += novatek-nt35510.o
obj-$(CONFIG_VIDEO_LCD_ORISETECH_OTM8009A) += orisetech_otm8009a.o
obj-$(CONFIG_VIDEO_LCD_RAYDIUM_RM68200) += raydium-rm68200.o
obj-$(CONFIG_VIDEO_LCD_RENESAS_R61307) += renesas-r61307.o
diff --git a/drivers/video/aat2870_backlight.c b/drivers/video/aat2870_backlight.c
new file mode 100644
index 00000000000..209d15b3639
--- /dev/null
+++ b/drivers/video/aat2870_backlight.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT
+
+#include <backlight.h>
+#include <dm.h>
+#include <i2c.h>
+#include <log.h>
+#include <linux/err.h>
+#include <asm/gpio.h>
+#include <power/regulator.h>
+
+#define AAT2870_BL_MIN_BRIGHTNESS 0x01
+#define AAT2870_BL_DEF_BRIGHTNESS 0x64
+#define AAT2870_BL_MAX_BRIGHTNESS 0xff
+
+#define AAT2870_BL_CH_EN 0x00
+#define AAT2870_BLM 0x01
+
+#define AAT2870_BL_CH_ALL 0xff
+
+#define AAT2870_CURRENT_MAX 27900000
+#define AAT2870_CURRENT_STEP 900000
+
+struct aat2870_backlight_priv {
+ struct gpio_desc enable_gpio;
+
+ int channels;
+ int max_current;
+};
+
+static int aat2870_backlight_enable(struct udevice *dev)
+{
+ struct aat2870_backlight_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ dm_gpio_set_value(&priv->enable_gpio, 1);
+
+ /* Enable backlight for defined set of channels */
+ ret = dm_i2c_reg_write(dev, AAT2870_BL_CH_EN, priv->channels);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int aat2870_backlight_set_brightness(struct udevice *dev, int percent)
+{
+ struct aat2870_backlight_priv *priv = dev_get_priv(dev);
+ int brightness, ret;
+
+ if (percent == BACKLIGHT_DEFAULT)
+ percent = AAT2870_BL_DEF_BRIGHTNESS;
+
+ if (percent < AAT2870_BL_MIN_BRIGHTNESS)
+ percent = AAT2870_BL_MIN_BRIGHTNESS;
+
+ if (percent > AAT2870_BL_MAX_BRIGHTNESS)
+ percent = AAT2870_BL_MAX_BRIGHTNESS;
+
+ brightness = percent * priv->max_current;
+ brightness /= AAT2870_BL_MAX_BRIGHTNESS;
+
+ /* Set brightness level */
+ ret = dm_i2c_reg_write(dev, AAT2870_BLM, brightness);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int aat2870_backlight_of_to_plat(struct udevice *dev)
+{
+ struct aat2870_backlight_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = gpio_request_by_name(dev, "enable-gpios", 0,
+ &priv->enable_gpio, GPIOD_IS_OUT);
+ if (ret) {
+ log_err("%s: cannot get enable-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /* Backlight is one of children but has no dedicated driver */
+ ofnode backlight = ofnode_find_subnode(dev_ofnode(dev), "backlight");
+ if (ofnode_valid(backlight) && ofnode_is_enabled(backlight)) {
+ /* Number of channel is equal to bit number */
+ priv->channels = dev_read_u32_default(dev, "channels", AAT2870_BL_CH_ALL);
+ if (priv->channels != AAT2870_BL_CH_ALL)
+ priv->channels = BIT(priv->channels);
+
+ /* 450mA - 27900mA range with a 900mA step */
+ priv->max_current = dev_read_u32_default(dev, "current-max-microamp",
+ AAT2870_CURRENT_MAX);
+ priv->max_current /= AAT2870_CURRENT_STEP;
+ }
+
+ return 0;
+}
+
+static int aat2870_backlight_probe(struct udevice *dev)
+{
+ if (device_get_uclass_id(dev->parent) != UCLASS_I2C)
+ return -EPROTONOSUPPORT;
+
+ return 0;
+}
+
+static const struct backlight_ops aat2870_backlight_ops = {
+ .enable = aat2870_backlight_enable,
+ .set_brightness = aat2870_backlight_set_brightness,
+};
+
+static const struct udevice_id aat2870_backlight_ids[] = {
+ { .compatible = "analogictech,aat2870" },
+ { .compatible = "skyworks,aat2870" },
+ { }
+};
+
+U_BOOT_DRIVER(aat2870_backlight) = {
+ .name = "aat2870_backlight",
+ .id = UCLASS_PANEL_BACKLIGHT,
+ .of_match = aat2870_backlight_ids,
+ .of_to_plat = aat2870_backlight_of_to_plat,
+ .probe = aat2870_backlight_probe,
+ .ops = &aat2870_backlight_ops,
+ .priv_auto = sizeof(struct aat2870_backlight_priv),
+};
diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c
index 980baee83cf..6d2c2c2e177 100644
--- a/drivers/video/console_truetype.c
+++ b/drivers/video/console_truetype.c
@@ -3,6 +3,8 @@
* Copyright (c) 2016 Google, Inc
*/
+#define LOG_CATEGORY UCLASS_VIDEO
+
#include <abuf.h>
#include <dm.h>
#include <log.h>
@@ -488,10 +490,12 @@ static int console_truetype_backspace(struct udevice *dev)
static int console_truetype_entry_start(struct udevice *dev)
{
+ struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct console_tt_priv *priv = dev_get_priv(dev);
/* A new input line has start, so clear our history */
priv->pos_ptr = 0;
+ vc_priv->last_ch = 0;
return 0;
}
@@ -733,14 +737,18 @@ static int truetype_select_font(struct udevice *dev, const char *name,
}
static int truetype_measure(struct udevice *dev, const char *name, uint size,
- const char *text, struct vidconsole_bbox *bbox)
+ const char *text, int pixel_limit,
+ struct vidconsole_bbox *bbox, struct alist *lines)
{
struct console_tt_metrics *met;
+ struct vidconsole_mline mline;
+ const char *s, *last_space;
+ int width, last_width;
stbtt_fontinfo *font;
int lsb, advance;
- const char *s;
- int width;
- int last;
+ int start;
+ int limit;
+ int lastch;
int ret;
ret = get_metrics(dev, name, size, &met);
@@ -751,27 +759,85 @@ static int truetype_measure(struct udevice *dev, const char *name, uint size,
if (!*text)
return 0;
+ limit = -1;
+ if (pixel_limit != -1)
+ limit = tt_ceil((double)pixel_limit / met->scale);
+
font = &met->font;
width = 0;
- for (last = 0, s = text; *s; s++) {
+ bbox->y1 = 0;
+ bbox->x1 = 0;
+ start = 0;
+ last_space = NULL;
+ last_width = 0;
+ for (lastch = 0, s = text; *s; s++) {
+ int neww;
int ch = *s;
- /* Used kerning to fine-tune the position of this character */
- if (last)
- width += stbtt_GetCodepointKernAdvance(font, last, ch);
+ if (ch == ' ') {
+ /*
+ * store the position and width so we can use it again
+ * if we need to word-wrap
+ */
+ last_space = s;
+ last_width = width;
+ }
/* First get some basic metrics about this character */
stbtt_GetCodepointHMetrics(font, ch, &advance, &lsb);
+ neww = width + advance;
+
+ /* Use kerning to fine-tune the position of this character */
+ if (lastch)
+ neww += stbtt_GetCodepointKernAdvance(font, lastch, ch);
+ lastch = ch;
+
+ /* see if we need to start a new line */
+ if (ch == '\n' || (limit != -1 && neww >= limit)) {
+ if (ch != '\n' && last_space) {
+ s = last_space;
+ width = last_width;
+ }
+ last_space = NULL;
+ mline.bbox.x0 = 0;
+ mline.bbox.y0 = bbox->y1;
+ mline.bbox.x1 = tt_ceil((double)width * met->scale);
+ bbox->x1 = max(bbox->x1, mline.bbox.x1);
+ bbox->y1 += met->font_size;
+ mline.bbox.y1 = bbox->y1;
+ mline.bbox.valid = true;
+ mline.start = start;
+ mline.len = (s - text) - start;
+ if (lines && !alist_add(lines, mline))
+ return log_msg_ret("ttm", -ENOMEM);
+ log_debug("line x1 %d y0 %d y1 %d start %d len %d text '%.*s'\n",
+ mline.bbox.x1, mline.bbox.y0, mline.bbox.y1,
+ mline.start, mline.len, mline.len, text + mline.start);
+
+ start = s - text;
+ start++;
+ lastch = 0;
+ neww = 0;
+ }
- width += advance;
- last = ch;
+ width = neww;
}
+ /* add the final line */
+ mline.bbox.x0 = 0;
+ mline.bbox.y0 = bbox->y1;
+ mline.bbox.x1 = tt_ceil((double)width * met->scale);
+ bbox->y1 += met->font_size;
+ mline.bbox.y1 = bbox->y1;
+ mline.start = start;
+ mline.len = (s - text) - start;
+ if (lines && !alist_add(lines, mline))
+ return log_msg_ret("ttM", -ENOMEM);
+
bbox->valid = true;
bbox->x0 = 0;
bbox->y0 = 0;
- bbox->x1 = tt_ceil((double)width * met->scale);
- bbox->y1 = met->font_size;
+ bbox->x1 = max(bbox->x1, mline.bbox.x1);
return 0;
}
diff --git a/drivers/video/hitachi-tx10d07vm0baa.c b/drivers/video/hitachi-tx10d07vm0baa.c
new file mode 100644
index 00000000000..95b2f7bfc41
--- /dev/null
+++ b/drivers/video/hitachi-tx10d07vm0baa.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Hitachi TX10D07VM0BAA DSI panel driver
+ *
+ * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <backlight.h>
+#include <dm.h>
+#include <panel.h>
+#include <log.h>
+#include <mipi_dsi.h>
+#include <linux/delay.h>
+#include <power/regulator.h>
+#include <asm/gpio.h>
+
+struct hitachi_tx10d07vm0baa_priv {
+ struct udevice *avci;
+ struct udevice *iovcc;
+
+ struct udevice *backlight;
+
+ struct gpio_desc reset_gpio;
+};
+
+static struct display_timing default_timing = {
+ .pixelclock.typ = 29816000,
+ .hactive.typ = 480,
+ .hfront_porch.typ = 10,
+ .hback_porch.typ = 10,
+ .hsync_len.typ = 10,
+ .vactive.typ = 800,
+ .vfront_porch.typ = 4,
+ .vback_porch.typ = 4,
+ .vsync_len.typ = 4,
+};
+
+#define dsi_generic_write_seq(dsi, cmd, seq...) do { \
+ static const u8 b[] = { cmd, seq }; \
+ int ret; \
+ ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \
+ if (ret < 0) \
+ return ret; \
+ } while (0)
+
+static int hitachi_tx10d07vm0baa_enable_backlight(struct udevice *dev)
+{
+ struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
+ struct mipi_dsi_device *dsi = plat->device;
+ int ret;
+
+ dsi_generic_write_seq(dsi, MIPI_DCS_SET_PARTIAL_AREA, 0x00,
+ 0x00, 0x03, 0x1f);
+ dsi_generic_write_seq(dsi, MIPI_DCS_SET_SCROLL_AREA, 0x00,
+ 0x00, 0x03, 0x20, 0x00, 0x00);
+
+ dsi_generic_write_seq(dsi, MIPI_DCS_SET_ADDRESS_MODE, 0x0a);
+ dsi_generic_write_seq(dsi, MIPI_DCS_SET_SCROLL_START, 0x00,
+ 0x00);
+
+ ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT);
+ if (ret) {
+ log_debug("%s: failed to set pixel format: %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0x00);
+ if (ret) {
+ log_debug("%s: failed to set tear scanline: %d\n", __func__, ret);
+ return ret;
+ }
+
+ dsi_generic_write_seq(dsi, 0x71, 0x00); /* Ex_Vsync_en */
+
+ dsi_generic_write_seq(dsi, 0xb2, 0x00); /* VCSEL */
+ dsi_generic_write_seq(dsi, 0xb4, 0xaa); /* setvgmpm */
+ dsi_generic_write_seq(dsi, 0xb5, 0x33); /* rbias1 */
+ dsi_generic_write_seq(dsi, 0xb6, 0x03); /* rbias2 */
+
+ dsi_generic_write_seq(dsi, 0xb7, 0x1a, 0x33, 0x03, 0x03,
+ 0x03, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x00, 0x04, 0x00, 0x01, 0x01, 0x01); /* set_ddvdhp */
+ dsi_generic_write_seq(dsi, 0xb8, 0x1c, 0x53, 0x03, 0x03,
+ 0x00, 0x01, 0x02, 0x00, 0x00, 0x04,
+ 0x00, 0x01, 0x01); /* set_ddvdhm */
+
+ dsi_generic_write_seq(dsi, 0xb9, 0x0a, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x02, 0x01); /* set_vgh */
+ dsi_generic_write_seq(dsi, 0xba, 0x0f, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x02, 0x01); /* set_vgl */
+ dsi_generic_write_seq(dsi, 0xbb, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x01); /* set_vcl */
+
+ dsi_generic_write_seq(dsi, 0xc1, 0x01); /* number of lines */
+ dsi_generic_write_seq(dsi, 0xc2, 0x08); /* number of fp lines */
+ dsi_generic_write_seq(dsi, 0xc3, 0x04); /* gateset(1) */
+ dsi_generic_write_seq(dsi, 0xc4, 0x4c); /* 1h period */
+ dsi_generic_write_seq(dsi, 0xc5, 0x03); /* source precharge */
+ dsi_generic_write_seq(dsi, 0xc6, 0xc4, 0x04); /* source precharge timing */
+ dsi_generic_write_seq(dsi, 0xc7, 0x00); /* source level */
+ dsi_generic_write_seq(dsi, 0xc8, 0x02); /* number of bp lines */
+ dsi_generic_write_seq(dsi, 0xc9, 0x10); /* gateset(2) */
+ dsi_generic_write_seq(dsi, 0xca, 0x04, 0x04); /* gateset(3) */
+ dsi_generic_write_seq(dsi, 0xcb, 0x03); /* gateset(4) */
+ dsi_generic_write_seq(dsi, 0xcc, 0x12); /* gateset(5) */
+ dsi_generic_write_seq(dsi, 0xcd, 0x12); /* gateset(6) */
+ dsi_generic_write_seq(dsi, 0xce, 0x30); /* gateset(7) */
+ dsi_generic_write_seq(dsi, 0xcf, 0x30); /* gateset(8) */
+ dsi_generic_write_seq(dsi, 0xd0, 0x40); /* gateset(9) */
+ dsi_generic_write_seq(dsi, 0xd1, 0x22); /* flhw */
+ dsi_generic_write_seq(dsi, 0xd2, 0x22); /* vckhw */
+ dsi_generic_write_seq(dsi, 0xd3, 0x04); /* flt */
+ dsi_generic_write_seq(dsi, 0xd4, 0x14); /* tctrl */
+ dsi_generic_write_seq(dsi, 0xd6, 0x02); /* dotinv */
+ dsi_generic_write_seq(dsi, 0xd7, 0x00); /* on/off sequence period */
+
+ dsi_generic_write_seq(dsi, 0xd8, 0x01, 0x05, 0x06, 0x0d,
+ 0x18, 0x09, 0x22, 0x23, 0x00); /* ponseqa */
+ dsi_generic_write_seq(dsi, 0xd9, 0x24, 0x01); /* ponseqb */
+ dsi_generic_write_seq(dsi, 0xde, 0x09, 0x0f, 0x21, 0x12,
+ 0x04); /* ponseqc */
+
+ dsi_generic_write_seq(dsi, 0xdf, 0x02, 0x06, 0x06, 0x06,
+ 0x06, 0x00); /* pofseqa */
+ dsi_generic_write_seq(dsi, 0xe0, 0x01); /* pofseqb */
+
+ ret = mipi_dsi_dcs_set_display_brightness(dsi, 0xff);
+ if (ret) {
+ log_debug("%s: failed to set display brightness: %d\n", __func__, ret);
+ return ret;
+ }
+
+ dsi_generic_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x40);
+
+ dsi_generic_write_seq(dsi, 0xe2, 0x00, 0x00); /* cabc pwm */
+ dsi_generic_write_seq(dsi, 0xe3, 0x03); /* cabc */
+ dsi_generic_write_seq(dsi, 0xe4, 0x66, 0x7b, 0x90, 0xa5,
+ 0xbb, 0xc7, 0xe1, 0xe5); /* cabc brightness */
+ dsi_generic_write_seq(dsi, 0xe5, 0xc5, 0xc5, 0xc9, 0xc9,
+ 0xd1, 0xe1, 0xf1, 0xfe); /* cabc brightness */
+ dsi_generic_write_seq(dsi, 0xe7, 0x2a); /* cabc */
+ dsi_generic_write_seq(dsi, 0xe8, 0x00); /* brt_rev */
+ dsi_generic_write_seq(dsi, 0xe9, 0x00); /* tefreq */
+
+ dsi_generic_write_seq(dsi, 0xea, 0x01); /* high speed ram */
+
+ dsi_generic_write_seq(dsi, 0xeb, 0x00, 0x33, 0x0e, 0x15,
+ 0xb7, 0x78, 0x88, 0x0f); /* gamma setting r pos */
+ dsi_generic_write_seq(dsi, 0xec, 0x00, 0x33, 0x0e, 0x15,
+ 0xb7, 0x78, 0x88, 0x0f); /* gamma setting r neg */
+ dsi_generic_write_seq(dsi, 0xed, 0x00, 0x33, 0x0e, 0x15,
+ 0xb7, 0x78, 0x88, 0x0f); /* gamma setting g pos */
+ dsi_generic_write_seq(dsi, 0xee, 0x00, 0x33, 0x0e, 0x15,
+ 0xb7, 0x78, 0x88, 0x0f); /* gamma setting g neg */
+ dsi_generic_write_seq(dsi, 0xef, 0x00, 0x33, 0x0e, 0x15,
+ 0xb7, 0x78, 0x88, 0x0f); /* gamma setting b pos */
+ dsi_generic_write_seq(dsi, 0xf0, 0x00, 0x33, 0x0e, 0x15,
+ 0xb7, 0x78, 0x88, 0x0f); /* gamma setting b neg */
+
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret) {
+ log_debug("%s: failed to exit sleep mode: %d\n", __func__, ret);
+ return ret;
+ }
+
+ mdelay(110);
+
+ ret = mipi_dsi_dcs_set_display_on(dsi);
+ if (ret) {
+ log_debug("%s: failed to set display on: %d\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int hitachi_tx10d07vm0baa_set_backlight(struct udevice *dev, int percent)
+{
+ struct hitachi_tx10d07vm0baa_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = backlight_enable(priv->backlight);
+ if (ret)
+ return ret;
+
+ return backlight_set_brightness(priv->backlight, percent);
+}
+
+static int hitachi_tx10d07vm0baa_timings(struct udevice *dev,
+ struct display_timing *timing)
+{
+ memcpy(timing, &default_timing, sizeof(*timing));
+ return 0;
+}
+
+static int hitachi_tx10d07vm0baa_of_to_plat(struct udevice *dev)
+{
+ struct hitachi_tx10d07vm0baa_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
+ "backlight", &priv->backlight);
+ if (ret) {
+ log_debug("%s: cannot get backlight: ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = device_get_supply_regulator(dev, "avci-supply", &priv->avci);
+ if (ret) {
+ log_debug("%s: cannot get avci-supply: ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = device_get_supply_regulator(dev, "iovcc-supply", &priv->iovcc);
+ if (ret) {
+ log_debug("%s: cannot get iovcc-supply: ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = gpio_request_by_name(dev, "reset-gpios", 0,
+ &priv->reset_gpio, GPIOD_IS_OUT);
+ if (ret) {
+ log_debug("%s: cannot decode reset-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int hitachi_tx10d07vm0baa_hw_init(struct udevice *dev)
+{
+ struct hitachi_tx10d07vm0baa_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 1);
+ if (ret) {
+ log_debug("%s: error entering reset (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ ret = regulator_set_enable_if_allowed(priv->iovcc, 1);
+ if (ret) {
+ log_debug("%s: enabling iovcc-supply failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regulator_set_enable_if_allowed(priv->avci, 1);
+ if (ret) {
+ log_debug("%s: enabling avci-supply failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ mdelay(25);
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+ if (ret) {
+ log_debug("%s: error exiting reset (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ mdelay(5);
+
+ return 0;
+}
+
+static int hitachi_tx10d07vm0baa_probe(struct udevice *dev)
+{
+ struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
+
+ /* fill characteristics of DSI data link */
+ plat->lanes = 2;
+ plat->format = MIPI_DSI_FMT_RGB888;
+ plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM;
+
+ return hitachi_tx10d07vm0baa_hw_init(dev);
+}
+
+static const struct panel_ops hitachi_tx10d07vm0baa_ops = {
+ .enable_backlight = hitachi_tx10d07vm0baa_enable_backlight,
+ .set_backlight = hitachi_tx10d07vm0baa_set_backlight,
+ .get_display_timing = hitachi_tx10d07vm0baa_timings,
+};
+
+static const struct udevice_id hitachi_tx10d07vm0baa_ids[] = {
+ { .compatible = "hit,tx10d07vm0baa" },
+ { }
+};
+
+U_BOOT_DRIVER(hitachi_tx10d07vm0baa) = {
+ .name = "hitachi_tx10d07vm0baa",
+ .id = UCLASS_PANEL,
+ .of_match = hitachi_tx10d07vm0baa_ids,
+ .ops = &hitachi_tx10d07vm0baa_ops,
+ .of_to_plat = hitachi_tx10d07vm0baa_of_to_plat,
+ .probe = hitachi_tx10d07vm0baa_probe,
+ .plat_auto = sizeof(struct mipi_dsi_panel_plat),
+ .priv_auto = sizeof(struct hitachi_tx10d07vm0baa_priv),
+};
diff --git a/drivers/video/lg-lh400wv3-sd04.c b/drivers/video/lg-lh400wv3-sd04.c
new file mode 100644
index 00000000000..0385b39867f
--- /dev/null
+++ b/drivers/video/lg-lh400wv3-sd04.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * LG LH400WV3-SD04 DSI panel driver
+ *
+ * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <backlight.h>
+#include <dm.h>
+#include <panel.h>
+#include <log.h>
+#include <mipi_dsi.h>
+#include <linux/delay.h>
+#include <power/regulator.h>
+#include <asm/gpio.h>
+
+struct lg_lh400wv3_priv {
+ struct udevice *avci;
+ struct udevice *iovcc;
+
+ struct udevice *backlight;
+
+ struct gpio_desc reset_gpio;
+};
+
+static struct display_timing default_timing = {
+ .pixelclock.typ = 29816000,
+ .hactive.typ = 480,
+ .hfront_porch.typ = 10,
+ .hback_porch.typ = 10,
+ .hsync_len.typ = 10,
+ .vactive.typ = 800,
+ .vfront_porch.typ = 4,
+ .vback_porch.typ = 4,
+ .vsync_len.typ = 4,
+};
+
+#define dsi_generic_write_seq(dsi, cmd, seq...) do { \
+ static const u8 b[] = { cmd, seq }; \
+ int ret; \
+ ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \
+ if (ret < 0) \
+ return ret; \
+ } while (0)
+
+static int lg_lh400wv3_enable_backlight(struct udevice *dev)
+{
+ struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
+ struct mipi_dsi_device *dsi = plat->device;
+ int ret;
+
+ dsi_generic_write_seq(dsi, MIPI_DCS_EXIT_INVERT_MODE);
+ dsi_generic_write_seq(dsi, MIPI_DCS_SET_TEAR_ON);
+
+ ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT |
+ MIPI_DCS_PIXEL_FMT_24BIT << 4);
+ if (ret < 0) {
+ log_debug("%s: failed to set pixel format: %d\n", __func__, ret);
+ return ret;
+ }
+
+ dsi_generic_write_seq(dsi, 0xb2, 0x00, 0xc8);
+ dsi_generic_write_seq(dsi, 0xb3, 0x00);
+ dsi_generic_write_seq(dsi, 0xb4, 0x04);
+ dsi_generic_write_seq(dsi, 0xb5, 0x42, 0x10, 0x10, 0x00, 0x20);
+ dsi_generic_write_seq(dsi, 0xb6, 0x0b, 0x0f, 0x3c, 0x13, 0x13, 0xe8);
+ dsi_generic_write_seq(dsi, 0xb7, 0x4c, 0x06, 0x0c, 0x00, 0x00);
+
+ dsi_generic_write_seq(dsi, 0xc0, 0x01, 0x11);
+ dsi_generic_write_seq(dsi, 0xc3, 0x07, 0x03, 0x04, 0x04, 0x04);
+ dsi_generic_write_seq(dsi, 0xc4, 0x12, 0x24, 0x18, 0x18, 0x02, 0x49);
+ dsi_generic_write_seq(dsi, 0xc5, 0x65);
+ dsi_generic_write_seq(dsi, 0xc6, 0x41, 0x63);
+
+ dsi_generic_write_seq(dsi, 0xd0, 0x00, 0x46, 0x74, 0x32, 0x1d, 0x03, 0x51, 0x15, 0x04);
+ dsi_generic_write_seq(dsi, 0xd1, 0x00, 0x46, 0x74, 0x32, 0x1d, 0x03, 0x51, 0x15, 0x04);
+ dsi_generic_write_seq(dsi, 0xd2, 0x00, 0x46, 0x74, 0x32, 0x1f, 0x03, 0x51, 0x15, 0x04);
+ dsi_generic_write_seq(dsi, 0xd3, 0x00, 0x46, 0x74, 0x32, 0x1f, 0x03, 0x51, 0x15, 0x04);
+ dsi_generic_write_seq(dsi, 0xd4, 0x01, 0x46, 0x74, 0x25, 0x00, 0x03, 0x51, 0x15, 0x04);
+ dsi_generic_write_seq(dsi, 0xd5, 0x01, 0x46, 0x74, 0x25, 0x00, 0x03, 0x51, 0x15, 0x04);
+
+ dsi_generic_write_seq(dsi, MIPI_DCS_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x01, 0xdf);
+ dsi_generic_write_seq(dsi, MIPI_DCS_SET_PAGE_ADDRESS, 0x00, 0x00, 0x03, 0x1f);
+
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret < 0) {
+ log_debug("%s: failed to exit sleep mode: %d\n", __func__, ret);
+ return ret;
+ }
+
+ mdelay(120);
+
+ dsi_generic_write_seq(dsi, MIPI_DCS_WRITE_MEMORY_START);
+
+ ret = mipi_dsi_dcs_set_display_on(dsi);
+ if (ret < 0) {
+ log_debug("%s: failed to set display on: %d\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lg_lh400wv3_set_backlight(struct udevice *dev, int percent)
+{
+ struct lg_lh400wv3_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = backlight_enable(priv->backlight);
+ if (ret)
+ return ret;
+
+ return backlight_set_brightness(priv->backlight, percent);
+}
+
+static int lg_lh400wv3_timings(struct udevice *dev, struct display_timing *timing)
+{
+ memcpy(timing, &default_timing, sizeof(*timing));
+ return 0;
+}
+
+static int lg_lh400wv3_of_to_plat(struct udevice *dev)
+{
+ struct lg_lh400wv3_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
+ "backlight", &priv->backlight);
+ if (ret) {
+ log_debug("%s: cannot get backlight: ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = device_get_supply_regulator(dev, "avci-supply", &priv->avci);
+ if (ret) {
+ log_debug("%s: cannot get avci-supply: ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = device_get_supply_regulator(dev, "iovcc-supply", &priv->iovcc);
+ if (ret) {
+ log_debug("%s: cannot get iovcc-supply: ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = gpio_request_by_name(dev, "reset-gpios", 0,
+ &priv->reset_gpio, GPIOD_IS_OUT);
+ if (ret) {
+ log_debug("%s: cannot decode reset-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lg_lh400wv3_hw_init(struct udevice *dev)
+{
+ struct lg_lh400wv3_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 1);
+ if (ret) {
+ log_debug("%s: error entering reset (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ ret = regulator_set_enable_if_allowed(priv->iovcc, 1);
+ if (ret) {
+ log_debug("%s: enabling iovcc-supply failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regulator_set_enable_if_allowed(priv->avci, 1);
+ if (ret) {
+ log_debug("%s: enabling avci-supply failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ mdelay(1);
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+ if (ret) {
+ log_debug("%s: error exiting reset (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ mdelay(10);
+
+ return 0;
+}
+
+static int lg_lh400wv3_probe(struct udevice *dev)
+{
+ struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
+
+ /* fill characteristics of DSI data link */
+ plat->lanes = 2;
+ plat->format = MIPI_DSI_FMT_RGB888;
+ plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM;
+
+ return lg_lh400wv3_hw_init(dev);
+}
+
+static const struct panel_ops lg_lh400wv3_ops = {
+ .enable_backlight = lg_lh400wv3_enable_backlight,
+ .set_backlight = lg_lh400wv3_set_backlight,
+ .get_display_timing = lg_lh400wv3_timings,
+};
+
+static const struct udevice_id lg_lh400wv3_ids[] = {
+ { .compatible = "lg,lh400wv3-sd04" },
+ { }
+};
+
+U_BOOT_DRIVER(lg_lh400wv3) = {
+ .name = "lg_lh400wv3",
+ .id = UCLASS_PANEL,
+ .of_match = lg_lh400wv3_ids,
+ .ops = &lg_lh400wv3_ops,
+ .of_to_plat = lg_lh400wv3_of_to_plat,
+ .probe = lg_lh400wv3_probe,
+ .plat_auto = sizeof(struct mipi_dsi_panel_plat),
+ .priv_auto = sizeof(struct lg_lh400wv3_priv),
+};
diff --git a/drivers/video/nexell_display.c b/drivers/video/nexell_display.c
index 7bda33fb16e..ea3776258a0 100644
--- a/drivers/video/nexell_display.c
+++ b/drivers/video/nexell_display.c
@@ -10,6 +10,7 @@
#include <config.h>
#include <command.h>
#include <dm.h>
+#include <env.h>
#include <mapmem.h>
#include <malloc.h>
#include <linux/compat.h>
diff --git a/drivers/video/novatek-nt35510.c b/drivers/video/novatek-nt35510.c
new file mode 100644
index 00000000000..f3432939c0c
--- /dev/null
+++ b/drivers/video/novatek-nt35510.c
@@ -0,0 +1,1253 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Amarula Solutions, Dario Binacchi <dario.binacchi@amarulasolutions.com>
+ *
+ * Inspired from the Linux kernel driver panel-novatek-nt35510.c
+ */
+
+#include <backlight.h>
+#include <dm.h>
+#include <mipi_dsi.h>
+#include <panel.h>
+#include <asm/gpio.h>
+#include <dm/device_compat.h>
+#include <linux/delay.h>
+#include <power/regulator.h>
+
+#define NT35510_CMD_CORRECT_GAMMA BIT(0)
+#define NT35510_CMD_CONTROL_DISPLAY BIT(1)
+#define NT35510_CMD_SETVCMOFF BIT(2)
+
+#define MCS_CMD_MAUCCTR 0xF0 /* Manufacturer command enable */
+#define MCS_CMD_READ_ID1 0xDA
+#define MCS_CMD_READ_ID2 0xDB
+#define MCS_CMD_READ_ID3 0xDC
+#define MCS_CMD_MTP_READ_SETTING 0xF8 /* Uncertain about name */
+#define MCS_CMD_MTP_READ_PARAM 0xFF /* Uncertain about name */
+
+/*
+ * These manufacturer commands are available after we enable manufacturer
+ * command set (MCS) for page 0.
+ */
+#define NT35510_P0_DOPCTR 0xB1
+#define NT35510_P0_SDHDTCTR 0xB6
+#define NT35510_P0_GSEQCTR 0xB7
+#define NT35510_P0_SDEQCTR 0xB8
+#define NT35510_P0_SDVPCTR 0xBA
+#define NT35510_P0_DPFRCTR1 0xBD
+#define NT35510_P0_DPFRCTR2 0xBE
+#define NT35510_P0_DPFRCTR3 0xBF
+#define NT35510_P0_DPMCTR12 0xCC
+
+#define NT35510_P0_DOPCTR_LEN 2
+#define NT35510_P0_GSEQCTR_LEN 2
+#define NT35510_P0_SDEQCTR_LEN 4
+#define NT35510_P0_SDVPCTR_LEN 1
+#define NT35510_P0_DPFRCTR1_LEN 5
+#define NT35510_P0_DPFRCTR2_LEN 5
+#define NT35510_P0_DPFRCTR3_LEN 5
+#define NT35510_P0_DPMCTR12_LEN 3
+
+#define NT35510_DOPCTR_0_RAMKP BIT(7) /* Contents kept in sleep */
+#define NT35510_DOPCTR_0_DSITE BIT(6) /* Enable TE signal */
+#define NT35510_DOPCTR_0_DSIG BIT(5) /* Enable generic read/write */
+#define NT35510_DOPCTR_0_DSIM BIT(4) /* Enable video mode on DSI */
+#define NT35510_DOPCTR_0_EOTP BIT(3) /* Support EoTP */
+#define NT35510_DOPCTR_0_N565 BIT(2) /* RGB or BGR pixel format */
+#define NT35510_DOPCTR_1_TW_PWR_SEL BIT(4) /* TE power selector */
+#define NT35510_DOPCTR_1_CRGB BIT(3) /* RGB or BGR byte order */
+#define NT35510_DOPCTR_1_CTB BIT(2) /* Vertical scanning direction */
+#define NT35510_DOPCTR_1_CRL BIT(1) /* Source driver data shift */
+#define NT35510_P0_SDVPCTR_PRG BIT(2) /* 0 = normal operation, 1 = VGLO */
+#define NT35510_P0_SDVPCTR_AVDD 0 /* source driver output = AVDD */
+#define NT35510_P0_SDVPCTR_OFFCOL 1 /* source driver output = off color */
+#define NT35510_P0_SDVPCTR_AVSS 2 /* source driver output = AVSS */
+#define NT35510_P0_SDVPCTR_HI_Z 3 /* source driver output = High impedance */
+
+/*
+ * These manufacturer commands are available after we enable manufacturer
+ * command set (MCS) for page 1.
+ */
+#define NT35510_P1_SETAVDD 0xB0
+#define NT35510_P1_SETAVEE 0xB1
+#define NT35510_P1_SETVCL 0xB2
+#define NT35510_P1_SETVGH 0xB3
+#define NT35510_P1_SETVRGH 0xB4
+#define NT35510_P1_SETVGL 0xB5
+#define NT35510_P1_BT1CTR 0xB6
+#define NT35510_P1_BT2CTR 0xB7
+#define NT35510_P1_BT3CTR 0xB8
+#define NT35510_P1_BT4CTR 0xB9 /* VGH boosting times/freq */
+#define NT35510_P1_BT5CTR 0xBA
+#define NT35510_P1_PFMCTR 0xBB
+#define NT35510_P1_SETVGP 0xBC
+#define NT35510_P1_SETVGN 0xBD
+#define NT35510_P1_SETVCMOFF 0xBE
+#define NT35510_P1_VGHCTR 0xBF /* VGH output ctrl */
+#define NT35510_P1_SET_GAMMA_RED_POS 0xD1
+#define NT35510_P1_SET_GAMMA_GREEN_POS 0xD2
+#define NT35510_P1_SET_GAMMA_BLUE_POS 0xD3
+#define NT35510_P1_SET_GAMMA_RED_NEG 0xD4
+#define NT35510_P1_SET_GAMMA_GREEN_NEG 0xD5
+#define NT35510_P1_SET_GAMMA_BLUE_NEG 0xD6
+
+/* AVDD and AVEE setting 3 bytes */
+#define NT35510_P1_AVDD_LEN 3
+#define NT35510_P1_AVEE_LEN 3
+#define NT35510_P1_VCL_LEN 3
+#define NT35510_P1_VGH_LEN 3
+#define NT35510_P1_VGL_LEN 3
+#define NT35510_P1_VGP_LEN 3
+#define NT35510_P1_VGN_LEN 3
+#define NT35510_P1_VCMOFF_LEN 2
+/* BT1CTR thru BT5CTR setting 3 bytes */
+#define NT35510_P1_BT1CTR_LEN 3
+#define NT35510_P1_BT2CTR_LEN 3
+#define NT35510_P1_BT3CTR_LEN 3
+#define NT35510_P1_BT4CTR_LEN 3
+#define NT35510_P1_BT5CTR_LEN 3
+/* 52 gamma parameters times two per color: positive and negative */
+#define NT35510_P1_GAMMA_LEN 52
+
+#define NT35510_WRCTRLD_BCTRL BIT(5)
+#define NT35510_WRCTRLD_A BIT(4)
+#define NT35510_WRCTRLD_DD BIT(3)
+#define NT35510_WRCTRLD_BL BIT(2)
+#define NT35510_WRCTRLD_DB BIT(1)
+#define NT35510_WRCTRLD_G BIT(0)
+
+#define NT35510_WRCABC_OFF 0
+#define NT35510_WRCABC_UI_MODE 1
+#define NT35510_WRCABC_STILL_MODE 2
+#define NT35510_WRCABC_MOVING_MODE 3
+
+/**
+ * struct nt35510_config - the display-specific NT35510 configuration
+ *
+ * Some of the settings provide an array of bytes, A, B C which mean:
+ * A = normal / idle off mode
+ * B = idle on mode
+ * C = partial / idle off mode
+ *
+ * Gamma correction arrays are 10bit numbers, two consecutive bytes
+ * makes out one point on the gamma correction curve. The points are
+ * not linearly placed along the X axis, we get points 0, 1, 3, 5
+ * 7, 11, 15, 23, 31, 47, 63, 95, 127, 128, 160, 192, 208, 224, 232,
+ * 240, 244, 248, 250, 252, 254, 255. The voltages tuples form
+ * V0, V1, V3 ... V255, with 0x0000 being the lowest voltage and
+ * 0x03FF being the highest voltage.
+ *
+ * Each value must be strictly higher than the previous value forming
+ * a rising curve like this:
+ *
+ * ^
+ * | V255
+ * | V254
+ * | ....
+ * | V5
+ * | V3
+ * | V1
+ * | V0
+ * +------------------------------------------->
+ *
+ * The details about all settings can be found in the NT35510 Application
+ * Note.
+ */
+struct nt35510_config {
+ /**
+ * @width_mm: physical panel width [mm]
+ */
+ u32 width_mm;
+ /**
+ * @height_mm: physical panel height [mm]
+ */
+ u32 height_mm;
+ /**
+ * @timings: display timings
+ */
+ struct display_timing timings;
+ /**
+ * @mode_flags: DSI operation mode related flags
+ */
+ unsigned long mode_flags;
+ /**
+ * @cmds: enable DSI commands
+ */
+ u32 cmds;
+ /**
+ * @avdd: setting for AVDD ranging from 0x00 = 6.5V to 0x14 = 4.5V
+ * in 0.1V steps the default is 0x05 which means 6.0V
+ */
+ u8 avdd[NT35510_P1_AVDD_LEN];
+ /**
+ * @bt1ctr: setting for boost power control for the AVDD step-up
+ * circuit (1)
+ * bits 0..2 in the lower nibble controls PCK, the booster clock
+ * frequency for the step-up circuit:
+ * 0 = Hsync/32
+ * 1 = Hsync/16
+ * 2 = Hsync/8
+ * 3 = Hsync/4
+ * 4 = Hsync/2
+ * 5 = Hsync
+ * 6 = Hsync x 2
+ * 7 = Hsync x 4
+ * bits 4..6 in the upper nibble controls BTP, the boosting
+ * amplification for the step-up circuit:
+ * 0 = Disable
+ * 1 = 1.5 x VDDB
+ * 2 = 1.66 x VDDB
+ * 3 = 2 x VDDB
+ * 4 = 2.5 x VDDB
+ * 5 = 3 x VDDB
+ * The defaults are 4 and 4 yielding 0x44
+ */
+ u8 bt1ctr[NT35510_P1_BT1CTR_LEN];
+ /**
+ * @avee: setting for AVEE ranging from 0x00 = -6.5V to 0x14 = -4.5V
+ * in 0.1V steps the default is 0x05 which means -6.0V
+ */
+ u8 avee[NT35510_P1_AVEE_LEN];
+ /**
+ * @bt2ctr: setting for boost power control for the AVEE step-up
+ * circuit (2)
+ * bits 0..2 in the lower nibble controls NCK, the booster clock
+ * frequency, the values are the same as for PCK in @bt1ctr.
+ * bits 4..5 in the upper nibble controls BTN, the boosting
+ * amplification for the step-up circuit.
+ * 0 = Disable
+ * 1 = -1.5 x VDDB
+ * 2 = -2 x VDDB
+ * 3 = -2.5 x VDDB
+ * 4 = -3 x VDDB
+ * The defaults are 4 and 3 yielding 0x34
+ */
+ u8 bt2ctr[NT35510_P1_BT2CTR_LEN];
+ /**
+ * @vcl: setting for VCL ranging from 0x00 = -2.5V to 0x11 = -4.0V
+ * in 1V steps, the default is 0x00 which means -2.5V
+ */
+ u8 vcl[NT35510_P1_VCL_LEN];
+ /**
+ * @bt3ctr: setting for boost power control for the VCL step-up
+ * circuit (3)
+ * bits 0..2 in the lower nibble controls CLCK, the booster clock
+ * frequency, the values are the same as for PCK in @bt1ctr.
+ * bits 4..5 in the upper nibble controls BTCL, the boosting
+ * amplification for the step-up circuit.
+ * 0 = Disable
+ * 1 = -0.5 x VDDB
+ * 2 = -1 x VDDB
+ * 3 = -2 x VDDB
+ * The defaults are 4 and 2 yielding 0x24
+ */
+ u8 bt3ctr[NT35510_P1_BT3CTR_LEN];
+ /**
+ * @vgh: setting for VGH ranging from 0x00 = 7.0V to 0x0B = 18.0V
+ * in 1V steps, the default is 0x08 which means 15V
+ */
+ u8 vgh[NT35510_P1_VGH_LEN];
+ /**
+ * @bt4ctr: setting for boost power control for the VGH step-up
+ * circuit (4)
+ * bits 0..2 in the lower nibble controls HCK, the booster clock
+ * frequency, the values are the same as for PCK in @bt1ctr.
+ * bits 4..5 in the upper nibble controls BTH, the boosting
+ * amplification for the step-up circuit.
+ * 0 = AVDD + VDDB
+ * 1 = AVDD - AVEE
+ * 2 = AVDD - AVEE + VDDB
+ * 3 = AVDD x 2 - AVEE
+ * The defaults are 4 and 3 yielding 0x34
+ */
+ u8 bt4ctr[NT35510_P1_BT4CTR_LEN];
+ /**
+ * @vgl: setting for VGL ranging from 0x00 = -2V to 0x0f = -15V in
+ * 1V steps, the default is 0x08 which means -10V
+ */
+ u8 vgl[NT35510_P1_VGL_LEN];
+ /**
+ * @bt5ctr: setting for boost power control for the VGL step-up
+ * circuit (5)
+ * bits 0..2 in the lower nibble controls LCK, the booster clock
+ * frequency, the values are the same as for PCK in @bt1ctr.
+ * bits 4..5 in the upper nibble controls BTL, the boosting
+ * amplification for the step-up circuit.
+ * 0 = AVEE + VCL
+ * 1 = AVEE - AVDD
+ * 2 = AVEE + VCL - AVDD
+ * 3 = AVEE x 2 - AVDD
+ * The defaults are 3 and 2 yielding 0x32
+ */
+ u8 bt5ctr[NT35510_P1_BT5CTR_LEN];
+ /**
+ * @vgp: setting for VGP, the positive gamma divider voltages
+ * VGMP the high voltage and VGSP the low voltage.
+ * The first byte contains bit 8 of VGMP and VGSP in bits 4 and 0
+ * The second byte contains bit 0..7 of VGMP
+ * The third byte contains bit 0..7 of VGSP
+ * VGMP 0x00 = 3.0V .. 0x108 = 6.3V in steps of 12.5mV
+ * VGSP 0x00 = 0V .. 0x111 = 3.7V in steps of 12.5mV
+ */
+ u8 vgp[NT35510_P1_VGP_LEN];
+ /**
+ * @vgn: setting for VGN, the negative gamma divider voltages,
+ * same layout of bytes as @vgp.
+ */
+ u8 vgn[NT35510_P1_VGN_LEN];
+ /**
+ * @vcmoff: setting the DC VCOM offset voltage
+ * The first byte contains bit 8 of VCM in bit 0 and VCMOFFSEL in bit 4.
+ * The second byte contains bits 0..7 of VCM.
+ * VCMOFFSEL the common voltage offset mode.
+ * VCMOFFSEL 0x00 = VCOM .. 0x01 Gamma.
+ * The default is 0x00.
+ * VCM the VCOM output voltage (VCMOFFSEL = 0) or the internal register
+ * offset for gamma voltage (VCMOFFSEL = 1).
+ * VCM 0x00 = 0V/0 .. 0x118 = 3.5V/280 in steps of 12.5mV/1step
+ * The default is 0x00 = 0V/0.
+ */
+ u8 vcmoff[NT35510_P1_VCMOFF_LEN];
+ /**
+ * @dopctr: setting optional control for display
+ * ERR bits 0..1 in the first byte is the ERR pin output signal setting.
+ * 0 = Disable, ERR pin output low
+ * 1 = ERR pin output CRC error only
+ * 2 = ERR pin output ECC error only
+ * 3 = ERR pin output CRC and ECC error
+ * The default is 0.
+ * N565 bit 2 in the first byte is the 16-bit/pixel format selection.
+ * 0 = R[4:0] + G[5:3] & G[2:0] + B[4:0]
+ * 1 = G[2:0] + R[4:0] & B[4:0] + G[5:3]
+ * The default is 0.
+ * DIS_EoTP_HS bit 3 in the first byte is "DSI protocol violation" error
+ * reporting.
+ * 0 = reporting when error
+ * 1 = not reporting when error
+ * DSIM bit 4 in the first byte is the video mode data type enable
+ * 0 = Video mode data type disable
+ * 1 = Video mode data type enable
+ * The default is 0.
+ * DSIG bit 5 int the first byte is the generic r/w data type enable
+ * 0 = Generic r/w disable
+ * 1 = Generic r/w enable
+ * The default is 0.
+ * DSITE bit 6 in the first byte is TE line enable
+ * 0 = TE line is disabled
+ * 1 = TE line is enabled
+ * The default is 0.
+ * RAMKP bit 7 in the first byte is the frame memory keep/loss in
+ * sleep-in mode
+ * 0 = contents loss in sleep-in
+ * 1 = contents keep in sleep-in
+ * The default is 0.
+ * CRL bit 1 in the second byte is the source driver data shift
+ * direction selection. This bit is XOR operation with bit RSMX
+ * of 3600h command.
+ * 0 (RMSX = 0) = S1 -> S1440
+ * 0 (RMSX = 1) = S1440 -> S1
+ * 1 (RMSX = 0) = S1440 -> S1
+ * 1 (RMSX = 1) = S1 -> S1440
+ * The default is 0.
+ * CTB bit 2 in the second byte is the vertical scanning direction
+ * selection for gate control signals. This bit is XOR operation
+ * with bit ML of 3600h command.
+ * 0 (ML = 0) = Forward (top -> bottom)
+ * 0 (ML = 1) = Reverse (bottom -> top)
+ * 1 (ML = 0) = Reverse (bottom -> top)
+ * 1 (ML = 1) = Forward (top -> bottom)
+ * The default is 0.
+ * CRGB bit 3 in the second byte is RGB-BGR order selection. This
+ * bit is XOR operation with bit RGB of 3600h command.
+ * 0 (RGB = 0) = RGB/Normal
+ * 0 (RGB = 1) = BGR/RB swap
+ * 1 (RGB = 0) = BGR/RB swap
+ * 1 (RGB = 1) = RGB/Normal
+ * The default is 0.
+ * TE_PWR_SEL bit 4 in the second byte is the TE output voltage
+ * level selection (only valid when DSTB_SEL = 0 or DSTB_SEL = 1,
+ * VSEL = High and VDDI = 1.665~3.3V).
+ * 0 = TE output voltage level is VDDI
+ * 1 = TE output voltage level is VDDA
+ * The default is 0.
+ */
+ u8 dopctr[NT35510_P0_DOPCTR_LEN];
+ /**
+ * @madctl: Memory data access control
+ * RSMY bit 0 is flip vertical. Flips the display image top to down.
+ * RSMX bit 1 is flip horizontal. Flips the display image left to right.
+ * MH bit 2 is the horizontal refresh order.
+ * RGB bit 3 is the RGB-BGR order.
+ * 0 = RGB color sequence
+ * 1 = BGR color sequence
+ * ML bit 4 is the vertical refresh order.
+ * MV bit 5 is the row/column exchange.
+ * MX bit 6 is the column address order.
+ * MY bit 7 is the row address order.
+ */
+ u8 madctl;
+ /**
+ * @sdhdtctr: source output data hold time
+ * 0x00..0x3F = 0..31.5us in steps of 0.5us
+ * The default is 0x05 = 2.5us.
+ */
+ u8 sdhdtctr;
+ /**
+ * @gseqctr: EQ control for gate signals
+ * GFEQ_XX[3:0]: time setting of EQ step for falling edge in steps
+ * of 0.5us.
+ * The default is 0x07 = 3.5us
+ * GREQ_XX[7:4]: time setting of EQ step for rising edge in steps
+ * of 0.5us.
+ * The default is 0x07 = 3.5us
+ */
+ u8 gseqctr[NT35510_P0_GSEQCTR_LEN];
+ /**
+ * @sdeqctr: Source driver control settings, first byte is
+ * 0 for mode 1 and 1 for mode 2. Mode 1 uses two steps and
+ * mode 2 uses three steps meaning EQS3 is not used in mode
+ * 1. Mode 2 is default. The last three parameters are EQS1, EQS2
+ * and EQS3, setting the rise time for each equalizer step:
+ * 0x00 = 0.0 us to 0x0f = 7.5 us in steps of 0.5us. The default
+ * is 0x07 = 3.5 us.
+ */
+ u8 sdeqctr[NT35510_P0_SDEQCTR_LEN];
+ /**
+ * @sdvpctr: power/voltage behaviour during vertical porch time
+ */
+ u8 sdvpctr;
+ /**
+ * @t1: the number of pixel clocks on one scanline, range
+ * 0x100 (258 ticks) .. 0x3FF (1024 ticks) so the value + 1
+ * clock ticks.
+ */
+ u16 t1;
+ /**
+ * @vbp: vertical back porch toward the PANEL note: not toward
+ * the DSI host; these are separate interfaces, in from DSI host
+ * and out to the panel.
+ */
+ u8 vbp;
+ /**
+ * @vfp: vertical front porch toward the PANEL.
+ */
+ u8 vfp;
+ /**
+ * @psel: pixel clock divisor: 0 = 1, 1 = 2, 2 = 4, 3 = 8.
+ */
+ u8 psel;
+ /**
+ * @dpmctr12: Display timing control 12
+ * Byte 1 bit 4 selects LVGL voltage level: 0 = VGLX, 1 = VGL_REG
+ * Byte 1 bit 1 selects gate signal mode: 0 = non-overlap, 1 = overlap
+ * Byte 1 bit 0 selects output signal control R/L swap, 0 = normal
+ * 1 = swap all O->E, L->R
+ * Byte 2 is CLW delay clock for CK O/E and CKB O/E signals:
+ * 0x00 = 0us .. 0xFF = 12.75us in 0.05us steps
+ * Byte 3 is FTI_H0 delay time for STP O/E signals:
+ * 0x00 = 0us .. 0xFF = 12.75us in 0.05us steps
+ */
+ u8 dpmctr12[NT35510_P0_DPMCTR12_LEN];
+ /**
+ * @gamma_corr_pos_r: Red gamma correction parameters, positive
+ */
+ u8 gamma_corr_pos_r[NT35510_P1_GAMMA_LEN];
+ /**
+ * @gamma_corr_pos_g: Green gamma correction parameters, positive
+ */
+ u8 gamma_corr_pos_g[NT35510_P1_GAMMA_LEN];
+ /**
+ * @gamma_corr_pos_b: Blue gamma correction parameters, positive
+ */
+ u8 gamma_corr_pos_b[NT35510_P1_GAMMA_LEN];
+ /**
+ * @gamma_corr_neg_r: Red gamma correction parameters, negative
+ */
+ u8 gamma_corr_neg_r[NT35510_P1_GAMMA_LEN];
+ /**
+ * @gamma_corr_neg_g: Green gamma correction parameters, negative
+ */
+ u8 gamma_corr_neg_g[NT35510_P1_GAMMA_LEN];
+ /**
+ * @gamma_corr_neg_b: Blue gamma correction parameters, negative
+ */
+ u8 gamma_corr_neg_b[NT35510_P1_GAMMA_LEN];
+ /**
+ * @wrdisbv: write display brightness
+ * 0x00 value means the lowest brightness and 0xff value means
+ * the highest brightness.
+ * The default is 0x00.
+ */
+ u8 wrdisbv;
+ /**
+ * @wrctrld: write control display
+ * G bit 0 selects gamma curve: 0 = Manual, 1 = Automatic
+ * DB bit 1 selects display brightness: 0 = Manual, 1 = Automatic
+ * BL bit 2 controls backlight control: 0 = Off, 1 = On
+ * DD bit 3 controls display dimming: 0 = Off, 1 = On
+ * A bit 4 controls LABC block: 0 = Off, 1 = On
+ * BCTRL bit 5 controls brightness block: 0 = Off, 1 = On
+ */
+ u8 wrctrld;
+ /**
+ * @wrcabc: write content adaptive brightness control
+ * There is possible to use 4 different modes for content adaptive
+ * image functionality:
+ * 0: Off
+ * 1: User Interface Image (UI-Mode)
+ * 2: Still Picture Image (Still-Mode)
+ * 3: Moving Picture Image (Moving-Mode)
+ * The default is 0
+ */
+ u8 wrcabc;
+ /**
+ * @wrcabcmb: write CABC minimum brightness
+ * Set the minimum brightness value of the display for CABC
+ * function.
+ * 0x00 value means the lowest brightness for CABC and 0xff
+ * value means the highest brightness for CABC.
+ * The default is 0x00.
+ */
+ u8 wrcabcmb;
+};
+
+/**
+ * struct nt35510 - state container for the NT35510 panel
+ */
+struct nt35510_panel_priv {
+ const struct nt35510_config *conf;
+ struct udevice *vdd_reg;
+ struct udevice *vddi_reg;
+ struct gpio_desc *reset;
+};
+
+/* Manufacturer command has strictly this byte sequence */
+static const u8 nt35510_mauc_mtp_read_param[] = { 0xAA, 0x55, 0x25, 0x01 };
+static const u8 nt35510_mauc_mtp_read_setting[] = { 0x01, 0x02, 0x00, 0x20,
+ 0x33, 0x13, 0x00, 0x40,
+ 0x00, 0x00, 0x23, 0x02 };
+static const u8 nt35510_mauc_select_page_0[] = { 0x55, 0xAA, 0x52, 0x08, 0x00 };
+static const u8 nt35510_mauc_select_page_1[] = { 0x55, 0xAA, 0x52, 0x08, 0x01 };
+static const u8 nt35510_vgh_on[] = { 0x01 };
+
+#define NT35510_ROTATE_0_SETTING 0x02
+#define NT35510_ROTATE_180_SETTING 0x00
+
+static inline struct mipi_dsi_device *to_mipi_dsi_device(struct udevice *dev)
+{
+ struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
+
+ return plat->device;
+}
+
+static inline const struct nt35510_config *to_panel_config(struct udevice *dev)
+{
+ struct nt35510_panel_priv *priv = dev_get_priv(dev);
+
+ return priv->conf;
+}
+
+static int nt35510_send_long(struct udevice *dev, u8 cmd, u8 cmdlen,
+ const u8 *seq)
+{
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
+ const u8 *seqp = seq;
+ int cmdwritten = 0;
+ int chunk = cmdlen;
+ int ret;
+
+ if (chunk > 15)
+ chunk = 15;
+ ret = mipi_dsi_dcs_write(dsi, cmd, seqp, chunk);
+ if (ret < 0) {
+ dev_err(dev, "error sending DCS command seq cmd %02x\n", cmd);
+ return ret;
+ }
+ cmdwritten += chunk;
+ seqp += chunk;
+
+ while (cmdwritten < cmdlen) {
+ chunk = cmdlen - cmdwritten;
+ if (chunk > 15)
+ chunk = 15;
+ ret = mipi_dsi_generic_write(dsi, seqp, chunk);
+ if (ret < 0) {
+ dev_err(dev, "error sending generic write seq %02x\n", cmd);
+ return ret;
+ }
+ cmdwritten += chunk;
+ seqp += chunk;
+ }
+ dev_dbg(dev, "sent command %02x %02x bytes\n", cmd, cmdlen);
+ return 0;
+}
+
+static int nt35510_setup_power(struct udevice *dev)
+{
+ const struct nt35510_config *conf = to_panel_config(dev);
+ int ret;
+
+ ret = nt35510_send_long(dev, NT35510_P1_SETAVDD,
+ NT35510_P1_AVDD_LEN,
+ conf->avdd);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P1_BT1CTR,
+ NT35510_P1_BT1CTR_LEN,
+ conf->bt1ctr);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P1_SETAVEE,
+ NT35510_P1_AVEE_LEN,
+ conf->avee);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P1_BT2CTR,
+ NT35510_P1_BT2CTR_LEN,
+ conf->bt2ctr);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P1_SETVCL,
+ NT35510_P1_VCL_LEN,
+ conf->vcl);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P1_BT3CTR,
+ NT35510_P1_BT3CTR_LEN,
+ conf->bt3ctr);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P1_SETVGH,
+ NT35510_P1_VGH_LEN,
+ conf->vgh);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P1_BT4CTR,
+ NT35510_P1_BT4CTR_LEN,
+ conf->bt4ctr);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P1_VGHCTR,
+ ARRAY_SIZE(nt35510_vgh_on),
+ nt35510_vgh_on);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P1_SETVGL,
+ NT35510_P1_VGL_LEN,
+ conf->vgl);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P1_BT5CTR,
+ NT35510_P1_BT5CTR_LEN,
+ conf->bt5ctr);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P1_SETVGP,
+ NT35510_P1_VGP_LEN,
+ conf->vgp);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P1_SETVGN,
+ NT35510_P1_VGN_LEN,
+ conf->vgn);
+ if (ret)
+ return ret;
+
+ if (conf->cmds & NT35510_CMD_SETVCMOFF) {
+ ret = nt35510_send_long(dev, NT35510_P1_SETVCMOFF,
+ NT35510_P1_VCMOFF_LEN,
+ conf->vcmoff);
+ if (ret)
+ return ret;
+ }
+
+ /* Typically 10 ms */
+ mdelay(10);
+
+ return 0;
+}
+
+static int nt35510_setup_display(struct udevice *dev)
+{
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
+ const struct nt35510_config *conf = to_panel_config(dev);
+ u8 dpfrctr[NT35510_P0_DPFRCTR1_LEN];
+ int ret;
+
+ ret = nt35510_send_long(dev, NT35510_P0_DOPCTR,
+ NT35510_P0_DOPCTR_LEN,
+ conf->dopctr);
+ if (ret)
+ return ret;
+
+ ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_ADDRESS_MODE, &conf->madctl,
+ sizeof(conf->madctl));
+ if (ret < 0)
+ return ret;
+
+ ret = mipi_dsi_dcs_write(dsi, NT35510_P0_SDHDTCTR, &conf->sdhdtctr,
+ sizeof(conf->sdhdtctr));
+ if (ret < 0)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P0_GSEQCTR,
+ NT35510_P0_GSEQCTR_LEN,
+ conf->gseqctr);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P0_SDEQCTR,
+ NT35510_P0_SDEQCTR_LEN,
+ conf->sdeqctr);
+ if (ret)
+ return ret;
+
+ ret = mipi_dsi_dcs_write(dsi, NT35510_P0_SDVPCTR,
+ &conf->sdvpctr, 1);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Display timing control for active and idle off mode:
+ * the first byte contains
+ * the two high bits of T1A and second byte the low 8 bits, and
+ * the valid range is 0x100 (257) to 0x3ff (1023) representing
+ * 258..1024 (+1) pixel clock ticks for one scanline. At 20MHz pixel
+ * clock this covers the range of 12.90us .. 51.20us in steps of
+ * 0.05us, the default is 0x184 (388) representing 389 ticks.
+ * The third byte is VBPDA, vertical back porch display active
+ * and the fourth VFPDA, vertical front porch display active,
+ * both given in number of scanlines in the range 0x02..0xff
+ * for 2..255 scanlines. The fifth byte is 2 bits selecting
+ * PSEL for active and idle off mode, how much the 20MHz clock
+ * is divided by 0..3. This needs to be adjusted to get the right
+ * frame rate.
+ */
+ dpfrctr[0] = (conf->t1 >> 8) & 0xFF;
+ dpfrctr[1] = conf->t1 & 0xFF;
+ /* Vertical back porch */
+ dpfrctr[2] = conf->vbp;
+ /* Vertical front porch */
+ dpfrctr[3] = conf->vfp;
+ dpfrctr[4] = conf->psel;
+ ret = nt35510_send_long(dev, NT35510_P0_DPFRCTR1,
+ NT35510_P0_DPFRCTR1_LEN,
+ dpfrctr);
+ if (ret)
+ return ret;
+ /* For idle and partial idle off mode we decrease front porch by one */
+ dpfrctr[3]--;
+ ret = nt35510_send_long(dev, NT35510_P0_DPFRCTR2,
+ NT35510_P0_DPFRCTR2_LEN,
+ dpfrctr);
+ if (ret)
+ return ret;
+ ret = nt35510_send_long(dev, NT35510_P0_DPFRCTR3,
+ NT35510_P0_DPFRCTR3_LEN,
+ dpfrctr);
+ if (ret)
+ return ret;
+
+ /* Enable TE on vblank */
+ ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+ if (ret)
+ return ret;
+
+ /* Turn on the pads? */
+ ret = nt35510_send_long(dev, NT35510_P0_DPMCTR12,
+ NT35510_P0_DPMCTR12_LEN,
+ conf->dpmctr12);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int nt35510_read_id(struct udevice *dev)
+{
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
+ u8 id1, id2, id3;
+ int ret;
+
+ ret = mipi_dsi_dcs_read(dsi, MCS_CMD_READ_ID1, &id1, 1);
+ if (ret < 0) {
+ dev_err(dev, "could not read MTP ID1\n");
+ return ret;
+ }
+
+ ret = mipi_dsi_dcs_read(dsi, MCS_CMD_READ_ID2, &id2, 1);
+ if (ret < 0) {
+ dev_err(dev, "could not read MTP ID2\n");
+ return ret;
+ }
+
+ ret = mipi_dsi_dcs_read(dsi, MCS_CMD_READ_ID3, &id3, 1);
+ if (ret < 0) {
+ dev_err(dev, "could not read MTP ID3\n");
+ return ret;
+ }
+
+ /*
+ * Multi-Time Programmable (?) memory contains manufacturer
+ * ID (e.g. Hydis 0x55), driver ID (e.g. NT35510 0xc0) and
+ * version.
+ */
+ dev_info(dev, "MTP ID manufacturer: %02x version: %02x driver: %02x\n",
+ id1, id2, id3);
+
+ return 0;
+}
+
+static int nt35510_init_sequence(struct udevice *dev)
+{
+ const struct nt35510_config *conf = to_panel_config(dev);
+ int ret;
+
+ ret = nt35510_send_long(dev, MCS_CMD_MTP_READ_PARAM,
+ ARRAY_SIZE(nt35510_mauc_mtp_read_param),
+ nt35510_mauc_mtp_read_param);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, MCS_CMD_MTP_READ_SETTING,
+ ARRAY_SIZE(nt35510_mauc_mtp_read_setting),
+ nt35510_mauc_mtp_read_setting);
+ if (ret)
+ return ret;
+
+ nt35510_read_id(dev);
+
+ /* Set up stuff in manufacturer control, page 1 */
+ ret = nt35510_send_long(dev, MCS_CMD_MAUCCTR,
+ ARRAY_SIZE(nt35510_mauc_select_page_1),
+ nt35510_mauc_select_page_1);
+ if (ret)
+ return ret;
+
+ ret = nt35510_setup_power(dev);
+ if (ret)
+ return ret;
+
+ if (conf->cmds & NT35510_CMD_CORRECT_GAMMA) {
+ ret = nt35510_send_long(dev, NT35510_P1_SET_GAMMA_RED_POS,
+ NT35510_P1_GAMMA_LEN,
+ conf->gamma_corr_pos_r);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P1_SET_GAMMA_GREEN_POS,
+ NT35510_P1_GAMMA_LEN,
+ conf->gamma_corr_pos_g);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P1_SET_GAMMA_BLUE_POS,
+ NT35510_P1_GAMMA_LEN,
+ conf->gamma_corr_pos_b);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P1_SET_GAMMA_RED_NEG,
+ NT35510_P1_GAMMA_LEN,
+ conf->gamma_corr_neg_r);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P1_SET_GAMMA_GREEN_NEG,
+ NT35510_P1_GAMMA_LEN,
+ conf->gamma_corr_neg_g);
+ if (ret)
+ return ret;
+
+ ret = nt35510_send_long(dev, NT35510_P1_SET_GAMMA_BLUE_NEG,
+ NT35510_P1_GAMMA_LEN,
+ conf->gamma_corr_neg_b);
+ if (ret)
+ return ret;
+ }
+
+ /* Set up stuff in manufacturer control, page 0 */
+ ret = nt35510_send_long(dev, MCS_CMD_MAUCCTR,
+ ARRAY_SIZE(nt35510_mauc_select_page_0),
+ nt35510_mauc_select_page_0);
+ if (ret)
+ return ret;
+
+ ret = nt35510_setup_display(dev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int nt35510_panel_enable_backlight(struct udevice *dev)
+{
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
+ const struct nt35510_config *conf = to_panel_config(dev);
+ int ret;
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0)
+ return ret;
+
+ ret = nt35510_init_sequence(dev);
+ if (ret)
+ return ret;
+
+ /* Exit sleep mode */
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret) {
+ dev_err(dev, "failed to exit sleep mode (%d)\n", ret);
+ return ret;
+ }
+
+ /* Up to 120 ms */
+ mdelay(120);
+
+ if (conf->cmds & NT35510_CMD_CONTROL_DISPLAY) {
+ ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
+ &conf->wrctrld,
+ sizeof(conf->wrctrld));
+ if (ret < 0)
+ return ret;
+
+ ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_POWER_SAVE,
+ &conf->wrcabc,
+ sizeof(conf->wrcabc));
+ if (ret < 0)
+ return ret;
+
+ ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_CABC_MIN_BRIGHTNESS,
+ &conf->wrcabcmb,
+ sizeof(conf->wrcabcmb));
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = mipi_dsi_dcs_set_display_on(dsi);
+ if (ret) {
+ dev_err(dev, "failed to turn display on (%d)\n", ret);
+ return ret;
+ }
+
+ /* Some 10 ms */
+ mdelay(10);
+
+ ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x7f);
+ if (ret < 0)
+ return ret;
+
+ /* Need to wait a few time before sending the first image */
+ mdelay(10);
+
+ return 0;
+}
+
+static int nt35510_panel_get_display_timing(struct udevice *dev,
+ struct display_timing *timings)
+{
+ struct nt35510_panel_priv *priv = dev_get_priv(dev);
+
+ memcpy(timings, &priv->conf->timings, sizeof(*timings));
+ return 0;
+}
+
+static int nt35510_panel_of_to_plat(struct udevice *dev)
+{
+ struct nt35510_panel_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ if (CONFIG_IS_ENABLED(DM_REGULATOR)) {
+ ret = device_get_supply_regulator(dev, "vdd-supply",
+ &priv->vdd_reg);
+ if (ret) {
+ dev_err(dev, "Warning: cannot get vdd supply\n");
+ return ret;
+ }
+
+ ret = device_get_supply_regulator(dev, "vddi-supply",
+ &priv->vddi_reg);
+ if (ret) {
+ dev_err(dev, "Warning: cannot get vddi supply\n");
+ return ret;
+ }
+ }
+
+ priv->reset =
+ devm_gpiod_get_optional(dev, "reset",
+ GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
+ if (IS_ERR(priv->reset)) {
+ dev_err(dev, "error getting RESET GPIO\n");
+ return PTR_ERR(priv->reset);
+ }
+
+ return 0;
+}
+
+static int nt35510_panel_probe(struct udevice *dev)
+{
+ struct nt35510_panel_priv *priv = dev_get_priv(dev);
+ struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
+ int ret;
+
+ priv->conf = (struct nt35510_config *)dev_get_driver_data(dev);
+ if (!priv->conf) {
+ dev_err(dev, "missing device configuration\n");
+ return -ENODEV;
+ }
+
+ if (CONFIG_IS_ENABLED(DM_REGULATOR)) {
+ dev_dbg(dev, "enable regulator '%s'\n", priv->vdd_reg->name);
+ ret = regulator_set_enable(priv->vdd_reg, true);
+ if (ret)
+ return ret;
+
+ dev_dbg(dev, "enable regulator '%s'\n", priv->vddi_reg->name);
+ ret = regulator_set_enable(priv->vddi_reg, true);
+ if (ret)
+ return ret;
+ }
+
+ /* Toggle RESET in accordance with datasheet page 370 */
+ if (priv->reset) {
+ dm_gpio_set_value(priv->reset, 1);
+ /* Active min 10 us according to datasheet, let's say 20 */
+ mdelay(1);
+ dm_gpio_set_value(priv->reset, 0);
+ /*
+ * 5 ms during sleep mode, 120 ms during sleep out mode
+ * according to datasheet, let's use 15 ms.
+ */
+ mdelay(150);
+ }
+
+ plat->lanes = 2;
+ plat->format = MIPI_DSI_FMT_RGB888;
+ plat->mode_flags = priv->conf->mode_flags;
+
+ return 0;
+}
+
+static const struct panel_ops nt35510_panel_ops = {
+ .enable_backlight = nt35510_panel_enable_backlight,
+ .get_display_timing = nt35510_panel_get_display_timing,
+};
+
+/*
+ * These gamma correction values are 10bit tuples, so only bits 0 and 1 is
+ * ever used in the first byte. They form a positive and negative gamma
+ * correction curve for each color, values must be strictly higher for each
+ * step on the curve. As can be seen these default curves goes from 0x0001
+ * to 0x03FE.
+ */
+#define NT35510_GAMMA_POS_DEFAULT 0x00, 0x01, 0x00, 0x43, 0x00, \
+ 0x6B, 0x00, 0x87, 0x00, 0xA3, 0x00, 0xCE, 0x00, 0xF1, 0x01, \
+ 0x27, 0x01, 0x53, 0x01, 0x98, 0x01, 0xCE, 0x02, 0x22, 0x02, \
+ 0x83, 0x02, 0x78, 0x02, 0x9E, 0x02, 0xDD, 0x03, 0x00, 0x03, \
+ 0x2E, 0x03, 0x54, 0x03, 0x7F, 0x03, 0x95, 0x03, 0xB3, 0x03, \
+ 0xC2, 0x03, 0xE1, 0x03, 0xF1, 0x03, 0xFE
+
+#define NT35510_GAMMA_NEG_DEFAULT 0x00, 0x01, 0x00, 0x43, 0x00, \
+ 0x6B, 0x00, 0x87, 0x00, 0xA3, 0x00, 0xCE, 0x00, 0xF1, 0x01, \
+ 0x27, 0x01, 0x53, 0x01, 0x98, 0x01, 0xCE, 0x02, 0x22, 0x02, \
+ 0x43, 0x02, 0x50, 0x02, 0x9E, 0x02, 0xDD, 0x03, 0x00, 0x03, \
+ 0x2E, 0x03, 0x54, 0x03, 0x7F, 0x03, 0x95, 0x03, 0xB3, 0x03, \
+ 0xC2, 0x03, 0xE1, 0x03, 0xF1, 0x03, 0xFE
+
+/*
+ * The Hydis HVA40WV1 panel
+ */
+static const struct nt35510_config nt35510_hydis_hva40wv1 = {
+ .width_mm = 52,
+ .height_mm = 86,
+ /**
+ * As the Hydis panel is used in command mode, the porches etc
+ * are settings programmed internally into the NT35510 controller
+ * and generated toward the physical display. As the panel is not
+ * used in video mode, these are not really exposed to the DSI
+ * host.
+ *
+ * Display frame rate control:
+ * Frame rate = (20 MHz / 1) / (389 * (7 + 50 + 800)) ~= 60 Hz
+ */
+ .timings = {
+ /* The internal pixel clock of the NT35510 is 20 MHz */
+ .pixelclock.typ = 20000,
+ .hactive.typ = 480,
+ .hfront_porch.typ = 2,
+ .hsync_len.typ = 0,
+ .hback_porch.typ = 5,
+ .vactive.typ = 800,
+ .vfront_porch.typ = 2,
+ .vsync_len.typ = 0,
+ .vback_porch.typ = 5,
+ },
+ .mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS,
+ .cmds = NT35510_CMD_CORRECT_GAMMA,
+ /* 0x09: AVDD = 5.6V */
+ .avdd = { 0x09, 0x09, 0x09 },
+ /* 0x34: PCK = Hsync/2, BTP = 2 x VDDB */
+ .bt1ctr = { 0x34, 0x34, 0x34 },
+ /* 0x09: AVEE = -5.6V */
+ .avee = { 0x09, 0x09, 0x09 },
+ /* 0x24: NCK = Hsync/2, BTN = -2 x VDDB */
+ .bt2ctr = { 0x24, 0x24, 0x24 },
+ /* VBCLA: -2.5V, VBCLB: -2.5V, VBCLC: -2.5V */
+ .vcl = { 0x00, 0x00, 0x00 },
+ /* 0x24: CLCK = Hsync/2, BTN = -1 x VDDB */
+ .bt3ctr = { 0x24, 0x24, 0x24 },
+ /* 0x05 = 12V */
+ .vgh = { 0x05, 0x05, 0x05 },
+ /* 0x24: NCKA = Hsync/2, VGH = 2 x AVDD - AVEE */
+ .bt4ctr = { 0x24, 0x24, 0x24 },
+ /* 0x0B = -13V */
+ .vgl = { 0x0B, 0x0B, 0x0B },
+ /* 0x24: LCKA = Hsync, VGL = AVDD + VCL - AVDD */
+ .bt5ctr = { 0x24, 0x24, 0x24 },
+ /* VGMP: 0x0A3 = 5.0375V, VGSP = 0V */
+ .vgp = { 0x00, 0xA3, 0x00 },
+ /* VGMP: 0x0A3 = 5.0375V, VGSP = 0V */
+ .vgn = { 0x00, 0xA3, 0x00 },
+ /* VCMOFFSEL = VCOM voltage offset mode, VCM = 0V */
+ .vcmoff = { 0x00, 0x00 },
+ /* Enable TE, EoTP and RGB pixel format */
+ .dopctr = { NT35510_DOPCTR_0_DSITE | NT35510_DOPCTR_0_EOTP |
+ NT35510_DOPCTR_0_N565, NT35510_DOPCTR_1_CTB },
+ .madctl = NT35510_ROTATE_0_SETTING,
+ /* 0x0A: SDT = 5 us */
+ .sdhdtctr = 0x0A,
+ /* EQ control for gate signals, 0x00 = 0 us */
+ .gseqctr = { 0x00, 0x00 },
+ /* SDEQCTR: source driver EQ mode 2, 2.5 us rise time on each step */
+ .sdeqctr = { 0x01, 0x05, 0x05, 0x05 },
+ /* SDVPCTR: Normal operation off color during v porch */
+ .sdvpctr = 0x01,
+ /* T1: number of pixel clocks on one scanline: 0x184 = 389 clocks */
+ .t1 = 0x0184,
+ /* VBP: vertical back porch toward the panel */
+ .vbp = 7,
+ /* VFP: vertical front porch toward the panel */
+ .vfp = 50,
+ /* PSEL: divide pixel clock 20MHz with 1 (no clock downscaling) */
+ .psel = 0,
+ /* DPTMCTR12: 0x03: LVGL = VGLX, overlap mode, swap R->L O->E */
+ .dpmctr12 = { 0x03, 0x00, 0x00, },
+ /* Default gamma correction values */
+ .gamma_corr_pos_r = { NT35510_GAMMA_POS_DEFAULT },
+ .gamma_corr_pos_g = { NT35510_GAMMA_POS_DEFAULT },
+ .gamma_corr_pos_b = { NT35510_GAMMA_POS_DEFAULT },
+ .gamma_corr_neg_r = { NT35510_GAMMA_NEG_DEFAULT },
+ .gamma_corr_neg_g = { NT35510_GAMMA_NEG_DEFAULT },
+ .gamma_corr_neg_b = { NT35510_GAMMA_NEG_DEFAULT },
+};
+
+static const struct nt35510_config nt35510_frida_frd400b25025 = {
+ .width_mm = 52,
+ .height_mm = 86,
+ .timings = {
+ .pixelclock.typ = 23000000,
+ .hactive.typ = 480,
+ .hfront_porch.typ = 34,
+ .hback_porch.typ = 34,
+ .hsync_len.typ = 2,
+ .vactive.typ = 800,
+ .vfront_porch.typ = 15,
+ .vback_porch.typ = 15,
+ .vsync_len.typ = 12,
+ },
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+ MIPI_DSI_MODE_LPM,
+ .cmds = NT35510_CMD_CONTROL_DISPLAY | NT35510_CMD_SETVCMOFF,
+ /* 0x03: AVDD = 6.2V */
+ .avdd = { 0x03, 0x03, 0x03 },
+ /* 0x46: PCK = 2 x Hsync, BTP = 2.5 x VDDB */
+ .bt1ctr = { 0x46, 0x46, 0x46 },
+ /* 0x03: AVEE = -6.2V */
+ .avee = { 0x03, 0x03, 0x03 },
+ /* 0x36: PCK = 2 x Hsync, BTP = 2 x VDDB */
+ .bt2ctr = { 0x36, 0x36, 0x36 },
+ /* VBCLA: -2.5V, VBCLB: -2.5V, VBCLC: -3.5V */
+ .vcl = { 0x00, 0x00, 0x02 },
+ /* 0x26: CLCK = 2 x Hsync, BTN = -1 x VDDB */
+ .bt3ctr = { 0x26, 0x26, 0x26 },
+ /* 0x09 = 16V */
+ .vgh = { 0x09, 0x09, 0x09 },
+ /* 0x36: HCK = 2 x Hsync, VGH = 2 x AVDD - AVEE */
+ .bt4ctr = { 0x36, 0x36, 0x36 },
+ /* 0x08 = -10V */
+ .vgl = { 0x08, 0x08, 0x08 },
+ /* 0x26: LCK = 2 x Hsync, VGL = AVDD + VCL - AVDD */
+ .bt5ctr = { 0x26, 0x26, 0x26 },
+ /* VGMP: 0x080 = 4.6V, VGSP = 0V */
+ .vgp = { 0x00, 0x80, 0x00 },
+ /* VGMP: 0x080 = 4.6V, VGSP = 0V */
+ .vgn = { 0x00, 0x80, 0x00 },
+ /* VCMOFFSEL = VCOM voltage offset mode, VCM = -1V */
+ .vcmoff = { 0x00, 0x50 },
+ .dopctr = { NT35510_DOPCTR_0_RAMKP | NT35510_DOPCTR_0_DSITE |
+ NT35510_DOPCTR_0_DSIG | NT35510_DOPCTR_0_DSIM |
+ NT35510_DOPCTR_0_EOTP | NT35510_DOPCTR_0_N565, 0 },
+ .madctl = NT35510_ROTATE_180_SETTING,
+ /* 0x03: SDT = 1.5 us */
+ .sdhdtctr = 0x03,
+ /* EQ control for gate signals, 0x00 = 0 us */
+ .gseqctr = { 0x00, 0x00 },
+ /* SDEQCTR: source driver EQ mode 2, 1 us rise time on each step */
+ .sdeqctr = { 0x01, 0x02, 0x02, 0x02 },
+ /* SDVPCTR: Normal operation off color during v porch */
+ .sdvpctr = 0x01,
+ /* T1: number of pixel clocks on one scanline: 0x184 = 389 clocks */
+ .t1 = 0x0184,
+ /* VBP: vertical back porch toward the panel */
+ .vbp = 0x1C,
+ /* VFP: vertical front porch toward the panel */
+ .vfp = 0x1C,
+ /* PSEL: divide pixel clock 23MHz with 1 (no clock downscaling) */
+ .psel = 0,
+ /* DPTMCTR12: 0x03: LVGL = VGLX, overlap mode, swap R->L O->E */
+ .dpmctr12 = { 0x03, 0x00, 0x00, },
+ /* write display brightness */
+ .wrdisbv = 0x7f,
+ /* write control display */
+ .wrctrld = NT35510_WRCTRLD_BCTRL | NT35510_WRCTRLD_DD |
+ NT35510_WRCTRLD_BL,
+ /* write content adaptive brightness control */
+ .wrcabc = NT35510_WRCABC_STILL_MODE,
+ /* write CABC minimum brightness */
+ .wrcabcmb = 0xff,
+};
+
+static const struct udevice_id nt35510_panel_ids[] = {
+ {
+ .compatible = "frida,frd400b25025",
+ .data = (ulong)&nt35510_frida_frd400b25025,
+ },
+ {
+ .compatible = "hydis,hva40wv1",
+ .data = (ulong)&nt35510_hydis_hva40wv1,
+ },
+ { }
+};
+
+U_BOOT_DRIVER(nt35510_panel) = {
+ .name = "nt35510_panel",
+ .id = UCLASS_PANEL,
+ .of_match = nt35510_panel_ids,
+ .ops = &nt35510_panel_ops,
+ .of_to_plat = nt35510_panel_of_to_plat,
+ .probe = nt35510_panel_probe,
+ .plat_auto = sizeof(struct mipi_dsi_panel_plat),
+ .priv_auto = sizeof(struct nt35510_panel_priv),
+};
diff --git a/drivers/video/tegra/Kconfig b/drivers/video/tegra/Kconfig
index 1a328407b13..f32972d937e 100644
--- a/drivers/video/tegra/Kconfig
+++ b/drivers/video/tegra/Kconfig
@@ -42,6 +42,16 @@ config TEGRA_BACKLIGHT_PWM
Enable support for the Display Controller dependent PWM backlight
found in the Tegra SoC and usually used with DSI panels.
+config TEGRA_8BIT_CPU_BRIDGE
+ bool "Enable 8 bit panel communication protocol for Tegra 20/30"
+ depends on VIDEO_BRIDGE && DM_GPIO
+ select VIDEO_TEGRA
+ select VIDEO_MIPI_DSI
+ help
+ Tegra 20 and Tegra 30 feature 8 bit CPU driver panel control
+ protocol. This option allows use it as a MIPI DSI bridge to
+ set up and control compatible panel.
+
config VIDEO_TEGRA124
bool "Enable video support on Tegra124"
imply VIDEO_DAMAGE
diff --git a/drivers/video/tegra/Makefile b/drivers/video/tegra/Makefile
index 3c50a0ba3c3..4995c93a8af 100644
--- a/drivers/video/tegra/Makefile
+++ b/drivers/video/tegra/Makefile
@@ -5,5 +5,6 @@ obj-$(CONFIG_VIDEO_TEGRA) += dc.o
obj-$(CONFIG_VIDEO_DSI_TEGRA) += dsi.o mipi.o mipi-phy.o
obj-$(CONFIG_VIDEO_HDMI_TEGRA) += hdmi.o
obj-$(CONFIG_TEGRA_BACKLIGHT_PWM) += dc-pwm-backlight.o
+obj-$(CONFIG_TEGRA_8BIT_CPU_BRIDGE) += cpu-bridge.o
obj-${CONFIG_VIDEO_TEGRA124} += tegra124/
diff --git a/drivers/video/tegra/cpu-bridge.c b/drivers/video/tegra/cpu-bridge.c
new file mode 100644
index 00000000000..e5fefe028f0
--- /dev/null
+++ b/drivers/video/tegra/cpu-bridge.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com>
+ *
+ * This driver uses 8-bit CPU interface found in Tegra 2
+ * and Tegra 3 to drive MIPI DSI panel.
+ */
+
+#include <dm.h>
+#include <dm/ofnode_graph.h>
+#include <log.h>
+#include <mipi_display.h>
+#include <mipi_dsi.h>
+#include <backlight.h>
+#include <panel.h>
+#include <video_bridge.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+
+#include "dc.h"
+
+struct tegra_cpu_bridge_priv {
+ struct dc_ctlr *dc;
+
+ struct mipi_dsi_host host;
+ struct mipi_dsi_device device;
+
+ struct udevice *panel;
+ struct display_timing timing;
+
+ struct gpio_desc dc_gpio;
+ struct gpio_desc rw_gpio;
+ struct gpio_desc cs_gpio;
+
+ struct gpio_desc data_gpios[8];
+
+ u32 pixel_format;
+ u32 spi_init_seq[4];
+};
+
+#define TEGRA_CPU_BRIDGE_COMM 0
+#define TEGRA_CPU_BRIDGE_DATA 1
+
+static void tegra_cpu_bridge_write(struct tegra_cpu_bridge_priv *priv,
+ u8 type, u8 value)
+{
+ int i;
+
+ dm_gpio_set_value(&priv->dc_gpio, type);
+
+ dm_gpio_set_value(&priv->cs_gpio, 0);
+ dm_gpio_set_value(&priv->rw_gpio, 0);
+
+ for (i = 0; i < 8; i++)
+ dm_gpio_set_value(&priv->data_gpios[i],
+ (value >> i) & 0x1);
+
+ dm_gpio_set_value(&priv->cs_gpio, 1);
+ dm_gpio_set_value(&priv->rw_gpio, 1);
+
+ udelay(10);
+
+ log_debug("%s: type 0x%x, val 0x%x\n",
+ __func__, type, value);
+}
+
+static ssize_t tegra_cpu_bridge_transfer(struct mipi_dsi_host *host,
+ const struct mipi_dsi_msg *msg)
+{
+ struct udevice *dev = (struct udevice *)host->dev;
+ struct tegra_cpu_bridge_priv *priv = dev_get_priv(dev);
+ u8 command = *(u8 *)msg->tx_buf;
+ const u8 *data = msg->tx_buf;
+ int i;
+
+ tegra_cpu_bridge_write(priv, TEGRA_CPU_BRIDGE_COMM, command);
+
+ for (i = 1; i < msg->tx_len; i++)
+ tegra_cpu_bridge_write(priv, TEGRA_CPU_BRIDGE_DATA, data[i]);
+
+ return 0;
+}
+
+static const struct mipi_dsi_host_ops tegra_cpu_bridge_host_ops = {
+ .transfer = tegra_cpu_bridge_transfer,
+};
+
+static int tegra_cpu_bridge_get_format(enum mipi_dsi_pixel_format format, u32 *fmt)
+{
+ switch (format) {
+ case MIPI_DSI_FMT_RGB888:
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ *fmt = BASE_COLOR_SIZE_888;
+ break;
+
+ case MIPI_DSI_FMT_RGB666:
+ *fmt = BASE_COLOR_SIZE_666;
+ break;
+
+ case MIPI_DSI_FMT_RGB565:
+ *fmt = BASE_COLOR_SIZE_565;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tegra_cpu_bridge_attach(struct udevice *dev)
+{
+ struct tegra_cpu_bridge_priv *priv = dev_get_priv(dev);
+ struct dc_disp_reg *disp = &priv->dc->disp;
+ struct dc_cmd_reg *cmd = &priv->dc->cmd;
+ struct dc_com_reg *com = &priv->dc->com;
+ u32 value;
+ int ret;
+
+ writel(CTRL_MODE_STOP << CTRL_MODE_SHIFT, &cmd->disp_cmd);
+ writel(0, &disp->disp_win_opt);
+ writel(GENERAL_UPDATE, &cmd->state_ctrl);
+ writel(GENERAL_ACT_REQ, &cmd->state_ctrl);
+
+ /* TODO: parametrize if needed */
+ writel(V_PULSE1_ENABLE, &disp->disp_signal_opt0);
+ writel(PULSE_POLARITY_LOW, &disp->v_pulse1.v_pulse_ctrl);
+
+ writel(PULSE_END(1), &disp->v_pulse1.v_pulse_pos[V_PULSE0_POSITION_A]);
+ writel(0, &disp->v_pulse1.v_pulse_pos[V_PULSE0_POSITION_B]);
+ writel(0, &disp->v_pulse1.v_pulse_pos[V_PULSE0_POSITION_C]);
+
+ ret = dev_read_u32_array(dev, "nvidia,init-sequence", priv->spi_init_seq, 4);
+ if (!ret) {
+ value = 1 << FRAME_INIT_SEQ_CYCLES_SHIFT |
+ DC_SIGNAL_VPULSE1 << INIT_SEQ_DC_SIGNAL_SHIFT |
+ INIT_SEQUENCE_MODE_PLCD | SEND_INIT_SEQUENCE;
+ writel(value, &disp->seq_ctrl);
+
+ writel(priv->spi_init_seq[0], &disp->spi_init_seq_data_a);
+ writel(priv->spi_init_seq[1], &disp->spi_init_seq_data_b);
+ writel(priv->spi_init_seq[2], &disp->spi_init_seq_data_c);
+ writel(priv->spi_init_seq[3], &disp->spi_init_seq_data_d);
+ }
+
+ value = readl(&cmd->disp_cmd);
+ value &= ~CTRL_MODE_MASK;
+ value |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT;
+ writel(value, &cmd->disp_cmd);
+
+ /* set LDC pin to V Pulse 1 */
+ value = readl(&com->pin_output_sel[6]) | LDC_OUTPUT_SELECT_V_PULSE1;
+ writel(value, &com->pin_output_sel[6]);
+
+ value = readl(&disp->disp_interface_ctrl);
+ value |= DATA_ALIGNMENT_LSB << DATA_ALIGNMENT_SHIFT;
+ writel(value, &disp->disp_interface_ctrl);
+
+ value = SC_H_QUALIFIER_NONE << SC1_H_QUALIFIER_SHIFT |
+ SC_V_QUALIFIER_VACTIVE << SC0_V_QUALIFIER_SHIFT |
+ SC_H_QUALIFIER_HACTIVE << SC0_H_QUALIFIER_SHIFT;
+ writel(value, &disp->shift_clk_opt);
+
+ value = readl(&disp->disp_color_ctrl);
+ value |= priv->pixel_format;
+ writel(value, &disp->disp_color_ctrl);
+
+ /* Perform panel setup */
+ panel_enable_backlight(priv->panel);
+
+ dm_gpio_set_value(&priv->cs_gpio, 0);
+
+ dm_gpio_free(dev, &priv->dc_gpio);
+ dm_gpio_free(dev, &priv->rw_gpio);
+ dm_gpio_free(dev, &priv->cs_gpio);
+
+ gpio_free_list(dev, priv->data_gpios, 8);
+
+ return 0;
+}
+
+static int tegra_cpu_bridge_set_panel(struct udevice *dev, int percent)
+{
+ struct tegra_cpu_bridge_priv *priv = dev_get_priv(dev);
+
+ return panel_set_backlight(priv->panel, percent);
+}
+
+static int tegra_cpu_bridge_panel_timings(struct udevice *dev,
+ struct display_timing *timing)
+{
+ struct tegra_cpu_bridge_priv *priv = dev_get_priv(dev);
+
+ memcpy(timing, &priv->timing, sizeof(*timing));
+
+ return 0;
+}
+
+static int tegra_cpu_bridge_hw_init(struct udevice *dev)
+{
+ struct tegra_cpu_bridge_priv *priv = dev_get_priv(dev);
+
+ dm_gpio_set_value(&priv->cs_gpio, 1);
+
+ dm_gpio_set_value(&priv->rw_gpio, 1);
+ dm_gpio_set_value(&priv->dc_gpio, 0);
+
+ return 0;
+}
+
+static int tegra_cpu_bridge_get_links(struct udevice *dev)
+{
+ struct tegra_cpu_bridge_priv *priv = dev_get_priv(dev);
+ int i, ret;
+
+ u32 num = ofnode_graph_get_port_count(dev_ofnode(dev));
+
+ for (i = 0; i < num; i++) {
+ ofnode remote = ofnode_graph_get_remote_node(dev_ofnode(dev), i, -1);
+
+ /* Look for DC source */
+ if (ofnode_name_eq(remote, "rgb")) {
+ ofnode dc = ofnode_get_parent(remote);
+
+ priv->dc = (struct dc_ctlr *)ofnode_get_addr(dc);
+ if (!priv->dc) {
+ log_err("%s: failed to get DC controller\n", __func__);
+ return -EINVAL;
+ }
+ }
+
+ /* Look for driven panel */
+ ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote, &priv->panel);
+ if (!ret)
+ return 0;
+ }
+
+ /* If this point is reached, no panels were found */
+ return -ENODEV;
+}
+
+static int tegra_cpu_bridge_probe(struct udevice *dev)
+{
+ struct tegra_cpu_bridge_priv *priv = dev_get_priv(dev);
+ struct mipi_dsi_device *device = &priv->device;
+ struct mipi_dsi_panel_plat *mipi_plat;
+ int ret;
+
+ ret = tegra_cpu_bridge_get_links(dev);
+ if (ret) {
+ log_debug("%s: links not found, ret %d\n", __func__, ret);
+ return ret;
+ }
+
+ panel_get_display_timing(priv->panel, &priv->timing);
+
+ mipi_plat = dev_get_plat(priv->panel);
+ mipi_plat->device = device;
+
+ priv->host.dev = (struct device *)dev;
+ priv->host.ops = &tegra_cpu_bridge_host_ops;
+
+ device->host = &priv->host;
+ device->lanes = mipi_plat->lanes;
+ device->format = mipi_plat->format;
+ device->mode_flags = mipi_plat->mode_flags;
+
+ tegra_cpu_bridge_get_format(device->format, &priv->pixel_format);
+
+ /* get control gpios */
+ ret = gpio_request_by_name(dev, "dc-gpios", 0,
+ &priv->dc_gpio, GPIOD_IS_OUT);
+ if (ret) {
+ log_debug("%s: could not decode dc-gpios (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ ret = gpio_request_by_name(dev, "rw-gpios", 0,
+ &priv->rw_gpio, GPIOD_IS_OUT);
+ if (ret) {
+ log_debug("%s: could not decode rw-gpios (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ ret = gpio_request_by_name(dev, "cs-gpios", 0,
+ &priv->cs_gpio, GPIOD_IS_OUT);
+ if (ret) {
+ log_debug("%s: could not decode cs-gpios (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ /* get data gpios */
+ ret = gpio_request_list_by_name(dev, "data-gpios",
+ priv->data_gpios, 8,
+ GPIOD_IS_OUT);
+ if (ret < 0) {
+ log_debug("%s: could not decode data-gpios (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ return tegra_cpu_bridge_hw_init(dev);
+}
+
+static const struct video_bridge_ops tegra_cpu_bridge_ops = {
+ .attach = tegra_cpu_bridge_attach,
+ .set_backlight = tegra_cpu_bridge_set_panel,
+ .get_display_timing = tegra_cpu_bridge_panel_timings,
+};
+
+static const struct udevice_id tegra_cpu_bridge_ids[] = {
+ { .compatible = "nvidia,tegra-8bit-cpu" },
+ { }
+};
+
+U_BOOT_DRIVER(tegra_8bit_cpu) = {
+ .name = "tegra_8bit_cpu",
+ .id = UCLASS_VIDEO_BRIDGE,
+ .of_match = tegra_cpu_bridge_ids,
+ .ops = &tegra_cpu_bridge_ops,
+ .bind = dm_scan_fdt_dev,
+ .probe = tegra_cpu_bridge_probe,
+ .priv_auto = sizeof(struct tegra_cpu_bridge_priv),
+};
diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c
index a1dfd35b7b8..f1b2d61bd8f 100644
--- a/drivers/video/vidconsole-uclass.c
+++ b/drivers/video/vidconsole-uclass.c
@@ -127,6 +127,9 @@ void vidconsole_set_cursor_pos(struct udevice *dev, int x, int y)
priv->xcur_frac = VID_TO_POS(x);
priv->xstart_frac = priv->xcur_frac;
priv->ycur = y;
+
+ /* make sure not to kern against the previous character */
+ priv->last_ch = 0;
vidconsole_entry_start(dev);
}
@@ -508,12 +511,14 @@ int vidconsole_put_char(struct udevice *dev, char ch)
return 0;
}
-int vidconsole_put_string(struct udevice *dev, const char *str)
+int vidconsole_put_stringn(struct udevice *dev, const char *str, int maxlen)
{
- const char *s;
+ const char *s, *end = NULL;
int ret;
- for (s = str; *s; s++) {
+ if (maxlen != -1)
+ end = str + maxlen;
+ for (s = str; *s && (maxlen == -1 || s < end); s++) {
ret = vidconsole_put_char(dev, *s);
if (ret)
return ret;
@@ -522,11 +527,19 @@ int vidconsole_put_string(struct udevice *dev, const char *str)
return 0;
}
+int vidconsole_put_string(struct udevice *dev, const char *str)
+{
+ return vidconsole_put_stringn(dev, str, -1);
+}
+
static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
{
struct udevice *dev = sdev->priv;
+ struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
int ret;
+ if (priv->quiet)
+ return;
ret = vidconsole_put_char(dev, ch);
if (ret) {
#ifdef DEBUG
@@ -544,8 +557,11 @@ static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
{
struct udevice *dev = sdev->priv;
+ struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
int ret;
+ if (priv->quiet)
+ return;
ret = vidconsole_put_string(dev, s);
if (ret) {
#ifdef DEBUG
@@ -608,14 +624,17 @@ int vidconsole_select_font(struct udevice *dev, const char *name, uint size)
}
int vidconsole_measure(struct udevice *dev, const char *name, uint size,
- const char *text, struct vidconsole_bbox *bbox)
+ const char *text, int limit,
+ struct vidconsole_bbox *bbox, struct alist *lines)
{
struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
struct vidconsole_ops *ops = vidconsole_get_ops(dev);
int ret;
if (ops->measure) {
- ret = ops->measure(dev, name, size, text, bbox);
+ if (lines)
+ alist_empty(lines);
+ ret = ops->measure(dev, name, size, text, limit, bbox, lines);
if (ret != -ENOSYS)
return ret;
}
@@ -784,3 +803,10 @@ void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
y = min_t(short, row * priv->y_charsize, vid_priv->ysize - 1);
vidconsole_set_cursor_pos(dev, x, y);
}
+
+void vidconsole_set_quiet(struct udevice *dev, bool quiet)
+{
+ struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+
+ priv->quiet = quiet;
+}
diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c
index 503cdb9f025..53641fc28b6 100644
--- a/drivers/video/video-uclass.c
+++ b/drivers/video/video-uclass.c
@@ -26,6 +26,7 @@
#ifdef CONFIG_SANDBOX
#include <asm/sdl.h>
#endif
+#include "vidconsole_internal.h"
/*
* Theory of operation:
@@ -216,6 +217,40 @@ int video_fill_part(struct udevice *dev, int xstart, int ystart, int xend,
return 0;
}
+int video_draw_box(struct udevice *dev, int x0, int y0, int x1, int y1,
+ int width, u32 colour)
+{
+ struct video_priv *priv = dev_get_uclass_priv(dev);
+ int pbytes = VNBYTES(priv->bpix);
+ void *start, *line;
+ int pixels = x1 - x0;
+ int row;
+
+ start = priv->fb + y0 * priv->line_length;
+ start += x0 * pbytes;
+ line = start;
+ for (row = y0; row < y1; row++) {
+ void *ptr = line;
+ int i;
+
+ for (i = 0; i < width; i++)
+ fill_pixel_and_goto_next(&ptr, colour, pbytes, pbytes);
+ if (row < y0 + width || row >= y1 - width) {
+ for (i = 0; i < pixels - width * 2; i++)
+ fill_pixel_and_goto_next(&ptr, colour, pbytes,
+ pbytes);
+ } else {
+ ptr += (pixels - width * 2) * pbytes;
+ }
+ for (i = 0; i < width; i++)
+ fill_pixel_and_goto_next(&ptr, colour, pbytes, pbytes);
+ line += priv->line_length;
+ }
+ video_damage(dev, x0, y0, x1 - x0, y1 - y0);
+
+ return 0;
+}
+
int video_reserve_from_bloblist(struct video_handoff *ho)
{
if (!ho->fb || ho->size == 0)
@@ -345,7 +380,7 @@ void video_set_default_colors(struct udevice *dev, bool invert)
struct video_priv *priv = dev_get_uclass_priv(dev);
int fore, back;
- if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
+ if (priv->white_on_black) {
/* White is used when switching to bold, use light gray here */
fore = VID_LIGHT_GRAY;
back = VID_BLACK;
@@ -481,6 +516,7 @@ int video_sync(struct udevice *vid, bool force)
video_flush_dcache(vid, true);
#if defined(CONFIG_VIDEO_SANDBOX_SDL)
+ /* to see the copy framebuffer, use priv->copy_fb */
sandbox_sdl_sync(priv->fb);
#endif
priv->last_sync = get_timer(0);
@@ -582,6 +618,18 @@ static void video_idle(struct cyclic_info *cyc)
video_sync_all();
}
+void video_set_white_on_black(struct udevice *dev, bool white_on_black)
+{
+ struct video_priv *priv = dev_get_uclass_priv(dev);
+
+ if (priv->white_on_black != white_on_black) {
+ priv->white_on_black = white_on_black;
+ video_set_default_colors(dev, false);
+
+ video_clear(dev);
+ }
+}
+
/* Set up the display ready for use */
static int video_post_probe(struct udevice *dev)
{
@@ -624,6 +672,8 @@ static int video_post_probe(struct udevice *dev)
if (IS_ENABLED(CONFIG_VIDEO_COPY) && plat->copy_base)
priv->copy_fb = map_sysmem(plat->copy_base, plat->size);
+ priv->white_on_black = CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK);
+
/* Set up colors */
video_set_default_colors(dev, false);