• 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 * Northstar DMU (Device Management Unit),
3 * i.e. clocks, I/O pads, GPIO etc.
4 * SoC-specific hardware support features.
5 *
6 * Documents:
7 * Northstar_top_power_uarch_v1_0.pdf
8 *
9 * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
10 *
11 * Permission to use, copy, modify, and/or distribute this software for any
12 * purpose with or without fee is hereby granted, provided that the above
13 * copyright notice and this permission notice appear in all copies.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 *
23 * $Id: $
24 */
25#include <linux/kernel.h>
26#include <linux/init.h>
27#include <linux/module.h>
28#include <linux/errno.h>
29#include <linux/err.h>
30#include <linux/clk.h>
31#include <linux/io.h>
32#include <linux/ioport.h>
33#include <linux/delay.h>
34#include <linux/proc_fs.h>
35
36#include <asm/clkdev.h>
37#include <mach/clkdev.h>
38#include <mach/io_map.h>
39#include <plat/plat-bcm5301x.h>
40
41#ifdef CONFIG_PROC_FS
42#define DMU_PROC_NAME	"dmu"
43#endif /* CONFIG_PROC_FS */
44
45static struct resource dmu_regs = {
46	.name = "dmu_regs",
47	.start = SOC_DMU_BASE_PA,
48	.end = SOC_DMU_BASE_PA + SZ_4K -1,
49	.flags = IORESOURCE_MEM,
50};
51
52/*
53 * Clock management scheme is a provisional implementation
54 * only intended to retreive the pre-set frequencies for each
55 * of the clocks.
56 * Better handling of post-dividers and fractional part of
57 * feedbeck dividers need to be added.
58 * Need to understand what diagnostics from CRU registers could
59 * be handy, and export that via a sysfs interface.
60 */
61
62/*
63 * The CRU contains two similar PLLs: LCPLL and GENPLL,
64 * both with several output channels divided from the PLL
65 * output
66 */
67
68/*
69 * Get PLL running status and update output frequency
70 */
71static int lcpll_status(struct clk * clk)
72{
73	u32 reg;
74	u64 x;
75	unsigned pdiv, ndiv_int, ndiv_frac;
76
77	if( clk->type != CLK_PLL)
78		return -EINVAL;
79
80	/* read status register */
81	reg = readl( clk->regs_base + 0x10 );
82
83	/* bit 12 is "lock" signal, has to be "1" for proper PLL operation */
84	if( (reg & (1 << 12)) == 0 )
85		{
86		clk->rate = 0;
87		}
88
89	/* Update PLL frequency */
90
91	/* control1 register */
92	reg = readl( clk->regs_base + 0x04);
93
94	/* feedback divider integer and fraction parts */
95	pdiv = ( reg >> 28 ) & 7 ;
96	ndiv_int = ( reg >> 20) & 0xff;
97	ndiv_frac = reg & ((1<<20)-1);
98
99	if( pdiv == 0 )
100		return -EIO;
101
102	x = clk->parent->rate / pdiv ;
103
104	x = x * ( (u64) ndiv_int << 20 | ndiv_frac ) ;
105
106	clk->rate = x >> 20 ;
107
108	return 0;
109}
110
111static const struct clk_ops lcpll_ops = {
112	.status = lcpll_status,
113};
114
115static int lcpll_chan_status(struct clk * clk)
116{
117	void * __iomem base;
118	u32 reg;
119	unsigned enable;
120	unsigned mdiv;
121
122	if( clk->parent == NULL || clk->type != CLK_DIV )
123		return -EINVAL;
124
125	/* Register address is only stored in PLL structure */
126	base = clk->parent->regs_base;
127	BUG_ON( base == NULL );
128
129	/* enable bit is in enableb_ch[] inversed */
130	enable = ((readl( base + 0 ) >> 6) & 7) ^ 7;
131
132	if( 0 == (enable & (1 << clk->chan)))
133		{
134		clk->rate = 0;
135		return -EIO;
136		}
137
138	/* get divider */
139	reg = readl(base + 0x08 );
140
141	mdiv = 0xff & ( reg >> ((0x3^clk->chan) << 3) );
142
143	/* when divisor is 0, it behaves as max+1 */
144	if( mdiv == 0 )
145		mdiv = 1 << 8;
146
147	clk->rate = ( clk->parent->rate / mdiv);
148	return 0;
149}
150
151
152static const struct clk_ops lcpll_chan_ops = {
153	.status = lcpll_chan_status,
154};
155
156/*
157 * LCPLL has 4 output channels
158 */
159static struct clk clk_lcpll = {
160	.ops 	= &lcpll_ops,
161	.name 	= "LCPLL",
162	.type	= CLK_PLL,
163	.chan	=	4,
164};
165
166/*
167 * LCPLL output clocks -
168 * chan 0 - PCIe ref clock, should be 1 GHz,
169 * chan 1 - SDIO clock, e.g. 200 MHz,
170 * chan 2 - DDR clock, typical 166.667 MHz for DDR667,
171 * chan 3 - Unknown
172 */
173
174static struct clk clk_lcpll_ch[4] = {
175	{ .ops	= &lcpll_chan_ops, .parent = &clk_lcpll, .type = CLK_DIV,
176	  .name	= "lcpll_ch0", .chan	= 0, },
177	{ .ops	= &lcpll_chan_ops, .parent = &clk_lcpll, .type = CLK_DIV,
178	  .name	= "lcpll_ch1", .chan	= 1, },
179	{ .ops	= &lcpll_chan_ops, .parent = &clk_lcpll, .type = CLK_DIV,
180	  .name	= "lcpll_ch2", .chan	= 2, },
181	{ .ops	= &lcpll_chan_ops, .parent = &clk_lcpll, .type = CLK_DIV,
182	  .name	= "lcpll_ch3", .chan	= 3, },
183};
184
185/*
186 * Get PLL running status and update output frequency
187 */
188static int genpll_status(struct clk * clk)
189{
190	u32 reg;
191	u64 x;
192	unsigned pdiv, ndiv_int, ndiv_frac;
193
194	if( clk->type != CLK_PLL)
195		return -EINVAL;
196
197	/* Offset of the PLL status register */
198	reg = readl( clk->regs_base + 0x20 );
199
200	/* bit 12 is "lock" signal, has to be "1" for proper PLL operation */
201	if( (reg & (1 << 12)) == 0 )
202		{
203		clk->rate = 0;
204		return -EIO;
205		}
206
207	/* Update PLL frequency */
208
209	/* get PLL feedback divider values from control5 */
210	reg = readl( clk->regs_base + 0x14);
211
212	/* feedback divider integer and fraction parts */
213	ndiv_int = reg >> 20;
214	ndiv_frac = reg & ((1<<20)-1);
215
216	/* get pdiv */
217	reg = readl( clk->regs_base + 0x18);
218	pdiv = (reg >> 24) & 7;
219
220	if( pdiv == 0 )
221		return -EIO;
222
223	x = clk->parent->rate / pdiv ;
224
225	x = x * ( (u64) ndiv_int << 20 | ndiv_frac ) ;
226
227	clk->rate = x >> 20 ;
228
229	return 0;
230}
231
232static const struct clk_ops genpll_ops = {
233	.status = genpll_status,
234};
235
236static int genpll_chan_status(struct clk * clk)
237{
238	void * __iomem base;
239	u32 reg;
240	unsigned enable;
241	unsigned mdiv;
242	unsigned off, shift;
243
244	if( clk->parent == NULL || clk->type != CLK_DIV )
245		return -EINVAL;
246
247	/* Register address is only stored in PLL structure */
248	base = clk->parent->regs_base;
249
250	BUG_ON( base == NULL );
251
252	/* enable bit is in enableb_ch[0..5] inversed */
253	enable = ((readl( base + 0x04 ) >> 12) & 0x3f) ^ 0x3f ;
254
255	if( 0 == (enable & (1 << clk->chan)))
256		{
257		clk->rate = 0;
258		return -EIO;
259		}
260
261	/* GENPLL has the 6 channels spread over two regs */
262	switch( clk->chan )
263		{
264		case 0:
265			off = 0x18; shift = 16;
266			break;
267
268		case 1:
269			off = 0x18; shift = 8;
270			break;
271
272		case 2:
273			off = 0x18; shift = 0;
274			break;
275
276		case 3:
277			off = 0x1c; shift = 16;
278			break;
279
280		case 4:
281			off = 0x1c; shift = 8;
282			break;
283
284		case 5:
285			off = 0x1c; shift = 0;
286			break;
287
288		default:
289			BUG_ON(clk->chan);
290			off = shift = 0;	/* fend off warnings */
291		}
292
293	reg = readl( base + off );
294
295	mdiv = 0xff & ( reg >> shift );
296
297	/* when divisor is 0, it behaves as max+1 */
298	if( mdiv == 0 )
299		mdiv = 1 << 8;
300
301	clk->rate = clk->parent->rate / mdiv;
302	return 0;
303}
304
305
306static const struct clk_ops genpll_chan_ops = {
307	.status = genpll_chan_status,
308};
309
310
311/*
312 * GENPLL has 6 output channels
313 */
314static struct clk clk_genpll = {
315	.ops 	= &genpll_ops,
316	.name 	= "GENPLL",
317	.type	= CLK_PLL,
318	.chan	=	6,
319};
320
321/*
322 * chan 0 - Ethernet switch and MAC, RGMII, need 250 MHz
323 * chan 1 - Ethernet switch slow clock, 150 Mhz
324 * chan 2 - USB PHY clock, need 30 MHz
325 * chan 3 - iProc N MHz clock, set from OTP
326 * chan 4 - iProc N/2 MHz clock, set from OTP
327 * chan 5 - iProc N/4 MHz clock, set from OTP
328 *
329 */
330
331static struct clk clk_genpll_ch[6] = {
332	{ .ops	= &genpll_chan_ops, .parent = &clk_genpll, .type = CLK_DIV,
333	  .name	= "genpll_ch0", .chan	= 0, },
334	{ .ops	= &genpll_chan_ops, .parent = &clk_genpll, .type = CLK_DIV,
335	  .name	= "genpll_ch1", .chan	= 1, },
336	{ .ops	= &genpll_chan_ops, .parent = &clk_genpll, .type = CLK_DIV,
337	  .name	= "genpll_ch2", .chan	= 2, },
338	{ .ops	= &genpll_chan_ops, .parent = &clk_genpll, .type = CLK_DIV,
339	  .name	= "genpll_ch3", .chan	= 3, },
340	{ .ops	= &genpll_chan_ops, .parent = &clk_genpll, .type = CLK_DIV,
341	  .name	= "genpll_ch4", .chan	= 4, },
342	{ .ops	= &genpll_chan_ops, .parent = &clk_genpll, .type = CLK_DIV,
343	  .name	= "genpll_ch5", .chan	= 5, },
344};
345
346/*
347 * This table is used to locate clock sources
348 * from device drivers
349 */
350
351static struct clk_lookup soc_clk_lookups[] = {
352	{
353	.con_id         = "pcie_clk", 		/* a.k.a. "c_clk100" */
354	.clk            = &clk_lcpll_ch[0],
355	},{
356	.con_id         = "sdio_clk", 		/* a.k.a. "c_clk200" */
357	.clk            = &clk_lcpll_ch[1],
358	},{
359	.con_id         = "ddr_clk",		/* a.k.a. "c_clk400" */
360	.clk            = &clk_lcpll_ch[2],
361	},{
362	.con_id         = "c_clk120",		/* unassigned ? */
363	.clk            = &clk_lcpll_ch[3],
364	},{
365	.con_id		= "en_phy_clk",		/* "c_clk250" */
366	.clk            = &clk_genpll_ch[0],
367	},{
368	.con_id		= "en_clk",		/* "c_clk150" */
369	.clk            = &clk_genpll_ch[1],
370	},{
371	.con_id         = "usb_phy_clk", 	/* "c_clk30" */
372	.clk            = &clk_genpll_ch[2],
373	},{
374	.con_id         = "iproc_fast_clk",	/* "c_clk500" */
375	.clk            = &clk_genpll_ch[3],
376	},{
377	.con_id         = "iproc_med_clk",	/* "c_clk250" */
378	.clk            = &clk_genpll_ch[4],
379	},{
380	.con_id         = "iproc_slow_clk",	/* "c_clk125" */
381	.clk            = &clk_genpll_ch[5],
382	}
383};
384void dmu_gpiomux_init(void)
385{
386#if defined(CONFIG_PLAT_MUX_CONSOLE) || defined(CONFIG_PLAT_MUX_CONSOLE_CCB) || \
387	defined(CONFIG_SOUND) || defined(CONFIG_SPI_BCM5301X) || defined(CONFIG_I2C_BCM5301X)
388	void * __iomem reg_addr;
389	u32 reg;
390
391	/* CRU_RESET register */
392	reg_addr = (void *)(SOC_DMU_BASE_VA + 0x1c0);
393#endif /* CONFIG_PLAT_MUX_CONSOLE || CONFIG_PLAT_MUX_CONSOLE_CCB || CONFIG_SOUND || CONFIG_SPI_BCM5301X || CONFIG_I2C_BCM5301X */
394
395#ifdef CONFIG_PLAT_MUX_CONSOLE
396	/* set iproc_reset_n to 0 to use UART1, but it never comes back */
397	reg = readl(reg_addr);
398	reg &= ~((u32)0xf << 12);
399	writel(reg, reg_addr);
400#endif /* CONFIG_PLAT_MUX_CONSOLE */
401#ifdef CONFIG_PLAT_MUX_CONSOLE_CCB
402	/* UART port 2 (ChipCommonB UART0) is multiplexed with GPIO[16:17] */
403	reg = readl(reg_addr);
404	reg &= ~((u32)0x3 << 16);
405	writel(reg, reg_addr);
406#endif /* CONFIG_PLAT_MUX_CONSOLE_CCB */
407#ifdef CONFIG_SOUND
408	/* I2S XTAL out */
409	reg = readl(reg_addr);
410	reg &= ~((u32)0x1 << 18);
411	writel(reg, reg_addr);
412#endif /* CONFIG_SOUND */
413#ifdef CONFIG_SPI_BCM5301X
414	/* SPI out */
415	reg = readl(reg_addr);
416	reg &= ~((u32)0xF << 0);
417	writel(reg, reg_addr);
418#endif /* CONFIG_SPI_BCM5301X */
419#ifdef CONFIG_I2C_BCM5301X
420	/* I2C out */
421	reg = readl(reg_addr);
422	reg &= ~((u32)0x3 << 4);
423	writel(reg, reg_addr);
424#endif /* CONFIG_I2C_BCM5301X */
425}
426
427/*
428 * Install above clocks into clock lookup table
429 * and initialize the register base address for each
430*/
431static void __init soc_clocks_init(void * __iomem cru_regs_base,
432	struct clk * clk_ref)
433{
434	void * __iomem reg;
435	u32 val;
436
437	/* registers are already mapped with the rest of DMU block */
438	/* Update register base address */
439	clk_lcpll.regs_base =	cru_regs_base + 0x00 ;
440	clk_genpll.regs_base =	cru_regs_base + 0x40 ;
441
442	/* Set parent as reference ckock */
443	clk_lcpll.parent	= clk_ref;
444	clk_genpll.parent	= clk_ref;
445
446#ifdef	__DEPRECATED__
447	{
448	int i ;
449	/* We need to clear dev_id fields in the lookups,
450	   because if it is set, it will not match by con_id */
451	for(i = 0; i < ARRAY_SIZE(soc_clk_lookups); i++ )
452		soc_clk_lookups[i].dev_id = NULL;
453	}
454#endif
455
456	/* Install clock sources into the lookup table */
457	clkdev_add_table(soc_clk_lookups, ARRAY_SIZE(soc_clk_lookups));
458
459	/* Correct GMAC 2.66G line rate issue, it should be 2Gbps */
460	/* This incorrect setting only exist in OTP present 4708 chip */
461	/* is a OTPed 4708 chip which Ndiv == 0x50 */
462	reg = clk_genpll.regs_base + 0x14;
463	val = readl(reg);
464	if (((val >> 20) & 0x3ff) == 0x50) {
465		/* CRU_CLKSET_KEY, unlock */
466		reg = clk_genpll.regs_base + 0x40;
467		val = 0x0000ea68;
468		writel(val, reg);
469
470		/* Change CH0_MDIV to 8 */
471		/* After changing the CH0_MDIV to 8, the customer has been reporting that
472		 * there are differences between input throughput vs. output throughput.
473		 * The output throughput is slightly lower (927.537 mbps input rate vs. 927.49
474		 * mbps output rate).  Below is the solution to fix it.
475		 * 1. Change the oscillator on WLAN reference board from 25.000 to 25.001
476		 * 2. Change the CH0_MDIV to 7
477		 */
478		reg = clk_genpll.regs_base + 0x18;
479		val = readl(reg);
480		val &= ~((u32)0xff << 16);
481		val |= ((u32)0x7 << 16);
482		writel(val, reg);
483
484		/* Load Enable CH0 */
485		reg = clk_genpll.regs_base + 0x4;
486		val = readl(reg);
487		val &= ~(u32)0x1;
488		writel(val, reg);
489		val |= (u32)0x1;
490		writel(val, reg);
491		val &= ~(u32)0x1;
492		writel(val, reg);
493
494		/* CRU_CLKSET_KEY, lock */
495		reg = clk_genpll.regs_base + 0x40;
496		val = 0x0;
497		writel(val, reg);
498	}
499}
500
501void __init soc_dmu_init( struct clk *	clk_ref )
502{
503	void * __iomem 	reg_base;
504
505        if( IS_ERR_OR_NULL( clk_ref ))
506                {
507                printk( KERN_ERR "DMU no clock source - skip init\n");
508                return;
509                }
510
511	BUG_ON( request_resource( &iomem_resource, &dmu_regs ));
512
513	/* DMU regs are mapped as part of the fixed mapping with CCA+CCB */
514	reg_base =	(void *) SOC_DMU_BASE_VA;
515
516	BUG_ON( IS_ERR_OR_NULL(reg_base ));
517
518	/* Initialize clocks */
519	soc_clocks_init( reg_base + 0x100, clk_ref );	/* CRU LCPLL control0 */
520
521	dmu_gpiomux_init();
522}
523
524
525
526
527
528
529
530void soc_clocks_show( void )
531{
532	unsigned i;
533
534	printk( "DMU Clocks:\n" );
535	for(i = 0; i < ARRAY_SIZE( soc_clk_lookups); i++ )
536		{
537		printk( "%s, %s: (%s) %lu\n",
538			soc_clk_lookups[i].con_id,
539			soc_clk_lookups[i].dev_id,
540			soc_clk_lookups[i].clk->name,
541			clk_get_rate( soc_clk_lookups[i].clk )
542			);
543		}
544	printk( "DMU Clocks# %u\n", i );
545}
546
547#ifdef CONFIG_PROC_FS
548static int dmu_temperature_status(char * buffer, char **start,
549	off_t offset, int length, int * eof, void * data)
550{
551	int len;
552	off_t pos, begin;
553	void *__iomem pvtmon_base;
554	u32 pvtmon_control0, pvtmon_status;
555	int temperature;
556
557	len = 0;
558	pos = begin = 0;
559
560	pvtmon_base = (void *)(SOC_DMU_BASE_VA + 0x2c0);	/* PVTMON control0 */
561
562	pvtmon_control0 = readl(pvtmon_base);
563	if (pvtmon_control0 & 0xf) {
564		pvtmon_control0 &= ~0xf;
565		writel(pvtmon_control0, pvtmon_base);
566	}
567
568	pvtmon_status = readl(pvtmon_base + 0x8);
569	temperature = 418 - ((5556 * pvtmon_status) / 10000);
570
571	len += sprintf(buffer + len, "CPU temperature\t: %d%cC\n\n",
572		temperature, 0xF8);
573
574	pos = begin + len;
575
576	if (pos < offset) {
577		len = 0;
578		begin = pos;
579	}
580
581	*eof = 1;
582
583	*start = buffer + (offset - begin);
584	len -= (offset - begin);
585
586	if (len > length)
587		len = length;
588
589	return len;
590}
591
592static void __init dmu_proc_init(void)
593{
594	struct proc_dir_entry *dmu, *dmu_temp;
595
596	dmu = proc_mkdir(DMU_PROC_NAME, NULL);
597
598	if (!dmu) {
599		printk(KERN_ERR "DMU create proc directory failed.\n");
600		return;
601	}
602
603	dmu_temp = create_proc_read_entry(DMU_PROC_NAME "/temperature", 0, NULL,
604		dmu_temperature_status, NULL);
605
606	if (!dmu_temp)
607		printk(KERN_ERR "DMU create proc entry failed.\n");
608}
609fs_initcall(dmu_proc_init);
610#endif /* CONFIG_PROC_FS */
611