diff options
author | Marcin Bukat <marcin.bukat@gmail.com> | 2010-12-07 22:04:02 +0000 |
---|---|---|
committer | Marcin Bukat <marcin.bukat@gmail.com> | 2010-12-07 22:04:02 +0000 |
commit | 3b6b4f90509acaa068d78cce746ef83318d85903 (patch) | |
tree | 2a33c7003320f8071a9cb0d9d36ceffb7ac43cdd /firmware | |
parent | 1930e9f4baee8086195e147b9580ebbf3042b3cc (diff) | |
download | rockbox-3b6b4f90509acaa068d78cce746ef83318d85903.tar.gz rockbox-3b6b4f90509acaa068d78cce746ef83318d85903.zip |
RTC s35380a - proper alarm support
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28763 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/drivers/rtc/rtc_s35380a.c | 189 |
1 files changed, 116 insertions, 73 deletions
diff --git a/firmware/drivers/rtc/rtc_s35380a.c b/firmware/drivers/rtc/rtc_s35380a.c index 9f935b9541..05796e0462 100644 --- a/firmware/drivers/rtc/rtc_s35380a.c +++ b/firmware/drivers/rtc/rtc_s35380a.c | |||
@@ -66,6 +66,35 @@ | |||
66 | #define STATUS_REG2_INT1ME 0x40 | 66 | #define STATUS_REG2_INT1ME 0x40 |
67 | #define STATUS_REG2_INT1FE 0x80 | 67 | #define STATUS_REG2_INT1FE 0x80 |
68 | 68 | ||
69 | /* REALTIME_DATA register bytes */ | ||
70 | #define TIME_YEAR 0 | ||
71 | #define TIME_MONTH 1 | ||
72 | #define TIME_DAY 2 | ||
73 | #define TIME_WEEKDAY 3 | ||
74 | #define TIME_HOUR 4 | ||
75 | #define TIME_MINUTE 5 | ||
76 | #define TIME_SECOND 6 | ||
77 | #define TIME_REG_SIZE 7 | ||
78 | |||
79 | /* INT1, INT2 register bytes */ | ||
80 | #define ALARM_WEEKDAY 0 | ||
81 | #define ALARM_HOUR 1 | ||
82 | #define ALARM_MINUTE 2 | ||
83 | #define ALARM_REG_SIZE 3 | ||
84 | |||
85 | /* INT1, INT2 register bits */ | ||
86 | #define A1WE 0x80 | ||
87 | #define A1HE 0x80 | ||
88 | #define A1mE 0x80 | ||
89 | |||
90 | #define A2WE 0x80 | ||
91 | #define A2HE 0x80 | ||
92 | #define A2mE 0x80 | ||
93 | |||
94 | #define AMPM 0x40 | ||
95 | |||
96 | static bool int_flag; | ||
97 | |||
69 | static void reverse_bits(unsigned char* v, int size) | 98 | static void reverse_bits(unsigned char* v, int size) |
70 | { | 99 | { |
71 | static const unsigned char flipnibble[] = | 100 | static const unsigned char flipnibble[] = |
@@ -79,75 +108,88 @@ static void reverse_bits(unsigned char* v, int size) | |||
79 | } | 108 | } |
80 | } | 109 | } |
81 | 110 | ||
111 | static inline void rtc_reset(void) | ||
112 | { | ||
113 | unsigned char reg = STATUS_REG1_RESET; | ||
114 | i2c_write(I2C_IFACE_1, RTC_ADDR|(STATUS_REG1<<1), ®, 1); | ||
115 | } | ||
116 | |||
82 | void rtc_init(void) | 117 | void rtc_init(void) |
83 | { | 118 | { |
84 | unsigned char status_reg; | 119 | unsigned char reg; |
85 | i2c_read(I2C_IFACE_1, RTC_ADDR | (STATUS_REG1<<1), &status_reg, 1); | 120 | static bool initialized = false; |
86 | 121 | ||
87 | if ( (status_reg & STATUS_REG1_POC) || | 122 | if ( initialized ) |
88 | (status_reg & STATUS_REG1_BLD) ) | 123 | return; |
89 | { | 124 | |
90 | /* perform rtc reset*/ | 125 | i2c_read(I2C_IFACE_1, RTC_ADDR|(STATUS_REG1<<1), ®, 1); |
91 | status_reg |= STATUS_REG1_RESET; | 126 | |
92 | i2c_write(I2C_IFACE_1, RTC_ADDR | (STATUS_REG1<<1), &status_reg, 1); | 127 | /* cache INT1, INT2 flags as reading the register seem to clear |
93 | } | 128 | * this bits (which is not described in datasheet) |
129 | */ | ||
130 | int_flag = ((reg & STATUS_REG1_INT1) || (reg & STATUS_REG1_INT2)); | ||
131 | |||
132 | /* test POC and BLD flags */ | ||
133 | if ( (reg & STATUS_REG1_POC) || (reg & STATUS_REG1_BLD)) | ||
134 | rtc_reset(); | ||
135 | |||
136 | i2c_read(I2C_IFACE_1, RTC_ADDR|(STATUS_REG2<<1), ®, 1); | ||
137 | |||
138 | /* test TEST flag */ | ||
139 | if ( reg & STATUS_REG2_TEST ) | ||
140 | rtc_reset(); | ||
94 | 141 | ||
95 | /* setup 24h time format */ | 142 | /* setup 24h time format */ |
96 | status_reg = STATUS_REG1_H1224; | 143 | reg = STATUS_REG1_H1224; |
97 | i2c_write(I2C_IFACE_1, RTC_ADDR | (STATUS_REG1<<1), &status_reg, 1); | 144 | i2c_write(I2C_IFACE_1, RTC_ADDR|(STATUS_REG1<<1), ®, 1); |
98 | 145 | ||
99 | #ifdef HAVE_RTC_ALARM | 146 | initialized = true; |
100 | rtc_check_alarm_started(false); | ||
101 | #endif | ||
102 | /* disable all alarms */ | ||
103 | status_reg = 0; | ||
104 | i2c_write(I2C_IFACE_1, RTC_ADDR | (STATUS_REG2<<1), &status_reg, 1); | ||
105 | } | 147 | } |
106 | 148 | ||
107 | int rtc_read_datetime(struct tm *tm) | 149 | int rtc_read_datetime(struct tm *tm) |
108 | { | 150 | { |
109 | unsigned char buf[7]; | 151 | unsigned char buf[TIME_REG_SIZE]; |
110 | unsigned int i; | 152 | unsigned int i; |
111 | int ret; | 153 | int ret; |
112 | 154 | ||
113 | ret = i2c_read(I2C_IFACE_1, RTC_ADDR | (REALTIME_DATA1<<1), buf, sizeof(buf)); | 155 | ret = i2c_read(I2C_IFACE_1, RTC_ADDR|(REALTIME_DATA1<<1), buf, sizeof(buf)); |
114 | reverse_bits(buf, sizeof(buf)); | 156 | reverse_bits(buf, sizeof(buf)); |
115 | 157 | ||
116 | buf[4] &= 0x3f; /* mask out p.m. flag */ | 158 | buf[TIME_HOUR] &= 0x3f; /* mask out p.m. flag */ |
117 | 159 | ||
118 | for (i = 0; i < sizeof(buf); i++) | 160 | for (i = 0; i < sizeof(buf); i++) |
119 | buf[i] = BCD2DEC(buf[i]); | 161 | buf[i] = BCD2DEC(buf[i]); |
120 | 162 | ||
121 | tm->tm_sec = buf[6]; | 163 | tm->tm_sec = buf[TIME_SECOND]; |
122 | tm->tm_min = buf[5]; | 164 | tm->tm_min = buf[TIME_MINUTE]; |
123 | tm->tm_hour = buf[4]; | 165 | tm->tm_hour = buf[TIME_HOUR]; |
124 | tm->tm_wday = buf[3]; | 166 | tm->tm_wday = buf[TIME_WEEKDAY]; |
125 | tm->tm_mday = buf[2]; | 167 | tm->tm_mday = buf[TIME_DAY]; |
126 | tm->tm_mon = buf[1] - 1; | 168 | tm->tm_mon = buf[TIME_MONTH] - 1; |
127 | tm->tm_year = buf[0] + 100; | 169 | tm->tm_year = buf[TIME_YEAR] + 100; |
128 | 170 | ||
129 | return ret; | 171 | return ret; |
130 | } | 172 | } |
131 | 173 | ||
132 | int rtc_write_datetime(const struct tm *tm) | 174 | int rtc_write_datetime(const struct tm *tm) |
133 | { | 175 | { |
134 | unsigned char buf[7]; | 176 | unsigned char buf[TIME_REG_SIZE]; |
135 | unsigned int i; | 177 | unsigned int i; |
136 | int ret; | 178 | int ret; |
137 | 179 | ||
138 | buf[6] = tm->tm_sec; | 180 | buf[TIME_SECOND] = tm->tm_sec; |
139 | buf[5] = tm->tm_min; | 181 | buf[TIME_MINUTE] = tm->tm_min; |
140 | buf[4] = tm->tm_hour; | 182 | buf[TIME_HOUR] = tm->tm_hour; |
141 | buf[3] = tm->tm_wday; | 183 | buf[TIME_WEEKDAY] = tm->tm_wday; |
142 | buf[2] = tm->tm_mday; | 184 | buf[TIME_DAY] = tm->tm_mday; |
143 | buf[1] = tm->tm_mon + 1; | 185 | buf[TIME_MONTH] = tm->tm_mon + 1; |
144 | buf[0] = tm->tm_year - 100; | 186 | buf[TIME_YEAR] = tm->tm_year - 100; |
145 | 187 | ||
146 | for (i = 0; i < sizeof(buf); i++) | 188 | for (i = 0; i < sizeof(buf); i++) |
147 | buf[i] = DEC2BCD(buf[i]); | 189 | buf[i] = DEC2BCD(buf[i]); |
148 | 190 | ||
149 | reverse_bits(buf, sizeof(buf)); | 191 | reverse_bits(buf, sizeof(buf)); |
150 | ret = i2c_write(I2C_IFACE_1, RTC_ADDR | (REALTIME_DATA1<<1), buf, sizeof(buf)); | 192 | ret = i2c_write(I2C_IFACE_1, RTC_ADDR|(REALTIME_DATA1<<1), buf, sizeof(buf)); |
151 | 193 | ||
152 | return ret; | 194 | return ret; |
153 | } | 195 | } |
@@ -155,75 +197,76 @@ int rtc_write_datetime(const struct tm *tm) | |||
155 | #ifdef HAVE_RTC_ALARM | 197 | #ifdef HAVE_RTC_ALARM |
156 | void rtc_set_alarm(int h, int m) | 198 | void rtc_set_alarm(int h, int m) |
157 | { | 199 | { |
158 | /* 1) get the date | 200 | unsigned char buf[ALARM_REG_SIZE]; |
159 | * 2) compare h:m with current time | ||
160 | if alarm time < current time set day += 1 | ||
161 | 3) check day if it is not needed to wrap around | ||
162 | 4) set the validity bits | ||
163 | 5) write to alarm register | ||
164 | */ | ||
165 | 201 | ||
166 | unsigned char buf[3]; | 202 | /* INT1 register can be accessed only when IN1AE flag is set */ |
167 | unsigned char reg; | 203 | rtc_enable_alarm(true); |
168 | 204 | ||
169 | /* 0x80 - validity flag */ | 205 | /* A1mE, A1HE - validity flags */ |
170 | buf[2] = DEC2BCD(m) | 0x80; | 206 | buf[ALARM_MINUTE] = DEC2BCD(m) | A1mE; |
171 | buf[1] = DEC2BCD(h) | 0x80; | 207 | buf[ALARM_HOUR] = DEC2BCD(h) | A1HE; |
172 | buf[0] = 0; | 208 | buf[ALARM_WEEKDAY] = 0; |
173 | 209 | ||
174 | reverse_bits(buf, sizeof(buf)); | 210 | /* AM/PM flag have to be set properly regardles of |
211 | * time format used (H1224 flag in STATUS_REG1) | ||
212 | * this is not described in datasheet for s35380a | ||
213 | * but is somehow described in datasheet for s35390a | ||
214 | */ | ||
215 | if ( h >= 12 ) | ||
216 | buf[ALARM_HOUR] |= AMPM; | ||
175 | 217 | ||
176 | reg = STATUS_REG2_INT1AE; | 218 | reverse_bits(buf, sizeof(buf)); |
177 | i2c_write(I2C_IFACE_1, RTC_ADDR | (STATUS_REG2<<1), ®, 1); | 219 | i2c_write(I2C_IFACE_1, RTC_ADDR|(INT1_REG<<1), buf, sizeof(buf)); |
178 | i2c_write(I2C_IFACE_1, RTC_ADDR | (INT1_REG<<1), buf, sizeof(buf)); | ||
179 | } | 220 | } |
180 | 221 | ||
181 | void rtc_get_alarm(int *h, int *m) | 222 | void rtc_get_alarm(int *h, int *m) |
182 | { | 223 | { |
183 | unsigned char buf[3]; | 224 | unsigned char buf[ALARM_REG_SIZE]; |
184 | unsigned char reg,reg2; | ||
185 | |||
186 | i2c_read(I2C_IFACE_1, RTC_ADDR | (STATUS_REG2<<1), ®, 1); | ||
187 | 225 | ||
188 | reg2 = reg | STATUS_REG2_INT1AE; | 226 | /* INT1 alarm register can be accessed only when INT1AE is set */ |
189 | i2c_write(I2C_IFACE_1, RTC_ADDR | (STATUS_REG2<<1), ®2, 1); | 227 | rtc_enable_alarm(true); |
190 | i2c_read(I2C_IFACE_1, RTC_ADDR | (INT1_REG<<1), buf, sizeof(buf)); | ||
191 | i2c_write(I2C_IFACE_1, RTC_ADDR | (STATUS_REG2<<1), ®, 1); | ||
192 | 228 | ||
229 | /* read the content of INT1 register */ | ||
230 | i2c_read(I2C_IFACE_1, RTC_ADDR|(INT1_REG<<1), buf, sizeof(buf)); | ||
193 | reverse_bits(buf, sizeof(buf)); | 231 | reverse_bits(buf, sizeof(buf)); |
194 | 232 | ||
195 | *h = BCD2DEC(buf[1] & 0x3f); /* mask out A1HE and PM/AM flag */ | 233 | *h = BCD2DEC(buf[ALARM_HOUR] & 0x3f); /* mask out A1HE and PM/AM flag */ |
196 | *m = BCD2DEC(buf[2] & 0x7f); /* mask out A1mE */ | 234 | *m = BCD2DEC(buf[ALARM_MINUTE] & 0x7f); /* mask out A1mE */ |
235 | |||
236 | rtc_enable_alarm(false); | ||
197 | } | 237 | } |
198 | 238 | ||
199 | bool rtc_check_alarm_flag(void) | 239 | bool rtc_check_alarm_flag(void) |
200 | { | 240 | { |
201 | unsigned char status_reg; | 241 | unsigned char reg; |
202 | i2c_read(I2C_IFACE_1, RTC_ADDR | (STATUS_REG1<<1), &status_reg, 1); | 242 | i2c_read(I2C_IFACE_1, RTC_ADDR|(STATUS_REG1<<1), ®, 1); |
203 | 243 | ||
204 | return (status_reg & (STATUS_REG1_INT1 | STATUS_REG1_INT2)); | 244 | return ((reg & STATUS_REG1_INT1) || (reg & STATUS_REG1_INT2)); |
205 | } | 245 | } |
206 | 246 | ||
207 | void rtc_enable_alarm(bool enable) | 247 | void rtc_enable_alarm(bool enable) |
208 | { | 248 | { |
209 | unsigned char status_reg2; | 249 | unsigned char reg = 0; |
210 | status_reg2 = enable ? STATUS_REG2_INT1AE:0; | 250 | |
211 | i2c_write(I2C_IFACE_1, RTC_ADDR | (STATUS_REG2<<1), &status_reg2, 1); | 251 | if (enable) |
252 | reg = STATUS_REG2_INT1AE; | ||
253 | |||
254 | i2c_write(I2C_IFACE_1, RTC_ADDR|(STATUS_REG2<<1), ®, 1); | ||
212 | } | 255 | } |
213 | 256 | ||
214 | bool rtc_check_alarm_started(bool release_alarm) | 257 | bool rtc_check_alarm_started(bool release_alarm) |
215 | { | 258 | { |
216 | static bool run_before = false, alarm_state; | 259 | static bool run_before; |
217 | bool rc; | 260 | bool rc; |
218 | 261 | ||
219 | if (run_before) | 262 | if (run_before) |
220 | { | 263 | { |
221 | rc = alarm_state; | 264 | rc = int_flag; |
222 | alarm_state &= ~release_alarm; | 265 | int_flag &= ~release_alarm; |
223 | } | 266 | } |
224 | else | 267 | else |
225 | { | 268 | { |
226 | rc = rtc_check_alarm_flag(); | 269 | rc = int_flag; |
227 | run_before = true; | 270 | run_before = true; |
228 | } | 271 | } |
229 | 272 | ||