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