Blame view

drivers/clk/clk-clps711x.c 5.89 KB
631c53478   Alexander Shiyan   clk: Add CLPS711X...
1
2
3
4
5
6
7
8
9
10
  /*
   *  Cirrus Logic CLPS711X CLK driver
   *
   *  Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   */
631c53478   Alexander Shiyan   clk: Add CLPS711X...
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  #include <linux/clk-provider.h>
  #include <linux/clkdev.h>
  #include <linux/io.h>
  #include <linux/ioport.h>
  #include <linux/of_address.h>
  #include <linux/slab.h>
  #include <linux/mfd/syscon/clps711x.h>
  
  #include <dt-bindings/clock/clps711x-clock.h>
  
  #define CLPS711X_SYSCON1	(0x0100)
  #define CLPS711X_SYSCON2	(0x1100)
  #define CLPS711X_SYSFLG2	(CLPS711X_SYSCON2 + SYSFLG_OFFSET)
  #define CLPS711X_PLLR		(0xa5a8)
  
  #define CLPS711X_EXT_FREQ	(13000000)
  #define CLPS711X_OSC_FREQ	(3686400)
  
  static const struct clk_div_table spi_div_table[] = {
  	{ .val = 0, .div = 32, },
  	{ .val = 1, .div = 8, },
  	{ .val = 2, .div = 2, },
  	{ .val = 3, .div = 1, },
  };
  
  static const struct clk_div_table timer_div_table[] = {
  	{ .val = 0, .div = 256, },
  	{ .val = 1, .div = 1, },
  };
  
  struct clps711x_clk {
f48d947a1   Stephen Boyd   clk: clps711x: Mi...
42
43
  	spinlock_t			lock;
  	struct clk_hw_onecell_data	clk_data;
631c53478   Alexander Shiyan   clk: Add CLPS711X...
44
45
46
47
48
49
50
51
52
53
54
  };
  
  static struct clps711x_clk * __init _clps711x_clk_init(void __iomem *base,
  						       u32 fref)
  {
  	u32 tmp, f_cpu, f_pll, f_bus, f_tim, f_pwm, f_spi;
  	struct clps711x_clk *clps711x_clk;
  	unsigned i;
  
  	if (!base)
  		return ERR_PTR(-ENOMEM);
f48d947a1   Stephen Boyd   clk: clps711x: Mi...
55
56
57
  	clps711x_clk = kzalloc(sizeof(*clps711x_clk) +
  			sizeof(*clps711x_clk->clk_data.hws) * CLPS711X_CLK_MAX,
  			GFP_KERNEL);
631c53478   Alexander Shiyan   clk: Add CLPS711X...
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  	if (!clps711x_clk)
  		return ERR_PTR(-ENOMEM);
  
  	spin_lock_init(&clps711x_clk->lock);
  
  	/* Read PLL multiplier value and sanity check */
  	tmp = readl(base + CLPS711X_PLLR) >> 24;
  	if (((tmp >= 10) && (tmp <= 50)) || !fref)
  		f_pll = DIV_ROUND_UP(CLPS711X_OSC_FREQ * tmp, 2);
  	else
  		f_pll = fref;
  
  	tmp = readl(base + CLPS711X_SYSFLG2);
  	if (tmp & SYSFLG2_CKMODE) {
  		f_cpu = CLPS711X_EXT_FREQ;
  		f_bus = CLPS711X_EXT_FREQ;
  		f_spi = DIV_ROUND_CLOSEST(CLPS711X_EXT_FREQ, 96);
  		f_pll = 0;
  		f_pwm = DIV_ROUND_CLOSEST(CLPS711X_EXT_FREQ, 128);
  	} else {
  		f_cpu = f_pll;
  		if (f_cpu > 36864000)
  			f_bus = DIV_ROUND_UP(f_cpu, 2);
  		else
  			f_bus = 36864000 / 2;
  		f_spi = DIV_ROUND_CLOSEST(f_cpu, 576);
  		f_pwm = DIV_ROUND_CLOSEST(f_cpu, 768);
  	}
  
  	if (tmp & SYSFLG2_CKMODE) {
  		if (readl(base + CLPS711X_SYSCON2) & SYSCON2_OSTB)
  			f_tim = DIV_ROUND_CLOSEST(CLPS711X_EXT_FREQ, 26);
  		else
  			f_tim = DIV_ROUND_CLOSEST(CLPS711X_EXT_FREQ, 24);
  	} else
  		f_tim = DIV_ROUND_CLOSEST(f_cpu, 144);
  
  	tmp = readl(base + CLPS711X_SYSCON1);
  	/* Timer1 in free running mode.
  	 * Counter will wrap around to 0xffff when it underflows
  	 * and will continue to count down.
  	 */
  	tmp &= ~(SYSCON1_TC1M | SYSCON1_TC1S);
  	/* Timer2 in prescale mode.
  	 * Value writen is automatically re-loaded when
  	 * the counter underflows.
  	 */
  	tmp |= SYSCON1_TC2M | SYSCON1_TC2S;
  	writel(tmp, base + CLPS711X_SYSCON1);
f48d947a1   Stephen Boyd   clk: clps711x: Mi...
107
108
109
110
111
112
113
114
115
116
117
118
  	clps711x_clk->clk_data.hws[CLPS711X_CLK_DUMMY] =
  		clk_hw_register_fixed_rate(NULL, "dummy", NULL, 0, 0);
  	clps711x_clk->clk_data.hws[CLPS711X_CLK_CPU] =
  		clk_hw_register_fixed_rate(NULL, "cpu", NULL, 0, f_cpu);
  	clps711x_clk->clk_data.hws[CLPS711X_CLK_BUS] =
  		clk_hw_register_fixed_rate(NULL, "bus", NULL, 0, f_bus);
  	clps711x_clk->clk_data.hws[CLPS711X_CLK_PLL] =
  		clk_hw_register_fixed_rate(NULL, "pll", NULL, 0, f_pll);
  	clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMERREF] =
  		clk_hw_register_fixed_rate(NULL, "timer_ref", NULL, 0, f_tim);
  	clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMER1] =
  		clk_hw_register_divider_table(NULL, "timer1", "timer_ref", 0,
631c53478   Alexander Shiyan   clk: Add CLPS711X...
119
120
  					   base + CLPS711X_SYSCON1, 5, 1, 0,
  					   timer_div_table, &clps711x_clk->lock);
f48d947a1   Stephen Boyd   clk: clps711x: Mi...
121
122
  	clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMER2] =
  		clk_hw_register_divider_table(NULL, "timer2", "timer_ref", 0,
631c53478   Alexander Shiyan   clk: Add CLPS711X...
123
124
  					   base + CLPS711X_SYSCON1, 7, 1, 0,
  					   timer_div_table, &clps711x_clk->lock);
f48d947a1   Stephen Boyd   clk: clps711x: Mi...
125
126
127
128
129
130
  	clps711x_clk->clk_data.hws[CLPS711X_CLK_PWM] =
  		clk_hw_register_fixed_rate(NULL, "pwm", NULL, 0, f_pwm);
  	clps711x_clk->clk_data.hws[CLPS711X_CLK_SPIREF] =
  		clk_hw_register_fixed_rate(NULL, "spi_ref", NULL, 0, f_spi);
  	clps711x_clk->clk_data.hws[CLPS711X_CLK_SPI] =
  		clk_hw_register_divider_table(NULL, "spi", "spi_ref", 0,
631c53478   Alexander Shiyan   clk: Add CLPS711X...
131
132
  					   base + CLPS711X_SYSCON1, 16, 2, 0,
  					   spi_div_table, &clps711x_clk->lock);
f48d947a1   Stephen Boyd   clk: clps711x: Mi...
133
134
135
136
  	clps711x_clk->clk_data.hws[CLPS711X_CLK_UART] =
  		clk_hw_register_fixed_factor(NULL, "uart", "bus", 0, 1, 10);
  	clps711x_clk->clk_data.hws[CLPS711X_CLK_TICK] =
  		clk_hw_register_fixed_rate(NULL, "tick", NULL, 0, 64);
631c53478   Alexander Shiyan   clk: Add CLPS711X...
137
  	for (i = 0; i < CLPS711X_CLK_MAX; i++)
f48d947a1   Stephen Boyd   clk: clps711x: Mi...
138
  		if (IS_ERR(clps711x_clk->clk_data.hws[i]))
631c53478   Alexander Shiyan   clk: Add CLPS711X...
139
140
  			pr_err("clk %i: register failed with %ld
  ",
f48d947a1   Stephen Boyd   clk: clps711x: Mi...
141
  			       i, PTR_ERR(clps711x_clk->clk_data.hws[i]));
631c53478   Alexander Shiyan   clk: Add CLPS711X...
142
143
144
145
146
147
148
149
150
151
152
153
154
  
  	return clps711x_clk;
  }
  
  void __init clps711x_clk_init(void __iomem *base)
  {
  	struct clps711x_clk *clps711x_clk;
  
  	clps711x_clk = _clps711x_clk_init(base, 73728000);
  
  	BUG_ON(IS_ERR(clps711x_clk));
  
  	/* Clocksource */
f48d947a1   Stephen Boyd   clk: clps711x: Mi...
155
  	clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMER1],
631c53478   Alexander Shiyan   clk: Add CLPS711X...
156
  			    NULL, "clps711x-timer.0");
f48d947a1   Stephen Boyd   clk: clps711x: Mi...
157
  	clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMER2],
631c53478   Alexander Shiyan   clk: Add CLPS711X...
158
159
160
  			    NULL, "clps711x-timer.1");
  
  	/* Drivers */
f48d947a1   Stephen Boyd   clk: clps711x: Mi...
161
  	clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_PWM],
631c53478   Alexander Shiyan   clk: Add CLPS711X...
162
  			    NULL, "clps711x-pwm");
f48d947a1   Stephen Boyd   clk: clps711x: Mi...
163
  	clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_UART],
631c53478   Alexander Shiyan   clk: Add CLPS711X...
164
  			    NULL, "clps711x-uart.0");
f48d947a1   Stephen Boyd   clk: clps711x: Mi...
165
  	clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_UART],
631c53478   Alexander Shiyan   clk: Add CLPS711X...
166
167
168
169
170
171
172
173
174
175
176
177
178
179
  			    NULL, "clps711x-uart.1");
  }
  
  #ifdef CONFIG_OF
  static void __init clps711x_clk_init_dt(struct device_node *np)
  {
  	void __iomem *base = of_iomap(np, 0);
  	struct clps711x_clk *clps711x_clk;
  	u32 fref = 0;
  
  	WARN_ON(of_property_read_u32(np, "startup-frequency", &fref));
  
  	clps711x_clk = _clps711x_clk_init(base, fref);
  	BUG_ON(IS_ERR(clps711x_clk));
f48d947a1   Stephen Boyd   clk: clps711x: Mi...
180
181
182
  	clps711x_clk->clk_data.num = CLPS711X_CLK_MAX;
  	of_clk_add_hw_provider(np, of_clk_hw_onecell_get,
  			       &clps711x_clk->clk_data);
631c53478   Alexander Shiyan   clk: Add CLPS711X...
183
  }
893b77980   Alexander Shiyan   clk: clps711x: Ch...
184
  CLK_OF_DECLARE(clps711x, "cirrus,ep7209-clk", clps711x_clk_init_dt);
631c53478   Alexander Shiyan   clk: Add CLPS711X...
185
  #endif