1/*
2 * PPC440GX system library
3 *
4 * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
5 * Copyright (c) 2003 - 2006 Zultys Technologies
6 *
7 * This program is free software; you can redistribute  it and/or modify it
8 * under  the terms of  the GNU General  Public License as published by the
9 * Free Software Foundation;  either version 2 of the  License, or (at your
10 * option) any later version.
11 *
12 */
13#include <linux/kernel.h>
14#include <linux/interrupt.h>
15#include <asm/ibm44x.h>
16#include <asm/mmu.h>
17#include <asm/processor.h>
18#include <syslib/ibm440gx_common.h>
19
20/*
21 * Calculate 440GX clocks
22 */
23static inline u32 __fix_zero(u32 v, u32 def){
24	return v ? v : def;
25}
26
27void __init ibm440gx_get_clocks(struct ibm44x_clocks* p, unsigned int sys_clk,
28	unsigned int ser_clk)
29{
30	u32 pllc  = CPR_READ(DCRN_CPR_PLLC);
31	u32 plld  = CPR_READ(DCRN_CPR_PLLD);
32	u32 uart0 = SDR_READ(DCRN_SDR_UART0);
33	u32 uart1 = SDR_READ(DCRN_SDR_UART1);
34#ifdef CONFIG_440EP
35	u32 uart2 = SDR_READ(DCRN_SDR_UART2);
36	u32 uart3 = SDR_READ(DCRN_SDR_UART3);
37#endif
38
39	/* Dividers */
40	u32 fbdv   = __fix_zero((plld >> 24) & 0x1f, 32);
41	u32 fwdva  = __fix_zero((plld >> 16) & 0xf, 16);
42	u32 fwdvb  = __fix_zero((plld >> 8) & 7, 8);
43	u32 lfbdv  = __fix_zero(plld & 0x3f, 64);
44	u32 pradv0 = __fix_zero((CPR_READ(DCRN_CPR_PRIMAD) >> 24) & 7, 8);
45	u32 prbdv0 = __fix_zero((CPR_READ(DCRN_CPR_PRIMBD) >> 24) & 7, 8);
46	u32 opbdv0 = __fix_zero((CPR_READ(DCRN_CPR_OPBD) >> 24) & 3, 4);
47	u32 perdv0 = __fix_zero((CPR_READ(DCRN_CPR_PERD) >> 24) & 3, 4);
48
49	/* Input clocks for primary dividers */
50	u32 clk_a, clk_b;
51
52	if (pllc & 0x40000000){
53		u32 m;
54
55		/* Feedback path */
56		switch ((pllc >> 24) & 7){
57		case 0:
58			/* PLLOUTx */
59			m = ((pllc & 0x20000000) ? fwdvb : fwdva) * lfbdv;
60			break;
61		case 1:
62			/* CPU */
63			m = fwdva * pradv0;
64			break;
65		case 5:
66			/* PERClk */
67			m = fwdvb * prbdv0 * opbdv0 * perdv0;
68			break;
69		default:
70			printk(KERN_EMERG "invalid PLL feedback source\n");
71			goto bypass;
72		}
73		m *= fbdv;
74		p->vco = sys_clk * m;
75		clk_a = p->vco / fwdva;
76		clk_b = p->vco / fwdvb;
77	}
78	else {
79bypass:
80		/* Bypass system PLL */
81		p->vco = 0;
82		clk_a = clk_b = sys_clk;
83	}
84
85	p->cpu = clk_a / pradv0;
86	p->plb = clk_b / prbdv0;
87	p->opb = p->plb / opbdv0;
88	p->ebc = p->opb / perdv0;
89
90	/* UARTs clock */
91	if (uart0 & 0x00800000)
92		p->uart0 = ser_clk;
93	else
94		p->uart0 = p->plb / __fix_zero(uart0 & 0xff, 256);
95
96	if (uart1 & 0x00800000)
97		p->uart1 = ser_clk;
98	else
99		p->uart1 = p->plb / __fix_zero(uart1 & 0xff, 256);
100#ifdef CONFIG_440EP
101	if (uart2 & 0x00800000)
102		p->uart2 = ser_clk;
103	else
104		p->uart2 = p->plb / __fix_zero(uart2 & 0xff, 256);
105
106	if (uart3 & 0x00800000)
107		p->uart3 = ser_clk;
108	else
109		p->uart3 = p->plb / __fix_zero(uart3 & 0xff, 256);
110#endif
111}
112
113/* Issue L2C diagnostic command */
114static inline u32 l2c_diag(u32 addr)
115{
116	mtdcr(DCRN_L2C0_ADDR, addr);
117	mtdcr(DCRN_L2C0_CMD, L2C_CMD_DIAG);
118	while (!(mfdcr(DCRN_L2C0_SR) & L2C_SR_CC)) ;
119	return mfdcr(DCRN_L2C0_DATA);
120}
121
122static irqreturn_t l2c_error_handler(int irq, void* dev)
123{
124	u32 sr = mfdcr(DCRN_L2C0_SR);
125	if (sr & L2C_SR_CPE){
126		/* Read cache trapped address */
127		u32 addr = l2c_diag(0x42000000);
128		printk(KERN_EMERG "L2C: Cache Parity Error, addr[16:26] = 0x%08x\n", addr);
129	}
130	if (sr & L2C_SR_TPE){
131		/* Read tag trapped address */
132		u32 addr = l2c_diag(0x82000000) >> 16;
133		printk(KERN_EMERG "L2C: Tag Parity Error, addr[16:26] = 0x%08x\n", addr);
134	}
135
136	/* Clear parity errors */
137	if (sr & (L2C_SR_CPE | L2C_SR_TPE)){
138		mtdcr(DCRN_L2C0_ADDR, 0);
139		mtdcr(DCRN_L2C0_CMD, L2C_CMD_CCP | L2C_CMD_CTE);
140	} else
141		printk(KERN_EMERG "L2C: LRU error\n");
142
143	return IRQ_HANDLED;
144}
145
146/* Enable L2 cache */
147void __init ibm440gx_l2c_enable(void){
148	u32 r;
149	unsigned long flags;
150
151	/* Install error handler */
152	if (request_irq(87, l2c_error_handler, IRQF_DISABLED, "L2C", 0) < 0){
153		printk(KERN_ERR "Cannot install L2C error handler, cache is not enabled\n");
154		return;
155	}
156
157	local_irq_save(flags);
158	asm volatile ("sync" ::: "memory");
159
160	/* Disable SRAM */
161	mtdcr(DCRN_SRAM0_DPC,   mfdcr(DCRN_SRAM0_DPC)   & ~SRAM_DPC_ENABLE);
162	mtdcr(DCRN_SRAM0_SB0CR, mfdcr(DCRN_SRAM0_SB0CR) & ~SRAM_SBCR_BU_MASK);
163	mtdcr(DCRN_SRAM0_SB1CR, mfdcr(DCRN_SRAM0_SB1CR) & ~SRAM_SBCR_BU_MASK);
164	mtdcr(DCRN_SRAM0_SB2CR, mfdcr(DCRN_SRAM0_SB2CR) & ~SRAM_SBCR_BU_MASK);
165	mtdcr(DCRN_SRAM0_SB3CR, mfdcr(DCRN_SRAM0_SB3CR) & ~SRAM_SBCR_BU_MASK);
166
167	/* Enable L2_MODE without ICU/DCU */
168	r = mfdcr(DCRN_L2C0_CFG) & ~(L2C_CFG_ICU | L2C_CFG_DCU | L2C_CFG_SS_MASK);
169	r |= L2C_CFG_L2M | L2C_CFG_SS_256;
170	mtdcr(DCRN_L2C0_CFG, r);
171
172	mtdcr(DCRN_L2C0_ADDR, 0);
173
174	/* Hardware Clear Command */
175	mtdcr(DCRN_L2C0_CMD, L2C_CMD_HCC);
176	while (!(mfdcr(DCRN_L2C0_SR) & L2C_SR_CC)) ;
177
178	/* Clear Cache Parity and Tag Errors */
179	mtdcr(DCRN_L2C0_CMD, L2C_CMD_CCP | L2C_CMD_CTE);
180
181	/* Enable 64G snoop region starting at 0 */
182	r = mfdcr(DCRN_L2C0_SNP0) & ~(L2C_SNP_BA_MASK | L2C_SNP_SSR_MASK);
183	r |= L2C_SNP_SSR_32G | L2C_SNP_ESR;
184	mtdcr(DCRN_L2C0_SNP0, r);
185
186	r = mfdcr(DCRN_L2C0_SNP1) & ~(L2C_SNP_BA_MASK | L2C_SNP_SSR_MASK);
187	r |= 0x80000000 | L2C_SNP_SSR_32G | L2C_SNP_ESR;
188	mtdcr(DCRN_L2C0_SNP1, r);
189
190	asm volatile ("sync" ::: "memory");
191
192	/* Enable ICU/DCU ports */
193	r = mfdcr(DCRN_L2C0_CFG);
194	r &= ~(L2C_CFG_DCW_MASK | L2C_CFG_PMUX_MASK | L2C_CFG_PMIM | L2C_CFG_TPEI
195		| L2C_CFG_CPEI | L2C_CFG_NAM | L2C_CFG_NBRM);
196	r |= L2C_CFG_ICU | L2C_CFG_DCU | L2C_CFG_TPC | L2C_CFG_CPC | L2C_CFG_FRAN
197		| L2C_CFG_CPIM | L2C_CFG_TPIM | L2C_CFG_LIM | L2C_CFG_SMCM;
198	mtdcr(DCRN_L2C0_CFG, r);
199
200	asm volatile ("sync; isync" ::: "memory");
201	local_irq_restore(flags);
202}
203
204/* Disable L2 cache */
205void __init ibm440gx_l2c_disable(void){
206	u32 r;
207	unsigned long flags;
208
209	local_irq_save(flags);
210	asm volatile ("sync" ::: "memory");
211
212	/* Disable L2C mode */
213	r = mfdcr(DCRN_L2C0_CFG) & ~(L2C_CFG_L2M | L2C_CFG_ICU | L2C_CFG_DCU);
214	mtdcr(DCRN_L2C0_CFG, r);
215
216	/* Enable SRAM */
217	mtdcr(DCRN_SRAM0_DPC, mfdcr(DCRN_SRAM0_DPC) | SRAM_DPC_ENABLE);
218	mtdcr(DCRN_SRAM0_SB0CR,
219	      SRAM_SBCR_BAS0 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW);
220	mtdcr(DCRN_SRAM0_SB1CR,
221	      SRAM_SBCR_BAS1 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW);
222	mtdcr(DCRN_SRAM0_SB2CR,
223	      SRAM_SBCR_BAS2 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW);
224	mtdcr(DCRN_SRAM0_SB3CR,
225	      SRAM_SBCR_BAS3 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW);
226
227	asm volatile ("sync; isync" ::: "memory");
228	local_irq_restore(flags);
229}
230
231void __init ibm440gx_l2c_setup(struct ibm44x_clocks* p)
232{
233	/* Disable L2C on rev.A, rev.B and 800MHz version of rev.C,
234	   enable it on all other revisions
235	 */
236	if (strcmp(cur_cpu_spec->cpu_name, "440GX Rev. A") == 0 ||
237			strcmp(cur_cpu_spec->cpu_name, "440GX Rev. B") == 0
238			|| (strcmp(cur_cpu_spec->cpu_name, "440GX Rev. C")
239				== 0 && p->cpu > 667000000))
240		ibm440gx_l2c_disable();
241	else
242		ibm440gx_l2c_enable();
243}
244
245int __init ibm440gx_get_eth_grp(void)
246{
247	return (SDR_READ(DCRN_SDR_PFC1) & DCRN_SDR_PFC1_EPS) >> DCRN_SDR_PFC1_EPS_SHIFT;
248}
249
250void __init ibm440gx_set_eth_grp(int group)
251{
252	SDR_WRITE(DCRN_SDR_PFC1, (SDR_READ(DCRN_SDR_PFC1) & ~DCRN_SDR_PFC1_EPS) | (group << DCRN_SDR_PFC1_EPS_SHIFT));
253}
254
255void __init ibm440gx_tah_enable(void)
256{
257	/* Enable TAH0 and TAH1 */
258	SDR_WRITE(DCRN_SDR_MFR,SDR_READ(DCRN_SDR_MFR) &
259			~DCRN_SDR_MFR_TAH0);
260	SDR_WRITE(DCRN_SDR_MFR,SDR_READ(DCRN_SDR_MFR) &
261			~DCRN_SDR_MFR_TAH1);
262}
263
264int ibm440gx_show_cpuinfo(struct seq_file *m){
265
266	u32 l2c_cfg = mfdcr(DCRN_L2C0_CFG);
267	const char* s;
268	if (l2c_cfg & L2C_CFG_L2M){
269	    switch (l2c_cfg & (L2C_CFG_ICU | L2C_CFG_DCU)){
270		case L2C_CFG_ICU: s = "I-Cache only";    break;
271		case L2C_CFG_DCU: s = "D-Cache only";    break;
272		default:	  s = "I-Cache/D-Cache"; break;
273	    }
274	}
275	else
276	    s = "disabled";
277
278	seq_printf(m, "L2-Cache\t: %s (0x%08x 0x%08x)\n", s,
279	    l2c_cfg, mfdcr(DCRN_L2C0_SR));
280
281	return 0;
282}
283
284void __init ibm440gx_platform_init(unsigned long r3, unsigned long r4,
285				   unsigned long r5, unsigned long r6,
286				   unsigned long r7)
287{
288	if (!strcmp(cur_cpu_spec->cpu_name, "440GX Rev. C") ||
289	    !strcmp(cur_cpu_spec->cpu_name, "440GX Rev. F"))
290		mtspr(SPRN_CCR1, mfspr(SPRN_CCR1) | CCR1_DPC);
291
292	ibm44x_platform_init(r3, r4, r5, r6, r7);
293}
294