Page MenuHomeFreeBSD

D58812.id184103.diff
No OneTemporary

D58812.id184103.diff

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_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,132 @@
+/*
+ * 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"
+
+struct audsys_softc {
+ struct simplebus_softc simplebus_sc;
+ struct mt_clk_softc clk_sc;
+};
+
+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 audsys_softc *sc;
+ phandle_t node;
+
+ sc = device_get_softc(dev);
+ sc->clk_sc.clk_def = &clk_def;
+ node = ofw_bus_get_node(dev);
+ if (node == -1) {
+ return (ENXIO);
+ }
+
+ bus_identify_children(dev);
+
+ simplebus_init(dev, node);
+ for (node = OF_child(node); node > 0; node = OF_peer(node)) {
+ simplebus_add_device(dev, node, 0, NULL, -1, NULL);
+ }
+
+ bus_attach_children(dev);
+
+ return (mt_clk_attach_sc(dev, &sc->clk_sc));
+}
+
+static device_method_t mt7622_audio_methods[] = {
+ DEVMETHOD(device_probe, audio_clk_probe),
+ DEVMETHOD(device_attach, audio_clk_attach),
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(mt7622_audio, mt7622_audio_driver, mt7622_audio_methods,
+sizeof(struct audsys_softc), 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,71 @@
+/*
+ * 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 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_reset_softc *sc;
+ static const uint16_t reset_offset[] = { 0x34 };
+ sc = device_get_softc(dev);
+
+ sc->clk_sc.clk_def = &clk_def;
+ sc->reset_offset = reset_offset;
+ sc->reset_num = nitems(reset_offset);
+ 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,83 @@
+/*
+ * 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 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_reset_softc *sc;
+ static uint16_t reset_offset[] = { 0x30 };
+ sc = device_get_softc(dev);
+
+ sc->clk_sc.clk_def = &clk_def;
+ sc->reset_offset = reset_offset;
+ sc->reset_num = nitems(reset_offset);
+ 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,80 @@
+/*
+ * 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 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_reset_softc *sc;
+ static const uint16_t reset_offset[] = { 0x34 };
+ sc = device_get_softc(dev);
+
+ sc->clk_sc.clk_def = &clk_pcie_def;
+ sc->reset_offset = reset_offset;
+ sc->reset_num = nitems(reset_offset);
+ 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,109 @@
+/*
+ * 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 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_reset_softc *sc;
+ static const uint16_t reset_offset[] = { 0x0, 0x4 };
+ sc = device_get_softc(dev);
+
+ sc->clk_sc.clk_def = &clk_def;
+ sc->reset_offset = reset_offset;
+ sc->reset_num = nitems(reset_offset);
+ return (mt_clk_attach_sc(dev, &sc->clk_sc));
+}
+
+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_reset_softc),
+ mt_clk_reset_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,69 @@
+/*
+ * 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 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_reset_softc *sc;
+ static const uint16_t reset_offset[] = { 0x34 };
+ sc = device_get_softc(dev);
+
+ sc->clk_sc.clk_def = &clk_ssusb_def;
+ sc->reset_offset = reset_offset;
+ sc->reset_num = nitems(reset_offset);
+ 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,531 @@
+/*
+ * 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),
+ FRATE(CLK_APMIXED_ARMPLL, "armpll", 120000000ULL),
+ FRATE(CLK_APMIXED_MAINPLL, "mainpll", 1120000000ULL),
+ FRATE(CLK_APMIXED_UNIV2PLL, "univ2pll", 2400000000ULL),
+ FRATE(CLK_APMIXED_ETH1PLL, "eth1pll", 500000000ULL),
+ FRATE(CLK_APMIXED_ETH2PLL, "eth2pll", 650000000ULL),
+ FRATE(CLK_APMIXED_AUD1PLL, "aud1pll", 147456000ULL),
+ FRATE(CLK_APMIXED_AUD2PLL, "aud2pll", 135475000ULL),
+ FRATE(CLK_APMIXED_TRGPLL, "trgpll", 725000000ULL),
+ FRATE(CLK_APMIXED_SGMIPLL, "sgmipll", 650000000ULL),
+
+ 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,143 @@
+#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 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_linked;
+ int num_fixed;
+ int num_muxes;
+ int num_gates;
+ int num_dived;
+};
+
+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; /* clock tables for this block */
+};
+
+struct mt_clk_reset_softc {
+ struct mt_clk_softc clk_sc;
+ const uint16_t *reset_offset; /* reset register offsets or NULL */
+ uint16_t reset_num; /* number of reset registers */
+};
+
+DECLARE_CLASS(mt_clk_driver);
+DECLARE_CLASS(mt_clk_reset_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_attach_sc(device_t dev, struct mt_clk_softc *sc);
+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,329 @@
+/*
+ * 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 "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);
+}
+
+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_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_sc(device_t dev, struct mt_clk_softc *sc)
+{
+ int rv, rid = 0;
+
+ sc->dev = 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);
+ }
+
+ return (0);
+}
+
+int
+mt_clk_attach(device_t dev)
+{
+ return (mt_clk_attach_sc(dev, device_get_softc(dev)));
+}
+
+static int
+mt_clk_detach(device_t dev)
+{
+ device_printf(dev, "Error: Clock driver cannot be detached\n");
+ return (EBUSY);
+}
+
+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_END
+};
+
+DEFINE_CLASS_0(mt_clk, mt_clk_driver, mt_clk_methods,
+sizeof(struct mt_clk_softc));
+
+
+static int
+mt_clk_hwreset_assert(device_t dev, intptr_t id, bool value)
+{
+ struct mt_clk_reset_softc *sc;
+ uint32_t mask;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+
+ if (id < 0 || id / 32 >= sc->reset_num) {
+ return (ENXIO);
+ }
+
+ reg = sc->reset_offset[id / 32];
+ mask = 1u << (id % 32);
+
+ CLKDEV_DEVICE_LOCK(dev);
+ CLKDEV_MODIFY_4(dev, reg, mask, value ? mask : 0);
+ CLKDEV_DEVICE_UNLOCK(dev);
+ return (0);
+}
+
+static device_method_t mt_clk_reset_methods[] = {
+ DEVMETHOD(hwreset_assert, mt_clk_hwreset_assert),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(mt_reset_clk, mt_clk_reset_driver, mt_clk_reset_methods,
+sizeof(struct mt_clk_reset_softc), mt_clk_driver);
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,189 @@
+/*
+ * Copyright (c) 2025, 2026 Martin Filla <freebsd@sysctl.cz>
+ * Copyright (c) 2025 Michal Meloun <mmel@FreeBSD.org>
+ *
+ * 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/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/uart/uart.h>
+#include <dev/uart/uart_cpu.h>
+#include <dev/uart/uart_cpu_fdt.h>
+#include <dev/uart/uart_bus.h>
+#include <dev/uart/uart_dev_ns8250.h>
+#include <dev/ic/ns16550.h>
+
+#include "uart_if.h"
+
+struct mt_softc {
+ struct ns8250_softc ns8250_base;
+ clk_t baud_clk;
+ clk_t bus_clk;
+};
+
+static int
+mt_uart_attach(struct uart_softc *sc)
+{
+ int rv;
+ struct ns8250_softc *ns8250 = (struct ns8250_softc *)sc;
+ struct uart_bas *bas = &sc->sc_bas;
+
+ rv = ns8250_bus_attach(sc);
+ if (rv != 0)
+ return (rv);
+
+ ns8250->ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask;
+ ns8250->ier |= ns8250->ier_rxbits;
+ uart_setreg(bas, REG_IER, ns8250->ier);
+ 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, ns8250_bus_ioctl),
+ KOBJMETHOD(uart_ipend, ns8250_bus_ipend),
+ KOBJMETHOD(uart_param, ns8250_bus_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 struct uart_class mt_uart_class = {
+ "mediatek class",
+ mt_methods,
+ sizeof(struct mt_softc),
+ .uc_ops = &uart_ns8250_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_by_ofw_name(dev, 0, "bus", &sc->bus_clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get UART bus clock: %d\n", rv);
+ return (ENXIO);
+ }
+ rv = clk_enable(sc->bus_clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable UART bus 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);
+ }
+
+ if (sc->bus_clk != NULL) {
+ clk_release(sc->bus_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
@@ -852,3 +852,16 @@
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/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/geom/geom_ctl.c b/sys/geom/geom_ctl.c
--- a/sys/geom/geom_ctl.c
+++ b/sys/geom/geom_ctl.c
@@ -68,7 +68,7 @@
{
make_dev_credf(MAKEDEV_ETERNAL, &g_ctl_cdevsw, 0, NULL,
- UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL);
+ UID_ROOT, GID_WHEEL, 0640, PATH_GEOM_CTL);
}
/*
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

Mime Type
text/plain
Expires
Wed, Aug 19, 5:09 PM (1 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
36948796
Default Alt Text
D58812.id184103.diff (84 KB)

Event Timeline