1 /*
2  * SPDX-License-Identifier: Apache-2.0
3  *
4  * Copyright (c) 2023 Arunmani Alagarsamy
5  * Author: Arunmani Alagarsamy  <arunmani27100@gmail.com>
6  *
7  * Copyright (c) 2025 Marcin Lyda <elektromarcin@gmail.com>
8  */
9 
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/drivers/rtc.h>
12 #include <zephyr/logging/log.h>
13 #include <zephyr/pm/device.h>
14 #include <zephyr/sys/util.h>
15 
16 #define DT_DRV_COMPAT maxim_ds1307
17 
18 LOG_MODULE_REGISTER(ds1307, CONFIG_RTC_LOG_LEVEL);
19 
20 /* DS1307 registers */
21 #define DS1307_REG_SECONDS 0x00
22 #define DS1307_REG_MINUTES 0x01
23 #define DS1307_REG_HOURS   0x02
24 #define DS1307_REG_DAY     0x03
25 #define DS1307_REG_DATE    0x04
26 #define DS1307_REG_MONTH   0x05
27 #define DS1307_REG_YEAR    0x06
28 #define DS1307_REG_CTRL    0x07
29 
30 /* DS1307 bitmasks */
31 #define SECONDS_BITS  GENMASK(6, 0)
32 #define MINUTES_BITS  GENMASK(7, 0)
33 #define HOURS_BITS    GENMASK(5, 0)
34 #define DATE_BITS     GENMASK(5, 0)
35 #define MONTHS_BITS   GENMASK(4, 0)
36 #define WEEKDAY_BITS  GENMASK(2, 0)
37 #define YEAR_BITS     GENMASK(7, 0)
38 #define VALIDATE_24HR BIT(6)
39 
40 #define CTRL_RS_BITS  GENMASK(1, 0)
41 #define CTRL_SQWE_BIT BIT(4)
42 
43 #define SQW_FREQ_1Hz     FIELD_PREP(CTRL_RS_BITS, 0x00)
44 #define SQW_FREQ_4096Hz  FIELD_PREP(CTRL_RS_BITS, 0x01)
45 #define SQW_FREQ_8192Hz  FIELD_PREP(CTRL_RS_BITS, 0x02)
46 #define SQW_FREQ_32768Hz FIELD_PREP(CTRL_RS_BITS, 0x03)
47 
48 /* SQW frequency property enum values */
49 #define SQW_PROP_ENUM_1HZ      0
50 #define SQW_PROP_ENUM_4096HZ   1
51 #define SQW_PROP_ENUM_8192HZ   2
52 #define SQW_PROP_ENUM_32768HZ  3
53 #define SQW_PROP_ENUM_DISABLED 4
54 
55 struct ds1307_config {
56 	struct i2c_dt_spec i2c_bus;
57 	uint8_t sqw_freq;
58 };
59 
60 struct ds1307_data {
61 	struct k_spinlock lock;
62 };
63 
ds1307_set_time(const struct device * dev,const struct rtc_time * tm)64 static int ds1307_set_time(const struct device *dev, const struct rtc_time *tm)
65 {
66 	int err;
67 	uint8_t regs[7];
68 
69 	struct ds1307_data *data = dev->data;
70 	const struct ds1307_config *config = dev->config;
71 
72 	k_spinlock_key_t key = k_spin_lock(&data->lock);
73 
74 	LOG_DBG("set time: year = %d, mon = %d, mday = %d, wday = %d, hour = %d, "
75 		"min = %d, sec = %d",
76 		tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_wday, tm->tm_hour, tm->tm_min,
77 		tm->tm_sec);
78 
79 	regs[0] = bin2bcd(tm->tm_sec) & SECONDS_BITS;
80 	regs[1] = bin2bcd(tm->tm_min);
81 	regs[2] = bin2bcd(tm->tm_hour);
82 	regs[3] = bin2bcd(tm->tm_wday);
83 	regs[4] = bin2bcd(tm->tm_mday);
84 	regs[5] = bin2bcd(tm->tm_mon);
85 	regs[6] = bin2bcd((tm->tm_year % 100));
86 
87 	err = i2c_burst_write_dt(&config->i2c_bus, DS1307_REG_SECONDS, regs, sizeof(regs));
88 
89 	k_spin_unlock(&data->lock, key);
90 
91 	return err;
92 }
93 
ds1307_get_time(const struct device * dev,struct rtc_time * timeptr)94 static int ds1307_get_time(const struct device *dev, struct rtc_time *timeptr)
95 {
96 	int err;
97 	uint8_t regs[7];
98 
99 	struct ds1307_data *data = dev->data;
100 	const struct ds1307_config *config = dev->config;
101 
102 	k_spinlock_key_t key = k_spin_lock(&data->lock);
103 
104 	err = i2c_burst_read_dt(&config->i2c_bus, DS1307_REG_SECONDS, regs, sizeof(regs));
105 	if (err != 0) {
106 		goto unlock;
107 	}
108 
109 	timeptr->tm_sec = bcd2bin(regs[0] & SECONDS_BITS);
110 	timeptr->tm_min = bcd2bin(regs[1] & MINUTES_BITS);
111 	timeptr->tm_hour = bcd2bin(regs[2] & HOURS_BITS); /* 24hr mode */
112 	timeptr->tm_wday = bcd2bin(regs[3] & WEEKDAY_BITS);
113 	timeptr->tm_mday = bcd2bin(regs[4] & DATE_BITS);
114 	timeptr->tm_mon = bcd2bin(regs[5] & MONTHS_BITS);
115 	timeptr->tm_year = bcd2bin(regs[6] & YEAR_BITS);
116 	timeptr->tm_year = timeptr->tm_year + 100;
117 
118 	/* Not used */
119 	timeptr->tm_nsec = 0;
120 	timeptr->tm_isdst = -1;
121 	timeptr->tm_yday = -1;
122 
123 	/* Validate the chip in 24hr mode */
124 	if (regs[2] & VALIDATE_24HR) {
125 		err = -ENODATA;
126 		goto unlock;
127 	}
128 
129 	LOG_DBG("get time: year = %d, mon = %d, mday = %d, wday = %d, hour = %d, "
130 		"min = %d, sec = %d",
131 		timeptr->tm_year, timeptr->tm_mon, timeptr->tm_mday, timeptr->tm_wday,
132 		timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec);
133 
134 unlock:
135 	k_spin_unlock(&data->lock, key);
136 
137 	return err;
138 }
139 
140 static DEVICE_API(rtc, ds1307_driver_api) = {
141 	.set_time = ds1307_set_time,
142 	.get_time = ds1307_get_time,
143 };
144 
ds1307_init(const struct device * dev)145 static int ds1307_init(const struct device *dev)
146 {
147 	int err;
148 	uint8_t reg_val;
149 	const struct ds1307_config *config = dev->config;
150 
151 	if (!i2c_is_ready_dt(&config->i2c_bus)) {
152 		LOG_ERR("I2C bus not ready");
153 		return -ENODEV;
154 	}
155 
156 	/* Configure SQW output frequency */
157 	reg_val = CTRL_SQWE_BIT;
158 	switch (config->sqw_freq) {
159 	case SQW_PROP_ENUM_1HZ:
160 		reg_val |= SQW_FREQ_1Hz;
161 		break;
162 	case SQW_PROP_ENUM_4096HZ:
163 		reg_val |= SQW_FREQ_4096Hz;
164 		break;
165 	case SQW_PROP_ENUM_8192HZ:
166 		reg_val |= SQW_FREQ_8192Hz;
167 		break;
168 	case SQW_PROP_ENUM_32768HZ:
169 		reg_val |= SQW_FREQ_32768Hz;
170 		break;
171 	case SQW_PROP_ENUM_DISABLED:
172 	default:
173 		reg_val &= ~CTRL_SQWE_BIT;
174 		break;
175 	}
176 	err = i2c_reg_write_byte_dt(&config->i2c_bus, DS1307_REG_CTRL, reg_val);
177 	if (err < 0) {
178 		LOG_ERR("Error: Configure SQW: %d", err);
179 	}
180 
181 	/* Ensure Clock Halt = 0 */
182 	err = i2c_reg_read_byte_dt(&config->i2c_bus, DS1307_REG_SECONDS, &reg_val);
183 	if (err < 0) {
184 		LOG_ERR("Error: Read SECONDS/Clock Halt register: %d", err);
185 	}
186 	if (reg_val & ~SECONDS_BITS) {
187 		/* Clock Halt bit is set */
188 		err = i2c_reg_write_byte_dt(&config->i2c_bus, DS1307_REG_SECONDS,
189 					    reg_val & SECONDS_BITS);
190 		if (err < 0) {
191 			LOG_ERR("Error: Clear Clock Halt bit: %d", err);
192 		}
193 	}
194 
195 	return 0;
196 }
197 
198 #define DS1307_DEFINE(inst)                                                                        \
199 	static struct ds1307_data ds1307_data_##inst;                                              \
200 	static const struct ds1307_config ds1307_config_##inst = {                                 \
201 		.i2c_bus = I2C_DT_SPEC_INST_GET(inst),                                             \
202 		.sqw_freq = DT_INST_ENUM_IDX_OR(inst, sqw_frequency, SQW_PROP_ENUM_DISABLED)	   \
203 	};											   \
204 	DEVICE_DT_INST_DEFINE(inst, &ds1307_init, NULL, &ds1307_data_##inst,                       \
205 			      &ds1307_config_##inst, POST_KERNEL, CONFIG_RTC_INIT_PRIORITY,        \
206 			      &ds1307_driver_api);
207 
208 DT_INST_FOREACH_STATUS_OKAY(DS1307_DEFINE)
209