[v4,2/9] clk: ralink: add clock and reset driver for MTMIPS SoCs

Message ID 20230617052435.359177-3-sergio.paracuellos@gmail.com
State New
Headers
Series mips: ralink: add complete clock and reset driver for mtmips SoCs |

Commit Message

Sergio Paracuellos June 17, 2023, 5:24 a.m. UTC
  Until now, clock related code for old ralink SoCs was based in fixed clocks
using 'clk_register_fixed_rate' and 'clkdev_create' directly doing in code
and not using device tree at all for their definition. Including this driver
is an effort to be able to define proper clocks using device tree and also
cleaning all the clock and reset related code from 'arch/mips/ralink' dir.
This clock and reset driver covers all the ralink SoCs but MT7621 which is
the newest and provides gating and some differences that make it different
from its predecesors. It has its own driver since some time ago. The ralink
SoCs we are taking about are RT2880, RT3050, RT3052, RT3350, RT3352, RT3883,
RT5350, MT7620, MT7628 and MT7688. Mostly the code in this new driver has
been extracted from 'arch/mips/ralink' and cleanly put using kernel clock
driver APIs. The clock plans for this SoCs only talks about relation between
CPU frequency and BUS frequency. This relation is different depending on the
particular SoC. CPU clock is derived from XTAL frequencies.

Depending on the SoC we have the following frequencies:
* RT2880 SoC:
    - XTAL: 40 MHz.
    - CPU: 250, 266, 280 or 300 MHz.
    - BUS: CPU / 2 MHz.
* RT3050, RT3052, RT3350:
    - XTAL: 40 MHz.
    - CPU: 320 or 384 MHz.
    - BUS: CPU / 3 MHz.
* RT3352:
    - XTAL: 40 MHz.
    - CPU: 384 or 400 MHz.
    - BUS: CPU / 3 MHz.
    - PERIPH: 40 MHz.
* RT3383:
    - XTAL: 40 MHz.
    - CPU: 250, 384, 480 or 500 MHz.
    - BUS: Depends on RAM Type and CPU:
        + RAM DDR2: 125. ELSE 83 MHz.
        + RAM DDR2: 128. ELSE 96 MHz.
        + RAM DDR2: 160. ELSE 120 MHz.
        + RAM DDR2: 166. ELSE 125 MHz.
* RT5350:
    - XTAL: 40 MHz.
    - CPU: 300, 320 or 360 MHz.
    - BUS: CPU / 3, CPU / 4, CPU / 3 MHz.
    - PERIPH: 40 MHz.
* MT7628 and MT7688:
    - XTAL: 20 MHz or 40 MHz.
    - CPU: 575 or 580 MHz.
    - BUS: CPU / 3.
    - PCMI2S: 480 MHz.
    - PERIPH: 40 MHz.
* MT7620:
    - XTAL: 20 MHz or 40 MHz.
    - PLL: XTAL, 480, 600 MHz.
    - CPU: depends on PLL and some mult and dividers.
    - BUS: depends on PLL and some mult and dividers.
    - PERIPH: 40 or XTAL MHz.

MT7620 is a bit more complex deriving CPU clock from a PLL and an bunch of
register reads and predividers. To derive CPU and BUS frequencies in the
MT7620 SoC 'mt7620_calc_rate()' helper is used.

In the case XTAL can have different frequencies and we need a different
clock frequency for peripherals 'periph' clock in introduced.

The rest of the peripherals present in the SoC just follow their parent
frequencies.

With this information the clk driver will provide all the clock and reset
functionality from a set of hardcoded clocks allowing to define a nice
device tree without fixed clocks.

Acked-by: Stephen Boyd <sboyd@kernel.org>
Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com>
---
 drivers/clk/ralink/Kconfig      |    7 +
 drivers/clk/ralink/Makefile     |    1 +
 drivers/clk/ralink/clk-mtmips.c | 1134 +++++++++++++++++++++++++++++++
 3 files changed, 1142 insertions(+)
 create mode 100644 drivers/clk/ralink/clk-mtmips.c
  

Comments

Shiji Yang June 17, 2023, 12:54 p.m. UTC | #1
Hi Sergio Paracuellos!

I found there are still some areas that need improvement.

>+
>+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);

drivers/clk/ralink/clk-mtmips.c: In function 'rt3883_bus_recalc_rate':
drivers/clk/ralink/clk-mtmips.c:502:1: error: control reaches end of non-void function [-Werror=return-type]
  502 | }
      | ^

Build error here, need to return a value, `parent_rate / 4` should be okay.
It seems that the program will never run into this default case.

>+	}
>+}
>+


>+
>+static const struct of_device_id mtmips_clk_of_match[] = {
>+	{ .compatible = "ralink,rt2880-reset" },
>+	{ .compatible = "ralink,rt2880-sysc" },
>+	{ .compatible = "ralink,rt3050-sysc" },
>+	{ .compatible = "ralink,rt3050-sysc" },

There are two `ralink,rt3050-sysc`. I think the second one should be
`ralink,rt3052-sysc`.

>+	{ .compatible = "ralink,rt3352-sysc" },
>+	{ .compatible = "ralink,rt3883-sysc" },
>+	{ .compatible = "ralink,rt5350-sysc" },
>+	{ .compatible = "ralink,mt7620a-sysc" },
>+	{ .compatible = "ralink,mt7620-sysc" },
>+	{ .compatible = "ralink,mt7628-sysc" },
>+	{ .compatible = "ralink,mt7688-sysc" },
>+	{}
>+};
>+


> void __init plat_time_init(void)
> {
>+	struct of_phandle_args clkspec;
> 	struct clk *clk;
>+	int cpu_clk_idx;
> 
> 	ralink_of_remap();
> 
>-	ralink_clk_init();
>-	clk = clk_get_sys("cpu", NULL);
>+	cpu_clk_idx = clk_cpu_index();
>+	if (cpu_clk_idx == -1)
>+		panic("unable to get CPU clock index");
>+
>+	of_clk_init(NULL);
>+	clkspec.np = of_find_node_by_name(NULL, "sysc");

The node name should be "syscon" as the example node name in the
dt-bindings document is "syscon".

>+	clkspec.args_count = 1;
>+	clkspec.args[0] = cpu_clk_idx;
>+	clk = of_clk_get_from_provider(&clkspec);
> 	if (IS_ERR(clk))
> 		panic("unable to get CPU clock, err=%ld", PTR_ERR(clk));
> 	pr_info("CPU Clock: %ldMHz\n", clk_get_rate(clk) / 1000000);


It seems that the clock calculation logic of the mt7620 has changed.
The kernel got an incorrect clock frequency 3480MHz. The correct CPU
clock should be 580MHz = 3480MHz / 6.

I will test and report mt7628 later.

mt7620 dmesg:
[    0.000000] Linux version 5.15.117 (db@Aspire-V-Nitro) (mipsel-openwrt-linux-musl-gcc (OpenWrt GCC 12.3.0 r23471+7-816fcf88f6) 12.3.0, GNU ld (GNU Binutils) 2.40.0) #0 Sat Jun 17 08:41:01 2023
[    0.000000] Board has DDR2
[    0.000000] Analog PMU set to hw control
[    0.000000] Digital PMU set to hw control
[    0.000000] SoC Type: MediaTek MT7620A ver:2 eco:6
[    0.000000] printk: bootconsole [early0] enabled
[    0.000000] CPU0 revision is: 00019650 (MIPS 24KEc)
[    0.000000] MIPS: machine is Haier HW-L1W
[    0.000000] Initrd not found or empty - disabling initrd
[    0.000000] Primary instruction cache 64kB, VIPT, 4-way, linesize 32 bytes.
[    0.000000] Primary data cache 32kB, 4-way, PIPT, no aliases, linesize 32 bytes
[    0.000000] Zone ranges:
[    0.000000]   Normal   [mem 0x0000000000000000-0x0000000007ffffff]
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000000000000-0x0000000007ffffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000000000000-0x0000000007ffffff]
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 32480
[    0.000000] Kernel command line: console=ttyS0,115200 rootfstype=squashfs,jffs2
[    0.000000] Dentry cache hash table entries: 16384 (order: 4, 65536 bytes, linear)
[    0.000000] Inode-cache hash table entries: 8192 (order: 3, 32768 bytes, linear)
[    0.000000] Writing ErrCtl register=00064000
[    0.000000] Readback ErrCtl register=00064000
[    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[    0.000000] Memory: 120992K/131072K available (5518K kernel code, 600K rwdata, 1192K rodata, 1192K init, 215K bss, 10080K reserved, 0K cma-reserved)
[    0.000000] SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] NR_IRQS: 256
[    0.000000] CPU Clock: 3480MHz
[    0.000000] clocksource: systick: mask: 0xffff max_cycles: 0xffff, max_idle_ns: 583261500 ns
[    0.000000] systick: enable autosleep mode
[    0.000000] systick: running - mult: 214748, shift: 32
[    0.000000] clocksource: MIPS: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 1098425544 ns
[    0.000000] sched_clock: 32 bits at 1740MHz, resolution 0ns, wraps every 1234186239ns
[    0.001354] Calibrating delay loop... 385.84 BogoMIPS (lpj=1929216)
[    0.012389] pid_max: default: 32768 minimum: 301
[    0.013357] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.014589] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.017429] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
[    0.019103] futex hash table entries: 256 (order: -1, 3072 bytes, linear)
[    0.020288] pinctrl core: initialized pinctrl subsystem
[    0.021415] NET: Registered PF_NETLINK/PF_ROUTE protocol family
[    0.105370] rt2880_gpio 10000600.gpio: registering 24 gpios
[    0.106328] rt2880_gpio 10000600.gpio: registering 24 irq handlers
[    0.107446] rt2880_gpio 10000688.gpio: registering 1 gpios
[    0.108381] rt2880_gpio 10000688.gpio: registering 1 irq handlers
[    0.109673] PCI host bridge to bus 0000:00
[    0.110372] pci_bus 0000:00: root bus resource [mem 0x20000000-0x2fffffff]
[    0.111528] pci_bus 0000:00: root bus resource [io  0x10160000-0x1016ffff]
[    0.112695] pci_bus 0000:00: No busn resource found for root bus, will use [bus 00-ff]
[    0.114046] pci 0000:00:00.0: [1814:0801] type 01 class 0x060400
[    0.115059] pci 0000:00:00.0: reg 0x10: [mem 0x00000000-0x7fffffff]
[    0.116118] pci 0000:00:00.0: reg 0x14: [mem 0x00000000-0x0000ffff]
[    0.117195] pci 0000:00:00.0: supports D1
[    0.117862] pci 0000:00:00.0: PME# supported from D0 D1 D3hot
[    0.119156] pci 0000:00:00.0: bridge configuration invalid ([bus 00-00]), reconfiguring
[    0.120556] pci 0000:01:00.0: [14c3:7650] type 00 class 0x028000
[    0.121570] pci 0000:01:00.0: reg 0x10: [mem 0x00000000-0x000fffff]
[    0.122658] pci 0000:01:00.0: PME# supported from D0 D3hot D3cold
[    0.123749] pci 0000:01:00.1: [14c3:8650] type 00 class 0x0d1100
[    0.124769] pci 0000:01:00.1: reg 0x10: [mem 0x00000000-0x000fffff]
[    0.125844] pci 0000:01:00.1: supports D1
[    0.126513] pci 0000:01:00.1: PME# supported from D0 D1 D3hot D3cold
[    0.127897] pci_bus 0000:01: busn_res: [bus 01-ff] end is updated to 01
[    0.129018] pci_bus 0000:00: busn_res: [bus 00-ff] end is updated to 01
[    0.130138] pci 0000:00:00.0: BAR 0: no space for [mem size 0x80000000]
[    0.131255] pci 0000:00:00.0: BAR 0: failed to assign [mem size 0x80000000]
[    0.132434] pci 0000:00:00.0: BAR 8: assigned [mem 0x20000000-0x201fffff]
[    0.133587] pci 0000:00:00.0: BAR 1: assigned [mem 0x20200000-0x2020ffff]
[    0.134738] pci 0000:01:00.0: BAR 0: assigned [mem 0x20000000-0x200fffff]
[    0.135890] pci 0000:01:00.1: BAR 0: assigned [mem 0x20100000-0x201fffff]
[    0.137040] pci 0000:00:00.0: PCI bridge to [bus 01]
[    0.137882] pci 0000:00:00.0:   bridge window [mem 0x20000000-0x201fffff]
[    0.139123] clocksource: Switched to clocksource MIPS
[    0.140241] NET: Registered PF_INET protocol family
[    0.141106] IP idents hash table entries: 2048 (order: 2, 16384 bytes, linear)
[    0.142473] tcp_listen_portaddr_hash hash table entries: 512 (order: 0, 4096 bytes, linear)
[    0.143893] Table-perturb hash table entries: 65536 (order: 6, 262144 bytes, linear)
[    0.145199] TCP established hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.146499] TCP bind hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.147695] TCP: Hash tables configured (established 1024 bind 1024)
[    0.148803] UDP hash table entries: 256 (order: 0, 4096 bytes, linear)
[    0.149920] UDP-Lite hash table entries: 256 (order: 0, 4096 bytes, linear)
[    0.151177] NET: Registered PF_UNIX/PF_LOCAL protocol family
[    0.152149] PCI: CLS 0 bytes, default 32
[    0.152847] rt-timer 10000100.timer: failed get clock rate
[    0.153771] rt-timer: probe of 10000100.timer failed with error -2
[    0.155602] workingset: timestamp_bits=14 max_order=15 bucket_order=1
[    0.157808] squashfs: version 4.0 (2009/01/31) Phillip Lougher
[    0.158792] jffs2: version 2.2 (NAND) (SUMMARY) (LZMA) (RTIME) (CMODE_PRIORITY) (c) 2001-2006 Red Hat, Inc.
[    0.161032] Serial: 8250/16550 driver, 2 ports, IRQ sharing disabled
[    0.162284] of_serial 10000c00.uartlite: failed to get clock: -2
[    0.163300] of_serial: probe of 10000c00.uartlite failed with error -2
[    0.164624] spi-rt2880 10000b00.spi: unable to get SYS clock
[    0.165580] spi-rt2880: probe of 10000b00.spi failed with error -2
[    0.171098] gsw: setting port4 to ephy mode
[    0.171828] mtk_soc_eth 10100000.ethernet: generated random MAC address f6:6c:93:9a:c8:ca
[    0.173208] mtk_soc_eth 10100000.ethernet: mdio-bus disabled
[    0.174205] mtk_soc_eth 10100000.ethernet: loaded mt7620 driver
[    0.175329] mtk_soc_eth 10100000.ethernet eth0: mediatek frame engine at 0xb0100000, irq 5
[    0.176805] rt2880_wdt: probe of 10000120.watchdog failed with error -2
[    0.178187] NET: Registered PF_INET6 protocol family
[    0.179980] Segment Routing with IPv6
[    0.180607] In-situ OAM (IOAM) with IPv6
[    0.181290] NET: Registered PF_PACKET protocol family
[    0.182148] 8021q: 802.1Q VLAN Support v1.8
[    0.183347] Warning: unable to open an initial console.
[    0.184558] /dev/root: Can't open blockdev
[    0.185253] VFS: Cannot open root device "(null)" or unknown-block(0,0): error -6
[    0.186516] Please append a correct "root=" boot option; here are the available partitions:
[    0.187934] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
[    0.189335] Rebooting in 1 seconds..


Thanks,
    Shiji Yang
  
Krzysztof Kozlowski June 17, 2023, 1:07 p.m. UTC | #2
On 17/06/2023 14:54, Shiji Yang wrote:
>> void __init plat_time_init(void)
>> {
>> +	struct of_phandle_args clkspec;
>> 	struct clk *clk;
>> +	int cpu_clk_idx;
>>
>> 	ralink_of_remap();
>>
>> -	ralink_clk_init();
>> -	clk = clk_get_sys("cpu", NULL);
>> +	cpu_clk_idx = clk_cpu_index();
>> +	if (cpu_clk_idx == -1)
>> +		panic("unable to get CPU clock index");
>> +
>> +	of_clk_init(NULL);
>> +	clkspec.np = of_find_node_by_name(NULL, "sysc");
> 
> The node name should be "syscon" as the example node name in the
> dt-bindings document is "syscon".

NAK for both.

Node names must not be an ABI, unless you talk about child of some
device node. I don't think this is the case here. Look by phandle (for a
device context) or by compatible (looks the case here).



Best regards,
Krzysztof
  
Sergio Paracuellos June 17, 2023, 1:26 p.m. UTC | #3
Hi Shiji Yang,

On Sat, Jun 17, 2023 at 2:55 PM Shiji Yang <yangshiji66@outlook.com> wrote:
>
> Hi Sergio Paracuellos!
>
> I found there are still some areas that need improvement.

Awesome! I did not expect to get testing before this is added to the
tree .As I have mentioned I have only tested this with Ralink 5350 ALL
One board. So thanks for testing this so far!! It is really helpful.

>
> >+
> >+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);
>
> drivers/clk/ralink/clk-mtmips.c: In function 'rt3883_bus_recalc_rate':
> drivers/clk/ralink/clk-mtmips.c:502:1: error: control reaches end of non-void function [-Werror=return-type]
>   502 | }
>       | ^
>
> Build error here, need to return a value, `parent_rate / 4` should be okay.
> It seems that the program will never run into this default case.

Ouch, it was a BUG() call here before changing it to WARN_ON_ONCE so
yes a return is needed! Thanks!

>
> >+      }
> >+}
> >+
>
>
> >+
> >+static const struct of_device_id mtmips_clk_of_match[] = {
> >+      { .compatible = "ralink,rt2880-reset" },
> >+      { .compatible = "ralink,rt2880-sysc" },
> >+      { .compatible = "ralink,rt3050-sysc" },
> >+      { .compatible = "ralink,rt3050-sysc" },
>
> There are two `ralink,rt3050-sysc`. I think the second one should be
> `ralink,rt3052-sysc`.

True :)

>
> >+      { .compatible = "ralink,rt3352-sysc" },
> >+      { .compatible = "ralink,rt3883-sysc" },
> >+      { .compatible = "ralink,rt5350-sysc" },
> >+      { .compatible = "ralink,mt7620a-sysc" },
> >+      { .compatible = "ralink,mt7620-sysc" },
> >+      { .compatible = "ralink,mt7628-sysc" },
> >+      { .compatible = "ralink,mt7688-sysc" },
> >+      {}
> >+};
> >+
>
>
> > void __init plat_time_init(void)
> > {
> >+      struct of_phandle_args clkspec;
> >       struct clk *clk;
> >+      int cpu_clk_idx;
> >
> >       ralink_of_remap();
> >
> >-      ralink_clk_init();
> >-      clk = clk_get_sys("cpu", NULL);
> >+      cpu_clk_idx = clk_cpu_index();
> >+      if (cpu_clk_idx == -1)
> >+              panic("unable to get CPU clock index");
> >+
> >+      of_clk_init(NULL);
> >+      clkspec.np = of_find_node_by_name(NULL, "sysc");
>
> The node name should be "syscon" as the example node name in the
> dt-bindings document is "syscon".

sysc is label to get this node since it is the one shared by all
different dtsi files.

>
> >+      clkspec.args_count = 1;
> >+      clkspec.args[0] = cpu_clk_idx;
> >+      clk = of_clk_get_from_provider(&clkspec);
> >       if (IS_ERR(clk))
> >               panic("unable to get CPU clock, err=%ld", PTR_ERR(clk));
> >       pr_info("CPU Clock: %ldMHz\n", clk_get_rate(clk) / 1000000);
>
>
> It seems that the clock calculation logic of the mt7620 has changed.
> The kernel got an incorrect clock frequency 3480MHz. The correct CPU
> clock should be 580MHz = 3480MHz / 6.

The only thing that changed a bit is the stuff with the CONFIG_USB
that I was told to change by Stephen. Can you check if that is the
problem with your bad cpu clock? Just comment the call
'mtmips_clk_regs_init' of the probe function.

>
> I will test and report mt7628 later.

Thanks!

>
> mt7620 dmesg:
> [    0.000000] Linux version 5.15.117 (db@Aspire-V-Nitro) (mipsel-openwrt-linux-musl-gcc (OpenWrt GCC 12.3.0 r23471+7-816fcf88f6) 12.3.0, GNU ld (GNU Binutils) 2.40.0) #0 Sat Jun 17 08:41:01 2023
> [    0.000000] Board has DDR2
> [    0.000000] Analog PMU set to hw control
> [    0.000000] Digital PMU set to hw control
> [    0.000000] SoC Type: MediaTek MT7620A ver:2 eco:6
> [    0.000000] printk: bootconsole [early0] enabled
> [    0.000000] CPU0 revision is: 00019650 (MIPS 24KEc)
> [    0.000000] MIPS: machine is Haier HW-L1W
> [    0.000000] Initrd not found or empty - disabling initrd
> [    0.000000] Primary instruction cache 64kB, VIPT, 4-way, linesize 32 bytes.
> [    0.000000] Primary data cache 32kB, 4-way, PIPT, no aliases, linesize 32 bytes
> [    0.000000] Zone ranges:
> [    0.000000]   Normal   [mem 0x0000000000000000-0x0000000007ffffff]
> [    0.000000] Movable zone start for each node
> [    0.000000] Early memory node ranges
> [    0.000000]   node   0: [mem 0x0000000000000000-0x0000000007ffffff]
> [    0.000000] Initmem setup node 0 [mem 0x0000000000000000-0x0000000007ffffff]
> [    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 32480
> [    0.000000] Kernel command line: console=ttyS0,115200 rootfstype=squashfs,jffs2
> [    0.000000] Dentry cache hash table entries: 16384 (order: 4, 65536 bytes, linear)
> [    0.000000] Inode-cache hash table entries: 8192 (order: 3, 32768 bytes, linear)
> [    0.000000] Writing ErrCtl register=00064000
> [    0.000000] Readback ErrCtl register=00064000
> [    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
> [    0.000000] Memory: 120992K/131072K available (5518K kernel code, 600K rwdata, 1192K rodata, 1192K init, 215K bss, 10080K reserved, 0K cma-reserved)
> [    0.000000] SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
> [    0.000000] NR_IRQS: 256
> [    0.000000] CPU Clock: 3480MHz
> [    0.000000] clocksource: systick: mask: 0xffff max_cycles: 0xffff, max_idle_ns: 583261500 ns
> [    0.000000] systick: enable autosleep mode
> [    0.000000] systick: running - mult: 214748, shift: 32
> [    0.000000] clocksource: MIPS: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 1098425544 ns
> [    0.000000] sched_clock: 32 bits at 1740MHz, resolution 0ns, wraps every 1234186239ns
> [    0.001354] Calibrating delay loop... 385.84 BogoMIPS (lpj=1929216)
> [    0.012389] pid_max: default: 32768 minimum: 301
> [    0.013357] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
> [    0.014589] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
> [    0.017429] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
> [    0.019103] futex hash table entries: 256 (order: -1, 3072 bytes, linear)
> [    0.020288] pinctrl core: initialized pinctrl subsystem
> [    0.021415] NET: Registered PF_NETLINK/PF_ROUTE protocol family
> [    0.105370] rt2880_gpio 10000600.gpio: registering 24 gpios
> [    0.106328] rt2880_gpio 10000600.gpio: registering 24 irq handlers
> [    0.107446] rt2880_gpio 10000688.gpio: registering 1 gpios
> [    0.108381] rt2880_gpio 10000688.gpio: registering 1 irq handlers
> [    0.109673] PCI host bridge to bus 0000:00
> [    0.110372] pci_bus 0000:00: root bus resource [mem 0x20000000-0x2fffffff]
> [    0.111528] pci_bus 0000:00: root bus resource [io  0x10160000-0x1016ffff]
> [    0.112695] pci_bus 0000:00: No busn resource found for root bus, will use [bus 00-ff]
> [    0.114046] pci 0000:00:00.0: [1814:0801] type 01 class 0x060400
> [    0.115059] pci 0000:00:00.0: reg 0x10: [mem 0x00000000-0x7fffffff]
> [    0.116118] pci 0000:00:00.0: reg 0x14: [mem 0x00000000-0x0000ffff]
> [    0.117195] pci 0000:00:00.0: supports D1
> [    0.117862] pci 0000:00:00.0: PME# supported from D0 D1 D3hot
> [    0.119156] pci 0000:00:00.0: bridge configuration invalid ([bus 00-00]), reconfiguring
> [    0.120556] pci 0000:01:00.0: [14c3:7650] type 00 class 0x028000
> [    0.121570] pci 0000:01:00.0: reg 0x10: [mem 0x00000000-0x000fffff]
> [    0.122658] pci 0000:01:00.0: PME# supported from D0 D3hot D3cold
> [    0.123749] pci 0000:01:00.1: [14c3:8650] type 00 class 0x0d1100
> [    0.124769] pci 0000:01:00.1: reg 0x10: [mem 0x00000000-0x000fffff]
> [    0.125844] pci 0000:01:00.1: supports D1
> [    0.126513] pci 0000:01:00.1: PME# supported from D0 D1 D3hot D3cold
> [    0.127897] pci_bus 0000:01: busn_res: [bus 01-ff] end is updated to 01
> [    0.129018] pci_bus 0000:00: busn_res: [bus 00-ff] end is updated to 01
> [    0.130138] pci 0000:00:00.0: BAR 0: no space for [mem size 0x80000000]
> [    0.131255] pci 0000:00:00.0: BAR 0: failed to assign [mem size 0x80000000]
> [    0.132434] pci 0000:00:00.0: BAR 8: assigned [mem 0x20000000-0x201fffff]
> [    0.133587] pci 0000:00:00.0: BAR 1: assigned [mem 0x20200000-0x2020ffff]
> [    0.134738] pci 0000:01:00.0: BAR 0: assigned [mem 0x20000000-0x200fffff]
> [    0.135890] pci 0000:01:00.1: BAR 0: assigned [mem 0x20100000-0x201fffff]
> [    0.137040] pci 0000:00:00.0: PCI bridge to [bus 01]
> [    0.137882] pci 0000:00:00.0:   bridge window [mem 0x20000000-0x201fffff]
> [    0.139123] clocksource: Switched to clocksource MIPS
> [    0.140241] NET: Registered PF_INET protocol family
> [    0.141106] IP idents hash table entries: 2048 (order: 2, 16384 bytes, linear)
> [    0.142473] tcp_listen_portaddr_hash hash table entries: 512 (order: 0, 4096 bytes, linear)
> [    0.143893] Table-perturb hash table entries: 65536 (order: 6, 262144 bytes, linear)
> [    0.145199] TCP established hash table entries: 1024 (order: 0, 4096 bytes, linear)
> [    0.146499] TCP bind hash table entries: 1024 (order: 0, 4096 bytes, linear)
> [    0.147695] TCP: Hash tables configured (established 1024 bind 1024)
> [    0.148803] UDP hash table entries: 256 (order: 0, 4096 bytes, linear)
> [    0.149920] UDP-Lite hash table entries: 256 (order: 0, 4096 bytes, linear)
> [    0.151177] NET: Registered PF_UNIX/PF_LOCAL protocol family
> [    0.152149] PCI: CLS 0 bytes, default 32
> [    0.152847] rt-timer 10000100.timer: failed get clock rate
> [    0.153771] rt-timer: probe of 10000100.timer failed with error -2
> [    0.155602] workingset: timestamp_bits=14 max_order=15 bucket_order=1
> [    0.157808] squashfs: version 4.0 (2009/01/31) Phillip Lougher
> [    0.158792] jffs2: version 2.2 (NAND) (SUMMARY) (LZMA) (RTIME) (CMODE_PRIORITY) (c) 2001-2006 Red Hat, Inc.
> [    0.161032] Serial: 8250/16550 driver, 2 ports, IRQ sharing disabled
> [    0.162284] of_serial 10000c00.uartlite: failed to get clock: -2
> [    0.163300] of_serial: probe of 10000c00.uartlite failed with error -2
> [    0.164624] spi-rt2880 10000b00.spi: unable to get SYS clock
> [    0.165580] spi-rt2880: probe of 10000b00.spi failed with error -2
> [    0.171098] gsw: setting port4 to ephy mode
> [    0.171828] mtk_soc_eth 10100000.ethernet: generated random MAC address f6:6c:93:9a:c8:ca
> [    0.173208] mtk_soc_eth 10100000.ethernet: mdio-bus disabled
> [    0.174205] mtk_soc_eth 10100000.ethernet: loaded mt7620 driver
> [    0.175329] mtk_soc_eth 10100000.ethernet eth0: mediatek frame engine at 0xb0100000, irq 5
> [    0.176805] rt2880_wdt: probe of 10000120.watchdog failed with error -2
> [    0.178187] NET: Registered PF_INET6 protocol family
> [    0.179980] Segment Routing with IPv6
> [    0.180607] In-situ OAM (IOAM) with IPv6
> [    0.181290] NET: Registered PF_PACKET protocol family
> [    0.182148] 8021q: 802.1Q VLAN Support v1.8
> [    0.183347] Warning: unable to open an initial console.
> [    0.184558] /dev/root: Can't open blockdev
> [    0.185253] VFS: Cannot open root device "(null)" or unknown-block(0,0): error -6
> [    0.186516] Please append a correct "root=" boot option; here are the available partitions:
> [    0.187934] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
> [    0.189335] Rebooting in 1 seconds..
>
>
> Thanks,
>     Shiji Yang

Best regards,
    Sergio Paracuellos
  
Sergio Paracuellos June 17, 2023, 1:31 p.m. UTC | #4
Hi Krzysztof,

On Sat, Jun 17, 2023 at 3:07 PM Krzysztof Kozlowski
<krzysztof.kozlowski@linaro.org> wrote:
>
> On 17/06/2023 14:54, Shiji Yang wrote:
> >> void __init plat_time_init(void)
> >> {
> >> +    struct of_phandle_args clkspec;
> >>      struct clk *clk;
> >> +    int cpu_clk_idx;
> >>
> >>      ralink_of_remap();
> >>
> >> -    ralink_clk_init();
> >> -    clk = clk_get_sys("cpu", NULL);
> >> +    cpu_clk_idx = clk_cpu_index();
> >> +    if (cpu_clk_idx == -1)
> >> +            panic("unable to get CPU clock index");
> >> +
> >> +    of_clk_init(NULL);
> >> +    clkspec.np = of_find_node_by_name(NULL, "sysc");
> >
> > The node name should be "syscon" as the example node name in the
> > dt-bindings document is "syscon".
>
> NAK for both.
>
> Node names must not be an ABI, unless you talk about child of some
> device node. I don't think this is the case here. Look by phandle (for a
> device context) or by compatible (looks the case here).

We need to get the cpu clock to set the initial cpu clock here. Search
by 'sysc' is the only  shared in all the dtsi files since it is the
clock provider node. Why is this not correct? I don't understand what
you mean with look by phandle for a device context. The case of
searching for compatible is a mess since as you can see in the
bindings there are tons of compatibles to search for, then (this code
is common to all ralink platforms).

Thanks in advance for clarification.

Best regards,
    Sergio Paracuellos
>
>
>
> Best regards,
> Krzysztof
>
  
Krzysztof Kozlowski June 17, 2023, 2:43 p.m. UTC | #5
On 17/06/2023 15:31, Sergio Paracuellos wrote:
> Hi Krzysztof,
> 
> On Sat, Jun 17, 2023 at 3:07 PM Krzysztof Kozlowski
> <krzysztof.kozlowski@linaro.org> wrote:
>>
>> On 17/06/2023 14:54, Shiji Yang wrote:
>>>> void __init plat_time_init(void)
>>>> {
>>>> +    struct of_phandle_args clkspec;
>>>>      struct clk *clk;
>>>> +    int cpu_clk_idx;
>>>>
>>>>      ralink_of_remap();
>>>>
>>>> -    ralink_clk_init();
>>>> -    clk = clk_get_sys("cpu", NULL);
>>>> +    cpu_clk_idx = clk_cpu_index();
>>>> +    if (cpu_clk_idx == -1)
>>>> +            panic("unable to get CPU clock index");
>>>> +
>>>> +    of_clk_init(NULL);
>>>> +    clkspec.np = of_find_node_by_name(NULL, "sysc");
>>>
>>> The node name should be "syscon" as the example node name in the
>>> dt-bindings document is "syscon".
>>
>> NAK for both.
>>
>> Node names must not be an ABI, unless you talk about child of some
>> device node. I don't think this is the case here. Look by phandle (for a
>> device context) or by compatible (looks the case here).
> 
> We need to get the cpu clock to set the initial cpu clock here. Search
> by 'sysc' is the only  shared in all the dtsi files since it is the
> clock provider node. Why is this not correct?

Because device node name can change anytime and your entire Linux driver
gets broken. Node name is not an ABI.

> I don't understand what
> you mean with look by phandle for a device context. 

Your device node should contain phandle to the other node.

> The case of
> searching for compatible is a mess since as you can see in the
> bindings there are tons of compatibles to search for, then (this code
> is common to all ralink platforms).

Compatible is one of the ways using ABI.

Best regards,
Krzysztof
  
Krzysztof Kozlowski June 17, 2023, 2:43 p.m. UTC | #6
On 17/06/2023 15:26, Sergio Paracuellos wrote:
>>> void __init plat_time_init(void)
>>> {
>>> +      struct of_phandle_args clkspec;
>>>       struct clk *clk;
>>> +      int cpu_clk_idx;
>>>
>>>       ralink_of_remap();
>>>
>>> -      ralink_clk_init();
>>> -      clk = clk_get_sys("cpu", NULL);
>>> +      cpu_clk_idx = clk_cpu_index();
>>> +      if (cpu_clk_idx == -1)
>>> +              panic("unable to get CPU clock index");
>>> +
>>> +      of_clk_init(NULL);
>>> +      clkspec.np = of_find_node_by_name(NULL, "sysc");
>>
>> The node name should be "syscon" as the example node name in the
>> dt-bindings document is "syscon".
> 
> sysc is label to get this node since it is the one shared by all
> different dtsi files.

If it is label, why do you use it to get by name? name != label.

Best regards,
Krzysztof
  
Sergio Paracuellos June 17, 2023, 3:31 p.m. UTC | #7
On Sat, Jun 17, 2023 at 4:44 PM Krzysztof Kozlowski
<krzysztof.kozlowski@linaro.org> wrote:
>
> On 17/06/2023 15:26, Sergio Paracuellos wrote:
> >>> void __init plat_time_init(void)
> >>> {
> >>> +      struct of_phandle_args clkspec;
> >>>       struct clk *clk;
> >>> +      int cpu_clk_idx;
> >>>
> >>>       ralink_of_remap();
> >>>
> >>> -      ralink_clk_init();
> >>> -      clk = clk_get_sys("cpu", NULL);
> >>> +      cpu_clk_idx = clk_cpu_index();
> >>> +      if (cpu_clk_idx == -1)
> >>> +              panic("unable to get CPU clock index");
> >>> +
> >>> +      of_clk_init(NULL);
> >>> +      clkspec.np = of_find_node_by_name(NULL, "sysc");
> >>
> >> The node name should be "syscon" as the example node name in the
> >> dt-bindings document is "syscon".
> >
> > sysc is label to get this node since it is the one shared by all
> > different dtsi files.
>
> If it is label, why do you use it to get by name? name != label.

Sorry I meant both of them, node name in dtsi file and also label for
referring to it from other nodes.

>
> Best regards,
> Krzysztof
>

Best regards,
    Sergio Paracuellos
  
Sergio Paracuellos June 17, 2023, 3:37 p.m. UTC | #8
On Sat, Jun 17, 2023 at 4:43 PM Krzysztof Kozlowski
<krzysztof.kozlowski@linaro.org> wrote:
>
> On 17/06/2023 15:31, Sergio Paracuellos wrote:
> > Hi Krzysztof,
> >
> > On Sat, Jun 17, 2023 at 3:07 PM Krzysztof Kozlowski
> > <krzysztof.kozlowski@linaro.org> wrote:
> >>
> >> On 17/06/2023 14:54, Shiji Yang wrote:
> >>>> void __init plat_time_init(void)
> >>>> {
> >>>> +    struct of_phandle_args clkspec;
> >>>>      struct clk *clk;
> >>>> +    int cpu_clk_idx;
> >>>>
> >>>>      ralink_of_remap();
> >>>>
> >>>> -    ralink_clk_init();
> >>>> -    clk = clk_get_sys("cpu", NULL);
> >>>> +    cpu_clk_idx = clk_cpu_index();
> >>>> +    if (cpu_clk_idx == -1)
> >>>> +            panic("unable to get CPU clock index");
> >>>> +
> >>>> +    of_clk_init(NULL);
> >>>> +    clkspec.np = of_find_node_by_name(NULL, "sysc");
> >>>
> >>> The node name should be "syscon" as the example node name in the
> >>> dt-bindings document is "syscon".
> >>
> >> NAK for both.
> >>
> >> Node names must not be an ABI, unless you talk about child of some
> >> device node. I don't think this is the case here. Look by phandle (for a
> >> device context) or by compatible (looks the case here).
> >
> > We need to get the cpu clock to set the initial cpu clock here. Search
> > by 'sysc' is the only  shared in all the dtsi files since it is the
> > clock provider node. Why is this not correct?
>
> Because device node name can change anytime and your entire Linux driver
> gets broken. Node name is not an ABI.

Understood.

>
> > I don't understand what
> > you mean with look by phandle for a device context.
>
> Your device node should contain phandle to the other node.
>
> > The case of
> > searching for compatible is a mess since as you can see in the
> > bindings there are tons of compatibles to search for, then (this code
> > is common to all ralink platforms).
>
> Compatible is one of the ways using ABI.

Ok so it is also a broken approach, then.

>
> Best regards,
> Krzysztof
>

Thanks for clarification.

Best regards,
    Sergio Paracuellos
  
Krzysztof Kozlowski June 17, 2023, 5:20 p.m. UTC | #9
On 17/06/2023 17:37, Sergio Paracuellos wrote:
>>> The case of
>>> searching for compatible is a mess since as you can see in the
>>> bindings there are tons of compatibles to search for, then (this code
>>> is common to all ralink platforms).
>>
>> Compatible is one of the ways using ABI.
> 
> Ok so it is also a broken approach, then.

What is exactly broken approach? Fetching by compatibles? Somehow many
other platforms do not have problem with that, even for multiple
compatibles. Why yours is special?

Anyway, it is not a correct way to get clocks frequency. There is CCF
for this, although maybe Ralink does not support it?

Best regards,
Krzysztof
  
Sergio Paracuellos June 17, 2023, 7:11 p.m. UTC | #10
On Sat, Jun 17, 2023 at 7:20 PM Krzysztof Kozlowski
<krzysztof.kozlowski@linaro.org> wrote:
>
> On 17/06/2023 17:37, Sergio Paracuellos wrote:
> >>> The case of
> >>> searching for compatible is a mess since as you can see in the
> >>> bindings there are tons of compatibles to search for, then (this code
> >>> is common to all ralink platforms).
> >>
> >> Compatible is one of the ways using ABI.
> >
> > Ok so it is also a broken approach, then.
>
> What is exactly broken approach? Fetching by compatibles? Somehow many
> other platforms do not have problem with that, even for multiple
> compatibles. Why yours is special?

I guess it is not special but I cannot figure out the way of getting
this clock using compatibles...

>
> Anyway, it is not a correct way to get clocks frequency. There is CCF
> for this, although maybe Ralink does not support it?

This means to use clk_get() if I understand properly but it does not
work at all for ralink...

>
> Best regards,
> Krzysztof
>

Thanks,
    Sergio Paracuellos
  
Sergio Paracuellos June 18, 2023, 5:04 a.m. UTC | #11
Hi krzysztof,

On Sat, Jun 17, 2023 at 9:11 PM Sergio Paracuellos
<sergio.paracuellos@gmail.com> wrote:
>
> On Sat, Jun 17, 2023 at 7:20 PM Krzysztof Kozlowski
> <krzysztof.kozlowski@linaro.org> wrote:
> >
> > On 17/06/2023 17:37, Sergio Paracuellos wrote:
> > >>> The case of
> > >>> searching for compatible is a mess since as you can see in the
> > >>> bindings there are tons of compatibles to search for, then (this code
> > >>> is common to all ralink platforms).
> > >>
> > >> Compatible is one of the ways using ABI.
> > >
> > > Ok so it is also a broken approach, then.
> >
> > What is exactly broken approach? Fetching by compatibles? Somehow many
> > other platforms do not have problem with that, even for multiple
> > compatibles. Why yours is special?

Ok so fetching by compatible would be a valid approach, then. The
following works for me. Would you also be ok doing it this way?

static const char *clk_cpu(int *idx)
{
    *idx = 1;

    switch (ralink_soc) {
    case RT2880_SOC:
        return "ralink,rt2880-sysc";
    case RT3883_SOC:
        return "ralink,rt3883-sysc";
    case RT305X_SOC_RT3050:
        return "ralink,rt3050-sysc";
    case RT305X_SOC_RT3052:
        return "ralink,rt3052-sysc";
    case RT305X_SOC_RT3350:
        return "ralink,rt3350-sysc";
    case RT305X_SOC_RT3352:
        return "ralink,rt3352-sysc";
    case RT305X_SOC_RT5350:
        return "ralink,rt5350-sysc";
    case MT762X_SOC_MT7620A:
        *idx = 2;
        return "ralink,mt7620a-sysc";
     case MT762X_SOC_MT7620N:
        *idx = 2;
        return "ralink,mt7620-sysc";
     case MT762X_SOC_MT7628AN:
        return "ralink,mt7628-sysc";
     case MT762X_SOC_MT7688:
       return "ralink,mt7688-sysc";
     default:
        *idx = -1;
        return "invalid";
  }
}

void __init plat_time_init(void)
{
    struct of_phandle_args clkspec;
    const char *compatible;
    struct clk *clk;
    int cpu_clk_idx;

    ralink_of_remap();

    compatible = clk_cpu(&cpu_clk_idx);
    if (cpu_clk_idx == -1)
        panic("unable to get CPU clock index");

   of_clk_init(NULL);
   clkspec.np = of_find_compatible_node(NULL, NULL, compatible);
   clkspec.args_count = 1;
   clkspec.args[0] = cpu_clk_idx;
   clk = of_clk_get_from_provider(&clkspec);
   if (IS_ERR(clk))
       panic("unable to get CPU clock, err=%ld", PTR_ERR(clk));
   pr_info("CPU Clock: %ldMHz\n", clk_get_rate(clk) / 1000000);
   mips_hpt_frequency = clk_get_rate(clk) / 2;
   clk_put(clk);
   timer_probe();
}

Thanks,
    Sergio Paracuellos
>
> I guess it is not special but I cannot figure out the way of getting
> this clock using compatibles...
>
> >
> > Anyway, it is not a correct way to get clocks frequency. There is CCF
> > for this, although maybe Ralink does not support it?
>
> This means to use clk_get() if I understand properly but it does not
> work at all for ralink...
>
> >
> > Best regards,
> > Krzysztof
> >
>
> Thanks,
>     Sergio Paracuellos
  

Patch

diff --git a/drivers/clk/ralink/Kconfig b/drivers/clk/ralink/Kconfig
index 6580d5edc676..7c4f335864a8 100644
--- a/drivers/clk/ralink/Kconfig
+++ b/drivers/clk/ralink/Kconfig
@@ -9,3 +9,10 @@  config CLK_MT7621
 	select MFD_SYSCON
 	help
 	  This driver supports MediaTek MT7621 basic clocks.
+
+config CLK_MTMIPS
+	bool "Clock driver for MTMIPS SoCs"
+	depends on SOC_RT305X || SOC_RT288X || SOC_RT3883 || SOC_MT7620 || COMPILE_TEST
+	select MFD_SYSCON
+	help
+	  This driver supports MTMIPS basic clocks.
diff --git a/drivers/clk/ralink/Makefile b/drivers/clk/ralink/Makefile
index cf6f9216379d..398c1bf8cbc1 100644
--- a/drivers/clk/ralink/Makefile
+++ b/drivers/clk/ralink/Makefile
@@ -1,2 +1,3 @@ 
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_CLK_MT7621) += clk-mt7621.o
+obj-$(CONFIG_CLK_MTMIPS) += clk-mtmips.o
diff --git a/drivers/clk/ralink/clk-mtmips.c b/drivers/clk/ralink/clk-mtmips.c
new file mode 100644
index 000000000000..955c0624d327
--- /dev/null
+++ b/drivers/clk/ralink/clk-mtmips.c
@@ -0,0 +1,1134 @@ 
+// 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("10000900.i2c", "bus") },
+	{ CLK_PERIPH("10000a00.i2s", "bus") },
+	{ CLK_PERIPH("10000b00.spi", "bus") },
+	{ CLK_PERIPH("10000b40.spi", "bus") },
+	{ CLK_PERIPH("10000100.timer", "bus") },
+	{ CLK_PERIPH("10000120.watchdog", "bus") },
+	{ CLK_PERIPH("10000500.uart", "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("10000900.i2c", "periph") },
+	{ CLK_PERIPH("10000a00.i2s", "periph") },
+	{ CLK_PERIPH("10000b00.spi", "bus") },
+	{ CLK_PERIPH("10000b40.spi", "bus") },
+	{ CLK_PERIPH("10000100.timer", "bus") },
+	{ CLK_PERIPH("10000120.watchdog", "bus") },
+	{ CLK_PERIPH("10000500.uart", "periph") },
+	{ 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("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("10000d00.uart1", "periph") },
+	{ CLK_PERIPH("10000e00.uart2", "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.uartlite", "periph") },
+	{ CLK_PERIPH("10000d00.uartlite", "periph") },
+	{ CLK_PERIPH("10000e00.uartlite", "periph") },
+	{ CLK_PERIPH("10000d00.uart1", "periph") },
+	{ CLK_PERIPH("10000e00.uart2", "periph") },
+	{ CLK_PERIPH("10180000.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 - 1) + 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 - 1) + 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  - 1) + 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);
+	}
+}
+
+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;
+	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 rt3050_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 rt3052_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 = &rt3050_clk_data,
+	},
+	{
+		.compatible = "ralink,rt3052-sysc",
+		.data = &rt3052_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,mt7620a-sysc",
+		.data = &mt7620_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(mt7620a_clk, "ralink,mt7620a-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,rt3050-sysc" },
+	{ .compatible = "ralink,rt3352-sysc" },
+	{ .compatible = "ralink,rt3883-sysc" },
+	{ .compatible = "ralink,rt5350-sysc" },
+	{ .compatible = "ralink,mt7620a-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);