]>
Commit | Line | Data |
---|---|---|
0b326fc2 HN |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * (C) Copyright 2019, Vaisala Oyj | |
4 | */ | |
5 | ||
6 | #include <common.h> | |
7 | #include <command.h> | |
8 | #include <dm.h> | |
9 | #include <i2c.h> | |
10 | #include <rtc.h> | |
336d4615 | 11 | #include <dm/device_compat.h> |
cd93d625 | 12 | #include <linux/bitops.h> |
0b326fc2 HN |
13 | |
14 | /* | |
15 | * RTC register addresses | |
16 | */ | |
17 | #define RTC_SEC_REG_ADDR 0x00 | |
18 | #define RTC_MIN_REG_ADDR 0x01 | |
19 | #define RTC_HR_REG_ADDR 0x02 | |
20 | #define RTC_DAY_REG_ADDR 0x03 | |
21 | #define RTC_DATE_REG_ADDR 0x04 | |
22 | #define RTC_MON_REG_ADDR 0x05 | |
23 | #define RTC_YR_REG_ADDR 0x06 | |
24 | #define RTC_CTL_REG_ADDR 0x0e | |
25 | #define RTC_STAT_REG_ADDR 0x0f | |
26 | #define RTC_TEST_REG_ADDR 0x13 | |
27 | ||
28 | /* | |
29 | * RTC control register bits | |
30 | */ | |
31 | #define RTC_CTL_BIT_A1IE BIT(0) /* Alarm 1 interrupt enable */ | |
32 | #define RTC_CTL_BIT_A2IE BIT(1) /* Alarm 2 interrupt enable */ | |
33 | #define RTC_CTL_BIT_INTCN BIT(2) /* Interrupt control */ | |
34 | #define RTC_CTL_BIT_DOSC BIT(7) /* Disable Oscillator */ | |
35 | ||
36 | /* | |
37 | * RTC status register bits | |
38 | */ | |
39 | #define RTC_STAT_BIT_A1F BIT(0) /* Alarm 1 flag */ | |
40 | #define RTC_STAT_BIT_A2F BIT(1) /* Alarm 2 flag */ | |
41 | #define RTC_STAT_BIT_EN32KHZ BIT(3) /* Enable 32KHz Output */ | |
42 | #define RTC_STAT_BIT_BB32KHZ BIT(6) /* Battery backed 32KHz Output */ | |
43 | #define RTC_STAT_BIT_OSF BIT(7) /* Oscillator stop flag */ | |
44 | ||
45 | /* | |
46 | * RTC test register bits | |
47 | */ | |
48 | #define RTC_TEST_BIT_SWRST BIT(7) /* Software reset */ | |
49 | ||
50 | #define RTC_DATE_TIME_REG_SIZE 7 | |
51 | #define RTC_SRAM_START 0x14 | |
52 | #define RTC_SRAM_END 0xFF | |
53 | #define RTC_SRAM_SIZE 236 | |
54 | ||
55 | struct ds3232_priv_data { | |
56 | u8 max_register; | |
57 | u8 sram_start; | |
58 | int sram_size; | |
59 | }; | |
60 | ||
61 | static int ds3232_rtc_read8(struct udevice *dev, unsigned int reg) | |
62 | { | |
63 | int ret; | |
64 | u8 buf; | |
65 | struct ds3232_priv_data *priv_data; | |
66 | ||
67 | priv_data = dev_get_priv(dev); | |
68 | if (!priv_data) | |
69 | return -EINVAL; | |
70 | ||
71 | if (reg > priv_data->max_register) | |
72 | return -EINVAL; | |
73 | ||
74 | ret = dm_i2c_read(dev, reg, &buf, sizeof(buf)); | |
75 | if (ret < 0) | |
76 | return ret; | |
77 | ||
78 | return buf; | |
79 | } | |
80 | ||
81 | static int ds3232_rtc_write8(struct udevice *dev, unsigned int reg, int val) | |
82 | { | |
83 | u8 buf = (u8)val; | |
84 | struct ds3232_priv_data *priv_data; | |
85 | ||
86 | priv_data = dev_get_priv(dev); | |
87 | if (!priv_data) | |
88 | return -EINVAL; | |
89 | ||
90 | if (reg > priv_data->max_register) | |
91 | return -EINVAL; | |
92 | ||
93 | return dm_i2c_write(dev, reg, &buf, sizeof(buf)); | |
94 | } | |
95 | ||
96 | static int reset_sram(struct udevice *dev) | |
97 | { | |
98 | int ret, sram_end, reg; | |
99 | struct ds3232_priv_data *priv_data; | |
100 | ||
101 | priv_data = dev_get_priv(dev); | |
102 | if (!priv_data) | |
103 | return -EINVAL; | |
104 | ||
105 | sram_end = priv_data->sram_start + priv_data->sram_size; | |
106 | ||
107 | for (reg = priv_data->sram_start; reg < sram_end; reg++) { | |
108 | ret = ds3232_rtc_write8(dev, reg, 0x00); | |
109 | if (ret < 0) | |
110 | return ret; | |
111 | } | |
112 | ||
113 | return 0; | |
114 | } | |
115 | ||
116 | static int verify_osc(struct udevice *dev) | |
117 | { | |
118 | int ret, rtc_status; | |
119 | ||
120 | ret = ds3232_rtc_read8(dev, RTC_STAT_REG_ADDR); | |
121 | if (ret < 0) | |
122 | return ret; | |
123 | ||
124 | rtc_status = ret; | |
125 | ||
126 | if (rtc_status & RTC_STAT_BIT_OSF) { | |
127 | dev_warn(dev, | |
128 | "oscillator discontinuity flagged, time unreliable\n"); | |
129 | /* | |
130 | * In case OSC was off we cannot trust the SRAM data anymore. | |
131 | * Reset it to 0x00. | |
132 | */ | |
133 | ret = reset_sram(dev); | |
134 | if (ret < 0) | |
135 | return ret; | |
136 | } | |
137 | ||
138 | return 0; | |
139 | } | |
140 | ||
141 | static int ds3232_rtc_set(struct udevice *dev, const struct rtc_time *tm) | |
142 | { | |
143 | u8 buf[RTC_DATE_TIME_REG_SIZE]; | |
144 | u8 is_century; | |
145 | ||
146 | if (tm->tm_year < 1900 || tm->tm_year > 2099) | |
147 | dev_warn(dev, "WARNING: year should be between 1900 and 2099!\n"); | |
148 | ||
149 | is_century = (tm->tm_year >= 2000) ? 0x80 : 0; | |
150 | ||
151 | buf[RTC_SEC_REG_ADDR] = bin2bcd(tm->tm_sec); | |
152 | buf[RTC_MIN_REG_ADDR] = bin2bcd(tm->tm_min); | |
153 | buf[RTC_HR_REG_ADDR] = bin2bcd(tm->tm_hour); | |
154 | buf[RTC_DAY_REG_ADDR] = bin2bcd(tm->tm_wday + 1); | |
155 | buf[RTC_DATE_REG_ADDR] = bin2bcd(tm->tm_mday); | |
156 | buf[RTC_MON_REG_ADDR] = bin2bcd(tm->tm_mon) | is_century; | |
157 | buf[RTC_YR_REG_ADDR] = bin2bcd(tm->tm_year % 100); | |
158 | ||
159 | return dm_i2c_write(dev, 0, buf, sizeof(buf)); | |
160 | } | |
161 | ||
162 | static int ds3232_rtc_get(struct udevice *dev, struct rtc_time *tm) | |
163 | { | |
164 | int ret; | |
165 | u8 buf[RTC_DATE_TIME_REG_SIZE]; | |
166 | u8 is_twelve_hr; | |
167 | u8 is_pm; | |
168 | u8 is_century; | |
169 | ||
170 | ret = verify_osc(dev); | |
171 | if (ret < 0) | |
172 | return ret; | |
173 | ||
174 | ret = dm_i2c_read(dev, 0, buf, sizeof(buf)); | |
175 | if (ret < 0) | |
176 | return ret; | |
177 | ||
178 | /* Extract additional information for AM/PM and century */ | |
179 | is_twelve_hr = buf[RTC_HR_REG_ADDR] & 0x40; | |
180 | is_pm = buf[RTC_HR_REG_ADDR] & 0x20; | |
181 | is_century = buf[RTC_MON_REG_ADDR] & 0x80; | |
182 | ||
183 | tm->tm_sec = bcd2bin(buf[RTC_SEC_REG_ADDR] & 0x7F); | |
184 | tm->tm_min = bcd2bin(buf[RTC_MIN_REG_ADDR] & 0x7F); | |
185 | ||
186 | if (is_twelve_hr) | |
187 | tm->tm_hour = bcd2bin(buf[RTC_HR_REG_ADDR] & 0x1F) | |
188 | + (is_pm ? 12 : 0); | |
189 | else | |
190 | tm->tm_hour = bcd2bin(buf[RTC_HR_REG_ADDR]); | |
191 | ||
192 | tm->tm_wday = bcd2bin((buf[RTC_DAY_REG_ADDR] & 0x07) - 1); | |
193 | tm->tm_mday = bcd2bin(buf[RTC_DATE_REG_ADDR] & 0x3F); | |
194 | tm->tm_mon = bcd2bin((buf[RTC_MON_REG_ADDR] & 0x7F)); | |
195 | tm->tm_year = bcd2bin(buf[RTC_YR_REG_ADDR]) | |
196 | + (is_century ? 2000 : 1900); | |
197 | tm->tm_yday = 0; | |
198 | tm->tm_isdst = 0; | |
199 | ||
200 | return 0; | |
201 | } | |
202 | ||
203 | static int ds3232_rtc_reset(struct udevice *dev) | |
204 | { | |
205 | int ret; | |
206 | ||
207 | ret = reset_sram(dev); | |
208 | if (ret < 0) | |
209 | return ret; | |
210 | ||
211 | /* | |
212 | * From datasheet | |
213 | * (https://datasheets.maximintegrated.com/en/ds/DS3232M.pdf): | |
214 | * | |
215 | * The device reset occurs during the normal acknowledge time slot | |
216 | * following the receipt of the data byte carrying that | |
217 | * SWRST instruction a NACK occurs due to the resetting action. | |
218 | * | |
219 | * Therefore we don't verify the result of I2C write operation since it | |
220 | * will fail due the NACK. | |
221 | */ | |
222 | ds3232_rtc_write8(dev, RTC_TEST_REG_ADDR, RTC_TEST_BIT_SWRST); | |
223 | ||
224 | return 0; | |
225 | } | |
226 | ||
227 | static int ds3232_probe(struct udevice *dev) | |
228 | { | |
229 | int rtc_status; | |
230 | int ret; | |
231 | struct ds3232_priv_data *priv_data; | |
232 | ||
233 | priv_data = dev_get_priv(dev); | |
234 | if (!priv_data) | |
235 | return -EINVAL; | |
236 | ||
237 | priv_data->sram_start = RTC_SRAM_START; | |
238 | priv_data->max_register = RTC_SRAM_END; | |
239 | priv_data->sram_size = RTC_SRAM_SIZE; | |
240 | ||
241 | ret = ds3232_rtc_read8(dev, RTC_STAT_REG_ADDR); | |
242 | if (ret < 0) | |
243 | return ret; | |
244 | ||
245 | rtc_status = ret; | |
246 | ||
247 | ret = verify_osc(dev); | |
248 | if (ret < 0) | |
249 | return ret; | |
250 | ||
251 | rtc_status &= ~(RTC_STAT_BIT_OSF | RTC_STAT_BIT_A1F | RTC_STAT_BIT_A2F); | |
252 | ||
253 | return ds3232_rtc_write8(dev, RTC_STAT_REG_ADDR, rtc_status); | |
254 | } | |
255 | ||
256 | static const struct rtc_ops ds3232_rtc_ops = { | |
257 | .get = ds3232_rtc_get, | |
258 | .set = ds3232_rtc_set, | |
259 | .reset = ds3232_rtc_reset, | |
260 | .read8 = ds3232_rtc_read8, | |
261 | .write8 = ds3232_rtc_write8 | |
262 | }; | |
263 | ||
264 | static const struct udevice_id ds3232_rtc_ids[] = { | |
265 | { .compatible = "dallas,ds3232" }, | |
266 | { } | |
267 | }; | |
268 | ||
269 | U_BOOT_DRIVER(rtc_ds3232) = { | |
270 | .name = "rtc-ds3232", | |
271 | .id = UCLASS_RTC, | |
272 | .probe = ds3232_probe, | |
273 | .of_match = ds3232_rtc_ids, | |
274 | .ops = &ds3232_rtc_ops, | |
41575d8e | 275 | .priv_auto = sizeof(struct ds3232_priv_data), |
0b326fc2 | 276 | }; |