diff options
Diffstat (limited to 'drivers')
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(®s->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(®s->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(®s->orsf); + + jr->tail = (jr->tail + slots_full) & (jr->size - 1); + sec_out32(®s->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(®s->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 = ð->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, ð->rst); + if (ret < 0) + goto err_clk_enable; + + ret = reset_deassert(ð->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(ð->rst); +err_reset_deassert: + if (device_ops->has_reset) + reset_free(ð->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(ð->rst); + reset_free(ð->rst); + } clk_release_bulk(ð->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); |