]>
Commit | Line | Data |
---|---|---|
03261851 RK |
1 | /* |
2 | * cma101.c -- lo-level support for Cogent CMA101 development board. | |
3 | * | |
2c8d7359 | 4 | * Copyright (c) 1996, 2001, 2002 Cygnus Support |
03261851 RK |
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 | ||
2c8d7359 MM |
182 | /* Allow the user to provide his/her own defaults. */ |
183 | unsigned int __sizemem_default; | |
184 | ||
03261851 RK |
185 | unsigned int |
186 | __sizemem () | |
187 | { | |
188 | volatile unsigned int *base; | |
189 | volatile unsigned int *probe; | |
190 | unsigned int baseorig; | |
191 | unsigned int sr; | |
ff524f6c | 192 | extern char end[]; |
b5b5b0d4 | 193 | char *endptr = (char *)&end; |
03261851 RK |
194 | int extra; |
195 | ||
2c8d7359 MM |
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 | ||
b5b5b0d4 MM |
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 | ||
03261851 RK |
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 | ||
b5b5b0d4 MM |
217 | extra = ((int) endptr & (sizeof (int) - 1)); |
218 | base = ((void *) endptr + sizeof (int) - extra); | |
03261851 RK |
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 <*/ |