diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-23 09:55:15 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-23 15:18:44 +0200 |
commit | 4fb1b79a804ca8de866be0ef718e54e1f62fa3ec (patch) | |
tree | 11b85c98244db22c927e7f1323ed222c43a589b0 /bsps/arm/altera-cyclone-v | |
parent | bsps: Move legacy network drivers to bsps (diff) | |
download | rtems-4fb1b79a804ca8de866be0ef718e54e1f62fa3ec.tar.bz2 |
bsps: Move RTC drivers to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps/arm/altera-cyclone-v')
-rw-r--r-- | bsps/arm/altera-cyclone-v/rtc/rtc.c | 835 |
1 files changed, 835 insertions, 0 deletions
diff --git a/bsps/arm/altera-cyclone-v/rtc/rtc.c b/bsps/arm/altera-cyclone-v/rtc/rtc.c new file mode 100644 index 0000000000..e0e468892b --- /dev/null +++ b/bsps/arm/altera-cyclone-v/rtc/rtc.c @@ -0,0 +1,835 @@ +/* + * Copyright (c) 2014 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * <info@embedded-brains.de> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +/* + * Driver for the DS1339 RTC (Maxim Semiconductors) -> RTC1 + * and the M41ST87 RTC (ST Microelectronics) -> RTC2 + * + * Please note the following points: + * - The day of week is ignored. + * - The century bit is interpreted the following way: + * - century not set: TOD_BASE_YEAR .. 1999 + * - century set: 2000 .. 2099 + * - century not set: 2100 .. (TOD_BASE_YEAR + 200) + */ + +#include <libchip/rtc.h> +#include <assert.h> +#include <rtems/score/todimpl.h> +#include <sys/filio.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <bsp/i2cdrv.h> + +#define ALTERA_CYCLONE_V_RTC_NUMBER 2 + + +/* ******************************* DS1339 ********************************** */ + + +#define DS1339_I2C_ADDRESS (0xD0 >> 1) /* 7-bit addressing! */ +#define DS1339_I2C_BUS_DEVICE "/dev/i2c0" + +#define DS1339_ADDR_TIME 0x00 + +#define DS1339_ADDR_CTRL 0x0E +#define DS1339_CTRL_EOSC 0x80 +#define DS1339_CTRL_BBSQI 0x20 +#define DS1339_CTRL_RS2 0x10 +#define DS1339_CTRL_RS1 0x08 +#define DS1339_CTRL_INTCN 0x04 +#define DS1339_CTRL_A2IE 0x02 +#define DS1339_CTRL_A1IE 0x01 + +#define DS1339_CTRL_DEFAULT (0x00) + +#define DS1339_ADDR_STATUS 0x0F +#define DS1339_STATUS_OSF 0x80 +#define DS1339_STATUS_A2F 0x02 +#define DS1339_STATUS_A1F 0x01 + +#define DS1339_STATUS_CLEAR (0x00) + +#define DS1339_ADDR_TRICKLE_CHARGE 0x10 + + +typedef struct +{ + uint8_t seconds; + uint8_t minutes; + uint8_t hours; +#define DS1339_HOURS_12_24_FLAG 0x40 +#define DS1339_HOURS_AM_PM_FLAG_OR_20_HOURS 0x20 +#define DS1339_HOURS_10_HOURS 0x10 + uint8_t weekday; + uint8_t date; + uint8_t month; +#define DS1339_MONTH_CENTURY 0x80 + uint8_t year; +} +ds1339_time_t; + + +/* The longest write transmission is writing the time + one address bit */ +#define DS1339_MAX_WRITE_SIZE (sizeof(ds1339_time_t) + 1) + + +/* Functions for converting the fields */ +static unsigned int ds1339_get_seconds(ds1339_time_t* time) +{ + uint8_t tens = time->seconds >> 4; + uint8_t ones = time->seconds & 0x0F; + + return tens * 10 + ones; +} + + +static unsigned int ds1339_get_minutes(ds1339_time_t* time) +{ + uint8_t tens = time->minutes >> 4; + uint8_t ones = time->minutes & 0x0F; + + return tens * 10 + ones; +} + + +static unsigned int ds1339_get_hours(ds1339_time_t* time) +{ + + uint8_t value = time->hours & 0x0F; + + if (time->hours & DS1339_HOURS_10_HOURS) + { + value += 10; + } + if (time->hours & DS1339_HOURS_AM_PM_FLAG_OR_20_HOURS) + { + if (time->hours & DS1339_HOURS_12_24_FLAG) + value += 12; + else + value += 20; + } + + return value; +} + + +static unsigned int ds1339_get_day_of_month(ds1339_time_t* time) +{ + + uint8_t tens = time->date >> 4; + uint8_t ones = time->date & 0x0F; + + return tens * 10 + ones; +} + + +static unsigned int ds1339_get_month(ds1339_time_t* time) +{ + + uint8_t tens = (time->month >> 4) & 0x07; + uint8_t ones = time->month & 0x0F; + + return tens * 10 + ones; +} + + +static unsigned int ds1339_get_year(ds1339_time_t* time) +{ + + unsigned int year = 1900; + + year += (time->year >> 4) * 10; + year += time->year & 0x0F; + if (time->month & DS1339_MONTH_CENTURY) + year += 100; + if (year < TOD_BASE_YEAR) + year += 200; + + return year; +} + + +static void ds1339_set_time(ds1339_time_t* time, + unsigned int second, + unsigned int minute, + unsigned int hour, + unsigned int day, + unsigned int month, + unsigned int year) +{ + + unsigned int tens; + unsigned int ones; + uint8_t century = 0; + + tens = second / 10; + ones = second % 10; + time->seconds = tens << 4 | ones; + + tens = minute / 10; + ones = minute % 10; + time->minutes = tens << 4 | ones; + + tens = hour / 10; + ones = hour % 10; + time->hours = tens << 4 | ones; + + /* Weekday is not used. Therefore it can be set to an arbitrary valid value */ + time->weekday = 1; + + tens = day / 10; + ones = day % 10; + time->date = tens << 4 | ones; + + tens = month / 10; + ones = month % 10; + if ((year >= 2000) && (year < 2100)) + century = DS1339_MONTH_CENTURY; + time->month = century | tens << 4 | ones; + + tens = (year % 100) / 10; + ones = year % 10; + time->year = tens << 4 | ones; + +} + + + +static rtems_status_code ds1339_open_file(int* fd) +{ + + int rv = 0; + rtems_status_code sc = RTEMS_SUCCESSFUL; + + *fd = open(DS1339_I2C_BUS_DEVICE, O_RDWR); + if (*fd == -1) + sc = RTEMS_IO_ERROR; + + if (sc == RTEMS_SUCCESSFUL) + { + rv = ioctl(*fd, I2C_IOC_SET_SLAVE_ADDRESS, DS1339_I2C_ADDRESS); + if (rv == -1) + sc = RTEMS_IO_ERROR; + } + + return sc; +} + + +/* Read size bytes from ds1339 register address addr to buf. */ +static rtems_status_code ds1339_read(uint8_t addr, void* buf, size_t size) +{ + + int fd = -1; + int rv = 0; + rtems_status_code sc = RTEMS_SUCCESSFUL; + + sc = ds1339_open_file(&fd); + + if (sc == RTEMS_SUCCESSFUL) + { + rv = write(fd, &addr, sizeof(addr)); + if (rv != sizeof(addr)) + sc = RTEMS_IO_ERROR; + } + + if (sc == RTEMS_SUCCESSFUL) + { + rv = read(fd, buf, size); + if (rv != size) + sc = RTEMS_IO_ERROR; + } + + rv = close(fd); + if (rv != 0) + sc = RTEMS_IO_ERROR; + + return sc; +} + + +/* Write size bytes from buf to ds1339 register address addr. */ +static rtems_status_code ds1339_write(uint8_t addr, void* buf, size_t size) +{ + + int fd = -1; + int rv = 0; + rtems_status_code sc = RTEMS_SUCCESSFUL; + /* The driver never writes many bytes. Therefore it should be less expensive + * to reserve the maximum number of bytes that will be written in one go than + * use a malloc. */ + uint8_t local_buf[DS1339_MAX_WRITE_SIZE]; + int write_size = size + 1; + + assert(write_size <= DS1339_MAX_WRITE_SIZE); + + local_buf[0] = addr; + memcpy(&local_buf[1], buf, size); + + sc = ds1339_open_file(&fd); + + if (sc == RTEMS_SUCCESSFUL) + { + rv = write(fd, local_buf, write_size); + if (rv != write_size) + sc = RTEMS_IO_ERROR; + } + + rv = close(fd); + if (rv != 0) + sc = RTEMS_IO_ERROR; + + return RTEMS_SUCCESSFUL; +} + + +static void altera_cyclone_v_ds1339_initialize(int minor) +{ + + rtems_status_code sc = RTEMS_SUCCESSFUL; + uint8_t status = 0; + + /* Check RTC valid */ + sc = ds1339_read(DS1339_ADDR_STATUS, &status, sizeof(status)); + assert(sc == RTEMS_SUCCESSFUL); + + if (status & DS1339_STATUS_OSF) + { + /* RTC has been stopped. Initialise it. */ + ds1339_time_t time; + + uint8_t write = DS1339_CTRL_DEFAULT; + sc = ds1339_write(DS1339_ADDR_CTRL, &write, sizeof(write)); + assert(sc == RTEMS_SUCCESSFUL); + + write = DS1339_STATUS_CLEAR; + sc = ds1339_write(DS1339_ADDR_STATUS, &write, sizeof(write)); + assert(sc == RTEMS_SUCCESSFUL); + + ds1339_set_time(&time, 0, 0, 0, 1, 1, TOD_BASE_YEAR); + sc = ds1339_write(DS1339_ADDR_TIME, &time, sizeof(time)); + assert(sc == RTEMS_SUCCESSFUL); + } + +} + + +static int altera_cyclone_v_ds1339_get_time(int minor, rtems_time_of_day* tod) +{ + + ds1339_time_t time; + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_time_of_day temp_tod; + + sc = ds1339_read(DS1339_ADDR_TIME, &time, sizeof(time)); + + if (sc == RTEMS_SUCCESSFUL) + { + temp_tod.ticks = 0; + temp_tod.second = ds1339_get_seconds(&time); + temp_tod.minute = ds1339_get_minutes(&time); + temp_tod.hour = ds1339_get_hours(&time); + temp_tod.day = ds1339_get_day_of_month(&time); + temp_tod.month = ds1339_get_month(&time); + temp_tod.year = ds1339_get_year(&time); + + if (_TOD_Validate(&temp_tod)) + memcpy(tod, &temp_tod, sizeof(temp_tod)); + else + sc = RTEMS_INVALID_CLOCK; + } + + return -sc; +} + + +static int altera_cyclone_v_ds1339_set_time(int minor, const rtems_time_of_day* tod) +{ + + ds1339_time_t time; + rtems_status_code sc = RTEMS_SUCCESSFUL; + + ds1339_set_time(&time, + tod->second, + tod->minute, + tod->hour, + tod->day, + tod->month, + tod->year + ); + + sc = ds1339_write(DS1339_ADDR_TIME, &time, sizeof(time)); + + return -sc; +} + + +static bool altera_cyclone_v_ds1339_probe(int minor) +{ + + rtems_status_code sc = RTEMS_SUCCESSFUL; + uint8_t buf; + + /* try to read from register address 0x00 */ + sc = ds1339_read(0x00, &buf, 1); + if (sc != RTEMS_SUCCESSFUL) + /* no RTC implemented */ + return false; + /* try to read from register address 0x20 (not implemented in DS1339) */ + sc = ds1339_read(0x20, &buf, 1); + if (sc == RTEMS_SUCCESSFUL) + /* RTC is not DS1339 */ + return false; + + return true; + +} + + +/* ******************************* M41ST87 ********************************** */ + + +#define M41ST87_I2C_ADDRESS (0xD0 >> 1) /* 7-bit addressing! */ +#define M41ST87_I2C_BUS_DEVICE "/dev/i2c0" + +#define M41ST87_ADDR_TIME 0x00 + +#define M41ST87_ADDR_CTRL 0x08 +#define M41ST87_CTRL_OUT 0x80 +#define M41ST87_CTRL_FT 0x40 +#define M41ST87_CTRL_S 0x20 +#define M41ST87_CTRL_CAL 0x1F + +#define M41ST87_ADDR_ALARM_HOUR 0x0C +#define M41ST87_BIT_HT 0x40 + +#define M41ST87_ADDR_FLAGS 0x0F +#define M41ST87_FLAG_WDF 0x80 +#define M41ST87_FLAG_AF 0x40 +#define M41ST87_FLAG_BL 0x10 +#define M41ST87_FLAG_OF 0x04 +#define M41ST87_FLAG_TB1 0x02 +#define M41ST87_FLAG_TB2 0x01 + +#define M41ST87_ADDR_USER_RAM 0x20 + + +typedef struct +{ + uint8_t sec100; + uint8_t seconds; +#define M41ST87_BIT_ST 0x80 + uint8_t minutes; +#define M41ST87_BIT_OFIE 0x80 + uint8_t hours; +#define M41ST87_BIT_CB1 0x80 +#define M41ST87_BIT_CB0 0x40 + uint8_t weekday; +#define M41ST87_BIT_TR 0x80 +#define M41ST87_BIT_THS 0x40 +#define M41ST87_BIT_CLRPW1 0x20 +#define M41ST87_BIT_CLRPW0 0x10 +#define M41ST87_BIT_32KE 0x08 + uint8_t day; +#define M41ST87_BIT_PFOD 0x80 + uint8_t month; + uint8_t year; +} +m41st87_time_t; + + +/* The longest write transmission is writing the time + one address bit */ +#define M41ST87_MAX_WRITE_SIZE (sizeof(m41st87_time_t) + 1) + + +/* Functions for converting the fields */ + +/* +static unsigned int m41st87_get_sec100(m41st87_time_t* time) +{ + + uint8_t tens = time->sec100 >> 4; + uint8_t ones = time->sec100 & 0x0F; + + return tens * 10 + ones; +} +*/ + + +static unsigned int m41st87_get_seconds(m41st87_time_t* time) +{ + + uint8_t tens = (time->seconds >> 4) & 0x07; + uint8_t ones = time->seconds & 0x0F; + + return tens * 10 + ones; +} + + +static unsigned int m41st87_get_minutes(m41st87_time_t* time) +{ + + uint8_t tens = (time->minutes >> 4) & 0x07; + uint8_t ones = time->minutes & 0x0F; + + return tens * 10 + ones; +} + + +static unsigned int m41st87_get_hours(m41st87_time_t* time) +{ + + uint8_t tens = (time->hours >> 4) & 0x03; + uint8_t ones = time->hours & 0x0F; + + return tens * 10 + ones; +} + + +/* +static unsigned int m41st87_get_day_of_week(m41st87_time_t* time) +{ + + return time->weekday & 0x07; +} +*/ + + +static unsigned int m41st87_get_day_of_month(m41st87_time_t* time) +{ + + uint8_t tens = (time->day >> 4) & 0x03; + uint8_t ones = time->day & 0x0F; + + return tens * 10 + ones; +} + + +static unsigned int m41st87_get_month(m41st87_time_t* time) +{ + + uint8_t tens = (time->month >> 4) & 0x01; + uint8_t ones = time->month & 0x0F; + + return tens * 10 + ones; +} + + +static unsigned int m41st87_get_year(m41st87_time_t* time) +{ + + uint8_t century = time->hours >> 6; + uint8_t tens = time->year >> 4; + uint8_t ones = time->year & 0x0F; + + return 1900 + century * 100 + tens * 10 + ones; +} + + +static void m41st87_set_time(m41st87_time_t* time, + unsigned int second, + unsigned int minute, + unsigned int hour, + unsigned int day, + unsigned int month, + unsigned int year) +{ + + unsigned int century; + unsigned int tens; + unsigned int ones; + + if (year < 1900) + year = 1900; + if (year > 2399) + year = 2399; + century = (year - 1900) / 100; + + /* Hundreds of seconds is not used, set to 0 */ + time->sec100 = 0; + + tens = second / 10; + ones = second % 10; + time->seconds = (time->seconds & 0x80) | (tens << 4) | ones; + + tens = minute / 10; + ones = minute % 10; + time->minutes = (time->minutes & 0x80) | (tens << 4) | ones; + + tens = hour / 10; + ones = hour % 10; + time->hours = (century << 6) | (tens << 4) | ones; + + /* Weekday is not used. Therefore it can be set to an arbitrary valid value */ + time->weekday = (time->weekday & 0xF8) | 1; + + tens = day / 10; + ones = day % 10; + time->day = (time->day & 0x80) | (tens << 4) | ones; + + tens = month / 10; + ones = month % 10; + time->month = (tens << 4) | ones; + + tens = (year % 100) / 10; + ones = year % 10; + time->year = (tens << 4) | ones; + +} + + + +static rtems_status_code m41st87_open_file(int* fd) +{ + + int rv = 0; + rtems_status_code sc = RTEMS_SUCCESSFUL; + + *fd = open(M41ST87_I2C_BUS_DEVICE, O_RDWR); + if (*fd == -1) + sc = RTEMS_IO_ERROR; + + if (sc == RTEMS_SUCCESSFUL) + { + rv = ioctl(*fd, I2C_IOC_SET_SLAVE_ADDRESS, M41ST87_I2C_ADDRESS); + if (rv == -1) + sc = RTEMS_IO_ERROR; + } + + return sc; +} + + +/* Read size bytes from m41st87 register address addr to buf. */ +static rtems_status_code m41st87_read(uint8_t addr, void* buf, size_t size) +{ + + int fd = -1; + int rv = 0; + rtems_status_code sc = RTEMS_SUCCESSFUL; + + sc = m41st87_open_file(&fd); + + if (sc == RTEMS_SUCCESSFUL) + { + rv = write(fd, &addr, sizeof(addr)); + if (rv != sizeof(addr)) + sc = RTEMS_IO_ERROR; + } + + if (sc == RTEMS_SUCCESSFUL) + { + rv = read(fd, buf, size); + if (rv != size) + sc = RTEMS_IO_ERROR; + } + + rv = close(fd); + if (rv != 0) + sc = RTEMS_IO_ERROR; + + return sc; +} + + +/* Write size bytes from buf to m41st87 register address addr. */ +static rtems_status_code m41st87_write(uint8_t addr, void* buf, size_t size) +{ + + int fd = -1; + int rv = 0; + rtems_status_code sc = RTEMS_SUCCESSFUL; + /* The driver never writes many bytes. Therefore it should be less expensive + * to reserve the maximum number of bytes that will be written in one go than + * use a malloc. */ + uint8_t local_buf[M41ST87_MAX_WRITE_SIZE]; + int write_size = size + 1; + + assert(write_size <= M41ST87_MAX_WRITE_SIZE); + + local_buf[0] = addr; + memcpy(&local_buf[1], buf, size); + + sc = m41st87_open_file(&fd); + + if (sc == RTEMS_SUCCESSFUL) + { + rv = write(fd, local_buf, write_size); + if (rv != write_size) + sc = RTEMS_IO_ERROR; + } + + rv = close(fd); + if (rv != 0) + sc = RTEMS_IO_ERROR; + + return RTEMS_SUCCESSFUL; +} + + +static void altera_cyclone_v_m41st87_initialize(int minor) +{ + + m41st87_time_t time; + rtems_status_code sc = RTEMS_SUCCESSFUL; + uint8_t value; + + /* Check RTC valid */ + sc = m41st87_read(M41ST87_ADDR_TIME, &time, sizeof(time)); + assert(sc == RTEMS_SUCCESSFUL); + + if (time.seconds & M41ST87_BIT_ST) + { + /* RTC has been stopped. Reset stop flag. */ + time.seconds = 0; + /* Initialise RTC. */ + m41st87_set_time(&time, 0, 0, 0, 1, 1, TOD_BASE_YEAR); + sc = m41st87_write(M41ST87_ADDR_TIME, &time, sizeof(time)); + assert(sc == RTEMS_SUCCESSFUL); + } + + /* Reset HT bit */ + sc = m41st87_read(M41ST87_ADDR_ALARM_HOUR, &value, 1); + assert(sc == RTEMS_SUCCESSFUL); + value &= ~M41ST87_BIT_HT; + sc = m41st87_write(M41ST87_ADDR_ALARM_HOUR, &value, 1); + assert(sc == RTEMS_SUCCESSFUL); + +} + + +static int altera_cyclone_v_m41st87_get_time(int minor, rtems_time_of_day* tod) +{ + + m41st87_time_t time; + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_time_of_day temp_tod; + + sc = m41st87_read(M41ST87_ADDR_TIME, &time, sizeof(time)); + if (sc != RTEMS_SUCCESSFUL) + return -sc; + + temp_tod.ticks = 0; + temp_tod.second = m41st87_get_seconds(&time); + temp_tod.minute = m41st87_get_minutes(&time); + temp_tod.hour = m41st87_get_hours(&time); + temp_tod.day = m41st87_get_day_of_month(&time); + temp_tod.month = m41st87_get_month(&time); + temp_tod.year = m41st87_get_year(&time); + + if (_TOD_Validate(&temp_tod)) + memcpy(tod, &temp_tod, sizeof(temp_tod)); + else + sc = RTEMS_INVALID_CLOCK; + + return -sc; +} + + +static int altera_cyclone_v_m41st87_set_time(int minor, const rtems_time_of_day* tod) +{ + + m41st87_time_t time; + rtems_status_code sc = RTEMS_SUCCESSFUL; + + /* first read to preserve the additional flags */ + sc = m41st87_read(M41ST87_ADDR_TIME, &time, sizeof(time)); + if (sc != RTEMS_SUCCESSFUL) + return -sc; + + m41st87_set_time(&time, + tod->second, + tod->minute, + tod->hour, + tod->day, + tod->month, + tod->year + ); + + sc = m41st87_write(M41ST87_ADDR_TIME, &time, sizeof(time)); + + return -sc; +} + + +static bool altera_cyclone_v_m41st87_probe(int minor) +{ + + rtems_status_code sc = RTEMS_SUCCESSFUL; + uint8_t buf; + + /* try to read from register address 0x00 */ + sc = m41st87_read(0x00, &buf, 1); + if (sc != RTEMS_SUCCESSFUL) + /* no RTC implemented */ + return false; + /* try to read from register address 0x20 (implemented in M41ST87) */ + sc = m41st87_read(0x20, &buf, 1); + if (sc != RTEMS_SUCCESSFUL) + /* RTC is not M41ST87 */ + return false; + + return true; + +} + + +/* **************************************** General ********************************** */ + + +const rtc_fns altera_cyclone_v_ds1339_ops = +{ + .deviceInitialize = altera_cyclone_v_ds1339_initialize, + .deviceGetTime = altera_cyclone_v_ds1339_get_time, + .deviceSetTime = altera_cyclone_v_ds1339_set_time +}; + + +const rtc_fns altera_cyclone_v_m41st87_ops = +{ + .deviceInitialize = altera_cyclone_v_m41st87_initialize, + .deviceGetTime = altera_cyclone_v_m41st87_get_time, + .deviceSetTime = altera_cyclone_v_m41st87_set_time +}; + + +size_t RTC_Count = ALTERA_CYCLONE_V_RTC_NUMBER; + +rtc_tbl RTC_Table[ALTERA_CYCLONE_V_RTC_NUMBER] = +{ + { + .sDeviceName = "/dev/rtc", + .deviceType = RTC_CUSTOM, + .pDeviceFns = &altera_cyclone_v_ds1339_ops, + .deviceProbe = altera_cyclone_v_ds1339_probe, + .pDeviceParams = NULL, + .ulCtrlPort1 = 0, + .ulDataPort = 0, + .getRegister = NULL, + .setRegister = NULL + }, + { + .sDeviceName = "/dev/rtc", + .deviceType = RTC_CUSTOM, + .pDeviceFns = &altera_cyclone_v_m41st87_ops, + .deviceProbe = altera_cyclone_v_m41st87_probe, + .pDeviceParams = NULL, + .ulCtrlPort1 = 0, + .ulDataPort = 0, + .getRegister = NULL, + .setRegister = NULL + } +}; |