linux/arch/m68k/atari/time.c
<<
>>
Prefs
   1/*
   2 * linux/arch/m68k/atari/time.c
   3 *
   4 * Atari time and real time clock stuff
   5 *
   6 * Assembled of parts of former atari/config.c 97-12-18 by Roman Hodek
   7 *
   8 * This file is subject to the terms and conditions of the GNU General Public
   9 * License.  See the file COPYING in the main directory of this archive
  10 * for more details.
  11 */
  12
  13#include <linux/types.h>
  14#include <linux/mc146818rtc.h>
  15#include <linux/interrupt.h>
  16#include <linux/init.h>
  17#include <linux/rtc.h>
  18#include <linux/bcd.h>
  19#include <linux/delay.h>
  20
  21#include <asm/atariints.h>
  22
  23DEFINE_SPINLOCK(rtc_lock);
  24EXPORT_SYMBOL_GPL(rtc_lock);
  25
  26void __init
  27atari_sched_init(irq_handler_t timer_routine)
  28{
  29    /* set Timer C data Register */
  30    st_mfp.tim_dt_c = INT_TICKS;
  31    /* start timer C, div = 1:100 */
  32    st_mfp.tim_ct_cd = (st_mfp.tim_ct_cd & 15) | 0x60;
  33    /* install interrupt service routine for MFP Timer C */
  34    if (request_irq(IRQ_MFP_TIMC, timer_routine, IRQ_TYPE_SLOW,
  35                    "timer", timer_routine))
  36        pr_err("Couldn't register timer interrupt\n");
  37}
  38
  39/* ++andreas: gettimeoffset fixed to check for pending interrupt */
  40
  41#define TICK_SIZE 10000
  42
  43/* This is always executed with interrupts disabled.  */
  44unsigned long atari_gettimeoffset (void)
  45{
  46  unsigned long ticks, offset = 0;
  47
  48  /* read MFP timer C current value */
  49  ticks = st_mfp.tim_dt_c;
  50  /* The probability of underflow is less than 2% */
  51  if (ticks > INT_TICKS - INT_TICKS / 50)
  52    /* Check for pending timer interrupt */
  53    if (st_mfp.int_pn_b & (1 << 5))
  54      offset = TICK_SIZE;
  55
  56  ticks = INT_TICKS - ticks;
  57  ticks = ticks * 10000L / INT_TICKS;
  58
  59  return ticks + offset;
  60}
  61
  62
  63static void mste_read(struct MSTE_RTC *val)
  64{
  65#define COPY(v) val->v=(mste_rtc.v & 0xf)
  66        do {
  67                COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ;
  68                COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ;
  69                COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ;
  70                COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ;
  71                COPY(year_tens) ;
  72        /* prevent from reading the clock while it changed */
  73        } while (val->sec_ones != (mste_rtc.sec_ones & 0xf));
  74#undef COPY
  75}
  76
  77static void mste_write(struct MSTE_RTC *val)
  78{
  79#define COPY(v) mste_rtc.v=val->v
  80        do {
  81                COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ;
  82                COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ;
  83                COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ;
  84                COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ;
  85                COPY(year_tens) ;
  86        /* prevent from writing the clock while it changed */
  87        } while (val->sec_ones != (mste_rtc.sec_ones & 0xf));
  88#undef COPY
  89}
  90
  91#define RTC_READ(reg)                           \
  92    ({  unsigned char   __val;                  \
  93                (void) atari_writeb(reg,&tt_rtc.regsel);        \
  94                __val = tt_rtc.data;            \
  95                __val;                          \
  96        })
  97
  98#define RTC_WRITE(reg,val)                      \
  99    do {                                        \
 100                atari_writeb(reg,&tt_rtc.regsel);       \
 101                tt_rtc.data = (val);            \
 102        } while(0)
 103
 104
 105#define HWCLK_POLL_INTERVAL     5
 106
 107int atari_mste_hwclk( int op, struct rtc_time *t )
 108{
 109    int hour, year;
 110    int hr24=0;
 111    struct MSTE_RTC val;
 112
 113    mste_rtc.mode=(mste_rtc.mode | 1);
 114    hr24=mste_rtc.mon_tens & 1;
 115    mste_rtc.mode=(mste_rtc.mode & ~1);
 116
 117    if (op) {
 118        /* write: prepare values */
 119
 120        val.sec_ones = t->tm_sec % 10;
 121        val.sec_tens = t->tm_sec / 10;
 122        val.min_ones = t->tm_min % 10;
 123        val.min_tens = t->tm_min / 10;
 124        hour = t->tm_hour;
 125        if (!hr24) {
 126            if (hour > 11)
 127                hour += 20 - 12;
 128            if (hour == 0 || hour == 20)
 129                hour += 12;
 130        }
 131        val.hr_ones = hour % 10;
 132        val.hr_tens = hour / 10;
 133        val.day_ones = t->tm_mday % 10;
 134        val.day_tens = t->tm_mday / 10;
 135        val.mon_ones = (t->tm_mon+1) % 10;
 136        val.mon_tens = (t->tm_mon+1) / 10;
 137        year = t->tm_year - 80;
 138        val.year_ones = year % 10;
 139        val.year_tens = year / 10;
 140        val.weekday = t->tm_wday;
 141        mste_write(&val);
 142        mste_rtc.mode=(mste_rtc.mode | 1);
 143        val.year_ones = (year % 4);     /* leap year register */
 144        mste_rtc.mode=(mste_rtc.mode & ~1);
 145    }
 146    else {
 147        mste_read(&val);
 148        t->tm_sec = val.sec_ones + val.sec_tens * 10;
 149        t->tm_min = val.min_ones + val.min_tens * 10;
 150        hour = val.hr_ones + val.hr_tens * 10;
 151        if (!hr24) {
 152            if (hour == 12 || hour == 12 + 20)
 153                hour -= 12;
 154            if (hour >= 20)
 155                hour += 12 - 20;
 156        }
 157        t->tm_hour = hour;
 158        t->tm_mday = val.day_ones + val.day_tens * 10;
 159        t->tm_mon  = val.mon_ones + val.mon_tens * 10 - 1;
 160        t->tm_year = val.year_ones + val.year_tens * 10 + 80;
 161        t->tm_wday = val.weekday;
 162    }
 163    return 0;
 164}
 165
 166int atari_tt_hwclk( int op, struct rtc_time *t )
 167{
 168    int sec=0, min=0, hour=0, day=0, mon=0, year=0, wday=0;
 169    unsigned long       flags;
 170    unsigned char       ctrl;
 171    int pm = 0;
 172
 173    ctrl = RTC_READ(RTC_CONTROL); /* control registers are
 174                                   * independent from the UIP */
 175
 176    if (op) {
 177        /* write: prepare values */
 178
 179        sec  = t->tm_sec;
 180        min  = t->tm_min;
 181        hour = t->tm_hour;
 182        day  = t->tm_mday;
 183        mon  = t->tm_mon + 1;
 184        year = t->tm_year - atari_rtc_year_offset;
 185        wday = t->tm_wday + (t->tm_wday >= 0);
 186
 187        if (!(ctrl & RTC_24H)) {
 188            if (hour > 11) {
 189                pm = 0x80;
 190                if (hour != 12)
 191                    hour -= 12;
 192            }
 193            else if (hour == 0)
 194                hour = 12;
 195        }
 196
 197        if (!(ctrl & RTC_DM_BINARY)) {
 198            sec = bin2bcd(sec);
 199            min = bin2bcd(min);
 200            hour = bin2bcd(hour);
 201            day = bin2bcd(day);
 202            mon = bin2bcd(mon);
 203            year = bin2bcd(year);
 204            if (wday >= 0)
 205                wday = bin2bcd(wday);
 206        }
 207    }
 208
 209    /* Reading/writing the clock registers is a bit critical due to
 210     * the regular update cycle of the RTC. While an update is in
 211     * progress, registers 0..9 shouldn't be touched.
 212     * The problem is solved like that: If an update is currently in
 213     * progress (the UIP bit is set), the process sleeps for a while
 214     * (50ms). This really should be enough, since the update cycle
 215     * normally needs 2 ms.
 216     * If the UIP bit reads as 0, we have at least 244 usecs until the
 217     * update starts. This should be enough... But to be sure,
 218     * additionally the RTC_SET bit is set to prevent an update cycle.
 219     */
 220
 221    while( RTC_READ(RTC_FREQ_SELECT) & RTC_UIP ) {
 222        if (in_atomic() || irqs_disabled())
 223            mdelay(1);
 224        else
 225            schedule_timeout_interruptible(HWCLK_POLL_INTERVAL);
 226    }
 227
 228    local_irq_save(flags);
 229    RTC_WRITE( RTC_CONTROL, ctrl | RTC_SET );
 230    if (!op) {
 231        sec  = RTC_READ( RTC_SECONDS );
 232        min  = RTC_READ( RTC_MINUTES );
 233        hour = RTC_READ( RTC_HOURS );
 234        day  = RTC_READ( RTC_DAY_OF_MONTH );
 235        mon  = RTC_READ( RTC_MONTH );
 236        year = RTC_READ( RTC_YEAR );
 237        wday = RTC_READ( RTC_DAY_OF_WEEK );
 238    }
 239    else {
 240        RTC_WRITE( RTC_SECONDS, sec );
 241        RTC_WRITE( RTC_MINUTES, min );
 242        RTC_WRITE( RTC_HOURS, hour + pm);
 243        RTC_WRITE( RTC_DAY_OF_MONTH, day );
 244        RTC_WRITE( RTC_MONTH, mon );
 245        RTC_WRITE( RTC_YEAR, year );
 246        if (wday >= 0) RTC_WRITE( RTC_DAY_OF_WEEK, wday );
 247    }
 248    RTC_WRITE( RTC_CONTROL, ctrl & ~RTC_SET );
 249    local_irq_restore(flags);
 250
 251    if (!op) {
 252        /* read: adjust values */
 253
 254        if (hour & 0x80) {
 255            hour &= ~0x80;
 256            pm = 1;
 257        }
 258
 259        if (!(ctrl & RTC_DM_BINARY)) {
 260            sec = bcd2bin(sec);
 261            min = bcd2bin(min);
 262            hour = bcd2bin(hour);
 263            day = bcd2bin(day);
 264            mon = bcd2bin(mon);
 265            year = bcd2bin(year);
 266            wday = bcd2bin(wday);
 267        }
 268
 269        if (!(ctrl & RTC_24H)) {
 270            if (!pm && hour == 12)
 271                hour = 0;
 272            else if (pm && hour != 12)
 273                hour += 12;
 274        }
 275
 276        t->tm_sec  = sec;
 277        t->tm_min  = min;
 278        t->tm_hour = hour;
 279        t->tm_mday = day;
 280        t->tm_mon  = mon - 1;
 281        t->tm_year = year + atari_rtc_year_offset;
 282        t->tm_wday = wday - 1;
 283    }
 284
 285    return( 0 );
 286}
 287
 288
 289int atari_mste_set_clock_mmss (unsigned long nowtime)
 290{
 291    short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
 292    struct MSTE_RTC val;
 293    unsigned char rtc_minutes;
 294
 295    mste_read(&val);
 296    rtc_minutes= val.min_ones + val.min_tens * 10;
 297    if ((rtc_minutes < real_minutes
 298         ? real_minutes - rtc_minutes
 299         : rtc_minutes - real_minutes) < 30)
 300    {
 301        val.sec_ones = real_seconds % 10;
 302        val.sec_tens = real_seconds / 10;
 303        val.min_ones = real_minutes % 10;
 304        val.min_tens = real_minutes / 10;
 305        mste_write(&val);
 306    }
 307    else
 308        return -1;
 309    return 0;
 310}
 311
 312int atari_tt_set_clock_mmss (unsigned long nowtime)
 313{
 314    int retval = 0;
 315    short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
 316    unsigned char save_control, save_freq_select, rtc_minutes;
 317
 318    save_control = RTC_READ (RTC_CONTROL); /* tell the clock it's being set */
 319    RTC_WRITE (RTC_CONTROL, save_control | RTC_SET);
 320
 321    save_freq_select = RTC_READ (RTC_FREQ_SELECT); /* stop and reset prescaler */
 322    RTC_WRITE (RTC_FREQ_SELECT, save_freq_select | RTC_DIV_RESET2);
 323
 324    rtc_minutes = RTC_READ (RTC_MINUTES);
 325    if (!(save_control & RTC_DM_BINARY))
 326        rtc_minutes = bcd2bin(rtc_minutes);
 327
 328    /* Since we're only adjusting minutes and seconds, don't interfere
 329       with hour overflow.  This avoids messing with unknown time zones
 330       but requires your RTC not to be off by more than 30 minutes.  */
 331    if ((rtc_minutes < real_minutes
 332         ? real_minutes - rtc_minutes
 333         : rtc_minutes - real_minutes) < 30)
 334        {
 335            if (!(save_control & RTC_DM_BINARY))
 336                {
 337                    real_seconds = bin2bcd(real_seconds);
 338                    real_minutes = bin2bcd(real_minutes);
 339                }
 340            RTC_WRITE (RTC_SECONDS, real_seconds);
 341            RTC_WRITE (RTC_MINUTES, real_minutes);
 342        }
 343    else
 344        retval = -1;
 345
 346    RTC_WRITE (RTC_FREQ_SELECT, save_freq_select);
 347    RTC_WRITE (RTC_CONTROL, save_control);
 348    return retval;
 349}
 350
 351/*
 352 * Local variables:
 353 *  c-indent-level: 4
 354 *  tab-width: 8
 355 * End:
 356 */
 357