• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/arch/arm/plat-brcm/
1/*
2 * iProc Clock Control Unit
3 * The software model repsresents the hardware clock hierarchy
4 *
5 */
6#include <linux/kernel.h>
7#include <linux/init.h>
8#include <linux/module.h>
9#include <linux/errno.h>
10#include <linux/err.h>
11#include <linux/clk.h>
12#include <linux/io.h>
13#include <linux/ioport.h>
14#include <linux/delay.h>
15
16#include <asm/clkdev.h>
17#include <mach/clkdev.h>
18
19static struct resource ccu_regs = {
20	.name = "cru_regs",
21	.start = 0x19000000,
22	.end =  0x19000fff,
23        .flags = IORESOURCE_MEM,
24};
25
26/*
27 * Clock management scheme is a provisional implementation
28 * only intended to retreive the pre-set frequencies for each
29 * of the clocks.
30 */
31
32/*
33 * Get PLL running status and update output frequency
34 * for ARMPLL channel 0
35 */
36static int a9pll0_status(struct clk * clk)
37{
38	u32 regA, regB, regC;
39	u32 pdiv, ndiv_int, ndiv_frac, mdiv;
40	u64 x;
41
42	if( clk->type != CLK_PLL)
43		return -EINVAL;
44
45	BUG_ON( ! clk->regs_base );
46	BUG_ON( ! clk->parent );
47
48	/* read status register */
49	regA = readl( clk->regs_base + 0x0c00 );/* IHOST_PROC_CLK_PLLARMA */
50	regB = readl( clk->regs_base + 0x0c04 );/* IHOST_PROC_CLK_PLLARMB */
51	regC = readl( clk->regs_base + 0x0c08 );/* IHOST_PROC_CLK_PLLARMB */
52
53	/* reg C bit 8 is bypass mode - input frequency to output */
54	if( (regC & (1 << 8)) == 1 )
55		{
56		clk->rate = clk->parent->rate;
57		return 0;
58		}
59
60	/* reg A bit 28 is "lock" signal, has to be "1" for proper operation */
61	if( (regA & (1 << 28)) == 0 )
62		{
63		clk->rate = 0;
64		return -EIO;
65		}
66
67	/* Update PLL frequency */
68
69
70	/* pdiv in bits 24..27 (4 bits) */
71	pdiv = ( regA >> 24 ) & 0xf ;
72	if( pdiv == 0 ) pdiv = 0x10 ;
73
74	/* feedback divider (int)  in bits 8..17 (10 bits) */
75	ndiv_int = ( regA >> 8) & ((1<<10)-1);
76	if( ndiv_int == 0 ) ndiv_int = 1 << 10 ;
77
78	/* feedback divider fraction in reg B, bits 0..19 */
79	ndiv_frac = regB & ((1<<20)-1);
80
81	/* post-divider is in reg C bits 0..7 */
82	mdiv = regC & 0xff ;
83	if(mdiv == 0) mdiv = 0x100;
84
85	x = ((u64) ndiv_int << 20) | ndiv_frac ;
86	x = (x * clk->parent->rate ) >> 20 ;
87	(void) do_div( x, pdiv );
88
89
90	(void) do_div(x, mdiv);
91	clk->rate = (u32)(x);
92
93	return 0;
94}
95
96
97/*
98 * Get PLL running status and update output frequency
99 * for ARMPLL channel 1
100 */
101static int a9pll1_status(struct clk * clk)
102{
103	u32 regA, regB, regC, regD;
104	unsigned pdiv, ndiv_int, ndiv_frac, mdiv;
105	u64 x;
106
107	if( clk->type != CLK_PLL)
108		return -EINVAL;
109
110	BUG_ON( ! clk->regs_base );
111	BUG_ON( ! clk->parent );
112
113	/* read status register */
114	regA = readl( clk->regs_base+0xc00 );/* IHOST_PROC_CLK_PLLARMB */
115	regB = readl( clk->regs_base+0xc04 );/* IHOST_PROC_CLK_PLLARMB */
116	regC = readl( clk->regs_base+0xc20 );/* IHOST_PROC_CLK_PLLARMCTRL5 */
117	regD = readl( clk->regs_base+0xc24 );/* IHOST_PROC_CLK_PLLARM_OFFSET*/
118
119	/* reg C bit 8 is bypass mode - input frequency to output */
120	if( (regC & (1 << 8)) == 1 )
121		{
122		clk->rate = clk->parent->rate;
123		return 0;
124		}
125
126	/* reg A bit 28 is "lock" signal, has to be "1" for proper operation */
127	if( (regA & (1 << 28)) == 0 )
128		{
129		clk->rate = 0;
130		return -EIO;
131		}
132
133	/* Update PLL frequency */
134
135
136	/* pdiv in bits 24..27 (4 bits) */
137	pdiv = ( regA >> 24 ) & 0xf ;
138	if( pdiv == 0 ) pdiv = 0x10 ;
139
140	/* Check if offset mode is active */
141	if( regD & (1<<29) )
142		{
143		/* pllarm_ndiv_int_offset bits 27:20 */
144		ndiv_int = ( regD >> 20 ) & 0xff ;
145		if( ndiv_int == 0 ) ndiv_int = 1 << 8 ;
146
147		/* pllarm_ndiv_frac_offset bits 19:0 */
148		ndiv_frac = regD & ((1<<20)-1);
149		}
150	/* If offset not active, channel 0 parameters are used */
151	else
152		{
153		/* feedback divider (int)  in bits 8..17 (10 bits) */
154		ndiv_int = ( regA >> 8) & ((1<<10)-1);
155		if( ndiv_int == 0 ) ndiv_int = 1 << 10 ;
156
157		/* feedback divider fraction in reg B, bits 0..19 */
158		ndiv_frac = regB & ((1<<20)-1);
159		}
160	/* post-divider is in reg C bits 0..7 */
161	mdiv = regC & 0xff ;
162	if(mdiv == 0) mdiv = 0x100;
163
164
165	x = ((u64) ndiv_int << 20) | ndiv_frac ;
166	x = (x * clk->parent->rate ) >> 20 ;
167	(void) do_div( x, pdiv );
168
169	(void) do_div(x, mdiv);
170	clk->rate = (u32)(x);
171
172	return 0;
173}
174
175
176static const struct clk_ops a9pll0_ops = {
177	.status = a9pll0_status,
178};
179
180static const struct clk_ops a9pll1_ops = {
181	.status = a9pll1_status,
182};
183
184
185/*
186 * iProc A9 PLL
187 * could be used as source for generated clocks
188 */
189static struct clk clk_a9pll[2] = {
190	{
191	.ops 	= &a9pll0_ops,
192	.name 	= "arm_cpu_clk",
193	.type	= CLK_PLL,
194	.chan	= 0xa,
195	},
196	{
197	.ops 	= &a9pll1_ops,
198	.name 	= "arm_cpu_h_clk",
199	.type	= CLK_PLL,
200	.chan	= 0xb,
201	},
202};
203
204/*
205 * Decode the Frequency ID setting for arm_clk
206 */
207static int iproc_cru_arm_freq_id( void * __iomem regs_base )
208{
209	u32 reg_f, reg;
210	unsigned policy, fid, i ;
211	u8 arm_clk_policy_mask = 0, apb0_clk_policy_mask = 0 ;
212
213	/* bits 0..2 freq# for policy0, 8..10 for policy1,
214	 * 16..18 policy2, 24..26 policy 3
215	 */
216	reg_f = readl( regs_base+0x008 );/*IHOST_PROC_CLK_POLICY_FREQ*/
217
218	for(i = 0; i < 4; i ++ ) {
219		/* Reg IHOST_PROC_CLK_POLICY<i>_MASK*/
220		/* bit 20 arm policy mask, bit 21 apb0 policy mask */
221		reg = readl( regs_base+0x010+i*4 );
222		arm_clk_policy_mask |= (1 & ( reg >> 20 )) << i;
223		apb0_clk_policy_mask |=  (1 & ( reg >> 21 )) << i;
224	}
225
226	/* TBD: Where to get hardware policy setting ? */
227	policy = 0 ;
228
229	/* Check for PLL policy software override */
230	reg = readl( regs_base+0xe00 );/* IHOST_PROC_CLK_ARM_DIV */
231	if( reg & (1 << 4 ))
232		policy = reg & 0xf ;
233
234	fid = (reg_f >> (8 * policy)) & 0xf;
235
236	/* Verify freq_id from debug register */
237	reg = readl( regs_base+0xec0 );/* IHOST_PROC_CLK_POLICY_DBG */
238	/* Bits 12..14 contain active frequency_id */
239	i = 0x7 & (reg >> 12);
240
241	if( fid != i ) {
242		printk(KERN_WARNING
243			"IPROC CRU clock frequency id override %d->%d\n",
244			fid, i );
245		fid = i ;
246		}
247
248	return fid ;
249}
250
251/*
252 * Get status of any of the ARMPLL output channels
253 */
254static int a9pll_chan_status(struct clk * clk)
255{
256	u32 reg;
257	unsigned freq_id, div ;
258
259	if( clk->type != CLK_DIV)
260		return -EINVAL;
261
262	BUG_ON( ! clk->regs_base );
263
264	freq_id = iproc_cru_arm_freq_id( clk->regs_base );
265
266
267	switch( clk->chan )
268		{
269		default:
270			return -EINVAL;
271
272		case 0x3a:	/* arm_clk */
273			if( freq_id == 7 ) {
274				clk->parent = &clk_a9pll[1]; /* arm_clk_h */
275				div = 1;
276				}
277			else if( freq_id == 6 ) {
278				clk->parent = &clk_a9pll[0]; /* arm_clk */
279				div = 1;
280				}
281			else if( freq_id == 2 ) {
282				struct clk * clk_lcpll_200;
283				clk_lcpll_200 =
284					clk_get_sys( NULL, "sdio_clk");
285				BUG_ON( ! clk_lcpll_200 );
286				clk->parent = clk_lcpll_200;
287				div = 2;
288				}
289			else {
290				clk->parent = clk_a9pll[0].parent;
291				div = 1;
292				}
293			break;
294
295		case 0x0f:	/* periph_clk */
296			div = 2;
297			break;
298
299		case 0x0a:
300			/* IHOST_PROC_CLK_ARM_DIV */
301       			reg = readl( clk->regs_base+0xe00 );
302			/* apb0_free_div bits 10:8 */
303			div = ( reg >> 8 ) & 0x7;
304			if( div == 0 ) div = 8;
305			break;
306
307		case 0x0b:
308			/* IHOST_PROC_CLK_ARM_DIV */
309       			reg = readl( clk->regs_base+0xe00 );
310			/* arm_switch_div bits 6:5 */
311			div = ( reg >> 5 ) & 0x3 ;
312			if( div == 0 ) div = 4;
313			break;
314
315		case 0x1a:
316			/* IHOST_PROC_CLK_APB_DIV apb_clk_div bits 1:0 */
317			reg = readl( clk->regs_base+0xa10 );
318			div = reg & 0x3 ;
319			if( div == 0 ) div = 4;
320			break;
321
322		}
323
324	BUG_ON( ! clk->parent );
325	/* Parent may have changed, use top-level API to force recursion */
326	clk->rate = clk_get_rate( clk->parent ) / div ;
327
328	return 0;
329}
330
331
332static const struct clk_ops a9pll_chan_ops = {
333	.status = a9pll_chan_status,
334};
335
336/*
337 * iProc A9 PLL output clocks
338 */
339static struct clk clk_a9chan[] = {
340	{ .ops = &a9pll_chan_ops, .type = CLK_DIV, .parent = &clk_a9pll[0],
341	.name = "arm_clk", 	.chan = 0x3a },
342	{ .ops = &a9pll_chan_ops, .type = CLK_DIV, .parent = &clk_a9chan[0],
343	.name = "periph_clk", 	.chan = 0x0f },
344	{ .ops = &a9pll_chan_ops, .type = CLK_DIV, .parent = &clk_a9chan[0],
345	.name = "apb0_free", 	.chan = 0x0a },
346	{ .ops = &a9pll_chan_ops, .type = CLK_DIV, .parent = &clk_a9chan[0],
347	.name = "arm_switch", 	.chan = 0x0b },
348	{ .ops = &a9pll_chan_ops, .type = CLK_DIV, .parent = &clk_a9chan[0],
349	.name = "apb_clk", 	.chan = 0x1a },
350};
351
352static struct clk_lookup cru_clk_lookups[] = {
353	{ .con_id= "armpll_clk", 	.clk= &clk_a9pll[0], },
354	{ .con_id= "armpll_h_clk",	.clk= &clk_a9pll[1], },
355	{ .con_id= "arm_clk", 	.clk= &clk_a9chan[0], },
356	{ .con_id= "periph_clk",.clk= &clk_a9chan[1], },
357	{ .con_id= "apb0_free",	.clk= &clk_a9chan[2], },
358	{ .con_id= "axi_clk",	.clk= &clk_a9chan[3], },
359	{ .con_id= "apb_clk",	.clk= &clk_a9chan[4], },
360};
361
362void __init soc_cru_init( struct clk * src_clk )
363{
364	void * __iomem reg_base;
365	unsigned i;
366
367	BUG_ON( request_resource( &iomem_resource, &ccu_regs ));
368
369	reg_base = ioremap( ccu_regs.start, resource_size(&ccu_regs));
370
371	BUG_ON( IS_ERR_OR_NULL(reg_base ));
372
373	/* Initialize clocks */
374	for(i = 0; i < ARRAY_SIZE(clk_a9pll); i++ )
375		{
376		clk_a9pll[i].regs_base = reg_base ;
377		clk_a9pll[i].parent = src_clk ;
378		}
379
380	for(i = 0; i < ARRAY_SIZE(clk_a9chan); i++ )
381		{
382		clk_a9chan[i].regs_base = reg_base ;
383		}
384
385	/* Install clock sources into the lookup table */
386	clkdev_add_table(cru_clk_lookups,
387			ARRAY_SIZE(cru_clk_lookups));
388}
389
390void cru_clocks_show( void )
391{
392	unsigned i;
393
394	printk( "CRU Clocks:\n" );
395	for(i = 0; i < ARRAY_SIZE( cru_clk_lookups); i++ )
396		{
397		printk( "%s: (%s) %lu\n",
398			cru_clk_lookups[i].con_id,
399			cru_clk_lookups[i].clk->name,
400			clk_get_rate( cru_clk_lookups[i].clk )
401			);
402		}
403	printk( "CRU Clocks# %u\n", i );
404}
405
406/*
407 * Perform reset of one of the two cores by means of the IPROC CRU
408 */
409int soc_cpu_reset(unsigned cpu, bool reset)
410{
411	void * __iomem reg_base;
412	u32 reg;
413
414	reg_base =	clk_a9pll[0].regs_base ;
415
416	if( cpu >= 2 || ! reg_base )
417		return -EINVAL;
418
419	/*
420	register IHOST_PROC_RST_WR_ACCESS	 0x19000F00
421	<password> bits[23:8] Read will return 0. 0x0000
422	<rstmgr_acc> bits[0] Reset manager access enable.
423		This bit must be 1 for any write access to the remaining Reset
424		manager registers. This field is protected from accidental
425		modification. Writing is allowed only when write data
426		bits [23:8] contain A5A5.
427	*/
428
429	/* Enable writing to reset control bits */
430	writel( 0xa5a5 << 8 | 0x1, reg_base + 0xf00 );
431
432	/*
433	register IHOST_PROC_RST_A9_CORE_SOFT_RSTN	0x19000F08
434	<a9_core_1_soft_rstn> bit[1] - Soft reset for a9_core_1.
435		When this bit is 0 a9_core_1 will be in reset state. Default=1
436	<a9_core_0_soft_rstn> bit[0] - Soft reset for a9_core_0.
437		When this bit is 0 a9_core_0 will be in reset state. Default=1
438	*/
439	reg = 0x3 ^ (1 << cpu) ;
440	writel( reg, reg_base + 0xf00 );
441	writel( 0x3, reg_base + 0xf00 );
442
443	/* Disable writing to reset control bits */
444	writel( 0x0, reg_base + 0xf00 );
445
446	return 0;
447}
448