1 /* 2 * DS1339 Timekeeper (on I2C) 3 */ 4 5 #include "u.h" 6 #include "../port/lib.h" 7 #include "mem.h" 8 #include "dat.h" 9 #include "fns.h" 10 #include "../port/error.h" 11 12 #include "io.h" 13 14 typedef struct Rtc Rtc; 15 typedef struct Rtcreg Rtcreg; 16 17 struct Rtc 18 { 19 int sec; 20 int min; 21 int hour; 22 int wday; 23 int mday; 24 int mon; 25 int year; 26 }; 27 28 struct Rtcreg 29 { 30 uchar sec; 31 uchar min; 32 uchar hour; 33 uchar wday; /* 1=Sun */ 34 uchar mday; /* 00-31 */ 35 uchar mon; /* 1-12 */ 36 uchar year; 37 }; 38 39 enum{ 40 Qdir = 0, 41 Qrtc, 42 43 Rtclen= 7, /* bytes read and written to timekeeper */ 44 }; 45 46 static QLock rtclock; /* mutex on nvram operations */ 47 static I2Cdev rtdev; 48 49 static Dirtab rtcdir[]={ 50 ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, 51 "rtc", {Qrtc, 0}, 0, 0664, 52 }; 53 54 static ulong rtc2sec(Rtc*); 55 static void sec2rtc(ulong, Rtc*); 56 static void setrtc(Rtc*); 57 58 static void 59 rtcreset(void) 60 { 61 rtdev.addr = 0x68; 62 rtdev.salen = 1; 63 i2csetup(1); 64 } 65 66 static Chan* 67 rtcattach(char *spec) 68 { 69 return devattach('r', spec); 70 } 71 72 static Walkqid* 73 rtcwalk(Chan *c, Chan *nc, char **name, int nname) 74 { 75 return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen); 76 } 77 78 static int 79 rtcstat(Chan *c, uchar *dp, int n) 80 { 81 return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen); 82 } 83 84 static Chan* 85 rtcopen(Chan *c, int omode) 86 { 87 omode = openmode(omode); 88 switch((ulong)c->qid.path){ 89 case Qrtc: 90 if(strcmp(up->env->user, eve)!=0 && omode!=OREAD) 91 error(Eperm); 92 break; 93 } 94 return devopen(c, omode, rtcdir, nelem(rtcdir), devgen); 95 } 96 97 static void 98 rtcclose(Chan*) 99 { 100 } 101 102 static long 103 rtcread(Chan *c, void *buf, long n, vlong offset) 104 { 105 ulong t, ot; 106 107 if(c->qid.type & QTDIR) 108 return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen); 109 110 switch((ulong)c->qid.path){ 111 case Qrtc: 112 qlock(&rtclock); 113 t = rtctime(); 114 do{ 115 ot = t; 116 t = rtctime(); /* make sure there's no skew */ 117 }while(t != ot); 118 qunlock(&rtclock); 119 return readnum(offset, buf, n, t, 12); 120 } 121 error(Egreg); 122 return -1; /* never reached */ 123 } 124 125 static long 126 rtcwrite(Chan *c, void *buf, long n, vlong off) 127 { 128 Rtc rtc; 129 ulong secs; 130 char *cp, sbuf[32]; 131 ulong offset = off; 132 133 switch((ulong)c->qid.path){ 134 case Qrtc: 135 if(offset!=0 || n >= sizeof(sbuf)-1) 136 error(Ebadarg); 137 memmove(sbuf, buf, n); 138 sbuf[n] = '\0'; 139 /* 140 * read the time 141 */ 142 cp = sbuf; 143 while(*cp){ 144 if(*cp>='0' && *cp<='9') 145 break; 146 cp++; 147 } 148 secs = strtoul(cp, 0, 0); 149 /* 150 * convert to bcd 151 */ 152 sec2rtc(secs, &rtc); 153 /* 154 * write it 155 */ 156 setrtc(&rtc); 157 return n; 158 } 159 error(Egreg); 160 return -1; /* never reached */ 161 } 162 163 Dev rtcdevtab = { 164 'r', 165 "rtc", 166 167 rtcreset, 168 devinit, 169 devshutdown, 170 rtcattach, 171 rtcwalk, 172 rtcstat, 173 rtcopen, 174 devcreate, 175 rtcclose, 176 rtcread, 177 devbread, 178 rtcwrite, 179 devbwrite, 180 devremove, 181 devwstat, 182 }; 183 184 static int 185 getbcd(int bcd) 186 { 187 return (bcd&0x0f) + 10 * (bcd>>4); 188 } 189 190 static int 191 putbcd(int val) 192 { 193 return (val % 10) | (((val/10) % 10) << 4); 194 } 195 196 long 197 rtctime(void) 198 { 199 Rtc rtc; 200 Rtcreg d; 201 int h; 202 203 if(waserror()){ 204 iprint("rtc: err %s\n", up->env->errstr); 205 return 0; 206 } 207 if(i2crecv(&rtdev, &d, Rtclen, 0) != Rtclen) 208 return 0; 209 poperror(); 210 rtc.sec = getbcd(d.sec); 211 rtc.min = getbcd(d.min); 212 if(d.hour & (1<<6)){ /* 12 hour clock */ 213 h = d.hour & 0x1F; 214 if(d.hour & (1<<5)) 215 h += 0x12; 216 rtc.hour = getbcd(h); 217 }else 218 rtc.hour = getbcd(d.hour); 219 rtc.mday = getbcd(d.mday); 220 rtc.mon = getbcd(d.mon & 0x7f); 221 rtc.year = getbcd(d.year); 222 if(rtc.mon < 1 || rtc.mon > 12) 223 return 0; 224 if(d.mon & (1<<7)) 225 rtc.year += 2000; 226 else 227 rtc.year += 1900; 228 return rtc2sec(&rtc); 229 } 230 231 static void 232 setrtc(Rtc *rtc) 233 { 234 Rtcreg d; 235 236 memset(&d, 0, sizeof(d)); 237 d.year = putbcd(rtc->year % 100); 238 d.mon = putbcd(rtc->mon); 239 if(rtc->year >= 2000) 240 d.mon |= 1<<7; 241 d.wday = rtc->wday+1; 242 d.mday = putbcd(rtc->mday); 243 d.hour = putbcd(rtc->hour); 244 d.min = putbcd(rtc->min); 245 d.sec = putbcd(rtc->sec); 246 i2csend(&rtdev, &d, Rtclen, 0); 247 } 248 249 #define SEC2MIN 60L 250 #define SEC2HOUR (60L*SEC2MIN) 251 #define SEC2DAY (24L*SEC2HOUR) 252 253 /* 254 * days per month plus days/year 255 */ 256 static int dmsize[] = 257 { 258 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 259 }; 260 static int ldmsize[] = 261 { 262 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 263 }; 264 265 /* 266 * return the days/month for the given year 267 */ 268 static int * 269 yrsize(int y) 270 { 271 272 if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0)) 273 return ldmsize; 274 else 275 return dmsize; 276 } 277 278 /* 279 * compute seconds since Jan 1 1970 280 */ 281 static ulong 282 rtc2sec(Rtc *rtc) 283 { 284 ulong secs; 285 int i; 286 int *d2m; 287 288 secs = 0; 289 290 /* 291 * seconds per year 292 */ 293 for(i = 1970; i < rtc->year; i++){ 294 d2m = yrsize(i); 295 secs += d2m[0] * SEC2DAY; 296 } 297 298 /* 299 * seconds per month 300 */ 301 d2m = yrsize(rtc->year); 302 for(i = 1; i < rtc->mon; i++) 303 secs += d2m[i] * SEC2DAY; 304 305 secs += (rtc->mday-1) * SEC2DAY; 306 secs += rtc->hour * SEC2HOUR; 307 secs += rtc->min * SEC2MIN; 308 secs += rtc->sec; 309 310 return secs; 311 } 312 313 /* 314 * compute rtc from seconds since Jan 1 1970 315 */ 316 static void 317 sec2rtc(ulong secs, Rtc *rtc) 318 { 319 int d; 320 long hms, day; 321 int *d2m; 322 323 /* 324 * break initial number into days 325 */ 326 hms = secs % SEC2DAY; 327 day = secs / SEC2DAY; 328 if(hms < 0) { 329 hms += SEC2DAY; 330 day -= 1; 331 } 332 333 /* 334 * day is the day number. 335 * generate day of the week. 336 * The addend is 4 mod 7 (1/1/1970 was Thursday) 337 */ 338 339 rtc->wday = (day + 7340036L) % 7; 340 341 /* 342 * generate hours:minutes:seconds 343 */ 344 rtc->sec = hms % 60; 345 d = hms / 60; 346 rtc->min = d % 60; 347 d /= 60; 348 rtc->hour = d; 349 350 /* 351 * year number 352 */ 353 if(day >= 0) 354 for(d = 1970; day >= *yrsize(d); d++) 355 day -= *yrsize(d); 356 else 357 for (d = 1970; day < 0; d--) 358 day += *yrsize(d-1); 359 rtc->year = d; 360 361 /* 362 * generate month 363 */ 364 d2m = yrsize(rtc->year); 365 for(d = 1; day >= d2m[d]; d++) 366 day -= d2m[d]; 367 rtc->mday = day + 1; 368 rtc->mon = d; 369 } 370