Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RTC] Update for setdatetime and settime #23

Merged
merged 7 commits into from
Nov 29, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 97 additions & 62 deletions source/rtc.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,38 @@
#define STAT_POWER (0x80)

/* Time flags */
#define TM_TEST (0x80)
#define TM_TEST (0x00800000)

/* Init results */
#define INIT_OK (0x00)
#define INIT_EPOWER (0x01)
#define INIT_E12HOUR (0x02)
#define INIT_ENORTC (0x02)

// Mask out data not needed from date and time
#define PM_TIME_FLAG ((unsigned int)0x00000080)
#define HOURS_MASK ((unsigned int)0x000000BF)
#define TIME_MASK (0x00FF7FBF)
#define DATE_MASK (0x073F1FFF)

// Amount of iterations to wait after writing the data,
// before touching other RTC-related registers. Dependant on optimization level
#define TIMEOUT_CYCLES_WAIT_AFTER_WRITE_PER_BYTE 1858

/* Compiler hacks */
#define assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)
#define unlikely(cond) __builtin_expect(!!(cond), 0)

static unsigned int rtc_read(unsigned int len);
static void rtc_write(unsigned int data, unsigned int len);
static void rtc_cmd(unsigned int cmd);
static void rtc_cmd_arg(unsigned int cmd, unsigned int data, unsigned int len);
static void rtc_cmd_arg_datetime(unsigned int cmd, __agbabi_datetime_t datetime);
static void rtc_cmd_arg_datetime(unsigned int cmd, __agbabi_datetime_t datetime, int is_12hr);
static void rtc_reset(void);
static unsigned int rtc_get_status(void);
static void rtc_set_status_24hr(void);
static int rtc_get_is_12hr(void);
static unsigned int get_time_pm_fix(unsigned int time);
static unsigned int set_time_pm_fix(unsigned int time, int is_12hr);

static int bcd_decode(unsigned int x) __attribute__((const));
static unsigned int bcd_encode(int x) __attribute__((const));
Expand Down Expand Up @@ -90,12 +104,9 @@ static unsigned int rtc_read(unsigned int len) {
return data;
}

static void rtc_cmd(const unsigned int cmd) {
*MMIO_GPIO_WRITE_ENABLE = RTC_CS | RTC_DATA | RTC_SCK;

int ii = 8;
while (ii--) {
unsigned int output = cmd >> ii & 1;
static void rtc_write(unsigned int data, unsigned int len) {
for (unsigned int ii = 0; ii < len; ++ii) {
unsigned int output = data >> ii & 1;
__asm__ volatile (
"strh %[select], [%[gpio]]" "\n\t"
"strh %[select], [%[gpio]]" "\n\t"
Expand All @@ -108,10 +119,10 @@ static void rtc_cmd(const unsigned int cmd) {
}
}

static void rtc_cmd_arg(const unsigned int cmd, unsigned int data, unsigned int len) {
static void rtc_cmd(const unsigned int cmd) {
*MMIO_GPIO_WRITE_ENABLE = RTC_CS | RTC_DATA | RTC_SCK;

unsigned ii = 8;
int ii = 8;
while (ii--) {
unsigned int output = cmd >> ii & 1;
__asm__ volatile (
Expand All @@ -124,51 +135,35 @@ static void rtc_cmd_arg(const unsigned int cmd, unsigned int data, unsigned int
[clock]"l"(RTC_CS | output << 1 | RTC_SCK)
);
}
}

static void rtc_cmd_arg(const unsigned int cmd, unsigned int data, unsigned int len) {
rtc_cmd(cmd);

assume(len > 0 && len <= 32);
for (ii = 0; ii < len; ++ii) {
unsigned int output = data >> ii & 1;
__asm__ volatile (
"strh %[select], [%[gpio]]" "\n\t"
"strh %[select], [%[gpio]]" "\n\t"
"strh %[select], [%[gpio]]" "\n\t"
"strh %[clock], [%[gpio]]"
:: [gpio]"l"(MMIO_GPIO_DATA),
[select]"l"(RTC_CS | output << 1),
[clock]"l"(RTC_CS | output << 1 | RTC_SCK)
);
}
rtc_write(data, len);

__asm__ volatile (
"L1%=: subs %[wait], #1" "\n\t"
"bne L1%="
:: [wait]"l"(((((TIMEOUT_CYCLES_WAIT_AFTER_WRITE_PER_BYTE * len) + 7) / 8) + 11 + 6) / 12)
);
}

static void rtc_cmd_arg_datetime(unsigned int cmd, __agbabi_datetime_t datetime) {
*MMIO_GPIO_WRITE_ENABLE = RTC_CS | RTC_DATA | RTC_SCK;
static void rtc_cmd_arg_datetime(unsigned int cmd, __agbabi_datetime_t datetime, int is_12hr) {
rtc_cmd(cmd);

unsigned ii = 8;
while (ii--) {
unsigned int output = cmd >> ii & 1;
__asm__ volatile (
"strh %[select], [%[gpio]]" "\n\t"
"strh %[select], [%[gpio]]" "\n\t"
"strh %[select], [%[gpio]]" "\n\t"
"strh %[clock], [%[gpio]]"
:: [gpio]"l"(MMIO_GPIO_DATA),
[select]"l"(RTC_CS | output << 1),
[clock]"l"(RTC_CS | output << 1 | RTC_SCK)
);
}
unsigned int date = datetime[0];
unsigned int time = set_time_pm_fix(datetime[1], is_12hr);

for (ii = 0; ii < 55; ++ii) {
unsigned int output = (datetime >> ii)[0] & 1;
__asm__ volatile (
"strh %[select], [%[gpio]]" "\n\t"
"strh %[select], [%[gpio]]" "\n\t"
"strh %[select], [%[gpio]]" "\n\t"
"strh %[clock], [%[gpio]]"
:: [gpio]"l"(MMIO_GPIO_DATA),
[select]"l"(RTC_CS | output << 1),
[clock]"l"(RTC_CS | output << 1 | RTC_SCK)
);
}
rtc_write(date, 32);
rtc_write(time, 24);

__asm__ volatile (
"L1%=: subs %[wait], #1" "\n\t"
"bne L1%="
:: [wait]"l"(((TIMEOUT_CYCLES_WAIT_AFTER_WRITE_PER_BYTE * 7) + 11 + 6) / 12)
);
}

static void rtc_reset(void) {
Expand Down Expand Up @@ -213,19 +208,59 @@ static void rtc_set_status_24hr(void) {
*MMIO_GPIO_DATA = RTC_SCK;
}

static int rtc_get_is_12hr(void) {
int is_12hr = 0;
unsigned int status = rtc_get_status();
if((status & STAT_24HOUR) == 0)
is_12hr = 1;

return is_12hr;
}

static unsigned int get_time_pm_fix(unsigned int time) {
unsigned hour = time & HOURS_MASK;
time &= (~HOURS_MASK);
if((hour & PM_TIME_FLAG) && (hour & (~PM_TIME_FLAG)) <= 0x11) {
hour += 0x12;
// BCD Math
if((hour & 0xF) >= 0xA)
hour += 0x10 - 0xA;
}

return (time | hour) & (~PM_TIME_FLAG);
}

static unsigned int set_time_pm_fix(unsigned int time, int is_12hr) {
time &= (~PM_TIME_FLAG);
unsigned hour = time & HOURS_MASK;
time &= (~HOURS_MASK);
if((hour >= 0x12) && is_12hr) {
hour -= 0x12;
// BCD Math
if((hour & 0xF) >= 0xE)
hour -= (0xE - 0x8);
hour |= PM_TIME_FLAG;
}

return time | hour;
}

int __agbabi_rtc_init(void) {
*MMIO_GPIO_ENABLE = 1;

unsigned int status = rtc_get_status();

if (unlikely((status & STAT_POWER) == STAT_POWER || (status & STAT_24HOUR) == 0)) {
if (unlikely((status & STAT_POWER) == STAT_POWER)) {
rtc_reset();
rtc_set_status_24hr();
}

const unsigned int time = __agbabi_rtc_time();
const __agbabi_datetime_t datetime = __agbabi_rtc_datetime();

if(unlikely(datetime[0] == 0))
return INIT_ENORTC;

if (time & TM_TEST) {
if (datetime[1] & TM_TEST) {
rtc_reset(); /* Reset to leave test mode */
rtc_set_status_24hr();
}
Expand All @@ -236,10 +271,6 @@ int __agbabi_rtc_init(void) {
return INIT_EPOWER;
}

if (unlikely((status & STAT_24HOUR) == 0)) {
return INIT_E12HOUR;
}

return INIT_OK;
}

Expand All @@ -251,7 +282,7 @@ unsigned int __agbabi_rtc_time(void) {
*MMIO_GPIO_DATA = RTC_CS | RTC_SCK;

rtc_cmd(cmd_time);
const unsigned int time = rtc_read(23);
const unsigned int time = get_time_pm_fix(rtc_read(24) & TIME_MASK);

*MMIO_GPIO_DATA = RTC_SCK;
*MMIO_GPIO_DATA = RTC_SCK;
Expand All @@ -260,13 +291,15 @@ unsigned int __agbabi_rtc_time(void) {
}

void __agbabi_rtc_settime(const unsigned int time) {
int is_12hr = rtc_get_is_12hr();

static const unsigned int cmd_time = MAKE_WRITE_CMD(0x3);

*MMIO_GPIO_WRITE_ENABLE = RTC_CS | RTC_SCK;
*MMIO_GPIO_DATA = RTC_SCK;
*MMIO_GPIO_DATA = RTC_CS | RTC_SCK;

rtc_cmd_arg(cmd_time, time, 23);
rtc_cmd_arg(cmd_time, set_time_pm_fix(time, is_12hr), 24);

*MMIO_GPIO_DATA = RTC_SCK;
*MMIO_GPIO_DATA = RTC_SCK;
Expand All @@ -281,8 +314,8 @@ __agbabi_datetime_t __agbabi_rtc_datetime(void) {

rtc_cmd(cmd_datetime);
const __agbabi_datetime_t datetime = {
rtc_read(32),
rtc_read(23)
rtc_read(32) & DATE_MASK,
get_time_pm_fix(rtc_read(24) & TIME_MASK)
};

*MMIO_GPIO_DATA = RTC_SCK;
Expand All @@ -292,13 +325,15 @@ __agbabi_datetime_t __agbabi_rtc_datetime(void) {
}

void __agbabi_rtc_setdatetime(const __agbabi_datetime_t datetime) {
int is_12hr = rtc_get_is_12hr();

static const unsigned int cmd_datetime = MAKE_WRITE_CMD(0x2);

*MMIO_GPIO_WRITE_ENABLE = RTC_CS | RTC_SCK;
*MMIO_GPIO_DATA = RTC_SCK;
*MMIO_GPIO_DATA = RTC_CS | RTC_SCK;

rtc_cmd_arg_datetime(cmd_datetime, datetime);
rtc_cmd_arg_datetime(cmd_datetime, datetime, is_12hr);

*MMIO_GPIO_DATA = RTC_SCK;
*MMIO_GPIO_DATA = RTC_SCK;
Expand Down