Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
CTCaer
GitHub Repository: CTCaer/hekate
Path: blob/master/bdk/rtc/max77620-rtc.c
1476 views
1
/*
2
* PMIC Real Time Clock driver for Nintendo Switch's MAX77620-RTC
3
*
4
* Copyright (c) 2018-2022 CTCaer
5
* Copyright (c) 2019 shchmue
6
*
7
* This program is free software; you can redistribute it and/or modify it
8
* under the terms and conditions of the GNU General Public License,
9
* version 2, as published by the Free Software Foundation.
10
*
11
* This program is distributed in the hope it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14
* more details.
15
*
16
* You should have received a copy of the GNU General Public License
17
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18
*/
19
20
#include <rtc/max77620-rtc.h>
21
#include <soc/i2c.h>
22
#include <soc/pmc.h>
23
#include <soc/timer.h>
24
#include <soc/t210.h>
25
26
int epoch_offset = 0;
27
28
void max77620_rtc_prep_read()
29
{
30
i2c_send_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_RTC_UPDATE0_REG, MAX77620_RTC_READ_UPDATE);
31
}
32
33
void max77620_rtc_get_time(rtc_time_t *time)
34
{
35
u8 val = 0;
36
37
// Update RTC regs from RTC clock.
38
i2c_send_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_RTC_UPDATE0_REG, MAX77620_RTC_READ_UPDATE);
39
40
// Get control reg config.
41
val = i2c_recv_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_RTC_CONTROL_REG);
42
// TODO: Check for binary format also?
43
44
// Get time.
45
time->sec = i2c_recv_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_RTC_SEC_REG) & 0x7F;
46
time->min = i2c_recv_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_RTC_MIN_REG) & 0x7F;
47
u8 hour = i2c_recv_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_RTC_HOUR_REG);
48
time->hour = hour & 0x1F;
49
50
if (!(val & MAX77620_RTC_24H) && (hour & MAX77620_RTC_HOUR_PM_MASK))
51
time->hour = (time->hour & 0xF) + 12;
52
53
// Get day of week. 1: Monday to 7: Sunday.
54
time->weekday = 0;
55
val = i2c_recv_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_RTC_WEEKDAY_REG);
56
for (int i = 0; i < 8; i++)
57
{
58
time->weekday++;
59
if (val & 1)
60
break;
61
val >>= 1;
62
}
63
64
// Get date.
65
time->day = i2c_recv_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_RTC_DATE_REG) & 0x1f;
66
time->month = (i2c_recv_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_RTC_MONTH_REG) & 0xF) - 1;
67
time->year = (i2c_recv_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_RTC_YEAR_REG) & 0x7F) + 2000;
68
}
69
70
void max77620_rtc_stop_alarm()
71
{
72
u8 val = 0;
73
74
// Update RTC regs from RTC clock.
75
i2c_send_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_RTC_UPDATE0_REG, MAX77620_RTC_READ_UPDATE);
76
msleep(16);
77
78
// Stop alarm for both ALARM1 and ALARM2. Horizon uses ALARM2.
79
for (int i = 0; i < (MAX77620_RTC_NR_TIME_REGS * 2); i++)
80
{
81
val = i2c_recv_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_ALARM1_SEC_REG + i);
82
val &= ~MAX77620_RTC_ALARM_EN_MASK;
83
i2c_send_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_ALARM1_SEC_REG + i, val);
84
}
85
86
// Update RTC clock from RTC regs.
87
i2c_send_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_RTC_UPDATE0_REG, MAX77620_RTC_WRITE_UPDATE);
88
}
89
90
void max77620_rtc_epoch_to_date(u32 epoch, rtc_time_t *time)
91
{
92
u32 tmp, edays, year, month, day;
93
94
// Set time.
95
time->sec = epoch % 60;
96
epoch /= 60;
97
time->min = epoch % 60;
98
epoch /= 60;
99
time->hour = epoch % 24;
100
epoch /= 24;
101
102
// Calculate base date values.
103
tmp = (u32)(((u64)4 * epoch + 102032) / 146097 + 15);
104
tmp = (u32)((u64)epoch + 2442113 + tmp - (tmp >> 2));
105
106
year = (20 * tmp - 2442) / 7305;
107
edays = tmp - 365 * year - (year >> 2);
108
month = edays * 1000 / 30601;
109
day = edays - month * 30 - month * 601 / 1000;
110
111
// Month/Year offset.
112
if (month < 14)
113
{
114
year -= 4716;
115
month--;
116
}
117
else
118
{
119
year -= 4715;
120
month -= 13;
121
}
122
123
// Set date.
124
time->year = year;
125
time->month = month;
126
time->day = day;
127
128
// Set weekday.
129
time->weekday = 0; //! TODO.
130
}
131
132
u32 max77620_rtc_date_to_epoch(const rtc_time_t *time)
133
{
134
u32 year, month, epoch;
135
136
//Year
137
year = time->year;
138
//Month of year
139
month = time->month;
140
141
// Month/Year offset.
142
if (month < 3)
143
{
144
month += 12;
145
year--;
146
}
147
148
epoch = (365 * year) + (year >> 2) - (year / 100) + (year / 400); // Years to days.
149
150
epoch += (30 * month) + (3 * (month + 1) / 5) + time->day; // Months to days.
151
epoch -= 719561; // Epoch time is 1/1/1970.
152
153
epoch *= 86400; // Days to seconds.
154
epoch += (3600 * time->hour) + (60 * time->min) + time->sec; // Add hours, minutes and seconds.
155
156
return epoch;
157
}
158
159
void max77620_rtc_get_time_adjusted(rtc_time_t *time)
160
{
161
max77620_rtc_get_time(time);
162
if (epoch_offset)
163
{
164
u32 epoch = (u32)((s64)max77620_rtc_date_to_epoch(time) + epoch_offset);
165
max77620_rtc_epoch_to_date(epoch, time);
166
}
167
}
168
169
void max77620_rtc_set_epoch_offset(int offset)
170
{
171
epoch_offset = offset;
172
}
173
174
void max77620_rtc_set_reboot_reason(rtc_reboot_reason_t *rr)
175
{
176
max77620_rtc_stop_alarm();
177
178
// Set reboot reason.
179
i2c_send_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_ALARM1_YEAR_REG, rr->enc.val1);
180
i2c_send_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_ALARM2_YEAR_REG, rr->enc.val2);
181
182
// Set reboot reason magic.
183
i2c_send_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_ALARM1_WEEKDAY_REG, RTC_REBOOT_REASON_MAGIC);
184
i2c_send_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_ALARM2_WEEKDAY_REG, RTC_REBOOT_REASON_MAGIC);
185
186
// Update RTC clock from RTC regs.
187
i2c_send_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_RTC_UPDATE0_REG, MAX77620_RTC_WRITE_UPDATE);
188
msleep(16);
189
}
190
191
bool max77620_rtc_get_reboot_reason(rtc_reboot_reason_t *rr)
192
{
193
u8 magic[2];
194
195
// Get reboot reason magic.
196
magic[0] = i2c_recv_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_ALARM1_WEEKDAY_REG);
197
magic[1] = i2c_recv_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_ALARM2_WEEKDAY_REG);
198
199
// Magic must be correct and match on both registers.
200
if (magic[0] != RTC_REBOOT_REASON_MAGIC || magic[0] != magic[1])
201
return false;
202
203
// Reboot reason setter is expected to have updated the actual regs already.
204
rr->enc.val1 = i2c_recv_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_ALARM1_YEAR_REG);
205
rr->enc.val2 = i2c_recv_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_ALARM2_YEAR_REG);
206
207
// Clear magic and update actual regs.
208
i2c_send_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_ALARM1_WEEKDAY_REG, 0);
209
i2c_send_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_ALARM2_WEEKDAY_REG, 0);
210
i2c_send_byte(I2C_5, MAX77620_RTC_I2C_ADDR, MAX77620_RTC_UPDATE0_REG, MAX77620_RTC_WRITE_UPDATE);
211
212
// Return reboot reason. False if [config] was selected.
213
return true;
214
}
215
216