--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MTMIPS SoCs Clock Driver
+ * Author: Sergio Paracuellos <sergio.paracuellos@gmail.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+
+/* Configuration registers */
+#define SYSC_REG_SYSTEM_CONFIG         0x10
+#define SYSC_REG_CLKCFG0               0x2c
+#define SYSC_REG_RESET_CTRL            0x34
+#define SYSC_REG_CPU_SYS_CLKCFG                0x3c
+#define SYSC_REG_CPLL_CONFIG0          0x54
+#define SYSC_REG_CPLL_CONFIG1          0x58
+
+/* RT2880 SoC */
+#define RT2880_CONFIG_CPUCLK_SHIFT     20
+#define RT2880_CONFIG_CPUCLK_MASK      0x3
+#define RT2880_CONFIG_CPUCLK_250       0x0
+#define RT2880_CONFIG_CPUCLK_266       0x1
+#define RT2880_CONFIG_CPUCLK_280       0x2
+#define RT2880_CONFIG_CPUCLK_300       0x3
+
+/* RT305X SoC */
+#define RT305X_SYSCFG_CPUCLK_SHIFT     18
+#define RT305X_SYSCFG_CPUCLK_MASK      0x1
+#define RT305X_SYSCFG_CPUCLK_LOW       0x0
+#define RT305X_SYSCFG_CPUCLK_HIGH      0x1
+
+/* RT3352 SoC */
+#define RT3352_SYSCFG0_CPUCLK_SHIFT    8
+#define RT3352_SYSCFG0_CPUCLK_MASK     0x1
+#define RT3352_SYSCFG0_CPUCLK_LOW      0x0
+#define RT3352_SYSCFG0_CPUCLK_HIGH     0x1
+
+/* RT3383 SoC */
+#define RT3883_SYSCFG0_DRAM_TYPE_DDR2  BIT(17)
+#define RT3883_SYSCFG0_CPUCLK_SHIFT    8
+#define RT3883_SYSCFG0_CPUCLK_MASK     0x3
+#define RT3883_SYSCFG0_CPUCLK_250      0x0
+#define RT3883_SYSCFG0_CPUCLK_384      0x1
+#define RT3883_SYSCFG0_CPUCLK_480      0x2
+#define RT3883_SYSCFG0_CPUCLK_500      0x3
+
+/* RT5350 SoC */
+#define RT5350_CLKCFG0_XTAL_SEL                BIT(20)
+#define RT5350_SYSCFG0_CPUCLK_SHIFT    8
+#define RT5350_SYSCFG0_CPUCLK_MASK     0x3
+#define RT5350_SYSCFG0_CPUCLK_360      0x0
+#define RT5350_SYSCFG0_CPUCLK_320      0x2
+#define RT5350_SYSCFG0_CPUCLK_300      0x3
+
+/* MT7620 and MT76x8 SoCs */
+#define MT7620_XTAL_FREQ_SEL           BIT(6)
+#define CPLL_CFG0_SW_CFG               BIT(31)
+#define CPLL_CFG0_PLL_MULT_RATIO_SHIFT 16
+#define CPLL_CFG0_PLL_MULT_RATIO_MASK   0x7
+#define CPLL_CFG0_LC_CURFCK            BIT(15)
+#define CPLL_CFG0_BYPASS_REF_CLK       BIT(14)
+#define CPLL_CFG0_PLL_DIV_RATIO_SHIFT  10
+#define CPLL_CFG0_PLL_DIV_RATIO_MASK   0x3
+#define CPLL_CFG1_CPU_AUX1             BIT(25)
+#define CPLL_CFG1_CPU_AUX0             BIT(24)
+#define CLKCFG0_PERI_CLK_SEL           BIT(4)
+#define CPU_SYS_CLKCFG_OCP_RATIO_SHIFT 16
+#define CPU_SYS_CLKCFG_OCP_RATIO_MASK  0xf
+#define CPU_SYS_CLKCFG_OCP_RATIO_1     0       /* 1:1   (Reserved) */
+#define CPU_SYS_CLKCFG_OCP_RATIO_1_5   1       /* 1:1.5 (Reserved) */
+#define CPU_SYS_CLKCFG_OCP_RATIO_2     2       /* 1:2   */
+#define CPU_SYS_CLKCFG_OCP_RATIO_2_5   3       /* 1:2.5 (Reserved) */
+#define CPU_SYS_CLKCFG_OCP_RATIO_3     4       /* 1:3   */
+#define CPU_SYS_CLKCFG_OCP_RATIO_3_5   5       /* 1:3.5 (Reserved) */
+#define CPU_SYS_CLKCFG_OCP_RATIO_4     6       /* 1:4   */
+#define CPU_SYS_CLKCFG_OCP_RATIO_5     7       /* 1:5   */
+#define CPU_SYS_CLKCFG_OCP_RATIO_10    8       /* 1:10  */
+#define CPU_SYS_CLKCFG_CPU_FDIV_SHIFT  8
+#define CPU_SYS_CLKCFG_CPU_FDIV_MASK   0x1f
+#define CPU_SYS_CLKCFG_CPU_FFRAC_SHIFT 0
+#define CPU_SYS_CLKCFG_CPU_FFRAC_MASK  0x1f
+
+/* clock scaling */
+#define CLKCFG_FDIV_MASK               0x1f00
+#define CLKCFG_FDIV_USB_VAL            0x0300
+#define CLKCFG_FFRAC_MASK              0x001f
+#define CLKCFG_FFRAC_USB_VAL           0x0003
+
+struct mtmips_clk;
+struct mtmips_clk_fixed;
+struct mtmips_clk_factor;
+
+struct mtmips_clk_data {
+       struct mtmips_clk *clk_base;
+       size_t num_clk_base;
+       struct mtmips_clk_fixed *clk_fixed;
+       size_t num_clk_fixed;
+       struct mtmips_clk_factor *clk_factor;
+       size_t num_clk_factor;
+       struct mtmips_clk *clk_periph;
+       size_t num_clk_periph;
+};
+
+struct mtmips_clk_priv {
+       struct regmap *sysc;
+       const struct mtmips_clk_data *data;
+};
+
+struct mtmips_clk {
+       struct clk_hw hw;
+       struct mtmips_clk_priv *priv;
+};
+
+struct mtmips_clk_fixed {
+       const char *name;
+       const char *parent;
+       unsigned long rate;
+       struct clk_hw *hw;
+};
+
+struct mtmips_clk_factor {
+       const char *name;
+       const char *parent;
+       int mult;
+       int div;
+       unsigned long flags;
+       struct clk_hw *hw;
+};
+
+static unsigned long mtmips_pherip_clk_rate(struct clk_hw *hw,
+                                           unsigned long parent_rate)
+{
+       return parent_rate;
+}
+
+static const struct clk_ops mtmips_periph_clk_ops = {
+       .recalc_rate = mtmips_pherip_clk_rate,
+};
+
+#define CLK_PERIPH(_name, _parent) {                           \
+       .init = &(const struct clk_init_data) {                 \
+               .name = _name,                                  \
+               .ops = &mtmips_periph_clk_ops,                  \
+               .parent_data = &(const struct clk_parent_data) {\
+                       .name = _parent,                        \
+                       .fw_name = _parent                      \
+               },                                              \
+               .num_parents = 1,                               \
+               /*                                              \
+                * There are drivers for these SoCs that are    \
+                * older than clock driver and are not prepared \
+                * for the clock. We don't want the kernel to   \
+                * disable anything so we add CLK_IS_CRITICAL   \
+                * flag here.                                   \
+                */                                             \
+               .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL  \
+       },                                                      \
+}
+
+static struct mtmips_clk rt2880_pherip_clks[] = {
+       { CLK_PERIPH("300100.timer", "bus") },
+       { CLK_PERIPH("300120.watchdog", "bus") },
+       { CLK_PERIPH("300500.uart", "bus") },
+       { CLK_PERIPH("300900.i2c", "bus") },
+       { CLK_PERIPH("300c00.uartlite", "bus") },
+       { CLK_PERIPH("400000.ethernet", "bus") },
+       { CLK_PERIPH("480000.wmac", "xtal") }
+};
+
+static struct mtmips_clk rt305x_pherip_clks[] = {
+       { CLK_PERIPH("10000100.timer", "bus") },
+       { CLK_PERIPH("10000120.watchdog", "bus") },
+       { CLK_PERIPH("10000500.uart", "bus") },
+       { CLK_PERIPH("10000900.i2c", "bus") },
+       { CLK_PERIPH("10000a00.i2s", "bus") },
+       { CLK_PERIPH("10000b00.spi", "bus") },
+       { CLK_PERIPH("10000b40.spi", "bus") },
+       { CLK_PERIPH("10000c00.uartlite", "bus") },
+       { CLK_PERIPH("10100000.ethernet", "bus") },
+       { CLK_PERIPH("10180000.wmac", "xtal") }
+};
+
+static struct mtmips_clk rt5350_pherip_clks[] = {
+       { CLK_PERIPH("10000100.timer", "bus") },
+       { CLK_PERIPH("10000120.watchdog", "bus") },
+       { CLK_PERIPH("10000500.uart", "periph") },
+       { CLK_PERIPH("10000900.i2c", "periph") },
+       { CLK_PERIPH("10000a00.i2s", "periph") },
+       { CLK_PERIPH("10000b00.spi", "bus") },
+       { CLK_PERIPH("10000b40.spi", "bus") },
+       { CLK_PERIPH("10000c00.uartlite", "periph") },
+       { CLK_PERIPH("10100000.ethernet", "bus") },
+       { CLK_PERIPH("10180000.wmac", "xtal") }
+};
+
+static struct mtmips_clk mt7620_pherip_clks[] = {
+       { CLK_PERIPH("10000100.timer", "periph") },
+       { CLK_PERIPH("10000120.watchdog", "periph") },
+       { CLK_PERIPH("10000500.uart", "periph") },
+       { CLK_PERIPH("10000900.i2c", "periph") },
+       { CLK_PERIPH("10000a00.i2s", "periph") },
+       { CLK_PERIPH("10000b00.spi", "bus") },
+       { CLK_PERIPH("10000b40.spi", "bus") },
+       { CLK_PERIPH("10000c00.uartlite", "periph") },
+       { CLK_PERIPH("10180000.wmac", "xtal") }
+};
+
+static struct mtmips_clk mt76x8_pherip_clks[] = {
+       { CLK_PERIPH("10000100.timer", "periph") },
+       { CLK_PERIPH("10000120.watchdog", "periph") },
+       { CLK_PERIPH("10000900.i2c", "periph") },
+       { CLK_PERIPH("10000a00.i2s", "pcmi2s") },
+       { CLK_PERIPH("10000b00.spi", "bus") },
+       { CLK_PERIPH("10000b40.spi", "bus") },
+       { CLK_PERIPH("10000c00.uart0", "periph") },
+       { CLK_PERIPH("10000d00.uart1", "periph") },
+       { CLK_PERIPH("10000e00.uart2", "periph") },
+       { CLK_PERIPH("10300000.wmac", "xtal") }
+};
+
+static int mtmips_register_pherip_clocks(struct device_node *np,
+                                        struct clk_hw_onecell_data *clk_data,
+                                        struct mtmips_clk_priv *priv)
+{
+       struct clk_hw **hws = clk_data->hws;
+       struct mtmips_clk *sclk;
+       size_t idx_start = priv->data->num_clk_base + priv->data->num_clk_fixed +
+                          priv->data->num_clk_factor;
+       int ret, i;
+
+       for (i = 0; i < priv->data->num_clk_periph; i++) {
+               int idx = idx_start + i;
+
+               sclk = &priv->data->clk_periph[i];
+               ret = of_clk_hw_register(np, &sclk->hw);
+               if (ret) {
+                       pr_err("Couldn't register peripheral clock %d\n", idx);
+                       goto err_clk_unreg;
+               }
+
+               hws[idx] = &sclk->hw;
+       }
+
+       return 0;
+
+err_clk_unreg:
+       while (--i >= 0) {
+               sclk = &priv->data->clk_periph[i];
+               clk_hw_unregister(&sclk->hw);
+       }
+       return ret;
+}
+
+#define CLK_FIXED(_name, _parent, _rate) \
+       {                                \
+               .name = _name,           \
+               .parent = _parent,       \
+               .rate = _rate            \
+       }
+
+static struct mtmips_clk_fixed rt305x_fixed_clocks[] = {
+       CLK_FIXED("xtal", NULL, 40000000)
+};
+
+static struct mtmips_clk_fixed rt3352_fixed_clocks[] = {
+       CLK_FIXED("periph", "xtal", 40000000)
+};
+
+static struct mtmips_clk_fixed mt76x8_fixed_clocks[] = {
+       CLK_FIXED("pcmi2s", "xtal", 480000000),
+       CLK_FIXED("periph", "xtal", 40000000)
+};
+
+static int mtmips_register_fixed_clocks(struct clk_hw_onecell_data *clk_data,
+                                       struct mtmips_clk_priv *priv)
+{
+       struct clk_hw **hws = clk_data->hws;
+       struct mtmips_clk_fixed *sclk;
+       size_t idx_start = priv->data->num_clk_base;
+       int ret, i;
+
+       for (i = 0; i < priv->data->num_clk_fixed; i++) {
+               int idx = idx_start + i;
+
+               sclk = &priv->data->clk_fixed[i];
+               sclk->hw = clk_hw_register_fixed_rate(NULL, sclk->name,
+                                                     sclk->parent, 0,
+                                                     sclk->rate);
+               if (IS_ERR(sclk->hw)) {
+                       pr_err("Couldn't register fixed clock %d\n", idx);
+                       goto err_clk_unreg;
+               }
+
+               hws[idx] = sclk->hw;
+       }
+
+       return 0;
+
+err_clk_unreg:
+       while (--i >= 0) {
+               sclk = &priv->data->clk_fixed[i];
+               clk_hw_unregister_fixed_rate(sclk->hw);
+       }
+       return ret;
+}
+
+#define CLK_FACTOR(_name, _parent, _mult, _div)                \
+       {                                               \
+               .name = _name,                          \
+               .parent = _parent,                      \
+               .mult = _mult,                          \
+               .div = _div,                            \
+               .flags = CLK_SET_RATE_PARENT            \
+       }
+
+static struct mtmips_clk_factor rt2880_factor_clocks[] = {
+       CLK_FACTOR("bus", "cpu", 1, 2)
+};
+
+static struct mtmips_clk_factor rt305x_factor_clocks[] = {
+       CLK_FACTOR("bus", "cpu", 1, 3)
+};
+
+static int mtmips_register_factor_clocks(struct clk_hw_onecell_data *clk_data,
+                                        struct mtmips_clk_priv *priv)
+{
+       struct clk_hw **hws = clk_data->hws;
+       struct mtmips_clk_factor *sclk;
+       size_t idx_start = priv->data->num_clk_base + priv->data->num_clk_fixed;
+       int ret, i;
+
+       for (i = 0; i < priv->data->num_clk_factor; i++) {
+               int idx = idx_start + i;
+
+               sclk = &priv->data->clk_factor[i];
+               sclk->hw = clk_hw_register_fixed_factor(NULL, sclk->name,
+                                                 sclk->parent, sclk->flags,
+                                                 sclk->mult, sclk->div);
+               if (IS_ERR(sclk->hw)) {
+                       pr_err("Couldn't register factor clock %d\n", idx);
+                       goto err_clk_unreg;
+               }
+
+               hws[idx] = sclk->hw;
+       }
+
+       return 0;
+
+err_clk_unreg:
+       while (--i >= 0) {
+               sclk = &priv->data->clk_factor[i];
+               clk_hw_unregister_fixed_factor(sclk->hw);
+       }
+       return ret;
+}
+
+static inline struct mtmips_clk *to_mtmips_clk(struct clk_hw *hw)
+{
+       return container_of(hw, struct mtmips_clk, hw);
+}
+
+static unsigned long rt5350_xtal_recalc_rate(struct clk_hw *hw,
+                                            unsigned long parent_rate)
+{
+       struct mtmips_clk *clk = to_mtmips_clk(hw);
+       struct regmap *sysc = clk->priv->sysc;
+       u32 val;
+
+       regmap_read(sysc, SYSC_REG_SYSTEM_CONFIG, &val);
+       if (!(val & RT5350_CLKCFG0_XTAL_SEL))
+               return 20000000;
+
+       return 40000000;
+}
+
+static unsigned long rt5350_cpu_recalc_rate(struct clk_hw *hw,
+                                           unsigned long xtal_clk)
+{
+       struct mtmips_clk *clk = to_mtmips_clk(hw);
+       struct regmap *sysc = clk->priv->sysc;
+       u32 t;
+
+       regmap_read(sysc, SYSC_REG_SYSTEM_CONFIG, &t);
+       t = (t >> RT5350_SYSCFG0_CPUCLK_SHIFT) & RT5350_SYSCFG0_CPUCLK_MASK;
+
+       switch (t) {
+       case RT5350_SYSCFG0_CPUCLK_360:
+               return 360000000;
+       case RT5350_SYSCFG0_CPUCLK_320:
+               return 320000000;
+       case RT5350_SYSCFG0_CPUCLK_300:
+               return 300000000;
+       default:
+               BUG();
+       }
+}
+
+static unsigned long rt5350_bus_recalc_rate(struct clk_hw *hw,
+                                           unsigned long parent_rate)
+{
+       if (parent_rate == 320000000)
+               return parent_rate / 4;
+
+       return parent_rate / 3;
+}
+
+static unsigned long rt3352_cpu_recalc_rate(struct clk_hw *hw,
+                                           unsigned long xtal_clk)
+{
+       struct mtmips_clk *clk = to_mtmips_clk(hw);
+       struct regmap *sysc = clk->priv->sysc;
+       u32 t;
+
+       regmap_read(sysc, SYSC_REG_SYSTEM_CONFIG, &t);
+       t = (t >> RT3352_SYSCFG0_CPUCLK_SHIFT) & RT3352_SYSCFG0_CPUCLK_MASK;
+
+       switch (t) {
+       case RT3352_SYSCFG0_CPUCLK_LOW:
+               return 384000000;
+       case RT3352_SYSCFG0_CPUCLK_HIGH:
+               return 400000000;
+       default:
+               BUG();
+       }
+}
+
+static unsigned long rt305x_cpu_recalc_rate(struct clk_hw *hw,
+                                           unsigned long xtal_clk)
+{
+       struct mtmips_clk *clk = to_mtmips_clk(hw);
+       struct regmap *sysc = clk->priv->sysc;
+       u32 t;
+
+       regmap_read(sysc, SYSC_REG_SYSTEM_CONFIG, &t);
+       t = (t >> RT305X_SYSCFG_CPUCLK_SHIFT) & RT305X_SYSCFG_CPUCLK_MASK;
+
+       switch (t) {
+       case RT305X_SYSCFG_CPUCLK_LOW:
+               return 320000000;
+       case RT305X_SYSCFG_CPUCLK_HIGH:
+               return 384000000;
+       default:
+               BUG();
+       }
+}
+
+static unsigned long rt3883_cpu_recalc_rate(struct clk_hw *hw,
+                                           unsigned long xtal_clk)
+{
+       struct mtmips_clk *clk = to_mtmips_clk(hw);
+       struct regmap *sysc = clk->priv->sysc;
+       u32 t;
+
+       regmap_read(sysc, SYSC_REG_SYSTEM_CONFIG, &t);
+       t = (t >> RT3883_SYSCFG0_CPUCLK_SHIFT) & RT3883_SYSCFG0_CPUCLK_MASK;
+
+       switch (t) {
+       case RT3883_SYSCFG0_CPUCLK_250:
+               return 250000000;
+       case RT3883_SYSCFG0_CPUCLK_384:
+               return 384000000;
+       case RT3883_SYSCFG0_CPUCLK_480:
+               return 480000000;
+       case RT3883_SYSCFG0_CPUCLK_500:
+               return 500000000;
+       default:
+               BUG();
+       }
+}
+
+static unsigned long rt3883_bus_recalc_rate(struct clk_hw *hw,
+                                           unsigned long parent_rate)
+{
+       struct mtmips_clk *clk = to_mtmips_clk(hw);
+       struct regmap *sysc = clk->priv->sysc;
+       u32 ddr2;
+       u32 t;
+
+       regmap_read(sysc, SYSC_REG_SYSTEM_CONFIG, &t);
+       ddr2 = t & RT3883_SYSCFG0_DRAM_TYPE_DDR2;
+
+       switch (parent_rate) {
+       case 250000000:
+               return (ddr2) ? 125000000 : 83000000;
+       case 384000000:
+               return (ddr2) ? 128000000 : 96000000;
+       case 480000000:
+               return (ddr2) ? 160000000 : 120000000;
+       case 500000000:
+               return (ddr2) ? 166000000 : 125000000;
+       default:
+               WARN_ON_ONCE(parent_rate == 0);
+               return parent_rate / 4;
+       }
+}
+
+static unsigned long rt2880_cpu_recalc_rate(struct clk_hw *hw,
+                                           unsigned long xtal_clk)
+{
+       struct mtmips_clk *clk = to_mtmips_clk(hw);
+       struct regmap *sysc = clk->priv->sysc;
+       u32 t;
+
+       regmap_read(sysc, SYSC_REG_SYSTEM_CONFIG, &t);
+       t = (t >> RT2880_CONFIG_CPUCLK_SHIFT) & RT2880_CONFIG_CPUCLK_MASK;
+
+       switch (t) {
+       case RT2880_CONFIG_CPUCLK_250:
+               return 250000000;
+       case RT2880_CONFIG_CPUCLK_266:
+               return 266000000;
+       case RT2880_CONFIG_CPUCLK_280:
+               return 280000000;
+       case RT2880_CONFIG_CPUCLK_300:
+               return 300000000;
+       default:
+               BUG();
+       }
+}
+
+static u32 mt7620_calc_rate(u32 ref_rate, u32 mul, u32 div)
+{
+       u64 t;
+
+       t = ref_rate;
+       t *= mul;
+       t = div_u64(t, div);
+
+       return t;
+}
+
+static unsigned long mt7620_pll_recalc_rate(struct clk_hw *hw,
+                                           unsigned long parent_rate)
+{
+       static const u32 clk_divider[] = { 2, 3, 4, 8 };
+       struct mtmips_clk *clk = to_mtmips_clk(hw);
+       struct regmap *sysc = clk->priv->sysc;
+       unsigned long cpu_pll;
+       u32 t;
+       u32 mul;
+       u32 div;
+
+       regmap_read(sysc, SYSC_REG_CPLL_CONFIG0, &t);
+       if (t & CPLL_CFG0_BYPASS_REF_CLK) {
+               cpu_pll = parent_rate;
+       } else if ((t & CPLL_CFG0_SW_CFG) == 0) {
+               cpu_pll = 600000000;
+       } else {
+               mul = (t >> CPLL_CFG0_PLL_MULT_RATIO_SHIFT) &
+                       CPLL_CFG0_PLL_MULT_RATIO_MASK;
+               mul += 24;
+               if (t & CPLL_CFG0_LC_CURFCK)
+                       mul *= 2;
+
+               div = (t >> CPLL_CFG0_PLL_DIV_RATIO_SHIFT) &
+                       CPLL_CFG0_PLL_DIV_RATIO_MASK;
+
+               WARN_ON_ONCE(div >= ARRAY_SIZE(clk_divider));
+
+               cpu_pll = mt7620_calc_rate(parent_rate, mul, clk_divider[div]);
+       }
+
+       regmap_read(sysc, SYSC_REG_CPLL_CONFIG1, &t);
+       if (t & CPLL_CFG1_CPU_AUX1)
+               return parent_rate;
+
+       if (t & CPLL_CFG1_CPU_AUX0)
+               return 480000000;
+
+       return cpu_pll;
+}
+
+static unsigned long mt7620_cpu_recalc_rate(struct clk_hw *hw,
+                                           unsigned long parent_rate)
+{
+       struct mtmips_clk *clk = to_mtmips_clk(hw);
+       struct regmap *sysc = clk->priv->sysc;
+       u32 t;
+       u32 mul;
+       u32 div;
+
+       regmap_read(sysc, SYSC_REG_CPU_SYS_CLKCFG, &t);
+       mul = t & CPU_SYS_CLKCFG_CPU_FFRAC_MASK;
+       div = (t >> CPU_SYS_CLKCFG_CPU_FDIV_SHIFT) &
+               CPU_SYS_CLKCFG_CPU_FDIV_MASK;
+
+       return mt7620_calc_rate(parent_rate, mul, div);
+}
+
+static unsigned long mt7620_bus_recalc_rate(struct clk_hw *hw,
+                                           unsigned long parent_rate)
+{
+       static const u32 ocp_dividers[16] = {
+               [CPU_SYS_CLKCFG_OCP_RATIO_2] = 2,
+               [CPU_SYS_CLKCFG_OCP_RATIO_3] = 3,
+               [CPU_SYS_CLKCFG_OCP_RATIO_4] = 4,
+               [CPU_SYS_CLKCFG_OCP_RATIO_5] = 5,
+               [CPU_SYS_CLKCFG_OCP_RATIO_10] = 10,
+       };
+       struct mtmips_clk *clk = to_mtmips_clk(hw);
+       struct regmap *sysc = clk->priv->sysc;
+       u32 t;
+       u32 ocp_ratio;
+       u32 div;
+
+       regmap_read(sysc, SYSC_REG_CPU_SYS_CLKCFG, &t);
+       ocp_ratio = (t >> CPU_SYS_CLKCFG_OCP_RATIO_SHIFT) &
+               CPU_SYS_CLKCFG_OCP_RATIO_MASK;
+
+       if (WARN_ON_ONCE(ocp_ratio >= ARRAY_SIZE(ocp_dividers)))
+               return parent_rate;
+
+       div = ocp_dividers[ocp_ratio];
+
+       if (WARN(!div, "invalid divider for OCP ratio %u", ocp_ratio))
+               return parent_rate;
+
+       return parent_rate / div;
+}
+
+static unsigned long mt7620_periph_recalc_rate(struct clk_hw *hw,
+                                              unsigned long parent_rate)
+{
+       struct mtmips_clk *clk = to_mtmips_clk(hw);
+       struct regmap *sysc = clk->priv->sysc;
+       u32 t;
+
+       regmap_read(sysc, SYSC_REG_CLKCFG0, &t);
+       if (t & CLKCFG0_PERI_CLK_SEL)
+               return parent_rate;
+
+       return 40000000;
+}
+
+static unsigned long mt76x8_xtal_recalc_rate(struct clk_hw *hw,
+                                            unsigned long parent_rate)
+{
+       struct mtmips_clk *clk = to_mtmips_clk(hw);
+       struct regmap *sysc = clk->priv->sysc;
+       u32 t;
+
+       regmap_read(sysc, SYSC_REG_SYSTEM_CONFIG, &t);
+       if (t & MT7620_XTAL_FREQ_SEL)
+               return 40000000;
+
+       return 20000000;
+}
+
+static unsigned long mt76x8_cpu_recalc_rate(struct clk_hw *hw,
+                                           unsigned long xtal_clk)
+{
+       if (xtal_clk == 40000000)
+               return 580000000;
+
+       return 575000000;
+}
+
+#define CLK_BASE(_name, _parent, _recalc) {                            \
+       .init = &(const struct clk_init_data) {                         \
+               .name = _name,                                          \
+               .ops = &(const struct clk_ops) {                        \
+                       .recalc_rate = _recalc,                         \
+               },                                                      \
+               .parent_data = &(const struct clk_parent_data) {        \
+                       .name = _parent,                                \
+                       .fw_name = _parent                              \
+               },                                                      \
+               .num_parents = _parent ? 1 : 0                          \
+       },                                                              \
+}
+
+static struct mtmips_clk rt2880_clks_base[] = {
+       { CLK_BASE("cpu", "xtal", rt2880_cpu_recalc_rate) }
+};
+
+static struct mtmips_clk rt305x_clks_base[] = {
+       { CLK_BASE("cpu", "xtal", rt305x_cpu_recalc_rate) }
+};
+
+static struct mtmips_clk rt3352_clks_base[] = {
+       { CLK_BASE("xtal", NULL, rt5350_xtal_recalc_rate) },
+       { CLK_BASE("cpu", "xtal", rt3352_cpu_recalc_rate) }
+};
+
+static struct mtmips_clk rt3883_clks_base[] = {
+       { CLK_BASE("cpu", "xtal", rt3883_cpu_recalc_rate) },
+       { CLK_BASE("bus", "cpu", rt3883_bus_recalc_rate) }
+};
+
+static struct mtmips_clk rt5350_clks_base[] = {
+       { CLK_BASE("xtal", NULL, rt5350_xtal_recalc_rate) },
+       { CLK_BASE("cpu", "xtal", rt5350_cpu_recalc_rate) },
+       { CLK_BASE("bus", "cpu", rt5350_bus_recalc_rate) }
+};
+
+static struct mtmips_clk mt7620_clks_base[] = {
+       { CLK_BASE("xtal", NULL, mt76x8_xtal_recalc_rate) },
+       { CLK_BASE("pll", "xtal", mt7620_pll_recalc_rate) },
+       { CLK_BASE("cpu", "pll", mt7620_cpu_recalc_rate) },
+       { CLK_BASE("periph", "xtal", mt7620_periph_recalc_rate) },
+       { CLK_BASE("bus", "cpu", mt7620_bus_recalc_rate) }
+};
+
+static struct mtmips_clk mt76x8_clks_base[] = {
+       { CLK_BASE("xtal", NULL, mt76x8_xtal_recalc_rate) },
+       { CLK_BASE("cpu", "xtal", mt76x8_cpu_recalc_rate) }
+};
+
+static int mtmips_register_clocks(struct device_node *np,
+                                 struct clk_hw_onecell_data *clk_data,
+                                 struct mtmips_clk_priv *priv)
+{
+       struct clk_hw **hws = clk_data->hws;
+       struct mtmips_clk *sclk;
+       int ret, i;
+
+       for (i = 0; i < priv->data->num_clk_base; i++) {
+               sclk = &priv->data->clk_base[i];
+               sclk->priv = priv;
+               ret = of_clk_hw_register(np, &sclk->hw);
+               if (ret) {
+                       pr_err("Couldn't register top clock %i\n", i);
+                       goto err_clk_unreg;
+               }
+
+               hws[i] = &sclk->hw;
+       }
+
+       return 0;
+
+err_clk_unreg:
+       while (--i >= 0) {
+               sclk = &priv->data->clk_base[i];
+               clk_hw_unregister(&sclk->hw);
+       }
+       return ret;
+}
+
+static const struct mtmips_clk_data rt2880_clk_data = {
+       .clk_base = rt2880_clks_base,
+       .num_clk_base = ARRAY_SIZE(rt2880_clks_base),
+       .clk_fixed = rt305x_fixed_clocks,
+       .num_clk_fixed = ARRAY_SIZE(rt305x_fixed_clocks),
+       .clk_factor = rt2880_factor_clocks,
+       .num_clk_factor = ARRAY_SIZE(rt2880_factor_clocks),
+       .clk_periph = rt2880_pherip_clks,
+       .num_clk_periph = ARRAY_SIZE(rt2880_pherip_clks),
+};
+
+static const struct mtmips_clk_data rt305x_clk_data = {
+       .clk_base = rt305x_clks_base,
+       .num_clk_base = ARRAY_SIZE(rt305x_clks_base),
+       .clk_fixed = rt305x_fixed_clocks,
+       .num_clk_fixed = ARRAY_SIZE(rt305x_fixed_clocks),
+       .clk_factor = rt305x_factor_clocks,
+       .num_clk_factor = ARRAY_SIZE(rt305x_factor_clocks),
+       .clk_periph = rt305x_pherip_clks,
+       .num_clk_periph = ARRAY_SIZE(rt305x_pherip_clks),
+};
+
+static const struct mtmips_clk_data rt3352_clk_data = {
+       .clk_base = rt3352_clks_base,
+       .num_clk_base = ARRAY_SIZE(rt3352_clks_base),
+       .clk_fixed = rt3352_fixed_clocks,
+       .num_clk_fixed = ARRAY_SIZE(rt3352_fixed_clocks),
+       .clk_factor = rt305x_factor_clocks,
+       .num_clk_factor = ARRAY_SIZE(rt305x_factor_clocks),
+       .clk_periph = rt5350_pherip_clks,
+       .num_clk_periph = ARRAY_SIZE(rt5350_pherip_clks),
+};
+
+static const struct mtmips_clk_data rt3883_clk_data = {
+       .clk_base = rt3883_clks_base,
+       .num_clk_base = ARRAY_SIZE(rt3883_clks_base),
+       .clk_fixed = rt305x_fixed_clocks,
+       .num_clk_fixed = ARRAY_SIZE(rt305x_fixed_clocks),
+       .clk_factor = NULL,
+       .num_clk_factor = 0,
+       .clk_periph = rt5350_pherip_clks,
+       .num_clk_periph = ARRAY_SIZE(rt5350_pherip_clks),
+};
+
+static const struct mtmips_clk_data rt5350_clk_data = {
+       .clk_base = rt5350_clks_base,
+       .num_clk_base = ARRAY_SIZE(rt5350_clks_base),
+       .clk_fixed = rt3352_fixed_clocks,
+       .num_clk_fixed = ARRAY_SIZE(rt3352_fixed_clocks),
+       .clk_factor = NULL,
+       .num_clk_factor = 0,
+       .clk_periph = rt5350_pherip_clks,
+       .num_clk_periph = ARRAY_SIZE(rt5350_pherip_clks),
+};
+
+static const struct mtmips_clk_data mt7620_clk_data = {
+       .clk_base = mt7620_clks_base,
+       .num_clk_base = ARRAY_SIZE(mt7620_clks_base),
+       .clk_fixed = NULL,
+       .num_clk_fixed = 0,
+       .clk_factor = NULL,
+       .num_clk_factor = 0,
+       .clk_periph = mt7620_pherip_clks,
+       .num_clk_periph = ARRAY_SIZE(mt7620_pherip_clks),
+};
+
+static const struct mtmips_clk_data mt76x8_clk_data = {
+       .clk_base = mt76x8_clks_base,
+       .num_clk_base = ARRAY_SIZE(mt76x8_clks_base),
+       .clk_fixed = mt76x8_fixed_clocks,
+       .num_clk_fixed = ARRAY_SIZE(mt76x8_fixed_clocks),
+       .clk_factor = rt305x_factor_clocks,
+       .num_clk_factor = ARRAY_SIZE(rt305x_factor_clocks),
+       .clk_periph = mt76x8_pherip_clks,
+       .num_clk_periph = ARRAY_SIZE(mt76x8_pherip_clks),
+};
+
+static const struct of_device_id mtmips_of_match[] = {
+       {
+               .compatible = "ralink,rt2880-sysc",
+               .data = &rt2880_clk_data,
+       },
+       {
+               .compatible = "ralink,rt3050-sysc",
+               .data = &rt305x_clk_data,
+       },
+       {
+               .compatible = "ralink,rt3052-sysc",
+               .data = &rt305x_clk_data,
+       },
+       {
+               .compatible = "ralink,rt3352-sysc",
+               .data = &rt3352_clk_data,
+       },
+       {
+               .compatible = "ralink,rt3883-sysc",
+               .data = &rt3883_clk_data,
+       },
+       {
+               .compatible = "ralink,rt5350-sysc",
+               .data = &rt5350_clk_data,
+       },
+       {
+               .compatible = "ralink,mt7620-sysc",
+               .data = &mt7620_clk_data,
+       },
+       {
+               .compatible = "ralink,mt7628-sysc",
+               .data = &mt76x8_clk_data,
+       },
+       {
+               .compatible = "ralink,mt7688-sysc",
+               .data = &mt76x8_clk_data,
+       },
+       {}
+};
+
+static void __init mtmips_clk_regs_init(struct device_node *node,
+                                       struct mtmips_clk_priv *priv)
+{
+       u32 t;
+
+       if (!of_device_is_compatible(node, "ralink,mt7620-sysc"))
+               return;
+
+       /*
+        * When the CPU goes into sleep mode, the BUS
+        * clock will be too low for USB to function properly.
+        * Adjust the busses fractional divider to fix this
+        */
+       regmap_read(priv->sysc, SYSC_REG_CPU_SYS_CLKCFG, &t);
+       t &= ~(CLKCFG_FDIV_MASK | CLKCFG_FFRAC_MASK);
+       t |= CLKCFG_FDIV_USB_VAL | CLKCFG_FFRAC_USB_VAL;
+       regmap_write(priv->sysc, SYSC_REG_CPU_SYS_CLKCFG, t);
+}
+
+static void __init mtmips_clk_init(struct device_node *node)
+{
+       const struct of_device_id *match;
+       const struct mtmips_clk_data *data;
+       struct mtmips_clk_priv *priv;
+       struct clk_hw_onecell_data *clk_data;
+       int ret, i, count;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return;
+
+       priv->sysc = syscon_node_to_regmap(node);
+       if (IS_ERR(priv->sysc)) {
+               pr_err("Could not get sysc syscon regmap\n");
+               goto free_clk_priv;
+       }
+
+       mtmips_clk_regs_init(node, priv);
+
+       match = of_match_node(mtmips_of_match, node);
+       if (WARN_ON(!match))
+               return;
+
+       data = match->data;
+       priv->data = data;
+       count = priv->data->num_clk_base + priv->data->num_clk_fixed +
+               priv->data->num_clk_factor + priv->data->num_clk_periph;
+       clk_data = kzalloc(struct_size(clk_data, hws, count), GFP_KERNEL);
+       if (!clk_data)
+               goto free_clk_priv;
+
+       ret = mtmips_register_clocks(node, clk_data, priv);
+       if (ret) {
+               pr_err("Couldn't register top clocks\n");
+               goto free_clk_data;
+       }
+
+       ret = mtmips_register_fixed_clocks(clk_data, priv);
+       if (ret) {
+               pr_err("Couldn't register fixed clocks\n");
+               goto unreg_clk_top;
+       }
+
+       ret = mtmips_register_factor_clocks(clk_data, priv);
+       if (ret) {
+               pr_err("Couldn't register factor clocks\n");
+               goto unreg_clk_fixed;
+       }
+
+       ret = mtmips_register_pherip_clocks(node, clk_data, priv);
+       if (ret) {
+               pr_err("Couldn't register peripheral clocks\n");
+               goto unreg_clk_factor;
+       }
+
+       clk_data->num = count;
+
+       ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+       if (ret) {
+               pr_err("Couldn't add clk hw provider\n");
+               goto unreg_clk_periph;
+       }
+
+       return;
+
+unreg_clk_periph:
+       for (i = 0; i < priv->data->num_clk_periph; i++) {
+               struct mtmips_clk *sclk = &priv->data->clk_periph[i];
+
+               clk_hw_unregister(&sclk->hw);
+       }
+
+unreg_clk_factor:
+       for (i = 0; i < priv->data->num_clk_factor; i++) {
+               struct mtmips_clk_factor *sclk = &priv->data->clk_factor[i];
+
+               clk_hw_unregister_fixed_factor(sclk->hw);
+       }
+
+unreg_clk_fixed:
+       for (i = 0; i < priv->data->num_clk_fixed; i++) {
+               struct mtmips_clk_fixed *sclk = &priv->data->clk_fixed[i];
+
+               clk_hw_unregister_fixed_rate(sclk->hw);
+       }
+
+unreg_clk_top:
+       for (i = 0; i < priv->data->num_clk_base; i++) {
+               struct mtmips_clk *sclk = &priv->data->clk_base[i];
+
+               clk_hw_unregister(&sclk->hw);
+       }
+
+free_clk_data:
+       kfree(clk_data);
+
+free_clk_priv:
+       kfree(priv);
+}
+CLK_OF_DECLARE_DRIVER(rt2880_clk, "ralink,rt2880-sysc", mtmips_clk_init);
+CLK_OF_DECLARE_DRIVER(rt3050_clk, "ralink,rt3050-sysc", mtmips_clk_init);
+CLK_OF_DECLARE_DRIVER(rt3052_clk, "ralink,rt3052-sysc", mtmips_clk_init);
+CLK_OF_DECLARE_DRIVER(rt3352_clk, "ralink,rt3352-sysc", mtmips_clk_init);
+CLK_OF_DECLARE_DRIVER(rt3883_clk, "ralink,rt3883-sysc", mtmips_clk_init);
+CLK_OF_DECLARE_DRIVER(rt5350_clk, "ralink,rt5350-sysc", mtmips_clk_init);
+CLK_OF_DECLARE_DRIVER(mt7620_clk, "ralink,mt7620-sysc", mtmips_clk_init);
+CLK_OF_DECLARE_DRIVER(mt7628_clk, "ralink,mt7628-sysc", mtmips_clk_init);
+CLK_OF_DECLARE_DRIVER(mt7688_clk, "ralink,mt7688-sysc", mtmips_clk_init);
+
+struct mtmips_rst {
+       struct reset_controller_dev rcdev;
+       struct regmap *sysc;
+};
+
+static struct mtmips_rst *to_mtmips_rst(struct reset_controller_dev *dev)
+{
+       return container_of(dev, struct mtmips_rst, rcdev);
+}
+
+static int mtmips_assert_device(struct reset_controller_dev *rcdev,
+                               unsigned long id)
+{
+       struct mtmips_rst *data = to_mtmips_rst(rcdev);
+       struct regmap *sysc = data->sysc;
+
+       return regmap_update_bits(sysc, SYSC_REG_RESET_CTRL, BIT(id), BIT(id));
+}
+
+static int mtmips_deassert_device(struct reset_controller_dev *rcdev,
+                                 unsigned long id)
+{
+       struct mtmips_rst *data = to_mtmips_rst(rcdev);
+       struct regmap *sysc = data->sysc;
+
+       return regmap_update_bits(sysc, SYSC_REG_RESET_CTRL, BIT(id), 0);
+}
+
+static int mtmips_reset_device(struct reset_controller_dev *rcdev,
+                              unsigned long id)
+{
+       int ret;
+
+       ret = mtmips_assert_device(rcdev, id);
+       if (ret < 0)
+               return ret;
+
+       return mtmips_deassert_device(rcdev, id);
+}
+
+static int mtmips_rst_xlate(struct reset_controller_dev *rcdev,
+                           const struct of_phandle_args *reset_spec)
+{
+       unsigned long id = reset_spec->args[0];
+
+       if (id == 0 || id >= rcdev->nr_resets)
+               return -EINVAL;
+
+       return id;
+}
+
+static const struct reset_control_ops reset_ops = {
+       .reset = mtmips_reset_device,
+       .assert = mtmips_assert_device,
+       .deassert = mtmips_deassert_device
+};
+
+static int mtmips_reset_init(struct device *dev, struct regmap *sysc)
+{
+       struct mtmips_rst *rst_data;
+
+       rst_data = devm_kzalloc(dev, sizeof(*rst_data), GFP_KERNEL);
+       if (!rst_data)
+               return -ENOMEM;
+
+       rst_data->sysc = sysc;
+       rst_data->rcdev.ops = &reset_ops;
+       rst_data->rcdev.owner = THIS_MODULE;
+       rst_data->rcdev.nr_resets = 32;
+       rst_data->rcdev.of_reset_n_cells = 1;
+       rst_data->rcdev.of_xlate = mtmips_rst_xlate;
+       rst_data->rcdev.of_node = dev_of_node(dev);
+
+       return devm_reset_controller_register(dev, &rst_data->rcdev);
+}
+
+static int mtmips_clk_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct device *dev = &pdev->dev;
+       struct mtmips_clk_priv *priv;
+       int ret;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->sysc = syscon_node_to_regmap(np);
+       if (IS_ERR(priv->sysc))
+               return dev_err_probe(dev, PTR_ERR(priv->sysc),
+                                    "Could not get sysc syscon regmap\n");
+
+       ret = mtmips_reset_init(dev, priv->sysc);
+       if (ret)
+               return dev_err_probe(dev, ret, "Could not init reset controller\n");
+
+       return 0;
+}
+
+static const struct of_device_id mtmips_clk_of_match[] = {
+       { .compatible = "ralink,rt2880-reset" },
+       { .compatible = "ralink,rt2880-sysc" },
+       { .compatible = "ralink,rt3050-sysc" },
+       { .compatible = "ralink,rt3052-sysc" },
+       { .compatible = "ralink,rt3352-sysc" },
+       { .compatible = "ralink,rt3883-sysc" },
+       { .compatible = "ralink,rt5350-sysc" },
+       { .compatible = "ralink,mt7620-sysc" },
+       { .compatible = "ralink,mt7628-sysc" },
+       { .compatible = "ralink,mt7688-sysc" },
+       {}
+};
+
+static struct platform_driver mtmips_clk_driver = {
+       .probe = mtmips_clk_probe,
+       .driver = {
+               .name = "mtmips-clk",
+               .of_match_table = mtmips_clk_of_match,
+       },
+};
+
+static int __init mtmips_clk_reset_init(void)
+{
+       return platform_driver_register(&mtmips_clk_driver);
+}
+arch_initcall(mtmips_clk_reset_init);