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 µchip_mcp7940n_bbram_init, \
231 NULL, \
232 µchip_mcp7940n_bbram_data_##inst, \
233 µchip_mcp7940n_bbram_config_##inst, \
234 POST_KERNEL, \
235 CONFIG_BBRAM_INIT_PRIORITY, \
236 µchip_mcp7940n_bbram_api);
237
238 DT_INST_FOREACH_STATUS_OKAY(MICROCHIP_MCP7940N_BBRAM_DEVICE)
239