[81329f9] | 1 | /* |
---|
| 2 | * Copyright (c) 2014 embedded brains GmbH. All rights reserved. |
---|
| 3 | * |
---|
| 4 | * embedded brains GmbH |
---|
| 5 | * Dornierstr. 4 |
---|
| 6 | * 82178 Puchheim |
---|
| 7 | * Germany |
---|
| 8 | * <info@embedded-brains.de> |
---|
| 9 | * |
---|
| 10 | * The license and distribution terms for this file may be |
---|
| 11 | * found in the file LICENSE in this distribution or at |
---|
| 12 | * http://www.rtems.org/license/LICENSE. |
---|
| 13 | */ |
---|
| 14 | |
---|
| 15 | /* |
---|
[a1d6f7a] | 16 | * Driver for the DS1339 RTC (Maxim Semiconductors) -> RTC1 |
---|
| 17 | * and the M41ST87 RTC (ST Microelectronics) -> RTC2 |
---|
[81329f9] | 18 | * |
---|
| 19 | * Please note the following points: |
---|
| 20 | * - The day of week is ignored. |
---|
| 21 | * - The century bit is interpreted the following way: |
---|
| 22 | * - century not set: TOD_BASE_YEAR .. 1999 |
---|
| 23 | * - century set: 2000 .. 2099 |
---|
| 24 | * - century not set: 2100 .. (TOD_BASE_YEAR + 200) |
---|
| 25 | */ |
---|
| 26 | |
---|
| 27 | #include <libchip/rtc.h> |
---|
| 28 | #include <assert.h> |
---|
| 29 | #include <rtems/score/todimpl.h> |
---|
| 30 | #include <sys/filio.h> |
---|
| 31 | #include <fcntl.h> |
---|
| 32 | #include <unistd.h> |
---|
| 33 | #include <bsp/i2cdrv.h> |
---|
| 34 | |
---|
[a1d6f7a] | 35 | #define ALTERA_CYCLONE_V_RTC_NUMBER 2 |
---|
[81329f9] | 36 | |
---|
| 37 | |
---|
[a1d6f7a] | 38 | /* ******************************* DS1339 ********************************** */ |
---|
[81329f9] | 39 | |
---|
| 40 | |
---|
[a1d6f7a] | 41 | #define DS1339_I2C_ADDRESS (0xD0 >> 1) /* 7-bit addressing! */ |
---|
| 42 | #define DS1339_I2C_BUS_DEVICE "/dev/i2c0" |
---|
[81329f9] | 43 | |
---|
[a1d6f7a] | 44 | #define DS1339_ADDR_TIME 0x00 |
---|
[81329f9] | 45 | |
---|
[a1d6f7a] | 46 | #define DS1339_ADDR_CTRL 0x0E |
---|
| 47 | #define DS1339_CTRL_EOSC 0x80 |
---|
| 48 | #define DS1339_CTRL_BBSQI 0x20 |
---|
| 49 | #define DS1339_CTRL_RS2 0x10 |
---|
| 50 | #define DS1339_CTRL_RS1 0x08 |
---|
| 51 | #define DS1339_CTRL_INTCN 0x04 |
---|
| 52 | #define DS1339_CTRL_A2IE 0x02 |
---|
| 53 | #define DS1339_CTRL_A1IE 0x01 |
---|
| 54 | |
---|
| 55 | #define DS1339_CTRL_DEFAULT (0x00) |
---|
| 56 | |
---|
| 57 | #define DS1339_ADDR_STATUS 0x0F |
---|
| 58 | #define DS1339_STATUS_OSF 0x80 |
---|
| 59 | #define DS1339_STATUS_A2F 0x02 |
---|
| 60 | #define DS1339_STATUS_A1F 0x01 |
---|
| 61 | |
---|
| 62 | #define DS1339_STATUS_CLEAR (0x00) |
---|
| 63 | |
---|
| 64 | #define DS1339_ADDR_TRICKLE_CHARGE 0x10 |
---|
| 65 | |
---|
| 66 | |
---|
| 67 | typedef struct |
---|
| 68 | { |
---|
| 69 | uint8_t seconds; |
---|
| 70 | uint8_t minutes; |
---|
| 71 | uint8_t hours; |
---|
[81329f9] | 72 | #define DS1339_HOURS_12_24_FLAG 0x40 |
---|
| 73 | #define DS1339_HOURS_AM_PM_FLAG_OR_20_HOURS 0x20 |
---|
| 74 | #define DS1339_HOURS_10_HOURS 0x10 |
---|
[a1d6f7a] | 75 | uint8_t weekday; |
---|
| 76 | uint8_t date; |
---|
| 77 | uint8_t month; |
---|
[81329f9] | 78 | #define DS1339_MONTH_CENTURY 0x80 |
---|
[a1d6f7a] | 79 | uint8_t year; |
---|
| 80 | } |
---|
| 81 | ds1339_time_t; |
---|
| 82 | |
---|
[81329f9] | 83 | |
---|
| 84 | /* The longest write transmission is writing the time + one address bit */ |
---|
[a1d6f7a] | 85 | #define DS1339_MAX_WRITE_SIZE (sizeof(ds1339_time_t) + 1) |
---|
| 86 | |
---|
[81329f9] | 87 | |
---|
| 88 | /* Functions for converting the fields */ |
---|
[a1d6f7a] | 89 | static unsigned int ds1339_get_seconds(ds1339_time_t* time) |
---|
| 90 | { |
---|
| 91 | uint8_t tens = time->seconds >> 4; |
---|
| 92 | uint8_t ones = time->seconds & 0x0F; |
---|
| 93 | |
---|
[81329f9] | 94 | return tens * 10 + ones; |
---|
| 95 | } |
---|
| 96 | |
---|
[a1d6f7a] | 97 | |
---|
| 98 | static unsigned int ds1339_get_minutes(ds1339_time_t* time) |
---|
| 99 | { |
---|
| 100 | uint8_t tens = time->minutes >> 4; |
---|
| 101 | uint8_t ones = time->minutes & 0x0F; |
---|
| 102 | |
---|
[81329f9] | 103 | return tens * 10 + ones; |
---|
| 104 | } |
---|
| 105 | |
---|
| 106 | |
---|
[a1d6f7a] | 107 | static unsigned int ds1339_get_hours(ds1339_time_t* time) |
---|
| 108 | { |
---|
| 109 | |
---|
| 110 | uint8_t value = time->hours & 0x0F; |
---|
| 111 | |
---|
| 112 | if (time->hours & DS1339_HOURS_10_HOURS) |
---|
| 113 | { |
---|
[81329f9] | 114 | value += 10; |
---|
| 115 | } |
---|
[a1d6f7a] | 116 | if (time->hours & DS1339_HOURS_AM_PM_FLAG_OR_20_HOURS) |
---|
| 117 | { |
---|
| 118 | if (time->hours & DS1339_HOURS_12_24_FLAG) |
---|
[81329f9] | 119 | value += 12; |
---|
[a1d6f7a] | 120 | else |
---|
[81329f9] | 121 | value += 20; |
---|
| 122 | } |
---|
| 123 | |
---|
| 124 | return value; |
---|
| 125 | } |
---|
| 126 | |
---|
[a1d6f7a] | 127 | |
---|
| 128 | static unsigned int ds1339_get_day_of_month(ds1339_time_t* time) |
---|
| 129 | { |
---|
| 130 | |
---|
| 131 | uint8_t tens = time->date >> 4; |
---|
| 132 | uint8_t ones = time->date & 0x0F; |
---|
| 133 | |
---|
[81329f9] | 134 | return tens * 10 + ones; |
---|
| 135 | } |
---|
| 136 | |
---|
[a1d6f7a] | 137 | |
---|
| 138 | static unsigned int ds1339_get_month(ds1339_time_t* time) |
---|
| 139 | { |
---|
| 140 | |
---|
| 141 | uint8_t tens = (time->month >> 4) & 0x07; |
---|
| 142 | uint8_t ones = time->month & 0x0F; |
---|
| 143 | |
---|
[81329f9] | 144 | return tens * 10 + ones; |
---|
| 145 | } |
---|
| 146 | |
---|
[a1d6f7a] | 147 | |
---|
| 148 | static unsigned int ds1339_get_year(ds1339_time_t* time) |
---|
| 149 | { |
---|
| 150 | |
---|
| 151 | unsigned int year = 1900; |
---|
| 152 | |
---|
[81329f9] | 153 | year += (time->year >> 4) * 10; |
---|
| 154 | year += time->year & 0x0F; |
---|
[a1d6f7a] | 155 | if (time->month & DS1339_MONTH_CENTURY) |
---|
[81329f9] | 156 | year += 100; |
---|
[a1d6f7a] | 157 | if (year < TOD_BASE_YEAR) |
---|
[81329f9] | 158 | year += 200; |
---|
[a1d6f7a] | 159 | |
---|
[81329f9] | 160 | return year; |
---|
| 161 | } |
---|
| 162 | |
---|
[a1d6f7a] | 163 | |
---|
| 164 | static void ds1339_set_time(ds1339_time_t* time, |
---|
| 165 | unsigned int second, |
---|
| 166 | unsigned int minute, |
---|
| 167 | unsigned int hour, |
---|
| 168 | unsigned int day, |
---|
| 169 | unsigned int month, |
---|
| 170 | unsigned int year) |
---|
| 171 | { |
---|
| 172 | |
---|
| 173 | unsigned int tens; |
---|
| 174 | unsigned int ones; |
---|
| 175 | uint8_t century = 0; |
---|
[81329f9] | 176 | |
---|
| 177 | tens = second / 10; |
---|
| 178 | ones = second % 10; |
---|
| 179 | time->seconds = tens << 4 | ones; |
---|
| 180 | |
---|
| 181 | tens = minute / 10; |
---|
| 182 | ones = minute % 10; |
---|
| 183 | time->minutes = tens << 4 | ones; |
---|
| 184 | |
---|
| 185 | tens = hour / 10; |
---|
| 186 | ones = hour % 10; |
---|
| 187 | time->hours = tens << 4 | ones; |
---|
| 188 | |
---|
| 189 | /* Weekday is not used. Therefore it can be set to an arbitrary valid value */ |
---|
| 190 | time->weekday = 1; |
---|
| 191 | |
---|
| 192 | tens = day / 10; |
---|
| 193 | ones = day % 10; |
---|
| 194 | time->date = tens << 4 | ones; |
---|
| 195 | |
---|
| 196 | tens = month / 10; |
---|
| 197 | ones = month % 10; |
---|
[a1d6f7a] | 198 | if ((year >= 2000) && (year < 2100)) |
---|
[81329f9] | 199 | century = DS1339_MONTH_CENTURY; |
---|
| 200 | time->month = century | tens << 4 | ones; |
---|
| 201 | |
---|
| 202 | tens = (year % 100) / 10; |
---|
| 203 | ones = year % 10; |
---|
| 204 | time->year = tens << 4 | ones; |
---|
[a1d6f7a] | 205 | |
---|
[81329f9] | 206 | } |
---|
| 207 | |
---|
[a1d6f7a] | 208 | |
---|
| 209 | |
---|
| 210 | static rtems_status_code ds1339_open_file(int* fd) |
---|
[81329f9] | 211 | { |
---|
[a1d6f7a] | 212 | |
---|
| 213 | int rv = 0; |
---|
| 214 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
[81329f9] | 215 | |
---|
| 216 | *fd = open(DS1339_I2C_BUS_DEVICE, O_RDWR); |
---|
[a1d6f7a] | 217 | if (*fd == -1) |
---|
[81329f9] | 218 | sc = RTEMS_IO_ERROR; |
---|
| 219 | |
---|
[a1d6f7a] | 220 | if (sc == RTEMS_SUCCESSFUL) |
---|
| 221 | { |
---|
[81329f9] | 222 | rv = ioctl(*fd, I2C_IOC_SET_SLAVE_ADDRESS, DS1339_I2C_ADDRESS); |
---|
[a1d6f7a] | 223 | if (rv == -1) |
---|
[81329f9] | 224 | sc = RTEMS_IO_ERROR; |
---|
| 225 | } |
---|
| 226 | |
---|
| 227 | return sc; |
---|
| 228 | } |
---|
| 229 | |
---|
[a1d6f7a] | 230 | |
---|
[81329f9] | 231 | /* Read size bytes from ds1339 register address addr to buf. */ |
---|
[a1d6f7a] | 232 | static rtems_status_code ds1339_read(uint8_t addr, void* buf, size_t size) |
---|
[81329f9] | 233 | { |
---|
[a1d6f7a] | 234 | |
---|
| 235 | int fd = -1; |
---|
| 236 | int rv = 0; |
---|
| 237 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
[81329f9] | 238 | |
---|
| 239 | sc = ds1339_open_file(&fd); |
---|
| 240 | |
---|
[a1d6f7a] | 241 | if (sc == RTEMS_SUCCESSFUL) |
---|
| 242 | { |
---|
[81329f9] | 243 | rv = write(fd, &addr, sizeof(addr)); |
---|
[a1d6f7a] | 244 | if (rv != sizeof(addr)) |
---|
[81329f9] | 245 | sc = RTEMS_IO_ERROR; |
---|
| 246 | } |
---|
| 247 | |
---|
[a1d6f7a] | 248 | if (sc == RTEMS_SUCCESSFUL) |
---|
| 249 | { |
---|
[81329f9] | 250 | rv = read(fd, buf, size); |
---|
[a1d6f7a] | 251 | if (rv != size) |
---|
[81329f9] | 252 | sc = RTEMS_IO_ERROR; |
---|
| 253 | } |
---|
| 254 | |
---|
| 255 | rv = close(fd); |
---|
[a1d6f7a] | 256 | if (rv != 0) |
---|
[81329f9] | 257 | sc = RTEMS_IO_ERROR; |
---|
| 258 | |
---|
| 259 | return sc; |
---|
| 260 | } |
---|
| 261 | |
---|
[a1d6f7a] | 262 | |
---|
[81329f9] | 263 | /* Write size bytes from buf to ds1339 register address addr. */ |
---|
[a1d6f7a] | 264 | static rtems_status_code ds1339_write(uint8_t addr, void* buf, size_t size) |
---|
[81329f9] | 265 | { |
---|
[a1d6f7a] | 266 | |
---|
| 267 | int fd = -1; |
---|
| 268 | int rv = 0; |
---|
| 269 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
[81329f9] | 270 | /* The driver never writes many bytes. Therefore it should be less expensive |
---|
| 271 | * to reserve the maximum number of bytes that will be written in one go than |
---|
| 272 | * use a malloc. */ |
---|
[a1d6f7a] | 273 | uint8_t local_buf[DS1339_MAX_WRITE_SIZE]; |
---|
| 274 | int write_size = size + 1; |
---|
[81329f9] | 275 | |
---|
| 276 | assert(write_size <= DS1339_MAX_WRITE_SIZE); |
---|
| 277 | |
---|
| 278 | local_buf[0] = addr; |
---|
| 279 | memcpy(&local_buf[1], buf, size); |
---|
| 280 | |
---|
| 281 | sc = ds1339_open_file(&fd); |
---|
| 282 | |
---|
[a1d6f7a] | 283 | if (sc == RTEMS_SUCCESSFUL) |
---|
| 284 | { |
---|
[81329f9] | 285 | rv = write(fd, local_buf, write_size); |
---|
[a1d6f7a] | 286 | if (rv != write_size) |
---|
[81329f9] | 287 | sc = RTEMS_IO_ERROR; |
---|
| 288 | } |
---|
| 289 | |
---|
| 290 | rv = close(fd); |
---|
[a1d6f7a] | 291 | if (rv != 0) |
---|
[81329f9] | 292 | sc = RTEMS_IO_ERROR; |
---|
| 293 | |
---|
| 294 | return RTEMS_SUCCESSFUL; |
---|
| 295 | } |
---|
| 296 | |
---|
[a1d6f7a] | 297 | |
---|
| 298 | static void altera_cyclone_v_ds1339_initialize(int minor) |
---|
[81329f9] | 299 | { |
---|
[a1d6f7a] | 300 | |
---|
| 301 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
| 302 | uint8_t status = 0; |
---|
[81329f9] | 303 | |
---|
| 304 | /* Check RTC valid */ |
---|
| 305 | sc = ds1339_read(DS1339_ADDR_STATUS, &status, sizeof(status)); |
---|
| 306 | assert(sc == RTEMS_SUCCESSFUL); |
---|
[a1d6f7a] | 307 | |
---|
| 308 | if (status & DS1339_STATUS_OSF) |
---|
| 309 | { |
---|
[81329f9] | 310 | /* RTC has been stopped. Initialise it. */ |
---|
| 311 | ds1339_time_t time; |
---|
| 312 | |
---|
[a1d6f7a] | 313 | uint8_t write = DS1339_CTRL_DEFAULT; |
---|
[81329f9] | 314 | sc = ds1339_write(DS1339_ADDR_CTRL, &write, sizeof(write)); |
---|
| 315 | assert(sc == RTEMS_SUCCESSFUL); |
---|
| 316 | |
---|
| 317 | write = DS1339_STATUS_CLEAR; |
---|
| 318 | sc = ds1339_write(DS1339_ADDR_STATUS, &write, sizeof(write)); |
---|
| 319 | assert(sc == RTEMS_SUCCESSFUL); |
---|
| 320 | |
---|
[a1d6f7a] | 321 | ds1339_set_time(&time, 0, 0, 0, 1, 1, TOD_BASE_YEAR); |
---|
[81329f9] | 322 | sc = ds1339_write(DS1339_ADDR_TIME, &time, sizeof(time)); |
---|
| 323 | assert(sc == RTEMS_SUCCESSFUL); |
---|
| 324 | } |
---|
[a1d6f7a] | 325 | |
---|
[81329f9] | 326 | } |
---|
| 327 | |
---|
[a1d6f7a] | 328 | |
---|
| 329 | static int altera_cyclone_v_ds1339_get_time(int minor, rtems_time_of_day* tod) |
---|
[81329f9] | 330 | { |
---|
| 331 | |
---|
[a1d6f7a] | 332 | ds1339_time_t time; |
---|
| 333 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
| 334 | rtems_time_of_day temp_tod; |
---|
[81329f9] | 335 | |
---|
[a1d6f7a] | 336 | sc = ds1339_read(DS1339_ADDR_TIME, &time, sizeof(time)); |
---|
[81329f9] | 337 | |
---|
[a1d6f7a] | 338 | if (sc == RTEMS_SUCCESSFUL) |
---|
| 339 | { |
---|
| 340 | temp_tod.ticks = 0; |
---|
| 341 | temp_tod.second = ds1339_get_seconds(&time); |
---|
| 342 | temp_tod.minute = ds1339_get_minutes(&time); |
---|
| 343 | temp_tod.hour = ds1339_get_hours(&time); |
---|
| 344 | temp_tod.day = ds1339_get_day_of_month(&time); |
---|
| 345 | temp_tod.month = ds1339_get_month(&time); |
---|
| 346 | temp_tod.year = ds1339_get_year(&time); |
---|
| 347 | |
---|
| 348 | if (_TOD_Validate(&temp_tod)) |
---|
[81329f9] | 349 | memcpy(tod, &temp_tod, sizeof(temp_tod)); |
---|
[a1d6f7a] | 350 | else |
---|
[81329f9] | 351 | sc = RTEMS_INVALID_CLOCK; |
---|
| 352 | } |
---|
| 353 | |
---|
| 354 | return -sc; |
---|
| 355 | } |
---|
| 356 | |
---|
[a1d6f7a] | 357 | |
---|
| 358 | static int altera_cyclone_v_ds1339_set_time(int minor, const rtems_time_of_day* tod) |
---|
[81329f9] | 359 | { |
---|
| 360 | |
---|
[a1d6f7a] | 361 | ds1339_time_t time; |
---|
| 362 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
| 363 | |
---|
| 364 | ds1339_set_time(&time, |
---|
| 365 | tod->second, |
---|
| 366 | tod->minute, |
---|
| 367 | tod->hour, |
---|
| 368 | tod->day, |
---|
| 369 | tod->month, |
---|
| 370 | tod->year |
---|
| 371 | ); |
---|
[81329f9] | 372 | |
---|
| 373 | sc = ds1339_write(DS1339_ADDR_TIME, &time, sizeof(time)); |
---|
| 374 | |
---|
| 375 | return -sc; |
---|
| 376 | } |
---|
| 377 | |
---|
[a1d6f7a] | 378 | |
---|
| 379 | static bool altera_cyclone_v_ds1339_probe(int minor) |
---|
| 380 | { |
---|
| 381 | |
---|
| 382 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
| 383 | uint8_t buf; |
---|
| 384 | |
---|
| 385 | /* try to read from register address 0x00 */ |
---|
| 386 | sc = ds1339_read(0x00, &buf, 1); |
---|
| 387 | if (sc != RTEMS_SUCCESSFUL) |
---|
| 388 | /* no RTC implemented */ |
---|
| 389 | return false; |
---|
| 390 | /* try to read from register address 0x20 (not implemented in DS1339) */ |
---|
| 391 | sc = ds1339_read(0x20, &buf, 1); |
---|
| 392 | if (sc == RTEMS_SUCCESSFUL) |
---|
| 393 | /* RTC is not DS1339 */ |
---|
| 394 | return false; |
---|
| 395 | |
---|
| 396 | printk("RTC detected, type = DS1339\n"); |
---|
| 397 | return true; |
---|
| 398 | |
---|
| 399 | } |
---|
| 400 | |
---|
| 401 | |
---|
| 402 | /* ******************************* M41ST87 ********************************** */ |
---|
| 403 | |
---|
| 404 | |
---|
| 405 | #define M41ST87_I2C_ADDRESS (0xD0 >> 1) /* 7-bit addressing! */ |
---|
| 406 | #define M41ST87_I2C_BUS_DEVICE "/dev/i2c0" |
---|
| 407 | |
---|
| 408 | #define M41ST87_ADDR_TIME 0x00 |
---|
| 409 | |
---|
| 410 | #define M41ST87_ADDR_CTRL 0x08 |
---|
| 411 | #define M41ST87_CTRL_OUT 0x80 |
---|
| 412 | #define M41ST87_CTRL_FT 0x40 |
---|
| 413 | #define M41ST87_CTRL_S 0x20 |
---|
| 414 | #define M41ST87_CTRL_CAL 0x1F |
---|
| 415 | |
---|
| 416 | #define M41ST87_ADDR_ALARM_HOUR 0x0C |
---|
| 417 | #define M41ST87_BIT_HT 0x40 |
---|
| 418 | |
---|
| 419 | #define M41ST87_ADDR_FLAGS 0x0F |
---|
| 420 | #define M41ST87_FLAG_WDF 0x80 |
---|
| 421 | #define M41ST87_FLAG_AF 0x40 |
---|
| 422 | #define M41ST87_FLAG_BL 0x10 |
---|
| 423 | #define M41ST87_FLAG_OF 0x04 |
---|
| 424 | #define M41ST87_FLAG_TB1 0x02 |
---|
| 425 | #define M41ST87_FLAG_TB2 0x01 |
---|
| 426 | |
---|
| 427 | #define M41ST87_ADDR_USER_RAM 0x20 |
---|
| 428 | |
---|
| 429 | |
---|
| 430 | typedef struct |
---|
| 431 | { |
---|
| 432 | uint8_t sec100; |
---|
| 433 | uint8_t seconds; |
---|
| 434 | #define M41ST87_BIT_ST 0x80 |
---|
| 435 | uint8_t minutes; |
---|
| 436 | #define M41ST87_BIT_OFIE 0x80 |
---|
| 437 | uint8_t hours; |
---|
| 438 | #define M41ST87_BIT_CB1 0x80 |
---|
| 439 | #define M41ST87_BIT_CB0 0x40 |
---|
| 440 | uint8_t weekday; |
---|
| 441 | #define M41ST87_BIT_TR 0x80 |
---|
| 442 | #define M41ST87_BIT_THS 0x40 |
---|
| 443 | #define M41ST87_BIT_CLRPW1 0x20 |
---|
| 444 | #define M41ST87_BIT_CLRPW0 0x10 |
---|
| 445 | #define M41ST87_BIT_32KE 0x08 |
---|
| 446 | uint8_t day; |
---|
| 447 | #define M41ST87_BIT_PFOD 0x80 |
---|
| 448 | uint8_t month; |
---|
| 449 | uint8_t year; |
---|
| 450 | } |
---|
| 451 | m41st87_time_t; |
---|
| 452 | |
---|
| 453 | |
---|
| 454 | /* The longest write transmission is writing the time + one address bit */ |
---|
| 455 | #define M41ST87_MAX_WRITE_SIZE (sizeof(m41st87_time_t) + 1) |
---|
| 456 | |
---|
| 457 | |
---|
| 458 | /* Functions for converting the fields */ |
---|
| 459 | |
---|
| 460 | /* |
---|
| 461 | static unsigned int m41st87_get_sec100(m41st87_time_t* time) |
---|
| 462 | { |
---|
| 463 | |
---|
| 464 | uint8_t tens = time->sec100 >> 4; |
---|
| 465 | uint8_t ones = time->sec100 & 0x0F; |
---|
| 466 | |
---|
| 467 | return tens * 10 + ones; |
---|
| 468 | } |
---|
| 469 | */ |
---|
| 470 | |
---|
| 471 | |
---|
| 472 | static unsigned int m41st87_get_seconds(m41st87_time_t* time) |
---|
| 473 | { |
---|
| 474 | |
---|
| 475 | uint8_t tens = (time->seconds >> 4) & 0x07; |
---|
| 476 | uint8_t ones = time->seconds & 0x0F; |
---|
| 477 | |
---|
| 478 | return tens * 10 + ones; |
---|
| 479 | } |
---|
| 480 | |
---|
| 481 | |
---|
| 482 | static unsigned int m41st87_get_minutes(m41st87_time_t* time) |
---|
| 483 | { |
---|
| 484 | |
---|
| 485 | uint8_t tens = (time->minutes >> 4) & 0x07; |
---|
| 486 | uint8_t ones = time->minutes & 0x0F; |
---|
| 487 | |
---|
| 488 | return tens * 10 + ones; |
---|
| 489 | } |
---|
| 490 | |
---|
| 491 | |
---|
| 492 | static unsigned int m41st87_get_hours(m41st87_time_t* time) |
---|
| 493 | { |
---|
| 494 | |
---|
| 495 | uint8_t tens = (time->hours >> 4) & 0x03; |
---|
| 496 | uint8_t ones = time->hours & 0x0F; |
---|
| 497 | |
---|
| 498 | return tens * 10 + ones; |
---|
| 499 | } |
---|
| 500 | |
---|
| 501 | |
---|
| 502 | /* |
---|
| 503 | static unsigned int m41st87_get_day_of_week(m41st87_time_t* time) |
---|
| 504 | { |
---|
| 505 | |
---|
| 506 | return time->weekday & 0x07; |
---|
| 507 | } |
---|
| 508 | */ |
---|
| 509 | |
---|
| 510 | |
---|
| 511 | static unsigned int m41st87_get_day_of_month(m41st87_time_t* time) |
---|
| 512 | { |
---|
| 513 | |
---|
| 514 | uint8_t tens = (time->day >> 4) & 0x03; |
---|
| 515 | uint8_t ones = time->day & 0x0F; |
---|
| 516 | |
---|
| 517 | return tens * 10 + ones; |
---|
| 518 | } |
---|
| 519 | |
---|
| 520 | |
---|
| 521 | static unsigned int m41st87_get_month(m41st87_time_t* time) |
---|
| 522 | { |
---|
| 523 | |
---|
| 524 | uint8_t tens = (time->month >> 4) & 0x01; |
---|
| 525 | uint8_t ones = time->month & 0x0F; |
---|
| 526 | |
---|
| 527 | return tens * 10 + ones; |
---|
| 528 | } |
---|
| 529 | |
---|
| 530 | |
---|
| 531 | static unsigned int m41st87_get_year(m41st87_time_t* time) |
---|
| 532 | { |
---|
| 533 | |
---|
| 534 | uint8_t century = time->hours >> 6; |
---|
| 535 | uint8_t tens = time->year >> 4; |
---|
| 536 | uint8_t ones = time->year & 0x0F; |
---|
| 537 | |
---|
| 538 | return 1900 + century * 100 + tens * 10 + ones; |
---|
| 539 | } |
---|
| 540 | |
---|
| 541 | |
---|
| 542 | static void m41st87_set_time(m41st87_time_t* time, |
---|
| 543 | unsigned int second, |
---|
| 544 | unsigned int minute, |
---|
| 545 | unsigned int hour, |
---|
| 546 | unsigned int day, |
---|
| 547 | unsigned int month, |
---|
| 548 | unsigned int year) |
---|
| 549 | { |
---|
| 550 | |
---|
| 551 | unsigned int century; |
---|
| 552 | unsigned int tens; |
---|
| 553 | unsigned int ones; |
---|
| 554 | |
---|
| 555 | if (year < 1900) |
---|
| 556 | year = 1900; |
---|
| 557 | if (year > 2399) |
---|
| 558 | year = 2399; |
---|
| 559 | century = (year - 1900) / 100; |
---|
| 560 | |
---|
| 561 | /* Hundreds of seconds is not used, set to 0 */ |
---|
| 562 | time->sec100 = 0; |
---|
| 563 | |
---|
| 564 | tens = second / 10; |
---|
| 565 | ones = second % 10; |
---|
| 566 | time->seconds = (time->seconds & 0x80) | (tens << 4) | ones; |
---|
| 567 | |
---|
| 568 | tens = minute / 10; |
---|
| 569 | ones = minute % 10; |
---|
| 570 | time->minutes = (time->minutes & 0x80) | (tens << 4) | ones; |
---|
| 571 | |
---|
| 572 | tens = hour / 10; |
---|
| 573 | ones = hour % 10; |
---|
| 574 | time->hours = (century << 6) | (tens << 4) | ones; |
---|
| 575 | |
---|
| 576 | /* Weekday is not used. Therefore it can be set to an arbitrary valid value */ |
---|
| 577 | time->weekday = (time->weekday & 0xF8) | 1; |
---|
| 578 | |
---|
| 579 | tens = day / 10; |
---|
| 580 | ones = day % 10; |
---|
| 581 | time->day = (time->day & 0x80) | (tens << 4) | ones; |
---|
| 582 | |
---|
| 583 | tens = month / 10; |
---|
| 584 | ones = month % 10; |
---|
| 585 | time->month = (tens << 4) | ones; |
---|
| 586 | |
---|
| 587 | tens = (year % 100) / 10; |
---|
| 588 | ones = year % 10; |
---|
| 589 | time->year = (tens << 4) | ones; |
---|
| 590 | |
---|
| 591 | } |
---|
| 592 | |
---|
| 593 | |
---|
| 594 | |
---|
| 595 | static rtems_status_code m41st87_open_file(int* fd) |
---|
| 596 | { |
---|
| 597 | |
---|
| 598 | int rv = 0; |
---|
| 599 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
| 600 | |
---|
| 601 | *fd = open(M41ST87_I2C_BUS_DEVICE, O_RDWR); |
---|
| 602 | if (*fd == -1) |
---|
| 603 | sc = RTEMS_IO_ERROR; |
---|
| 604 | |
---|
| 605 | if (sc == RTEMS_SUCCESSFUL) |
---|
| 606 | { |
---|
| 607 | rv = ioctl(*fd, I2C_IOC_SET_SLAVE_ADDRESS, M41ST87_I2C_ADDRESS); |
---|
| 608 | if (rv == -1) |
---|
| 609 | sc = RTEMS_IO_ERROR; |
---|
| 610 | } |
---|
| 611 | |
---|
| 612 | return sc; |
---|
| 613 | } |
---|
| 614 | |
---|
| 615 | |
---|
| 616 | /* Read size bytes from m41st87 register address addr to buf. */ |
---|
| 617 | static rtems_status_code m41st87_read(uint8_t addr, void* buf, size_t size) |
---|
| 618 | { |
---|
| 619 | |
---|
| 620 | int fd = -1; |
---|
| 621 | int rv = 0; |
---|
| 622 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
| 623 | |
---|
| 624 | sc = m41st87_open_file(&fd); |
---|
| 625 | |
---|
| 626 | if (sc == RTEMS_SUCCESSFUL) |
---|
| 627 | { |
---|
| 628 | rv = write(fd, &addr, sizeof(addr)); |
---|
| 629 | if (rv != sizeof(addr)) |
---|
| 630 | sc = RTEMS_IO_ERROR; |
---|
| 631 | } |
---|
| 632 | |
---|
| 633 | if (sc == RTEMS_SUCCESSFUL) |
---|
| 634 | { |
---|
| 635 | rv = read(fd, buf, size); |
---|
| 636 | if (rv != size) |
---|
| 637 | sc = RTEMS_IO_ERROR; |
---|
| 638 | } |
---|
| 639 | |
---|
| 640 | rv = close(fd); |
---|
| 641 | if (rv != 0) |
---|
| 642 | sc = RTEMS_IO_ERROR; |
---|
| 643 | |
---|
| 644 | return sc; |
---|
| 645 | } |
---|
| 646 | |
---|
| 647 | |
---|
| 648 | /* Write size bytes from buf to m41st87 register address addr. */ |
---|
| 649 | static rtems_status_code m41st87_write(uint8_t addr, void* buf, size_t size) |
---|
| 650 | { |
---|
| 651 | |
---|
| 652 | int fd = -1; |
---|
| 653 | int rv = 0; |
---|
| 654 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
| 655 | /* The driver never writes many bytes. Therefore it should be less expensive |
---|
| 656 | * to reserve the maximum number of bytes that will be written in one go than |
---|
| 657 | * use a malloc. */ |
---|
| 658 | uint8_t local_buf[M41ST87_MAX_WRITE_SIZE]; |
---|
| 659 | int write_size = size + 1; |
---|
| 660 | |
---|
| 661 | assert(write_size <= M41ST87_MAX_WRITE_SIZE); |
---|
| 662 | |
---|
| 663 | local_buf[0] = addr; |
---|
| 664 | memcpy(&local_buf[1], buf, size); |
---|
| 665 | |
---|
| 666 | sc = m41st87_open_file(&fd); |
---|
| 667 | |
---|
| 668 | if (sc == RTEMS_SUCCESSFUL) |
---|
| 669 | { |
---|
| 670 | rv = write(fd, local_buf, write_size); |
---|
| 671 | if (rv != write_size) |
---|
| 672 | sc = RTEMS_IO_ERROR; |
---|
| 673 | } |
---|
| 674 | |
---|
| 675 | rv = close(fd); |
---|
| 676 | if (rv != 0) |
---|
| 677 | sc = RTEMS_IO_ERROR; |
---|
| 678 | |
---|
| 679 | return RTEMS_SUCCESSFUL; |
---|
| 680 | } |
---|
| 681 | |
---|
| 682 | |
---|
| 683 | static void altera_cyclone_v_m41st87_initialize(int minor) |
---|
| 684 | { |
---|
| 685 | |
---|
| 686 | m41st87_time_t time; |
---|
| 687 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
| 688 | uint8_t value; |
---|
| 689 | |
---|
| 690 | /* Check RTC valid */ |
---|
| 691 | sc = m41st87_read(M41ST87_ADDR_TIME, &time, sizeof(time)); |
---|
| 692 | assert(sc == RTEMS_SUCCESSFUL); |
---|
| 693 | |
---|
| 694 | if (time.seconds & M41ST87_BIT_ST) |
---|
| 695 | { |
---|
| 696 | /* RTC has been stopped. Reset stop flag. */ |
---|
| 697 | time.seconds = 0; |
---|
| 698 | /* Initialise RTC. */ |
---|
| 699 | m41st87_set_time(&time, 0, 0, 0, 1, 1, TOD_BASE_YEAR); |
---|
| 700 | sc = m41st87_write(M41ST87_ADDR_TIME, &time, sizeof(time)); |
---|
| 701 | assert(sc == RTEMS_SUCCESSFUL); |
---|
| 702 | } |
---|
| 703 | |
---|
| 704 | /* Reset HT bit */ |
---|
| 705 | sc = m41st87_read(M41ST87_ADDR_ALARM_HOUR, &value, 1); |
---|
| 706 | assert(sc == RTEMS_SUCCESSFUL); |
---|
| 707 | value &= ~M41ST87_BIT_HT; |
---|
| 708 | sc = m41st87_write(M41ST87_ADDR_ALARM_HOUR, &value, 1); |
---|
| 709 | assert(sc == RTEMS_SUCCESSFUL); |
---|
| 710 | |
---|
| 711 | } |
---|
| 712 | |
---|
| 713 | |
---|
| 714 | static int altera_cyclone_v_m41st87_get_time(int minor, rtems_time_of_day* tod) |
---|
| 715 | { |
---|
| 716 | |
---|
| 717 | m41st87_time_t time; |
---|
| 718 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
| 719 | rtems_time_of_day temp_tod; |
---|
| 720 | |
---|
| 721 | sc = m41st87_read(M41ST87_ADDR_TIME, &time, sizeof(time)); |
---|
| 722 | if (sc != RTEMS_SUCCESSFUL) |
---|
| 723 | return -sc; |
---|
| 724 | |
---|
| 725 | temp_tod.ticks = 0; |
---|
| 726 | temp_tod.second = m41st87_get_seconds(&time); |
---|
| 727 | temp_tod.minute = m41st87_get_minutes(&time); |
---|
| 728 | temp_tod.hour = m41st87_get_hours(&time); |
---|
| 729 | temp_tod.day = m41st87_get_day_of_month(&time); |
---|
| 730 | temp_tod.month = m41st87_get_month(&time); |
---|
| 731 | temp_tod.year = m41st87_get_year(&time); |
---|
| 732 | |
---|
| 733 | if (_TOD_Validate(&temp_tod)) |
---|
| 734 | memcpy(tod, &temp_tod, sizeof(temp_tod)); |
---|
| 735 | else |
---|
| 736 | sc = RTEMS_INVALID_CLOCK; |
---|
| 737 | |
---|
| 738 | return -sc; |
---|
| 739 | } |
---|
| 740 | |
---|
| 741 | |
---|
| 742 | static int altera_cyclone_v_m41st87_set_time(int minor, const rtems_time_of_day* tod) |
---|
| 743 | { |
---|
| 744 | |
---|
| 745 | m41st87_time_t time; |
---|
| 746 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
| 747 | |
---|
| 748 | /* first read to preserve the additional flags */ |
---|
| 749 | sc = m41st87_read(M41ST87_ADDR_TIME, &time, sizeof(time)); |
---|
| 750 | if (sc != RTEMS_SUCCESSFUL) |
---|
| 751 | return -sc; |
---|
| 752 | |
---|
| 753 | m41st87_set_time(&time, |
---|
| 754 | tod->second, |
---|
| 755 | tod->minute, |
---|
| 756 | tod->hour, |
---|
| 757 | tod->day, |
---|
| 758 | tod->month, |
---|
| 759 | tod->year |
---|
| 760 | ); |
---|
| 761 | |
---|
| 762 | sc = m41st87_write(M41ST87_ADDR_TIME, &time, sizeof(time)); |
---|
| 763 | |
---|
| 764 | return -sc; |
---|
| 765 | } |
---|
| 766 | |
---|
| 767 | |
---|
| 768 | static bool altera_cyclone_v_m41st87_probe(int minor) |
---|
[81329f9] | 769 | { |
---|
[a1d6f7a] | 770 | |
---|
| 771 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
| 772 | uint8_t buf; |
---|
| 773 | |
---|
| 774 | /* try to read from register address 0x00 */ |
---|
| 775 | sc = m41st87_read(0x00, &buf, 1); |
---|
| 776 | if (sc != RTEMS_SUCCESSFUL) |
---|
| 777 | /* no RTC implemented */ |
---|
| 778 | return false; |
---|
| 779 | /* try to read from register address 0x20 (implemented in M41ST87) */ |
---|
| 780 | sc = m41st87_read(0x20, &buf, 1); |
---|
| 781 | if (sc != RTEMS_SUCCESSFUL) |
---|
| 782 | /* RTC is not M41ST87 */ |
---|
| 783 | return false; |
---|
| 784 | |
---|
| 785 | printk("RTC detected, type = M41ST87\n"); |
---|
[81329f9] | 786 | return true; |
---|
[a1d6f7a] | 787 | |
---|
[81329f9] | 788 | } |
---|
| 789 | |
---|
[a1d6f7a] | 790 | |
---|
| 791 | /* **************************************** General ********************************** */ |
---|
| 792 | |
---|
| 793 | |
---|
| 794 | const rtc_fns altera_cyclone_v_ds1339_ops = |
---|
| 795 | { |
---|
| 796 | .deviceInitialize = altera_cyclone_v_ds1339_initialize, |
---|
| 797 | .deviceGetTime = altera_cyclone_v_ds1339_get_time, |
---|
| 798 | .deviceSetTime = altera_cyclone_v_ds1339_set_time |
---|
[81329f9] | 799 | }; |
---|
| 800 | |
---|
| 801 | |
---|
[a1d6f7a] | 802 | const rtc_fns altera_cyclone_v_m41st87_ops = |
---|
| 803 | { |
---|
| 804 | .deviceInitialize = altera_cyclone_v_m41st87_initialize, |
---|
| 805 | .deviceGetTime = altera_cyclone_v_m41st87_get_time, |
---|
| 806 | .deviceSetTime = altera_cyclone_v_m41st87_set_time |
---|
| 807 | }; |
---|
| 808 | |
---|
| 809 | |
---|
| 810 | size_t RTC_Count = ALTERA_CYCLONE_V_RTC_NUMBER; |
---|
| 811 | rtems_device_minor_number RTC_Minor = 0; |
---|
| 812 | |
---|
[81329f9] | 813 | |
---|
[a1d6f7a] | 814 | rtc_tbl RTC_Table[ALTERA_CYCLONE_V_RTC_NUMBER] = |
---|
| 815 | { |
---|
| 816 | { |
---|
| 817 | .sDeviceName = "/dev/rtc", |
---|
| 818 | .deviceType = RTC_CUSTOM, |
---|
| 819 | .pDeviceFns = &altera_cyclone_v_ds1339_ops, |
---|
| 820 | .deviceProbe = altera_cyclone_v_ds1339_probe, |
---|
| 821 | .pDeviceParams = NULL, |
---|
| 822 | .ulCtrlPort1 = 0, |
---|
| 823 | .ulDataPort = 0, |
---|
| 824 | .getRegister = NULL, |
---|
| 825 | .setRegister = NULL |
---|
| 826 | }, |
---|
[81329f9] | 827 | { |
---|
| 828 | .sDeviceName = "/dev/rtc", |
---|
| 829 | .deviceType = RTC_CUSTOM, |
---|
[a1d6f7a] | 830 | .pDeviceFns = &altera_cyclone_v_m41st87_ops, |
---|
| 831 | .deviceProbe = altera_cyclone_v_m41st87_probe, |
---|
[81329f9] | 832 | .pDeviceParams = NULL, |
---|
| 833 | .ulCtrlPort1 = 0, |
---|
| 834 | .ulDataPort = 0, |
---|
| 835 | .getRegister = NULL, |
---|
| 836 | .setRegister = NULL |
---|
| 837 | } |
---|
| 838 | }; |
---|