GUI: Fix Tomato RAF theme for all builds. Compilation typo.
[tomato.git] / release / src-rt-6.x.4708 / linux / linux-2.6.36 / arch / arm / plat-brcm / iproc_cru.c
blob49313394df8c6313f1e1b3f424f07dbb4b141cfb
1 /*
2 * iProc Clock Control Unit
3 * The software model repsresents the hardware clock hierarchy
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>
16 #include <asm/clkdev.h>
17 #include <mach/clkdev.h>
19 static struct resource ccu_regs = {
20 .name = "cru_regs",
21 .start = 0x19000000,
22 .end = 0x19000fff,
23 .flags = IORESOURCE_MEM,
27 * Clock management scheme is a provisional implementation
28 * only intended to retreive the pre-set frequencies for each
29 * of the clocks.
33 * Get PLL running status and update output frequency
34 * for ARMPLL channel 0
36 static int a9pll0_status(struct clk * clk)
38 u32 regA, regB, regC;
39 u32 pdiv, ndiv_int, ndiv_frac, mdiv;
40 u64 x;
42 if( clk->type != CLK_PLL)
43 return -EINVAL;
45 BUG_ON( ! clk->regs_base );
46 BUG_ON( ! clk->parent );
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 */
53 /* reg C bit 8 is bypass mode - input frequency to output */
54 if( (regC & (1 << 8)) == 1 )
56 clk->rate = clk->parent->rate;
57 return 0;
60 /* reg A bit 28 is "lock" signal, has to be "1" for proper operation */
61 if( (regA & (1 << 28)) == 0 )
63 clk->rate = 0;
64 return -EIO;
67 /* Update PLL frequency */
70 /* pdiv in bits 24..27 (4 bits) */
71 pdiv = ( regA >> 24 ) & 0xf ;
72 if( pdiv == 0 ) pdiv = 0x10 ;
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 ;
78 /* feedback divider fraction in reg B, bits 0..19 */
79 ndiv_frac = regB & ((1<<20)-1);
81 /* post-divider is in reg C bits 0..7 */
82 mdiv = regC & 0xff ;
83 if(mdiv == 0) mdiv = 0x100;
85 x = ((u64) ndiv_int << 20) | ndiv_frac ;
86 x = (x * clk->parent->rate ) >> 20 ;
87 (void) do_div( x, pdiv );
90 (void) do_div(x, mdiv);
91 clk->rate = (u32)(x);
93 return 0;
98 * Get PLL running status and update output frequency
99 * for ARMPLL channel 1
101 static int a9pll1_status(struct clk * clk)
103 u32 regA, regB, regC, regD;
104 unsigned pdiv, ndiv_int, ndiv_frac, mdiv;
105 u64 x;
107 if( clk->type != CLK_PLL)
108 return -EINVAL;
110 BUG_ON( ! clk->regs_base );
111 BUG_ON( ! clk->parent );
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*/
119 /* reg C bit 8 is bypass mode - input frequency to output */
120 if( (regC & (1 << 8)) == 1 )
122 clk->rate = clk->parent->rate;
123 return 0;
126 /* reg A bit 28 is "lock" signal, has to be "1" for proper operation */
127 if( (regA & (1 << 28)) == 0 )
129 clk->rate = 0;
130 return -EIO;
133 /* Update PLL frequency */
136 /* pdiv in bits 24..27 (4 bits) */
137 pdiv = ( regA >> 24 ) & 0xf ;
138 if( pdiv == 0 ) pdiv = 0x10 ;
140 /* Check if offset mode is active */
141 if( regD & (1<<29) )
143 /* pllarm_ndiv_int_offset bits 27:20 */
144 ndiv_int = ( regD >> 20 ) & 0xff ;
145 if( ndiv_int == 0 ) ndiv_int = 1 << 8 ;
147 /* pllarm_ndiv_frac_offset bits 19:0 */
148 ndiv_frac = regD & ((1<<20)-1);
150 /* If offset not active, channel 0 parameters are used */
151 else
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 ;
157 /* feedback divider fraction in reg B, bits 0..19 */
158 ndiv_frac = regB & ((1<<20)-1);
160 /* post-divider is in reg C bits 0..7 */
161 mdiv = regC & 0xff ;
162 if(mdiv == 0) mdiv = 0x100;
165 x = ((u64) ndiv_int << 20) | ndiv_frac ;
166 x = (x * clk->parent->rate ) >> 20 ;
167 (void) do_div( x, pdiv );
169 (void) do_div(x, mdiv);
170 clk->rate = (u32)(x);
172 return 0;
176 static const struct clk_ops a9pll0_ops = {
177 .status = a9pll0_status,
180 static const struct clk_ops a9pll1_ops = {
181 .status = a9pll1_status,
186 * iProc A9 PLL
187 * could be used as source for generated clocks
189 static struct clk clk_a9pll[2] = {
191 .ops = &a9pll0_ops,
192 .name = "arm_cpu_clk",
193 .type = CLK_PLL,
194 .chan = 0xa,
197 .ops = &a9pll1_ops,
198 .name = "arm_cpu_h_clk",
199 .type = CLK_PLL,
200 .chan = 0xb,
205 * Decode the Frequency ID setting for arm_clk
207 static int iproc_cru_arm_freq_id( void * __iomem regs_base )
209 u32 reg_f, reg;
210 unsigned policy, fid, i ;
211 u8 arm_clk_policy_mask = 0, apb0_clk_policy_mask = 0 ;
213 /* bits 0..2 freq# for policy0, 8..10 for policy1,
214 * 16..18 policy2, 24..26 policy 3
216 reg_f = readl( regs_base+0x008 );/*IHOST_PROC_CLK_POLICY_FREQ*/
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;
226 /* TBD: Where to get hardware policy setting ? */
227 policy = 0 ;
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 ;
234 fid = (reg_f >> (8 * policy)) & 0xf;
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);
241 if( fid != i ) {
242 printk(KERN_WARNING
243 "IPROC CRU clock frequency id override %d->%d\n",
244 fid, i );
245 fid = i ;
248 return fid ;
252 * Get status of any of the ARMPLL output channels
254 static int a9pll_chan_status(struct clk * clk)
256 u32 reg;
257 unsigned freq_id, div ;
259 if( clk->type != CLK_DIV)
260 return -EINVAL;
262 BUG_ON( ! clk->regs_base );
264 freq_id = iproc_cru_arm_freq_id( clk->regs_base );
267 switch( clk->chan )
269 default:
270 return -EINVAL;
272 case 0x3a: /* arm_clk */
273 if( freq_id == 7 ) {
274 clk->parent = &clk_a9pll[1]; /* arm_clk_h */
275 div = 1;
277 else if( freq_id == 6 ) {
278 clk->parent = &clk_a9pll[0]; /* arm_clk */
279 div = 1;
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;
289 else {
290 clk->parent = clk_a9pll[0].parent;
291 div = 1;
293 break;
295 case 0x0f: /* periph_clk */
296 div = 2;
297 break;
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;
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;
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;
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 ;
328 return 0;
332 static const struct clk_ops a9pll_chan_ops = {
333 .status = a9pll_chan_status,
337 * iProc A9 PLL output clocks
339 static 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 },
352 static 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], },
362 void __init soc_cru_init( struct clk * src_clk )
364 void * __iomem reg_base;
365 unsigned i;
367 BUG_ON( request_resource( &iomem_resource, &ccu_regs ));
369 reg_base = ioremap( ccu_regs.start, resource_size(&ccu_regs));
371 BUG_ON( IS_ERR_OR_NULL(reg_base ));
373 /* Initialize clocks */
374 for(i = 0; i < ARRAY_SIZE(clk_a9pll); i++ )
376 clk_a9pll[i].regs_base = reg_base ;
377 clk_a9pll[i].parent = src_clk ;
380 for(i = 0; i < ARRAY_SIZE(clk_a9chan); i++ )
382 clk_a9chan[i].regs_base = reg_base ;
385 /* Install clock sources into the lookup table */
386 clkdev_add_table(cru_clk_lookups,
387 ARRAY_SIZE(cru_clk_lookups));
390 void cru_clocks_show( void )
392 unsigned i;
394 printk( "CRU Clocks:\n" );
395 for(i = 0; i < ARRAY_SIZE( cru_clk_lookups); i++ )
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 )
403 printk( "CRU Clocks# %u\n", i );
407 * Perform reset of one of the two cores by means of the IPROC CRU
409 int soc_cpu_reset(unsigned cpu, bool reset)
411 void * __iomem reg_base;
412 u32 reg;
414 reg_base = clk_a9pll[0].regs_base ;
416 if( cpu >= 2 || ! reg_base )
417 return -EINVAL;
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.
429 /* Enable writing to reset control bits */
430 writel( 0xa5a5 << 8 | 0x1, reg_base + 0xf00 );
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
439 reg = 0x3 ^ (1 << cpu) ;
440 writel( reg, reg_base + 0xf00 );
441 writel( 0x3, reg_base + 0xf00 );
443 /* Disable writing to reset control bits */
444 writel( 0x0, reg_base + 0xf00 );
446 return 0;