blob: f447f5f0444d223b4fef066e2be27e1fbf7fda20 [file] [log] [blame]
Tomasz Malesinskicd630c92007-03-24 19:26:13 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: $
9 *
10 * Copyright (C) 2007 by Tomasz Malesinski
11 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
Tomasz Malesinskicd630c92007-03-24 19:26:13 +000016 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include <stdlib.h>
23#include "pnx0101.h"
24#include "system.h"
25
26static struct
27{
28 unsigned char freq;
29 unsigned char sys_mult;
30 unsigned char sys_div;
Tomasz Malesinskicd630c92007-03-24 19:26:13 +000031}
32perf_modes[3] ICONST_ATTR =
33{
Tomasz Malesinski026032f2007-09-20 22:13:48 +000034 {12, 4, 4},
35 {48, 4, 1},
36 {60, 5, 1}
Tomasz Malesinskicd630c92007-03-24 19:26:13 +000037};
38
39static int performance_mode, bus_divider;
40
41static void cgu_set_sel_stage_input(int clock, int input)
42{
43 int s = CGU.base_ssr[clock];
44 if (s & 1)
45 CGU.base_fs2[clock] = input;
46 else
47 CGU.base_fs1[clock] = input;
48 CGU.base_scr[clock] = (s & 3) ^ 3;
49}
50
51static void cgu_reset_sel_stage_clocks(int first_esr, int n_esr,
52 int first_div, int n_div)
53{
54 int i;
55 for (i = 0; i < n_esr; i++)
56 CGU.clk_esr[first_esr + i] = 0;
57 for (i = 0; i < n_div; i++)
58 CGU.base_fdc[first_div + i] = 0;
59}
60
61static void cgu_configure_div(int div, int n, int m)
62{
63 int msub, madd, div_size, max_n;
64 unsigned long cfg;
65
66 if (n == m)
67 {
68 CGU.base_fdc[div] = CGU.base_fdc[div] & ~1;
69 return;
70 }
71
72 msub = -n;
73 madd = m - n;
74 div_size = (div == PNX0101_HIPREC_FDC) ? 10 : 8;
75 max_n = 1 << div_size;
76 while ((madd << 1) < max_n && (msub << 1) >= -max_n)
77 {
78 madd <<= 1;
79 msub <<= 1;
80 }
81 cfg = (((msub << div_size) | madd) << 3) | 4;
82 CGU.base_fdc[div] = CGU.base_fdc[div] & ~1;
83 CGU.base_fdc[div] = cfg | 2;
84 CGU.base_fdc[div] = cfg;
85 CGU.base_fdc[div] = cfg | 1;
86}
87
88static void cgu_connect_div_to_clock(int rel_div, int esr)
89{
90 CGU.clk_esr[esr] = (rel_div << 1) | 1;
91}
92
Tomasz Malesinskicd630c92007-03-24 19:26:13 +000093static void cgu_enable_clock(int clock)
94{
95 CGU.clk_pcr[clock] |= 1;
96}
97
98static void cgu_start_sel_stage_dividers(int bcr)
99{
100 CGU.base_bcr[bcr] = 1;
101}
102
103/* Convert a pointer that points to IRAM (0x4xxxx) to a pointer that
104 points to the uncached page (0x0xxxx) that is also mapped to IRAM. */
Tomasz Malesinski026032f2007-09-20 22:13:48 +0000105static inline void *noncached(void *p)
106{
Tomasz Malesinskicd630c92007-03-24 19:26:13 +0000107 return (void *)(((unsigned long)p) & 0xffff);
108}
109
Tomasz Malesinski026032f2007-09-20 22:13:48 +0000110/* To avoid SRAM accesses while changing memory controller settings we
111 run this routine from uncached copy of IRAM. All times are in CPU
112 cycles. At CPU frequencies lower than 60 MHz we could use faster
113 settings, but since DMA may access SRAM at any time, changing
114 memory timings together with CPU frequency would be tricky. */
115static void do_set_mem_timings(void) ICODE_ATTR;
116static void do_set_mem_timings(void)
Tomasz Malesinskicd630c92007-03-24 19:26:13 +0000117{
Michael Sevakisaf395f42008-03-26 01:50:41 +0000118 int old_irq = disable_irq_save();
Tomasz Malesinskicd630c92007-03-24 19:26:13 +0000119 while ((EMC.status & 3) != 0);
120 EMC.control = 5;
Tomasz Malesinski026032f2007-09-20 22:13:48 +0000121 EMCSTATIC0.waitrd = 6;
122 EMCSTATIC0.waitwr = 5;
123 EMCSTATIC1.waitrd = 5;
124 EMCSTATIC1.waitwr = 4; /* OF uses 5 here */
125 EMCSTATIC2.waitrd = 4;
126 EMCSTATIC2.waitwr = 3;
127 EMCSTATIC0.waitoen = 1;
128 EMCSTATIC1.waitoen = 1;
129 EMCSTATIC2.waitoen = 1;
130 /* Enable write buffers for SRAM. */
Tomasz Malesinskicd630c92007-03-24 19:26:13 +0000131#ifndef DEBUG
Tomasz Malesinski026032f2007-09-20 22:13:48 +0000132 EMCSTATIC1.config = 0x80081;
Tomasz Malesinskicd630c92007-03-24 19:26:13 +0000133#endif
134 EMC.control = 1;
Michael Sevakisaf395f42008-03-26 01:50:41 +0000135 restore_irq(old_irq);
Tomasz Malesinskicd630c92007-03-24 19:26:13 +0000136}
137
Tomasz Malesinski026032f2007-09-20 22:13:48 +0000138static void emc_set_mem_timings(void)
Tomasz Malesinskicd630c92007-03-24 19:26:13 +0000139{
Tomasz Malesinski026032f2007-09-20 22:13:48 +0000140 void (*f)(void) = noncached(do_set_mem_timings);
141 (*f)();
Tomasz Malesinskicd630c92007-03-24 19:26:13 +0000142}
143
144static void cgu_set_sys_mult(int i)
145{
146 cgu_set_sel_stage_input(PNX0101_SEL_STAGE_SYS, PNX0101_MAIN_CLOCK_FAST);
147 cgu_set_sel_stage_input(PNX0101_SEL_STAGE_APB3, PNX0101_MAIN_CLOCK_FAST);
148
149 PLL.lppdn = 1;
150 PLL.lpfin = 1;
151 PLL.lpmbyp = 0;
152 PLL.lpdbyp = 0;
153 PLL.lppsel = 1;
154 PLL.lpmsel = i - 1;
155 PLL.lppdn = 0;
156 while (!PLL.lplock);
157
158 cgu_configure_div(PNX0101_FIRST_DIV_SYS + 1, 1, (i == 5) ? 15 : 12);
159 cgu_connect_div_to_clock(1, 0x11);
160 cgu_enable_clock(0x11);
161 cgu_start_sel_stage_dividers(PNX0101_BCR_SYS);
162
163 cgu_set_sel_stage_input(PNX0101_SEL_STAGE_SYS,
164 PNX0101_MAIN_CLOCK_MAIN_PLL);
165 cgu_set_sel_stage_input(PNX0101_SEL_STAGE_APB3,
166 PNX0101_MAIN_CLOCK_MAIN_PLL);
167}
168
169static void pnx0101_set_performance_mode(int mode)
170{
171 int old = performance_mode;
Tomasz Malesinski026032f2007-09-20 22:13:48 +0000172 if (perf_modes[old].sys_mult != perf_modes[mode].sys_mult)
173 cgu_set_sys_mult(perf_modes[mode].sys_mult);
174 if (perf_modes[old].sys_div != perf_modes[mode].sys_div)
175 cgu_configure_div(bus_divider, 1, perf_modes[mode].sys_div);
Tomasz Malesinskicd630c92007-03-24 19:26:13 +0000176 performance_mode = mode;
177}
178
179static void pnx0101_init_clocks(void)
180{
181 bus_divider = PNX0101_FIRST_DIV_SYS + (CGU.clk_esr[0] >> 1);
182 performance_mode = 0;
Tomasz Malesinski026032f2007-09-20 22:13:48 +0000183 emc_set_mem_timings();
Tomasz Malesinskicd630c92007-03-24 19:26:13 +0000184 pnx0101_set_performance_mode(2);
Tomasz Malesinski026032f2007-09-20 22:13:48 +0000185
Tomasz Malesinskicd630c92007-03-24 19:26:13 +0000186 cgu_set_sel_stage_input(PNX0101_SEL_STAGE_APB1,
187 PNX0101_MAIN_CLOCK_FAST);
188 cgu_reset_sel_stage_clocks(PNX0101_FIRST_ESR_APB1, PNX0101_N_ESR_APB1,
189 PNX0101_FIRST_DIV_APB1, PNX0101_N_DIV_APB1);
190 cgu_configure_div(PNX0101_FIRST_DIV_APB1, 1, 4);
191 cgu_connect_div_to_clock(0, PNX0101_ESR_APB1);
192 cgu_connect_div_to_clock(0, PNX0101_ESR_T0);
193 cgu_connect_div_to_clock(0, PNX0101_ESR_T1);
194 cgu_connect_div_to_clock(0, PNX0101_ESR_I2C);
195 cgu_enable_clock(PNX0101_CLOCK_APB1);
196 cgu_enable_clock(PNX0101_CLOCK_T0);
197 cgu_enable_clock(PNX0101_CLOCK_T1);
198 cgu_enable_clock(PNX0101_CLOCK_I2C);
199}
200
201#ifdef HAVE_ADJUSTABLE_CPU_FREQ
202void set_cpu_frequency(long frequency)
203{
204 switch (frequency)
205 {
206 case CPUFREQ_MAX:
207 pnx0101_set_performance_mode(2);
208 cpu_frequency = CPUFREQ_MAX;
209 break;
210 case CPUFREQ_NORMAL:
211 pnx0101_set_performance_mode(1);
212 cpu_frequency = CPUFREQ_NORMAL;
213 break;
214 case CPUFREQ_DEFAULT:
215 default:
216 pnx0101_set_performance_mode(0);
217 cpu_frequency = CPUFREQ_DEFAULT;
218 break;
219 }
220
221}
222#endif
223
224interrupt_handler_t interrupt_vector[0x1d] __attribute__ ((section(".idata")));
225
226#define IRQ_READ(reg, dest) \
227 do { unsigned long v2; \
228 do { \
229 dest = (reg); \
230 v2 = (reg); \
231 } while ((dest != v2)); \
232 } while (0);
233
234#define IRQ_WRITE_WAIT(reg, val, cond) \
235 do { unsigned long v, v2; \
236 do { \
237 (reg) = (val); \
238 v = (reg); \
239 v2 = (reg); \
240 } while ((v != v2) || !(cond)); \
241 } while (0);
242
243static void undefined_int(void)
244{
245}
246
247void irq(void)
248{
Tomasz Malesinski026032f2007-09-20 22:13:48 +0000249 unsigned long n;
Tomasz Malesinskicd630c92007-03-24 19:26:13 +0000250 IRQ_READ(INTVECTOR[0], n)
251 (*(interrupt_vector[n >> 3]))();
252}
253
254void fiq(void)
255{
256}
257
258void irq_enable_int(int n)
259{
260 IRQ_WRITE_WAIT(INTREQ[n], INTREQ_WEENABLE | INTREQ_ENABLE, v & 0x10000);
261}
262
263void irq_disable_int(int n)
264{
265 IRQ_WRITE_WAIT(INTREQ[n], INTREQ_WEENABLE, (v & 0x10000) == 0);
266}
267
268void irq_set_int_handler(int n, interrupt_handler_t handler)
269{
270 interrupt_vector[n] = handler;
271}
272
273void system_init(void)
274{
275 int i;
276
277 /* turn off watchdog */
278 (*(volatile unsigned long *)0x80002804) = 0;
279
280 /*
281 IRQ_WRITE_WAIT(INTVECTOR[0], 0, v == 0);
282 IRQ_WRITE_WAIT(INTVECTOR[1], 0, v == 0);
283 IRQ_WRITE_WAIT(INTPRIOMASK[0], 0, v == 0);
284 IRQ_WRITE_WAIT(INTPRIOMASK[1], 0, v == 0);
285 */
286
Tomasz Malesinski026032f2007-09-20 22:13:48 +0000287 for (i = 1; i <= 0x1c; i++)
Tomasz Malesinskicd630c92007-03-24 19:26:13 +0000288 {
289 IRQ_WRITE_WAIT(INTREQ[i],
290 INTREQ_WEPRIO | INTREQ_WETARGET |
291 INTREQ_WEENABLE | INTREQ_WEACTVLO | 1,
292 (v & 0x3010f) == 1);
293 IRQ_WRITE_WAIT(INTREQ[i], INTREQ_WEENABLE, (v & 0x10000) == 0);
294 IRQ_WRITE_WAIT(INTREQ[i], INTREQ_WEPRIO | 1, (v & 0xf) == 1);
Tomasz Malesinski026032f2007-09-20 22:13:48 +0000295 interrupt_vector[i] = undefined_int;
Tomasz Malesinskicd630c92007-03-24 19:26:13 +0000296 }
297 interrupt_vector[0] = undefined_int;
298 pnx0101_init_clocks();
299}
300
301
302void system_reboot(void)
303{
304 (*(volatile unsigned long *)0x80002804) = 1;
305 while (1);
306}
307
308int system_memory_guard(int newmode)
309{
310 (void)newmode;
311 return 0;
312}