1 /*
2  * Copyright (c) 2025 Marcin Lyda <elektromarcin@gmail.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/sys/util.h>
9 #include <zephyr/logging/log.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/drivers/rtc.h>
12 #include "rtc_utils.h"
13 
14 LOG_MODULE_REGISTER(bq32002, CONFIG_RTC_LOG_LEVEL);
15 
16 #define DT_DRV_COMPAT ti_bq32002
17 
18 #if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 0
19 #warning "Texas Instruments BQ32002 RTC driver enabled without any devices"
20 #endif
21 
22 /* Registers */
23 #define BQ32002_SECONDS_REG    0x00
24 #define BQ32002_MINUTES_REG    0x01
25 #define BQ32002_CENT_HOURS_REG 0x02
26 #define BQ32002_DAY_REG        0x03
27 #define BQ32002_DATE_REG       0x04
28 #define BQ32002_MONTH_REG      0x05
29 #define BQ32002_YEARS_REG      0x06
30 #define BQ32002_CAL_CFG1_REG   0x07
31 #define BQ32002_CFG2_REG       0x09
32 #define BQ32002_SF_KEY_1_REG   0x20
33 #define BQ32002_SF_KEY_2_REG   0x21
34 #define BQ32002_SFR_REG        0x22
35 
36 /* Bitmasks */
37 #define BQ32002_SECONDS_MASK GENMASK(6, 0)
38 #define BQ32002_MINUTES_MASK GENMASK(6, 0)
39 #define BQ32002_HOURS_MASK   GENMASK(5, 0)
40 #define BQ32002_DAY_MASK     GENMASK(2, 0)
41 #define BQ32002_DATE_MASK    GENMASK(5, 0)
42 #define BQ32002_MONTH_MASK   GENMASK(4, 0)
43 #define BQ32002_YEAR_MASK    GENMASK(7, 0)
44 #define BQ32002_CAL_MASK     GENMASK(4, 0)
45 
46 #define BQ32002_OSC_STOP_MASK  BIT(7)
47 #define BQ32002_OSC_FAIL_MASK  BIT(7)
48 #define BQ32002_CENT_EN_MASK   BIT(7)
49 #define BQ32002_CENT_MASK      BIT(6)
50 #define BQ32002_OUT_MASK       BIT(7)
51 #define BQ32002_FREQ_TEST_MASK BIT(6)
52 #define BQ32002_CAL_SIGN_MASK  BIT(5)
53 #define BQ32002_FTF_MASK       BIT(0)
54 
55 /* Keys to unlock special function register */
56 #define BQ32002_SF_KEY_1 0x5E
57 #define BQ32002_SF_KEY_2 0xC7
58 
59 /* BQ32002 counts weekdays from 1 to 7 */
60 #define BQ32002_DAY_OFFSET -1
61 
62 /* BQ32002 counts months from 1 to 12 */
63 #define BQ32002_MONTH_OFFSET -1
64 
65 /* Year 2000 value represented as tm_year value */
66 #define BQ32002_TM_YEAR_2000 (2000 - 1900)
67 
68 /* Calibration constants, see BQ32002 datasheet, Table 12, p.16 */
69 #define BQ32002_CAL_PPB_PER_LSB_POS 2034 /* 1e9 / 491520 */
70 #define BQ32002_CAL_PPB_PER_LSB_NEG 4069 /* 1e9 / 245760 */
71 #define BQ32002_CAL_PPB_MIN         (-31 * BQ32002_CAL_PPB_PER_LSB_POS)
72 #define BQ32002_CAL_PPB_MAX         (31 * BQ32002_CAL_PPB_PER_LSB_NEG)
73 
74 /* IRQ frequency property enum values */
75 #define BQ32002_IRQ_FREQ_ENUM_1HZ      0
76 #define BQ32002_IRQ_FREQ_ENUM_512HZ    1
77 #define BQ32002_IRQ_FREQ_ENUM_DISABLED 2
78 
79 /* RTC time fields supported by BQ32002 */
80 #define BQ32002_RTC_TIME_MASK                                                                      \
81 	(RTC_ALARM_TIME_MASK_SECOND | RTC_ALARM_TIME_MASK_MINUTE | RTC_ALARM_TIME_MASK_HOUR |      \
82 	 RTC_ALARM_TIME_MASK_MONTH | RTC_ALARM_TIME_MASK_MONTHDAY | RTC_ALARM_TIME_MASK_YEAR |     \
83 	 RTC_ALARM_TIME_MASK_WEEKDAY)
84 
85 struct bq32002_config {
86 	struct i2c_dt_spec i2c;
87 	uint8_t irq_freq;
88 };
89 
90 struct bq32002_data {
91 	struct k_sem lock;
92 };
93 
bq32002_lock_sem(const struct device * dev)94 static void bq32002_lock_sem(const struct device *dev)
95 {
96 	struct bq32002_data *data = dev->data;
97 
98 	(void)k_sem_take(&data->lock, K_FOREVER);
99 }
100 
bq32002_unlock_sem(const struct device * dev)101 static void bq32002_unlock_sem(const struct device *dev)
102 {
103 	struct bq32002_data *data = dev->data;
104 
105 	k_sem_give(&data->lock);
106 }
107 
bq32002_set_irq_frequency(const struct device * dev)108 static int bq32002_set_irq_frequency(const struct device *dev)
109 {
110 	const struct bq32002_config *config = dev->config;
111 	uint8_t sf_regs[3];
112 	uint8_t cfg1_val;
113 	uint8_t cfg2_val;
114 	int err;
115 
116 	switch (config->irq_freq) {
117 	case BQ32002_IRQ_FREQ_ENUM_1HZ:
118 		cfg1_val = BQ32002_FREQ_TEST_MASK;
119 		cfg2_val = BQ32002_FTF_MASK;
120 		break;
121 	case BQ32002_IRQ_FREQ_ENUM_512HZ:
122 		cfg1_val = BQ32002_FREQ_TEST_MASK;
123 		cfg2_val = 0;
124 		break;
125 	default:
126 		cfg1_val = BQ32002_OUT_MASK;
127 		cfg2_val = 0;
128 		break;
129 	}
130 
131 	err = i2c_reg_update_byte_dt(&config->i2c, BQ32002_CAL_CFG1_REG, BQ32002_FREQ_TEST_MASK,
132 				     cfg1_val);
133 	if (err) {
134 		return err;
135 	}
136 
137 	/* Update FTF value if frequency output enabled */
138 	if (cfg1_val & BQ32002_FREQ_TEST_MASK) {
139 		sf_regs[0] = BQ32002_SF_KEY_1;
140 		sf_regs[1] = BQ32002_SF_KEY_2;
141 		sf_regs[2] = cfg2_val;
142 		err = i2c_burst_write_dt(&config->i2c, BQ32002_SF_KEY_1_REG, sf_regs,
143 					 sizeof(sf_regs));
144 		if (err) {
145 			return err;
146 		}
147 	}
148 
149 	return 0;
150 }
151 
bq32002_set_time(const struct device * dev,const struct rtc_time * timeptr)152 static int bq32002_set_time(const struct device *dev, const struct rtc_time *timeptr)
153 {
154 	const struct bq32002_config *config = dev->config;
155 	int err;
156 	uint8_t regs[7];
157 
158 	if ((timeptr == NULL) || !rtc_utils_validate_rtc_time(timeptr, BQ32002_RTC_TIME_MASK)) {
159 		return -EINVAL;
160 	}
161 
162 	bq32002_lock_sem(dev);
163 
164 	/* Update the registers */
165 	regs[0] = bin2bcd(timeptr->tm_sec) & BQ32002_SECONDS_MASK;
166 	regs[1] = bin2bcd(timeptr->tm_min) & BQ32002_MINUTES_MASK; /* Clear oscillator fail flag */
167 	regs[2] = (bin2bcd(timeptr->tm_hour) & BQ32002_HOURS_MASK) | BQ32002_CENT_EN_MASK;
168 	regs[3] = bin2bcd(timeptr->tm_wday - BQ32002_DAY_OFFSET) & BQ32002_DAY_MASK;
169 	regs[4] = bin2bcd(timeptr->tm_mday) & BQ32002_DATE_MASK;
170 	regs[5] = bin2bcd(timeptr->tm_mon - BQ32002_MONTH_OFFSET) & BQ32002_MONTH_MASK;
171 
172 	/* Determine which century we're in */
173 	if (timeptr->tm_year >= BQ32002_TM_YEAR_2000) {
174 		regs[2] |= BQ32002_CENT_MASK;
175 		regs[6] = bin2bcd(timeptr->tm_year - BQ32002_TM_YEAR_2000) & BQ32002_YEAR_MASK;
176 	} else {
177 		regs[6] = bin2bcd(timeptr->tm_year) & BQ32002_YEAR_MASK;
178 	}
179 
180 	/* Write new time to the chip */
181 	err = i2c_burst_write_dt(&config->i2c, BQ32002_SECONDS_REG, regs, sizeof(regs));
182 
183 	bq32002_unlock_sem(dev);
184 
185 	if (!err) {
186 		LOG_DBG("Set time: year: %d, month: %d, month day: %d, week day: %d, hour: %d, "
187 			"minute: %d, second: %d",
188 			timeptr->tm_year, timeptr->tm_mon, timeptr->tm_mday, timeptr->tm_wday,
189 			timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec);
190 	}
191 
192 	return err;
193 }
194 
bq32002_get_time(const struct device * dev,struct rtc_time * timeptr)195 static int bq32002_get_time(const struct device *dev, struct rtc_time *timeptr)
196 {
197 	const struct bq32002_config *config = dev->config;
198 	int err;
199 	uint8_t reg_val;
200 	uint8_t regs[7];
201 
202 	if (timeptr == NULL) {
203 		return -EINVAL;
204 	}
205 
206 	bq32002_lock_sem(dev);
207 
208 	err = i2c_reg_read_byte_dt(&config->i2c, BQ32002_MINUTES_REG, &reg_val);
209 	if (err) {
210 		goto out_unlock;
211 	}
212 
213 	/* Oscillator failure detected, data might be invalid */
214 	if (reg_val & BQ32002_OSC_FAIL_MASK) {
215 		err = -ENODATA;
216 		goto out_unlock;
217 	}
218 
219 	err = i2c_burst_read_dt(&config->i2c, BQ32002_SECONDS_REG, regs, sizeof(regs));
220 	if (err) {
221 		goto out_unlock;
222 	}
223 
224 	timeptr->tm_sec = bcd2bin(regs[0] & BQ32002_SECONDS_MASK);
225 	timeptr->tm_min = bcd2bin(regs[1] & BQ32002_MINUTES_MASK);
226 	timeptr->tm_hour = bcd2bin(regs[2] & BQ32002_HOURS_MASK);
227 	timeptr->tm_wday = bcd2bin(regs[3] & BQ32002_DAY_MASK) + BQ32002_DAY_OFFSET;
228 	timeptr->tm_mday = bcd2bin(regs[4] & BQ32002_DATE_MASK);
229 	timeptr->tm_mon = bcd2bin(regs[5] & BQ32002_MONTH_MASK) + BQ32002_MONTH_OFFSET;
230 	timeptr->tm_year = bcd2bin(regs[6] & BQ32002_YEAR_MASK);
231 	timeptr->tm_yday = -1;  /* Unsupported */
232 	timeptr->tm_isdst = -1; /* Unsupported */
233 	timeptr->tm_nsec = 0;   /* Unsupported */
234 
235 	/* Apply century offset */
236 	if (regs[2] & BQ32002_CENT_MASK) {
237 		timeptr->tm_year += BQ32002_TM_YEAR_2000;
238 	}
239 
240 out_unlock:
241 	bq32002_unlock_sem(dev);
242 
243 	if (!err) {
244 		LOG_DBG("Read time: year: %d, month: %d, month day: %d, week day: %d, hour: %d, "
245 			"minute: "
246 			"%d, second: %d",
247 			timeptr->tm_year, timeptr->tm_mon, timeptr->tm_mday, timeptr->tm_wday,
248 			timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec);
249 	}
250 
251 	return err;
252 }
253 
254 #ifdef CONFIG_RTC_CALIBRATION
255 
bq32002_set_calibration(const struct device * dev,int32_t freq_ppb)256 static int bq32002_set_calibration(const struct device *dev, int32_t freq_ppb)
257 {
258 	const struct bq32002_config *config = dev->config;
259 	int err;
260 	uint8_t offset;
261 	uint8_t reg_val;
262 
263 	if ((freq_ppb < BQ32002_CAL_PPB_MIN) || (freq_ppb > BQ32002_CAL_PPB_MAX)) {
264 		LOG_ERR("Calibration value %d ppb out of range", freq_ppb);
265 		return -EINVAL;
266 	}
267 
268 	err = i2c_reg_read_byte_dt(&config->i2c, BQ32002_CAL_CFG1_REG, &reg_val);
269 	if (err) {
270 		return err;
271 	}
272 
273 	reg_val &= ~(BQ32002_CAL_SIGN_MASK | BQ32002_CAL_MASK);
274 
275 	if (freq_ppb > 0) {
276 		reg_val |= BQ32002_CAL_SIGN_MASK; /* Negative sign speeds the oscillator up */
277 		offset =
278 			DIV_ROUND_CLOSEST(freq_ppb, BQ32002_CAL_PPB_PER_LSB_NEG) & BQ32002_CAL_MASK;
279 	} else {
280 		offset = DIV_ROUND_CLOSEST(-freq_ppb, BQ32002_CAL_PPB_PER_LSB_POS) &
281 			 BQ32002_CAL_MASK;
282 	}
283 	reg_val |= offset;
284 
285 	err = i2c_reg_write_byte_dt(&config->i2c, BQ32002_CAL_CFG1_REG, reg_val);
286 	if (err) {
287 		return err;
288 	}
289 
290 	LOG_DBG("Set calibration: frequency ppb: %d, offset value: %d, sign: %d", freq_ppb, offset,
291 		freq_ppb > 0);
292 
293 	return 0;
294 }
295 
bq32002_get_calibration(const struct device * dev,int32_t * freq_ppb)296 static int bq32002_get_calibration(const struct device *dev, int32_t *freq_ppb)
297 {
298 	const struct bq32002_config *config = dev->config;
299 	uint8_t reg_val;
300 	uint8_t offset;
301 	int err;
302 
303 	err = i2c_reg_read_byte_dt(&config->i2c, BQ32002_CAL_CFG1_REG, &reg_val);
304 	if (err) {
305 		return err;
306 	}
307 
308 	offset = reg_val & BQ32002_CAL_MASK;
309 
310 	if (reg_val & BQ32002_CAL_SIGN_MASK) {
311 		*freq_ppb = offset * BQ32002_CAL_PPB_PER_LSB_NEG;
312 	} else {
313 		*freq_ppb = -offset * BQ32002_CAL_PPB_PER_LSB_POS;
314 	}
315 
316 	LOG_DBG("Get calibration: frequency ppb: %d, offset value: %d, sign: %d", *freq_ppb, offset,
317 		*freq_ppb > 0);
318 
319 	return 0;
320 }
321 
322 #endif
323 
324 static DEVICE_API(rtc, bq32002_driver_api) = {
325 	.set_time = bq32002_set_time,
326 	.get_time = bq32002_get_time,
327 #ifdef CONFIG_RTC_CALIBRATION
328 	.set_calibration = bq32002_set_calibration,
329 	.get_calibration = bq32002_get_calibration
330 #endif
331 };
332 
bq32002_init(const struct device * dev)333 static int bq32002_init(const struct device *dev)
334 {
335 	const struct bq32002_config *config = dev->config;
336 	struct bq32002_data *data = dev->data;
337 	int err;
338 
339 	(void)k_sem_init(&data->lock, 1, 1);
340 
341 	if (!i2c_is_ready_dt(&config->i2c)) {
342 		LOG_ERR("I2C bus not ready");
343 		return -ENODEV;
344 	}
345 
346 	/* Start the oscillator */
347 	err = i2c_reg_update_byte_dt(&config->i2c, BQ32002_SECONDS_REG, BQ32002_OSC_STOP_MASK, 0);
348 	if (err) {
349 		return err;
350 	}
351 
352 	/* Configure IRQ output frequency */
353 	err = bq32002_set_irq_frequency(dev);
354 	if (err) {
355 		return err;
356 	}
357 
358 	return 0;
359 }
360 
361 #define BQ32002_INIT(inst)                                                                         \
362 	static struct bq32002_data bq32002_data_##inst;                                            \
363 	static const struct bq32002_config bq32002_config_##inst = {                               \
364 		.i2c = I2C_DT_SPEC_INST_GET(inst),                                                 \
365 		.irq_freq =                                                                        \
366 			DT_INST_ENUM_IDX_OR(inst, irq_frequency, BQ32002_IRQ_FREQ_ENUM_DISABLED)   \
367 		};										   \
368 	DEVICE_DT_INST_DEFINE(inst, &bq32002_init, NULL, &bq32002_data_##inst,                     \
369 			      &bq32002_config_##inst, POST_KERNEL, CONFIG_RTC_INIT_PRIORITY,       \
370 			      &bq32002_driver_api);
371 
372 DT_INST_FOREACH_STATUS_OKAY(BQ32002_INIT)
373