1 /*
2 * Copyright (c) 2023 SILA Embedded Solutions GmbH
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #include <zephyr/kernel.h>
7 #include <zephyr/device.h>
8 #include <zephyr/drivers/i2c.h>
9 #include <zephyr/drivers/i2c/stm32.h>
10 #include <zephyr/drivers/pinctrl.h>
11 #include <zephyr/drivers/smbus.h>
12 #include <zephyr/logging/log.h>
13 #include <zephyr/sys/byteorder.h>
14 #include <soc.h>
15
16 #include "smbus_utils.h"
17
18 LOG_MODULE_REGISTER(stm32_smbus, CONFIG_SMBUS_LOG_LEVEL);
19
20 struct smbus_stm32_config {
21 const struct pinctrl_dev_config *pcfg;
22 const struct device *i2c_dev;
23 };
24
25 struct smbus_stm32_data {
26 uint32_t config;
27 const struct device *dev;
28 #ifdef CONFIG_SMBUS_STM32_SMBALERT
29 sys_slist_t smbalert_callbacks;
30 struct k_work smbalert_work;
31 #endif /* CONFIG_SMBUS_STM32_SMBALERT */
32 };
33
34 #ifdef CONFIG_SMBUS_STM32_SMBALERT
smbus_stm32_smbalert_isr(const struct device * dev)35 static void smbus_stm32_smbalert_isr(const struct device *dev)
36 {
37 struct smbus_stm32_data *data = dev->data;
38
39 k_work_submit(&data->smbalert_work);
40 }
41
smbus_stm32_smbalert_work(struct k_work * work)42 static void smbus_stm32_smbalert_work(struct k_work *work)
43 {
44 struct smbus_stm32_data *data = CONTAINER_OF(work, struct smbus_stm32_data, smbalert_work);
45 const struct device *dev = data->dev;
46
47 LOG_DBG("%s: got SMB alert", dev->name);
48
49 smbus_loop_alert_devices(dev, &data->smbalert_callbacks);
50 }
51
smbus_stm32_smbalert_set_cb(const struct device * dev,struct smbus_callback * cb)52 static int smbus_stm32_smbalert_set_cb(const struct device *dev, struct smbus_callback *cb)
53 {
54 struct smbus_stm32_data *data = dev->data;
55
56 return smbus_callback_set(&data->smbalert_callbacks, cb);
57 }
58
smbus_stm32_smbalert_remove_cb(const struct device * dev,struct smbus_callback * cb)59 static int smbus_stm32_smbalert_remove_cb(const struct device *dev, struct smbus_callback *cb)
60 {
61 struct smbus_stm32_data *data = dev->data;
62
63 return smbus_callback_remove(&data->smbalert_callbacks, cb);
64 }
65 #endif /* CONFIG_SMBUS_STM32_SMBALERT */
66
smbus_stm32_init(const struct device * dev)67 static int smbus_stm32_init(const struct device *dev)
68 {
69 const struct smbus_stm32_config *config = dev->config;
70 struct smbus_stm32_data *data = dev->data;
71 int result;
72
73 data->dev = dev;
74
75 if (!device_is_ready(config->i2c_dev)) {
76 LOG_ERR("%s: I2C device is not ready", dev->name);
77 return -ENODEV;
78 }
79
80 result = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
81 if (result < 0) {
82 LOG_ERR("%s: pinctrl setup failed (%d)", dev->name, result);
83 return result;
84 }
85
86 #ifdef CONFIG_SMBUS_STM32_SMBALERT
87 k_work_init(&data->smbalert_work, smbus_stm32_smbalert_work);
88
89 i2c_stm32_smbalert_set_callback(config->i2c_dev, smbus_stm32_smbalert_isr, dev);
90 #endif /* CONFIG_SMBUS_STM32_SMBALERT */
91
92 return 0;
93 }
94
smbus_stm32_configure(const struct device * dev,uint32_t config_value)95 static int smbus_stm32_configure(const struct device *dev, uint32_t config_value)
96 {
97 const struct smbus_stm32_config *config = dev->config;
98 struct smbus_stm32_data *data = dev->data;
99
100 if (config_value & SMBUS_MODE_PEC) {
101 LOG_ERR("%s: not implemented", dev->name);
102 return -EINVAL;
103 }
104
105 if (config_value & SMBUS_MODE_HOST_NOTIFY) {
106 LOG_ERR("%s: not available", dev->name);
107 return -EINVAL;
108 }
109
110 if (config_value & SMBUS_MODE_CONTROLLER) {
111 LOG_DBG("%s: configuring SMB in host mode", dev->name);
112 i2c_stm32_set_smbus_mode(config->i2c_dev, I2CSTM32MODE_SMBUSHOST);
113 } else {
114 LOG_DBG("%s: configuring SMB in device mode", dev->name);
115 i2c_stm32_set_smbus_mode(config->i2c_dev, I2CSTM32MODE_SMBUSDEVICE);
116 }
117
118 if (config_value & SMBUS_MODE_SMBALERT) {
119 LOG_DBG("%s: activating SMB alert", dev->name);
120 i2c_stm32_smbalert_enable(config->i2c_dev);
121 } else {
122 LOG_DBG("%s: deactivating SMB alert", dev->name);
123 i2c_stm32_smbalert_disable(config->i2c_dev);
124 }
125
126 data->config = config_value;
127 return 0;
128 }
129
smbus_stm32_get_config(const struct device * dev,uint32_t * config)130 static int smbus_stm32_get_config(const struct device *dev, uint32_t *config)
131 {
132 struct smbus_stm32_data *data = dev->data;
133 *config = data->config;
134 return 0;
135 }
136
smbus_stm32_quick(const struct device * dev,uint16_t periph_addr,enum smbus_direction rw)137 static int smbus_stm32_quick(const struct device *dev, uint16_t periph_addr,
138 enum smbus_direction rw)
139 {
140 const struct smbus_stm32_config *config = dev->config;
141
142 switch (rw) {
143 case SMBUS_MSG_WRITE:
144 return i2c_write(config->i2c_dev, NULL, 0, periph_addr);
145 case SMBUS_MSG_READ:
146 return i2c_read(config->i2c_dev, NULL, 0, periph_addr);
147 default:
148 LOG_ERR("%s: invalid smbus direction %i", dev->name, rw);
149 return -EINVAL;
150 }
151 }
152
smbus_stm32_byte_write(const struct device * dev,uint16_t periph_addr,uint8_t command)153 static int smbus_stm32_byte_write(const struct device *dev, uint16_t periph_addr, uint8_t command)
154 {
155 const struct smbus_stm32_config *config = dev->config;
156
157 return i2c_write(config->i2c_dev, &command, sizeof(command), periph_addr);
158 }
159
smbus_stm32_byte_read(const struct device * dev,uint16_t periph_addr,uint8_t * byte)160 static int smbus_stm32_byte_read(const struct device *dev, uint16_t periph_addr, uint8_t *byte)
161 {
162 const struct smbus_stm32_config *config = dev->config;
163
164 return i2c_read(config->i2c_dev, byte, sizeof(*byte), periph_addr);
165 }
166
smbus_stm32_byte_data_write(const struct device * dev,uint16_t periph_addr,uint8_t command,uint8_t byte)167 static int smbus_stm32_byte_data_write(const struct device *dev, uint16_t periph_addr,
168 uint8_t command, uint8_t byte)
169 {
170 const struct smbus_stm32_config *config = dev->config;
171 uint8_t buffer[] = {
172 command,
173 byte,
174 };
175
176 return i2c_write(config->i2c_dev, buffer, ARRAY_SIZE(buffer), periph_addr);
177 }
178
smbus_stm32_byte_data_read(const struct device * dev,uint16_t periph_addr,uint8_t command,uint8_t * byte)179 static int smbus_stm32_byte_data_read(const struct device *dev, uint16_t periph_addr,
180 uint8_t command, uint8_t *byte)
181 {
182 const struct smbus_stm32_config *config = dev->config;
183
184 return i2c_write_read(config->i2c_dev, periph_addr, &command, sizeof(command), byte,
185 sizeof(*byte));
186 }
187
smbus_stm32_word_data_write(const struct device * dev,uint16_t periph_addr,uint8_t command,uint16_t word)188 static int smbus_stm32_word_data_write(const struct device *dev, uint16_t periph_addr,
189 uint8_t command, uint16_t word)
190 {
191 const struct smbus_stm32_config *config = dev->config;
192 uint8_t buffer[sizeof(command) + sizeof(word)];
193
194 buffer[0] = command;
195 sys_put_le16(word, buffer + 1);
196
197 return i2c_write(config->i2c_dev, buffer, ARRAY_SIZE(buffer), periph_addr);
198 }
199
smbus_stm32_word_data_read(const struct device * dev,uint16_t periph_addr,uint8_t command,uint16_t * word)200 static int smbus_stm32_word_data_read(const struct device *dev, uint16_t periph_addr,
201 uint8_t command, uint16_t *word)
202 {
203 const struct smbus_stm32_config *config = dev->config;
204 int result;
205
206 result = i2c_write_read(config->i2c_dev, periph_addr, &command, sizeof(command), word,
207 sizeof(*word));
208 *word = sys_le16_to_cpu(*word);
209
210 return result;
211 }
212
smbus_stm32_pcall(const struct device * dev,uint16_t periph_addr,uint8_t command,uint16_t send_word,uint16_t * recv_word)213 static int smbus_stm32_pcall(const struct device *dev, uint16_t periph_addr, uint8_t command,
214 uint16_t send_word, uint16_t *recv_word)
215 {
216 const struct smbus_stm32_config *config = dev->config;
217 uint8_t buffer[sizeof(command) + sizeof(send_word)];
218 int result;
219
220 buffer[0] = command;
221 sys_put_le16(send_word, buffer + 1);
222
223 result = i2c_write_read(config->i2c_dev, periph_addr, buffer, ARRAY_SIZE(buffer), recv_word,
224 sizeof(*recv_word));
225 *recv_word = sys_le16_to_cpu(*recv_word);
226
227 return result;
228 }
229
smbus_stm32_block_write(const struct device * dev,uint16_t periph_addr,uint8_t command,uint8_t count,uint8_t * buf)230 static int smbus_stm32_block_write(const struct device *dev, uint16_t periph_addr, uint8_t command,
231 uint8_t count, uint8_t *buf)
232 {
233 const struct smbus_stm32_config *config = dev->config;
234 struct i2c_msg messages[] = {
235 {
236 .buf = &command,
237 .len = sizeof(command),
238 .flags = 0,
239 },
240 {
241 .buf = buf,
242 .len = count,
243 .flags = 0,
244 },
245 };
246
247 return i2c_transfer(config->i2c_dev, messages, ARRAY_SIZE(messages), periph_addr);
248 }
249
250 static DEVICE_API(smbus, smbus_stm32_api) = {
251 .configure = smbus_stm32_configure,
252 .get_config = smbus_stm32_get_config,
253 .smbus_quick = smbus_stm32_quick,
254 .smbus_byte_write = smbus_stm32_byte_write,
255 .smbus_byte_read = smbus_stm32_byte_read,
256 .smbus_byte_data_write = smbus_stm32_byte_data_write,
257 .smbus_byte_data_read = smbus_stm32_byte_data_read,
258 .smbus_word_data_write = smbus_stm32_word_data_write,
259 .smbus_word_data_read = smbus_stm32_word_data_read,
260 .smbus_pcall = smbus_stm32_pcall,
261 .smbus_block_write = smbus_stm32_block_write,
262 #ifdef CONFIG_SMBUS_STM32_SMBALERT
263 .smbus_smbalert_set_cb = smbus_stm32_smbalert_set_cb,
264 .smbus_smbalert_remove_cb = smbus_stm32_smbalert_remove_cb,
265 #else
266 .smbus_smbalert_set_cb = NULL,
267 .smbus_smbalert_remove_cb = NULL,
268 #endif /* CONFIG_SMBUS_STM32_SMBALERT */
269 .smbus_block_read = NULL,
270 .smbus_block_pcall = NULL,
271 .smbus_host_notify_set_cb = NULL,
272 .smbus_host_notify_remove_cb = NULL,
273 };
274
275 #define DT_DRV_COMPAT st_stm32_smbus
276
277 #define SMBUS_STM32_DEVICE_INIT(n) \
278 PINCTRL_DT_INST_DEFINE(n); \
279 static struct smbus_stm32_config smbus_stm32_config_##n = { \
280 .i2c_dev = DEVICE_DT_GET(DT_INST_PROP(n, i2c)), \
281 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
282 }; \
283 \
284 static struct smbus_stm32_data smbus_stm32_data_##n; \
285 \
286 SMBUS_DEVICE_DT_INST_DEFINE(n, smbus_stm32_init, NULL, &smbus_stm32_data_##n, \
287 &smbus_stm32_config_##n, POST_KERNEL, \
288 CONFIG_SMBUS_INIT_PRIORITY, &smbus_stm32_api);
289
290 DT_INST_FOREACH_STATUS_OKAY(SMBUS_STM32_DEVICE_INIT)
291