162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * arch/m68k/q40/config.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 1999 Richard Zidlicky 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * originally based on: 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * linux/bvme/config.c 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 1162306a36Sopenharmony_ci * License. See the file README.legal in the main directory of this archive 1262306a36Sopenharmony_ci * for more details. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/errno.h> 1662306a36Sopenharmony_ci#include <linux/types.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/mm.h> 1962306a36Sopenharmony_ci#include <linux/tty.h> 2062306a36Sopenharmony_ci#include <linux/console.h> 2162306a36Sopenharmony_ci#include <linux/linkage.h> 2262306a36Sopenharmony_ci#include <linux/init.h> 2362306a36Sopenharmony_ci#include <linux/major.h> 2462306a36Sopenharmony_ci#include <linux/serial_reg.h> 2562306a36Sopenharmony_ci#include <linux/rtc.h> 2662306a36Sopenharmony_ci#include <linux/vt_kern.h> 2762306a36Sopenharmony_ci#include <linux/bcd.h> 2862306a36Sopenharmony_ci#include <linux/platform_device.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <asm/io.h> 3162306a36Sopenharmony_ci#include <asm/bootinfo.h> 3262306a36Sopenharmony_ci#include <asm/setup.h> 3362306a36Sopenharmony_ci#include <asm/irq.h> 3462306a36Sopenharmony_ci#include <asm/traps.h> 3562306a36Sopenharmony_ci#include <asm/machdep.h> 3662306a36Sopenharmony_ci#include <asm/q40_master.h> 3762306a36Sopenharmony_ci#include <asm/config.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ciextern void q40_init_IRQ(void); 4062306a36Sopenharmony_cistatic void q40_get_model(char *model); 4162306a36Sopenharmony_ciextern void q40_sched_init(void); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int q40_hwclk(int, struct rtc_time *); 4462306a36Sopenharmony_cistatic int q40_get_rtc_pll(struct rtc_pll_info *pll); 4562306a36Sopenharmony_cistatic int q40_set_rtc_pll(struct rtc_pll_info *pll); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ciextern void q40_mksound(unsigned int /*freq*/, unsigned int /*ticks*/); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void q40_mem_console_write(struct console *co, const char *b, 5062306a36Sopenharmony_ci unsigned int count); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ciextern int ql_ticks; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic struct console q40_console_driver = { 5562306a36Sopenharmony_ci .name = "debug", 5662306a36Sopenharmony_ci .write = q40_mem_console_write, 5762306a36Sopenharmony_ci .flags = CON_PRINTBUFFER, 5862306a36Sopenharmony_ci .index = -1, 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* early debugging function:*/ 6362306a36Sopenharmony_ciextern char *q40_mem_cptr; /*=(char *)0xff020000;*/ 6462306a36Sopenharmony_cistatic int _cpleft; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void q40_mem_console_write(struct console *co, const char *s, 6762306a36Sopenharmony_ci unsigned int count) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci const char *p = s; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (count < _cpleft) { 7262306a36Sopenharmony_ci while (count-- > 0) { 7362306a36Sopenharmony_ci *q40_mem_cptr = *p++; 7462306a36Sopenharmony_ci q40_mem_cptr += 4; 7562306a36Sopenharmony_ci _cpleft--; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic int __init q40_debug_setup(char *arg) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci /* useful for early debugging stages - writes kernel messages into SRAM */ 8362306a36Sopenharmony_ci if (MACH_IS_Q40 && !strncmp(arg, "mem", 3)) { 8462306a36Sopenharmony_ci /*pr_info("using NVRAM debug, q40_mem_cptr=%p\n",q40_mem_cptr);*/ 8562306a36Sopenharmony_ci _cpleft = 2000 - ((long)q40_mem_cptr-0xff020000) / 4; 8662306a36Sopenharmony_ci register_console(&q40_console_driver); 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ciearly_param("debug", q40_debug_setup); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#if 0 9462306a36Sopenharmony_civoid printq40(char *str) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci int l = strlen(str); 9762306a36Sopenharmony_ci char *p = q40_mem_cptr; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci while (l-- > 0 && _cpleft-- > 0) { 10062306a36Sopenharmony_ci *p = *str++; 10162306a36Sopenharmony_ci p += 4; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci q40_mem_cptr = p; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci#endif 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int halted; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#ifdef CONFIG_HEARTBEAT 11062306a36Sopenharmony_cistatic void q40_heartbeat(int on) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci if (halted) 11362306a36Sopenharmony_ci return; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (on) 11662306a36Sopenharmony_ci Q40_LED_ON(); 11762306a36Sopenharmony_ci else 11862306a36Sopenharmony_ci Q40_LED_OFF(); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci#endif 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void q40_reset(void) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci halted = 1; 12562306a36Sopenharmony_ci pr_info("*******************************************\n" 12662306a36Sopenharmony_ci "Called q40_reset : press the RESET button!!\n" 12762306a36Sopenharmony_ci "*******************************************\n"); 12862306a36Sopenharmony_ci Q40_LED_ON(); 12962306a36Sopenharmony_ci while (1) 13062306a36Sopenharmony_ci ; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void q40_halt(void) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci halted = 1; 13662306a36Sopenharmony_ci pr_info("*******************\n" 13762306a36Sopenharmony_ci " Called q40_halt\n" 13862306a36Sopenharmony_ci "*******************\n"); 13962306a36Sopenharmony_ci Q40_LED_ON(); 14062306a36Sopenharmony_ci while (1) 14162306a36Sopenharmony_ci ; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void q40_get_model(char *model) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci sprintf(model, "Q40"); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic unsigned int serports[] = 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci 0x3f8,0x2f8,0x3e8,0x2e8,0 15262306a36Sopenharmony_ci}; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void __init q40_disable_irqs(void) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci unsigned i, j; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci j = 0; 15962306a36Sopenharmony_ci while ((i = serports[j++])) 16062306a36Sopenharmony_ci outb(0, i + UART_IER); 16162306a36Sopenharmony_ci master_outb(0, EXT_ENABLE_REG); 16262306a36Sopenharmony_ci master_outb(0, KEY_IRQ_ENABLE_REG); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_civoid __init config_q40(void) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci mach_sched_init = q40_sched_init; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci mach_init_IRQ = q40_init_IRQ; 17062306a36Sopenharmony_ci mach_hwclk = q40_hwclk; 17162306a36Sopenharmony_ci mach_get_rtc_pll = q40_get_rtc_pll; 17262306a36Sopenharmony_ci mach_set_rtc_pll = q40_set_rtc_pll; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci mach_reset = q40_reset; 17562306a36Sopenharmony_ci mach_get_model = q40_get_model; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT_M68K_BEEP) 17862306a36Sopenharmony_ci mach_beep = q40_mksound; 17962306a36Sopenharmony_ci#endif 18062306a36Sopenharmony_ci#ifdef CONFIG_HEARTBEAT 18162306a36Sopenharmony_ci mach_heartbeat = q40_heartbeat; 18262306a36Sopenharmony_ci#endif 18362306a36Sopenharmony_ci mach_halt = q40_halt; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* disable a few things that SMSQ might have left enabled */ 18662306a36Sopenharmony_ci q40_disable_irqs(); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ciint __init q40_parse_bootinfo(const struct bi_record *rec) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci return 1; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/* 19662306a36Sopenharmony_ci * Looks like op is non-zero for setting the clock, and zero for 19762306a36Sopenharmony_ci * reading the clock. 19862306a36Sopenharmony_ci * 19962306a36Sopenharmony_ci * struct hwclk_time { 20062306a36Sopenharmony_ci * unsigned sec; 0..59 20162306a36Sopenharmony_ci * unsigned min; 0..59 20262306a36Sopenharmony_ci * unsigned hour; 0..23 20362306a36Sopenharmony_ci * unsigned day; 1..31 20462306a36Sopenharmony_ci * unsigned mon; 0..11 20562306a36Sopenharmony_ci * unsigned year; 00... 20662306a36Sopenharmony_ci * int wday; 0..6, 0 is Sunday, -1 means unknown/don't set 20762306a36Sopenharmony_ci * }; 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int q40_hwclk(int op, struct rtc_time *t) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci if (op) { 21362306a36Sopenharmony_ci /* Write.... */ 21462306a36Sopenharmony_ci Q40_RTC_CTRL |= Q40_RTC_WRITE; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci Q40_RTC_SECS = bin2bcd(t->tm_sec); 21762306a36Sopenharmony_ci Q40_RTC_MINS = bin2bcd(t->tm_min); 21862306a36Sopenharmony_ci Q40_RTC_HOUR = bin2bcd(t->tm_hour); 21962306a36Sopenharmony_ci Q40_RTC_DATE = bin2bcd(t->tm_mday); 22062306a36Sopenharmony_ci Q40_RTC_MNTH = bin2bcd(t->tm_mon + 1); 22162306a36Sopenharmony_ci Q40_RTC_YEAR = bin2bcd(t->tm_year%100); 22262306a36Sopenharmony_ci if (t->tm_wday >= 0) 22362306a36Sopenharmony_ci Q40_RTC_DOW = bin2bcd(t->tm_wday+1); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci Q40_RTC_CTRL &= ~(Q40_RTC_WRITE); 22662306a36Sopenharmony_ci } else { 22762306a36Sopenharmony_ci /* Read.... */ 22862306a36Sopenharmony_ci Q40_RTC_CTRL |= Q40_RTC_READ; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci t->tm_year = bcd2bin (Q40_RTC_YEAR); 23162306a36Sopenharmony_ci t->tm_mon = bcd2bin (Q40_RTC_MNTH)-1; 23262306a36Sopenharmony_ci t->tm_mday = bcd2bin (Q40_RTC_DATE); 23362306a36Sopenharmony_ci t->tm_hour = bcd2bin (Q40_RTC_HOUR); 23462306a36Sopenharmony_ci t->tm_min = bcd2bin (Q40_RTC_MINS); 23562306a36Sopenharmony_ci t->tm_sec = bcd2bin (Q40_RTC_SECS); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci Q40_RTC_CTRL &= ~(Q40_RTC_READ); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (t->tm_year < 70) 24062306a36Sopenharmony_ci t->tm_year += 100; 24162306a36Sopenharmony_ci t->tm_wday = bcd2bin(Q40_RTC_DOW)-1; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return 0; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/* get and set PLL calibration of RTC clock */ 24862306a36Sopenharmony_ci#define Q40_RTC_PLL_MASK ((1<<5)-1) 24962306a36Sopenharmony_ci#define Q40_RTC_PLL_SIGN (1<<5) 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int q40_get_rtc_pll(struct rtc_pll_info *pll) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci int tmp = Q40_RTC_CTRL; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci pll->pll_ctrl = 0; 25662306a36Sopenharmony_ci pll->pll_value = tmp & Q40_RTC_PLL_MASK; 25762306a36Sopenharmony_ci if (tmp & Q40_RTC_PLL_SIGN) 25862306a36Sopenharmony_ci pll->pll_value = -pll->pll_value; 25962306a36Sopenharmony_ci pll->pll_max = 31; 26062306a36Sopenharmony_ci pll->pll_min = -31; 26162306a36Sopenharmony_ci pll->pll_posmult = 512; 26262306a36Sopenharmony_ci pll->pll_negmult = 256; 26362306a36Sopenharmony_ci pll->pll_clock = 125829120; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic int q40_set_rtc_pll(struct rtc_pll_info *pll) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci if (!pll->pll_ctrl) { 27162306a36Sopenharmony_ci /* the docs are a bit unclear so I am doublesetting */ 27262306a36Sopenharmony_ci /* RTC_WRITE here ... */ 27362306a36Sopenharmony_ci int tmp = (pll->pll_value & 31) | (pll->pll_value<0 ? 32 : 0) | 27462306a36Sopenharmony_ci Q40_RTC_WRITE; 27562306a36Sopenharmony_ci Q40_RTC_CTRL |= Q40_RTC_WRITE; 27662306a36Sopenharmony_ci Q40_RTC_CTRL = tmp; 27762306a36Sopenharmony_ci Q40_RTC_CTRL &= ~(Q40_RTC_WRITE); 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci } else 28062306a36Sopenharmony_ci return -EINVAL; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci#define PCIDE_BASE1 0x1f0 28462306a36Sopenharmony_ci#define PCIDE_BASE2 0x170 28562306a36Sopenharmony_ci#define PCIDE_CTL 0x206 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic const struct resource q40_pata_rsrc_0[] __initconst = { 28862306a36Sopenharmony_ci DEFINE_RES_MEM(q40_isa_io_base + PCIDE_BASE1 * 4, 0x38), 28962306a36Sopenharmony_ci DEFINE_RES_MEM(q40_isa_io_base + (PCIDE_BASE1 + PCIDE_CTL) * 4, 2), 29062306a36Sopenharmony_ci DEFINE_RES_IO(PCIDE_BASE1, 8), 29162306a36Sopenharmony_ci DEFINE_RES_IO(PCIDE_BASE1 + PCIDE_CTL, 1), 29262306a36Sopenharmony_ci DEFINE_RES_IRQ(14), 29362306a36Sopenharmony_ci}; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic const struct resource q40_pata_rsrc_1[] __initconst = { 29662306a36Sopenharmony_ci DEFINE_RES_MEM(q40_isa_io_base + PCIDE_BASE2 * 4, 0x38), 29762306a36Sopenharmony_ci DEFINE_RES_MEM(q40_isa_io_base + (PCIDE_BASE2 + PCIDE_CTL) * 4, 2), 29862306a36Sopenharmony_ci DEFINE_RES_IO(PCIDE_BASE2, 8), 29962306a36Sopenharmony_ci DEFINE_RES_IO(PCIDE_BASE2 + PCIDE_CTL, 1), 30062306a36Sopenharmony_ci DEFINE_RES_IRQ(15), 30162306a36Sopenharmony_ci}; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic __init int q40_platform_init(void) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci if (!MACH_IS_Q40) 30662306a36Sopenharmony_ci return -ENODEV; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci platform_device_register_simple("q40kbd", -1, NULL, 0); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci platform_device_register_simple("atari-falcon-ide", 0, q40_pata_rsrc_0, 31162306a36Sopenharmony_ci ARRAY_SIZE(q40_pata_rsrc_0)); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci platform_device_register_simple("atari-falcon-ide", 1, q40_pata_rsrc_1, 31462306a36Sopenharmony_ci ARRAY_SIZE(q40_pata_rsrc_1)); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return 0; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ciarch_initcall(q40_platform_init); 319