forked from Imagelibrary/rtems
854 lines
20 KiB
C
854 lines
20 KiB
C
/* SPDX-License-Identifier: BSD-2-Clause */
|
|
|
|
/**
|
|
* @file
|
|
*
|
|
* @ingroup RTEMSBSPsARMCycV
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2014 embedded brains GmbH & Co. KG
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* 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 <rtems/rtems/clockimpl.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);
|
|
|
|
sc = _TOD_Validate(&temp_tod, TOD_ENABLE_TICKS_VALIDATION);
|
|
if (sc == RTEMS_SUCCESSFUL)
|
|
memcpy(tod, &temp_tod, sizeof(temp_tod));
|
|
}
|
|
|
|
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);
|
|
|
|
sc = _TOD_Validate(&temp_tod, TOD_ENABLE_TICKS_VALIDATION);
|
|
if (sc == RTEMS_SUCCESSFUL)
|
|
memcpy(tod, &temp_tod, sizeof(temp_tod));
|
|
|
|
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
|
|
}
|
|
};
|