1 | /* |
---|
2 | * cma101.c -- lo-level support for Cogent CMA101 development board. |
---|
3 | * |
---|
4 | * Copyright (c) 1996, 2001, 2002 Cygnus Support |
---|
5 | * |
---|
6 | * The authors hereby grant permission to use, copy, modify, distribute, |
---|
7 | * and license this software and its documentation for any purpose, provided |
---|
8 | * that existing copyright notices are retained in all copies and that this |
---|
9 | * notice is included verbatim in any distributions. No written agreement, |
---|
10 | * license, or royalty fee is required for any of the authorized uses. |
---|
11 | * Modifications to this software may be copyrighted by their authors |
---|
12 | * and need not follow the licensing terms described here, provided that |
---|
13 | * the new terms are clearly indicated on the first page of each file where |
---|
14 | * they apply. |
---|
15 | */ |
---|
16 | |
---|
17 | #ifdef __mips16 |
---|
18 | /* The assembler portions of this file need to be re-written to |
---|
19 | support mips16, if and when that seems useful. |
---|
20 | */ |
---|
21 | #error cma101.c can not be compiled -mips16 |
---|
22 | #endif |
---|
23 | |
---|
24 | |
---|
25 | #include <time.h> /* standard ANSI time routines */ |
---|
26 | |
---|
27 | /* Normally these would appear in a header file for external |
---|
28 | use. However, we are only building a simple example world at the |
---|
29 | moment: */ |
---|
30 | |
---|
31 | #include "regs.S" |
---|
32 | |
---|
33 | #if defined(MIPSEB) |
---|
34 | #define BYTEREG(b,o) ((volatile unsigned char *)(PHYS_TO_K1((b) + (o) + 7))) |
---|
35 | #endif /* MIPSEB */ |
---|
36 | #if defined(MIPSEL) |
---|
37 | #define BYTEREG(b,o) ((volatile unsigned char *)(PHYS_TO_K1((b) + (o)))) |
---|
38 | #endif /* MIPSEL */ |
---|
39 | |
---|
40 | /* I/O addresses: */ |
---|
41 | #define RTCLOCK_BASE (0x0E800000) /* Mk48T02 NVRAM/RTC */ |
---|
42 | #define UART_BASE (0x0E900000) /* NS16C552 DUART */ |
---|
43 | #define LCD_BASE (0x0EB00000) /* Alphanumeric display */ |
---|
44 | |
---|
45 | /* LCD panel manifests: */ |
---|
46 | #define LCD_DATA BYTEREG(LCD_BASE,0) |
---|
47 | #define LCD_CMD BYTEREG(LCD_BASE,8) |
---|
48 | |
---|
49 | #define LCD_STAT_BUSY (0x80) |
---|
50 | #define LCD_SET_DDADDR (0x80) |
---|
51 | |
---|
52 | /* RTC manifests */ |
---|
53 | /* The lo-offsets are the NVRAM locations (0x7F8 bytes) */ |
---|
54 | #define RTC_CONTROL BYTEREG(RTCLOCK_BASE,0x3FC0) |
---|
55 | #define RTC_SECS BYTEREG(RTCLOCK_BASE,0x3FC8) |
---|
56 | #define RTC_MINS BYTEREG(RTCLOCK_BASE,0x3FD0) |
---|
57 | #define RTC_HOURS BYTEREG(RTCLOCK_BASE,0x3FD8) |
---|
58 | #define RTC_DAY BYTEREG(RTCLOCK_BASE,0x3FE0) |
---|
59 | #define RTC_DATE BYTEREG(RTCLOCK_BASE,0x3FE8) |
---|
60 | #define RTC_MONTH BYTEREG(RTCLOCK_BASE,0x3FF0) |
---|
61 | #define RTC_YEAR BYTEREG(RTCLOCK_BASE,0x3FF8) |
---|
62 | |
---|
63 | #define RTC_CTL_LOCK_READ (0x40) /* lock RTC whilst reading */ |
---|
64 | #define RTC_CTL_LOCK_WRITE (0x80) /* lock RTC whilst writing */ |
---|
65 | |
---|
66 | /* Macro to force out-standing memory transfers to complete before |
---|
67 | next sequence. For the moment we assume that the processor in the |
---|
68 | CMA101 board supports at least ISA II. */ |
---|
69 | #define DOSYNC() asm(" .set mips2 ; sync ; .set mips0") |
---|
70 | |
---|
71 | /* We disable interrupts by writing zero to all of the masks, and the |
---|
72 | global interrupt enable bit: */ |
---|
73 | #define INTDISABLE(sr,tmp) asm("\ |
---|
74 | .set mips2 ; \ |
---|
75 | mfc0 %0,$12 ; \ |
---|
76 | lui %1,0xffff ; \ |
---|
77 | ori %1,%1,0xfffe ; \ |
---|
78 | and %1, %0, %1 ; \ |
---|
79 | mtc0 %1,$12 ; \ |
---|
80 | .set mips0" : "=d" (sr), "=d" (tmp)) |
---|
81 | #define INTRESTORE(sr) asm("\ |
---|
82 | .set mips2 ; \ |
---|
83 | mtc0 %0,$12 ; \ |
---|
84 | .set mips0" : : "d" (sr)) |
---|
85 | |
---|
86 | /* TODO:FIXME: The CPU card support should be in separate source file |
---|
87 | from the standard CMA101 support provided in this file. */ |
---|
88 | |
---|
89 | /* The CMA101 board being used contains a CMA257 Vr4300 CPU: |
---|
90 | MasterClock is at 33MHz. PClock is derived from MasterClock by |
---|
91 | multiplying by the ratio defined by the DivMode pins: |
---|
92 | DivMode(1:0) MasterClock PClock Ratio |
---|
93 | 00 100MHz 100MHz 1:1 |
---|
94 | 01 100MHz 150MHz 1.5:1 |
---|
95 | 10 100MHz 200MHz 2:1 |
---|
96 | 11 100Mhz 300MHz 3:1 |
---|
97 | |
---|
98 | Are these pins reflected in the EC bits in the CONFIG register? or |
---|
99 | is that talking about a different clock multiplier? |
---|
100 | 110 = 1 |
---|
101 | 111 = 1.5 |
---|
102 | 000 = 2 |
---|
103 | 001 = 3 |
---|
104 | (all other values are undefined) |
---|
105 | */ |
---|
106 | |
---|
107 | #define MASTERCLOCK (33) /* ticks per uS */ |
---|
108 | unsigned int pclock; /* number of PClock ticks per uS */ |
---|
109 | void |
---|
110 | set_pclock (void) |
---|
111 | { |
---|
112 | unsigned int config; |
---|
113 | asm volatile ("mfc0 %0,$16 ; nop ; nop" : "=r" (config)); /* nasty CP0 register constant */ |
---|
114 | switch ((config >> 28) & 0x7) { |
---|
115 | case 0x7 : /* 1.5:1 */ |
---|
116 | pclock = (MASTERCLOCK + (MASTERCLOCK / 2)); |
---|
117 | break; |
---|
118 | |
---|
119 | case 0x0 : /* 2:1 */ |
---|
120 | pclock = (2 * MASTERCLOCK); |
---|
121 | break; |
---|
122 | |
---|
123 | case 0x1 : /* 3:1 */ |
---|
124 | pclock = (3 * MASTERCLOCK); |
---|
125 | break; |
---|
126 | |
---|
127 | case 0x6 : /* 1:1 */ |
---|
128 | default : /* invalid configuration, so assume the lowest */ |
---|
129 | pclock = MASTERCLOCK; |
---|
130 | break; |
---|
131 | } |
---|
132 | |
---|
133 | return; |
---|
134 | } |
---|
135 | |
---|
136 | #define PCLOCK_WAIT(x) __cpu_timer_poll((x) * pclock) |
---|
137 | |
---|
138 | /* NOTE: On the Cogent CMA101 board the LCD controller will sometimes |
---|
139 | return not-busy, even though it is. The work-around is to perform a |
---|
140 | ~50uS delay before checking the busy signal. */ |
---|
141 | |
---|
142 | static int |
---|
143 | lcd_busy (void) |
---|
144 | { |
---|
145 | PCLOCK_WAIT(50); /* 50uS delay */ |
---|
146 | return(*LCD_CMD & LCD_STAT_BUSY); |
---|
147 | } |
---|
148 | |
---|
149 | /* Note: This code *ASSUMES* that the LCD has already been initialised |
---|
150 | by the monitor. It only provides code to write to the LCD, and is |
---|
151 | not a complete device driver. */ |
---|
152 | |
---|
153 | void |
---|
154 | lcd_display (int line, const char *msg) |
---|
155 | { |
---|
156 | int n; |
---|
157 | |
---|
158 | if (lcd_busy ()) |
---|
159 | return; |
---|
160 | |
---|
161 | *LCD_CMD = (LCD_SET_DDADDR | (line == 1 ? 0x40 : 0x00)); |
---|
162 | |
---|
163 | for (n = 0; n < 16; n++) { |
---|
164 | if (lcd_busy ()) |
---|
165 | return; |
---|
166 | if (*msg) |
---|
167 | *LCD_DATA = *msg++; |
---|
168 | else |
---|
169 | *LCD_DATA = ' '; |
---|
170 | } |
---|
171 | |
---|
172 | return; |
---|
173 | } |
---|
174 | |
---|
175 | #define SM_PATTERN (0x55AA55AA) |
---|
176 | #define SM_INCR ((256 << 10) / sizeof(unsigned int)) /* 64K words */ |
---|
177 | |
---|
178 | extern unsigned int __buserr_count(void); |
---|
179 | extern void __default_buserr_handler(void); |
---|
180 | extern void __restore_buserr_handler(void); |
---|
181 | |
---|
182 | /* Allow the user to provide his/her own defaults. */ |
---|
183 | unsigned int __sizemem_default; |
---|
184 | |
---|
185 | unsigned int |
---|
186 | __sizemem () |
---|
187 | { |
---|
188 | volatile unsigned int *base; |
---|
189 | volatile unsigned int *probe; |
---|
190 | unsigned int baseorig; |
---|
191 | unsigned int sr; |
---|
192 | extern char end[]; |
---|
193 | char *endptr = (char *)&end; |
---|
194 | int extra; |
---|
195 | |
---|
196 | /* If the linker script provided a value for the memory size (or the user |
---|
197 | overrode it in a debugger), use that. */ |
---|
198 | if (__sizemem_default) |
---|
199 | return __sizemem_default; |
---|
200 | |
---|
201 | /* If we are running in kernel segment 0 (possibly cached), try sizing memory |
---|
202 | in kernel segment 1 (uncached) to avoid some problems with monitors. */ |
---|
203 | if (endptr >= K0BASE_ADDR && endptr < K1BASE_ADDR) |
---|
204 | endptr = (endptr - K0BASE_ADDR) + K1BASE_ADDR; |
---|
205 | |
---|
206 | INTDISABLE(sr,baseorig); /* disable all interrupt masks */ |
---|
207 | |
---|
208 | __default_buserr_handler(); |
---|
209 | __cpu_flush(); |
---|
210 | |
---|
211 | DOSYNC(); |
---|
212 | |
---|
213 | /* _end is the end of the user program. _end may not be properly aligned |
---|
214 | for an int pointer, so we adjust the address to make sure it is safe. |
---|
215 | We use void * arithmetic to avoid accidentally truncating the pointer. */ |
---|
216 | |
---|
217 | extra = ((int) endptr & (sizeof (int) - 1)); |
---|
218 | base = ((void *) endptr + sizeof (int) - extra); |
---|
219 | baseorig = *base; |
---|
220 | |
---|
221 | *base = SM_PATTERN; |
---|
222 | /* This assumes that the instructions fetched between the store, and |
---|
223 | the following read will have changed the data bus contents: */ |
---|
224 | if (*base == SM_PATTERN) { |
---|
225 | probe = base; |
---|
226 | for (;;) { |
---|
227 | unsigned int probeorig; |
---|
228 | probe += SM_INCR; |
---|
229 | probeorig = *probe; |
---|
230 | /* Check if a bus error occurred: */ |
---|
231 | if (!__buserr_count()) { |
---|
232 | *probe = SM_PATTERN; |
---|
233 | DOSYNC(); |
---|
234 | if (*probe == SM_PATTERN) { |
---|
235 | *probe = ~SM_PATTERN; |
---|
236 | DOSYNC(); |
---|
237 | if (*probe == ~SM_PATTERN) { |
---|
238 | if (*base == SM_PATTERN) { |
---|
239 | *probe = probeorig; |
---|
240 | continue; |
---|
241 | } |
---|
242 | } |
---|
243 | } |
---|
244 | *probe = probeorig; |
---|
245 | } |
---|
246 | break; |
---|
247 | } |
---|
248 | } |
---|
249 | |
---|
250 | *base = baseorig; |
---|
251 | __restore_buserr_handler(); |
---|
252 | __cpu_flush(); |
---|
253 | |
---|
254 | DOSYNC(); |
---|
255 | |
---|
256 | INTRESTORE(sr); /* restore interrupt mask to entry state */ |
---|
257 | |
---|
258 | return((probe - base) * sizeof(unsigned int)); |
---|
259 | } |
---|
260 | |
---|
261 | /* Provided as a function, so as to avoid reading the I/O location |
---|
262 | multiple times: */ |
---|
263 | static int |
---|
264 | convertbcd(byte) |
---|
265 | unsigned char byte; |
---|
266 | { |
---|
267 | return ((((byte >> 4) & 0xF) * 10) + (byte & 0xF)); |
---|
268 | } |
---|
269 | |
---|
270 | time_t |
---|
271 | time (_timer) |
---|
272 | time_t *_timer; |
---|
273 | { |
---|
274 | time_t result = 0; |
---|
275 | struct tm tm; |
---|
276 | *RTC_CONTROL |= RTC_CTL_LOCK_READ; |
---|
277 | DOSYNC(); |
---|
278 | |
---|
279 | tm.tm_sec = convertbcd(*RTC_SECS); |
---|
280 | tm.tm_min = convertbcd(*RTC_MINS); |
---|
281 | tm.tm_hour = convertbcd(*RTC_HOURS); |
---|
282 | tm.tm_mday = convertbcd(*RTC_DATE); |
---|
283 | tm.tm_mon = convertbcd(*RTC_MONTH); |
---|
284 | tm.tm_year = convertbcd(*RTC_YEAR); |
---|
285 | |
---|
286 | DOSYNC(); |
---|
287 | *RTC_CONTROL &= ~(RTC_CTL_LOCK_READ | RTC_CTL_LOCK_WRITE); |
---|
288 | |
---|
289 | tm.tm_isdst = 0; |
---|
290 | |
---|
291 | /* Check for invalid time information */ |
---|
292 | if ((tm.tm_sec < 60) && (tm.tm_min < 60) && (tm.tm_hour < 24) |
---|
293 | && (tm.tm_mday < 32) && (tm.tm_mon < 13)) { |
---|
294 | |
---|
295 | /* Get the correct year number, but keep it in YEAR-1900 form: */ |
---|
296 | if (tm.tm_year < 70) |
---|
297 | tm.tm_year += 100; |
---|
298 | |
---|
299 | #if 0 /* NOTE: mon_printf() can only accept 4 arguments (format string + 3 fields) */ |
---|
300 | mon_printf("[DBG: s=%d m=%d h=%d]", tm.tm_sec, tm.tm_min, tm.tm_hour); |
---|
301 | mon_printf("[DBG: d=%d m=%d y=%d]", tm.tm_mday, tm.tm_mon, tm.tm_year); |
---|
302 | #endif |
---|
303 | |
---|
304 | /* Convert the time-structure into a second count */ |
---|
305 | result = mktime (&tm); |
---|
306 | } |
---|
307 | |
---|
308 | if (_timer != NULL) |
---|
309 | *_timer = result; |
---|
310 | |
---|
311 | return (result); |
---|
312 | } |
---|
313 | |
---|
314 | /*> EOF cma101.c <*/ |
---|