1 /*
2 * arch/m68k/q40/config.c
3 *
4 * Copyright (C) 1999 Richard Zidlicky
5 *
6 * originally based on:
7 *
8 * linux/bvme/config.c
9 *
10 * This file is subject to the terms and conditions of the GNU General Public
11 * License. See the file README.legal in the main directory of this archive
12 * for more details.
13 */
14
15 #include <linux/config.h>
16 #include <linux/types.h>
17 #include <linux/kernel.h>
18 #include <linux/mm.h>
19 #include <linux/tty.h>
20 #include <linux/console.h>
21 #include <linux/linkage.h>
22 #include <linux/init.h>
23 #include <linux/major.h>
24 #include <linux/serial_reg.h>
25 #include <linux/rtc.h>
26 #include <linux/vt_kern.h>
27
28 #include <asm/io.h>
29 #include <asm/rtc.h>
30 #include <asm/bootinfo.h>
31 #include <asm/system.h>
32 #include <asm/pgtable.h>
33 #include <asm/setup.h>
34 #include <asm/irq.h>
35 #include <asm/traps.h>
36 #include <asm/rtc.h>
37 #include <asm/machdep.h>
38 #include <asm/q40_master.h>
39 #include <asm/keyboard.h>
40
41 extern void floppy_setup(char *str, int *ints);
42
43 extern int q40kbd_translate(unsigned char scancode, unsigned char *keycode,
44 char raw_mode);
45 extern void q40_process_int (int level, struct pt_regs *regs);
46 extern void (*q40_sys_default_handler[]) (int, void *, struct pt_regs *); /* added just for debugging */
47 extern void q40_init_IRQ (void);
48 extern void q40_free_irq (unsigned int, void *);
49 extern int q40_get_irq_list (char *);
50 extern void q40_enable_irq (unsigned int);
51 extern void q40_disable_irq (unsigned int);
52 static void q40_get_model(char *model);
53 static int q40_get_hardware_list(char *buffer);
54 extern int q40_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname, void *dev_id);
55 extern void q40_sched_init(void (*handler)(int, void *, struct pt_regs *));
56
57 extern unsigned long q40_gettimeoffset (void);
58 extern void q40_gettod (int *year, int *mon, int *day, int *hour,
59 int *min, int *sec);
60 extern int q40_hwclk (int, struct rtc_time *);
61 extern unsigned int q40_get_ss (void);
62 extern int q40_set_clock_mmss (unsigned long);
63 static int q40_get_rtc_pll(struct rtc_pll_info *pll);
64 static int q40_set_rtc_pll(struct rtc_pll_info *pll);
65 extern void q40_reset (void);
66 void q40_halt(void);
67 extern void q40_waitbut(void);
68 void q40_set_vectors (void);
69
70 void q40_mksound(unsigned int /*freq*/, unsigned int /*ticks*/ );
71
72 extern char *saved_command_line;
73 extern char m68k_debug_device[];
74 static void q40_mem_console_write(struct console *co, const char *b,
75 unsigned int count);
76
77 extern int ql_ticks;
78
79 static struct console q40_console_driver = {
80 name: "debug",
81 flags: CON_PRINTBUFFER,
82 index: -1,
83 };
84
85
86 /* early debugging function:*/
87 extern char *q40_mem_cptr; /*=(char *)0xff020000;*/
88 static int _cpleft;
89
90 #if 0
91 int q40_kbd_translate(unsigned char keycode, unsigned char *keycodep, char raw_mode)
92 {
93 *keycodep = keycode;
94 return 1;
95 }
96 #endif
97
q40_mem_console_write(struct console * co,const char * s,unsigned int count)98 static void q40_mem_console_write(struct console *co, const char *s,
99 unsigned int count)
100 {
101 char *p=(char *)s;
102
103 if (count<_cpleft)
104 while (count-- >0){
105 *q40_mem_cptr=*p++;
106 q40_mem_cptr+=4;
107 _cpleft--;
108 }
109 }
110 #if 0
111 void printq40(char *str)
112 {
113 int l=strlen(str);
114 char *p=q40_mem_cptr;
115
116 while (l-- >0 && _cpleft-- >0)
117 {
118 *p=*str++;
119 p+=4;
120 }
121 q40_mem_cptr=p;
122 }
123 #endif
124
125 #if 0
126 int q40_kbdrate (struct kbd_repeat *k)
127 {
128 return 0;
129 }
130 #endif
131
132 static int halted=0;
133
134 #ifdef CONFIG_HEARTBEAT
q40_heartbeat(int on)135 static void q40_heartbeat(int on)
136 {
137 if (halted) return;
138
139 if (on)
140 Q40_LED_ON();
141 else
142 Q40_LED_OFF();
143 }
144 #endif
145
q40_reset()146 void q40_reset()
147 {
148 halted=1;
149 printk ("\n\n*******************************************\n"
150 "Called q40_reset : press the RESET button!! \n"
151 "*******************************************\n");
152 Q40_LED_ON();
153 while(1) ;
154 }
q40_halt()155 void q40_halt()
156 {
157 halted=1;
158 printk ("\n\n*******************\n"
159 " Called q40_halt\n"
160 "*******************\n");
161 Q40_LED_ON();
162 while(1) ;
163 }
164
q40_get_model(char * model)165 static void q40_get_model(char *model)
166 {
167 sprintf(model, "Q40");
168 }
169
170 /* No hardware options on Q40? */
171
q40_get_hardware_list(char * buffer)172 static int q40_get_hardware_list(char *buffer)
173 {
174 *buffer = '\0';
175 return 0;
176 }
177
178 static unsigned int serports[]={0x3f8,0x2f8,0x3e8,0x2e8,0};
q40_disable_irqs(void)179 void q40_disable_irqs(void)
180 {
181 unsigned i,j;
182
183 j=0;
184 while((i=serports[j++])) outb(0,i+UART_IER);
185 master_outb(0,EXT_ENABLE_REG);
186 master_outb(0,KEY_IRQ_ENABLE_REG);
187 }
188
config_q40(void)189 void __init config_q40(void)
190 {
191 mach_sched_init = q40_sched_init;
192
193 #ifdef CONFIG_VT
194 mach_keyb_init = q40kbd_init_hw;
195 mach_kbd_translate = q40kbd_translate;
196 kd_mksound = q40_mksound;
197 #endif
198 mach_init_IRQ = q40_init_IRQ;
199 mach_gettimeoffset = q40_gettimeoffset;
200 mach_gettod = q40_gettod;
201 mach_hwclk = q40_hwclk;
202 mach_get_ss = q40_get_ss;
203 mach_get_rtc_pll = q40_get_rtc_pll;
204 mach_set_rtc_pll = q40_set_rtc_pll;
205 mach_set_clock_mmss = q40_set_clock_mmss;
206
207 mach_reset = q40_reset;
208 mach_free_irq = q40_free_irq;
209 mach_process_int = q40_process_int;
210 mach_get_irq_list = q40_get_irq_list;
211 mach_request_irq = q40_request_irq;
212 enable_irq = q40_enable_irq;
213 disable_irq = q40_disable_irq;
214 mach_default_handler = &q40_sys_default_handler;
215 mach_get_model = q40_get_model;
216 mach_get_hardware_list = q40_get_hardware_list;
217
218 #ifdef CONFIG_MAGIC_SYSRQ
219 mach_sysrq_key = 0x54;
220 #endif
221 #ifdef CONFIG_HEARTBEAT
222 mach_heartbeat = q40_heartbeat;
223 #endif
224 mach_halt = q40_halt;
225 #ifdef CONFIG_DUMMY_CONSOLE
226 conswitchp = &dummy_con;
227 #endif
228
229 /* disable a few things that SMSQ might have left enabled */
230 q40_disable_irqs();
231
232 /* no DMA at all, but ide-scsi requires it.. make sure
233 * all physical RAM fits into the boundary - otherwise
234 * allocator may play costly and useless tricks */
235 mach_max_dma_address = 1024*1024*1024;
236
237 /* useful for early debugging stages - writes kernel messages into SRAM */
238 if (!strncmp( m68k_debug_device,"mem",3 ))
239 {
240 /*printk("using NVRAM debug, q40_mem_cptr=%p\n",q40_mem_cptr);*/
241 _cpleft=2000-((long)q40_mem_cptr-0xff020000)/4;
242 q40_console_driver.write = q40_mem_console_write;
243 register_console(&q40_console_driver);
244 }
245 }
246
247
q40_parse_bootinfo(const struct bi_record * rec)248 int q40_parse_bootinfo(const struct bi_record *rec)
249 {
250 return 1;
251 }
252
253
bcd2bin(unsigned char b)254 static inline unsigned char bcd2bin (unsigned char b)
255 {
256 return ((b>>4)*10 + (b&15));
257 }
258
bin2bcd(unsigned char b)259 static inline unsigned char bin2bcd (unsigned char b)
260 {
261 return (((b/10)*16) + (b%10));
262 }
263
264
q40_gettimeoffset(void)265 unsigned long q40_gettimeoffset (void)
266 {
267 return 5000*(ql_ticks!=0);
268 }
269
q40_gettod(int * year,int * mon,int * day,int * hour,int * min,int * sec)270 extern void q40_gettod (int *year, int *mon, int *day, int *hour,
271 int *min, int *sec)
272 {
273 Q40_RTC_CTRL |= Q40_RTC_READ;
274 *year = bcd2bin (Q40_RTC_YEAR);
275 *mon = bcd2bin (Q40_RTC_MNTH);
276 *day = bcd2bin (Q40_RTC_DATE);
277 *hour = bcd2bin (Q40_RTC_HOUR);
278 *min = bcd2bin (Q40_RTC_MINS);
279 *sec = bcd2bin (Q40_RTC_SECS);
280 Q40_RTC_CTRL &= ~(Q40_RTC_READ);
281
282 }
283
284
285
286 /*
287 * Looks like op is non-zero for setting the clock, and zero for
288 * reading the clock.
289 *
290 * struct hwclk_time {
291 * unsigned sec; 0..59
292 * unsigned min; 0..59
293 * unsigned hour; 0..23
294 * unsigned day; 1..31
295 * unsigned mon; 0..11
296 * unsigned year; 00...
297 * int wday; 0..6, 0 is Sunday, -1 means unknown/don't set
298 * };
299 */
300
q40_hwclk(int op,struct rtc_time * t)301 int q40_hwclk(int op, struct rtc_time *t)
302 {
303 if (op)
304 { /* Write.... */
305 Q40_RTC_CTRL |= Q40_RTC_WRITE;
306
307 Q40_RTC_SECS = bin2bcd(t->tm_sec);
308 Q40_RTC_MINS = bin2bcd(t->tm_min);
309 Q40_RTC_HOUR = bin2bcd(t->tm_hour);
310 Q40_RTC_DATE = bin2bcd(t->tm_mday);
311 Q40_RTC_MNTH = bin2bcd(t->tm_mon + 1);
312 Q40_RTC_YEAR = bin2bcd(t->tm_year%100);
313 if (t->tm_wday >= 0)
314 Q40_RTC_DOW = bin2bcd(t->tm_wday+1);
315
316 Q40_RTC_CTRL &= ~(Q40_RTC_WRITE);
317 }
318 else
319 { /* Read.... */
320 Q40_RTC_CTRL |= Q40_RTC_READ;
321
322 t->tm_year = bcd2bin (Q40_RTC_YEAR);
323 t->tm_mon = bcd2bin (Q40_RTC_MNTH)-1;
324 t->tm_mday = bcd2bin (Q40_RTC_DATE);
325 t->tm_hour = bcd2bin (Q40_RTC_HOUR);
326 t->tm_min = bcd2bin (Q40_RTC_MINS);
327 t->tm_sec = bcd2bin (Q40_RTC_SECS);
328
329 Q40_RTC_CTRL &= ~(Q40_RTC_READ);
330
331 if (t->tm_year < 70)
332 t->tm_year += 100;
333 t->tm_wday = bcd2bin(Q40_RTC_DOW)-1;
334
335 }
336
337 return 0;
338 }
339
q40_get_ss()340 unsigned int q40_get_ss()
341 {
342 return bcd2bin(Q40_RTC_SECS);
343 }
344
345 /*
346 * Set the minutes and seconds from seconds value 'nowtime'. Fail if
347 * clock is out by > 30 minutes. Logic lifted from atari code.
348 */
349
q40_set_clock_mmss(unsigned long nowtime)350 int q40_set_clock_mmss (unsigned long nowtime)
351 {
352 int retval = 0;
353 short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
354
355 int rtc_minutes;
356
357
358 rtc_minutes = bcd2bin (Q40_RTC_MINS);
359
360 if ((rtc_minutes < real_minutes
361 ? real_minutes - rtc_minutes
362 : rtc_minutes - real_minutes) < 30)
363 {
364 Q40_RTC_CTRL |= Q40_RTC_WRITE;
365 Q40_RTC_MINS = bin2bcd(real_minutes);
366 Q40_RTC_SECS = bin2bcd(real_seconds);
367 Q40_RTC_CTRL &= ~(Q40_RTC_WRITE);
368 }
369 else
370 retval = -1;
371
372
373 return retval;
374 }
375
376
377 /* get and set PLL calibration of RTC clock */
378 #define Q40_RTC_PLL_MASK ((1<<5)-1)
379 #define Q40_RTC_PLL_SIGN (1<<5)
380
q40_get_rtc_pll(struct rtc_pll_info * pll)381 static int q40_get_rtc_pll(struct rtc_pll_info *pll)
382 {
383 int tmp=Q40_RTC_CTRL;
384 pll->pll_value = tmp & Q40_RTC_PLL_MASK;
385 if (tmp & Q40_RTC_PLL_SIGN)
386 pll->pll_value = -pll->pll_value;
387 pll->pll_max=31;
388 pll->pll_min=-31;
389 pll->pll_posmult=512;
390 pll->pll_negmult=256;
391 pll->pll_clock=125829120;
392 return 0;
393 }
394
q40_set_rtc_pll(struct rtc_pll_info * pll)395 static int q40_set_rtc_pll(struct rtc_pll_info *pll)
396 {
397 if (!pll->pll_ctrl){
398 /* the docs are a bit unclear so I am doublesetting RTC_WRITE here ... */
399 int tmp=(pll->pll_value & 31) | (pll->pll_value<0 ? 32 : 0) | Q40_RTC_WRITE;
400 Q40_RTC_CTRL |= Q40_RTC_WRITE;
401 Q40_RTC_CTRL = tmp;
402 Q40_RTC_CTRL &= ~(Q40_RTC_WRITE);
403 return 0;
404 } else return -EINVAL;
405 }
406