config.c 7.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*
 *  arch/m68k/q40/config.c
 *
 *  Copyright (C) 1999 Richard Zidlicky
 *
 * originally based on:
 *
 *  linux/bvme/config.c
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file README.legal in the main directory of this archive
 * for more details.
 */

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/console.h>
#include <linux/linkage.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/serial_reg.h>
#include <linux/rtc.h>
#include <linux/vt_kern.h>
A
Adrian Bunk 已提交
26
#include <linux/bcd.h>
27
#include <linux/platform_device.h>
L
Linus Torvalds 已提交
28 29 30 31 32 33 34 35 36 37 38

#include <asm/io.h>
#include <asm/rtc.h>
#include <asm/bootinfo.h>
#include <asm/pgtable.h>
#include <asm/setup.h>
#include <asm/irq.h>
#include <asm/traps.h>
#include <asm/machdep.h>
#include <asm/q40_master.h>

R
Roman Zippel 已提交
39
extern void q40_init_IRQ(void);
L
Linus Torvalds 已提交
40
static void q40_get_model(char *model);
41
extern void q40_sched_init(irq_handler_t handler);
L
Linus Torvalds 已提交
42

43 44 45 46
static unsigned long q40_gettimeoffset(void);
static int q40_hwclk(int, struct rtc_time *);
static unsigned int q40_get_ss(void);
static int q40_set_clock_mmss(unsigned long);
L
Linus Torvalds 已提交
47 48 49
static int q40_get_rtc_pll(struct rtc_pll_info *pll);
static int q40_set_rtc_pll(struct rtc_pll_info *pll);

R
Roman Zippel 已提交
50
extern void q40_mksound(unsigned int /*freq*/, unsigned int /*ticks*/);
L
Linus Torvalds 已提交
51 52

static void q40_mem_console_write(struct console *co, const char *b,
R
Roman Zippel 已提交
53
				  unsigned int count);
L
Linus Torvalds 已提交
54 55 56 57

extern int ql_ticks;

static struct console q40_console_driver = {
R
Roman Zippel 已提交
58
	.name	= "debug",
R
Roman Zippel 已提交
59
	.write	= q40_mem_console_write,
R
Roman Zippel 已提交
60 61
	.flags	= CON_PRINTBUFFER,
	.index	= -1,
L
Linus Torvalds 已提交
62 63 64 65 66 67 68 69 70 71
};


/* early debugging function:*/
extern char *q40_mem_cptr; /*=(char *)0xff020000;*/
static int _cpleft;

static void q40_mem_console_write(struct console *co, const char *s,
				  unsigned int count)
{
R
Roman Zippel 已提交
72 73 74 75 76 77 78 79 80
	const char *p = s;

	if (count < _cpleft) {
		while (count-- > 0) {
			*q40_mem_cptr = *p++;
			q40_mem_cptr += 4;
			_cpleft--;
		}
	}
L
Linus Torvalds 已提交
81
}
R
Roman Zippel 已提交
82

R
Roman Zippel 已提交
83 84 85 86 87 88 89 90 91 92 93 94 95
static int __init q40_debug_setup(char *arg)
{
	/* useful for early debugging stages - writes kernel messages into SRAM */
	if (MACH_IS_Q40 && !strncmp(arg, "mem", 3)) {
		/*printk("using NVRAM debug, q40_mem_cptr=%p\n",q40_mem_cptr);*/
		_cpleft = 2000 - ((long)q40_mem_cptr-0xff020000) / 4;
		register_console(&q40_console_driver);
	}
	return 0;
}

early_param("debug", q40_debug_setup);

L
Linus Torvalds 已提交
96 97 98
#if 0
void printq40(char *str)
{
R
Roman Zippel 已提交
99 100 101 102 103 104 105 106
	int l = strlen(str);
	char *p = q40_mem_cptr;

	while (l-- > 0 && _cpleft-- > 0) {
		*p = *str++;
		p += 4;
	}
	q40_mem_cptr = p;
L
Linus Torvalds 已提交
107 108 109
}
#endif

R
Roman Zippel 已提交
110
static int halted;
L
Linus Torvalds 已提交
111 112 113 114

#ifdef CONFIG_HEARTBEAT
static void q40_heartbeat(int on)
{
R
Roman Zippel 已提交
115 116
	if (halted)
		return;
L
Linus Torvalds 已提交
117

R
Roman Zippel 已提交
118 119 120 121
	if (on)
		Q40_LED_ON();
	else
		Q40_LED_OFF();
L
Linus Torvalds 已提交
122 123 124
}
#endif

125
static void q40_reset(void)
L
Linus Torvalds 已提交
126
{
R
Roman Zippel 已提交
127 128
        halted = 1;
        printk("\n\n*******************************************\n"
129
		"Called q40_reset : press the RESET button!!\n"
L
Linus Torvalds 已提交
130 131
		"*******************************************\n");
	Q40_LED_ON();
R
Roman Zippel 已提交
132 133
	while (1)
		;
L
Linus Torvalds 已提交
134
}
135 136

static void q40_halt(void)
L
Linus Torvalds 已提交
137
{
R
Roman Zippel 已提交
138 139 140 141
        halted = 1;
        printk("\n\n*******************\n"
		   "  Called q40_halt\n"
		   "*******************\n");
L
Linus Torvalds 已提交
142
	Q40_LED_ON();
R
Roman Zippel 已提交
143 144
	while (1)
		;
L
Linus Torvalds 已提交
145 146 147 148
}

static void q40_get_model(char *model)
{
R
Roman Zippel 已提交
149
	sprintf(model, "Q40");
L
Linus Torvalds 已提交
150 151
}

R
Roman Zippel 已提交
152 153 154 155
static unsigned int serports[] =
{
	0x3f8,0x2f8,0x3e8,0x2e8,0
};
156 157

static void q40_disable_irqs(void)
L
Linus Torvalds 已提交
158
{
R
Roman Zippel 已提交
159
	unsigned i, j;
L
Linus Torvalds 已提交
160

R
Roman Zippel 已提交
161 162 163 164 165
	j = 0;
	while ((i = serports[j++]))
		outb(0, i + UART_IER);
	master_outb(0, EXT_ENABLE_REG);
	master_outb(0, KEY_IRQ_ENABLE_REG);
L
Linus Torvalds 已提交
166 167 168 169
}

void __init config_q40(void)
{
R
Roman Zippel 已提交
170
	mach_sched_init = q40_sched_init;
L
Linus Torvalds 已提交
171

R
Roman Zippel 已提交
172 173 174 175 176 177 178
	mach_init_IRQ = q40_init_IRQ;
	mach_gettimeoffset = q40_gettimeoffset;
	mach_hwclk = q40_hwclk;
	mach_get_ss = q40_get_ss;
	mach_get_rtc_pll = q40_get_rtc_pll;
	mach_set_rtc_pll = q40_set_rtc_pll;
	mach_set_clock_mmss = q40_set_clock_mmss;
L
Linus Torvalds 已提交
179

R
Roman Zippel 已提交
180 181
	mach_reset = q40_reset;
	mach_get_model = q40_get_model;
L
Linus Torvalds 已提交
182 183

#if defined(CONFIG_INPUT_M68K_BEEP) || defined(CONFIG_INPUT_M68K_BEEP_MODULE)
R
Roman Zippel 已提交
184
	mach_beep = q40_mksound;
L
Linus Torvalds 已提交
185 186
#endif
#ifdef CONFIG_HEARTBEAT
R
Roman Zippel 已提交
187
	mach_heartbeat = q40_heartbeat;
L
Linus Torvalds 已提交
188
#endif
R
Roman Zippel 已提交
189 190 191 192 193 194 195 196 197
	mach_halt = q40_halt;

	/* disable a few things that SMSQ might have left enabled */
	q40_disable_irqs();

	/* no DMA at all, but ide-scsi requires it.. make sure
	 * all physical RAM fits into the boundary - otherwise
	 * allocator may play costly and useless tricks */
	mach_max_dma_address = 1024*1024*1024;
L
Linus Torvalds 已提交
198 199 200 201 202
}


int q40_parse_bootinfo(const struct bi_record *rec)
{
R
Roman Zippel 已提交
203
	return 1;
L
Linus Torvalds 已提交
204 205 206
}


207
static unsigned long q40_gettimeoffset(void)
L
Linus Torvalds 已提交
208
{
R
Roman Zippel 已提交
209
	return 5000 * (ql_ticks != 0);
L
Linus Torvalds 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
}


/*
 * Looks like op is non-zero for setting the clock, and zero for
 * reading the clock.
 *
 *  struct hwclk_time {
 *         unsigned        sec;       0..59
 *         unsigned        min;       0..59
 *         unsigned        hour;      0..23
 *         unsigned        day;       1..31
 *         unsigned        mon;       0..11
 *         unsigned        year;      00...
 *         int             wday;      0..6, 0 is Sunday, -1 means unknown/don't set
 * };
 */

228
static int q40_hwclk(int op, struct rtc_time *t)
L
Linus Torvalds 已提交
229
{
R
Roman Zippel 已提交
230 231 232
	if (op) {
		/* Write.... */
		Q40_RTC_CTRL |= Q40_RTC_WRITE;
L
Linus Torvalds 已提交
233 234 235 236 237 238 239 240 241 242

		Q40_RTC_SECS = bin2bcd(t->tm_sec);
		Q40_RTC_MINS = bin2bcd(t->tm_min);
		Q40_RTC_HOUR = bin2bcd(t->tm_hour);
		Q40_RTC_DATE = bin2bcd(t->tm_mday);
		Q40_RTC_MNTH = bin2bcd(t->tm_mon + 1);
		Q40_RTC_YEAR = bin2bcd(t->tm_year%100);
		if (t->tm_wday >= 0)
			Q40_RTC_DOW = bin2bcd(t->tm_wday+1);

R
Roman Zippel 已提交
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
		Q40_RTC_CTRL &= ~(Q40_RTC_WRITE);
	} else {
		/* Read....  */
		Q40_RTC_CTRL |= Q40_RTC_READ;

		t->tm_year = bcd2bin (Q40_RTC_YEAR);
		t->tm_mon  = bcd2bin (Q40_RTC_MNTH)-1;
		t->tm_mday = bcd2bin (Q40_RTC_DATE);
		t->tm_hour = bcd2bin (Q40_RTC_HOUR);
		t->tm_min  = bcd2bin (Q40_RTC_MINS);
		t->tm_sec  = bcd2bin (Q40_RTC_SECS);

		Q40_RTC_CTRL &= ~(Q40_RTC_READ);

		if (t->tm_year < 70)
			t->tm_year += 100;
		t->tm_wday = bcd2bin(Q40_RTC_DOW)-1;
L
Linus Torvalds 已提交
260 261 262 263 264
	}

	return 0;
}

265
static unsigned int q40_get_ss(void)
L
Linus Torvalds 已提交
266 267 268 269 270 271 272 273 274
{
	return bcd2bin(Q40_RTC_SECS);
}

/*
 * Set the minutes and seconds from seconds value 'nowtime'.  Fail if
 * clock is out by > 30 minutes.  Logic lifted from atari code.
 */

275
static int q40_set_clock_mmss(unsigned long nowtime)
L
Linus Torvalds 已提交
276 277 278 279 280 281
{
	int retval = 0;
	short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;

	int rtc_minutes;

R
Roman Zippel 已提交
282
	rtc_minutes = bcd2bin(Q40_RTC_MINS);
L
Linus Torvalds 已提交
283

R
Roman Zippel 已提交
284 285 286 287
	if ((rtc_minutes < real_minutes ?
	     real_minutes - rtc_minutes :
	     rtc_minutes - real_minutes) < 30) {
		Q40_RTC_CTRL |= Q40_RTC_WRITE;
L
Linus Torvalds 已提交
288 289 290
		Q40_RTC_MINS = bin2bcd(real_minutes);
		Q40_RTC_SECS = bin2bcd(real_seconds);
		Q40_RTC_CTRL &= ~(Q40_RTC_WRITE);
R
Roman Zippel 已提交
291
	} else
L
Linus Torvalds 已提交
292 293 294 295 296 297 298 299 300 301 302 303
		retval = -1;

	return retval;
}


/* get and set PLL calibration of RTC clock */
#define Q40_RTC_PLL_MASK ((1<<5)-1)
#define Q40_RTC_PLL_SIGN (1<<5)

static int q40_get_rtc_pll(struct rtc_pll_info *pll)
{
R
Roman Zippel 已提交
304 305
	int tmp = Q40_RTC_CTRL;

L
Linus Torvalds 已提交
306 307 308
	pll->pll_value = tmp & Q40_RTC_PLL_MASK;
	if (tmp & Q40_RTC_PLL_SIGN)
		pll->pll_value = -pll->pll_value;
R
Roman Zippel 已提交
309 310 311 312 313 314
	pll->pll_max = 31;
	pll->pll_min = -31;
	pll->pll_posmult = 512;
	pll->pll_negmult = 256;
	pll->pll_clock = 125829120;

L
Linus Torvalds 已提交
315 316 317 318 319
	return 0;
}

static int q40_set_rtc_pll(struct rtc_pll_info *pll)
{
R
Roman Zippel 已提交
320
	if (!pll->pll_ctrl) {
L
Linus Torvalds 已提交
321 322 323 324 325 326 327 328 329 330 331
		/* the docs are a bit unclear so I am doublesetting */
		/* RTC_WRITE here ... */
		int tmp = (pll->pll_value & 31) | (pll->pll_value<0 ? 32 : 0) |
			  Q40_RTC_WRITE;
		Q40_RTC_CTRL |= Q40_RTC_WRITE;
		Q40_RTC_CTRL = tmp;
		Q40_RTC_CTRL &= ~(Q40_RTC_WRITE);
		return 0;
	} else
		return -EINVAL;
}
332 333 334 335 336

static __init int q40_add_kbd_device(void)
{
	struct platform_device *pdev;

337 338 339
	if (!MACH_IS_Q40)
		return -ENODEV;

340 341 342 343 344 345 346
	pdev = platform_device_register_simple("q40kbd", -1, NULL, 0);
	if (IS_ERR(pdev))
		return PTR_ERR(pdev);

	return 0;
}
arch_initcall(q40_add_kbd_device);