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/clocksource.h>
  20#include <linux/delay.h>
  21#include <linux/export.h>
  22
  23#include <asm/atariints.h>
  24#include <asm/machdep.h>
  25
  26DEFINE_SPINLOCK(rtc_lock);
  27EXPORT_SYMBOL_GPL(rtc_lock);
  28
  29static u64 atari_read_clk(struct clocksource *cs);
  30
  31static struct clocksource atari_clk = {
  32        .name   = "mfp",
  33        .rating = 100,
  34        .read   = atari_read_clk,
  35        .mask   = CLOCKSOURCE_MASK(32),
  36        .flags  = CLOCK_SOURCE_IS_CONTINUOUS,
  37};
  38
  39static u32 clk_total;
  40static u8 last_timer_count;
  41
  42static irqreturn_t mfp_timer_c_handler(int irq, void *dev_id)
  43{
  44        unsigned long flags;
  45
  46        local_irq_save(flags);
  47        do {
  48                last_timer_count = st_mfp.tim_dt_c;
  49        } while (last_timer_count == 1);
  50        clk_total += INT_TICKS;
  51        legacy_timer_tick(1);
  52        timer_heartbeat();
  53        local_irq_restore(flags);
  54
  55        return IRQ_HANDLED;
  56}
  57
  58void __init
  59atari_sched_init(void)
  60{
  61    /* set Timer C data Register */
  62    st_mfp.tim_dt_c = INT_TICKS;
  63    /* start timer C, div = 1:100 */
  64    st_mfp.tim_ct_cd = (st_mfp.tim_ct_cd & 15) | 0x60;
  65    /* install interrupt service routine for MFP Timer C */
  66    if (request_irq(IRQ_MFP_TIMC, mfp_timer_c_handler, IRQF_TIMER, "timer",
  67                    NULL))
  68        pr_err("Couldn't register timer interrupt\n");
  69
  70    clocksource_register_hz(&atari_clk, INT_CLK);
  71}
  72
  73/* ++andreas: gettimeoffset fixed to check for pending interrupt */
  74
  75static u64 atari_read_clk(struct clocksource *cs)
  76{
  77        unsigned long flags;
  78        u8 count;
  79        u32 ticks;
  80
  81        local_irq_save(flags);
  82        /* Ensure that the count is monotonically decreasing, even though
  83         * the result may briefly stop changing after counter wrap-around.
  84         */
  85        count = min(st_mfp.tim_dt_c, last_timer_count);
  86        last_timer_count = count;
  87
  88        ticks = INT_TICKS - count;
  89        ticks += clk_total;
  90        local_irq_restore(flags);
  91
  92        return ticks;
  93}
  94
  95
  96static void mste_read(struct MSTE_RTC *val)
  97{
  98#define COPY(v) val->v=(mste_rtc.v & 0xf)
  99        do {
 100                COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ;
 101                COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ;
 102                COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ;
 103                COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ;
 104                COPY(year_tens) ;
 105        /* prevent from reading the clock while it changed */
 106        } while (val->sec_ones != (mste_rtc.sec_ones & 0xf));
 107#undef COPY
 108}
 109
 110static void mste_write(struct MSTE_RTC *val)
 111{
 112#define COPY(v) mste_rtc.v=val->v
 113        do {
 114                COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ;
 115                COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ;
 116                COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ;
 117                COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ;
 118                COPY(year_tens) ;
 119        /* prevent from writing the clock while it changed */
 120        } while (val->sec_ones != (mste_rtc.sec_ones & 0xf));
 121#undef COPY
 122}
 123
 124#define RTC_READ(reg)                           \
 125    ({  unsigned char   __val;                  \
 126                (void) atari_writeb(reg,&tt_rtc.regsel);        \
 127                __val = tt_rtc.data;            \
 128                __val;                          \
 129        })
 130
 131#define RTC_WRITE(reg,val)                      \
 132    do {                                        \
 133                atari_writeb(reg,&tt_rtc.regsel);       \
 134                tt_rtc.data = (val);            \
 135        } while(0)
 136
 137
 138#define HWCLK_POLL_INTERVAL     5
 139
 140int atari_mste_hwclk( int op, struct rtc_time *t )
 141{
 142    int hour, year;
 143    int hr24=0;
 144    struct MSTE_RTC val;
 145
 146    mste_rtc.mode=(mste_rtc.mode | 1);
 147    hr24=mste_rtc.mon_tens & 1;
 148    mste_rtc.mode=(mste_rtc.mode & ~1);
 149
 150    if (op) {
 151        /* write: prepare values */
 152
 153        val.sec_ones = t->tm_sec % 10;
 154        val.sec_tens = t->tm_sec / 10;
 155        val.min_ones = t->tm_min % 10;
 156        val.min_tens = t->tm_min / 10;
 157        hour = t->tm_hour;
 158        if (!hr24) {
 159            if (hour > 11)
 160                hour += 20 - 12;
 161            if (hour == 0 || hour == 20)
 162                hour += 12;
 163        }
 164        val.hr_ones = hour % 10;
 165        val.hr_tens = hour / 10;
 166        val.day_ones = t->tm_mday % 10;
 167        val.day_tens = t->tm_mday / 10;
 168        val.mon_ones = (t->tm_mon+1) % 10;
 169        val.mon_tens = (t->tm_mon+1) / 10;
 170        year = t->tm_year - 80;
 171        val.year_ones = year % 10;
 172        val.year_tens = year / 10;
 173        val.weekday = t->tm_wday;
 174        mste_write(&val);
 175        mste_rtc.mode=(mste_rtc.mode | 1);
 176        val.year_ones = (year % 4);     /* leap year register */
 177        mste_rtc.mode=(mste_rtc.mode & ~1);
 178    }
 179    else {
 180        mste_read(&val);
 181        t->tm_sec = val.sec_ones + val.sec_tens * 10;
 182        t->tm_min = val.min_ones + val.min_tens * 10;
 183        hour = val.hr_ones + val.hr_tens * 10;
 184        if (!hr24) {
 185            if (hour == 12 || hour == 12 + 20)
 186                hour -= 12;
 187            if (hour >= 20)
 188                hour += 12 - 20;
 189        }
 190        t->tm_hour = hour;
 191        t->tm_mday = val.day_ones + val.day_tens * 10;
 192        t->tm_mon  = val.mon_ones + val.mon_tens * 10 - 1;
 193        t->tm_year = val.year_ones + val.year_tens * 10 + 80;
 194        t->tm_wday = val.weekday;
 195    }
 196    return 0;
 197}
 198
 199int atari_tt_hwclk( int op, struct rtc_time *t )
 200{
 201    int sec=0, min=0, hour=0, day=0, mon=0, year=0, wday=0;
 202    unsigned long       flags;
 203    unsigned char       ctrl;
 204    int pm = 0;
 205
 206    ctrl = RTC_READ(RTC_CONTROL); /* control registers are
 207                                   * independent from the UIP */
 208
 209    if (op) {
 210        /* write: prepare values */
 211
 212        sec  = t->tm_sec;
 213        min  = t->tm_min;
 214        hour = t->tm_hour;
 215        day  = t->tm_mday;
 216        mon  = t->tm_mon + 1;
 217        year = t->tm_year - atari_rtc_year_offset;
 218        wday = t->tm_wday + (t->tm_wday >= 0);
 219
 220        if (!(ctrl & RTC_24H)) {
 221            if (hour > 11) {
 222                pm = 0x80;
 223                if (hour != 12)
 224                    hour -= 12;
 225            }
 226            else if (hour == 0)
 227                hour = 12;
 228        }
 229
 230        if (!(ctrl & RTC_DM_BINARY)) {
 231            sec = bin2bcd(sec);
 232            min = bin2bcd(min);
 233            hour = bin2bcd(hour);
 234            day = bin2bcd(day);
 235            mon = bin2bcd(mon);
 236            year = bin2bcd(year);
 237            if (wday >= 0)
 238                wday = bin2bcd(wday);
 239        }
 240    }
 241
 242    /* Reading/writing the clock registers is a bit critical due to
 243     * the regular update cycle of the RTC. While an update is in
 244     * progress, registers 0..9 shouldn't be touched.
 245     * The problem is solved like that: If an update is currently in
 246     * progress (the UIP bit is set), the process sleeps for a while
 247     * (50ms). This really should be enough, since the update cycle
 248     * normally needs 2 ms.
 249     * If the UIP bit reads as 0, we have at least 244 usecs until the
 250     * update starts. This should be enough... But to be sure,
 251     * additionally the RTC_SET bit is set to prevent an update cycle.
 252     */
 253
 254    while( RTC_READ(RTC_FREQ_SELECT) & RTC_UIP ) {
 255        if (in_atomic() || irqs_disabled())
 256            mdelay(1);
 257        else
 258            schedule_timeout_interruptible(HWCLK_POLL_INTERVAL);
 259    }
 260
 261    local_irq_save(flags);
 262    RTC_WRITE( RTC_CONTROL, ctrl | RTC_SET );
 263    if (!op) {
 264        sec  = RTC_READ( RTC_SECONDS );
 265        min  = RTC_READ( RTC_MINUTES );
 266        hour = RTC_READ( RTC_HOURS );
 267        day  = RTC_READ( RTC_DAY_OF_MONTH );
 268        mon  = RTC_READ( RTC_MONTH );
 269        year = RTC_READ( RTC_YEAR );
 270        wday = RTC_READ( RTC_DAY_OF_WEEK );
 271    }
 272    else {
 273        RTC_WRITE( RTC_SECONDS, sec );
 274        RTC_WRITE( RTC_MINUTES, min );
 275        RTC_WRITE( RTC_HOURS, hour + pm);
 276        RTC_WRITE( RTC_DAY_OF_MONTH, day );
 277        RTC_WRITE( RTC_MONTH, mon );
 278        RTC_WRITE( RTC_YEAR, year );
 279        if (wday >= 0) RTC_WRITE( RTC_DAY_OF_WEEK, wday );
 280    }
 281    RTC_WRITE( RTC_CONTROL, ctrl & ~RTC_SET );
 282    local_irq_restore(flags);
 283
 284    if (!op) {
 285        /* read: adjust values */
 286
 287        if (hour & 0x80) {
 288            hour &= ~0x80;
 289            pm = 1;
 290        }
 291
 292        if (!(ctrl & RTC_DM_BINARY)) {
 293            sec = bcd2bin(sec);
 294            min = bcd2bin(min);
 295            hour = bcd2bin(hour);
 296            day = bcd2bin(day);
 297            mon = bcd2bin(mon);
 298            year = bcd2bin(year);
 299            wday = bcd2bin(wday);
 300        }
 301
 302        if (!(ctrl & RTC_24H)) {
 303            if (!pm && hour == 12)
 304                hour = 0;
 305            else if (pm && hour != 12)
 306                hour += 12;
 307        }
 308
 309        t->tm_sec  = sec;
 310        t->tm_min  = min;
 311        t->tm_hour = hour;
 312        t->tm_mday = day;
 313        t->tm_mon  = mon - 1;
 314        t->tm_year = year + atari_rtc_year_offset;
 315        t->tm_wday = wday - 1;
 316    }
 317
 318    return( 0 );
 319}
 320