summaryrefslogtreecommitdiff
path: root/firmware/target/arm/pnx0101/system-pnx0101.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/pnx0101/system-pnx0101.c')
-rw-r--r--firmware/target/arm/pnx0101/system-pnx0101.c351
1 files changed, 351 insertions, 0 deletions
diff --git a/firmware/target/arm/pnx0101/system-pnx0101.c b/firmware/target/arm/pnx0101/system-pnx0101.c
new file mode 100644
index 0000000000..9d8bb405a8
--- /dev/null
+++ b/firmware/target/arm/pnx0101/system-pnx0101.c
@@ -0,0 +1,351 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: $
9 *
10 * Copyright (C) 2007 by Tomasz Malesinski
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#include <stdlib.h>
21#include "pnx0101.h"
22#include "system.h"
23
24static struct
25{
26 unsigned char freq;
27 unsigned char sys_mult;
28 unsigned char sys_div;
29 unsigned char mem_conf[9];
30}
31perf_modes[3] ICONST_ATTR =
32{
33 {12, 4, 4, {2, 1, 1, 1, 1, 1, 1, 1, 1, 0}},
34 {48, 4, 1, {5, 4, 1, 4, 4, 1, 3, 3, 1, 0}},
35 {60, 5, 1, {6, 5, 1, 5, 5, 1, 4, 3, 1, 1}}
36};
37
38static int performance_mode, bus_divider;
39
40static void cgu_set_sel_stage_input(int clock, int input)
41{
42 int s = CGU.base_ssr[clock];
43 if (s & 1)
44 CGU.base_fs2[clock] = input;
45 else
46 CGU.base_fs1[clock] = input;
47 CGU.base_scr[clock] = (s & 3) ^ 3;
48}
49
50static void cgu_reset_sel_stage_clocks(int first_esr, int n_esr,
51 int first_div, int n_div)
52{
53 int i;
54 for (i = 0; i < n_esr; i++)
55 CGU.clk_esr[first_esr + i] = 0;
56 for (i = 0; i < n_div; i++)
57 CGU.base_fdc[first_div + i] = 0;
58}
59
60static void cgu_configure_div(int div, int n, int m)
61{
62 int msub, madd, div_size, max_n;
63 unsigned long cfg;
64
65 if (n == m)
66 {
67 CGU.base_fdc[div] = CGU.base_fdc[div] & ~1;
68 return;
69 }
70
71 msub = -n;
72 madd = m - n;
73 div_size = (div == PNX0101_HIPREC_FDC) ? 10 : 8;
74 max_n = 1 << div_size;
75 while ((madd << 1) < max_n && (msub << 1) >= -max_n)
76 {
77 madd <<= 1;
78 msub <<= 1;
79 }
80 cfg = (((msub << div_size) | madd) << 3) | 4;
81 CGU.base_fdc[div] = CGU.base_fdc[div] & ~1;
82 CGU.base_fdc[div] = cfg | 2;
83 CGU.base_fdc[div] = cfg;
84 CGU.base_fdc[div] = cfg | 1;
85}
86
87static void cgu_connect_div_to_clock(int rel_div, int esr)
88{
89 CGU.clk_esr[esr] = (rel_div << 1) | 1;
90}
91
92static void cgu_disconnect_div_from_clock(int esr)
93{
94 CGU.clk_esr[esr] = 0;
95}
96
97static void cgu_enable_clock(int clock)
98{
99 CGU.clk_pcr[clock] |= 1;
100}
101
102static void cgu_start_sel_stage_dividers(int bcr)
103{
104 CGU.base_bcr[bcr] = 1;
105}
106
107/* Convert a pointer that points to IRAM (0x4xxxx) to a pointer that
108 points to the uncached page (0x0xxxx) that is also mapped to IRAM. */
109static inline void *noncached(void *p) {
110 return (void *)(((unsigned long)p) & 0xffff);
111}
112
113/* TODO: if it works, then comment it */
114static void do_set_mem_timings(int mode) ICODE_ATTR;
115static void do_set_mem_timings(int mode)
116{
117 unsigned char *mem_conf = noncached(perf_modes[mode].mem_conf);
118 int old_irq = set_irq_level(HIGHEST_IRQ_LEVEL);
119 while ((EMC.status & 3) != 0);
120 EMC.control = 5;
121 EMCSTATIC0.waitrd = mem_conf[0];
122 EMCSTATIC0.waitwr = mem_conf[1];
123 EMCSTATIC1.waitrd = mem_conf[3];
124 EMCSTATIC1.waitwr = mem_conf[4];
125 EMCSTATIC2.waitrd = mem_conf[6];
126 EMCSTATIC2.waitwr = mem_conf[7];
127 EMCSTATIC0.waitoen = mem_conf[2];
128 EMCSTATIC1.waitoen = mem_conf[5];
129 EMCSTATIC2.waitoen = mem_conf[8];
130#ifndef DEBUG
131 EMCSTATIC1.config = mem_conf[9] ? 0x80081 : 0x81;
132#endif
133 EMC.control = 1;
134 set_irq_level(old_irq);
135}
136
137static void emc_set_mem_timings(int mode) {
138 void (*f)(int) = noncached(do_set_mem_timings);
139 (*f)(mode);
140}
141
142/*
143static void do_enable_write_buffers(int on) ICODE_ATTR;
144static void do_enable_write_buffers(int on) {
145 int old_irq = set_irq_level(HIGHEST_IRQ_LEVEL);
146 while ((EMC.status & 3) != 0);
147 EMC.control = 5;
148 EMCSTATIC1.config = on ? 0x80081 : 0x81;
149 EMC.control = 1;
150 set_irq_level(old_irq);
151}
152
153void emc_enable_write_buffers(int on) {
154 void (*f)(int) = noncached(do_enable_write_buffers);
155 (*f)(on);
156}
157*/
158
159/* can it be replaced? */
160static void cgu_busy_wait(int n)
161{
162 while (n > 0)
163 {
164 n--;
165 }
166}
167
168static void cgu_set_sys_mult(int i)
169{
170 cgu_set_sel_stage_input(PNX0101_SEL_STAGE_SYS, PNX0101_MAIN_CLOCK_FAST);
171 cgu_set_sel_stage_input(PNX0101_SEL_STAGE_APB3, PNX0101_MAIN_CLOCK_FAST);
172
173 PLL.lppdn = 1;
174 PLL.lpfin = 1;
175 PLL.lpmbyp = 0;
176 PLL.lpdbyp = 0;
177 PLL.lppsel = 1;
178 PLL.lpmsel = i - 1;
179 PLL.lppdn = 0;
180 while (!PLL.lplock);
181
182 cgu_configure_div(PNX0101_FIRST_DIV_SYS + 1, 1, (i == 5) ? 15 : 12);
183 cgu_connect_div_to_clock(1, 0x11);
184 cgu_enable_clock(0x11);
185 cgu_start_sel_stage_dividers(PNX0101_BCR_SYS);
186
187 cgu_set_sel_stage_input(PNX0101_SEL_STAGE_SYS,
188 PNX0101_MAIN_CLOCK_MAIN_PLL);
189 cgu_set_sel_stage_input(PNX0101_SEL_STAGE_APB3,
190 PNX0101_MAIN_CLOCK_MAIN_PLL);
191}
192
193static void pnx0101_set_performance_mode(int mode)
194{
195 int old = performance_mode;
196 if (perf_modes[old].freq < perf_modes[mode].freq)
197 {
198 emc_set_mem_timings(mode);
199 if (perf_modes[old].sys_mult != perf_modes[mode].sys_mult)
200 cgu_set_sys_mult(perf_modes[mode].sys_mult);
201 if (perf_modes[old].sys_div != perf_modes[mode].sys_div)
202 cgu_configure_div(bus_divider, 1, perf_modes[mode].sys_div);
203 }
204 else if (perf_modes[old].freq > perf_modes[mode].freq)
205 {
206 if (perf_modes[old].sys_mult != perf_modes[mode].sys_mult)
207 cgu_set_sys_mult(perf_modes[mode].sys_mult);
208 if (perf_modes[old].sys_div != perf_modes[mode].sys_div)
209 cgu_configure_div(bus_divider, 1, perf_modes[mode].sys_div);
210 emc_set_mem_timings(mode);
211 }
212 performance_mode = mode;
213}
214
215static void pnx0101_init_clocks(void)
216{
217 bus_divider = PNX0101_FIRST_DIV_SYS + (CGU.clk_esr[0] >> 1);
218 performance_mode = 0;
219 pnx0101_set_performance_mode(2);
220/*
221#ifndef DEBUG
222 emc_enable_write_buffers(1);
223#endif
224*/
225 cgu_set_sel_stage_input(PNX0101_SEL_STAGE_APB1,
226 PNX0101_MAIN_CLOCK_FAST);
227 cgu_reset_sel_stage_clocks(PNX0101_FIRST_ESR_APB1, PNX0101_N_ESR_APB1,
228 PNX0101_FIRST_DIV_APB1, PNX0101_N_DIV_APB1);
229 cgu_configure_div(PNX0101_FIRST_DIV_APB1, 1, 4);
230 cgu_connect_div_to_clock(0, PNX0101_ESR_APB1);
231 cgu_connect_div_to_clock(0, PNX0101_ESR_T0);
232 cgu_connect_div_to_clock(0, PNX0101_ESR_T1);
233 cgu_connect_div_to_clock(0, PNX0101_ESR_I2C);
234 cgu_enable_clock(PNX0101_CLOCK_APB1);
235 cgu_enable_clock(PNX0101_CLOCK_T0);
236 cgu_enable_clock(PNX0101_CLOCK_T1);
237 cgu_enable_clock(PNX0101_CLOCK_I2C);
238}
239
240#ifdef HAVE_ADJUSTABLE_CPU_FREQ
241void set_cpu_frequency(long frequency)
242{
243 switch (frequency)
244 {
245 case CPUFREQ_MAX:
246 pnx0101_set_performance_mode(2);
247 cpu_frequency = CPUFREQ_MAX;
248 break;
249 case CPUFREQ_NORMAL:
250 pnx0101_set_performance_mode(1);
251 cpu_frequency = CPUFREQ_NORMAL;
252 break;
253 case CPUFREQ_DEFAULT:
254 default:
255 pnx0101_set_performance_mode(0);
256 cpu_frequency = CPUFREQ_DEFAULT;
257 break;
258 }
259
260}
261#endif
262
263interrupt_handler_t interrupt_vector[0x1d] __attribute__ ((section(".idata")));
264
265#define IRQ_READ(reg, dest) \
266 do { unsigned long v2; \
267 do { \
268 dest = (reg); \
269 v2 = (reg); \
270 } while ((dest != v2)); \
271 } while (0);
272
273#define IRQ_WRITE_WAIT(reg, val, cond) \
274 do { unsigned long v, v2; \
275 do { \
276 (reg) = (val); \
277 v = (reg); \
278 v2 = (reg); \
279 } while ((v != v2) || !(cond)); \
280 } while (0);
281
282static void undefined_int(void)
283{
284}
285
286void irq(void)
287{
288 int n;
289 IRQ_READ(INTVECTOR[0], n)
290 (*(interrupt_vector[n >> 3]))();
291}
292
293void fiq(void)
294{
295}
296
297void irq_enable_int(int n)
298{
299 IRQ_WRITE_WAIT(INTREQ[n], INTREQ_WEENABLE | INTREQ_ENABLE, v & 0x10000);
300}
301
302void irq_disable_int(int n)
303{
304 IRQ_WRITE_WAIT(INTREQ[n], INTREQ_WEENABLE, (v & 0x10000) == 0);
305}
306
307void irq_set_int_handler(int n, interrupt_handler_t handler)
308{
309 interrupt_vector[n] = handler;
310}
311
312void system_init(void)
313{
314 int i;
315
316 /* turn off watchdog */
317 (*(volatile unsigned long *)0x80002804) = 0;
318
319 /*
320 IRQ_WRITE_WAIT(INTVECTOR[0], 0, v == 0);
321 IRQ_WRITE_WAIT(INTVECTOR[1], 0, v == 0);
322 IRQ_WRITE_WAIT(INTPRIOMASK[0], 0, v == 0);
323 IRQ_WRITE_WAIT(INTPRIOMASK[1], 0, v == 0);
324 */
325
326 for (i = 0; i < 0x1c; i++)
327 {
328 IRQ_WRITE_WAIT(INTREQ[i],
329 INTREQ_WEPRIO | INTREQ_WETARGET |
330 INTREQ_WEENABLE | INTREQ_WEACTVLO | 1,
331 (v & 0x3010f) == 1);
332 IRQ_WRITE_WAIT(INTREQ[i], INTREQ_WEENABLE, (v & 0x10000) == 0);
333 IRQ_WRITE_WAIT(INTREQ[i], INTREQ_WEPRIO | 1, (v & 0xf) == 1);
334 interrupt_vector[i + 1] = undefined_int;
335 }
336 interrupt_vector[0] = undefined_int;
337 pnx0101_init_clocks();
338}
339
340
341void system_reboot(void)
342{
343 (*(volatile unsigned long *)0x80002804) = 1;
344 while (1);
345}
346
347int system_memory_guard(int newmode)
348{
349 (void)newmode;
350 return 0;
351}