1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2012-2016 Zhang, Keguang <keguang.zhang@gmail.com>
4 */
5
6#include <linux/clkdev.h>
7#include <linux/clk-provider.h>
8#include <linux/io.h>
9#include <linux/err.h>
10
11#include <loongson1.h>
12#include "clk.h"
13
14#define OSC		(33 * 1000000)
15#define DIV_APB		2
16
17static DEFINE_SPINLOCK(_lock);
18
19static unsigned long ls1x_pll_recalc_rate(struct clk_hw *hw,
20					  unsigned long parent_rate)
21{
22	u32 pll, rate;
23
24	pll = __raw_readl(LS1X_CLK_PLL_FREQ);
25	rate = 12 + (pll & GENMASK(5, 0));
26	rate *= OSC;
27	rate >>= 1;
28
29	return rate;
30}
31
32static const struct clk_ops ls1x_pll_clk_ops = {
33	.recalc_rate = ls1x_pll_recalc_rate,
34};
35
36static const char *const cpu_parents[] = { "cpu_clk_div", "osc_clk", };
37static const char *const ahb_parents[] = { "ahb_clk_div", "osc_clk", };
38static const char *const dc_parents[] = { "dc_clk_div", "osc_clk", };
39
40void __init ls1x_clk_init(void)
41{
42	struct clk_hw *hw;
43
44	hw = clk_hw_register_fixed_rate(NULL, "osc_clk", NULL, 0, OSC);
45	clk_hw_register_clkdev(hw, "osc_clk", NULL);
46
47	/* clock derived from 33 MHz OSC clk */
48	hw = clk_hw_register_pll(NULL, "pll_clk", "osc_clk",
49				 &ls1x_pll_clk_ops, 0);
50	clk_hw_register_clkdev(hw, "pll_clk", NULL);
51
52	/* clock derived from PLL clk */
53	/*                                 _____
54	 *         _______________________|     |
55	 * OSC ___/                       | MUX |___ CPU CLK
56	 *        \___ PLL ___ CPU DIV ___|     |
57	 *                                |_____|
58	 */
59	hw = clk_hw_register_divider(NULL, "cpu_clk_div", "pll_clk",
60				   CLK_GET_RATE_NOCACHE, LS1X_CLK_PLL_DIV,
61				   DIV_CPU_SHIFT, DIV_CPU_WIDTH,
62				   CLK_DIVIDER_ONE_BASED |
63				   CLK_DIVIDER_ROUND_CLOSEST, &_lock);
64	clk_hw_register_clkdev(hw, "cpu_clk_div", NULL);
65	hw = clk_hw_register_mux(NULL, "cpu_clk", cpu_parents,
66			       ARRAY_SIZE(cpu_parents),
67			       CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV,
68			       BYPASS_CPU_SHIFT, BYPASS_CPU_WIDTH, 0, &_lock);
69	clk_hw_register_clkdev(hw, "cpu_clk", NULL);
70
71	/*                                 _____
72	 *         _______________________|     |
73	 * OSC ___/                       | MUX |___ DC  CLK
74	 *        \___ PLL ___ DC  DIV ___|     |
75	 *                                |_____|
76	 */
77	hw = clk_hw_register_divider(NULL, "dc_clk_div", "pll_clk",
78				   0, LS1X_CLK_PLL_DIV, DIV_DC_SHIFT,
79				   DIV_DC_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock);
80	clk_hw_register_clkdev(hw, "dc_clk_div", NULL);
81	hw = clk_hw_register_mux(NULL, "dc_clk", dc_parents,
82			       ARRAY_SIZE(dc_parents),
83			       CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV,
84			       BYPASS_DC_SHIFT, BYPASS_DC_WIDTH, 0, &_lock);
85	clk_hw_register_clkdev(hw, "dc_clk", NULL);
86
87	/*                                 _____
88	 *         _______________________|     |
89	 * OSC ___/                       | MUX |___ DDR CLK
90	 *        \___ PLL ___ DDR DIV ___|     |
91	 *                                |_____|
92	 */
93	hw = clk_hw_register_divider(NULL, "ahb_clk_div", "pll_clk",
94				   0, LS1X_CLK_PLL_DIV, DIV_DDR_SHIFT,
95				   DIV_DDR_WIDTH, CLK_DIVIDER_ONE_BASED,
96				   &_lock);
97	clk_hw_register_clkdev(hw, "ahb_clk_div", NULL);
98	hw = clk_hw_register_mux(NULL, "ahb_clk", ahb_parents,
99			       ARRAY_SIZE(ahb_parents),
100			       CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV,
101			       BYPASS_DDR_SHIFT, BYPASS_DDR_WIDTH, 0, &_lock);
102	clk_hw_register_clkdev(hw, "ahb_clk", NULL);
103	clk_hw_register_clkdev(hw, "ls1x-dma", NULL);
104	clk_hw_register_clkdev(hw, "stmmaceth", NULL);
105
106	/* clock derived from AHB clk */
107	/* APB clk is always half of the AHB clk */
108	hw = clk_hw_register_fixed_factor(NULL, "apb_clk", "ahb_clk", 0, 1,
109					DIV_APB);
110	clk_hw_register_clkdev(hw, "apb_clk", NULL);
111	clk_hw_register_clkdev(hw, "ls1x-ac97", NULL);
112	clk_hw_register_clkdev(hw, "ls1x-i2c", NULL);
113	clk_hw_register_clkdev(hw, "ls1x-nand", NULL);
114	clk_hw_register_clkdev(hw, "ls1x-pwmtimer", NULL);
115	clk_hw_register_clkdev(hw, "ls1x-spi", NULL);
116	clk_hw_register_clkdev(hw, "ls1x-wdt", NULL);
117	clk_hw_register_clkdev(hw, "serial8250", NULL);
118}
119