1 /*
2  * SPDX-License-Identifier: Apache-2.0
3  *
4  * Copyright (c) 2023 Arunmani Alagarsamy
5  * Author: Arunmani Alagarsamy  <arunmani27100@gmail.com>
6  */
7 
8 #include <zephyr/drivers/i2c.h>
9 #include <zephyr/drivers/rtc.h>
10 #include <zephyr/logging/log.h>
11 #include <zephyr/pm/device.h>
12 #include <zephyr/sys/util.h>
13 
14 #define DT_DRV_COMPAT maxim_ds1307
15 
16 LOG_MODULE_REGISTER(ds1307, CONFIG_RTC_LOG_LEVEL);
17 
18 /* DS1307 registers */
19 #define DS1307_REG_SECONDS 0x00
20 #define DS1307_REG_MINUTES 0x01
21 #define DS1307_REG_HOURS   0x02
22 #define DS1307_REG_DAY     0x03
23 #define DS1307_REG_DATE    0x04
24 #define DS1307_REG_MONTH   0x05
25 #define DS1307_REG_YEAR    0x06
26 #define DS1307_REG_CTRL    0x07
27 
28 #define SECONDS_BITS  GENMASK(6, 0)
29 #define MINUTES_BITS  GENMASK(7, 0)
30 #define HOURS_BITS    GENMASK(5, 0)
31 #define DATE_BITS     GENMASK(5, 0)
32 #define MONTHS_BITS   GENMASK(4, 0)
33 #define WEEKDAY_BITS  GENMASK(2, 0)
34 #define YEAR_BITS     GENMASK(7, 0)
35 #define VALIDATE_24HR BIT(6)
36 
37 struct ds1307_config {
38 	struct i2c_dt_spec i2c_bus;
39 };
40 
41 struct ds1307_data {
42 	struct k_spinlock lock;
43 };
44 
ds1307_set_time(const struct device * dev,const struct rtc_time * tm)45 static int ds1307_set_time(const struct device *dev, const struct rtc_time *tm)
46 {
47 	int err;
48 	uint8_t regs[7];
49 
50 	struct ds1307_data *data = dev->data;
51 	const struct ds1307_config *config = dev->config;
52 
53 	k_spinlock_key_t key = k_spin_lock(&data->lock);
54 
55 	LOG_DBG("set time: year = %d, mon = %d, mday = %d, wday = %d, hour = %d, "
56 		"min = %d, sec = %d",
57 		tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_wday, tm->tm_hour, tm->tm_min,
58 		tm->tm_sec);
59 
60 	regs[0] = bin2bcd(tm->tm_sec) & SECONDS_BITS;
61 	regs[1] = bin2bcd(tm->tm_min);
62 	regs[2] = bin2bcd(tm->tm_hour);
63 	regs[3] = bin2bcd(tm->tm_wday);
64 	regs[4] = bin2bcd(tm->tm_mday);
65 	regs[5] = bin2bcd(tm->tm_mon);
66 	regs[6] = bin2bcd((tm->tm_year % 100));
67 
68 	err = i2c_burst_write_dt(&config->i2c_bus, DS1307_REG_SECONDS, regs, sizeof(regs));
69 
70 	k_spin_unlock(&data->lock, key);
71 
72 	return err;
73 }
74 
ds1307_get_time(const struct device * dev,struct rtc_time * timeptr)75 static int ds1307_get_time(const struct device *dev, struct rtc_time *timeptr)
76 {
77 	int err;
78 	uint8_t regs[7];
79 
80 	struct ds1307_data *data = dev->data;
81 	const struct ds1307_config *config = dev->config;
82 
83 	k_spinlock_key_t key = k_spin_lock(&data->lock);
84 
85 	err = i2c_burst_read_dt(&config->i2c_bus, DS1307_REG_SECONDS, regs, sizeof(regs));
86 	if (err != 0) {
87 		goto unlock;
88 	}
89 
90 	timeptr->tm_sec = bcd2bin(regs[0] & SECONDS_BITS);
91 	timeptr->tm_min = bcd2bin(regs[1] & MINUTES_BITS);
92 	timeptr->tm_hour = bcd2bin(regs[2] & HOURS_BITS); /* 24hr mode */
93 	timeptr->tm_wday = bcd2bin(regs[3] & WEEKDAY_BITS);
94 	timeptr->tm_mday = bcd2bin(regs[4] & DATE_BITS);
95 	timeptr->tm_mon = bcd2bin(regs[5] & MONTHS_BITS);
96 	timeptr->tm_year = bcd2bin(regs[6] & YEAR_BITS);
97 	timeptr->tm_year = timeptr->tm_year + 100;
98 
99 	/* Not used */
100 	timeptr->tm_nsec = 0;
101 	timeptr->tm_isdst = -1;
102 	timeptr->tm_yday = -1;
103 
104 	/* Validate the chip in 24hr mode */
105 	if (regs[2] & VALIDATE_24HR) {
106 		err = -ENODATA;
107 		goto unlock;
108 	}
109 
110 	LOG_DBG("get time: year = %d, mon = %d, mday = %d, wday = %d, hour = %d, "
111 		"min = %d, sec = %d",
112 		timeptr->tm_year, timeptr->tm_mon, timeptr->tm_mday, timeptr->tm_wday,
113 		timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec);
114 
115 unlock:
116 	k_spin_unlock(&data->lock, key);
117 
118 	return err;
119 }
120 
121 static const struct rtc_driver_api ds1307_driver_api = {
122 	.set_time = ds1307_set_time,
123 	.get_time = ds1307_get_time,
124 };
125 
ds1307_init(const struct device * dev)126 static int ds1307_init(const struct device *dev)
127 {
128 	int err;
129 	const struct ds1307_config *config = dev->config;
130 
131 	if (!i2c_is_ready_dt(&config->i2c_bus)) {
132 		LOG_ERR("I2C bus not ready");
133 		return -ENODEV;
134 	}
135 
136 	/* Disable squarewave output */
137 	err = i2c_reg_write_byte_dt(&config->i2c_bus, DS1307_REG_CTRL, 0x00);
138 	if (err < 0) {
139 		LOG_ERR("Error: SQW:%d\n", err);
140 	}
141 
142 	/* Make clock halt = 0 */
143 	err = i2c_reg_write_byte_dt(&config->i2c_bus, DS1307_REG_SECONDS, 0x00);
144 	if (err < 0) {
145 		LOG_ERR("Error: Set clock halt bit:%d\n", err);
146 	}
147 
148 	return 0;
149 }
150 
151 #define DS1307_DEFINE(inst)                                                                        \
152 	static struct ds1307_data ds1307_data_##inst;                                              \
153 	static const struct ds1307_config ds1307_config_##inst = {                                 \
154 		.i2c_bus = I2C_DT_SPEC_INST_GET(inst),                                             \
155 	};                                                                                         \
156 	DEVICE_DT_INST_DEFINE(inst, &ds1307_init, NULL, &ds1307_data_##inst,                       \
157 			      &ds1307_config_##inst, POST_KERNEL, CONFIG_RTC_INIT_PRIORITY,        \
158 			      &ds1307_driver_api);
159 
160 DT_INST_FOREACH_STATUS_OKAY(DS1307_DEFINE)
161