Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F176992011
D57732.1793707462.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
106 KB
Referenced Files
None
Subscribers
None
D57732.1793707462.diff
View Options
diff --git a/sys/arm64/conf/GENERIC b/sys/arm64/conf/GENERIC
--- a/sys/arm64/conf/GENERIC
+++ b/sys/arm64/conf/GENERIC
@@ -38,6 +38,7 @@
include "std.hisilicon"
include "std.imx"
include "std.marvell"
+include "std.mediatek"
include "std.nvidia"
include "std.nxp"
include "std.qcom"
diff --git a/sys/arm64/conf/MEDIATEK b/sys/arm64/conf/MEDIATEK
new file mode 100644
--- /dev/null
+++ b/sys/arm64/conf/MEDIATEK
@@ -0,0 +1,38 @@
+#
+# MEDIATEK -- Mediatek kernel configuration file for FreeBSD/arm64
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# https://docs.freebsd.org/en/books/handbook/kernelconfig/#kernelconfig-config
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+
+#NO_UNIVERSE
+
+cpu ARM64
+ident MEDIATEK
+
+makeoptions DEBUG=-g # Enable kernel debugging sybmbols
+makeoptions WITH_KERNEL_BIN=1
+
+options EARLY_PRINTF=ns8250
+options SOCDEV_PA=0x11002000
+options UART_NS8250_EARLY_REG_SHIFT=2
+options UART_NS8250_EARLY_REG_IO_WIDTH=2
+options BOOTVERBOSE
+
+# Include SoC specific configuration
+include "std.arm64"
+include "std.dev"
+include "std.mediatek"
+
diff --git a/sys/arm64/conf/std.mediatek b/sys/arm64/conf/std.mediatek
new file mode 100644
--- /dev/null
+++ b/sys/arm64/conf/std.mediatek
@@ -0,0 +1,11 @@
+options SOC_MEDIATEK
+
+device uart_ns8250
+
+# GPIO / PINCTRL
+device mt7622_pinctrl
+
+options FDT
+
+# DTBs
+makeoptions MODULES_EXTRA+="dtb/mediatek"
diff --git a/sys/arm64/mediatek/mt7622_clk_apmixedsys.c b/sys/arm64/mediatek/mt7622_clk_apmixedsys.c
new file mode 100644
--- /dev/null
+++ b/sys/arm64/mediatek/mt7622_clk_apmixedsys.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2026 Martin Filla <freebsd@sysctl.cz>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <dev/fdt/simplebus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dt-bindings/clock/mt7622-clk.h>
+#include <dev/clk/clk_gate.h>
+
+#include "mt_clk.h"
+#include "mt_clk_pll.h"
+
+#define MT7622_PLL_FMAX 2500000000ULL
+#define CON0_MT7622_RST_BAR (1u << 27)
+
+#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pd_reg, _pd_shift, _pcw_reg, _pcw_shift) \
+ MT_PLL(_id, _name, "clkxtal", _reg, _pwr_reg, _en_mask, _flags, \
+ _pcwbits, _pd_reg, _pd_shift, _pcw_reg, _pcw_shift, \
+ CON0_MT7622_RST_BAR, MT7622_PLL_FMAX)
+
+static struct ofw_compat_data compat_data[] = {
+ {"mediatek,mt7622-apmixedsys", 1},
+ {NULL, 0},
+};
+
+/* Register layout from Linux clk-mt7622-apmixedsys.c */
+static struct mt_clk_pll_def plls_clk[] = {
+ PLL(CLK_APMIXED_ARMPLL, "armpll", 0x0200, 0x020C, 0,
+ MT_PLL_AO, 21, 0x0204, 24, 0x0204, 0),
+ PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x0210, 0x021C, 0,
+ MT_PLL_HAVE_RST_BAR, 21, 0x0214, 24, 0x0214, 0),
+ PLL(CLK_APMIXED_UNIV2PLL, "univ2pll", 0x0220, 0x022C, 0,
+ MT_PLL_HAVE_RST_BAR, 7, 0x0224, 24, 0x0224, 14),
+ PLL(CLK_APMIXED_ETH1PLL, "eth1pll", 0x0300, 0x0310, 0,
+ 0, 21, 0x0300, 1, 0x0304, 0),
+ PLL(CLK_APMIXED_ETH2PLL, "eth2pll", 0x0314, 0x0320, 0,
+ 0, 21, 0x0314, 1, 0x0318, 0),
+ PLL(CLK_APMIXED_AUD1PLL, "aud1pll", 0x0324, 0x0330, 0,
+ 0, 31, 0x0324, 1, 0x0328, 0),
+ PLL(CLK_APMIXED_AUD2PLL, "aud2pll", 0x0334, 0x0340, 0,
+ 0, 31, 0x0334, 1, 0x0338, 0),
+ PLL(CLK_APMIXED_TRGPLL, "trgpll", 0x0344, 0x0354, 0,
+ 0, 21, 0x0344, 1, 0x0348, 0),
+ PLL(CLK_APMIXED_SGMIPLL, "sgmipll", 0x0358, 0x0368, 0,
+ 0, 21, 0x0358, 1, 0x035C, 0),
+};
+
+static struct clk_gate_def gates_clk[] = {
+ GATE(CLK_APMIXED_MAIN_CORE_EN, "main_core_en", "mainpll", 0x0008, 5),
+};
+
+static struct mt_clk_def clk_def = {
+ .pll_def = plls_clk,
+ .num_pll = nitems(plls_clk),
+ .gates_def = gates_clk,
+ .num_gates = nitems(gates_clk),
+};
+
+static int
+apmixedsys_clk_probe(device_t dev)
+{
+
+ return (mt_clk_probe(dev, compat_data,
+ "Mediatek mt7622 apmixedsys clocks"));
+}
+
+static int
+apmixedsys_clk_attach(device_t dev)
+{
+ struct mt_clk_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->clk_def = &clk_def;
+
+ return (mt_clk_attach(dev));
+}
+
+static device_method_t mt7622_apmixedsys_methods[] = {
+ DEVMETHOD(device_probe, apmixedsys_clk_probe),
+ DEVMETHOD(device_attach, apmixedsys_clk_attach),
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(mt7622_apmixedsys, mt7622_apmixedsys_driver,
+ mt7622_apmixedsys_methods, sizeof(struct mt_clk_softc), mt_clk_driver);
+
+/* PLLs must be registered before topckgen, which derives its clocks from them. */
+EARLY_DRIVER_MODULE(mt7622_apmixedsys, simplebus, mt7622_apmixedsys_driver,
+ NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE + 1);
diff --git a/sys/arm64/mediatek/mt7622_clk_audio.c b/sys/arm64/mediatek/mt7622_clk_audio.c
new file mode 100644
--- /dev/null
+++ b/sys/arm64/mediatek/mt7622_clk_audio.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2025, 2026 Martin Filla <freebsd@sysctl.cz>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <dev/fdt/simplebus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dt-bindings/clock/mt7622-clk.h>
+#include <dev/clk/clk_gate.h>
+
+#include "mt_clk.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"mediatek,mt7622-audsys", 1},
+ {NULL, 0},
+};
+
+static struct clk_gate_def gates_clk[] = {
+ /* AUDIO0 */
+ GATE(CLK_AUDIO_AFE, "audio_afe", "rtc", 0x0, 2),
+ GATE(CLK_AUDIO_HDMI, "audio_hdmi", "apll1_ck_sel", 0x0, 20),
+ GATE(CLK_AUDIO_SPDF, "audio_spdf", "apll1_ck_sel", 0x0, 21),
+ GATE(CLK_AUDIO_APLL, "audio_apll", "apll1_ck_sel", 0x0, 23),
+ /* AUDIO1 */
+ GATE(CLK_AUDIO_I2SIN1, "audio_i2sin1", "a1sys_hp_sel", 0x10, 0),
+ GATE(CLK_AUDIO_I2SIN2, "audio_i2sin2", "a1sys_hp_sel", 0x10, 1),
+ GATE(CLK_AUDIO_I2SIN3, "audio_i2sin3", "a1sys_hp_sel", 0x10, 2),
+ GATE(CLK_AUDIO_I2SIN4, "audio_i2sin4", "a1sys_hp_sel", 0x10, 3),
+ GATE(CLK_AUDIO_I2SO1, "audio_i2so1", "a1sys_hp_sel", 0x10, 6),
+ GATE(CLK_AUDIO_I2SO2, "audio_i2so2", "a1sys_hp_sel", 0x10, 7),
+ GATE(CLK_AUDIO_I2SO3, "audio_i2so3", "a1sys_hp_sel", 0x10, 8),
+ GATE(CLK_AUDIO_I2SO4, "audio_i2so4", "a1sys_hp_sel", 0x10, 9),
+ GATE(CLK_AUDIO_ASRCI1, "audio_asrci1", "asm_h_sel", 0x10, 12),
+ GATE(CLK_AUDIO_ASRCI2, "audio_asrci2", "asm_h_sel", 0x10, 13),
+ GATE(CLK_AUDIO_ASRCO1, "audio_asrco1", "asm_h_sel", 0x10, 14),
+ GATE(CLK_AUDIO_ASRCO2, "audio_asrco2", "asm_h_sel", 0x10, 15),
+ GATE(CLK_AUDIO_INTDIR, "audio_intdir", "intdir_sel", 0x10, 20),
+ GATE(CLK_AUDIO_A1SYS, "audio_a1sys", "a1sys_hp_sel", 0x10, 21),
+ GATE(CLK_AUDIO_A2SYS, "audio_a2sys", "a2sys_hp_sel", 0x10, 22),
+ GATE(CLK_AUDIO_AFE_CONN, "audio_afe_conn", "a1sys_hp_sel", 0x10, 23),
+ /* AUDIO2 */
+ GATE(CLK_AUDIO_UL1, "audio_ul1", "a1sys_hp_sel", 0x14, 0),
+ GATE(CLK_AUDIO_UL2, "audio_ul2", "a1sys_hp_sel", 0x14, 1),
+ GATE(CLK_AUDIO_UL3, "audio_ul3", "a1sys_hp_sel", 0x14, 2),
+ GATE(CLK_AUDIO_UL4, "audio_ul4", "a1sys_hp_sel", 0x14, 3),
+ GATE(CLK_AUDIO_UL5, "audio_ul5", "a1sys_hp_sel", 0x14, 4),
+ GATE(CLK_AUDIO_UL6, "audio_ul6", "a1sys_hp_sel", 0x14, 5),
+ GATE(CLK_AUDIO_DL1, "audio_dl1", "a1sys_hp_sel", 0x14, 6),
+ GATE(CLK_AUDIO_DL2, "audio_dl2", "a1sys_hp_sel", 0x14, 7),
+ GATE(CLK_AUDIO_DL3, "audio_dl3", "a1sys_hp_sel", 0x14, 8),
+ GATE(CLK_AUDIO_DL4, "audio_dl4", "a1sys_hp_sel", 0x14, 9),
+ GATE(CLK_AUDIO_DL5, "audio_dl5", "a1sys_hp_sel", 0x14, 10),
+ GATE(CLK_AUDIO_DL6, "audio_dl6", "a1sys_hp_sel", 0x14, 11),
+ GATE(CLK_AUDIO_DLMCH, "audio_dlmch", "a1sys_hp_sel", 0x14, 12),
+ GATE(CLK_AUDIO_ARB1, "audio_arb1", "a1sys_hp_sel", 0x14, 13),
+ GATE(CLK_AUDIO_AWB, "audio_awb", "a1sys_hp_sel", 0x14, 14),
+ GATE(CLK_AUDIO_AWB2, "audio_awb2", "a1sys_hp_sel", 0x14, 15),
+ GATE(CLK_AUDIO_DAI, "audio_dai", "a1sys_hp_sel", 0x14, 16),
+ GATE(CLK_AUDIO_MOD, "audio_mod", "a1sys_hp_sel", 0x14, 17),
+ /* AUDIO3 */
+ GATE(CLK_AUDIO_ASRCI3, "audio_asrci3", "asm_h_sel", 0x634, 2),
+ GATE(CLK_AUDIO_ASRCI4, "audio_asrci4", "asm_h_sel", 0x634, 3),
+ GATE(CLK_AUDIO_ASRCO3, "audio_asrco3", "asm_h_sel", 0x634, 6),
+ GATE(CLK_AUDIO_ASRCO4, "audio_asrco4", "asm_h_sel", 0x634, 7),
+ GATE(CLK_AUDIO_MEM_ASRC1, "audio_mem_asrc1", "asm_h_sel", 0x634, 10),
+ GATE(CLK_AUDIO_MEM_ASRC2, "audio_mem_asrc2", "asm_h_sel", 0x634, 11),
+ GATE(CLK_AUDIO_MEM_ASRC3, "audio_mem_asrc3", "asm_h_sel", 0x634, 12),
+ GATE(CLK_AUDIO_MEM_ASRC4, "audio_mem_asrc4", "asm_h_sel", 0x634, 13),
+ GATE(CLK_AUDIO_MEM_ASRC5, "audio_mem_asrc5", "asm_h_sel", 0x634, 14),
+};
+
+static struct mt_clk_def clk_def = {
+ .gates_def = gates_clk,
+ .num_gates = nitems(gates_clk),
+};
+
+static int
+audio_clk_probe(device_t dev)
+{
+ return (mt_clk_probe(dev, compat_data, "Mediatek mt7622 audio clocks"));
+}
+
+static int
+audio_clk_attach(device_t dev)
+{
+ struct mt_clk_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->clk_def = &clk_def;
+
+ return (mt_clk_attach(dev));
+}
+
+static device_method_t mt7622_audio_methods[] = {
+ DEVMETHOD(device_probe, audio_clk_probe),
+ DEVMETHOD(device_attach, audio_clk_attach),
+ DEVMETHOD(device_detach, mt_clk_detach),
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_2(mt7622_audio, mt7622_audio_driver, mt7622_audio_methods,
+ sizeof(struct mt_clk_softc), mt_clk_driver, simplebus_driver);
+
+EARLY_DRIVER_MODULE(mt7622_audio, simplebus, mt7622_audio_driver, NULL, NULL,
+ BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE + 4);
+MODULE_VERSION(mt7622_audio, 1);
diff --git a/sys/arm64/mediatek/mt7622_clk_eth.c b/sys/arm64/mediatek/mt7622_clk_eth.c
new file mode 100644
--- /dev/null
+++ b/sys/arm64/mediatek/mt7622_clk_eth.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2025, 2026 Martin Filla <freebsd@sysctl.cz>
+ * Copyright (c) 2025 Michal Meloun <mmel@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <dev/fdt/simplebus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dt-bindings/clock/mt7622-clk.h>
+#include <dev/clk/clk_gate.h>
+
+#include "mt_clk.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"mediatek,mt7622-ethsys", 1},
+ {NULL, 0},
+};
+
+static struct clk_gate_def gates_clk[] = {
+ /* eth */
+ GATE(CLK_ETH_HSDMA_EN, "eth_hsdma_en", "eth_sel", 0x30, 5),
+ GATE(CLK_ETH_ESW_EN, "eth_esw_en", "eth_500m", 0x30, 6),
+ GATE(CLK_ETH_GP2_EN, "eth_gp2_en", "txclk_src_pre", 0x30, 7),
+ GATE(CLK_ETH_GP1_EN, "eth_gp1_en", "txclk_src_pre", 0x30, 8),
+ GATE(CLK_ETH_GP0_EN, "eth_gp0_en", "txclk_src_pre", 0x30, 9),
+};
+
+static struct mt_clk_def clk_def = {
+ .gates_def = gates_clk,
+ .num_gates = nitems(gates_clk),
+};
+
+static const uint16_t reset_offset[] = { 0x34 };
+static const struct mt_clk_reset_def reset_def = {
+ .reset_offset = reset_offset,
+ .reset_num = nitems(reset_offset),
+};
+
+static int
+eth_clk_probe(device_t dev)
+{
+ return (mt_clk_probe(dev, compat_data,
+ "Mediatek mt7622 ethernet clocks"));
+}
+
+static int
+eth_clk_attach(device_t dev)
+{
+ struct mt_clk_softc *sc;
+ sc = device_get_softc(dev);
+
+ sc->clk_def = &clk_def;
+ sc->reset_def = &reset_def;
+
+ return (mt_clk_attach(dev));
+}
+
+static device_method_t mt7622_eth_methods[] = {
+ DEVMETHOD(device_probe, eth_clk_probe),
+ DEVMETHOD(device_attach, eth_clk_attach),
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(mt7622_eth, mt7622_eth_driver, mt7622_eth_methods,
+sizeof(struct mt_clk_softc), mt_clk_driver);
+
+EARLY_DRIVER_MODULE(mt7622_eth, simplebus, mt7622_eth_driver, NULL, NULL,
+ BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE + 5);
diff --git a/sys/arm64/mediatek/mt7622_clk_infracfg.c b/sys/arm64/mediatek/mt7622_clk_infracfg.c
new file mode 100644
--- /dev/null
+++ b/sys/arm64/mediatek/mt7622_clk_infracfg.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2025, 2026 Martin Filla <freebsd@sysctl.cz>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <dev/fdt/simplebus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dt-bindings/clock/mt7622-clk.h>
+#include <dev/clk/clk_mux.h>
+#include <dev/clk/clk_gate.h>
+
+#include "mt_clk.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"mediatek,mt7622-infracfg", 1},
+ {NULL, 0},
+};
+
+PLIST(infra_mux1_parents) = {
+ "clkxtal",
+ "armpll",
+ "main_core_en",
+ "armpll"
+};
+
+static struct clk_gate_def gates_clk[] = {
+ GATE(CLK_INFRA_DBGCLK_PD, "infra_dbgclk_pd", "axi_sel", 0x44, 0),
+ GATE(CLK_INFRA_TRNG, "trng_ck", "axi_sel", 0x44, 2),
+ GATE(CLK_INFRA_AUDIO_PD, "infra_audio_pd", "aud_intbus_sel", 0x44, 5),
+ GATE(CLK_INFRA_IRRX_PD, "infra_irrx_pd", "irrx_sel", 0x44, 16),
+ GATE(CLK_INFRA_APXGPT_PD, "infra_apxgpt_pd", "f10m_ref_sel", 0x44, 18),
+ GATE(CLK_INFRA_PMIC_PD, "infra_pmic_pd", "pmicspi_sel", 0x44, 22),
+};
+
+static struct clk_mux_def muxes_clk[] = {
+ MUX0(CLK_INFRA_MUX1_SEL, "infra_mux1_sel", infra_mux1_parents, 0x000, 2, 2),
+};
+
+static struct mt_clk_def clk_def = {
+ .gates_def = gates_clk,
+ .num_gates = nitems(gates_clk),
+ .muxes_def = muxes_clk,
+ .num_muxes = nitems(muxes_clk),
+};
+
+static const uint16_t reset_offset[] = { 0x30 };
+static const struct mt_clk_reset_def reset_def = {
+ .reset_offset = reset_offset,
+ .reset_num = nitems(reset_offset),
+};
+
+static int
+infracfg_clk_probe(device_t dev)
+{
+ return (mt_clk_probe(dev, compat_data, "Mediatek infracfg clocks"));
+}
+
+static int
+infracfg_clk_attach(device_t dev)
+{
+ struct mt_clk_softc *sc;
+ sc = device_get_softc(dev);
+
+ sc->clk_def = &clk_def;
+ sc->reset_def = &reset_def;
+
+ return (mt_clk_attach(dev));
+}
+
+static device_method_t mt7622_infracfg_methods[] = {
+ DEVMETHOD(device_probe, infracfg_clk_probe),
+ DEVMETHOD(device_attach, infracfg_clk_attach),
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(mt7622_infracfg, mt7622_infracfg_driver, mt7622_infracfg_methods,
+sizeof(struct mt_clk_softc), mt_clk_driver);
+
+EARLY_DRIVER_MODULE(mt7622_infracfg, simplebus, mt7622_infracfg_driver, NULL, NULL,
+ BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE + 4);
diff --git a/sys/arm64/mediatek/mt7622_clk_pciesys.c b/sys/arm64/mediatek/mt7622_clk_pciesys.c
new file mode 100644
--- /dev/null
+++ b/sys/arm64/mediatek/mt7622_clk_pciesys.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2025, 2026 Martin Filla <freebsd@sysctl.cz>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <dev/fdt/simplebus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dt-bindings/clock/mt7622-clk.h>
+#include <dev/clk/clk_gate.h>
+
+#include "mt_clk.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"mediatek,mt7622-pciesys", 1},
+ {NULL, 0},
+};
+
+static struct clk_gate_def gates_pcie_clk[] = {
+ GATE(CLK_PCIE_P1_AUX_EN, "pcie_p1_aux_en", "p1_1mhz", 0x30, 12),
+ GATE(CLK_PCIE_P1_OBFF_EN, "pcie_p1_obff_en", "free_run_4mhz", 0x30, 13),
+ GATE(CLK_PCIE_P1_AHB_EN, "pcie_p1_ahb_en", "axi_sel", 0x30, 14),
+ GATE(CLK_PCIE_P1_AXI_EN, "pcie_p1_axi_en", "hif_sel", 0x30, 15),
+ GATE(CLK_PCIE_P1_MAC_EN, "pcie_p1_mac_en", "pcie1_mac_en", 0x30, 16),
+ GATE(CLK_PCIE_P1_PIPE_EN, "pcie_p1_pipe_en", "pcie1_pipe_en", 0x30, 17),
+ GATE(CLK_PCIE_P0_AUX_EN, "pcie_p0_aux_en", "p0_1mhz", 0x30, 18),
+ GATE(CLK_PCIE_P0_OBFF_EN, "pcie_p0_obff_en", "free_run_4mhz", 0x30, 19),
+ GATE(CLK_PCIE_P0_AHB_EN, "pcie_p0_ahb_en", "axi_sel", 0x30, 20),
+ GATE(CLK_PCIE_P0_AXI_EN, "pcie_p0_axi_en", "hif_sel", 0x30, 21),
+ GATE(CLK_PCIE_P0_MAC_EN, "pcie_p0_mac_en", "pcie0_mac_en", 0x30, 22),
+ GATE(CLK_PCIE_P0_PIPE_EN, "pcie_p0_pipe_en", "pcie0_pipe_en", 0x30, 23),
+ GATE(CLK_SATA_AHB_EN, "sata_ahb_en", "axi_sel", 0x30, 26),
+ GATE(CLK_SATA_AXI_EN, "sata_axi_en", "hif_sel", 0x30, 27),
+ GATE(CLK_SATA_ASIC_EN, "sata_asic_en", "sata_asic", 0x30, 28),
+ GATE(CLK_SATA_RBC_EN, "sata_rbc_en", "sata_rbc", 0x30, 29),
+ GATE(CLK_SATA_PM_EN, "sata_pm_en", "univpll2_d4", 0x30, 30),
+};
+
+static struct mt_clk_def clk_pcie_def = {
+ .gates_def = gates_pcie_clk,
+ .num_gates = nitems(gates_pcie_clk),
+};
+
+static const uint16_t reset_offset[] = { 0x34 };
+static const struct mt_clk_reset_def reset_def = {
+ .reset_offset = reset_offset,
+ .reset_num = nitems(reset_offset),
+};
+
+static int
+mt7622_pciesys_clk_probe(device_t dev)
+{
+ return (mt_clk_probe(dev, compat_data, "Mediatek mt7622 pciesys clocks"));
+}
+
+static int
+mt7622_pciesys_clk_attach(device_t dev)
+{
+ struct mt_clk_softc *sc;
+ sc = device_get_softc(dev);
+
+ sc->clk_def = &clk_pcie_def;
+ sc->reset_def = &reset_def;
+
+ return (mt_clk_attach(dev));
+}
+
+static device_method_t mt7622_pciesys_clk_methods[] = {
+ DEVMETHOD(device_probe, mt7622_pciesys_clk_probe),
+ DEVMETHOD(device_attach, mt7622_pciesys_clk_attach),
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(mt7622_pciesys, mt7622_pciesys_driver, mt7622_pciesys_clk_methods,
+sizeof(struct mt_clk_softc), mt_clk_driver);
+
+EARLY_DRIVER_MODULE(mt7622_pciesys, simplebus, mt7622_pciesys_driver, NULL, NULL,
+ BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE + 5);
diff --git a/sys/arm64/mediatek/mt7622_clk_pericfg.c b/sys/arm64/mediatek/mt7622_clk_pericfg.c
new file mode 100644
--- /dev/null
+++ b/sys/arm64/mediatek/mt7622_clk_pericfg.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2025, 2026 Martin Filla <freebsd@sysctl.cz>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <dev/fdt/simplebus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dt-bindings/clock/mt7622-clk.h>
+#include <dev/clk/clk_mux.h>
+#include <dev/clk/clk_gate.h>
+
+#include "mt_clk.h"
+
+#define PERICFG_CG0 0x10
+#define PERICFG_CG1 0x14
+
+static struct ofw_compat_data compat_data[] = {
+ {"mediatek,mt7622-pericfg", 1},
+ {NULL, 0},
+};
+
+PLIST(peribus_ck_parents) = {
+ "syspll1_d8",
+ "syspll1_d4"
+};
+
+static struct clk_gate_def gates_clk[] = {
+ GATE(CLK_PERI_THERM_PD, "peri_therm_pd", "axi_sel", PERICFG_CG0, 1),
+ GATE(CLK_PERI_PWM1_PD, "peri_pwm1_pd", "clkxtal", PERICFG_CG0, 2),
+ GATE(CLK_PERI_PWM2_PD, "peri_pwm2_pd", "clkxtal", PERICFG_CG0, 3),
+ GATE(CLK_PERI_PWM3_PD, "peri_pwm3_pd", "clkxtal", PERICFG_CG0, 4),
+ GATE(CLK_PERI_PWM4_PD, "peri_pwm4_pd", "clkxtal", PERICFG_CG0, 5),
+ GATE(CLK_PERI_PWM5_PD, "peri_pwm5_pd", "clkxtal", PERICFG_CG0, 6),
+ GATE(CLK_PERI_PWM6_PD, "peri_pwm6_pd", "clkxtal", PERICFG_CG0, 7),
+ GATE(CLK_PERI_PWM7_PD, "peri_pwm7_pd", "clkxtal", PERICFG_CG0, 8),
+ GATE(CLK_PERI_PWM_PD, "peri_pwm_pd", "clkxtal", PERICFG_CG0, 9),
+ GATE(CLK_PERI_AP_DMA_PD, "peri_ap_dma_pd", "axi_sel", PERICFG_CG0, 12),
+ GATE(CLK_PERI_MSDC30_0_PD, "peri_msdc30_0", "msdc30_0_sel", PERICFG_CG0, 13),
+ GATE(CLK_PERI_MSDC30_1_PD, "peri_msdc30_1", "msdc30_1_sel", PERICFG_CG0, 14),
+ GATE(CLK_PERI_UART0_PD, "peri_uart0_pd", "axi_sel", PERICFG_CG0, 17),
+ GATE(CLK_PERI_UART1_PD, "peri_uart1_pd", "axi_sel", PERICFG_CG0, 18),
+ GATE(CLK_PERI_UART2_PD, "peri_uart2_pd", "axi_sel", PERICFG_CG0, 19),
+ GATE(CLK_PERI_UART3_PD, "peri_uart3_pd", "axi_sel", PERICFG_CG0, 20),
+ GATE(CLK_PERI_UART4_PD, "peri_uart4_pd", "axi_sel", PERICFG_CG0, 21),
+ GATE(CLK_PERI_BTIF_PD, "peri_btif_pd", "axi_sel", PERICFG_CG0, 22),
+ GATE(CLK_PERI_I2C0_PD, "peri_i2c0_pd", "axi_sel", PERICFG_CG0, 23),
+ GATE(CLK_PERI_I2C1_PD, "peri_i2c1_pd", "axi_sel", PERICFG_CG0, 24),
+ GATE(CLK_PERI_I2C2_PD, "peri_i2c2_pd", "axi_sel", PERICFG_CG0, 25),
+ GATE(CLK_PERI_SPI1_PD, "peri_spi1_pd", "spi1_sel", PERICFG_CG0, 26),
+ GATE(CLK_PERI_AUXADC_PD, "peri_auxadc_pd", "clkxtal", PERICFG_CG0, 27),
+ GATE(CLK_PERI_SPI0_PD, "peri_spi0_pd", "spi0_sel", PERICFG_CG0, 28),
+ GATE(CLK_PERI_SNFI_PD, "peri_snfi_pd", "spinfi_infra_bclk_sel", PERICFG_CG0, 29),
+ GATE(CLK_PERI_NFI_PD, "peri_nfi_pd", "axi_sel", PERICFG_CG0, 30),
+ GATE(CLK_PERI_NFIECC_PD, "peri_nfiecc_pd", "axi_sel", PERICFG_CG0, 31),
+
+ GATE(CLK_PERI_FLASH_PD, "peri_flash_pd", "flash_sel", PERICFG_CG1, 1),
+ GATE(CLK_PERI_IRTX_PD, "peri_irtx_pd", "irtx_sel", PERICFG_CG1, 2),
+};
+
+static struct clk_mux_def muxes_clk[] = {
+ MUX0(0, "peribus_ck_sel_mux", peribus_ck_parents, 0x05C, 0 , 1),
+};
+
+static struct mt_clk_def clk_def = {
+ .gates_def = gates_clk,
+ .num_gates = nitems(gates_clk),
+ .muxes_def = muxes_clk,
+ .num_muxes = nitems(muxes_clk),
+};
+
+static const uint16_t reset_offset[] = { 0x0, 0x4 };
+static const struct mt_clk_reset_def reset_def = {
+ .reset_offset = reset_offset,
+ .reset_num = nitems(reset_offset),
+};
+
+static int
+pericfg_clk_probe(device_t dev)
+{
+ return (mt_clk_probe(dev, compat_data, "Mediatek pericfg clocks"));
+}
+
+static int
+pericfg_clk_attach(device_t dev)
+{
+ struct mt_clk_softc *sc;
+ sc = device_get_softc(dev);
+
+ sc->clk_def = &clk_def;
+ sc->reset_def = &reset_def;
+
+ return (mt_clk_attach(dev));
+}
+
+static device_method_t mt7622_pericfg_methods[] = {
+ DEVMETHOD(device_probe, pericfg_clk_probe),
+ DEVMETHOD(device_attach, pericfg_clk_attach),
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(mt7622_pericfg, mt7622_pericfg_driver,
+mt7622_pericfg_methods, sizeof(struct mt_clk_softc), mt_clk_driver);
+
+EARLY_DRIVER_MODULE(mt7622_pericfg, simplebus, mt7622_pericfg_driver,
+ NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE + 3);
diff --git a/sys/arm64/mediatek/mt7622_clk_sgmii.c b/sys/arm64/mediatek/mt7622_clk_sgmii.c
new file mode 100644
--- /dev/null
+++ b/sys/arm64/mediatek/mt7622_clk_sgmii.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2025, 2026 Martin Filla <freebsd@sysctl.cz>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <dev/fdt/simplebus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dt-bindings/clock/mt7622-clk.h>
+#include <dev/clk/clk_gate.h>
+
+#include "mt_clk.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"mediatek,mt7622-sgmiisys", 1},
+ {NULL, 0},
+};
+
+static struct clk_gate_def gates_clk[] = {
+ /* sgmii */
+ GATE(CLK_SGMII_TX250M_EN, "sgmii_tx250m_en", "ssusb_tx250m", 0xE4, 2),
+ GATE(CLK_SGMII_RX250M_EN, "sgmii_rx250m_en", "ssusb_eq_rx250m", 0xE4, 3),
+ GATE(CLK_SGMII_CDR_REF, "sgmii_cdr_ref", "ssusb_cdr_ref", 0xE4, 4),
+ GATE(CLK_SGMII_CDR_FB, "sgmii_cdr_fb", "ssusb_cdr_fb", 0xE4, 5),
+};
+
+static struct mt_clk_def clk_def = {
+ .gates_def = gates_clk,
+ .num_gates = nitems(gates_clk),
+};
+
+static int
+sgmii_clk_probe(device_t dev)
+{
+ return (mt_clk_probe(dev, compat_data, "Mediatek mt7622 sgmii clocks"));
+}
+
+static int
+sgmii_clk_attach(device_t dev)
+{
+ struct mt_clk_softc *sc;
+ sc = device_get_softc(dev);
+
+ sc->clk_def = &clk_def;
+ return (mt_clk_attach(dev));
+}
+
+static device_method_t mt7622_sgmii_methods[] = {
+ DEVMETHOD(device_probe, sgmii_clk_probe),
+ DEVMETHOD(device_attach, sgmii_clk_attach),
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(mt7622_sgmii, mt7622_sgmii_driver, mt7622_sgmii_methods,
+sizeof(struct mt_clk_softc), mt_clk_driver);
+
+EARLY_DRIVER_MODULE(mt7622_sgmii, simplebus, mt7622_sgmii_driver, NULL, NULL,
+ BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE + 5);
diff --git a/sys/arm64/mediatek/mt7622_clk_ssusbsys.c b/sys/arm64/mediatek/mt7622_clk_ssusbsys.c
new file mode 100644
--- /dev/null
+++ b/sys/arm64/mediatek/mt7622_clk_ssusbsys.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2025, 2026 Martin Filla <freebsd@sysctl.cz>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <dev/fdt/simplebus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dt-bindings/clock/mt7622-clk.h>
+#include <dev/clk/clk_gate.h>
+
+#include "mt_clk.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"mediatek,mt7622-ssusbsys", 1},
+ {NULL, 0},
+};
+
+static struct clk_gate_def gates_ssusb_clk[] = {
+ GATE(CLK_SSUSB_U2_PHY_1P_EN, "ssusb_u2_phy_1p", "to_u2_phy_1p", 0x30, 0),
+ GATE(CLK_SSUSB_U2_PHY_EN, "ssusb_u2_phy_en", "to_u2_phy", 0x30, 1),
+ GATE(CLK_SSUSB_REF_EN, "ssusb_ref_en", "to_usb3_ref", 0x30, 5),
+ GATE(CLK_SSUSB_SYS_EN, "ssusb_sys_en", "to_usb3_sys", 0x30, 6),
+ GATE(CLK_SSUSB_MCU_EN, "ssusb_mcu_en", "axi_sel", 0x30, 7),
+ GATE(CLK_SSUSB_DMA_EN, "ssusb_dma_en", "hif_sel", 0x30, 8),
+};
+
+static struct mt_clk_def clk_ssusb_def = {
+ .gates_def = gates_ssusb_clk,
+ .num_gates = nitems(gates_ssusb_clk),
+};
+
+static const uint16_t reset_offset[] = { 0x34 };
+static const struct mt_clk_reset_def reset_def = {
+ .reset_offset = reset_offset,
+ .reset_num = nitems(reset_offset),
+};
+
+static int
+mt7622_ssusbsys_clk_probe(device_t dev)
+{
+ return (mt_clk_probe(dev, compat_data, "Mediatek mt7622 ssusbsys clocks"));
+}
+
+static int
+mt7622_ssusbsys_clk_attach(device_t dev)
+{
+ struct mt_clk_softc *sc;
+ sc = device_get_softc(dev);
+
+ sc->clk_def = &clk_ssusb_def;
+ sc->reset_def = &reset_def;
+
+ return (mt_clk_attach(dev));
+}
+
+static device_method_t mt7622_ssusbsys_methods[] = {
+ DEVMETHOD(device_probe, mt7622_ssusbsys_clk_probe),
+ DEVMETHOD(device_attach, mt7622_ssusbsys_clk_attach),
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(mt7622_ssusbsys, mt7622_ssusbsys_driver, mt7622_ssusbsys_methods,
+sizeof(struct mt_clk_softc), mt_clk_driver);
+
+EARLY_DRIVER_MODULE(mt7622_ssusbsys, simplebus, mt7622_ssusbsys_driver, NULL, NULL,
+ BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE + 5);
diff --git a/sys/arm64/mediatek/mt7622_clk_topckgen.c b/sys/arm64/mediatek/mt7622_clk_topckgen.c
new file mode 100644
--- /dev/null
+++ b/sys/arm64/mediatek/mt7622_clk_topckgen.c
@@ -0,0 +1,521 @@
+/*
+ * Copyright (c) 2025, 2026 Martin Filla <freebsd@sysctl.cz>
+ * Copyright (c) 2025 Michal Meloun <mmel@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <dev/fdt/simplebus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dt-bindings/clock/mt7622-clk.h>
+#include <dev/clk/clk_fixed.h>
+#include <dev/clk/clk_div.h>
+#include <dev/clk/clk_mux.h>
+#include <dev/clk/clk_gate.h>
+
+#include "mt_clk.h"
+
+#define CLK_CFG_0 0x040
+#define CLK_CFG_1 0x050
+#define CLK_CFG_2 0x060
+#define CLK_CFG_3 0x070
+#define CLK_CFG_4 0x080
+#define CLK_CFG_5 0x090
+#define CLK_CFG_6 0x0A0
+#define CLK_CFG_7 0x0B0
+#define CLK_AUDDIV_0 0x120
+
+static struct ofw_compat_data compat_data[] = {
+ {"mediatek,mt7622-topckgen", 1},
+ {NULL, 0},
+};
+
+/* Parent lists */
+PLIST(eth_ck_parents) = {
+ "clkxtal",
+ "syspll1_d2",
+ "univpll1_d2",
+ "syspll1_d4",
+ "univpll_d5",
+ "sgmiipll_d2",
+ "univpll_d7",
+ "dmpll_ck"};
+
+PLIST(ddrphycfg_ck_parents) = {
+ "clkxtal",
+ "syspll1_d8"
+};
+
+PLIST(mem_ck_parents) = {
+ "clkxtal",
+ "dmpll_ck"
+};
+
+PLIST(axi_ck_parents) = {
+ "clkxtal",
+ "syspll1_d2",
+ "syspll_d5",
+ "syspll1_d4",
+ "univpll_d5",
+ "univpll2_d2",
+ "univpll_d7",
+ "dmpll_ck"
+};
+
+PLIST(pwm_ck_parents) = {
+ "clkxtal",
+ "univpll2_d4",
+ "univpll3_d2",
+ "univpll1_d4"
+};
+
+PLIST(f10m_ref_ck_parents) = {
+ "clkxtal",
+ "syspll4_d16"
+};
+
+PLIST(nfi_infra_ck_parents) = {
+ "clkxtal",
+ "clkxtal",
+ "clkxtal",
+ "clkxtal",
+ "clkxtal",
+ "clkxtal",
+ "clkxtal",
+ "clkxtal",
+ "univpll2_d8",
+ "syspll1_d8",
+ "univpll1_d8",
+ "syspll4_d2",
+ "univpll2_d4",
+ "univpll3_d2",
+ "syspll1_d4",
+ "syspll_d7"
+};
+
+PLIST(flash_ck_parents) = {
+ "clkxtal",
+ "univpll_div80_d4",
+ "syspll2_d8",
+ "syspll3_d4",
+ "univpll3_d4",
+ "univpll1_d8",
+ "syspll2_d4",
+ "univpll2_d4"
+};
+
+PLIST(uart_ck_parents) = {
+ "clkxtal",
+ "univpll2_d8"
+};
+
+PLIST(spi0_ck_parents) = {
+ "clkxtal",
+ "syspll3_d2",
+ "clkxtal",
+ "syspll2_d4",
+ "syspll4_d2",
+ "univpll2_d4",
+ "univpll1_d8",
+ "clkxtal"
+};
+
+PLIST(spi1_ck_parents) = {
+ "clkxtal",
+ "syspll3_d2",
+ "clkxtal",
+ "syspll2_d4",
+ "syspll4_d2",
+ "univpll2_d4",
+ "univpll1_d8",
+ "clkxtal"
+};
+
+PLIST(msdc50_0_ck_parents) = {
+ "clkxtal",
+ "univpll2_d8",
+ "univpll2_d4",
+ "syspll_d7",
+ "univpll2_d2",
+ "univpll_d5",
+ "univpll1_d2",
+ "univpll_d3"
+};
+
+PLIST(msdc30_0_ck_parents) = {
+ "clkxtal",
+ "univpll2_d16",
+ "univ48m_ck",
+ "syspll2_d4",
+ "univpll2_d4",
+ "syspll_d7",
+ "syspll2_d2",
+ "univpll2_d2"
+};
+
+PLIST(msdc30_1_ck_parents) = {
+ "clkxtal",
+ "univpll2_d16",
+ "univ48m_ck",
+ "syspll2_d4",
+ "univpll2_d4",
+ "syspll_d7",
+ "syspll2_d2",
+ "univpll2_d2"
+};
+
+PLIST(a1sys_hp_ck_parents) = {
+ "clkxtal",
+ "aud1pll_ck",
+ "aud2pll_ck",
+ "clkxtal"
+};
+
+PLIST(a2sys_hp_ck_parents) = {
+ "clkxtal",
+ "aud1pll_ck",
+ "aud2pll_ck",
+ "clkxtal"
+};
+
+PLIST(intdir_ck_parents) = {
+ "clkxtal",
+ "syspll_d2",
+ "univpll_d2",
+ "sgmiipll_ck"
+};
+
+PLIST(aud_intbus_ck_parents) = {
+ "clkxtal",
+ "syspll1_d4",
+ "syspll4_d2",
+ "syspll3_d2"
+};
+
+PLIST(pmicspi_ck_parents) = {
+ "clkxtal",
+ "syspll1_d8",
+ "syspll3_d4",
+ "syspll1_d16",
+ "univpll3_d4",
+ "univpll2_d16",
+ "dmpll_d8"
+};
+
+PLIST(scp_ck_parents) = {
+ "clkxtal",
+ "syspll1_d8",
+ "univpll2_d2",
+ "univpll2_d4"
+};
+
+PLIST(atb_ck_parents) = {
+ "clkxtal",
+ "syspll1_d2",
+ "syspll_d5",
+ "dmpll_ck"
+};
+
+PLIST(hif_ck_parents) = {
+ "clkxtal",
+ "syspll1_d2",
+ "univpll1_d2",
+ "syspll1_d4",
+ "univpll_d5",
+ "sgmiipll_d2",
+ "univpll_d7",
+ "dmpll_ck"
+};
+
+PLIST(audio_ck_parents) = {
+ "clkxtal",
+ "syspll3_d4",
+ "syspll4_d4",
+ "univpll1_d16"
+};
+
+PLIST(usb20_ck_parents) = {
+ "clkxtal",
+ "univpll3_d4",
+ "syspll1_d8",
+ "clkxtal"
+};
+
+PLIST(aud1_ck_parents) = {
+ "clkxtal",
+ "aud1pll_ck"
+};
+
+PLIST(aud2_ck_parents) = {
+ "clkxtal",
+ "aud2pll_ck"
+};
+
+PLIST(irrx_ck_parents) = {
+ "clkxtal",
+ "syspll4_d16"
+};
+
+PLIST(irtx_ck_parents) = {
+ "clkxtal",
+ "syspll4_d16"
+};
+
+PLIST(asm_l_ck_parents) = {
+ "clkxtal",
+ "syspll_d5",
+ "univpll2_d2",
+ "univpll2_d4"
+};
+
+PLIST(asm_m_ck_parents) = {
+ "clkxtal",
+ "syspll_d5",
+ "univpll2_d2",
+ "univpll2_d4"
+};
+
+PLIST(asm_h_ck_parents) = {
+ "clkxtal",
+ "syspll_d5",
+ "univpll2_d2",
+ "univpll2_d4"
+};
+
+PLIST(apll_ck_parents) = {
+ "aud1_sel",
+ "aud2_sel"
+};
+
+PLIST(apll2_ck_parents) = {
+ "aud2_sel",
+ "aud1_sel"
+};
+
+static struct clk_fixed_def fixed_clk[] = {
+ FRATE(CLK_TOP_TO_U2_PHY, "to_u2_phy", 31250000),
+ FRATE(CLK_TOP_TO_U2_PHY_1P, "to_u2_phy_1p", 31250000),
+ FRATE(CLK_TOP_PCIE0_PIPE_EN, "pcie0_pipe_en", 125000000),
+ FRATE(CLK_TOP_PCIE1_PIPE_EN, "pcie1_pipe_en", 125000000),
+ FRATE(CLK_TOP_SSUSB_TX250M, "ssusb_tx250m", 250000000),
+ FRATE(CLK_TOP_SSUSB_EQ_RX250M, "ssusb_eq_rx250m", 250000000),
+ FRATE(CLK_TOP_SSUSB_CDR_REF, "ssusb_cdr_ref", 33333333),
+ FRATE(CLK_TOP_SSUSB_CDR_FB, "ssusb_cdr_fb", 50000000),
+ FRATE(CLK_TOP_SATA_ASIC, "sata_asic", 50000000),
+ FRATE(CLK_TOP_SATA_RBC, "sata_rbc", 50000000),
+
+ FFACT(CLK_TOP_TO_USB3_SYS, "to_usb3_sys", "eth1pll", 1, 4),
+ FFACT(CLK_TOP_P1_1MHZ, "p1_1mhz", "eth1pll", 1, 500),
+ FFACT(CLK_TOP_4MHZ, "free_run_4mhz", "eth1pll", 1, 125),
+ FFACT(CLK_TOP_P0_1MHZ, "p0_1mhz", "eth1pll", 1, 500),
+ FFACT(CLK_TOP_TXCLK_SRC_PRE, "txclk_src_pre", "sgmiipll_d2", 1, 1),
+ FFACT(CLK_TOP_RTC, "rtc", "clkxtal", 1, 1024),
+ FFACT(CLK_TOP_MEMPLL, "mempll", "clkxtal", 32, 1),
+ FFACT(CLK_TOP_DMPLL, "dmpll_ck", "mempll", 1, 1),
+ FFACT(CLK_TOP_SYSPLL_D2, "syspll_d2", "mainpll", 1, 2),
+ FFACT(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "mainpll", 1, 4),
+ FFACT(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "mainpll", 1, 8),
+ FFACT(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "mainpll", 1, 16),
+ FFACT(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "mainpll", 1, 12),
+ FFACT(CLK_TOP_SYSPLL2_D8, "syspll2_d8", "mainpll", 1, 24),
+ FFACT(CLK_TOP_SYSPLL_D5, "syspll_d5", "mainpll", 1, 5),
+ FFACT(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "mainpll", 1, 10),
+ FFACT(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "mainpll", 1, 20),
+ FFACT(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "mainpll", 1, 14),
+ FFACT(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "mainpll", 1, 28),
+ FFACT(CLK_TOP_SYSPLL4_D16, "syspll4_d16", "mainpll", 1, 112),
+ FFACT(CLK_TOP_UNIVPLL, "univpll", "univ2pll", 1, 2),
+ FFACT(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll", 1, 2),
+ FFACT(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univpll", 1, 4),
+ FFACT(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univpll", 1, 8),
+ FFACT(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univpll", 1, 16),
+ FFACT(CLK_TOP_UNIVPLL1_D16, "univpll1_d16", "univpll", 1, 32),
+ FFACT(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univpll", 1, 6),
+ FFACT(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univpll", 1, 12),
+ FFACT(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univpll", 1, 24),
+ FFACT(CLK_TOP_UNIVPLL2_D16, "univpll2_d16", "univpll", 1, 48),
+ FFACT(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5),
+ FFACT(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univpll", 1, 10),
+ FFACT(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univpll", 1, 20),
+ FFACT(CLK_TOP_UNIVPLL3_D16, "univpll3_d16", "univpll", 1, 80),
+ FFACT(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll", 1, 7),
+ FFACT(CLK_TOP_UNIVPLL_D80_D4, "univpll_div80_d4", "univpll", 1, 320),
+ FFACT(CLK_TOP_SGMIIPLL, "sgmiipll_ck", "sgmipll", 1, 1),
+ FFACT(CLK_TOP_SGMIIPLL_D2, "sgmiipll_d2", "sgmipll", 1, 2),
+
+ FFACT(/*CLK_TOP_SYSPLL_D7*/ 0, "syspll_d7", "mainpll", 1, 8),
+ FFACT(/*CLK_TOP_SYSPLL2_D2 */0, "syspll2_d2", "mainpll", 1, 6),
+ FFACT(/*CLK_TOP_UNIVPLL_D3*/ 0, "univpll_d3", "univpll", 1, 3),
+ FFACT(/*CLK_TOP_SYSPLL1_D16*/ 0, "syspll1_d16", "syspll_d2", 1, 16),
+ FFACT(/*CLK_TOP_DMPLL_D8*/ 0, "dmpll_d8", "dmpll_ck", 1, 8),
+ FFACT(CLK_TOP_AUD1PLL, "aud1pll_ck", "aud1pll", 1, 1),
+ FFACT(CLK_TOP_AUD2PLL, "aud2pll_ck", "aud2pll", 1, 1),
+ FFACT(CLK_TOP_UNIV48M, "univ48m_ck", "univpll", 1, 25),
+ FFACT(CLK_TOP_TO_USB3_REF, "to_usb3_ref", "univpll2_d4", 1, 4),
+ FFACT(CLK_TOP_PCIE1_MAC_EN, "pcie1_mac_en", "univpll1_d4", 1, 1),
+ FFACT(CLK_TOP_PCIE0_MAC_EN, "pcie0_mac_en", "univpll1_d4", 1, 1),
+ FFACT(CLK_TOP_ETH_500M, "eth_500m", "eth1pll", 1, 1),
+ FFACT(CLK_TOP_AUD_I2S2_MCK, "aud_i2s2_mck", "i2s2_mck_sel", 1, 2),
+};
+
+static struct clk_gate_def gates_clk[] = {
+ I_GATE(CLK_TOP_ETH_SEL, "eth_sel", "eth_sel_mux", CLK_CFG_0, 31),
+ I_GATE(CLK_TOP_DDRPHYCFG_SEL, "ddrphycfg_sel", "ddrphycfg_sel_mux", CLK_CFG_0, 23),
+ I_GATE(CLK_TOP_MEM_SEL, "mem_sel", "mem_sel_mux", CLK_CFG_0, 15),
+ I_GATE(CLK_TOP_AXI_SEL, "axi_sel", "axi_sel_mux", CLK_CFG_0, 7),
+
+ I_GATE(CLK_TOP_PWM_SEL, "pwm_sel", "pwm_sel_mux", CLK_CFG_1, 7),
+ I_GATE(CLK_TOP_F10M_REF_SEL, "f10m_ref_sel", "f10m_ref_sel_mux", CLK_CFG_1, 15),
+ I_GATE(CLK_TOP_NFI_INFRA_SEL, "spinfi_infra_bclk_sel", "spinfi_infra_bclk_sel_mux", CLK_CFG_1, 23),
+ I_GATE(CLK_TOP_FLASH_SEL, "flash_sel", "flash_sel_mux", CLK_CFG_1, 31),
+
+ I_GATE(CLK_TOP_UART_SEL, "uart_sel", "uart_sel_mux", CLK_CFG_2, 7),
+ I_GATE(CLK_TOP_SPI0_SEL, "spi0_sel", "spi0_sel_mux", CLK_CFG_2, 15),
+ I_GATE(CLK_TOP_SPI1_SEL, "spi1_sel", "spi1_sel_mux", CLK_CFG_2, 23),
+ I_GATE(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel", "msdc50_0_sel_mux", CLK_CFG_2, 31),
+
+ I_GATE(CLK_TOP_MSDC30_0_SEL, "msdc30_0_sel", "msdc30_0_sel_mux", CLK_CFG_3, 7),
+ I_GATE(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", "msdc30_1_sel_mux", CLK_CFG_3, 15),
+ I_GATE(CLK_TOP_A1SYS_HP_SEL, "a1sys_hp_sel", "a1sys_hp_sel_mux", CLK_CFG_3, 23),
+ I_GATE(CLK_TOP_A2SYS_HP_SEL, "a2sys_hp_sel", "a2sys_hp_sel_mux", CLK_CFG_3, 31),
+
+ I_GATE(CLK_TOP_INTDIR_SEL, "intdir_sel", "intdir_sel_mux", CLK_CFG_4, 7),
+ I_GATE(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel", "aud_intbus_sel_mux", CLK_CFG_4, 15),
+ I_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", "pmicspi_sel_mux", CLK_CFG_4, 23),
+ I_GATE(CLK_TOP_SCP_SEL, "scp_sel", "scp_sel_mux", CLK_CFG_4, 31),
+
+ I_GATE(CLK_TOP_ATB_SEL, "atb_sel", "atb_sel_mux", CLK_CFG_5, 7),
+ I_GATE(CLK_TOP_HIF_SEL, "hif_sel", "hif_sel_mux", CLK_CFG_5, 15),
+ I_GATE(CLK_TOP_AUDIO_SEL, "audio_sel", "audio_sel_mux", CLK_CFG_5, 23),
+ I_GATE(CLK_TOP_U2_SEL, "usb20_sel", "usb20_sel_mux", CLK_CFG_5, 31),
+
+ I_GATE(CLK_TOP_AUD1_SEL, "aud1_sel", "aud1_sel_mux", CLK_CFG_6, 7),
+ I_GATE(CLK_TOP_AUD2_SEL, "aud2_sel", "aud2_sel_mux", CLK_CFG_6, 15),
+ I_GATE(CLK_TOP_IRRX_SEL, "irrx_sel", "irrx_sel_mux", CLK_CFG_6, 23),
+ I_GATE(CLK_TOP_IRTX_SEL, "irtx_sel", "irtx_sel_mux", CLK_CFG_6, 31),
+
+ I_GATE(CLK_TOP_ASM_L_SEL, "asm_l_sel", "asm_l_sel_mux", CLK_CFG_7, 7),
+ I_GATE(CLK_TOP_ASM_M_SEL, "asm_m_sel", "asm_m_sel_mux", CLK_CFG_7, 15),
+ I_GATE(CLK_TOP_ASM_H_SEL, "asm_h_sel", "asm_h_sel_mux", CLK_CFG_7, 23),
+
+ I_GATE(CLK_TOP_APLL1_SEL, "apll1_ck_sel", "apll1_ck_div", CLK_AUDDIV_0, 0),
+ I_GATE(CLK_TOP_APLL2_SEL, "apll2_ck_sel", "apll2_ck_div", CLK_AUDDIV_0, 0),
+ I_GATE(CLK_TOP_I2S0_MCK_SEL, "i2s0_mck_sel", "i2s0_mck_div", CLK_AUDDIV_0, 0),
+ I_GATE(CLK_TOP_I2S1_MCK_SEL, "i2s1_mck_sel", "i2s1_mck_div", CLK_AUDDIV_0, 0),
+ I_GATE(CLK_TOP_I2S2_MCK_SEL, "i2s2_mck_sel", "i2s2_mck_div", CLK_AUDDIV_0, 0),
+ I_GATE(CLK_TOP_I2S3_MCK_SEL, "i2s3_mck_sel", "i2s3_mck_div", CLK_AUDDIV_0, 0),
+};
+
+static struct clk_mux_def muxes_clk[] = {
+ MUX0(0, "eth_sel_mux", eth_ck_parents, CLK_CFG_0, 24, 3),
+ MUX0(0, "ddrphycfg_sel_mux", ddrphycfg_ck_parents, CLK_CFG_0, 16, 1),
+ MUX0(0, "mem_sel_mux", mem_ck_parents, CLK_CFG_0, 8, 1),
+ MUX0(0, "axi_sel_mux", axi_ck_parents, CLK_CFG_0, 0, 3),
+
+ MUX0(0, "pwm_sel_mux", pwm_ck_parents, CLK_CFG_1, 0, 2),
+ MUX0(0, "f10m_ref_sel_mux", f10m_ref_ck_parents, CLK_CFG_1, 8, 1),
+ MUX0(0, "spinfi_infra_bclk_sel_mux", nfi_infra_ck_parents, CLK_CFG_1, 16, 4),
+ MUX0(0, "flash_sel_mux", flash_ck_parents, CLK_CFG_1, 24, 3),
+
+ MUX0(0, "uart_sel_mux", uart_ck_parents, CLK_CFG_2, 0, 1),
+ MUX0(0, "spi0_sel_mux", spi0_ck_parents, CLK_CFG_2, 8, 3),
+ MUX0(0, "spi1_sel_mux", spi1_ck_parents, CLK_CFG_2, 16, 3),
+ MUX0(0, "msdc50_0_sel_mux", msdc50_0_ck_parents, CLK_CFG_2, 24, 3),
+
+ MUX0(0, "msdc30_0_sel_mux", msdc30_0_ck_parents, CLK_CFG_3, 0, 3),
+ MUX0(0, "msdc30_1_sel_mux", msdc30_1_ck_parents, CLK_CFG_3, 8, 3),
+ MUX0(0, "a1sys_hp_sel_mux", a1sys_hp_ck_parents, CLK_CFG_3, 16, 2),
+ MUX0(0, "a2sys_hp_sel_mux", a2sys_hp_ck_parents, CLK_CFG_3, 24, 2),
+
+ MUX0(0, "intdir_sel_mux", intdir_ck_parents, CLK_CFG_4, 0, 2),
+ MUX0(0, "aud_intbus_sel_mux", aud_intbus_ck_parents, CLK_CFG_4, 8, 2),
+ MUX0(0, "pmicspi_sel_mux", pmicspi_ck_parents, CLK_CFG_4, 16, 3),
+ MUX0(0, "scp_sel_mux", scp_ck_parents, CLK_CFG_4, 24, 2),
+
+ MUX0(0, "atb_sel_mux", atb_ck_parents, CLK_CFG_5, 0, 2),
+ MUX0(0, "hif_sel_mux", hif_ck_parents, CLK_CFG_5, 8, 3),
+ MUX0(0, "audio_sel_mux", audio_ck_parents, CLK_CFG_5, 8, 3),
+ MUX0(0, "usb20_sel_mux", usb20_ck_parents, CLK_CFG_5, 24, 2),
+
+ MUX0(0, "aud1_sel_mux", aud1_ck_parents, CLK_CFG_6, 0, 1),
+ MUX0(0, "aud2_sel_mux", aud2_ck_parents, CLK_CFG_6, 8, 1),
+ MUX0(0, "irrx_sel_mux", irrx_ck_parents, CLK_CFG_6, 16, 1),
+ MUX0(0, "irtx_sel_mux", irtx_ck_parents, CLK_CFG_6, 24, 1),
+
+ MUX0(0, "asm_l_sel_mux", asm_l_ck_parents, CLK_CFG_7, 0, 2),
+ MUX0(0, "asm_m_sel_mux", asm_m_ck_parents, CLK_CFG_7, 8, 2),
+ MUX0(0, "asm_h_sel_mux", asm_h_ck_parents, CLK_CFG_7, 16, 2),
+
+ MUX0(0, "apll1_ck_mux", apll_ck_parents, CLK_AUDDIV_0, 6, 1),
+ MUX0(0, "apll2_ck_mux", apll2_ck_parents, CLK_AUDDIV_0, 7, 1),
+ MUX0(0, "i2s0_mck_mux", apll_ck_parents, CLK_AUDDIV_0, 8, 1),
+ MUX0(0, "i2s1_mck_mux", apll_ck_parents, CLK_AUDDIV_0, 9, 1),
+ MUX0(0, "i2s2_mck_mux", apll_ck_parents, CLK_AUDDIV_0, 10, 1),
+ MUX0(0, "i2s3_mck_mux", apll_ck_parents, CLK_AUDDIV_0, 11, 1),
+};
+
+static struct clk_div_def dived_clk[] = {
+ DIV(CLK_TOP_APLL1_DIV, "apll1_ck_div", "apll1_ck_mux",
+ 0x120, 24, 3),
+ DIV(CLK_TOP_APLL2_DIV, "apll2_ck_div", "apll2_ck_mux",
+ 0x120, 28, 3),
+ DIV(CLK_TOP_I2S0_MCK_DIV, "i2s0_mck_div", "i2s0_mck_mux",
+ 0x124, 0, 7),
+ DIV(CLK_TOP_I2S1_MCK_DIV, "i2s1_mck_div", "i2s1_mck_mux",
+ 0x124, 8, 7),
+ DIV(CLK_TOP_I2S2_MCK_DIV, "i2s2_mck_div", "i2s2_mck_mux",
+ 0x124, 16, 7),
+ DIV(CLK_TOP_I2S3_MCK_DIV, "i2s3_mck_div", "i2s3_mck_mux",
+ 0x124, 24, 7),
+ DIV(CLK_TOP_A1SYS_HP_DIV, "a1sys_div", "a1sys_hp_sel",
+ 0x128, 8, 7),
+ DIV(CLK_TOP_A2SYS_HP_DIV, "a2sys_div", "a2sys_hp_sel",
+ 0x128, 24, 7),
+};
+
+static struct mt_clk_def clk_def = {
+ .fixed_def = fixed_clk,
+ .num_fixed = nitems(fixed_clk),
+ .gates_def = gates_clk,
+ .num_gates = nitems(gates_clk),
+ .dived_def = dived_clk,
+ .num_dived = nitems(dived_clk),
+ .muxes_def = muxes_clk,
+ .num_muxes = nitems(muxes_clk),
+};
+
+static int
+topckgen_clk_probe(device_t dev)
+{
+ return (mt_clk_probe(dev, compat_data, "Mediatek Topckgen clocks"));
+}
+
+static int
+topckgen_clk_attach(device_t dev)
+{
+ struct mt_clk_softc *sc;
+ sc = device_get_softc(dev);
+
+ sc->clk_def = &clk_def;
+ return (mt_clk_attach(dev));
+}
+
+static device_method_t mt7622_topckgen_methods[] = {
+ DEVMETHOD(device_probe, topckgen_clk_probe),
+ DEVMETHOD(device_attach, topckgen_clk_attach),
+ DEVMETHOD_END
+
+};
+
+DEFINE_CLASS_1(mt7622_topckgen, mt7622_topckgen_driver, mt7622_topckgen_methods,
+sizeof(struct mt_clk_softc), mt_clk_driver);
+
+EARLY_DRIVER_MODULE(mt7622_topckgen, simplebus, mt7622_topckgen_driver,
+ NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE + 2);
diff --git a/sys/arm64/mediatek/mt7622_pinctrl.c b/sys/arm64/mediatek/mt7622_pinctrl.c
new file mode 100644
--- /dev/null
+++ b/sys/arm64/mediatek/mt7622_pinctrl.c
@@ -0,0 +1,469 @@
+/*
+ * Copyright (c) 2025, 2026 Martin Filla <freebsd@sysctl.cz>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/libkern.h>
+#include <sys/kernel.h>
+#include <sys/cdefs.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/systm.h>
+
+#include <machine/bus.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/fdt/fdt_pinctrl.h>
+#include <dev/fdt/simplebus.h>
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/openfirm.h>
+
+static struct ofw_compat_data compat_data[] ={
+ {"mediatek,mt7622-pinctrl", 1},
+ {NULL, 0}
+};
+
+struct mt7622_pinmux_desc {
+ const char *modes[8];
+ bus_size_t reg_offset;
+ int shift;
+};
+
+struct mt7622_functions_desc {
+ const char *group;
+ const char *function;
+ const uint32_t pin;
+ const char *mode;
+};
+
+struct mt7622_pinctrl_softc {
+ device_t dev;
+ struct resource *mem_res;
+ int mem_rid;
+ const struct mt7622_pinmux_desc *pinmux;
+ const struct mt7622_functions_desc *functions;
+};
+
+/* for mt7622 from pinctrl-mt7622.txt */
+static const struct mt7622_functions_desc functions[] = {
+ /* group, function, pin, mode */
+ /* emmc functions */
+ {"emmc", "emmc", 40, NULL},
+ {"emmc", "emmc", 41, NULL},
+ {"emmc", "emmc", 42, NULL},
+ {"emmc", "emmc", 43, NULL},
+ {"emmc", "emmc", 44, NULL},
+ {"emmc", "emmc", 45, NULL},
+ {"emmc", "emmc", 47, NULL},
+ {"emmc", "emmc", 48, NULL},
+ {"emmc", "emmc", 49, NULL},
+ {"emmc", "emmc", 50, NULL},
+ /* emmc_rst */
+ {"emmc_rst", "emmc", 37, "EMMC"},
+
+ /* eth functions*/
+ /* eth_pins: eth-pins {
+ mux {
+ function = "eth";
+ groups = "mdc_mdio", "rgmii_via_gmac2";
+ };
+ }; */
+
+ /* rgmii_via_gmac1 */
+ {"rgmii_via_gmac1", "eth", 59, "G1_TXD"},
+ {"rgmii_via_gmac1", "eth", 60, "G1_TXD"},
+ {"rgmii_via_gmac1", "eth", 61, "G1_TXD"},
+ {"rgmii_via_gmac1", "eth", 62, "G1_TXD"},
+ {"rgmii_via_gmac1", "eth", 63, "G1_TXEN"},
+ {"rgmii_via_gmac1", "eth", 64, "G1_TXC"},
+ {"rgmii_via_gmac1", "eth", 65, "G1_RXD"},
+ {"rgmii_via_gmac1", "eth", 66, "G1_RXD"},
+ {"rgmii_via_gmac1", "eth", 67, "G1_RXD"},
+ {"rgmii_via_gmac1", "eth", 68, "G1_RXD"},
+ {"rgmii_via_gmac1", "eth", 69, "G1_RXDV"},
+ {"rgmii_via_gmac1", "eth", 70, "G1_RXCV"},
+ /* rgmii_via_gmac2 */
+ {"rgmii_via_gmac2", "eth", 25, "RGMII"},
+ {"rgmii_via_gmac2", "eth", 26, NULL},
+ {"rgmii_via_gmac2", "eth", 27, NULL},
+ {"rgmii_via_gmac2", "eth", 28, NULL},
+ {"rgmii_via_gmac2", "eth", 29, NULL},
+ {"rgmii_via_gmac2", "eth", 30, NULL},
+ {"rgmii_via_gmac2", "eth", 31, NULL},
+ {"rgmii_via_gmac2", "eth", 32, NULL},
+ {"rgmii_via_gmac2", "eth", 33, NULL},
+ {"rgmii_via_gmac2", "eth", 34, NULL},
+ {"rgmii_via_gmac2", "eth", 35, NULL},
+ {"rgmii_via_gmac2", "eth", 36, NULL},
+ /* mdc_mdio */
+ {"mdc_mdio", "eth", 23, "MDIO"},
+ {"mdc_mdio", "eth", 24, NULL},
+
+ /* i2c1_0 */
+ {"i2c1_0", "i2c", 55, "I2C1_SCL"},
+ {"i2c1_0", "i2c", 56, "I2C1_SDA"},
+ /* i2c2_0 */
+ {"i2c2_0", "i2c", 57, "I2C2_SCL"},
+ {"i2c2_0", "i2c", 58, "I2C2_SDA"},
+ /* i2s1_in_data */
+ {"i2s1_in_data", "i2c", 1, "I2S1_IN"},
+ /* i2s_out_mclk_bclk_ws */
+ {"i2s_out_mclk_bclk_ws", "i2c", 3, "I2S_BCLK_OUT"},
+ {"i2s_out_mclk_bclk_ws", "i2c", 4, "I2S_WS_OUT"},
+ {"i2s_out_mclk_bclk_ws", "i2c", 5, "I2S_MCLK"},
+ /* i2s1_out_data */
+ {"i2s1_out_data", "i2c", 2, "I2S1_OUT"},
+
+ /* ir_1_tx */
+ {"ir_1_tx", "ir", 59, "IR_T"},
+ /* ir_1_rx */
+ {"ir_1_rx", "ir", 60, "IR_R"},
+
+ /* wled */
+ {"wled", "led", 85, "WLED_N"},
+
+ /* par_nand */
+ {"par_nand", "flash", 37, "Parallel NAND Flash"},
+ {"par_nand", "flash", 38, NULL},
+ {"par_nand", "flash", 39, NULL},
+ {"par_nand", "flash", 40, NULL},
+ {"par_nand", "flash", 41, NULL},
+ {"par_nand", "flash", 42, NULL},
+ {"par_nand", "flash", 43, NULL},
+ {"par_nand", "flash", 44, NULL},
+ {"par_nand", "flash", 45, NULL},
+ {"par_nand", "flash", 46, NULL},
+ {"par_nand", "flash", 47, NULL},
+ {"par_nand", "flash", 48, NULL},
+ {"par_nand", "flash", 49, NULL},
+ {"par_nand", "flash", 50, NULL},
+ /* snfi */
+ {"snfi", "flash", 8, "SNFI_WP"},
+ {"snfi", "flash", 9, "SNFI_HOLD"},
+ {"snfi", "flash", 10, NULL},
+ {"snfi", "flash", 11, NULL},
+ {"snfi", "flash", 12, NULL},
+ {"snfi", "flash", 13, NULL},
+ /* spi_nor */
+ {"spi_nor", "flash", 8, "SPI_WP"},
+ {"spi_nor", "flash", 9, "SPI_HOLD"},
+ {"spi_nor", "flash", 10, "SPI NOR Flash"},
+ {"spi_nor", "flash", 11, NULL},
+ {"spi_nor", "flash", 12, NULL},
+ {"spi_nor", "flash", 13, NULL},
+
+ /* pcie0_1_waken */
+ {"pcie0_1_waken", "pcie", 79, "PCIE0_PAD_WAKE"},
+ /* pcie1_0_waken */
+ {"pcie1_0_waken", "pcie", 14, NULL},
+ /* pcie0_1_clkreq */
+ {"pcie0_1_clkreq", "pcie", 80, "CPIE0_PAD_CLKREQ"},
+ /* pcie1_0_clkreq */
+ {"pcie1_0_clkreq", "pcie", 15, NULL},
+ /* pcie0_pad_perst */
+ {"pcie0_pad_perst", "pcie", 83, "PCIE0_PAD_PERST"},
+ /* pcie1_pad_perst */
+ {"pcie1_pad_perst", "pcie", 84, "PCIE1_PAD_PERST"},
+
+ /* pmic_bus */
+ {"pmic_bus", "pmic", 71, "PMIC I2C"},
+ {"pmic_bus", "pmic", 72, NULL},
+
+ /* pwm_ch1_0 */
+ {"pwm_ch1_0", "pwm", 51, "PWM_CH1"},
+ /* pwm_ch2_0 */
+ {"pwm_ch2_0", "pwm", 52, "PWM_CH2"},
+ /* pwm_ch3_2 */
+ {"pwm_ch3_2", "pwm", 97, "PWM_CH3"},
+ /* pwm_ch4_1 */
+ {"pwm_ch4_1", "pwm", 67, "PWM_CH4"},
+ /* pwm_ch5_0 */
+ {"pwm_ch5_0", "pwm", 68, "PWM_CH5"},
+ /* pwm_ch6_0 */
+ {"pwm_ch6_0", "pwm", 69, "PWM_CH6"},
+
+ /* sd_0 */
+ {"sd_0", "sd", 16, "SD_D2"},
+ {"sd_0", "sd", 17, "SD_D2"},
+ {"sd_0", "sd", 18, "SD_D1"},
+ {"sd_0", "sd", 19, "SD_D0"},
+ {"sd_0", "sd", 20, "SD_CLK"},
+ {"sd_0", "sd", 21, "SD_CMD"},
+
+ /* spic0_0 */
+ {"spic0_0", "spi", 63, "SPIC0_CLK"},
+ {"spic0_0", "spi", 64, "SPIC0_MOSI"},
+ {"spic0_0", "spi", 65, "SPIC0_MISO"},
+ {"spic0_0", "spi", 66, "SPIC0_CS"},
+ /* spic1_0 */
+ {"spic1_0", "spi", 67, "SPIC1_CLK"},
+ {"spic1_0", "spi", 68, "SPIC1_MOSI"},
+ {"spic1_0", "spi", 69, "SPIC1_MISO"},
+ {"spic1_0", "spi", 70, "SPIC1_CS"},
+
+ /* uart0_0_tx_rx */
+ {"uart0_0_tx_rx", "uart", 6, "UART0"},
+ {"uart0_0_tx_rx", "uart", 7, NULL},
+ /* uart2_1_tx_rx */
+ {"uart2_1_tx_rx", "uart", 51, "UART_TXD2"},
+ {"uart2_1_tx_rx", "uart", 52, "UART_RXD2"},
+
+ /* watchdog */
+ {"watchdog", "watchdog", 78, "WATCHDOG"},
+};
+
+static const struct mt7622_pinmux_desc pinmux[] = {
+ /* from MT7622_Reference_Manual_for_Develope_Board(BPi) */
+ /* number of pin , mode 0..7, register, shift */
+ [23] = {{"MDIO", "GPIO", NULL, NULL, NULL, NULL, NULL, NULL}, 0x300, 24},
+ [24] = {{NULL, "GPIO", NULL, NULL, NULL, NULL, NULL, NULL}, 0x300, 24},
+ [37] = {{"Parallel NAND Flash", "GPIO", "EMMC", NULL, NULL, NULL, NULL, NULL}, 0x300, 20},
+ [50] = {{NULL, "GPIO", NULL, NULL, NULL, NULL, NULL, NULL}, 0x300, 20},
+ [71] = {{"PMIC I2C", "GPIO", NULL, NULL, NULL, NULL, NULL, NULL}, 0x300, 16},
+ [72] = {{NULL, "GPIO", NULL, NULL, NULL, NULL, NULL, NULL}, 0x300, 16},
+ [25] = {{"RGMII", "GPIO", "SDXC", NULL, NULL, NULL, NULL, NULL}, 0x300, 12},
+ [36] = {{NULL, "GPIO", NULL, NULL, NULL, NULL, NULL, NULL}, 0x300, 12},
+ [10] = {{"SPI NOR Flash", "GPIO", "SPI NAND Flash", NULL, NULL, NULL, NULL, NULL}, 0x300, 8},
+ [13] = {{NULL, "GPIO", NULL, NULL, NULL, NULL, NULL, NULL}, 0x300, 8},
+ [6] = {{"UART0", "GPIO", NULL, NULL, NULL, NULL, NULL, NULL}, 0x300, 4},
+ [7] = {{NULL, "GPIO", NULL, NULL, NULL, NULL, NULL, NULL}, 0x300, 4},
+
+ [21] = {{"I2S4_OUT", "GPIO21", "SD_CMD", NULL, NULL, "ANTSEL", "BT_SPXT_C0", "DBG_UTIF"}, 0x310, 28},
+ [20] = {{"I2S3_OUT", "GPIO20", "SD_CLK", NULL, NULL, "ANTSEL", "BT_SPXT_C1", "DBG_UTIF"}, 0x310, 24},
+ [19] = {{"I2S2_OUT", "GPIO19", "SD_D0", NULL, NULL, "ANTSEL", "BT_IPATH_EN", "DBG_UTIF"}, 0x310, 20},
+ [18] = {{"I2S4_IN", "GPIO18", "SD_D1", NULL, NULL, "ANTSEL", "BT_ERX_EN", "DBG_UTIF"}, 0x310, 16},
+ [76] = {{"SPIC1_CS", "GPIO76", "UART CTS1", "I2C2_SDA", "PWM_CH4", "ANTSEL", NULL, "DBG_UTIF"}, 0x310, 12},
+ [75] = {{"SPIC1_MISO", "GPIO75", "UART RTS1", "I2C2_SCL", "PWM_CH3", "ANTSEL", NULL, "DBG_UTIF"}, 0x310, 8},
+ [74] = {{"SPIC1_MOSI", "GPIO74", "UART RDX1", "I2C2_SDA", "PWM_CH4", "ANTSEL", NULL, "DBG_UTIF"}, 0x310, 4},
+ [73] = {{"SPIC1_CLK", "GPIO73", "UART TXD1", "I2C1_SCL", "PWM_CH1", "ANTSEL", NULL, "DBG_UTIF"}, 0x310, 0},
+
+ [77] = {{"GPIO_D/GPIO77", "GPIO77", NULL, NULL, "PWM_CH5", "ANTSEL", NULL, "DBG_UTIF"}, 0x320, 28},
+ [17] = {{"I2S3_IN", "GPIO17", "SD_D2", NULL, "IR_R", "ANTSEL", "BT_ELNA_EN", "DBG_UTIF"}, 0x320, 24},
+ [16] = {{"I2S2_IN", "GPIO16", "SD_D3", NULL, "IR_T", "ANTSEL", "BT_EPA_EN", "DBG_UTIF"}, 0x320, 20},
+ [0] = {{"GPIO_A/GPIO0", "GPIO0", NULL, NULL, NULL, NULL, NULL, NULL}, 0x320, 16},
+ [78] = {{"WATCHDOG", "GPIO78", NULL, NULL, "PWM_CH6", NULL, NULL, "DBG_UTIF"}, 0x320, 12},
+ [35] = {{"GPIO_A/GPIO0", "GPIO35", "PCIE0_PAD_CLKREQ", "PCIE1_PAD_CLKREQ", NULL, "ANTSEL", NULL, NULL}, 0x320, 8},
+ [34] = {{"GPIO_A/GPIO0", "GPIO34", "PCIE0_PAD_WAKE", "PCIE1_PAD_WAKE", NULL, "ANTSEL", NULL, "EXT_BGCK"}, 0x320, 4},
+ [5] = {{"I2S_MCLK", "GPIO5", NULL, NULL, NULL, NULL, NULL, "DBG_UTIF"}, 0x320, 0},
+
+ [57] = {{"I2C2_SCL", "GPIO57", "UART_RTS1", "TDM_OUT_MCLK", NULL, NULL, NULL, NULL}, 0x330, 28},
+ [56] = {{"I2C1_SDA", "GPIO56", "UART_RXD1", "TDM_IN_DATA", NULL, NULL, NULL, NULL}, 0x330, 24},
+ [55] = {{"I2C1_SCL", "GPIO55", "UART_TXD1", "TDM_OUT_DATA", NULL, NULL, NULL, NULL}, 0x330, 20},
+ [54] = {{"UART_CTS2", "GPIO54", NULL, "PWM_CH4", NULL, NULL, NULL, NULL}, 0x330, 16},
+ [53] = {{"UART_RTS2", "GPIO53", NULL, "PWM_CH3", NULL, NULL, NULL, NULL}, 0x330, 12},
+ [52] = {{"UART_RXD2", "GPIO52", NULL, "PWM_CH2", NULL, NULL, NULL, NULL}, 0x330, 8},
+ [51] = {{"UART_TXD2", "GPIO51", NULL, "PWM_CH1", NULL, NULL, NULL, NULL}, 0x330, 4},
+ [84] = {{"PCIE1_PAD_PERST", "GPIO84", NULL, NULL, NULL, NULL, NULL, NULL}, 0x330, 0},
+
+ [65] = {{"ESW_RXD", "GPIO65", "G1_RXD", NULL, "SPIC0_MISO", NULL, NULL, NULL}, 0x340, 28},
+ [64] = {{"ESW_TXC", "GPIO64", "G1_TXC", NULL, "SPIC0_MOSI", NULL, NULL, NULL}, 0x340, 24},
+ [63] = {{"ESW_TXEN", "GPIO63", "G1_TXEN", NULL, "SPIC0_CLK", NULL, NULL, NULL}, 0x340, 20},
+ [62] = {{"ESW_TXD", "GPIO62", "G1_TXD", "TDM_IN_WS", "UART_CTS2_N", "UART_RXD4", NULL, NULL}, 0x340, 16},
+ [61] = {{"ESW_TXD", "GPIO61", "G1_TXD", "TDM_IN_BCLK", "UART_RTS2_N", "UART_TXD4", NULL, NULL}, 0x340, 12},
+ [60] = {{"ESW_TXD", "GPIO60", "G1_TXD", "TDM_IN_MCLK", "UART_RXD2", "IR_R", NULL, NULL}, 0x340, 8},
+ [59] = {{"ESW_TXD", "GPIO59", "G1_TXD", "TMD_OUT_WS", "UART_TXD2", "IR_T", NULL, NULL}, 0x340, 4},
+ [58] = {{"I2C2_SDA", "GPIO58", "UART_CTS1", "TDM_OUT_BCLK", NULL, NULL, NULL, NULL}, 0x340, 0},
+
+ [83] = {{"PCIE0_PAD_PERST", "GPIO83", NULL, NULL, NULL, NULL, NULL, NULL}, 0x350, 28},
+ [9] = {{"SPI_HOLD", "GPIO9", "SNFI_HOLD", "TDM_OUT_BCLK", NULL, NULL, "FPC_DL_STS", NULL}, 0x350, 24},
+ [8] = {{"SPI_WP", "GPIO8", "SNFI_WP", "TDM_OUT_MCLK", NULL, NULL, "FPC_DAT_STS", NULL}, 0x350, 20},
+ [70] = {{"ESW_RXC", "GPIO70", "G1_RXCV", "SPIC1_CS", NULL, NULL, NULL, NULL}, 0x350, 16},
+ [69] = {{"ESW_RXDV", "GPIO69", "G1_RXDV", "PWM_CH6", "SPIC1_MISO", NULL, NULL, NULL}, 0x350, 12},
+ [68] = {{"ESW_RXD", "GPIO68", "G1_RXD", "PWM_CH5", "SPIC1_MOSI", NULL, NULL, NULL}, 0x350, 8},
+ [67] = {{"ESW_RXD", "GPIO67", "G1_RXD", "PWM_CH4", "SPIC1_CLK", NULL, NULL, NULL}, 0x350, 4},
+ [66] = {{"ESW_RXD", "GPIO66", "G1_RXD", NULL, "SPIC0_CS", NULL, NULL, NULL}, 0x350, 0},
+
+ [90] = {{"I2C2_SDA", "GPIO90", NULL, NULL, NULL, NULL, NULL, NULL}, 0x360, 24},
+ [89] = {{"I2C2_SCL", "GPIO89", NULL, NULL, NULL, NULL, NULL, NULL}, 0x360, 20},
+ [88] = {{"I2C1_SDA", "GPIO88", NULL, NULL, NULL, NULL, NULL, NULL}, 0x360, 16},
+ [87] = {{"I2C1_SCL", "GPIO87", NULL, NULL, NULL, NULL, NULL, NULL}, 0x360, 12},
+ [86] = {{"EPHY_LED0_N", "GPIO86", NULL, NULL, "CPUM_HW_SEL", NULL, "FPC_CTL", "JTRST_N"}, 0x360, 8},
+ [85] = {{"WLED_N", "GPIO85", NULL, NULL, NULL, NULL, NULL, NULL}, 0x360, 4},
+ [102] = {{"GPIO_E/GPIO102", "GPIO102", NULL, NULL, NULL, NULL, "ANTSEL", "FPC_DATA"}, 0x360, 0},
+
+ [97] = {{"PWM_CH3", "GPIO97", "UART_TXD4", NULL, "AICE_TCKC", "ANTSEL", "FPC_DATA", "W_JTCLK"}, 0x380, 28},
+ [96] = {{"PWM_CH2", "GPIO96", "UART_CTS4", "UART_RXD2", "CPUM_CK_XI", "ANTSEL", "FPC_DATA", "W_DBGACK"}, 0x380, 24},
+ [95] = {{"PWM_CH1", "GPIO95", "UART_RTS4", "UART_TXD2", "CPUM", "ANTSEL", "FPC_DATA", "W_DBGIN"}, 0x380, 20},
+ [22] = {{"GPIO_B/GPIO22", "GPIO22", NULL, "TSF_INTR", NULL, NULL, "ANTSEL", "DBG_UTIF"}, 0x380, 16},
+
+ [94] = {{"UART_CTS4", "GPIO94", "EPHY_LED4_N", "DFD_TMS", "CPUM", "ANTSEL", "FPC_CTL", "JTMS"}, 0x390, 28},
+ [93] = {{"UART_RTS4", "GPIO93", "EPHY_LED3_N", "DFD_TCK", "CPUM", "ANTSEL", "FPC_CTL", "JTCLK"}, 0x390, 24},
+ [92] = {{"UART_RXD4", "GPIO92", "EPHY_LED2_N", "DFD_TDO", "CPUM", "ANTSEL", "FPC_CTL", "JTDO"}, 0x390, 20},
+ [91] = {{"UART_TXD4", "GPIO91", "EPHY_LED1_N", "DFD_TDI", "CPUM_2B_SEL", "ANTSEL", "FPC_CK_XI", "JTDI"}, 0x390, 16},
+ [101] = {{"PWM_CH7", "GPIO101", NULL, NULL, NULL, "ANTSEL", "FPC_DATA", "DBG_UART_TXD"}, 0x390, 12},
+ [100] = {{"PWM_CH6", "GPIO100", NULL, "IR_R", NULL, "ANTSEL", "FPC_DATA", "W_JTRST_N"}, 0x390, 8},
+ [99] = {{"PWM_CH5", "GPIO99", NULL, "IR_T", "AICE_TMSC", "ANTSEL", "FPC_DATA", "W_JTMS"}, 0x390, 4},
+ [98] = {{"PWM_CH4", "GPIO98", "UART_RXD4", NULL, NULL, "ANTSEL", "FPC_DATA", "W_JTDI"}, 0x390, 0},
+
+ [4] = {{"I2S_WS_OUT", "GPIO4", "UART_RXD2", "i2S_WS_IN", NULL, NULL, NULL, NULL}, 0x3A0, 28},
+ [3] = {{"I2S_BCLK_OUT", "GPIO3", "UART_TXD2", "i2S_BCLK_IN", NULL, NULL, NULL, NULL}, 0x3A0, 24},
+ [2] = {{"I2S1_OUT", "GPIO2", "UART_CTS2_N", NULL, NULL, NULL, NULL, NULL}, 0x3A0, 20},
+ [1] = {{"I2S1_IN", "GPIO1", "UART_RTS2_N", NULL, NULL, NULL, NULL, NULL}, 0x3A0, 16},
+ [82] = {{"UART_RXD3", "GPIO82", "SPDIF_R", "SPIC0_MOSI", "PWM_CH7", "ANTSEL", NULL, "DBG_UTIF"}, 0x3A0, 12},
+ [81] = {{"UART_TXD3", "GPIO81", "SPDIF_T", "SPIC0_CLK", "PWM_CH6", "ANTSEL", NULL, "DBG_UTIF"}, 0x3A0, 8},
+ [80] = {{"UART_CTS3", "GPIO80", NULL, "SPIC0_CS", "CPIE0_PAD_CLKREQ", "ANTSEL", NULL, "DBG_UTIF"}, 0x3A0, 4},
+ [79] = {{"UART_RTS3", "GPIO79", NULL, "SPIC0_MISO", "PCIE0_PAD_WAKE", "ANTSEL", NULL, "DBG_UTIF"}, 0x3A0, 0}
+};
+
+static void
+mt7622_pinctrl_process_entry(struct mt7622_pinctrl_softc *sc, const char *group, char *function)
+{
+ for (int i = 0; i < nitems(functions); i++) {
+ if ((strcmp(functions[i].group, group) == 0) &&
+ (strcmp(functions[i].function, function) == 0)) {
+
+ uint32_t pin = functions[i].pin;
+ const char *mode = functions[i].mode;
+ const struct mt7622_pinmux_desc *pinmux = &sc->pinmux[pin];
+ if (mode != NULL && pinmux != NULL) {
+ for (int j = 0; j < nitems(pinmux->modes); j++) {
+ if (pinmux->modes[j] != NULL) {
+ if (strcmp(pinmux->modes[j], mode) == 0) {
+ uint32_t val = bus_read_4(sc->mem_res, pinmux->reg_offset);
+ val &= ~(0xF << pinmux->shift);
+ val |= (j << pinmux->shift);
+ device_printf(sc->dev,
+ "Pin %d: reg 0x%lX (shift %d): mode %d, mask 0x%X, reg val 0x%08X\n",
+ pin,
+ pinmux->reg_offset,
+ pinmux->shift,
+ j,
+ 0xF << pinmux->shift,
+ val
+ );
+ bus_write_4(sc->mem_res, pinmux->reg_offset, val);
+
+ uint32_t check_val = bus_read_4(sc->mem_res,
+ pinmux->reg_offset);
+ uint32_t set_bits = (check_val >> pinmux->shift) & 0xF;
+ if (set_bits != (j & 0xF)) {
+ device_printf(sc->dev,
+ "Warning: Pin %d: mode not set correctly! Expected %d, got %d in reg 0x%X\n",
+ pin, j & 0xF, set_bits, check_val);
+ } else {
+ device_printf(sc->dev,
+ "Pin %d successfully set to mode %d, reg 0x%lX = 0x%08X\n",
+ pin, set_bits, pinmux->reg_offset,
+ check_val);
+ }
+
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+static int
+mt7622_pinctrl_process_node(struct mt7622_pinctrl_softc *sc, phandle_t child)
+{
+ char mux[64];
+ const char **groups = NULL;
+ char *function = NULL;
+ int num_groups = 0;
+
+ if (OF_getprop(child, "name", &mux, sizeof(mux)) > 0) {
+ if (strncmp(mux, "mux", 3) == 0) {
+ num_groups = ofw_bus_string_list_to_array(child, "groups", &groups);
+ if (num_groups <= 0) {
+ return (ENOENT);
+ }
+
+ if (OF_getprop_alloc(child, "function", (void **) &function) == -1) {
+ OF_prop_free(groups);
+ return (ENOENT);
+ }
+
+ for (int i = 0; i < num_groups; i++) {
+ mt7622_pinctrl_process_entry(sc, groups[i], function);
+ }
+ }
+ }
+ OF_prop_free(groups);
+ OF_prop_free(function);
+ return (0);
+}
+
+static int
+mt7622_pinctrl_configure(device_t dev, phandle_t cfgxref)
+{
+ struct mt7622_pinctrl_softc *sc;
+ phandle_t child, node;
+ sc = device_get_softc(dev);
+ node = OF_node_from_xref(cfgxref);
+
+ for (child = OF_child(node); child != 0 && child != -1; child = OF_peer(child)) {
+ mt7622_pinctrl_process_node(sc, child);
+ }
+
+ return (0);
+}
+
+static int
+mt7622_pinctrl_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Mediatek 7622 pinctrl configuration");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mt7622_pinctrl_attach(device_t dev)
+{
+ struct mt7622_pinctrl_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ sc->mem_rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Could not map memory resource\n");
+ return (ENXIO);
+ }
+
+ sc->pinmux = pinmux;
+ sc->functions = functions;
+
+ fdt_pinctrl_register(dev, NULL);
+ fdt_pinctrl_configure_tree(dev);
+
+ return (0);
+}
+
+static int mt7622_pinctrl_detach(device_t dev)
+{
+ struct mt7622_pinctrl_softc *sc;
+ sc = device_get_softc(dev);
+
+ if (sc->mem_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res);
+ sc->mem_res = NULL;
+ }
+
+ return (0);
+}
+
+static device_method_t mt7622_pinctrl_methods[] = {
+ DEVMETHOD(device_probe, mt7622_pinctrl_probe),
+ DEVMETHOD(device_attach, mt7622_pinctrl_attach),
+ DEVMETHOD(device_detach, mt7622_pinctrl_detach),
+ DEVMETHOD(fdt_pinctrl_configure, mt7622_pinctrl_configure),
+ DEVMETHOD_END
+};
+
+static DEFINE_CLASS_0(mt7622_pinctrl, mt7622_pinctrl_driver, mt7622_pinctrl_methods,
+sizeof(struct mt7622_pinctrl_softc));
+EARLY_DRIVER_MODULE(mt7622_pinctrl, simplebus, mt7622_pinctrl_driver, NULL, NULL, 71);
diff --git a/sys/arm64/mediatek/mt_clk.h b/sys/arm64/mediatek/mt_clk.h
new file mode 100644
--- /dev/null
+++ b/sys/arm64/mediatek/mt_clk.h
@@ -0,0 +1,144 @@
+#ifndef __MT_CLK_H__
+#define __MT_CLK_H__
+
+/*
+ * Copyright (c) 2025, 2026 Martin Filla <freebsd@sysctl.cz>
+ * Copyright (c) 2025 Michal Meloun <mmel@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/* Parent list */
+#define PLIST(_name) static const char *_name[]
+
+/* Standard gate. */
+#define GATE(_id, cname, plist, o, s) \
+{ \
+ .clkdef.id = _id, \
+ .clkdef.name = cname, \
+ .clkdef.parent_names = (const char *[]){plist}, \
+ .clkdef.parent_cnt = 1, \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
+ .offset = o, \
+ .shift = s, \
+ .mask = 1, \
+ .on_value = 1, \
+ .off_value = 0, \
+}
+
+/* Fixed rate clock. */
+#define FRATE(_id, _name, _freq) \
+{ \
+ .clkdef.id = _id, \
+ .clkdef.name = _name, \
+ .clkdef.parent_cnt = 0, \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
+ .freq = _freq, \
+}
+
+/* Link. */
+#define LINK(_idx, _clkname, _pname) \
+{ \
+ .clkdef.id = _idx, \
+ .clkdef.name = _clkname, \
+ .clkdef.parent_name = _pname, \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS \
+}
+
+/* Fixed factor clock. */
+#define FFACT(_id, _name, _pname, _mult, _div) \
+{ \
+ .clkdef.id = _id, \
+ .clkdef.name = _name, \
+ .clkdef.parent_names = (const char *[]){_pname}, \
+ .clkdef.parent_cnt = 1, \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
+ .mult = _mult, \
+ .div = _div, \
+}
+
+/* Divided clock. */
+#define DIV(_id, _name, _pname, _reg, _shift, _width) \
+{ \
+ .clkdef.id = _id, \
+ .clkdef.name = _name, \
+ .clkdef.parent_names = (const char *[]){_pname}, \
+ .clkdef.parent_cnt = 1, \
+ .offset = _reg, \
+ .i_shift = _shift, \
+ .i_width = _width, \
+}
+
+/* Pure multiplexer. */
+#define MUX0(_id, cname, plists, _reg, _shift, _width) \
+{ \
+ .clkdef.id = _id, \
+ .clkdef.name = cname, \
+ .clkdef.parent_names = plists, \
+ .clkdef.parent_cnt = nitems(plists), \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
+ .offset = _reg, \
+ .shift = _shift, \
+ .width = _width, \
+}
+
+/* Inverter gate. */
+#define I_GATE(_id, cname, plist, o, s) \
+{ \
+ .clkdef.id = _id, \
+ .clkdef.name = cname, \
+ .clkdef.parent_names = (const char *[]){plist}, \
+ .clkdef.parent_cnt = 1, \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
+ .offset = o, \
+ .shift = s, \
+ .mask = 1, \
+ .on_value = 0, \
+ .off_value = 1, \
+}
+
+struct mt_clk_def {
+ struct mt_clk_pll_def *pll_def;
+ struct clk_link_def *linked_def;
+ struct clk_fixed_def *fixed_def;
+ struct clk_mux_def *muxes_def;
+ struct clk_gate_def *gates_def;
+ struct clk_div_def *dived_def;
+ int num_pll;
+ int num_linked;
+ int num_fixed;
+ int num_muxes;
+ int num_gates;
+ int num_dived;
+};
+
+struct mt_clk_reset_def {
+ const uint16_t *reset_offset;
+ uint16_t reset_num;
+};
+
+struct mt_clk_softc {
+ device_t dev;
+ struct resource *mem_res;
+ struct mtx mtx;
+ struct clkdom *clkdom;
+ struct syscon *syscon;
+ const struct mt_clk_def *clk_def;
+ const struct mt_clk_reset_def *reset_def;
+};
+
+DECLARE_CLASS(mt_clk_driver);
+
+int mt_clk_probe(device_t dev, struct ofw_compat_data *compat,
+ const char *desc);
+int mt_clk_attach(device_t dev);
+int mt_clk_detach(device_t dev);
+int mt_clkdev_read_4(device_t dev, bus_addr_t addr, uint32_t *val);
+int mt_clkdev_write_4(device_t dev, bus_addr_t addr, uint32_t val);
+int mt_clkdev_modify_4(device_t dev, bus_addr_t addr, uint32_t clear_mask,
+ uint32_t set_mask);
+void mt_clkdev_device_lock(device_t dev);
+void mt_clkdev_device_unlock(device_t dev);
+int mt_register_clocks(device_t dev, struct mt_clk_softc *sc,
+const struct mt_clk_def *cldef);
+#endif
diff --git a/sys/arm64/mediatek/mt_clk.c b/sys/arm64/mediatek/mt_clk.c
new file mode 100644
--- /dev/null
+++ b/sys/arm64/mediatek/mt_clk.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2025, 2026 Martin Filla <freebsd@sysctl.cz>
+ * Copyright (c) 2025 Michal Meloun <mmel@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/fdt/simplebus.h>
+
+#include <dt-bindings/clock/mt7622-clk.h>
+#include <dev/clk/clk_fixed.h>
+#include <dev/clk/clk_div.h>
+#include <dev/clk/clk_mux.h>
+#include <dev/clk/clk_gate.h>
+#include <dev/clk/clk_link.h>
+#include <dev/syscon/syscon.h>
+#include <dev/hwreset/hwreset.h>
+
+#include "mt_clk.h"
+#include "mt_clk_pll.h"
+
+#include "clkdev_if.h"
+#include "syscon_if.h"
+#include "hwreset_if.h"
+
+static int
+init_fixeds(struct mt_clk_softc *sc, struct clk_fixed_def *clks, int nclks)
+{
+ int i, rv;
+
+ for (i = 0; i < nclks; i++) {
+ rv = clknode_fixed_register(sc->clkdom, clks + i);
+ if (rv != 0) {
+ device_printf(sc->dev, "clknode_fixed_register failed");
+ return (ENXIO);
+ }
+ }
+
+ return (0);
+}
+
+static int
+init_linked(struct mt_clk_softc *sc, struct clk_link_def *clks, int nclks)
+{
+ for (int i = 0; i < nclks; i++) {
+ int rv = clknode_link_register(sc->clkdom, clks + i);
+ if (rv != 0) {
+ device_printf(sc->dev, "clknode_link_register failed");
+ return (ENXIO);
+ }
+ }
+
+ return (0);
+}
+
+static int
+init_muxes(struct mt_clk_softc *sc, struct clk_mux_def *clks, int nclks)
+{
+ int i, rv;
+
+ for (i = 0; i < nclks; i++) {
+ rv = clknode_mux_register(sc->clkdom, clks + i);
+ if (rv != 0) {
+ device_printf(sc->dev, "clknode_mux_register failed");
+ return (ENXIO);
+ }
+ }
+
+ return (0);
+}
+
+static int
+init_gates(struct mt_clk_softc *sc, struct clk_gate_def *clks, int nclks)
+{
+ int i, rv;
+
+ for (i = 0; i < nclks; i++) {
+ rv = clknode_gate_register(sc->clkdom, clks + i);
+ if (rv != 0) {
+ device_printf(sc->dev, "clknode_gate_register failed");
+ return (ENXIO);
+ }
+ }
+
+ return (0);
+}
+
+static int
+init_div(struct mt_clk_softc *sc, struct clk_div_def *clks, int nclks)
+{
+ int i, rv;
+
+ for (i = 0; i < nclks; i++) {
+ rv = clknode_div_register(sc->clkdom, clks + i);
+ if (rv != 0) {
+ device_printf(sc->dev, "clknode_div_register failed");
+ return (ENXIO);
+ }
+ }
+
+ return (0);
+}
+
+static int
+init_plls(struct mt_clk_softc *sc, struct mt_clk_pll_def *clks, int nclks)
+{
+ int i, rv;
+
+ for (i = 0; i < nclks; i++) {
+ rv = mt_clk_pll_register(sc->clkdom, clks + i);
+ if (rv != 0) {
+ device_printf(sc->dev, "mt_clk_pll_register failed\n");
+ return (ENXIO);
+ }
+ }
+
+ return (0);
+}
+
+int
+mt_clkdev_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
+{
+ struct mt_clk_softc *sc;
+ sc = device_get_softc(dev);
+
+ *val = bus_read_4(sc->mem_res, addr);
+ return (0);
+}
+
+int
+mt_clkdev_write_4(device_t dev, bus_addr_t addr, uint32_t val)
+{
+ struct mt_clk_softc *sc;
+ sc = device_get_softc(dev);
+
+ bus_write_4(sc->mem_res, addr, val);
+ return (0);
+}
+
+int
+mt_clkdev_modify_4(device_t dev, bus_addr_t addr, uint32_t clear_mask,
+ uint32_t set_mask)
+{
+ struct mt_clk_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+
+ reg = bus_read_4(sc->mem_res, addr);
+ reg &= ~clear_mask;
+ reg |= set_mask;
+ bus_write_4(sc->mem_res, addr, reg);
+ return (0);
+}
+
+void
+mt_clkdev_device_lock(device_t dev)
+{
+ struct mt_clk_softc *sc;
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->mtx);
+}
+
+void
+mt_clkdev_device_unlock(device_t dev)
+{
+ struct mt_clk_softc *sc;
+ sc = device_get_softc(dev);
+ mtx_unlock(&sc->mtx);
+}
+
+int
+mt_register_clocks(device_t dev, struct mt_clk_softc *sc,
+ const struct mt_clk_def *cldef)
+{
+ int rv;
+
+ sc->clkdom = clkdom_create(dev);
+ if (sc->clkdom == NULL) {
+ device_printf(dev, "failed to create clkdom");
+ return (ENXIO);
+ }
+
+ rv = init_plls(sc, cldef->pll_def, cldef->num_pll);
+ if(rv != 0) {
+ return (ENXIO);
+ }
+
+ rv = init_fixeds(sc, cldef->fixed_def, cldef->num_fixed);
+ if(rv != 0) {
+ return (ENXIO);
+ }
+
+ rv = init_linked(sc, cldef->linked_def, cldef->num_linked);
+ if(rv != 0) {
+ return (ENXIO);
+ }
+
+ rv = init_muxes(sc, cldef->muxes_def, cldef->num_muxes);
+ if(rv != 0) {
+ return (ENXIO);
+ }
+
+ rv = init_gates(sc, cldef->gates_def, cldef->num_gates);
+ if(rv != 0) {
+ return (ENXIO);
+ }
+
+ rv = init_div(sc, cldef->dived_def, cldef->num_dived);
+ if(rv != 0) {
+ return (ENXIO);
+ }
+
+ rv = clkdom_finit(sc->clkdom);
+ if (rv != 0) {
+ device_printf(dev, "clkdom_finit failed: %d\n", rv);
+ return (ENXIO);
+ }
+ else if (bootverbose) {
+ clkdom_dump(sc->clkdom);
+ }
+
+ return (0);
+}
+
+int
+mt_clk_probe(device_t dev, struct ofw_compat_data *compat, const char *desc)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ if (ofw_bus_search_compatible(dev, compat)->ocd_data == 0)
+ return (ENXIO);
+ device_set_desc(dev, desc);
+ return (BUS_PROBE_DEFAULT);
+}
+
+int
+mt_clk_attach(device_t dev)
+{
+ struct mt_clk_softc *sc;
+ int rv, rid = 0;
+ phandle_t node;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "cannot allocate memory resource\n");
+ mtx_destroy(&sc->mtx);
+ return (ENXIO);
+ }
+
+ /* Register as a syscon provider only when the node asks for it. */
+ if (ofw_bus_is_compatible(dev, "syscon")) {
+ sc->syscon = syscon_create_ofw_node(dev, &syscon_class,
+ ofw_bus_get_node(dev));
+ if (sc->syscon == NULL) {
+ device_printf(dev, "failed to create/register syscon\n");
+ if(sc->mem_res != NULL) {
+ bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->mem_res);
+ }
+ mtx_destroy(&sc->mtx);
+ return (ENXIO);
+ }
+ }
+
+ rv = mt_register_clocks(dev, sc, sc->clk_def);
+ if (rv != 0) {
+ device_printf(dev, "failed to register clocks\n");
+
+ if(sc->mem_res != NULL) {
+ bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->mem_res);
+ }
+
+ mtx_destroy(&sc->mtx);
+ return (ENXIO);
+ }
+
+ /*
+ * Clocks are already registered and cannot be unregistered, so
+ * failing attach here would leave clknodes referencing a freed
+ * softc. Keep the clock provider alive and only skip children.
+ */
+ if (OF_child(node) > 0) {
+ rv = simplebus_attach_impl(dev, SB_FLAG_NO_RANGES, node);
+ if (rv != 0) {
+ device_printf(dev,
+ "failed to attach simplebus (%d), "
+ "child nodes skipped\n", rv);
+ return (0);
+ }
+ }
+
+ bus_attach_children(dev);
+
+ return (0);
+}
+
+int
+mt_clk_detach(device_t dev)
+{
+ device_printf(dev, "Error: Clock driver cannot be detached\n");
+ return (EBUSY);
+}
+
+static int
+mt_clk_hwreset_assert(device_t dev, intptr_t id, bool assert)
+{
+ struct mt_clk_softc *sc;
+ uint32_t mask, reg, val;
+
+ sc = device_get_softc(dev);
+
+ if (sc->reset_def == NULL)
+ return (ENXIO);
+ if (id < 0 || id / 32 >= sc->reset_def->reset_num)
+ return (ENXIO);
+
+ reg = sc->reset_def->reset_offset[id / 32];
+ mask = 1u << (id % 32);
+
+ mtx_lock(&sc->mtx);
+ val = bus_read_4(sc->mem_res, reg);
+ val &= ~mask;
+ if (assert) {
+ val |= mask;
+ }
+ bus_write_4(sc->mem_res, reg, val);
+ mtx_unlock(&sc->mtx);
+
+ return (0);
+}
+
+static device_method_t mt_clk_methods[] = {
+ DEVMETHOD(device_detach, mt_clk_detach),
+ DEVMETHOD(clkdev_read_4, mt_clkdev_read_4),
+ DEVMETHOD(clkdev_write_4, mt_clkdev_write_4),
+ DEVMETHOD(clkdev_modify_4, mt_clkdev_modify_4),
+ DEVMETHOD(clkdev_device_lock, mt_clkdev_device_lock),
+ DEVMETHOD(clkdev_device_unlock, mt_clkdev_device_unlock),
+ DEVMETHOD(hwreset_assert, mt_clk_hwreset_assert),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(mt_clk, mt_clk_driver, mt_clk_methods,
+sizeof(struct mt_clk_softc));
diff --git a/sys/arm64/mediatek/mt_clk_pll.h b/sys/arm64/mediatek/mt_clk_pll.h
new file mode 100644
--- /dev/null
+++ b/sys/arm64/mediatek/mt_clk_pll.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2026 Martin Filla <freebsd@sysctl.cz>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef __MT_CLK_PLL_H__
+#define __MT_CLK_PLL_H__
+
+#include <dev/clk/clk.h>
+
+/*
+ * Mediatek PLL.
+ *
+ * The output frequency is given by the PCW (feedback divider, 7 integer
+ * bits followed by pcwbits - 7 fractional bits) and a 3 bit power-of-two
+ * post divider:
+ *
+ * fout = fin * pcw / 2^(pcwbits - 7) / 2^postdiv
+ */
+struct mt_clk_pll_def {
+ struct clknode_init_def clkdef;
+ uint32_t base_reg; /* CON0, EN is bit 0 */
+ uint32_t pwr_reg; /* PWR_CON0 */
+ uint32_t en_mask; /* Extra enable bits in CON0 */
+ uint32_t rst_bar_mask; /* CON0 reset bar */
+ uint32_t pd_reg; /* Post divider register */
+ uint32_t pd_shift;
+ uint32_t pcw_reg; /* PCW register */
+ uint32_t pcw_shift;
+ uint32_t pcwbits; /* Total PCW width */
+ uint32_t pcwibits; /* Integer PCW bits, 0 = 7 */
+ uint64_t fmin; /* VCO range, 0 = 1000 MHz */
+ uint64_t fmax;
+ uint32_t flags;
+#define MT_PLL_HAVE_RST_BAR 0x0001
+#define MT_PLL_AO 0x0002 /* Always on, never disable. */
+};
+
+#define MT_PLL(_id, _name, _pname, _reg, _pwr_reg, _en_mask, _flags, \
+ _pcwbits, _pd_reg, _pd_shift, _pcw_reg, _pcw_shift, _rst_bar, _fmax) \
+{ \
+ .clkdef.id = _id, \
+ .clkdef.name = _name, \
+ .clkdef.parent_names = (const char *[]){_pname}, \
+ .clkdef.parent_cnt = 1, \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
+ .base_reg = _reg, \
+ .pwr_reg = _pwr_reg, \
+ .en_mask = _en_mask, \
+ .rst_bar_mask = _rst_bar, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .pcwbits = _pcwbits, \
+ .fmax = _fmax, \
+ .flags = _flags, \
+}
+
+int mt_clk_pll_register(struct clkdom *clkdom, struct mt_clk_pll_def *clkdef);
+
+#endif /* __MT_CLK_PLL_H__ */
diff --git a/sys/arm64/mediatek/mt_clk_pll.c b/sys/arm64/mediatek/mt_clk_pll.c
new file mode 100644
--- /dev/null
+++ b/sys/arm64/mediatek/mt_clk_pll.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright (c) 2026 Martin Filla <freebsd@sysctl.cz>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <dev/clk/clk.h>
+#include "mt_clk_pll.h"
+#include "clkdev_if.h"
+
+#define PLL_PWR_ON (1u << 0)
+#define PLL_ISO_EN (1u << 1)
+#define PLL_EN (1u << 0)
+
+#define PLL_POSTDIV_MASK 0x7
+#define PLL_POSTDIV_MAX 4 /* Valid post dividers are /1 ... /16 */
+#define PLL_INTEGER_BITS 7
+#define PLL_FMIN_DEFAULT 1000000000ULL
+
+#define PLL_PWR_DELAY 1 /* us, PWR_ON and ISO_EN settle */
+#define PLL_LOCK_DELAY 20 /* us, PLL lock after EN */
+
+#define RD4(_clk, off, val) \
+ CLKDEV_READ_4(clknode_get_device(_clk), off, val)
+#define MD4(_clk, off, clr, set) \
+ CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set)
+#define DEVICE_LOCK(_clk) \
+ CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
+#define DEVICE_UNLOCK(_clk) \
+ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
+
+struct mt_clk_pll_sc {
+ uint32_t base_reg;
+ uint32_t pwr_reg;
+ uint32_t en_mask;
+ uint32_t rst_bar_mask;
+ uint32_t pd_reg;
+ uint32_t pd_shift;
+ uint32_t pcw_reg;
+ uint32_t pcw_shift;
+ uint32_t pcwbits;
+ uint32_t pcwibits;
+ uint64_t fmin;
+ uint64_t fmax;
+ uint32_t flags;
+ bool reported;
+};
+
+struct mt_clk_pll_state {
+ uint32_t con0; /* Raw registers, for diagnostics */
+ uint32_t pwr;
+ bool enabled;
+ uint32_t postdiv; /* Raw 3 bit field */
+ uint32_t pcw;
+};
+
+static int
+mt_clk_pll_read_state(struct clknode *clk, struct mt_clk_pll_state *st)
+{
+ struct mt_clk_pll_sc *sc;
+ uint32_t con0, pwr, pd, pcw;
+ int rv;
+
+ sc = clknode_get_softc(clk);
+
+ DEVICE_LOCK(clk);
+ rv = RD4(clk, sc->pwr_reg, &pwr);
+ if (rv == 0)
+ rv = RD4(clk, sc->base_reg, &con0);
+ if (rv == 0)
+ rv = RD4(clk, sc->pd_reg, &pd);
+ if (rv == 0)
+ rv = RD4(clk, sc->pcw_reg, &pcw);
+ DEVICE_UNLOCK(clk);
+ if (rv != 0)
+ return (rv);
+
+ st->con0 = con0;
+ st->pwr = pwr;
+ st->enabled = (con0 & PLL_EN) != 0;
+ st->postdiv = (pd >> sc->pd_shift) & PLL_POSTDIV_MASK;
+ st->pcw = (pcw >> sc->pcw_shift) & ((1u << sc->pcwbits) - 1);
+
+ return (0);
+}
+
+/* Same rounding as Linux __mtk_pll_recalc_rate(). */
+static uint64_t
+mt_clk_pll_calc_freq(struct mt_clk_pll_sc *sc, uint64_t fin, uint32_t pcw,
+ uint32_t postdiv)
+{
+ uint64_t vco;
+ uint32_t fbits;
+
+ fbits = sc->pcwbits > sc->pcwibits ? sc->pcwbits - sc->pcwibits : 0;
+ vco = fin * pcw;
+ if (fbits != 0 && (vco & ((1ULL << fbits) - 1)) != 0)
+ vco = (vco >> fbits) + 1;
+ else
+ vco >>= fbits;
+
+ return (howmany(vco, 1ULL << postdiv));
+}
+
+/* Check that the state programmed by the firmware makes sense. */
+static int
+mt_clk_pll_check(struct mt_clk_pll_sc *sc, struct mt_clk_pll_state *st)
+{
+
+ if (!st->enabled)
+ return (ENXIO);
+ if (st->pcw == 0 || st->postdiv > PLL_POSTDIV_MAX)
+ return (EINVAL);
+ return (0);
+}
+
+static int
+mt_clk_pll_init(struct clknode *clk, device_t dev)
+{
+ struct mt_clk_pll_sc *sc;
+ struct mt_clk_pll_state st;
+ int rv;
+
+ sc = clknode_get_softc(clk);
+ clknode_init_parent_idx(clk, 0);
+
+ rv = mt_clk_pll_read_state(clk, &st);
+ if (rv != 0) {
+ device_printf(dev, "%s: cannot read PLL registers: %d\n",
+ clknode_get_name(clk), rv);
+ return (0);
+ }
+
+ rv = mt_clk_pll_check(sc, &st);
+ if (bootverbose)
+ device_printf(dev, "%s: CON0 %#x PWR_CON %#x%s%s%s\n",
+ clknode_get_name(clk), st.con0, st.pwr,
+ (st.pwr & PLL_PWR_ON) == 0 ? " PWR_OFF" : "",
+ (st.pwr & PLL_ISO_EN) != 0 ? " ISO" : "",
+ (sc->flags & MT_PLL_HAVE_RST_BAR) != 0 &&
+ (st.con0 & sc->rst_bar_mask) == 0 ? " RST" : "");
+ if (rv == ENXIO) {
+ if (bootverbose)
+ device_printf(dev, "%s: disabled\n",
+ clknode_get_name(clk));
+ return (0);
+ }
+ if (rv != 0) {
+ device_printf(dev, "%s: invalid setup (pcw %#x, postdiv %u)\n",
+ clknode_get_name(clk), st.pcw, st.postdiv);
+ return (0);
+ }
+
+ /*
+ * The parent is not linked yet, the frequency is checked and
+ * reported by the first recalc.
+ */
+ return (0);
+}
+
+static int
+mt_clk_pll_recalc(struct clknode *clk, uint64_t *freq)
+{
+ struct mt_clk_pll_sc *sc;
+ struct mt_clk_pll_state st;
+ uint64_t fin, vco;
+ int rv;
+
+ sc = clknode_get_softc(clk);
+
+ rv = mt_clk_pll_read_state(clk, &st);
+ if (rv != 0)
+ return (rv);
+
+ /*
+ * As in Linux the rate does not depend on the gate: a disabled PLL
+ * reports the frequency it will run at once enabled. A PLL without
+ * a valid setup cannot produce any clock.
+ */
+ if (st.pcw == 0 || st.postdiv > PLL_POSTDIV_MAX) {
+ *freq = 0;
+ return (0);
+ }
+
+ fin = *freq;
+ *freq = mt_clk_pll_calc_freq(sc, fin, st.pcw, st.postdiv);
+
+ if (!sc->reported && fin != 0) {
+ sc->reported = true;
+ vco = *freq << st.postdiv;
+ if (vco < sc->fmin || vco > sc->fmax)
+ printf("%s: VCO %ju Hz out of range %ju - %ju Hz\n",
+ clknode_get_name(clk), (uintmax_t)vco,
+ (uintmax_t)sc->fmin, (uintmax_t)sc->fmax);
+ if (bootverbose)
+ printf("%s: %ju Hz (pcw %#x, postdiv /%u)\n",
+ clknode_get_name(clk), (uintmax_t)*freq, st.pcw,
+ 1u << st.postdiv);
+ }
+ return (0);
+}
+
+static int
+mt_clk_pll_get_gate(struct clknode *clk, bool *enabled)
+{
+ struct mt_clk_pll_state st;
+ int rv;
+
+ rv = mt_clk_pll_read_state(clk, &st);
+ if (rv != 0)
+ return (rv);
+ *enabled = st.enabled;
+ return (0);
+}
+
+/* Linux mtk_pll_prepare() */
+static int
+mt_clk_pll_enable(struct clknode *clk, struct mt_clk_pll_sc *sc)
+{
+ int rv;
+
+ rv = MD4(clk, sc->pwr_reg, 0, PLL_PWR_ON);
+ if (rv != 0)
+ return (rv);
+ DELAY(PLL_PWR_DELAY);
+
+ rv = MD4(clk, sc->pwr_reg, PLL_ISO_EN, 0);
+ if (rv != 0)
+ return (rv);
+ DELAY(PLL_PWR_DELAY);
+
+ rv = MD4(clk, sc->base_reg, 0, PLL_EN | sc->en_mask);
+ if (rv != 0)
+ return (rv);
+ DELAY(PLL_LOCK_DELAY);
+
+ if ((sc->flags & MT_PLL_HAVE_RST_BAR) != 0)
+ rv = MD4(clk, sc->base_reg, 0, sc->rst_bar_mask);
+ return (rv);
+}
+
+/* Linux mtk_pll_unprepare() */
+static int
+mt_clk_pll_disable(struct clknode *clk, struct mt_clk_pll_sc *sc)
+{
+ int rv;
+
+ if ((sc->flags & MT_PLL_HAVE_RST_BAR) != 0) {
+ rv = MD4(clk, sc->base_reg, sc->rst_bar_mask, 0);
+ if (rv != 0)
+ return (rv);
+ }
+
+ rv = MD4(clk, sc->base_reg, PLL_EN | sc->en_mask, 0);
+ if (rv != 0)
+ return (rv);
+
+ rv = MD4(clk, sc->pwr_reg, 0, PLL_ISO_EN);
+ if (rv != 0)
+ return (rv);
+
+ return (MD4(clk, sc->pwr_reg, PLL_PWR_ON, 0));
+}
+
+static int
+mt_clk_pll_set_gate(struct clknode *clk, bool enable)
+{
+ struct mt_clk_pll_sc *sc;
+ struct mt_clk_pll_state st;
+ int rv;
+
+ sc = clknode_get_softc(clk);
+
+ if (!enable && (sc->flags & (MT_PLL_AO)) != 0)
+ return (0);
+
+ rv = mt_clk_pll_read_state(clk, &st);
+ if (rv != 0)
+ return (rv);
+
+ /* Nothing to do, and never re-run the sequence on a running PLL. */
+ if (st.enabled == enable)
+ return (0);
+
+ if (enable && (st.pcw == 0 || st.postdiv > PLL_POSTDIV_MAX)) {
+ printf("%s: cannot enable PLL, invalid setup "
+ "(pcw %#x, postdiv %u)\n", clknode_get_name(clk),
+ st.pcw, st.postdiv);
+ return (EINVAL);
+ }
+
+ DEVICE_LOCK(clk);
+ if (enable)
+ rv = mt_clk_pll_enable(clk, sc);
+ else
+ rv = mt_clk_pll_disable(clk, sc);
+ DEVICE_UNLOCK(clk);
+ if (rv != 0)
+ return (rv);
+
+ /* Verify that the hardware followed. */
+ rv = mt_clk_pll_read_state(clk, &st);
+ if (rv != 0)
+ return (rv);
+ if (st.enabled != enable) {
+ printf("%s: PLL %s failed (CON0 %#x PWR_CON %#x)\n",
+ clknode_get_name(clk), enable ? "enable" : "disable",
+ st.con0, st.pwr);
+ return (EIO);
+ }
+ if (bootverbose)
+ printf("%s: PLL %s\n", clknode_get_name(clk),
+ enable ? "enabled" : "disabled");
+ return (0);
+}
+
+static int
+mt_clk_pll_calc_values(struct mt_clk_pll_sc *sc, uint64_t fin, uint64_t freq,
+ int flags, uint32_t *pcwp, uint32_t *postdivp, uint64_t *foutp)
+{
+ uint64_t fout, pcw, pcw_max;
+ uint32_t fbits, val;
+
+ if (fin == 0 || freq == 0)
+ return (EINVAL);
+
+ if (freq > sc->fmax) {
+ if ((flags & CLK_SET_ROUND_DOWN) == 0)
+ return (ERANGE);
+ freq = sc->fmax;
+ }
+
+ for (val = 0; val <= PLL_POSTDIV_MAX; val++) {
+ if ((freq << val) >= sc->fmin)
+ break;
+ }
+ if (val > PLL_POSTDIV_MAX)
+ return (ERANGE);
+
+ fbits = sc->pcwbits > sc->pcwibits ? sc->pcwbits - sc->pcwibits : 0;
+ pcw_max = (1ULL << sc->pcwbits) - 1;
+ pcw = ((freq << val) << fbits) / fin;
+ if (pcw == 0 || pcw > pcw_max)
+ return (ERANGE);
+
+ fout = mt_clk_pll_calc_freq(sc, fin, pcw, val);
+ if ((flags & CLK_SET_ROUND_UP) != 0 && fout < freq && pcw < pcw_max)
+ fout = mt_clk_pll_calc_freq(sc, fin, ++pcw, val);
+ else if ((flags & CLK_SET_ROUND_DOWN) != 0 && fout > freq && pcw > 1)
+ fout = mt_clk_pll_calc_freq(sc, fin, --pcw, val);
+
+ if (fout != freq && CLK_SET_ROUND(flags) == 0)
+ return (ERANGE);
+ if ((flags & CLK_SET_ROUND_ANY) == CLK_SET_ROUND_UP && fout < freq)
+ return (ERANGE);
+ if ((flags & CLK_SET_ROUND_ANY) == CLK_SET_ROUND_DOWN && fout > freq)
+ return (ERANGE);
+
+ *pcwp = (uint32_t)pcw;
+ *postdivp = val;
+ *foutp = fout;
+ return (0);
+}
+
+static int
+mt_clk_pll_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
+ int flags, int *done)
+{
+ struct mt_clk_pll_sc *sc;
+ struct mt_clk_pll_state st;
+ uint64_t cur, freq;
+ uint32_t pcw, postdiv;
+ int rv;
+
+ sc = clknode_get_softc(clk);
+
+ rv = mt_clk_pll_read_state(clk, &st);
+ if (rv != 0)
+ return (rv);
+
+ /* The frequency the PLL runs at is always acceptable. */
+ if (st.pcw != 0 && st.postdiv <= PLL_POSTDIV_MAX) {
+ cur = mt_clk_pll_calc_freq(sc, fin, st.pcw, st.postdiv);
+ if (*fout == cur) {
+ *done = 1;
+ return (0);
+ }
+ }
+
+ rv = mt_clk_pll_calc_values(sc, fin, *fout, flags, &pcw, &postdiv,
+ &freq);
+ if (rv != 0)
+ return (rv);
+
+ /* Dry run: only report what would be set. */
+ if ((flags & CLK_SET_DRYRUN) != 0) {
+ *fout = freq;
+ *done = 1;
+ return (0);
+ }
+
+ if (pcw == st.pcw && postdiv == st.postdiv) {
+ *fout = freq;
+ *done = 1;
+ return (0);
+ }
+
+ /* Reprogramming a PLL is not supported yet. */
+ printf("%s: changing PLL frequency to %ju Hz is not supported\n",
+ clknode_get_name(clk), (uintmax_t)freq);
+ return (EOPNOTSUPP);
+}
+
+static clknode_method_t mt_clk_pll_methods[] = {
+ CLKNODEMETHOD(clknode_init, mt_clk_pll_init),
+ CLKNODEMETHOD(clknode_recalc_freq, mt_clk_pll_recalc),
+ CLKNODEMETHOD(clknode_get_gate, mt_clk_pll_get_gate),
+ CLKNODEMETHOD(clknode_set_gate, mt_clk_pll_set_gate),
+ CLKNODEMETHOD(clknode_set_freq, mt_clk_pll_set_freq),
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(mt_clk_pll, mt_clk_pll_class, mt_clk_pll_methods,
+ sizeof(struct mt_clk_pll_sc), clknode_class);
+
+int
+mt_clk_pll_register(struct clkdom *clkdom, struct mt_clk_pll_def *clkdef)
+{
+ struct clknode *clk;
+ struct mt_clk_pll_sc *sc;
+
+ if (clkdef->pcwbits == 0 || clkdef->pcwbits > 31 ||
+ clkdef->fmax == 0)
+ return (EINVAL);
+
+ clk = clknode_create(clkdom, &mt_clk_pll_class, &clkdef->clkdef);
+ if (clk == NULL)
+ return (ENXIO);
+
+ sc = clknode_get_softc(clk);
+ sc->base_reg = clkdef->base_reg;
+ sc->pwr_reg = clkdef->pwr_reg;
+ sc->en_mask = clkdef->en_mask;
+ sc->rst_bar_mask = clkdef->rst_bar_mask;
+ sc->pd_reg = clkdef->pd_reg;
+ sc->pd_shift = clkdef->pd_shift;
+ sc->pcw_reg = clkdef->pcw_reg;
+ sc->pcw_shift = clkdef->pcw_shift;
+ sc->pcwbits = clkdef->pcwbits;
+ sc->pcwibits = clkdef->pcwibits != 0 ?
+ clkdef->pcwibits : PLL_INTEGER_BITS;
+ sc->fmin = clkdef->fmin != 0 ? clkdef->fmin : PLL_FMIN_DEFAULT;
+ sc->fmax = clkdef->fmax;
+ sc->flags = clkdef->flags;
+
+ clknode_register(clkdom, clk);
+ return (0);
+}
diff --git a/sys/arm64/mediatek/mt_uart.c b/sys/arm64/mediatek/mt_uart.c
new file mode 100644
--- /dev/null
+++ b/sys/arm64/mediatek/mt_uart.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 2025, 2026 Martin Filla <freebsd@sysctl.cz>
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <machine/bus.h>
+
+#include <dev/clk/clk.h>
+#include <dev/uart/uart.h>
+#ifdef FDT
+#include <dev/uart/uart_cpu_fdt.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+#include <dev/uart/uart_bus.h>
+#include <dev/uart/uart_cpu.h>
+#include <dev/uart/uart_dev_ns8250.h>
+#include <dev/ic/ns16550.h>
+
+#include "uart_if.h"
+
+#define MTK_UART_HIGHS 0x09 /* High speed mode (oversampling) select */
+#define MTK_UART_SAMPLE_COUNT 0x0a /* Sample count, high speed mode 3 only */
+#define MTK_UART_SAMPLE_POINT 0x0b /* Sample point, high speed mode 3 only */
+#define MTK_UART_RATE_FIX 0x0d /* Fixed-rate override register */
+
+#define MTK_UART_HIGHS_16X 0 /* baud = rclk / 16 / divisor */
+#define MTK_UART_HIGHS_8X 1 /* baud = rclk / 8 / divisor */
+#define MTK_UART_HIGHS_4X 2 /* baud = rclk / 4 / divisor */
+#define MTK_UART_HIGHS_SAMPLE 3 /* baud = rclk / (sample_count+1) / div */
+
+#define UART_DIV_MAX 0xffff /* Divisor latch is 16 bit. */
+
+#define MTK_DIV_ROUND_CLOSEST(n, d) (((n) + (d) / 2) / (d))
+
+struct mt_softc {
+ struct ns8250_softc ns8250_base;
+ clk_t baud_clk;
+ clk_t bus_clk;
+};
+
+static uint8_t
+mt_uart_lcr(int databits, int stopbits, int parity)
+{
+ uint8_t lcr;
+
+ lcr = 0;
+ if (databits >= 8) {
+ lcr |= LCR_8BITS;
+ }
+ else if (databits == 7) {
+ lcr |= LCR_7BITS;
+ }
+ else if (databits == 6) {
+ lcr |= LCR_6BITS;
+ }
+ else {
+ lcr |= LCR_5BITS;
+ }
+
+ if (stopbits > 1) {
+ lcr |= LCR_STOPB;
+ }
+
+ lcr |= parity << 3;
+
+ return (lcr);
+}
+
+static uint32_t
+mt_uart_calc_baud(u_int rclk, uint8_t highs, uint32_t divisor,
+ uint8_t sample_count)
+{
+ if (divisor == 0)
+ return (0);
+ switch (highs & 0x3) {
+ case MTK_UART_HIGHS_16X:
+ return (rclk / 16 / divisor);
+ case MTK_UART_HIGHS_8X:
+ return (rclk / 8 / divisor);
+ case MTK_UART_HIGHS_4X:
+ return (rclk / 4 / divisor);
+ default: /* MTK_UART_HIGHS_SAMPLE */
+ return (rclk / ((sample_count + 1) * divisor));
+ }
+}
+
+static int
+mt_uart_get_baud(struct uart_bas *bas)
+{
+ uint32_t divisor;
+ uint8_t highs, lcr, sample_count;
+
+ if (bas->rclk == 0)
+ return (0);
+
+ lcr = uart_getreg(bas, REG_LCR);
+ uart_setreg(bas, REG_LCR, lcr | LCR_DLAB);
+ uart_barrier(bas);
+ divisor = uart_getreg(bas, REG_DLL) |
+ (uart_getreg(bas, REG_DLH) << 8);
+ uart_barrier(bas);
+ uart_setreg(bas, REG_LCR, lcr);
+ uart_barrier(bas);
+
+ highs = uart_getreg(bas, MTK_UART_HIGHS);
+ sample_count = uart_getreg(bas, MTK_UART_SAMPLE_COUNT);
+
+ return (mt_uart_calc_baud(bas->rclk, highs, divisor, sample_count));
+}
+
+static void
+mt_uart_set_baud(struct uart_bas *bas, int baudrate)
+{
+ uint32_t divisor, samples;
+ uint8_t lcr;
+
+ if (baudrate <= 0 || bas->rclk == 0)
+ return;
+
+ uart_setreg(bas, MTK_UART_RATE_FIX, 0);
+ uart_barrier(bas);
+
+ divisor = howmany(bas->rclk, (uint32_t)baudrate * 256);
+ if (divisor == 0)
+ divisor = 1;
+ if (divisor > UART_DIV_MAX)
+ divisor = UART_DIV_MAX;
+
+ samples = MTK_DIV_ROUND_CLOSEST(bas->rclk, divisor * (uint32_t)baudrate);
+ if (samples < 2)
+ samples = 2;
+ if (samples > 256)
+ samples = 256;
+
+ uart_setreg(bas, MTK_UART_HIGHS, MTK_UART_HIGHS_SAMPLE);
+ uart_barrier(bas);
+
+ lcr = uart_getreg(bas, REG_LCR);
+ uart_setreg(bas, REG_LCR, lcr | LCR_DLAB);
+ uart_barrier(bas);
+ uart_setreg(bas, REG_DLL, divisor & 0xff);
+ uart_setreg(bas, REG_DLH, (divisor >> 8) & 0xff);
+ uart_barrier(bas);
+ uart_setreg(bas, REG_LCR, lcr);
+ uart_barrier(bas);
+ uart_setreg(bas, MTK_UART_SAMPLE_COUNT, samples - 1);
+ uart_setreg(bas, MTK_UART_SAMPLE_POINT, (samples - 2) >> 1);
+ uart_barrier(bas);
+}
+
+static int
+mt_uart_param(struct uart_softc *sc, int baudrate, int databits, int stopbits,
+ int parity)
+{
+ struct uart_bas *bas = &sc->sc_bas;
+
+ uart_lock(sc->sc_hwmtx);
+ uart_setreg(bas, REG_LCR, mt_uart_lcr(databits, stopbits, parity));
+ uart_barrier(bas);
+ mt_uart_set_baud(bas, baudrate);
+ uart_unlock(sc->sc_hwmtx);
+
+ if (bootverbose) {
+ device_printf(sc->sc_dev, "set baud %d %d %d\n", baudrate,
+ databits, stopbits);
+ }
+
+ return (0);
+}
+
+static int
+mt_uart_ioctl(struct uart_softc *sc, int request, intptr_t data)
+{
+ int baudrate;
+
+ if (request != UART_IOCTL_BAUD)
+ return (ns8250_bus_ioctl(sc, request, data));
+
+ uart_lock(sc->sc_hwmtx);
+ baudrate = mt_uart_get_baud(&sc->sc_bas);
+ uart_unlock(sc->sc_hwmtx);
+
+ if (baudrate <= 0)
+ return (ENXIO);
+ *(int *)data = baudrate;
+ return (0);
+}
+
+static int
+mt_uart_attach(struct uart_softc *sc)
+{
+ struct uart_bas *bas = &sc->sc_bas;
+ int rv;
+
+ rv = ns8250_bus_attach(sc);
+ if (rv != 0) {
+ return (rv);
+ }
+
+ uart_setreg(bas, MTK_UART_RATE_FIX, 0);
+ uart_barrier(bas);
+
+ return (0);
+}
+
+static kobj_method_t mt_methods[] = {
+ KOBJMETHOD(uart_probe, ns8250_bus_probe),
+ KOBJMETHOD(uart_attach, mt_uart_attach),
+ KOBJMETHOD(uart_detach, ns8250_bus_detach),
+ KOBJMETHOD(uart_flush, ns8250_bus_flush),
+ KOBJMETHOD(uart_getsig, ns8250_bus_getsig),
+ KOBJMETHOD(uart_ioctl, mt_uart_ioctl),
+ KOBJMETHOD(uart_ipend, ns8250_bus_ipend),
+ KOBJMETHOD(uart_param, mt_uart_param),
+ KOBJMETHOD(uart_receive, ns8250_bus_receive),
+ KOBJMETHOD(uart_setsig, ns8250_bus_setsig),
+ KOBJMETHOD(uart_transmit, ns8250_bus_transmit),
+ KOBJMETHOD(uart_txbusy, ns8250_bus_txbusy),
+ KOBJMETHOD(uart_grab, ns8250_bus_grab),
+ KOBJMETHOD(uart_ungrab, ns8250_bus_ungrab),
+ KOBJMETHOD_END
+};
+
+
+static int
+mt_uart_probe_bas(struct uart_bas *bas)
+{
+ return (uart_ns8250_ops.probe(bas));
+}
+
+static void
+mt_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
+ int parity)
+{
+ uart_ns8250_ops.init(bas, 0, databits, stopbits, parity);
+ mt_uart_set_baud(bas, baudrate);
+}
+
+static void
+mt_uart_term(struct uart_bas *bas)
+{
+ uart_ns8250_ops.term(bas);
+}
+
+static void
+mt_uart_putc(struct uart_bas *bas, int c)
+{
+ uart_ns8250_ops.putc(bas, c);
+}
+
+static int
+mt_uart_rxready(struct uart_bas *bas)
+{
+ return (uart_ns8250_ops.rxready(bas));
+}
+
+static int
+mt_uart_getc(struct uart_bas *bas, struct mtx *hwmtx)
+{
+ return (uart_ns8250_ops.getc(bas, hwmtx));
+}
+
+static struct uart_ops mt_uart_ops = {
+ .probe = mt_uart_probe_bas,
+ .init = mt_uart_init,
+ .term = mt_uart_term,
+ .putc = mt_uart_putc,
+ .rxready = mt_uart_rxready,
+ .getc = mt_uart_getc,
+};
+
+static struct uart_class mt_uart_class = {
+ "mediatek class",
+ mt_methods,
+ sizeof(struct mt_softc),
+ .uc_ops = &mt_uart_ops,
+ .uc_range = 8,
+ .uc_rclk = 0,
+ .uc_rshift = 2,
+ .uc_riowidth = 4,
+};
+
+/* Compatible devices. */
+static struct ofw_compat_data compat_data[] = {
+ {"mediatek,mt6577-uart", (uintptr_t) &mt_uart_class},
+ {NULL, (uintptr_t) NULL},
+};
+
+UART_FDT_CLASS(compat_data);
+
+/*
+ * UART Driver interface.
+ */
+static int mt_uart_get_shift(phandle_t node)
+{
+ pcell_t shift;
+
+ if ((OF_getencprop(node, "reg-shift", &shift, sizeof(shift))) <= 0)
+ {
+ shift = 2;
+ }
+
+ return ((int) shift);
+}
+
+static int
+mt_uart_probe(device_t dev)
+{
+ struct mt_softc *sc;
+ phandle_t node;
+ uint64_t freq;
+ int shift;
+ int rv;
+ const struct ofw_compat_data *cd;
+
+ sc = device_get_softc(dev);
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ cd = ofw_bus_search_compatible(dev, compat_data);
+ if (cd->ocd_data == 0)
+ return (ENXIO);
+ sc->ns8250_base.base.sc_class = (struct uart_class *)cd->ocd_data;
+
+ node = ofw_bus_get_node(dev);
+ shift = mt_uart_get_shift(node);
+ rv = clk_get_by_ofw_name(dev, 0, "baud", &sc->baud_clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get UART baud clock: %d\n", rv);
+ return (ENXIO);
+ }
+ rv = clk_enable(sc->baud_clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable UART baud clock: %d\n", rv);
+ return (ENXIO);
+ }
+
+ rv = clk_get_freq(sc->baud_clk, &freq);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get freq UART clock: %d\n", rv);
+ return (ENXIO);
+ }
+
+ return (uart_bus_probe(dev, shift, 0, (int)freq, 0, 0, 0));
+}
+
+static int
+mt_uart_detach(device_t dev)
+{
+ struct mt_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->baud_clk != NULL) {
+ clk_release(sc->baud_clk);
+ }
+
+ return (uart_bus_detach(dev));
+}
+
+static device_method_t mt_uart_bus_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mt_uart_probe),
+ DEVMETHOD(device_attach, uart_bus_attach),
+ DEVMETHOD(device_detach, mt_uart_detach),
+ DEVMETHOD_END
+};
+
+static driver_t mt_uart_driver = {
+ uart_driver_name,
+ mt_uart_bus_methods,
+ sizeof(struct mt_softc),
+};
+
+DRIVER_MODULE(mdtk_uart, simplebus, mt_uart_driver, 0, 0);
diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64
--- a/sys/conf/files.arm64
+++ b/sys/conf/files.arm64
@@ -865,3 +865,18 @@
dev/clk/xilinx/zynqmp_clk_mux.c optional fdt soc_xilinx_zynq
dev/clk/xilinx/zynqmp_clk_pll.c optional fdt soc_xilinx_zynq
dev/clk/xilinx/zynqmp_reset.c optional fdt soc_xilinx_zynq
+
+# Mediatek
+arm64/mediatek/mt_uart.c optional fdt soc_mediatek
+arm64/mediatek/mt7622_clk_topckgen.c optional clk fdt soc_mediatek
+arm64/mediatek/mt7622_clk_pericfg.c optional clk fdt soc_mediatek
+arm64/mediatek/mt7622_clk_infracfg.c optional clk fdt soc_mediatek
+arm64/mediatek/mt7622_clk_audio.c optional clk fdt soc_mediatek
+arm64/mediatek/mt7622_clk_eth.c optional clk fdt soc_mediatek
+arm64/mediatek/mt7622_clk_sgmii.c optional clk fdt soc_mediatek
+arm64/mediatek/mt7622_clk_ssusbsys.c optional clk fdt soc_mediatek
+arm64/mediatek/mt7622_clk_pciesys.c optional clk fdt soc_mediatek
+arm64/mediatek/mt_clk.c optional clk fdt soc_mediatek
+arm64/mediatek/mt_clk_pll.c optional clk fdt soc_mediatek
+arm64/mediatek/mt7622_clk_apmixedsys.c optional clk fdt soc_mediatek
+arm64/mediatek/mt7622_pinctrl.c optional fdt mt7622_pinctrl soc_mediatek
diff --git a/sys/conf/options.arm64 b/sys/conf/options.arm64
--- a/sys/conf/options.arm64
+++ b/sys/conf/options.arm64
@@ -39,6 +39,7 @@
SOC_HISI_HI6220 opt_soc.h
SOC_INTEL_STRATIX10 opt_soc.h
SOC_MARVELL_8K opt_soc.h
+SOC_MEDIATEK opt_soc.h
SOC_NVIDIA_TEGRA210 opt_soc.h
SOC_NXP_LS opt_soc.h
SOC_ROCKCHIP opt_soc.h
diff --git a/sys/modules/dtb/mediatek/Makefile b/sys/modules/dtb/mediatek/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/dtb/mediatek/Makefile
@@ -0,0 +1,7 @@
+.if ${MACHINE_ARCH} == "aarch64"
+DTS= mediatek/mt7622-bananapi-bpi-r64.dts \
+ mediatek/mt7986a-bananapi-bpi-r3-mini.dts \
+ mediatek/mt7986a-bananapi-bpi-r3.dts
+.endif
+
+.include <bsd.dtb.mk>
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Nov 3, 12:04 PM (8 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41366660
Default Alt Text
D57732.1793707462.diff (106 KB)
Attached To
Mode
D57732: arm64: mediatek: add initial MT7622/Banana Pi R64 support
Attached
Detach File
Event Timeline