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