Skip to content

Commit 9b36e72

Browse files
pdgendtcarlescufi
authored andcommitted
drivers: i2c: Add NXP SC18IM704 I2C support
Implement external I2C controller driver with NXP's SC18IM704 device. Signed-off-by: Pieter De Gendt <pieter.degendt@basalte.be>
1 parent d40fcb9 commit 9b36e72

File tree

7 files changed

+543
-0
lines changed

7 files changed

+543
-0
lines changed

drivers/i2c/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ zephyr_library_sources_ifdef(CONFIG_I2C_TCA954X i2c_tca954x.c)
3939
zephyr_library_sources_ifdef(CONFIG_I2C_XEC_V2 i2c_mchp_xec_v2.c)
4040
zephyr_library_sources_ifdef(CONFIG_I2C_GD32 i2c_gd32.c)
4141
zephyr_library_sources_ifdef(CONFIG_I2C_ANDES_ATCIIC100 i2c_andes_atciic100.c)
42+
zephyr_library_sources_ifdef(CONFIG_I2C_SC18IM704 i2c_sc18im704.c)
4243

4344
zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V1
4445
i2c_ll_stm32_v1.c

drivers/i2c/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ source "drivers/i2c/Kconfig.rcar"
6464
source "drivers/i2c/Kconfig.tca954x"
6565
source "drivers/i2c/Kconfig.gd32"
6666
source "drivers/i2c/Kconfig.andes_atciic100"
67+
source "drivers/i2c/Kconfig.sc18im704"
6768

6869
config I2C_INIT_PRIORITY
6970
int "Init priority"

drivers/i2c/Kconfig.sc18im704

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Copyright (c) 2023 Basalte bv
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config I2C_SC18IM704
5+
bool "NXP SC18IM704 I2C controller driver"
6+
default y
7+
depends on DT_HAS_NXP_SC18IM704_I2C_ENABLED
8+
help
9+
Enables NXP SC18IM704 I2C controller driver
10+
11+
if I2C_SC18IM704
12+
13+
config I2C_SC18IM704_INIT_PRIORITY
14+
int "SC18IM704 I2C init priority"
15+
default 51
16+
help
17+
SC18IM704 I2C controller initialization priority.
18+
19+
Note: Has to be greater than the UART bus initialization priority.
20+
21+
config I2C_SC18IM704_VERIFY
22+
bool "Verify SC18IM704 I2C transfers"
23+
default y
24+
help
25+
Verify the I2C state register after I2C transfers to detect errors.
26+
27+
endif

drivers/i2c/i2c_sc18im704.c

Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
/*
2+
* Copyright (c), 2023 Basalte bv
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT nxp_sc18im704_i2c
8+
9+
#include <errno.h>
10+
#include <zephyr/kernel.h>
11+
#include <zephyr/device.h>
12+
#include <zephyr/init.h>
13+
#include <zephyr/drivers/gpio.h>
14+
#include <zephyr/drivers/i2c.h>
15+
#include <zephyr/drivers/uart.h>
16+
17+
#include <zephyr/logging/log.h>
18+
LOG_MODULE_REGISTER(i2c_sc18im, CONFIG_I2C_LOG_LEVEL);
19+
20+
#include "i2c_sc18im704.h"
21+
22+
struct i2c_sc18im_config {
23+
const struct device *bus;
24+
uint32_t bus_speed;
25+
const struct gpio_dt_spec reset_gpios;
26+
};
27+
28+
struct i2c_sc18im_data {
29+
struct k_mutex lock;
30+
uint32_t i2c_config;
31+
};
32+
33+
int sc18im704_claim(const struct device *dev)
34+
{
35+
struct i2c_sc18im_data *data = dev->data;
36+
37+
return k_mutex_lock(&data->lock, K_FOREVER);
38+
}
39+
40+
int sc18im704_release(const struct device *dev)
41+
{
42+
struct i2c_sc18im_data *data = dev->data;
43+
44+
return k_mutex_unlock(&data->lock);
45+
}
46+
47+
int sc18im704_transfer(const struct device *dev,
48+
const uint8_t *tx_data, uint8_t tx_len,
49+
uint8_t *rx_data, uint8_t rx_len)
50+
{
51+
const struct i2c_sc18im_config *cfg = dev->config;
52+
struct i2c_sc18im_data *data = dev->data;
53+
int ret = 0;
54+
55+
ret = k_mutex_lock(&data->lock, K_FOREVER);
56+
if (ret < 0) {
57+
return ret;
58+
}
59+
60+
if (tx_data != NULL) {
61+
for (uint8_t i = 0; i < tx_len; ++i) {
62+
uart_poll_out(cfg->bus, tx_data[i]);
63+
}
64+
}
65+
66+
if (rx_data != NULL) {
67+
uint64_t end;
68+
69+
for (uint8_t i = 0; i < rx_len && ret == 0; ++i) {
70+
/* Make sure we don't wait forever */
71+
end = sys_clock_timeout_end_calc(K_SECONDS(1));
72+
73+
do {
74+
ret = uart_poll_in(cfg->bus, &rx_data[i]);
75+
} while (ret == -1 && end > k_uptime_ticks());
76+
}
77+
78+
/* -1 indicates we timed out */
79+
ret = ret == -1 ? -EAGAIN : ret;
80+
81+
if (ret < 0) {
82+
LOG_ERR("Failed to read data (%d)", ret);
83+
}
84+
}
85+
86+
k_mutex_unlock(&data->lock);
87+
88+
return ret;
89+
}
90+
91+
static int i2c_sc18im_configure(const struct device *dev, uint32_t config)
92+
{
93+
struct i2c_sc18im_data *data = dev->data;
94+
95+
if (!(I2C_MODE_CONTROLLER & config)) {
96+
return -EINVAL;
97+
}
98+
99+
if (I2C_ADDR_10_BITS & config) {
100+
return -EINVAL;
101+
}
102+
103+
if (I2C_SPEED_GET(config) != I2C_SPEED_GET(data->i2c_config)) {
104+
uint8_t buf[] = {
105+
SC18IM704_CMD_WRITE_REG,
106+
SC18IM704_REG_I2C_CLK_L,
107+
0,
108+
SC18IM704_CMD_STOP,
109+
};
110+
int ret;
111+
112+
/* CLK value is calculated as 15000000 / (8 * freq), see datasheet */
113+
switch (I2C_SPEED_GET(config)) {
114+
case I2C_SPEED_STANDARD:
115+
buf[2] = 0x13; /* 99 kHz */
116+
break;
117+
case I2C_SPEED_FAST:
118+
buf[2] = 0x05; /* 375 kHz */
119+
break;
120+
default:
121+
return -EINVAL;
122+
}
123+
124+
ret = sc18im704_transfer(dev, buf, sizeof(buf), NULL, 0);
125+
if (ret < 0) {
126+
LOG_ERR("Failed to set I2C speed (%d)", ret);
127+
return -EIO;
128+
}
129+
}
130+
131+
data->i2c_config = config;
132+
133+
return 0;
134+
}
135+
136+
static int i2c_sc18im_get_config(const struct device *dev, uint32_t *config)
137+
{
138+
struct i2c_sc18im_data *data = dev->data;
139+
140+
*config = data->i2c_config;
141+
142+
return 0;
143+
}
144+
145+
static int i2c_sc18im_transfer_msg(const struct device *dev,
146+
struct i2c_msg *msg,
147+
uint16_t addr)
148+
{
149+
uint8_t start[] = {
150+
SC18IM704_CMD_I2C_START,
151+
0x00,
152+
0x00,
153+
};
154+
uint8_t stop = SC18IM704_CMD_STOP;
155+
int ret;
156+
157+
if (msg->flags & I2C_MSG_ADDR_10_BITS || msg->len > UINT8_MAX) {
158+
return -EINVAL;
159+
}
160+
161+
start[1] = addr | (msg->flags & I2C_MSG_RW_MASK);
162+
start[2] = msg->len;
163+
164+
ret = sc18im704_transfer(dev, start, sizeof(start), NULL, 0);
165+
if (ret < 0) {
166+
return ret;
167+
}
168+
169+
if (msg->flags & I2C_MSG_READ) {
170+
/* Send the stop character before reading */
171+
ret = sc18im704_transfer(dev, &stop, 1, msg->buf, msg->len);
172+
if (ret < 0) {
173+
return ret;
174+
}
175+
176+
} else {
177+
ret = sc18im704_transfer(dev, msg->buf, msg->len, NULL, 0);
178+
if (ret < 0) {
179+
return ret;
180+
}
181+
182+
if (msg->flags & I2C_MSG_STOP) {
183+
ret = sc18im704_transfer(dev, &stop, 1, NULL, 0);
184+
if (ret < 0) {
185+
return ret;
186+
}
187+
}
188+
}
189+
190+
return 0;
191+
}
192+
193+
static int i2c_sc18im_transfer(const struct device *dev,
194+
struct i2c_msg *msgs,
195+
uint8_t num_msgs, uint16_t addr)
196+
{
197+
int ret;
198+
199+
if (num_msgs == 0) {
200+
return 0;
201+
}
202+
203+
ret = sc18im704_claim(dev);
204+
if (ret < 0) {
205+
LOG_ERR("Failed to claim I2C bridge (%d)", ret);
206+
return ret;
207+
}
208+
209+
for (uint8_t i = 0; i < num_msgs && ret == 0; ++i) {
210+
ret = i2c_sc18im_transfer_msg(dev, &msgs[i], addr);
211+
}
212+
213+
#ifdef CONFIG_I2C_SC18IM704_VERIFY
214+
if (ret == 0) {
215+
uint8_t buf[] = {
216+
SC18IM704_CMD_READ_REG,
217+
SC18IM704_REG_I2C_STAT,
218+
SC18IM704_CMD_STOP,
219+
};
220+
uint8_t data;
221+
222+
ret = sc18im704_transfer(dev, buf, sizeof(buf), &data, 1);
223+
224+
if (ret == 0 && data != SC18IM704_I2C_STAT_OK) {
225+
ret = -EIO;
226+
}
227+
}
228+
#endif /* CONFIG_I2C_SC18IM704_VERIFY */
229+
230+
sc18im704_release(dev);
231+
232+
return ret;
233+
}
234+
235+
static int i2c_sc18im_init(const struct device *dev)
236+
{
237+
const struct i2c_sc18im_config *cfg = dev->config;
238+
struct i2c_sc18im_data *data = dev->data;
239+
int ret;
240+
241+
/* The device baudrate after reset is 9600 */
242+
struct uart_config uart_cfg = {
243+
.baudrate = 9600,
244+
.parity = UART_CFG_PARITY_NONE,
245+
.stop_bits = UART_CFG_STOP_BITS_1,
246+
.data_bits = UART_CFG_DATA_BITS_8,
247+
.flow_ctrl = UART_CFG_FLOW_CTRL_NONE,
248+
};
249+
250+
k_mutex_init(&data->lock);
251+
252+
if (!device_is_ready(cfg->bus)) {
253+
LOG_ERR("UART bus not ready");
254+
return -ENODEV;
255+
}
256+
257+
ret = uart_configure(cfg->bus, &uart_cfg);
258+
if (ret < 0) {
259+
LOG_ERR("Failed to configure UART (%d)", ret);
260+
return ret;
261+
}
262+
263+
if (cfg->reset_gpios.port) {
264+
uint8_t buf[2];
265+
266+
if (!device_is_ready(cfg->reset_gpios.port)) {
267+
LOG_ERR("Reset GPIO device not ready");
268+
return -ENODEV;
269+
}
270+
271+
ret = gpio_pin_configure_dt(&cfg->reset_gpios, GPIO_OUTPUT_ACTIVE);
272+
if (ret < 0) {
273+
LOG_ERR("Failed to configure reset GPIO (%d)", ret);
274+
return ret;
275+
}
276+
277+
ret = gpio_pin_set_dt(&cfg->reset_gpios, 0);
278+
if (ret < 0) {
279+
LOG_ERR("Failed to set reset GPIO (%d)", ret);
280+
return ret;
281+
}
282+
283+
/* The device sends "OK" */
284+
ret = sc18im704_transfer(dev, NULL, 0, buf, sizeof(buf));
285+
if (ret < 0) {
286+
LOG_ERR("Failed to get OK (%d)", ret);
287+
return ret;
288+
}
289+
}
290+
291+
if (cfg->bus_speed != 9600) {
292+
uint16_t brg = (7372800 / cfg->bus_speed) - 16;
293+
uint8_t buf[] = {
294+
SC18IM704_CMD_WRITE_REG,
295+
SC18IM704_REG_BRG0,
296+
brg & 0xff,
297+
SC18IM704_REG_BRG1,
298+
brg >> 8,
299+
SC18IM704_CMD_STOP,
300+
};
301+
302+
ret = sc18im704_transfer(dev, buf, sizeof(buf), NULL, 0);
303+
if (ret < 0) {
304+
LOG_ERR("Failed to set baudrate (%d)", ret);
305+
return ret;
306+
}
307+
308+
/* Make sure UART buffer is sent */
309+
k_msleep(1);
310+
311+
/* Re-configure the UART controller with the new baudrate */
312+
uart_cfg.baudrate = cfg->bus_speed;
313+
ret = uart_configure(cfg->bus, &uart_cfg);
314+
if (ret < 0) {
315+
LOG_ERR("Failed to re-configure UART (%d)", ret);
316+
return ret;
317+
}
318+
}
319+
320+
return 0;
321+
}
322+
323+
static const struct i2c_driver_api i2c_sc18im_driver_api = {
324+
.configure = i2c_sc18im_configure,
325+
.get_config = i2c_sc18im_get_config,
326+
.transfer = i2c_sc18im_transfer,
327+
};
328+
329+
#define I2C_SC18IM_DEFINE(n) \
330+
\
331+
static const struct i2c_sc18im_config i2c_sc18im_config_##n = { \
332+
.bus = DEVICE_DT_GET(DT_BUS(DT_INST_PARENT(n))), \
333+
.bus_speed = DT_PROP_OR(DT_INST_PARENT(n), target_speed, 9600), \
334+
.reset_gpios = GPIO_DT_SPEC_GET_OR(DT_INST_PARENT(n), reset_gpios, {0}), \
335+
}; \
336+
static struct i2c_sc18im_data i2c_sc18im_data_##n = { \
337+
.i2c_config = I2C_MODE_CONTROLLER | (I2C_SPEED_STANDARD << I2C_SPEED_SHIFT), \
338+
}; \
339+
\
340+
DEVICE_DT_INST_DEFINE(n, i2c_sc18im_init, NULL, \
341+
&i2c_sc18im_data_##n, &i2c_sc18im_config_##n, \
342+
POST_KERNEL, CONFIG_I2C_SC18IM704_INIT_PRIORITY, \
343+
&i2c_sc18im_driver_api);
344+
345+
DT_INST_FOREACH_STATUS_OKAY(I2C_SC18IM_DEFINE)

0 commit comments

Comments
 (0)