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