Skip to content

Commit 8a4dbb5

Browse files
henrikbrixandersenMaureenHelm
authored andcommitted
drivers: i2c: rv32m1: add I2C driver for the RV32M1 RI5CY SoC
Add driver and device tree binding for the Low Power Inter-Integrated Circuit (LPI2C) controllers found in the RV32M1 RI5CY SoC. Signed-off-by: Henrik Brix Andersen <[email protected]>
1 parent a061443 commit 8a4dbb5

File tree

10 files changed

+423
-0
lines changed

10 files changed

+423
-0
lines changed

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@
140140
/drivers/usb/ @jfischer-phytec-iot @finikorg
141141
/drivers/usb/device/usb_dc_stm32.c @ydamigos @loicpoulain
142142
/drivers/i2c/i2c_ll_stm32* @ldts @ydamigos
143+
/drivers/i2c/i2c_rv32m1_lpi2c* @henrikbrixandersen
143144
/drivers/wifi/ @jukkar @tbursztyka @pfalcon
144145
/drivers/wifi/eswifi/ @loicpoulain
145146
/dts/arm/st/ @erwango

drivers/i2c/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ zephyr_library_sources_ifdef(CONFIG_I2C_SBCON i2c_sbcon.c)
2020
zephyr_library_sources_ifdef(CONFIG_I2C_SIFIVE i2c_sifive.c)
2121
zephyr_library_sources_ifdef(CONFIG_I2C_NIOS2 i2c_nios2.c)
2222
zephyr_library_sources_ifdef(CONFIG_I2C_GECKO i2c_gecko.c)
23+
zephyr_library_sources_ifdef(CONFIG_I2C_RV32M1_LPI2C i2c_rv32m1_lpi2c.c)
2324

2425
zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V1
2526
i2c_ll_stm32_v1.c

drivers/i2c/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,4 +300,11 @@ config I2C_NIOS2
300300
help
301301
Enable the Nios-II I2C driver.
302302

303+
config I2C_RV32M1_LPI2C
304+
bool "RV32M1 LPI2C driver"
305+
depends on HAS_RV32M1_LPI2C && CLOCK_CONTROL
306+
select HAS_DTS_I2C
307+
help
308+
Enable the RV32M1 LPI2C driver.
309+
303310
endif # I2C

drivers/i2c/i2c_rv32m1_lpi2c.c

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
/*
2+
* Copyright (c) 2019, Henrik Brix Andersen <[email protected]>
3+
*
4+
* Based on the i2c_mcux_lpi2c.c driver, which is:
5+
* Copyright (c) 2016 Freescale Semiconductor, Inc.
6+
* Copyright (c) 2019, NXP
7+
*
8+
* SPDX-License-Identifier: Apache-2.0
9+
*/
10+
11+
#include <i2c.h>
12+
#include <clock_control.h>
13+
#include <fsl_lpi2c.h>
14+
#include <logging/log.h>
15+
LOG_MODULE_REGISTER(rv32m1_lpi2c);
16+
17+
#include "i2c-priv.h"
18+
19+
struct rv32m1_lpi2c_config {
20+
LPI2C_Type *base;
21+
char *clock_controller;
22+
clock_control_subsys_t clock_subsys;
23+
clock_ip_name_t clock_ip_name;
24+
u32_t clock_ip_src;
25+
u32_t bitrate;
26+
void (*irq_config_func)(struct device *dev);
27+
};
28+
29+
struct rv32m1_lpi2c_data {
30+
lpi2c_master_handle_t handle;
31+
struct k_sem transfer_sync;
32+
struct k_sem completion_sync;
33+
status_t completion_status;
34+
};
35+
36+
static int rv32m1_lpi2c_configure(struct device *dev, u32_t dev_config)
37+
{
38+
const struct rv32m1_lpi2c_config *config = dev->config->config_info;
39+
struct device *clk;
40+
u32_t baudrate;
41+
u32_t clk_freq;
42+
int err;
43+
44+
if (!(I2C_MODE_MASTER & dev_config)) {
45+
/* Slave mode not supported - yet */
46+
LOG_ERR("Slave mode not supported");
47+
return -ENOTSUP;
48+
}
49+
50+
if (I2C_ADDR_10_BITS & dev_config) {
51+
/* FSL LPI2C driver only supports 7-bit addressing */
52+
LOG_ERR("10 bit addressing not supported");
53+
return -ENOTSUP;
54+
}
55+
56+
switch (I2C_SPEED_GET(dev_config)) {
57+
case I2C_SPEED_STANDARD:
58+
baudrate = KHZ(100);
59+
break;
60+
case I2C_SPEED_FAST:
61+
baudrate = KHZ(400);
62+
break;
63+
case I2C_SPEED_FAST_PLUS:
64+
baudrate = MHZ(1);
65+
break;
66+
/* TODO: only if SCL pin implements current source pull-up */
67+
/* case I2C_SPEED_HIGH: */
68+
/* baudrate = KHZ(3400); */
69+
/* break; */
70+
/* TODO: ultra-fast requires pin_config setting */
71+
/* case I2C_SPEED_ULTRA: */
72+
/* baudrate = MHZ(5); */
73+
/* break; */
74+
default:
75+
LOG_ERR("Unsupported speed");
76+
return -ENOTSUP;
77+
}
78+
79+
clk = device_get_binding(config->clock_controller);
80+
if (!clk) {
81+
LOG_ERR("Could not get clock controller '%s'",
82+
config->clock_controller);
83+
return -EINVAL;
84+
}
85+
86+
err = clock_control_get_rate(clk, config->clock_subsys, &clk_freq);
87+
if (err) {
88+
LOG_ERR("Could not get clock frequency (err %d)", err);
89+
return -EINVAL;
90+
}
91+
92+
LPI2C_MasterSetBaudRate(config->base, clk_freq, baudrate);
93+
94+
return 0;
95+
}
96+
97+
static void rv32m1_lpi2c_master_transfer_callback(LPI2C_Type *base,
98+
lpi2c_master_handle_t *handle,
99+
status_t completionStatus,
100+
void *userData)
101+
{
102+
struct device *dev = userData;
103+
struct rv32m1_lpi2c_data *data = dev->driver_data;
104+
105+
ARG_UNUSED(base);
106+
ARG_UNUSED(handle);
107+
108+
data->completion_status = completionStatus;
109+
k_sem_give(&data->completion_sync);
110+
}
111+
112+
static u32_t rv32m1_lpi2c_convert_flags(int msg_flags)
113+
{
114+
u32_t flags = 0U;
115+
116+
if (!(msg_flags & I2C_MSG_STOP)) {
117+
flags |= kLPI2C_TransferNoStopFlag;
118+
}
119+
120+
if (msg_flags & I2C_MSG_RESTART) {
121+
flags |= kLPI2C_TransferRepeatedStartFlag;
122+
}
123+
124+
return flags;
125+
}
126+
127+
static int rv32m1_lpi2c_transfer(struct device *dev, struct i2c_msg *msgs,
128+
u8_t num_msgs, u16_t addr)
129+
{
130+
const struct rv32m1_lpi2c_config *config = dev->config->config_info;
131+
struct rv32m1_lpi2c_data *data = dev->driver_data;
132+
lpi2c_master_transfer_t transfer;
133+
status_t status;
134+
int ret = 0;
135+
136+
k_sem_take(&data->transfer_sync, K_FOREVER);
137+
138+
/* Iterate over all the messages */
139+
for (int i = 0; i < num_msgs; i++) {
140+
if (I2C_MSG_ADDR_10_BITS & msgs->flags) {
141+
ret = -ENOTSUP;
142+
goto out;
143+
}
144+
145+
/* Initialize the transfer descriptor */
146+
transfer.flags = rv32m1_lpi2c_convert_flags(msgs->flags);
147+
148+
/* Prevent the controller to send a start condition between
149+
* messages, except if explicitly requested.
150+
*/
151+
if (i != 0 && !(msgs->flags & I2C_MSG_RESTART)) {
152+
transfer.flags |= kLPI2C_TransferNoStartFlag;
153+
}
154+
155+
transfer.slaveAddress = addr;
156+
transfer.direction = (msgs->flags & I2C_MSG_READ)
157+
? kLPI2C_Read : kLPI2C_Write;
158+
transfer.subaddress = 0;
159+
transfer.subaddressSize = 0;
160+
transfer.data = msgs->buf;
161+
transfer.dataSize = msgs->len;
162+
163+
/* Start the transfer */
164+
status = LPI2C_MasterTransferNonBlocking(config->base,
165+
&data->handle,
166+
&transfer);
167+
168+
/* Return an error if the transfer didn't start successfully
169+
* e.g., if the bus was busy
170+
*/
171+
if (status != kStatus_Success) {
172+
LOG_DBG("Could not start transfer (status %d)", status);
173+
ret = -EIO;
174+
goto out;
175+
}
176+
177+
/* Wait for the transfer to complete */
178+
k_sem_take(&data->completion_sync, K_FOREVER);
179+
180+
/* Return an error if the transfer didn't complete
181+
* successfully. e.g., nak, timeout, lost arbitration
182+
*/
183+
if (data->completion_status != kStatus_Success) {
184+
LOG_DBG("Transfer failed (status %d)",
185+
data->completion_status);
186+
LPI2C_MasterTransferAbort(config->base, &data->handle);
187+
ret = -EIO;
188+
goto out;
189+
}
190+
191+
/* Move to the next message */
192+
msgs++;
193+
}
194+
195+
out:
196+
k_sem_give(&data->transfer_sync);
197+
return ret;
198+
}
199+
200+
static void rv32m1_lpi2c_isr(void *arg)
201+
{
202+
struct device *dev = (struct device *)arg;
203+
const struct rv32m1_lpi2c_config *config = dev->config->config_info;
204+
struct rv32m1_lpi2c_data *data = dev->driver_data;
205+
206+
LPI2C_MasterTransferHandleIRQ(config->base, &data->handle);
207+
}
208+
209+
static int rv32m1_lpi2c_init(struct device *dev)
210+
{
211+
const struct rv32m1_lpi2c_config *config = dev->config->config_info;
212+
struct rv32m1_lpi2c_data *data = dev->driver_data;
213+
lpi2c_master_config_t master_config;
214+
struct device *clk;
215+
u32_t clk_freq, dev_cfg;
216+
int err;
217+
218+
CLOCK_SetIpSrc(config->clock_ip_name, config->clock_ip_src);
219+
220+
clk = device_get_binding(config->clock_controller);
221+
if (!clk) {
222+
LOG_ERR("Could not get clock controller '%s'",
223+
config->clock_controller);
224+
return -EINVAL;
225+
}
226+
227+
err = clock_control_on(clk, config->clock_subsys);
228+
if (err) {
229+
LOG_ERR("Could not turn on clock (err %d)", err);
230+
return -EINVAL;
231+
}
232+
233+
err = clock_control_get_rate(clk, config->clock_subsys, &clk_freq);
234+
if (err) {
235+
LOG_ERR("Could not get clock frequency (err %d)", err);
236+
return -EINVAL;
237+
}
238+
239+
LPI2C_MasterGetDefaultConfig(&master_config);
240+
LPI2C_MasterInit(config->base, &master_config, clk_freq);
241+
LPI2C_MasterTransferCreateHandle(config->base, &data->handle,
242+
rv32m1_lpi2c_master_transfer_callback,
243+
dev);
244+
245+
dev_cfg = i2c_map_dt_bitrate(config->bitrate);
246+
err = rv32m1_lpi2c_configure(dev, dev_cfg | I2C_MODE_MASTER);
247+
if (err) {
248+
LOG_ERR("Could not configure controller (err %d)", err);
249+
return err;
250+
}
251+
252+
config->irq_config_func(dev);
253+
254+
return 0;
255+
}
256+
257+
static const struct i2c_driver_api rv32m1_lpi2c_driver_api = {
258+
.configure = rv32m1_lpi2c_configure,
259+
.transfer = rv32m1_lpi2c_transfer,
260+
};
261+
262+
#define RV32M1_LPI2C_DEVICE(id) \
263+
static void rv32m1_lpi2c_irq_config_func_##id(struct device *dev); \
264+
static const struct rv32m1_lpi2c_config rv32m1_lpi2c_##id##_config = { \
265+
.base = \
266+
(LPI2C_Type *)DT_OPENISA_RV32M1_LPI2C_I2C_##id##_BASE_ADDRESS, \
267+
.clock_controller = \
268+
DT_OPENISA_RV32M1_LPI2C_I2C_##id##_CLOCK_CONTROLLER, \
269+
.clock_subsys = \
270+
(clock_control_subsys_t) \
271+
DT_OPENISA_RV32M1_LPI2C_I2C_##id##_CLOCK_NAME, \
272+
.clock_ip_name = kCLOCK_Lpi2c##id, \
273+
.clock_ip_src = kCLOCK_IpSrcFircAsync, \
274+
.bitrate = DT_OPENISA_RV32M1_LPI2C_I2C_##id##_CLOCK_FREQUENCY, \
275+
.irq_config_func = rv32m1_lpi2c_irq_config_func_##id, \
276+
}; \
277+
static struct rv32m1_lpi2c_data rv32m1_lpi2c_##id##_data = { \
278+
.transfer_sync = Z_SEM_INITIALIZER( \
279+
rv32m1_lpi2c_##id##_data.transfer_sync, 1, 1), \
280+
.completion_sync = Z_SEM_INITIALIZER( \
281+
rv32m1_lpi2c_##id##_data.completion_sync, 0, 1), \
282+
}; \
283+
DEVICE_AND_API_INIT(rv32m1_lpi2c_##id, \
284+
DT_OPENISA_RV32M1_LPI2C_I2C_##id##_LABEL, \
285+
&rv32m1_lpi2c_init, \
286+
&rv32m1_lpi2c_##id##_data, \
287+
&rv32m1_lpi2c_##id##_config, \
288+
POST_KERNEL, CONFIG_I2C_INIT_PRIORITY, \
289+
&rv32m1_lpi2c_driver_api); \
290+
static void rv32m1_lpi2c_irq_config_func_##id(struct device *dev) \
291+
{ \
292+
IRQ_CONNECT(DT_OPENISA_RV32M1_LPI2C_I2C_##id##_IRQ, \
293+
DT_OPENISA_RV32M1_LPI2C_I2C_##id##_IRQ_PRI, \
294+
rv32m1_lpi2c_isr, DEVICE_GET(rv32m1_lpi2c_##id), \
295+
0); \
296+
irq_enable(DT_OPENISA_RV32M1_LPI2C_I2C_##id##_IRQ); \
297+
} \
298+
299+
#ifdef CONFIG_I2C_0
300+
RV32M1_LPI2C_DEVICE(0)
301+
#endif
302+
303+
#ifdef CONFIG_I2C_1
304+
RV32M1_LPI2C_DEVICE(1)
305+
#endif
306+
307+
#ifdef CONFIG_I2C_2
308+
RV32M1_LPI2C_DEVICE(2)
309+
#endif
310+
311+
#ifdef CONFIG_I2C_3
312+
RV32M1_LPI2C_DEVICE(3)
313+
#endif
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#
2+
# Copyright (c) 2019, Henrik Brix Andersen <[email protected]>
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
---
7+
title: OpenISA LPI2C
8+
version: 0.1
9+
10+
description: >
11+
This binding gives a base representation of the OpenISA LPI2C controller
12+
13+
inherits:
14+
!include i2c.yaml
15+
16+
properties:
17+
compatible:
18+
constraint: "openisa,rv32m1-lpi2c"
19+
20+
reg:
21+
type: int
22+
description: mmio register space
23+
generation: define
24+
category: required
25+
26+
interrupts:
27+
type: compound
28+
category: required
29+
description: required interrupts
30+
generation: define

0 commit comments

Comments
 (0)