linux/arch/m68k/q40/config.c
<<
>>
Prefs
   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/types.h>
  16#include <linux/kernel.h>
  17#include <linux/mm.h>
  18#include <linux/tty.h>
  19#include <linux/console.h>
  20#include <linux/linkage.h>
  21#include <linux/init.h>
  22#include <linux/major.h>
  23#include <linux/serial_reg.h>
  24#include <linux/rtc.h>
  25#include <linux/vt_kern.h>
  26#include <linux/bcd.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/machdep.h>
  37#include <asm/q40_master.h>
  38
  39extern void q40_init_IRQ(void);
  40static void q40_get_model(char *model);
  41extern void q40_sched_init(irq_handler_t handler);
  42
  43static unsigned long q40_gettimeoffset(void);
  44static int q40_hwclk(int, struct rtc_time *);
  45static unsigned int q40_get_ss(void);
  46static int q40_set_clock_mmss(unsigned long);
  47static int q40_get_rtc_pll(struct rtc_pll_info *pll);
  48static int q40_set_rtc_pll(struct rtc_pll_info *pll);
  49
  50extern void q40_mksound(unsigned int /*freq*/, unsigned int /*ticks*/);
  51
  52static void q40_mem_console_write(struct console *co, const char *b,
  53                                  unsigned int count);
  54
  55extern int ql_ticks;
  56
  57static struct console q40_console_driver = {
  58        .name   = "debug",
  59        .write  = q40_mem_console_write,
  60        .flags  = CON_PRINTBUFFER,
  61        .index  = -1,
  62};
  63
  64
  65/* early debugging function:*/
  66extern char *q40_mem_cptr; /*=(char *)0xff020000;*/
  67static int _cpleft;
  68
  69static void q40_mem_console_write(struct console *co, const char *s,
  70                                  unsigned int count)
  71{
  72        const char *p = s;
  73
  74        if (count < _cpleft) {
  75                while (count-- > 0) {
  76                        *q40_mem_cptr = *p++;
  77                        q40_mem_cptr += 4;
  78                        _cpleft--;
  79                }
  80        }
  81}
  82
  83static int __init q40_debug_setup(char *arg)
  84{
  85        /* useful for early debugging stages - writes kernel messages into SRAM */
  86        if (MACH_IS_Q40 && !strncmp(arg, "mem", 3)) {
  87                /*printk("using NVRAM debug, q40_mem_cptr=%p\n",q40_mem_cptr);*/
  88                _cpleft = 2000 - ((long)q40_mem_cptr-0xff020000) / 4;
  89                register_console(&q40_console_driver);
  90        }
  91        return 0;
  92}
  93
  94early_param("debug", q40_debug_setup);
  95
  96#if 0
  97void printq40(char *str)
  98{
  99        int l = strlen(str);
 100        char *p = q40_mem_cptr;
 101
 102        while (l-- > 0 && _cpleft-- > 0) {
 103                *p = *str++;
 104                p += 4;
 105        }
 106        q40_mem_cptr = p;
 107}
 108#endif
 109
 110static int halted;
 111
 112#ifdef CONFIG_HEARTBEAT
 113static void q40_heartbeat(int on)
 114{
 115        if (halted)
 116                return;
 117
 118        if (on)
 119                Q40_LED_ON();
 120        else
 121                Q40_LED_OFF();
 122}
 123#endif
 124
 125static void q40_reset(void)
 126{
 127        halted = 1;
 128        printk("\n\n*******************************************\n"
 129                "Called q40_reset : press the RESET button!! \n"
 130                "*******************************************\n");
 131        Q40_LED_ON();
 132        while (1)
 133                ;
 134}
 135
 136static void q40_halt(void)
 137{
 138        halted = 1;
 139        printk("\n\n*******************\n"
 140                   "  Called q40_halt\n"
 141                   "*******************\n");
 142        Q40_LED_ON();
 143        while (1)
 144                ;
 145}
 146
 147static void q40_get_model(char *model)
 148{
 149        sprintf(model, "Q40");
 150}
 151
 152static unsigned int serports[] =
 153{
 154        0x3f8,0x2f8,0x3e8,0x2e8,0
 155};
 156
 157static void q40_disable_irqs(void)
 158{
 159        unsigned i, j;
 160
 161        j = 0;
 162        while ((i = serports[j++]))
 163                outb(0, i + UART_IER);
 164        master_outb(0, EXT_ENABLE_REG);
 165        master_outb(0, KEY_IRQ_ENABLE_REG);
 166}
 167
 168void __init config_q40(void)
 169{
 170        mach_sched_init = q40_sched_init;
 171
 172        mach_init_IRQ = q40_init_IRQ;
 173        mach_gettimeoffset = q40_gettimeoffset;
 174        mach_hwclk = q40_hwclk;
 175        mach_get_ss = q40_get_ss;
 176        mach_get_rtc_pll = q40_get_rtc_pll;
 177        mach_set_rtc_pll = q40_set_rtc_pll;
 178        mach_set_clock_mmss = q40_set_clock_mmss;
 179
 180        mach_reset = q40_reset;
 181        mach_get_model = q40_get_model;
 182
 183#if defined(CONFIG_INPUT_M68K_BEEP) || defined(CONFIG_INPUT_M68K_BEEP_MODULE)
 184        mach_beep = q40_mksound;
 185#endif
 186#ifdef CONFIG_HEARTBEAT
 187        mach_heartbeat = q40_heartbeat;
 188#endif
 189        mach_halt = q40_halt;
 190
 191        /* disable a few things that SMSQ might have left enabled */
 192        q40_disable_irqs();
 193
 194        /* no DMA at all, but ide-scsi requires it.. make sure
 195         * all physical RAM fits into the boundary - otherwise
 196         * allocator may play costly and useless tricks */
 197        mach_max_dma_address = 1024*1024*1024;
 198}
 199
 200
 201int q40_parse_bootinfo(const struct bi_record *rec)
 202{
 203        return 1;
 204}
 205
 206
 207static unsigned long q40_gettimeoffset(void)
 208{
 209        return 5000 * (ql_ticks != 0);
 210}
 211
 212
 213/*
 214 * Looks like op is non-zero for setting the clock, and zero for
 215 * reading the clock.
 216 *
 217 *  struct hwclk_time {
 218 *         unsigned        sec;       0..59
 219 *         unsigned        min;       0..59
 220 *         unsigned        hour;      0..23
 221 *         unsigned        day;       1..31
 222 *         unsigned        mon;       0..11
 223 *         unsigned        year;      00...
 224 *         int             wday;      0..6, 0 is Sunday, -1 means unknown/don't set
 225 * };
 226 */
 227
 228static int q40_hwclk(int op, struct rtc_time *t)
 229{
 230        if (op) {
 231                /* Write.... */
 232                Q40_RTC_CTRL |= Q40_RTC_WRITE;
 233
 234                Q40_RTC_SECS = bin2bcd(t->tm_sec);
 235                Q40_RTC_MINS = bin2bcd(t->tm_min);
 236                Q40_RTC_HOUR = bin2bcd(t->tm_hour);
 237                Q40_RTC_DATE = bin2bcd(t->tm_mday);
 238                Q40_RTC_MNTH = bin2bcd(t->tm_mon + 1);
 239                Q40_RTC_YEAR = bin2bcd(t->tm_year%100);
 240                if (t->tm_wday >= 0)
 241                        Q40_RTC_DOW = bin2bcd(t->tm_wday+1);
 242
 243                Q40_RTC_CTRL &= ~(Q40_RTC_WRITE);
 244        } else {
 245                /* Read....  */
 246                Q40_RTC_CTRL |= Q40_RTC_READ;
 247
 248                t->tm_year = bcd2bin (Q40_RTC_YEAR);
 249                t->tm_mon  = bcd2bin (Q40_RTC_MNTH)-1;
 250                t->tm_mday = bcd2bin (Q40_RTC_DATE);
 251                t->tm_hour = bcd2bin (Q40_RTC_HOUR);
 252                t->tm_min  = bcd2bin (Q40_RTC_MINS);
 253                t->tm_sec  = bcd2bin (Q40_RTC_SECS);
 254
 255                Q40_RTC_CTRL &= ~(Q40_RTC_READ);
 256
 257                if (t->tm_year < 70)
 258                        t->tm_year += 100;
 259                t->tm_wday = bcd2bin(Q40_RTC_DOW)-1;
 260        }
 261
 262        return 0;
 263}
 264
 265static unsigned int q40_get_ss(void)
 266{
 267        return bcd2bin(Q40_RTC_SECS);
 268}
 269
 270/*
 271 * Set the minutes and seconds from seconds value 'nowtime'.  Fail if
 272 * clock is out by > 30 minutes.  Logic lifted from atari code.
 273 */
 274
 275static int q40_set_clock_mmss(unsigned long nowtime)
 276{
 277        int retval = 0;
 278        short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
 279
 280        int rtc_minutes;
 281
 282        rtc_minutes = bcd2bin(Q40_RTC_MINS);
 283
 284        if ((rtc_minutes < real_minutes ?
 285             real_minutes - rtc_minutes :
 286             rtc_minutes - real_minutes) < 30) {
 287                Q40_RTC_CTRL |= Q40_RTC_WRITE;
 288                Q40_RTC_MINS = bin2bcd(real_minutes);
 289                Q40_RTC_SECS = bin2bcd(real_seconds);
 290                Q40_RTC_CTRL &= ~(Q40_RTC_WRITE);
 291        } else
 292                retval = -1;
 293
 294        return retval;
 295}
 296
 297
 298/* get and set PLL calibration of RTC clock */
 299#define Q40_RTC_PLL_MASK ((1<<5)-1)
 300#define Q40_RTC_PLL_SIGN (1<<5)
 301
 302static int q40_get_rtc_pll(struct rtc_pll_info *pll)
 303{
 304        int tmp = Q40_RTC_CTRL;
 305
 306        pll->pll_value = tmp & Q40_RTC_PLL_MASK;
 307        if (tmp & Q40_RTC_PLL_SIGN)
 308                pll->pll_value = -pll->pll_value;
 309        pll->pll_max = 31;
 310        pll->pll_min = -31;
 311        pll->pll_posmult = 512;
 312        pll->pll_negmult = 256;
 313        pll->pll_clock = 125829120;
 314
 315        return 0;
 316}
 317
 318static int q40_set_rtc_pll(struct rtc_pll_info *pll)
 319{
 320        if (!pll->pll_ctrl) {
 321                /* the docs are a bit unclear so I am doublesetting */
 322                /* RTC_WRITE here ... */
 323                int tmp = (pll->pll_value & 31) | (pll->pll_value<0 ? 32 : 0) |
 324                          Q40_RTC_WRITE;
 325                Q40_RTC_CTRL |= Q40_RTC_WRITE;
 326                Q40_RTC_CTRL = tmp;
 327                Q40_RTC_CTRL &= ~(Q40_RTC_WRITE);
 328                return 0;
 329        } else
 330                return -EINVAL;
 331}
 332