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