xref: /kernel/linux/linux-5.10/arch/m68k/q40/config.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci *  arch/m68k/q40/config.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *  Copyright (C) 1999 Richard Zidlicky
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * originally based on:
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *  linux/bvme/config.c
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
118c2ecf20Sopenharmony_ci * License.  See the file README.legal in the main directory of this archive
128c2ecf20Sopenharmony_ci * for more details.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/errno.h>
168c2ecf20Sopenharmony_ci#include <linux/types.h>
178c2ecf20Sopenharmony_ci#include <linux/kernel.h>
188c2ecf20Sopenharmony_ci#include <linux/mm.h>
198c2ecf20Sopenharmony_ci#include <linux/tty.h>
208c2ecf20Sopenharmony_ci#include <linux/console.h>
218c2ecf20Sopenharmony_ci#include <linux/linkage.h>
228c2ecf20Sopenharmony_ci#include <linux/init.h>
238c2ecf20Sopenharmony_ci#include <linux/major.h>
248c2ecf20Sopenharmony_ci#include <linux/serial_reg.h>
258c2ecf20Sopenharmony_ci#include <linux/rtc.h>
268c2ecf20Sopenharmony_ci#include <linux/vt_kern.h>
278c2ecf20Sopenharmony_ci#include <linux/bcd.h>
288c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include <asm/io.h>
318c2ecf20Sopenharmony_ci#include <asm/bootinfo.h>
328c2ecf20Sopenharmony_ci#include <asm/setup.h>
338c2ecf20Sopenharmony_ci#include <asm/irq.h>
348c2ecf20Sopenharmony_ci#include <asm/traps.h>
358c2ecf20Sopenharmony_ci#include <asm/machdep.h>
368c2ecf20Sopenharmony_ci#include <asm/q40_master.h>
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ciextern void q40_init_IRQ(void);
398c2ecf20Sopenharmony_cistatic void q40_get_model(char *model);
408c2ecf20Sopenharmony_ciextern void q40_sched_init(irq_handler_t handler);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic int q40_hwclk(int, struct rtc_time *);
438c2ecf20Sopenharmony_cistatic unsigned int q40_get_ss(void);
448c2ecf20Sopenharmony_cistatic int q40_get_rtc_pll(struct rtc_pll_info *pll);
458c2ecf20Sopenharmony_cistatic int q40_set_rtc_pll(struct rtc_pll_info *pll);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ciextern void q40_mksound(unsigned int /*freq*/, unsigned int /*ticks*/);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic void q40_mem_console_write(struct console *co, const char *b,
508c2ecf20Sopenharmony_ci				  unsigned int count);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ciextern int ql_ticks;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic struct console q40_console_driver = {
558c2ecf20Sopenharmony_ci	.name	= "debug",
568c2ecf20Sopenharmony_ci	.write	= q40_mem_console_write,
578c2ecf20Sopenharmony_ci	.flags	= CON_PRINTBUFFER,
588c2ecf20Sopenharmony_ci	.index	= -1,
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* early debugging function:*/
638c2ecf20Sopenharmony_ciextern char *q40_mem_cptr; /*=(char *)0xff020000;*/
648c2ecf20Sopenharmony_cistatic int _cpleft;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic void q40_mem_console_write(struct console *co, const char *s,
678c2ecf20Sopenharmony_ci				  unsigned int count)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	const char *p = s;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (count < _cpleft) {
728c2ecf20Sopenharmony_ci		while (count-- > 0) {
738c2ecf20Sopenharmony_ci			*q40_mem_cptr = *p++;
748c2ecf20Sopenharmony_ci			q40_mem_cptr += 4;
758c2ecf20Sopenharmony_ci			_cpleft--;
768c2ecf20Sopenharmony_ci		}
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic int __init q40_debug_setup(char *arg)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	/* useful for early debugging stages - writes kernel messages into SRAM */
838c2ecf20Sopenharmony_ci	if (MACH_IS_Q40 && !strncmp(arg, "mem", 3)) {
848c2ecf20Sopenharmony_ci		/*pr_info("using NVRAM debug, q40_mem_cptr=%p\n",q40_mem_cptr);*/
858c2ecf20Sopenharmony_ci		_cpleft = 2000 - ((long)q40_mem_cptr-0xff020000) / 4;
868c2ecf20Sopenharmony_ci		register_console(&q40_console_driver);
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci	return 0;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ciearly_param("debug", q40_debug_setup);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#if 0
948c2ecf20Sopenharmony_civoid printq40(char *str)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	int l = strlen(str);
978c2ecf20Sopenharmony_ci	char *p = q40_mem_cptr;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	while (l-- > 0 && _cpleft-- > 0) {
1008c2ecf20Sopenharmony_ci		*p = *str++;
1018c2ecf20Sopenharmony_ci		p += 4;
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci	q40_mem_cptr = p;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci#endif
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int halted;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci#ifdef CONFIG_HEARTBEAT
1108c2ecf20Sopenharmony_cistatic void q40_heartbeat(int on)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	if (halted)
1138c2ecf20Sopenharmony_ci		return;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (on)
1168c2ecf20Sopenharmony_ci		Q40_LED_ON();
1178c2ecf20Sopenharmony_ci	else
1188c2ecf20Sopenharmony_ci		Q40_LED_OFF();
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci#endif
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic void q40_reset(void)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	halted = 1;
1258c2ecf20Sopenharmony_ci	pr_info("*******************************************\n"
1268c2ecf20Sopenharmony_ci		"Called q40_reset : press the RESET button!!\n"
1278c2ecf20Sopenharmony_ci		"*******************************************\n");
1288c2ecf20Sopenharmony_ci	Q40_LED_ON();
1298c2ecf20Sopenharmony_ci	while (1)
1308c2ecf20Sopenharmony_ci		;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic void q40_halt(void)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	halted = 1;
1368c2ecf20Sopenharmony_ci	pr_info("*******************\n"
1378c2ecf20Sopenharmony_ci		"  Called q40_halt\n"
1388c2ecf20Sopenharmony_ci		"*******************\n");
1398c2ecf20Sopenharmony_ci	Q40_LED_ON();
1408c2ecf20Sopenharmony_ci	while (1)
1418c2ecf20Sopenharmony_ci		;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic void q40_get_model(char *model)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	sprintf(model, "Q40");
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic unsigned int serports[] =
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	0x3f8,0x2f8,0x3e8,0x2e8,0
1528c2ecf20Sopenharmony_ci};
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic void __init q40_disable_irqs(void)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	unsigned i, j;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	j = 0;
1598c2ecf20Sopenharmony_ci	while ((i = serports[j++]))
1608c2ecf20Sopenharmony_ci		outb(0, i + UART_IER);
1618c2ecf20Sopenharmony_ci	master_outb(0, EXT_ENABLE_REG);
1628c2ecf20Sopenharmony_ci	master_outb(0, KEY_IRQ_ENABLE_REG);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_civoid __init config_q40(void)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	mach_sched_init = q40_sched_init;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	mach_init_IRQ = q40_init_IRQ;
1708c2ecf20Sopenharmony_ci	mach_hwclk = q40_hwclk;
1718c2ecf20Sopenharmony_ci	mach_get_ss = q40_get_ss;
1728c2ecf20Sopenharmony_ci	mach_get_rtc_pll = q40_get_rtc_pll;
1738c2ecf20Sopenharmony_ci	mach_set_rtc_pll = q40_set_rtc_pll;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	mach_reset = q40_reset;
1768c2ecf20Sopenharmony_ci	mach_get_model = q40_get_model;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT_M68K_BEEP)
1798c2ecf20Sopenharmony_ci	mach_beep = q40_mksound;
1808c2ecf20Sopenharmony_ci#endif
1818c2ecf20Sopenharmony_ci#ifdef CONFIG_HEARTBEAT
1828c2ecf20Sopenharmony_ci	mach_heartbeat = q40_heartbeat;
1838c2ecf20Sopenharmony_ci#endif
1848c2ecf20Sopenharmony_ci	mach_halt = q40_halt;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* disable a few things that SMSQ might have left enabled */
1878c2ecf20Sopenharmony_ci	q40_disable_irqs();
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/* no DMA at all, but ide-scsi requires it.. make sure
1908c2ecf20Sopenharmony_ci	 * all physical RAM fits into the boundary - otherwise
1918c2ecf20Sopenharmony_ci	 * allocator may play costly and useless tricks */
1928c2ecf20Sopenharmony_ci	mach_max_dma_address = 1024*1024*1024;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ciint __init q40_parse_bootinfo(const struct bi_record *rec)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	return 1;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/*
2028c2ecf20Sopenharmony_ci * Looks like op is non-zero for setting the clock, and zero for
2038c2ecf20Sopenharmony_ci * reading the clock.
2048c2ecf20Sopenharmony_ci *
2058c2ecf20Sopenharmony_ci *  struct hwclk_time {
2068c2ecf20Sopenharmony_ci *         unsigned        sec;       0..59
2078c2ecf20Sopenharmony_ci *         unsigned        min;       0..59
2088c2ecf20Sopenharmony_ci *         unsigned        hour;      0..23
2098c2ecf20Sopenharmony_ci *         unsigned        day;       1..31
2108c2ecf20Sopenharmony_ci *         unsigned        mon;       0..11
2118c2ecf20Sopenharmony_ci *         unsigned        year;      00...
2128c2ecf20Sopenharmony_ci *         int             wday;      0..6, 0 is Sunday, -1 means unknown/don't set
2138c2ecf20Sopenharmony_ci * };
2148c2ecf20Sopenharmony_ci */
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic int q40_hwclk(int op, struct rtc_time *t)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	if (op) {
2198c2ecf20Sopenharmony_ci		/* Write.... */
2208c2ecf20Sopenharmony_ci		Q40_RTC_CTRL |= Q40_RTC_WRITE;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci		Q40_RTC_SECS = bin2bcd(t->tm_sec);
2238c2ecf20Sopenharmony_ci		Q40_RTC_MINS = bin2bcd(t->tm_min);
2248c2ecf20Sopenharmony_ci		Q40_RTC_HOUR = bin2bcd(t->tm_hour);
2258c2ecf20Sopenharmony_ci		Q40_RTC_DATE = bin2bcd(t->tm_mday);
2268c2ecf20Sopenharmony_ci		Q40_RTC_MNTH = bin2bcd(t->tm_mon + 1);
2278c2ecf20Sopenharmony_ci		Q40_RTC_YEAR = bin2bcd(t->tm_year%100);
2288c2ecf20Sopenharmony_ci		if (t->tm_wday >= 0)
2298c2ecf20Sopenharmony_ci			Q40_RTC_DOW = bin2bcd(t->tm_wday+1);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		Q40_RTC_CTRL &= ~(Q40_RTC_WRITE);
2328c2ecf20Sopenharmony_ci	} else {
2338c2ecf20Sopenharmony_ci		/* Read....  */
2348c2ecf20Sopenharmony_ci		Q40_RTC_CTRL |= Q40_RTC_READ;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci		t->tm_year = bcd2bin (Q40_RTC_YEAR);
2378c2ecf20Sopenharmony_ci		t->tm_mon  = bcd2bin (Q40_RTC_MNTH)-1;
2388c2ecf20Sopenharmony_ci		t->tm_mday = bcd2bin (Q40_RTC_DATE);
2398c2ecf20Sopenharmony_ci		t->tm_hour = bcd2bin (Q40_RTC_HOUR);
2408c2ecf20Sopenharmony_ci		t->tm_min  = bcd2bin (Q40_RTC_MINS);
2418c2ecf20Sopenharmony_ci		t->tm_sec  = bcd2bin (Q40_RTC_SECS);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		Q40_RTC_CTRL &= ~(Q40_RTC_READ);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci		if (t->tm_year < 70)
2468c2ecf20Sopenharmony_ci			t->tm_year += 100;
2478c2ecf20Sopenharmony_ci		t->tm_wday = bcd2bin(Q40_RTC_DOW)-1;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	return 0;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic unsigned int q40_get_ss(void)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	return bcd2bin(Q40_RTC_SECS);
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci/* get and set PLL calibration of RTC clock */
2598c2ecf20Sopenharmony_ci#define Q40_RTC_PLL_MASK ((1<<5)-1)
2608c2ecf20Sopenharmony_ci#define Q40_RTC_PLL_SIGN (1<<5)
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic int q40_get_rtc_pll(struct rtc_pll_info *pll)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	int tmp = Q40_RTC_CTRL;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	pll->pll_ctrl = 0;
2678c2ecf20Sopenharmony_ci	pll->pll_value = tmp & Q40_RTC_PLL_MASK;
2688c2ecf20Sopenharmony_ci	if (tmp & Q40_RTC_PLL_SIGN)
2698c2ecf20Sopenharmony_ci		pll->pll_value = -pll->pll_value;
2708c2ecf20Sopenharmony_ci	pll->pll_max = 31;
2718c2ecf20Sopenharmony_ci	pll->pll_min = -31;
2728c2ecf20Sopenharmony_ci	pll->pll_posmult = 512;
2738c2ecf20Sopenharmony_ci	pll->pll_negmult = 256;
2748c2ecf20Sopenharmony_ci	pll->pll_clock = 125829120;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	return 0;
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic int q40_set_rtc_pll(struct rtc_pll_info *pll)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	if (!pll->pll_ctrl) {
2828c2ecf20Sopenharmony_ci		/* the docs are a bit unclear so I am doublesetting */
2838c2ecf20Sopenharmony_ci		/* RTC_WRITE here ... */
2848c2ecf20Sopenharmony_ci		int tmp = (pll->pll_value & 31) | (pll->pll_value<0 ? 32 : 0) |
2858c2ecf20Sopenharmony_ci			  Q40_RTC_WRITE;
2868c2ecf20Sopenharmony_ci		Q40_RTC_CTRL |= Q40_RTC_WRITE;
2878c2ecf20Sopenharmony_ci		Q40_RTC_CTRL = tmp;
2888c2ecf20Sopenharmony_ci		Q40_RTC_CTRL &= ~(Q40_RTC_WRITE);
2898c2ecf20Sopenharmony_ci		return 0;
2908c2ecf20Sopenharmony_ci	} else
2918c2ecf20Sopenharmony_ci		return -EINVAL;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic __init int q40_add_kbd_device(void)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct platform_device *pdev;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if (!MACH_IS_Q40)
2998c2ecf20Sopenharmony_ci		return -ENODEV;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	pdev = platform_device_register_simple("q40kbd", -1, NULL, 0);
3028c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(pdev);
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ciarch_initcall(q40_add_kbd_device);
305