Page MenuHomeFreeBSD

D57732.1793707462.diff
No OneTemporary

Authored By
Unknown
Size
106 KB
Referenced Files
None
Subscribers
None

D57732.1793707462.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_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

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)

Event Timeline