1 /*
2  * Copyright (c) 2023, Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT microchip_mcp7940n
8 
9 #include <string.h>
10 #include <zephyr/device.h>
11 #include <zephyr/devicetree.h>
12 #include <zephyr/drivers/i2c.h>
13 #include <zephyr/drivers/bbram.h>
14 #include <zephyr/kernel.h>
15 #include <zephyr/sys/util.h>
16 #include <zephyr/logging/log.h>
17 
18 LOG_MODULE_REGISTER(bbram_microchip_mcp7940n, CONFIG_BBRAM_LOG_LEVEL);
19 
20 #define MICROCHIP_MCP7940N_SRAM_OFFSET 0x20
21 #define MICROCHIP_MCP7940N_SRAM_SIZE 64
22 #define MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS 0x03
23 #define MICROCHIP_MCP7940N_RTCWKDAY_VBATEN_BIT BIT(3)
24 #define MICROCHIP_MCP7940N_RTCWKDAY_PWRFAIL_BIT BIT(4)
25 
26 struct microchip_mcp7940n_bbram_data {
27 	struct k_mutex lock;
28 };
29 
30 struct microchip_mcp7940n_bbram_config {
31 	struct i2c_dt_spec i2c;
32 };
33 
microchip_mcp7940n_bbram_init(const struct device * dev)34 static int microchip_mcp7940n_bbram_init(const struct device *dev)
35 {
36 	const struct microchip_mcp7940n_bbram_config *config = dev->config;
37 	struct microchip_mcp7940n_bbram_data *data = dev->data;
38 	int32_t rc = 0;
39 	uint8_t buffer;
40 
41 	if (!device_is_ready(config->i2c.bus)) {
42 		LOG_ERR("I2C device %s is not ready", config->i2c.bus->name);
43 		return -ENODEV;
44 	}
45 
46 	k_mutex_init(&data->lock);
47 
48 	rc = i2c_reg_read_byte_dt(&config->i2c,
49 				  MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS,
50 				  &buffer);
51 
52 	if (rc != 0) {
53 		LOG_ERR("Failed to read RTCWKDAY register: %d", rc);
54 	}
55 
56 	return rc;
57 }
58 
microchip_mcp7940n_bbram_size(const struct device * dev,size_t * size)59 static int microchip_mcp7940n_bbram_size(const struct device *dev, size_t *size)
60 {
61 	*size = MICROCHIP_MCP7940N_SRAM_SIZE;
62 
63 	return 0;
64 }
65 
microchip_mcp7940n_bbram_is_invalid(const struct device * dev)66 static int microchip_mcp7940n_bbram_is_invalid(const struct device *dev)
67 {
68 	const struct microchip_mcp7940n_bbram_config *config = dev->config;
69 	struct microchip_mcp7940n_bbram_data *data = dev->data;
70 	int32_t rc = 0;
71 	uint8_t buffer;
72 	bool data_valid = true;
73 
74 	k_mutex_lock(&data->lock, K_FOREVER);
75 
76 	rc = i2c_reg_read_byte_dt(&config->i2c,
77 				  MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS,
78 				  &buffer);
79 
80 
81 	if ((buffer & MICROCHIP_MCP7940N_RTCWKDAY_PWRFAIL_BIT)) {
82 		data_valid = false;
83 
84 		buffer &= (buffer ^ MICROCHIP_MCP7940N_RTCWKDAY_PWRFAIL_BIT);
85 
86 		rc = i2c_reg_write_byte_dt(&config->i2c,
87 					   MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS,
88 					   buffer);
89 
90 		if (rc != 0) {
91 			LOG_ERR("Failed to write RTCWKDAY register: %d", rc);
92 			goto finish;
93 		}
94 
95 	}
96 
97 finish:
98 	k_mutex_unlock(&data->lock);
99 
100 	if (rc == 0 && data_valid == true) {
101 		rc = 1;
102 	}
103 
104 	return rc;
105 }
106 
microchip_mcp7940n_bbram_check_standby_power(const struct device * dev)107 static int microchip_mcp7940n_bbram_check_standby_power(const struct device *dev)
108 {
109 	const struct microchip_mcp7940n_bbram_config *config = dev->config;
110 	struct microchip_mcp7940n_bbram_data *data = dev->data;
111 	int32_t rc = 0;
112 	uint8_t buffer;
113 	bool power_enabled = true;
114 
115 	k_mutex_lock(&data->lock, K_FOREVER);
116 
117 	rc = i2c_reg_read_byte_dt(&config->i2c,
118 				  MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS,
119 				  &buffer);
120 
121 
122 	if (!(buffer & MICROCHIP_MCP7940N_RTCWKDAY_VBATEN_BIT)) {
123 		power_enabled = false;
124 
125 		buffer |= MICROCHIP_MCP7940N_RTCWKDAY_VBATEN_BIT;
126 
127 		rc = i2c_reg_write_byte_dt(&config->i2c,
128 					   MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS,
129 					   buffer);
130 
131 		if (rc != 0) {
132 			LOG_ERR("Failed to write RTCWKDAY register: %d", rc);
133 			goto finish;
134 		}
135 
136 	}
137 
138 finish:
139 	k_mutex_unlock(&data->lock);
140 
141 	if (rc == 0 && power_enabled == true) {
142 		rc = 1;
143 	}
144 
145 	return rc;
146 }
147 
microchip_mcp7940n_bbram_read(const struct device * dev,size_t offset,size_t size,uint8_t * buffer)148 static int microchip_mcp7940n_bbram_read(const struct device *dev, size_t offset, size_t size,
149 					 uint8_t *buffer)
150 {
151 	const struct microchip_mcp7940n_bbram_config *config = dev->config;
152 	struct microchip_mcp7940n_bbram_data *data = dev->data;
153 	size_t i = 0;
154 	int32_t rc = 0;
155 
156 	if (size == 0 || (offset + size) > MICROCHIP_MCP7940N_SRAM_SIZE) {
157 		return -EINVAL;
158 	}
159 
160 	k_mutex_lock(&data->lock, K_FOREVER);
161 
162 	while (i < size) {
163 		LOG_DBG("Read from 0x%x", (MICROCHIP_MCP7940N_SRAM_OFFSET + offset + i));
164 		rc = i2c_reg_read_byte_dt(&config->i2c,
165 					  (MICROCHIP_MCP7940N_SRAM_OFFSET + offset + i),
166 					  &buffer[i]);
167 
168 		if (rc != 0) {
169 			goto finish;
170 		}
171 
172 		++i;
173 	}
174 
175 finish:
176 	k_mutex_unlock(&data->lock);
177 
178 	return rc;
179 }
180 
microchip_mcp7940n_bbram_write(const struct device * dev,size_t offset,size_t size,const uint8_t * buffer)181 static int microchip_mcp7940n_bbram_write(const struct device *dev, size_t offset, size_t size,
182 					  const uint8_t *buffer)
183 {
184 	const struct microchip_mcp7940n_bbram_config *config = dev->config;
185 	struct microchip_mcp7940n_bbram_data *data = dev->data;
186 	size_t i = 0;
187 	int32_t rc = 0;
188 
189 	if (size == 0 || (offset + size) > MICROCHIP_MCP7940N_SRAM_SIZE) {
190 		return -EINVAL;
191 	}
192 
193 	k_mutex_lock(&data->lock, K_FOREVER);
194 
195 	while (i < size) {
196 		LOG_DBG("Write 0x%x to 0x%x", buffer[i],
197 			(MICROCHIP_MCP7940N_SRAM_OFFSET + offset + i));
198 		rc = i2c_reg_write_byte_dt(&config->i2c,
199 					   (MICROCHIP_MCP7940N_SRAM_OFFSET + offset + i),
200 					   buffer[i]);
201 
202 		if (rc != 0) {
203 			goto finish;
204 		}
205 
206 		++i;
207 	}
208 
209 finish:
210 	k_mutex_unlock(&data->lock);
211 
212 	return rc;
213 }
214 
215 static const struct bbram_driver_api microchip_mcp7940n_bbram_api = {
216 	.get_size = microchip_mcp7940n_bbram_size,
217 	.check_invalid = microchip_mcp7940n_bbram_is_invalid,
218 	.check_standby_power = microchip_mcp7940n_bbram_check_standby_power,
219 	.read = microchip_mcp7940n_bbram_read,
220 	.write = microchip_mcp7940n_bbram_write,
221 };
222 
223 #define MICROCHIP_MCP7940N_BBRAM_DEVICE(inst)							\
224 	static struct microchip_mcp7940n_bbram_data microchip_mcp7940n_bbram_data_##inst;	\
225 	static const struct microchip_mcp7940n_bbram_config					\
226 		microchip_mcp7940n_bbram_config_##inst = {					\
227 		.i2c = I2C_DT_SPEC_INST_GET(inst),						\
228 	};											\
229 	DEVICE_DT_INST_DEFINE(inst,								\
230 			      &microchip_mcp7940n_bbram_init,					\
231 			      NULL,								\
232 			      &microchip_mcp7940n_bbram_data_##inst,				\
233 			      &microchip_mcp7940n_bbram_config_##inst,				\
234 			      POST_KERNEL,							\
235 			      CONFIG_BBRAM_INIT_PRIORITY,					\
236 			      &microchip_mcp7940n_bbram_api);
237 
238 DT_INST_FOREACH_STATUS_OKAY(MICROCHIP_MCP7940N_BBRAM_DEVICE)
239