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