drivers: i2c: stm32 LL F1/F4 (v1) STM32 F3/L4X (v2)
Supports STM32 F1/F4 (v1 controller) and STM32 F3/L4X (v2 controller) v1 could also support L1X. v2 could also support F7X. Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org> Signed-off-by: Yannis Damigos <giannis.damigos@gmail.com> Reviewed-by: Yannis Damigos <giannis.damigos@gmail.com> Tested-by: Yannis Damigos <giannis.damigos@gmail.com> Tested-by: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
This commit is contained in:
parent
220ddb9738
commit
21fbc9b07e
12 changed files with 1040 additions and 739 deletions
|
@ -14,5 +14,6 @@ config SOC_SERIES_STM32F1X
|
||||||
select HAS_STM32CUBE
|
select HAS_STM32CUBE
|
||||||
select CPU_HAS_SYSTICK
|
select CPU_HAS_SYSTICK
|
||||||
select CLOCK_CONTROL_STM32_CUBE if CLOCK_CONTROL
|
select CLOCK_CONTROL_STM32_CUBE if CLOCK_CONTROL
|
||||||
|
select I2C_STM32_V1 if I2C
|
||||||
help
|
help
|
||||||
Enable support for STM32F1 MCU series
|
Enable support for STM32F1 MCU series
|
||||||
|
|
|
@ -15,5 +15,6 @@ config SOC_SERIES_STM32F3X
|
||||||
select CPU_HAS_SYSTICK
|
select CPU_HAS_SYSTICK
|
||||||
select HAS_STM32CUBE
|
select HAS_STM32CUBE
|
||||||
select CLOCK_CONTROL_STM32_CUBE if CLOCK_CONTROL
|
select CLOCK_CONTROL_STM32_CUBE if CLOCK_CONTROL
|
||||||
|
select I2C_STM32_V2 if I2C
|
||||||
help
|
help
|
||||||
Enable support for STM32F3 MCU series
|
Enable support for STM32F3 MCU series
|
||||||
|
|
|
@ -16,5 +16,6 @@ config SOC_SERIES_STM32F4X
|
||||||
select CPU_HAS_MPU
|
select CPU_HAS_MPU
|
||||||
select CPU_HAS_SYSTICK
|
select CPU_HAS_SYSTICK
|
||||||
select CLOCK_CONTROL_STM32_CUBE if CLOCK_CONTROL
|
select CLOCK_CONTROL_STM32_CUBE if CLOCK_CONTROL
|
||||||
|
select I2C_STM32_V1 if I2C
|
||||||
help
|
help
|
||||||
Enable support for STM32F4 MCU series
|
Enable support for STM32F4 MCU series
|
||||||
|
|
|
@ -15,5 +15,6 @@ config SOC_SERIES_STM32L4X
|
||||||
select CPU_HAS_MPU
|
select CPU_HAS_MPU
|
||||||
select CPU_HAS_SYSTICK
|
select CPU_HAS_SYSTICK
|
||||||
select CLOCK_CONTROL_STM32_CUBE if CLOCK_CONTROL
|
select CLOCK_CONTROL_STM32_CUBE if CLOCK_CONTROL
|
||||||
|
select I2C_STM32_V2 if I2C
|
||||||
help
|
help
|
||||||
Enable support for STM32L4 MCU series
|
Enable support for STM32L4 MCU series
|
||||||
|
|
|
@ -90,19 +90,30 @@ config I2C_NRF5_GPIO_SCL_PIN
|
||||||
help
|
help
|
||||||
The GPIO pin to use for SCL.
|
The GPIO pin to use for SCL.
|
||||||
|
|
||||||
config I2C_STM32LX
|
config I2C_STM32_V1
|
||||||
bool "STM32Lx MCU I2C Driver"
|
bool "STM32 V1 Driver (F1/F4X)"
|
||||||
depends on SOC_FAMILY_STM32 && SOC_SERIES_STM32L4X
|
depends on SOC_FAMILY_STM32
|
||||||
|
depends on SOC_SERIES_STM32F1X || SOC_SERIES_STM32F4X
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
Enable I2C support on the STM32Lxx family of processors.
|
Enable I2C support on the STM32 F1 and F4X family of processors. This
|
||||||
|
driver also supports the F2 and L1 series.
|
||||||
|
|
||||||
config I2C_STM32LX_INTERRUPT
|
config I2C_STM32_V2
|
||||||
bool "STM32Lx MCU I2C Interrupt Support"
|
bool "STM32 V2 Driver (F3/L4X)"
|
||||||
depends on I2C_STM32LX
|
depends on SOC_FAMILY_STM32
|
||||||
|
depends on SOC_SERIES_STM32F3X || SOC_SERIES_STM32L4X
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
Enable Interrupt support for the I2C Driver of STM32Lxx family.
|
Enable I2C support on the STM32 F3 and L4X family of processors. This
|
||||||
|
driver also supports the F0, F7 and L0 series.
|
||||||
|
|
||||||
|
config I2C_STM32_INTERRUPT
|
||||||
|
bool "STM32 MCU I2C Interrupt Support"
|
||||||
|
depends on I2C_STM32_V1 || I2C_STM32_V2
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Enable Interrupt support for the I2C Driver
|
||||||
|
|
||||||
config I2C_BITBANG
|
config I2C_BITBANG
|
||||||
bool
|
bool
|
||||||
|
@ -252,6 +263,32 @@ config I2C_2_IRQ_PRI
|
||||||
help
|
help
|
||||||
IRQ priority.
|
IRQ priority.
|
||||||
|
|
||||||
|
config I2C_3
|
||||||
|
bool "Enable I2C Port 3"
|
||||||
|
default n
|
||||||
|
|
||||||
|
config I2C_3_NAME
|
||||||
|
string "Port 3 device name"
|
||||||
|
depends on I2C_3
|
||||||
|
default "I2C_3"
|
||||||
|
|
||||||
|
config I2C_3_DEFAULT_CFG
|
||||||
|
hex "Port 3 default configuration"
|
||||||
|
depends on I2C_3
|
||||||
|
default 0x0
|
||||||
|
help
|
||||||
|
Allows the I2C port to be brought up with a default configuration.
|
||||||
|
This is useful to set if other drivers depend upon using the I2C bus
|
||||||
|
before the application has a chance to custom configure the port.
|
||||||
|
Setting this value does not prohibit the application from customizing
|
||||||
|
the values later. Refer to include/i2c.h file for proper values.
|
||||||
|
|
||||||
|
config I2C_3_IRQ_PRI
|
||||||
|
int "Port 3 interrupt priority"
|
||||||
|
depends on I2C_3
|
||||||
|
help
|
||||||
|
IRQ priority.
|
||||||
|
|
||||||
config I2C_SS_0
|
config I2C_SS_0
|
||||||
bool "Enable I2C_SS_0"
|
bool "Enable I2C_SS_0"
|
||||||
depends on I2C_QMSI_SS
|
depends on I2C_QMSI_SS
|
||||||
|
|
|
@ -7,5 +7,6 @@ obj-$(CONFIG_I2C_NRF5) += i2c_nrf5.o
|
||||||
obj-$(CONFIG_I2C_QMSI) += i2c_qmsi.o
|
obj-$(CONFIG_I2C_QMSI) += i2c_qmsi.o
|
||||||
obj-$(CONFIG_I2C_QMSI_SS) += i2c_qmsi_ss.o
|
obj-$(CONFIG_I2C_QMSI_SS) += i2c_qmsi_ss.o
|
||||||
obj-$(CONFIG_I2C_SBCON) += i2c_sbcon.o
|
obj-$(CONFIG_I2C_SBCON) += i2c_sbcon.o
|
||||||
obj-$(CONFIG_I2C_STM32LX) += i2c_stm32lx.o
|
obj-$(CONFIG_I2C_STM32_V1) += i2c_ll_stm32_v1.o i2c_ll_stm32.o
|
||||||
|
obj-$(CONFIG_I2C_STM32_V2) += i2c_ll_stm32_v2.o i2c_ll_stm32.o
|
||||||
obj-$(CONFIG_TWIHS_SAM) += twihs_sam.o
|
obj-$(CONFIG_TWIHS_SAM) += twihs_sam.o
|
||||||
|
|
241
drivers/i2c/i2c_ll_stm32.c
Normal file
241
drivers/i2c/i2c_ll_stm32.c
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 BayLibre, SAS
|
||||||
|
* Copyright (c) 2017 Linaro Ltd
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <clock_control/stm32_clock_control.h>
|
||||||
|
#include <clock_control.h>
|
||||||
|
#include <misc/util.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <board.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <i2c.h>
|
||||||
|
#include "i2c_ll_stm32.h"
|
||||||
|
|
||||||
|
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_I2C_LEVEL
|
||||||
|
#include <logging/sys_log.h>
|
||||||
|
|
||||||
|
static int i2c_stm32_runtime_configure(struct device *dev, u32_t config)
|
||||||
|
{
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG(dev);
|
||||||
|
struct i2c_stm32_data *data = DEV_DATA(dev);
|
||||||
|
I2C_TypeDef *i2c = cfg->i2c;
|
||||||
|
u32_t clock;
|
||||||
|
|
||||||
|
if (data->dev_config.bits.is_slave_read)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
data->dev_config.raw = config;
|
||||||
|
|
||||||
|
clock_control_get_rate(device_get_binding(STM32_CLOCK_CONTROL_NAME),
|
||||||
|
(clock_control_subsys_t *) &cfg->pclken, &clock);
|
||||||
|
|
||||||
|
LL_I2C_Disable(i2c);
|
||||||
|
LL_I2C_SetMode(i2c, LL_I2C_MODE_I2C);
|
||||||
|
|
||||||
|
return stm32_i2c_configure_timing(dev, clock);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define OPERATION(msg) (((struct i2c_msg *) msg)->flags & I2C_MSG_RW_MASK)
|
||||||
|
|
||||||
|
static int i2c_stm32_transfer(struct device *dev, struct i2c_msg *msg,
|
||||||
|
u8_t num_msgs, u16_t slave)
|
||||||
|
{
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG(dev);
|
||||||
|
struct i2c_msg *current, *next;
|
||||||
|
I2C_TypeDef *i2c = cfg->i2c;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
LL_I2C_Enable(i2c);
|
||||||
|
|
||||||
|
current = msg;
|
||||||
|
while (num_msgs > 0) {
|
||||||
|
unsigned int flags = 0;
|
||||||
|
|
||||||
|
if (current->len > 255)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* do NOT issue the i2c stop condition at the end of transfer */
|
||||||
|
if (num_msgs > 1) {
|
||||||
|
next = current + 1;
|
||||||
|
if (OPERATION(current) != OPERATION(next)) {
|
||||||
|
flags = I2C_MSG_RESTART;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((current->flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) {
|
||||||
|
ret = stm32_i2c_msg_write(dev, current, flags, slave);
|
||||||
|
} else {
|
||||||
|
ret = stm32_i2c_msg_read(dev, current, flags, slave);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
current++;
|
||||||
|
num_msgs--;
|
||||||
|
};
|
||||||
|
|
||||||
|
LL_I2C_Disable(i2c);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_driver_api api_funcs = {
|
||||||
|
.configure = i2c_stm32_runtime_configure,
|
||||||
|
.transfer = i2c_stm32_transfer,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int i2c_stm32_init(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device *clock = device_get_binding(STM32_CLOCK_CONTROL_NAME);
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG(dev);
|
||||||
|
struct i2c_stm32_data *data = DEV_DATA(dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(clock);
|
||||||
|
clock_control_on(clock, (clock_control_subsys_t *) &cfg->pclken);
|
||||||
|
|
||||||
|
ret = i2c_stm32_runtime_configure(dev, data->dev_config.raw);
|
||||||
|
if (ret < 0) {
|
||||||
|
SYS_LOG_ERR("i2c: failure initializing");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#ifdef CONFIG_I2C_STM32_INTERRUPT
|
||||||
|
k_sem_init(&data->device_sync_sem, 0, UINT_MAX);
|
||||||
|
cfg->irq_config_func(dev);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_I2C_1
|
||||||
|
|
||||||
|
#ifdef CONFIG_I2C_STM32_INTERRUPT
|
||||||
|
static void i2c_stm32_irq_config_func_1(struct device *port);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct i2c_stm32_config i2c_stm32_cfg_1 = {
|
||||||
|
.i2c = (I2C_TypeDef *) I2C1_BASE,
|
||||||
|
.pclken = {
|
||||||
|
.enr = LL_APB1_GRP1_PERIPH_I2C1,
|
||||||
|
.bus = STM32_CLOCK_BUS_APB1,
|
||||||
|
},
|
||||||
|
#ifdef CONFIG_I2C_STM32_INTERRUPT
|
||||||
|
.irq_config_func = i2c_stm32_irq_config_func_1,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct i2c_stm32_data i2c_stm32_dev_data_1 = {
|
||||||
|
.dev_config.raw = CONFIG_I2C_1_DEFAULT_CFG,
|
||||||
|
};
|
||||||
|
|
||||||
|
DEVICE_AND_API_INIT(i2c_stm32_1, CONFIG_I2C_1_NAME, &i2c_stm32_init,
|
||||||
|
&i2c_stm32_dev_data_1, &i2c_stm32_cfg_1,
|
||||||
|
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
||||||
|
&api_funcs);
|
||||||
|
|
||||||
|
#ifdef CONFIG_I2C_STM32_INTERRUPT
|
||||||
|
static void i2c_stm32_irq_config_func_1(struct device *dev)
|
||||||
|
{
|
||||||
|
IRQ_CONNECT(I2C1_EV_IRQn, CONFIG_I2C_1_IRQ_PRI,
|
||||||
|
stm32_i2c_event_isr, DEVICE_GET(i2c_stm32_1), 0);
|
||||||
|
irq_enable(I2C1_EV_IRQn);
|
||||||
|
|
||||||
|
IRQ_CONNECT(I2C1_ER_IRQn, CONFIG_I2C_1_IRQ_PRI,
|
||||||
|
stm32_i2c_error_isr, DEVICE_GET(i2c_stm32_1), 0);
|
||||||
|
irq_enable(I2C1_ER_IRQn);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* CONFIG_I2C_1 */
|
||||||
|
|
||||||
|
#ifdef CONFIG_I2C_2
|
||||||
|
|
||||||
|
#ifdef CONFIG_I2C_STM32_INTERRUPT
|
||||||
|
static void i2c_stm32_irq_config_func_2(struct device *port);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct i2c_stm32_config i2c_stm32_cfg_2 = {
|
||||||
|
.i2c = (I2C_TypeDef *) I2C2_BASE,
|
||||||
|
.pclken = {
|
||||||
|
.enr = LL_APB1_GRP1_PERIPH_I2C2,
|
||||||
|
.bus = STM32_CLOCK_BUS_APB1,
|
||||||
|
},
|
||||||
|
#ifdef CONFIG_I2C_STM32_INTERRUPT
|
||||||
|
.irq_config_func = i2c_stm32_irq_config_func_2,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct i2c_stm32_data i2c_stm32_dev_data_2 = {
|
||||||
|
.dev_config.raw = CONFIG_I2C_2_DEFAULT_CFG,
|
||||||
|
};
|
||||||
|
|
||||||
|
DEVICE_AND_API_INIT(i2c_stm32_2, CONFIG_I2C_2_NAME, &i2c_stm32_init,
|
||||||
|
&i2c_stm32_dev_data_2, &i2c_stm32_cfg_2,
|
||||||
|
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
||||||
|
&api_funcs);
|
||||||
|
|
||||||
|
#ifdef CONFIG_I2C_STM32_INTERRUPT
|
||||||
|
static void i2c_stm32_irq_config_func_2(struct device *dev)
|
||||||
|
{
|
||||||
|
IRQ_CONNECT(I2C2_EV_IRQn, CONFIG_I2C_2_IRQ_PRI,
|
||||||
|
stm32_i2c_event_isr, DEVICE_GET(i2c_stm32_2), 0);
|
||||||
|
irq_enable(I2C2_EV_IRQn);
|
||||||
|
|
||||||
|
IRQ_CONNECT(I2C2_ER_IRQn, CONFIG_I2C_2_IRQ_PRI,
|
||||||
|
stm32_i2c_error_isr, DEVICE_GET(i2c_stm32_2), 0);
|
||||||
|
irq_enable(I2C2_ER_IRQn);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* CONFIG_I2C_2 */
|
||||||
|
|
||||||
|
#ifdef CONFIG_I2C_3
|
||||||
|
|
||||||
|
#ifndef I2C3_BASE
|
||||||
|
#error "I2C_3 is not available on the platform that you selected"
|
||||||
|
#endif /* I2C3_BASE */
|
||||||
|
|
||||||
|
#ifdef CONFIG_I2C_STM32_INTERRUPT
|
||||||
|
static void i2c_stm32_irq_config_func_3(struct device *port);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct i2c_stm32_config i2c_stm32_cfg_3 = {
|
||||||
|
.i2c = (I2C_TypeDef *) I2C3_BASE,
|
||||||
|
.pclken = {
|
||||||
|
.enr = LL_APB1_GRP1_PERIPH_I2C3,
|
||||||
|
.bus = STM32_CLOCK_BUS_APB1,
|
||||||
|
},
|
||||||
|
#ifdef CONFIG_I2C_STM32_INTERRUPT
|
||||||
|
.irq_config_func = i2c_stm32_irq_config_func_3,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct i2c_stm32_data i2c_stm32_dev_data_3 = {
|
||||||
|
.dev_config.raw = CONFIG_I2C_3_DEFAULT_CFG,
|
||||||
|
};
|
||||||
|
|
||||||
|
DEVICE_AND_API_INIT(i2c_stm32_3, CONFIG_I2C_3_NAME, &i2c_stm32_init,
|
||||||
|
&i2c_stm32_dev_data_3, &i2c_stm32_cfg_3,
|
||||||
|
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
||||||
|
&api_funcs);
|
||||||
|
|
||||||
|
#ifdef CONFIG_I2C_STM32_INTERRUPT
|
||||||
|
static void i2c_stm32_irq_config_func_3(struct device *dev)
|
||||||
|
{
|
||||||
|
IRQ_CONNECT(I2C3_EV_IRQn, CONFIG_I2C_3_IRQ_PRI,
|
||||||
|
stm32_i2c_event_isr, DEVICE_GET(i2c_stm32_3), 0);
|
||||||
|
irq_enable(I2C3_EV_IRQn);
|
||||||
|
|
||||||
|
IRQ_CONNECT(I2C3_ER_IRQn, CONFIG_I2C_3_IRQ_PRI,
|
||||||
|
stm32_i2c_error_isr, DEVICE_GET(i2c_stm32_3), 0);
|
||||||
|
irq_enable(I2C3_ER_IRQn);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* CONFIG_I2C_3 */
|
||||||
|
|
||||||
|
|
57
drivers/i2c/i2c_ll_stm32.h
Normal file
57
drivers/i2c/i2c_ll_stm32.h
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 BayLibre, SAS
|
||||||
|
* Copyright (c) 2017 Linaro Ltd
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _STM32_I2C_H_
|
||||||
|
#define _STM32_I2C_H_
|
||||||
|
|
||||||
|
typedef void (*irq_config_func_t)(struct device *port);
|
||||||
|
|
||||||
|
struct i2c_stm32_config {
|
||||||
|
#ifdef CONFIG_I2C_STM32_INTERRUPT
|
||||||
|
irq_config_func_t irq_config_func;
|
||||||
|
#endif
|
||||||
|
struct stm32_pclken pclken;
|
||||||
|
I2C_TypeDef *i2c;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct i2c_stm32_data {
|
||||||
|
#ifdef CONFIG_I2C_STM32_INTERRUPT
|
||||||
|
struct k_sem device_sync_sem;
|
||||||
|
#endif
|
||||||
|
union dev_config dev_config;
|
||||||
|
#ifdef CONFIG_I2C_STM32_V1
|
||||||
|
u16_t slave_address;
|
||||||
|
#endif
|
||||||
|
struct {
|
||||||
|
#ifdef CONFIG_I2C_STM32_V1
|
||||||
|
unsigned int is_restart;
|
||||||
|
unsigned int flags;
|
||||||
|
#endif
|
||||||
|
unsigned int is_write;
|
||||||
|
unsigned int is_nack;
|
||||||
|
unsigned int is_err;
|
||||||
|
struct i2c_msg *msg;
|
||||||
|
unsigned int len;
|
||||||
|
u8_t *buf;
|
||||||
|
} current;
|
||||||
|
};
|
||||||
|
|
||||||
|
s32_t stm32_i2c_msg_write(struct device *dev, struct i2c_msg *msg, u32_t flg,
|
||||||
|
u16_t sadr);
|
||||||
|
s32_t stm32_i2c_msg_read(struct device *dev, struct i2c_msg *msg, u32_t flg,
|
||||||
|
u16_t sadr);
|
||||||
|
s32_t stm32_i2c_configure_timing(struct device *dev, u32_t clk);
|
||||||
|
|
||||||
|
void stm32_i2c_event_isr(void *arg);
|
||||||
|
void stm32_i2c_error_isr(void *arg);
|
||||||
|
|
||||||
|
#define DEV_DATA(dev) ((struct i2c_stm32_data * const)(dev)->driver_data)
|
||||||
|
#define DEV_CFG(dev) \
|
||||||
|
((const struct i2c_stm32_config * const)(dev)->config->config_info)
|
||||||
|
|
||||||
|
#endif /* _STM32_I2C_H_ */
|
375
drivers/i2c/i2c_ll_stm32_v1.c
Normal file
375
drivers/i2c/i2c_ll_stm32_v1.c
Normal file
|
@ -0,0 +1,375 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017, I-SENSE group of ICCS
|
||||||
|
* Copyright (c) 2017 Linaro Ltd
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* I2C Driver for: STM32F1, STM32F2, STM32F4 and STM32L1
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <clock_control/stm32_clock_control.h>
|
||||||
|
#include <clock_control.h>
|
||||||
|
#include <misc/util.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <board.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <i2c.h>
|
||||||
|
#include "i2c_ll_stm32.h"
|
||||||
|
|
||||||
|
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_I2C_LEVEL
|
||||||
|
#include <logging/sys_log.h>
|
||||||
|
|
||||||
|
#define I2C_REQUEST_WRITE 0x00
|
||||||
|
#define I2C_REQUEST_READ 0x01
|
||||||
|
#define HEADER 0xF0
|
||||||
|
|
||||||
|
#ifdef CONFIG_I2C_STM32_INTERRUPT
|
||||||
|
static inline void handle_sb(I2C_TypeDef *i2c, struct i2c_stm32_data *data)
|
||||||
|
{
|
||||||
|
u16_t saddr = data->slave_address;
|
||||||
|
u8_t slave;
|
||||||
|
|
||||||
|
if (data->dev_config.bits.use_10_bit_addr) {
|
||||||
|
slave = (((saddr & 0x0300) >> 7) & 0xFF);
|
||||||
|
u8_t header = slave | HEADER;
|
||||||
|
|
||||||
|
if (data->current.is_restart == 0) {
|
||||||
|
data->current.is_restart = 1;
|
||||||
|
} else {
|
||||||
|
header |= I2C_REQUEST_READ;
|
||||||
|
data->current.is_restart = 0;
|
||||||
|
}
|
||||||
|
LL_I2C_TransmitData8(i2c, header);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
slave = (saddr << 1) & 0xFF;
|
||||||
|
if (data->current.is_write) {
|
||||||
|
LL_I2C_TransmitData8(i2c, slave | I2C_REQUEST_WRITE);
|
||||||
|
} else {
|
||||||
|
LL_I2C_TransmitData8(i2c, slave | I2C_REQUEST_READ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void handle_addr(I2C_TypeDef *i2c, struct i2c_stm32_data *data)
|
||||||
|
{
|
||||||
|
if (data->dev_config.bits.use_10_bit_addr) {
|
||||||
|
if (!data->current.is_write && data->current.is_restart) {
|
||||||
|
data->current.is_restart = 0;
|
||||||
|
LL_I2C_ClearFlag_ADDR(i2c);
|
||||||
|
LL_I2C_GenerateStartCondition(i2c);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!data->current.is_write && data->current.len == 1) {
|
||||||
|
LL_I2C_AcknowledgeNextData(i2c, LL_I2C_ACK);
|
||||||
|
}
|
||||||
|
LL_I2C_ClearFlag_ADDR(i2c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void handle_txe(I2C_TypeDef *i2c, struct i2c_stm32_data *data)
|
||||||
|
{
|
||||||
|
if (data->current.len) {
|
||||||
|
LL_I2C_TransmitData8(i2c, *data->current.buf);
|
||||||
|
data->current.buf++;
|
||||||
|
data->current.len--;
|
||||||
|
} else if (LL_I2C_IsActiveFlag_BTF(i2c) && !data->current.len) {
|
||||||
|
if ((data->current.flags & I2C_MSG_RESTART) == 0) {
|
||||||
|
LL_I2C_GenerateStopCondition(i2c);
|
||||||
|
}
|
||||||
|
k_sem_give(&data->device_sync_sem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void handle_rxne(I2C_TypeDef *i2c, struct i2c_stm32_data *data)
|
||||||
|
{
|
||||||
|
if (data->current.len) {
|
||||||
|
*data->current.buf = LL_I2C_ReceiveData8(i2c);
|
||||||
|
data->current.buf++;
|
||||||
|
data->current.len--;
|
||||||
|
if (data->current.len == 1) {
|
||||||
|
LL_I2C_AcknowledgeNextData(i2c, LL_I2C_NACK);
|
||||||
|
if ((data->current.flags & I2C_MSG_RESTART) == 0) {
|
||||||
|
LL_I2C_GenerateStopCondition(i2c);
|
||||||
|
}
|
||||||
|
k_sem_give(&data->device_sync_sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data->current.is_err = 1;
|
||||||
|
k_sem_give(&data->device_sync_sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stm32_i2c_event_isr(void *arg)
|
||||||
|
{
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG((struct device *)arg);
|
||||||
|
struct i2c_stm32_data *data = DEV_DATA((struct device *)arg);
|
||||||
|
I2C_TypeDef *i2c = cfg->i2c;
|
||||||
|
|
||||||
|
if (LL_I2C_IsActiveFlag_SB(i2c)) {
|
||||||
|
handle_sb(i2c, data);
|
||||||
|
} else if (LL_I2C_IsActiveFlag_ADD10(i2c)) {
|
||||||
|
LL_I2C_TransmitData8(i2c, data->slave_address);
|
||||||
|
} else if (LL_I2C_IsActiveFlag_ADDR(i2c)) {
|
||||||
|
handle_addr(i2c, data);
|
||||||
|
} else if (LL_I2C_IsActiveFlag_TXE(i2c)) {
|
||||||
|
handle_txe(i2c, data);
|
||||||
|
} else if (LL_I2C_IsActiveFlag_RXNE(i2c)) {
|
||||||
|
handle_rxne(i2c, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stm32_i2c_error_isr(void *arg)
|
||||||
|
{
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG((struct device *)arg);
|
||||||
|
struct i2c_stm32_data *data = DEV_DATA((struct device *)arg);
|
||||||
|
I2C_TypeDef *i2c = cfg->i2c;
|
||||||
|
|
||||||
|
if (LL_I2C_IsActiveFlag_AF(i2c)) {
|
||||||
|
LL_I2C_ClearFlag_AF(i2c);
|
||||||
|
data->current.is_nack = 1;
|
||||||
|
k_sem_give(&data->device_sync_sem);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data->current.is_err = 1;
|
||||||
|
k_sem_give(&data->device_sync_sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
s32_t stm32_i2c_msg_write(struct device *dev, struct i2c_msg *msg,
|
||||||
|
u32_t flags, u16_t saddr)
|
||||||
|
{
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG(dev);
|
||||||
|
struct i2c_stm32_data *data = DEV_DATA(dev);
|
||||||
|
I2C_TypeDef *i2c = cfg->i2c;
|
||||||
|
s32_t ret = 0;
|
||||||
|
|
||||||
|
data->current.len = msg->len;
|
||||||
|
data->current.buf = msg->buf;
|
||||||
|
data->current.flags = flags;
|
||||||
|
data->current.is_restart = 0;
|
||||||
|
data->current.is_write = 1;
|
||||||
|
data->current.is_nack = 0;
|
||||||
|
data->current.is_err = 0;
|
||||||
|
data->slave_address = saddr;
|
||||||
|
|
||||||
|
LL_I2C_EnableIT_EVT(i2c);
|
||||||
|
LL_I2C_EnableIT_ERR(i2c);
|
||||||
|
LL_I2C_AcknowledgeNextData(i2c, LL_I2C_ACK);
|
||||||
|
LL_I2C_GenerateStartCondition(i2c);
|
||||||
|
LL_I2C_EnableIT_TX(i2c);
|
||||||
|
LL_I2C_EnableIT_ERR(i2c);
|
||||||
|
k_sem_take(&data->device_sync_sem, K_FOREVER);
|
||||||
|
|
||||||
|
if (data->current.is_nack || data->current.is_err) {
|
||||||
|
LL_I2C_DisableIT_TX(i2c);
|
||||||
|
LL_I2C_DisableIT_ERR(i2c);
|
||||||
|
|
||||||
|
if (data->current.is_nack)
|
||||||
|
SYS_LOG_DBG("%s: NACK", __func__);
|
||||||
|
|
||||||
|
if (data->current.is_err)
|
||||||
|
SYS_LOG_DBG("%s: ERR %d", __func__,
|
||||||
|
data->current.is_err);
|
||||||
|
|
||||||
|
data->current.is_nack = 0;
|
||||||
|
data->current.is_err = 0;
|
||||||
|
ret = -EIO;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
LL_I2C_DisableIT_TX(i2c);
|
||||||
|
LL_I2C_DisableIT_ERR(i2c);
|
||||||
|
error:
|
||||||
|
LL_I2C_DisableIT_EVT(i2c);
|
||||||
|
LL_I2C_DisableIT_ERR(i2c);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32_t stm32_i2c_msg_read(struct device *dev, struct i2c_msg *msg,
|
||||||
|
u32_t flags, u16_t saddr)
|
||||||
|
{
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG(dev);
|
||||||
|
struct i2c_stm32_data *data = DEV_DATA(dev);
|
||||||
|
I2C_TypeDef *i2c = cfg->i2c;
|
||||||
|
s32_t ret = 0;
|
||||||
|
|
||||||
|
data->current.len = msg->len;
|
||||||
|
data->current.buf = msg->buf;
|
||||||
|
data->current.flags = flags;
|
||||||
|
data->current.is_restart = 0;
|
||||||
|
data->current.is_write = 0;
|
||||||
|
data->current.is_err = 0;
|
||||||
|
data->slave_address = saddr;
|
||||||
|
|
||||||
|
LL_I2C_EnableIT_EVT(i2c);
|
||||||
|
LL_I2C_EnableIT_ERR(i2c);
|
||||||
|
LL_I2C_AcknowledgeNextData(i2c, LL_I2C_ACK);
|
||||||
|
LL_I2C_GenerateStartCondition(i2c);
|
||||||
|
LL_I2C_EnableIT_RX(i2c);
|
||||||
|
k_sem_take(&data->device_sync_sem, K_FOREVER);
|
||||||
|
|
||||||
|
if (data->current.is_err) {
|
||||||
|
LL_I2C_DisableIT_RX(i2c);
|
||||||
|
SYS_LOG_DBG("%s: ERR %d", __func__, data->current.is_err);
|
||||||
|
data->current.is_err = 0;
|
||||||
|
ret = -EIO;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
LL_I2C_DisableIT_RX(i2c);
|
||||||
|
error:
|
||||||
|
LL_I2C_DisableIT_EVT(i2c);
|
||||||
|
LL_I2C_DisableIT_ERR(i2c);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
s32_t stm32_i2c_msg_write(struct device *dev, struct i2c_msg *msg,
|
||||||
|
u32_t flags, u16_t saddr)
|
||||||
|
{
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG(dev);
|
||||||
|
struct i2c_stm32_data *data = DEV_DATA(dev);
|
||||||
|
I2C_TypeDef *i2c = cfg->i2c;
|
||||||
|
u32_t len = msg->len;
|
||||||
|
u8_t *buf = msg->buf;
|
||||||
|
|
||||||
|
LL_I2C_AcknowledgeNextData(i2c, LL_I2C_ACK);
|
||||||
|
LL_I2C_GenerateStartCondition(i2c);
|
||||||
|
while (!LL_I2C_IsActiveFlag_SB(i2c)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
if (data->dev_config.bits.use_10_bit_addr) {
|
||||||
|
u8_t slave = (((saddr & 0x0300) >> 7) & 0xFF);
|
||||||
|
u8_t header = slave | HEADER;
|
||||||
|
|
||||||
|
LL_I2C_TransmitData8(i2c, header);
|
||||||
|
while (!LL_I2C_IsActiveFlag_ADD10(i2c)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
slave = data->slave_address & 0xFF;
|
||||||
|
LL_I2C_TransmitData8(i2c, slave);
|
||||||
|
} else {
|
||||||
|
u8_t slave = (saddr << 1) & 0xFF;
|
||||||
|
|
||||||
|
LL_I2C_TransmitData8(i2c, slave | I2C_REQUEST_WRITE);
|
||||||
|
}
|
||||||
|
while (!LL_I2C_IsActiveFlag_ADDR(i2c)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
LL_I2C_ClearFlag_ADDR(i2c);
|
||||||
|
while (len) {
|
||||||
|
while (1) {
|
||||||
|
if (LL_I2C_IsActiveFlag_TXE(i2c)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (LL_I2C_IsActiveFlag_AF(i2c)) {
|
||||||
|
LL_I2C_ClearFlag_AF(i2c);
|
||||||
|
SYS_LOG_DBG("%s: NACK", __func__);
|
||||||
|
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
LL_I2C_TransmitData8(i2c, *buf);
|
||||||
|
buf++;
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
while (!LL_I2C_IsActiveFlag_BTF(i2c)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
if ((flags & I2C_MSG_RESTART) == 0) {
|
||||||
|
LL_I2C_GenerateStopCondition(i2c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32_t stm32_i2c_msg_read(struct device *dev, struct i2c_msg *msg,
|
||||||
|
u32_t flags, u16_t saddr)
|
||||||
|
{
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG(dev);
|
||||||
|
struct i2c_stm32_data *data = DEV_DATA(dev);
|
||||||
|
I2C_TypeDef *i2c = cfg->i2c;
|
||||||
|
u32_t len = msg->len;
|
||||||
|
u8_t *buf = msg->buf;
|
||||||
|
|
||||||
|
LL_I2C_AcknowledgeNextData(i2c, LL_I2C_ACK);
|
||||||
|
LL_I2C_GenerateStartCondition(i2c);
|
||||||
|
while (!LL_I2C_IsActiveFlag_SB(i2c)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
if (data->dev_config.bits.use_10_bit_addr) {
|
||||||
|
u8_t slave = (((saddr & 0x0300) >> 7) & 0xFF);
|
||||||
|
u8_t header = slave | HEADER;
|
||||||
|
|
||||||
|
LL_I2C_TransmitData8(i2c, header);
|
||||||
|
while (!LL_I2C_IsActiveFlag_ADD10(i2c)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
slave = saddr & 0xFF;
|
||||||
|
LL_I2C_TransmitData8(i2c, slave);
|
||||||
|
while (!LL_I2C_IsActiveFlag_ADDR(i2c)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
LL_I2C_ClearFlag_ADDR(i2c);
|
||||||
|
LL_I2C_GenerateStartCondition(i2c);
|
||||||
|
while (!LL_I2C_IsActiveFlag_SB(i2c)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
header |= I2C_REQUEST_READ;
|
||||||
|
LL_I2C_TransmitData8(i2c, header);
|
||||||
|
} else {
|
||||||
|
u8_t slave = ((saddr) << 1) & 0xFF;
|
||||||
|
|
||||||
|
LL_I2C_TransmitData8(i2c, slave | I2C_REQUEST_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!LL_I2C_IsActiveFlag_ADDR(i2c)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
if (len == 1) {
|
||||||
|
LL_I2C_AcknowledgeNextData(i2c, LL_I2C_ACK);
|
||||||
|
}
|
||||||
|
LL_I2C_ClearFlag_ADDR(i2c);
|
||||||
|
while (len) {
|
||||||
|
while (!LL_I2C_IsActiveFlag_RXNE(i2c)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
*buf = LL_I2C_ReceiveData8(i2c);
|
||||||
|
buf++;
|
||||||
|
len--;
|
||||||
|
if (len == 1) {
|
||||||
|
LL_I2C_AcknowledgeNextData(i2c, LL_I2C_NACK);
|
||||||
|
if ((flags & I2C_MSG_RESTART) == 0) {
|
||||||
|
LL_I2C_GenerateStopCondition(i2c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
s32_t stm32_i2c_configure_timing(struct device *dev, u32_t clock)
|
||||||
|
{
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG(dev);
|
||||||
|
struct i2c_stm32_data *data = DEV_DATA(dev);
|
||||||
|
I2C_TypeDef *i2c = cfg->i2c;
|
||||||
|
|
||||||
|
switch (data->dev_config.bits.speed) {
|
||||||
|
case I2C_SPEED_STANDARD:
|
||||||
|
LL_I2C_ConfigSpeed(i2c, clock, 100000, LL_I2C_DUTYCYCLE_2);
|
||||||
|
break;
|
||||||
|
case I2C_SPEED_FAST:
|
||||||
|
LL_I2C_ConfigSpeed(i2c, clock, 400000, LL_I2C_DUTYCYCLE_2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
316
drivers/i2c/i2c_ll_stm32_v2.c
Normal file
316
drivers/i2c/i2c_ll_stm32_v2.c
Normal file
|
@ -0,0 +1,316 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 BayLibre, SAS
|
||||||
|
* Copyright (c) 2017 Linaro Ltd
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* I2C Driver for: STM32F0, STM32F3, STM32F7, STM32L0 and STM32L4
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <clock_control/stm32_clock_control.h>
|
||||||
|
#include <clock_control.h>
|
||||||
|
#include <misc/util.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <board.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <i2c.h>
|
||||||
|
#include "i2c_ll_stm32.h"
|
||||||
|
|
||||||
|
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_I2C_LEVEL
|
||||||
|
#include <logging/sys_log.h>
|
||||||
|
|
||||||
|
static inline void msg_init(struct device *dev, struct i2c_msg *msg,
|
||||||
|
unsigned int flags, u16_t slave, uint32_t transfer)
|
||||||
|
{
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG(dev);
|
||||||
|
struct i2c_stm32_data *data = DEV_DATA(dev);
|
||||||
|
I2C_TypeDef *i2c = cfg->i2c;
|
||||||
|
unsigned int len = msg->len;
|
||||||
|
|
||||||
|
if (data->dev_config.bits.use_10_bit_addr) {
|
||||||
|
LL_I2C_SetMasterAddressingMode(i2c,
|
||||||
|
LL_I2C_ADDRESSING_MODE_10BIT);
|
||||||
|
LL_I2C_SetSlaveAddr(i2c, (uint32_t) slave);
|
||||||
|
} else {
|
||||||
|
LL_I2C_SetMasterAddressingMode(i2c,
|
||||||
|
LL_I2C_ADDRESSING_MODE_7BIT);
|
||||||
|
LL_I2C_SetSlaveAddr(i2c, (uint32_t) slave << 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
LL_I2C_SetTransferRequest(i2c, transfer);
|
||||||
|
LL_I2C_SetTransferSize(i2c, len);
|
||||||
|
|
||||||
|
if ((flags & I2C_MSG_RESTART) == I2C_MSG_RESTART) {
|
||||||
|
LL_I2C_DisableAutoEndMode(i2c);
|
||||||
|
} else {
|
||||||
|
LL_I2C_EnableAutoEndMode(i2c);
|
||||||
|
}
|
||||||
|
|
||||||
|
LL_I2C_DisableReloadMode(i2c);
|
||||||
|
LL_I2C_GenerateStartCondition(i2c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void msg_done(struct device *dev, unsigned int flags)
|
||||||
|
{
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG(dev);
|
||||||
|
I2C_TypeDef *i2c = cfg->i2c;
|
||||||
|
|
||||||
|
if ((flags & I2C_MSG_RESTART) == 0) {
|
||||||
|
while (!LL_I2C_IsActiveFlag_STOP(i2c)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
LL_I2C_GenerateStopCondition(i2c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_I2C_STM32_INTERRUPT
|
||||||
|
void stm32_i2c_event_isr(void *arg)
|
||||||
|
{
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG((struct device *)arg);
|
||||||
|
struct i2c_stm32_data *data = DEV_DATA((struct device *)arg);
|
||||||
|
I2C_TypeDef *i2c = cfg->i2c;
|
||||||
|
|
||||||
|
if (data->current.is_write) {
|
||||||
|
if (data->current.len && LL_I2C_IsEnabledIT_TX(i2c)) {
|
||||||
|
LL_I2C_TransmitData8(i2c, *data->current.buf);
|
||||||
|
} else {
|
||||||
|
LL_I2C_DisableIT_TX(i2c);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (data->current.len && LL_I2C_IsEnabledIT_RX(i2c)) {
|
||||||
|
*data->current.buf = LL_I2C_ReceiveData8(i2c);
|
||||||
|
} else {
|
||||||
|
LL_I2C_DisableIT_RX(i2c);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data->current.buf++;
|
||||||
|
data->current.len--;
|
||||||
|
if (!data->current.len)
|
||||||
|
k_sem_give(&data->device_sync_sem);
|
||||||
|
|
||||||
|
return;
|
||||||
|
error:
|
||||||
|
data->current.is_err = 1;
|
||||||
|
k_sem_give(&data->device_sync_sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stm32_i2c_error_isr(void *arg)
|
||||||
|
{
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG((struct device *)arg);
|
||||||
|
struct i2c_stm32_data *data = DEV_DATA((struct device *)arg);
|
||||||
|
I2C_TypeDef *i2c = cfg->i2c;
|
||||||
|
|
||||||
|
if (LL_I2C_IsActiveFlag_NACK(i2c)) {
|
||||||
|
LL_I2C_ClearFlag_NACK(i2c);
|
||||||
|
data->current.is_nack = 1;
|
||||||
|
} else {
|
||||||
|
data->current.is_err = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_sem_give(&data->device_sync_sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
int stm32_i2c_msg_write(struct device *dev, struct i2c_msg *msg,
|
||||||
|
unsigned int flags, uint16_t slave)
|
||||||
|
{
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG(dev);
|
||||||
|
struct i2c_stm32_data *data = DEV_DATA(dev);
|
||||||
|
I2C_TypeDef *i2c = cfg->i2c;
|
||||||
|
|
||||||
|
data->current.len = msg->len;
|
||||||
|
data->current.buf = msg->buf;
|
||||||
|
data->current.is_write = 1;
|
||||||
|
data->current.is_nack = 0;
|
||||||
|
data->current.is_err = 0;
|
||||||
|
|
||||||
|
msg_init(dev, msg, flags, slave, LL_I2C_REQUEST_WRITE);
|
||||||
|
LL_I2C_EnableIT_TX(i2c);
|
||||||
|
LL_I2C_EnableIT_NACK(i2c);
|
||||||
|
|
||||||
|
k_sem_take(&data->device_sync_sem, K_FOREVER);
|
||||||
|
if (data->current.is_nack || data->current.is_err) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg_done(dev, flags);
|
||||||
|
LL_I2C_DisableIT_TX(i2c);
|
||||||
|
LL_I2C_DisableIT_NACK(i2c);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
error:
|
||||||
|
LL_I2C_DisableIT_TX(i2c);
|
||||||
|
LL_I2C_DisableIT_NACK(i2c);
|
||||||
|
|
||||||
|
if (data->current.is_nack) {
|
||||||
|
SYS_LOG_DBG("%s: NACK", __func__);
|
||||||
|
data->current.is_nack = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->current.is_err) {
|
||||||
|
SYS_LOG_DBG("%s: ERR %d", __func__,
|
||||||
|
data->current.is_err);
|
||||||
|
data->current.is_err = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
int stm32_i2c_msg_read(struct device *dev, struct i2c_msg *msg,
|
||||||
|
unsigned int flags, uint16_t slave)
|
||||||
|
{
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG(dev);
|
||||||
|
struct i2c_stm32_data *data = DEV_DATA(dev);
|
||||||
|
I2C_TypeDef *i2c = cfg->i2c;
|
||||||
|
|
||||||
|
data->current.len = msg->len;
|
||||||
|
data->current.buf = msg->buf;
|
||||||
|
data->current.is_write = 0;
|
||||||
|
data->current.is_err = 0;
|
||||||
|
|
||||||
|
msg_init(dev, msg, flags, slave, LL_I2C_REQUEST_READ);
|
||||||
|
LL_I2C_EnableIT_RX(i2c);
|
||||||
|
|
||||||
|
k_sem_take(&data->device_sync_sem, K_FOREVER);
|
||||||
|
if (data->current.is_err) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg_done(dev, flags | msg->flags);
|
||||||
|
LL_I2C_DisableIT_RX(i2c);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
error:
|
||||||
|
LL_I2C_DisableIT_RX(i2c);
|
||||||
|
SYS_LOG_DBG("%s: ERR %d", __func__, data->current.is_err);
|
||||||
|
data->current.is_err = 0;
|
||||||
|
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* !CONFIG_I2C_STM32_INTERRUPT */
|
||||||
|
int stm32_i2c_msg_write(struct device *dev, struct i2c_msg *msg,
|
||||||
|
unsigned int flags, uint16_t slave)
|
||||||
|
{
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG(dev);
|
||||||
|
I2C_TypeDef *i2c = cfg->i2c;
|
||||||
|
unsigned int len = msg->len;
|
||||||
|
u8_t *buf = msg->buf;
|
||||||
|
|
||||||
|
msg_init(dev, msg, flags, slave, LL_I2C_REQUEST_WRITE);
|
||||||
|
|
||||||
|
while (len) {
|
||||||
|
while (1) {
|
||||||
|
if (LL_I2C_IsActiveFlag_TXIS(i2c)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LL_I2C_IsActiveFlag_NACK(i2c)) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LL_I2C_TransmitData8(i2c, *buf);
|
||||||
|
buf++;
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg_done(dev, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
error:
|
||||||
|
LL_I2C_ClearFlag_NACK(i2c);
|
||||||
|
SYS_LOG_DBG("%s: NACK", __func__);
|
||||||
|
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
int stm32_i2c_msg_read(struct device *dev, struct i2c_msg *msg,
|
||||||
|
unsigned int flags, uint16_t slave)
|
||||||
|
{
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG(dev);
|
||||||
|
I2C_TypeDef *i2c = cfg->i2c;
|
||||||
|
unsigned int len = msg->len;
|
||||||
|
u8_t *buf = msg->buf;
|
||||||
|
|
||||||
|
msg_init(dev, msg, flags, slave, LL_I2C_REQUEST_READ);
|
||||||
|
|
||||||
|
while (len) {
|
||||||
|
while (!LL_I2C_IsActiveFlag_RXNE(i2c)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
*buf = LL_I2C_ReceiveData8(i2c);
|
||||||
|
buf++;
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg_done(dev, flags | msg->flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int stm32_i2c_configure_timing(struct device *dev, u32_t clock)
|
||||||
|
{
|
||||||
|
const struct i2c_stm32_config *cfg = DEV_CFG(dev);
|
||||||
|
struct i2c_stm32_data *data = DEV_DATA(dev);
|
||||||
|
I2C_TypeDef *i2c = cfg->i2c;
|
||||||
|
u32_t i2c_hold_time_min, i2c_setup_time_min;
|
||||||
|
u32_t i2c_h_min_time, i2c_l_min_time;
|
||||||
|
u32_t presc = 1;
|
||||||
|
u32_t timing;
|
||||||
|
|
||||||
|
switch (data->dev_config.bits.speed) {
|
||||||
|
case I2C_SPEED_STANDARD:
|
||||||
|
i2c_h_min_time = 4000;
|
||||||
|
i2c_l_min_time = 4700;
|
||||||
|
i2c_hold_time_min = 500;
|
||||||
|
i2c_setup_time_min = 1250;
|
||||||
|
break;
|
||||||
|
case I2C_SPEED_FAST:
|
||||||
|
i2c_h_min_time = 600;
|
||||||
|
i2c_l_min_time = 1300;
|
||||||
|
i2c_hold_time_min = 375;
|
||||||
|
i2c_setup_time_min = 500;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate period until prescaler matches */
|
||||||
|
do {
|
||||||
|
u32_t t_presc = clock / presc;
|
||||||
|
u32_t ns_presc = NSEC_PER_SEC / t_presc;
|
||||||
|
u32_t sclh = i2c_h_min_time / ns_presc;
|
||||||
|
u32_t scll = i2c_l_min_time / ns_presc;
|
||||||
|
u32_t sdadel = i2c_hold_time_min / ns_presc;
|
||||||
|
u32_t scldel = i2c_setup_time_min / ns_presc;
|
||||||
|
|
||||||
|
if ((sclh - 1) > 255 || (scll - 1) > 255) {
|
||||||
|
++presc;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sdadel > 15 || (scldel - 1) > 15) {
|
||||||
|
++presc;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
timing = __LL_I2C_CONVERT_TIMINGS(presc - 1,
|
||||||
|
scldel - 1, sdadel, sclh - 1, scll - 1);
|
||||||
|
break;
|
||||||
|
} while (presc < 16);
|
||||||
|
|
||||||
|
if (presc >= 16) {
|
||||||
|
SYS_LOG_DBG("I2C:failed to find prescaler value");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
LL_I2C_SetTiming(i2c, timing);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -1,521 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2016 BayLibre, SAS
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <kernel.h>
|
|
||||||
|
|
||||||
#include <board.h>
|
|
||||||
#include <i2c.h>
|
|
||||||
#include <clock_control.h>
|
|
||||||
|
|
||||||
#include <misc/util.h>
|
|
||||||
|
|
||||||
#include <clock_control/stm32_clock_control.h>
|
|
||||||
#include "i2c_stm32lx.h"
|
|
||||||
|
|
||||||
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_I2C_LEVEL
|
|
||||||
#include <logging/sys_log.h>
|
|
||||||
|
|
||||||
/* convenience defines */
|
|
||||||
#define DEV_CFG(dev) \
|
|
||||||
((const struct i2c_stm32lx_config * const)(dev)->config->config_info)
|
|
||||||
#define DEV_DATA(dev) \
|
|
||||||
((struct i2c_stm32lx_data * const)(dev)->driver_data)
|
|
||||||
#define I2C_STRUCT(dev) \
|
|
||||||
((volatile struct i2c_stm32lx *)(DEV_CFG(dev))->base)
|
|
||||||
|
|
||||||
static int i2c_stm32lx_runtime_configure(struct device *dev, u32_t config)
|
|
||||||
{
|
|
||||||
volatile struct i2c_stm32lx *i2c = I2C_STRUCT(dev);
|
|
||||||
struct i2c_stm32lx_data *data = DEV_DATA(dev);
|
|
||||||
const struct i2c_stm32lx_config *cfg = DEV_CFG(dev);
|
|
||||||
u32_t clock;
|
|
||||||
u32_t i2c_h_min_time, i2c_l_min_time;
|
|
||||||
u32_t i2c_hold_time_min, i2c_setup_time_min;
|
|
||||||
u32_t presc = 1;
|
|
||||||
|
|
||||||
data->dev_config.raw = config;
|
|
||||||
|
|
||||||
clock_control_get_rate(data->clock,
|
|
||||||
(clock_control_subsys_t *)&cfg->pclken, &clock);
|
|
||||||
|
|
||||||
if (data->dev_config.bits.is_slave_read)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
/* Disable peripheral */
|
|
||||||
i2c->cr1.bit.pe = 0;
|
|
||||||
while (i2c->cr1.bit.pe)
|
|
||||||
;
|
|
||||||
|
|
||||||
switch (data->dev_config.bits.speed) {
|
|
||||||
case I2C_SPEED_STANDARD:
|
|
||||||
i2c_h_min_time = 4000;
|
|
||||||
i2c_l_min_time = 4700;
|
|
||||||
i2c_hold_time_min = 500;
|
|
||||||
i2c_setup_time_min = 1250;
|
|
||||||
break;
|
|
||||||
case I2C_SPEED_FAST:
|
|
||||||
i2c_h_min_time = 600;
|
|
||||||
i2c_l_min_time = 1300;
|
|
||||||
i2c_hold_time_min = 375;
|
|
||||||
i2c_setup_time_min = 500;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculate period until prescaler matches */
|
|
||||||
do {
|
|
||||||
u32_t t_presc = clock / presc;
|
|
||||||
u32_t ns_presc = NSEC_PER_SEC / t_presc;
|
|
||||||
u32_t sclh = i2c_h_min_time / ns_presc;
|
|
||||||
u32_t scll = i2c_l_min_time / ns_presc;
|
|
||||||
u32_t sdadel = i2c_hold_time_min / ns_presc;
|
|
||||||
u32_t scldel = i2c_setup_time_min / ns_presc;
|
|
||||||
|
|
||||||
if ((sclh - 1) > 255 ||
|
|
||||||
(scll - 1) > 255 ||
|
|
||||||
sdadel > 15 ||
|
|
||||||
(scldel - 1) > 15) {
|
|
||||||
++presc;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
i2c->timingr.bit.presc = presc-1;
|
|
||||||
i2c->timingr.bit.scldel = scldel-1;
|
|
||||||
i2c->timingr.bit.sdadel = sdadel;
|
|
||||||
i2c->timingr.bit.sclh = sclh-1;
|
|
||||||
i2c->timingr.bit.scll = scll-1;
|
|
||||||
|
|
||||||
break;
|
|
||||||
} while (presc < 16);
|
|
||||||
|
|
||||||
if (presc >= 16) {
|
|
||||||
SYS_LOG_DBG("I2C:failed to find prescaler value");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_I2C_STM32LX_INTERRUPT
|
|
||||||
static void i2c_stm32lx_ev_isr(void *arg)
|
|
||||||
{
|
|
||||||
struct device * const dev = (struct device *)arg;
|
|
||||||
volatile struct i2c_stm32lx *i2c = I2C_STRUCT(dev);
|
|
||||||
struct i2c_stm32lx_data *data = DEV_DATA(dev);
|
|
||||||
|
|
||||||
if (data->current.is_write) {
|
|
||||||
if (data->current.len && i2c->isr.bit.txis) {
|
|
||||||
i2c->txdr = *data->current.buf;
|
|
||||||
data->current.buf++;
|
|
||||||
data->current.len--;
|
|
||||||
|
|
||||||
if (!data->current.len)
|
|
||||||
k_sem_give(&data->device_sync_sem);
|
|
||||||
} else {
|
|
||||||
/* Impossible situation */
|
|
||||||
data->current.is_err = 1;
|
|
||||||
i2c->cr1.bit.txie = 0;
|
|
||||||
|
|
||||||
k_sem_give(&data->device_sync_sem);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (data->current.len && i2c->isr.bit.rxne) {
|
|
||||||
*data->current.buf = i2c->rxdr.bit.data;
|
|
||||||
data->current.buf++;
|
|
||||||
data->current.len--;
|
|
||||||
|
|
||||||
if (!data->current.len)
|
|
||||||
k_sem_give(&data->device_sync_sem);
|
|
||||||
} else {
|
|
||||||
/* Impossible situation */
|
|
||||||
data->current.is_err = 1;
|
|
||||||
i2c->cr1.bit.rxie = 0;
|
|
||||||
|
|
||||||
k_sem_give(&data->device_sync_sem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void i2c_stm32lx_er_isr(void *arg)
|
|
||||||
{
|
|
||||||
struct device * const dev = (struct device *)arg;
|
|
||||||
volatile struct i2c_stm32lx *i2c = I2C_STRUCT(dev);
|
|
||||||
struct i2c_stm32lx_data *data = DEV_DATA(dev);
|
|
||||||
|
|
||||||
if (i2c->isr.bit.nackf) {
|
|
||||||
i2c->icr.bit.nack = 1;
|
|
||||||
|
|
||||||
data->current.is_nack = 1;
|
|
||||||
|
|
||||||
k_sem_give(&data->device_sync_sem);
|
|
||||||
} else {
|
|
||||||
/* Unknown Error */
|
|
||||||
data->current.is_err = 1;
|
|
||||||
|
|
||||||
k_sem_give(&data->device_sync_sem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static inline void transfer_setup(struct device *dev, u16_t slave_address,
|
|
||||||
unsigned int read_transfer)
|
|
||||||
{
|
|
||||||
volatile struct i2c_stm32lx *i2c = I2C_STRUCT(dev);
|
|
||||||
struct i2c_stm32lx_data *data = DEV_DATA(dev);
|
|
||||||
|
|
||||||
if (data->dev_config.bits.use_10_bit_addr) {
|
|
||||||
i2c->cr2.bit.add10 = 1;
|
|
||||||
i2c->cr2.bit.sadd = slave_address;
|
|
||||||
} else
|
|
||||||
i2c->cr2.bit.sadd = slave_address << 1;
|
|
||||||
|
|
||||||
i2c->cr2.bit.rd_wrn = !!read_transfer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int msg_write(struct device *dev, struct i2c_msg *msg,
|
|
||||||
unsigned int flags)
|
|
||||||
{
|
|
||||||
volatile struct i2c_stm32lx *i2c = I2C_STRUCT(dev);
|
|
||||||
#ifdef CONFIG_I2C_STM32LX_INTERRUPT
|
|
||||||
struct i2c_stm32lx_data *data = DEV_DATA(dev);
|
|
||||||
#endif
|
|
||||||
unsigned int len = msg->len;
|
|
||||||
u8_t *buf = msg->buf;
|
|
||||||
|
|
||||||
if (len > 255)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
#ifdef CONFIG_I2C_STM32LX_INTERRUPT
|
|
||||||
data->current.len = len;
|
|
||||||
data->current.buf = buf;
|
|
||||||
data->current.is_nack = 0;
|
|
||||||
data->current.is_err = 0;
|
|
||||||
data->current.is_write = 1;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
i2c->cr2.bit.nbytes = len;
|
|
||||||
|
|
||||||
if ((flags & I2C_MSG_RESTART) == I2C_MSG_RESTART)
|
|
||||||
i2c->cr2.bit.autoend = 0;
|
|
||||||
else
|
|
||||||
i2c->cr2.bit.autoend = 1;
|
|
||||||
|
|
||||||
i2c->cr2.bit.reload = 0;
|
|
||||||
i2c->cr2.bit.start = 1;
|
|
||||||
|
|
||||||
while (i2c->cr2.bit.start)
|
|
||||||
;
|
|
||||||
|
|
||||||
#ifdef CONFIG_I2C_STM32LX_INTERRUPT
|
|
||||||
i2c->cr1.bit.txie = 1;
|
|
||||||
i2c->cr1.bit.nackie = 1;
|
|
||||||
|
|
||||||
k_sem_take(&data->device_sync_sem, K_FOREVER);
|
|
||||||
|
|
||||||
if (data->current.is_nack || data->current.is_err) {
|
|
||||||
i2c->cr1.bit.txie = 0;
|
|
||||||
i2c->cr1.bit.nackie = 0;
|
|
||||||
if (data->current.is_nack)
|
|
||||||
SYS_LOG_DBG("%s: NACK", __func__);
|
|
||||||
if (data->current.is_err)
|
|
||||||
SYS_LOG_DBG("%s: ERR %d", __func__,
|
|
||||||
data->current.is_err);
|
|
||||||
data->current.is_nack = 0;
|
|
||||||
data->current.is_err = 0;
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
while (len) {
|
|
||||||
do {
|
|
||||||
if (i2c->isr.bit.txis)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (i2c->isr.bit.nackf) {
|
|
||||||
i2c->icr.bit.nack = 1;
|
|
||||||
SYS_LOG_DBG("%s: NACK", __func__);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
} while (1);
|
|
||||||
|
|
||||||
i2c->txdr = *buf;
|
|
||||||
|
|
||||||
buf++;
|
|
||||||
len--;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ((flags & I2C_MSG_RESTART) == 0) {
|
|
||||||
while (!i2c->isr.bit.stopf)
|
|
||||||
;
|
|
||||||
i2c->icr.bit.stop = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_I2C_STM32LX_INTERRUPT
|
|
||||||
i2c->cr1.bit.txie = 0;
|
|
||||||
i2c->cr1.bit.nackie = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int msg_read(struct device *dev, struct i2c_msg *msg,
|
|
||||||
unsigned int flags)
|
|
||||||
{
|
|
||||||
volatile struct i2c_stm32lx *i2c = I2C_STRUCT(dev);
|
|
||||||
#ifdef CONFIG_I2C_STM32LX_INTERRUPT
|
|
||||||
struct i2c_stm32lx_data *data = DEV_DATA(dev);
|
|
||||||
#endif
|
|
||||||
unsigned int len = msg->len;
|
|
||||||
u8_t *buf = msg->buf;
|
|
||||||
|
|
||||||
if (len > 255)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
#ifdef CONFIG_I2C_STM32LX_INTERRUPT
|
|
||||||
data->current.len = len;
|
|
||||||
data->current.buf = buf;
|
|
||||||
data->current.is_err = 0;
|
|
||||||
data->current.is_write = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
i2c->cr2.bit.nbytes = len;
|
|
||||||
|
|
||||||
if ((flags & I2C_MSG_RESTART) == I2C_MSG_RESTART)
|
|
||||||
i2c->cr2.bit.autoend = 0;
|
|
||||||
else
|
|
||||||
i2c->cr2.bit.autoend = 1;
|
|
||||||
|
|
||||||
i2c->cr2.bit.reload = 0;
|
|
||||||
i2c->cr2.bit.start = 1;
|
|
||||||
|
|
||||||
while (i2c->cr2.bit.start)
|
|
||||||
;
|
|
||||||
|
|
||||||
#ifdef CONFIG_I2C_STM32LX_INTERRUPT
|
|
||||||
i2c->cr1.bit.rxie = 1;
|
|
||||||
|
|
||||||
k_sem_take(&data->device_sync_sem, K_FOREVER);
|
|
||||||
|
|
||||||
if (data->current.is_err) {
|
|
||||||
i2c->cr1.bit.rxie = 0;
|
|
||||||
if (data->current.is_err)
|
|
||||||
SYS_LOG_DBG("%s: ERR %d", __func__,
|
|
||||||
data->current.is_err);
|
|
||||||
data->current.is_err = 0;
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
while (len) {
|
|
||||||
while (!i2c->isr.bit.rxne)
|
|
||||||
;
|
|
||||||
|
|
||||||
*buf = i2c->rxdr.bit.data;
|
|
||||||
|
|
||||||
buf++;
|
|
||||||
len--;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ((flags & I2C_MSG_RESTART) == 0) {
|
|
||||||
while (!i2c->isr.bit.stopf)
|
|
||||||
;
|
|
||||||
i2c->icr.bit.stop = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_I2C_STM32LX_INTERRUPT
|
|
||||||
i2c->cr1.bit.rxie = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i2c_stm32lx_transfer(struct device *dev,
|
|
||||||
struct i2c_msg *msgs, u8_t num_msgs,
|
|
||||||
u16_t slave_address)
|
|
||||||
{
|
|
||||||
volatile struct i2c_stm32lx *i2c = I2C_STRUCT(dev);
|
|
||||||
struct i2c_msg *cur_msg = msgs;
|
|
||||||
u8_t msg_left = num_msgs;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
/* Enable Peripheral */
|
|
||||||
i2c->cr1.bit.pe = 1;
|
|
||||||
|
|
||||||
/* Process all messages one-by-one */
|
|
||||||
while (msg_left > 0) {
|
|
||||||
unsigned int flags = 0;
|
|
||||||
|
|
||||||
if (cur_msg->len > 255)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (msg_left > 1 &&
|
|
||||||
(cur_msg[0].flags & I2C_MSG_RW_MASK) !=
|
|
||||||
(cur_msg[1].flags & I2C_MSG_RW_MASK))
|
|
||||||
flags |= I2C_MSG_RESTART;
|
|
||||||
|
|
||||||
if ((cur_msg->flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) {
|
|
||||||
transfer_setup(dev, slave_address, 0);
|
|
||||||
ret = msg_write(dev, cur_msg, flags);
|
|
||||||
} else {
|
|
||||||
transfer_setup(dev, slave_address, 1);
|
|
||||||
ret = msg_read(dev, cur_msg, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
ret = -EIO;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_msg++;
|
|
||||||
msg_left--;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Disable Peripheral */
|
|
||||||
i2c->cr1.bit.pe = 0;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct i2c_driver_api api_funcs = {
|
|
||||||
.configure = i2c_stm32lx_runtime_configure,
|
|
||||||
.transfer = i2c_stm32lx_transfer,
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline void __i2c_stm32lx_get_clock(struct device *dev)
|
|
||||||
{
|
|
||||||
struct i2c_stm32lx_data *data = DEV_DATA(dev);
|
|
||||||
struct device *clk =
|
|
||||||
device_get_binding(STM32_CLOCK_CONTROL_NAME);
|
|
||||||
|
|
||||||
__ASSERT_NO_MSG(clk);
|
|
||||||
|
|
||||||
data->clock = clk;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i2c_stm32lx_init(struct device *dev)
|
|
||||||
{
|
|
||||||
volatile struct i2c_stm32lx *i2c = I2C_STRUCT(dev);
|
|
||||||
struct i2c_stm32lx_data *data = DEV_DATA(dev);
|
|
||||||
const struct i2c_stm32lx_config *cfg = DEV_CFG(dev);
|
|
||||||
|
|
||||||
k_sem_init(&data->device_sync_sem, 0, UINT_MAX);
|
|
||||||
|
|
||||||
__i2c_stm32lx_get_clock(dev);
|
|
||||||
|
|
||||||
/* enable clock */
|
|
||||||
clock_control_on(data->clock,
|
|
||||||
(clock_control_subsys_t *)&cfg->pclken);
|
|
||||||
|
|
||||||
/* Reset config */
|
|
||||||
i2c->cr1.val = 0;
|
|
||||||
i2c->cr2.val = 0;
|
|
||||||
i2c->oar1.val = 0;
|
|
||||||
i2c->oar2.val = 0;
|
|
||||||
i2c->timingr.val = 0;
|
|
||||||
i2c->timeoutr.val = 0;
|
|
||||||
i2c->pecr.val = 0;
|
|
||||||
i2c->icr.val = 0xFFFFFFFF;
|
|
||||||
|
|
||||||
/* Try to Setup HW */
|
|
||||||
i2c_stm32lx_runtime_configure(dev, data->dev_config.raw);
|
|
||||||
|
|
||||||
#ifdef CONFIG_I2C_STM32LX_INTERRUPT
|
|
||||||
cfg->irq_config_func(dev);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_I2C_1
|
|
||||||
|
|
||||||
#ifdef CONFIG_I2C_STM32LX_INTERRUPT
|
|
||||||
static void i2c_stm32lx_irq_config_func_1(struct device *port);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const struct i2c_stm32lx_config i2c_stm32lx_cfg_1 = {
|
|
||||||
.base = (u8_t *)I2C1_BASE,
|
|
||||||
.pclken = { .bus = STM32_CLOCK_BUS_APB1,
|
|
||||||
.enr = LL_APB1_GRP1_PERIPH_I2C1 },
|
|
||||||
#ifdef CONFIG_I2C_STM32LX_INTERRUPT
|
|
||||||
.irq_config_func = i2c_stm32lx_irq_config_func_1,
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct i2c_stm32lx_data i2c_stm32lx_dev_data_1 = {
|
|
||||||
.dev_config.raw = CONFIG_I2C_1_DEFAULT_CFG,
|
|
||||||
};
|
|
||||||
|
|
||||||
DEVICE_AND_API_INIT(i2c_stm32lx_1, CONFIG_I2C_1_NAME, &i2c_stm32lx_init,
|
|
||||||
&i2c_stm32lx_dev_data_1, &i2c_stm32lx_cfg_1,
|
|
||||||
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
|
||||||
&api_funcs);
|
|
||||||
|
|
||||||
#ifdef CONFIG_I2C_STM32LX_INTERRUPT
|
|
||||||
static void i2c_stm32lx_irq_config_func_1(struct device *dev)
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_SOC_SERIES_STM32L4X
|
|
||||||
#define PORT_1_EV_IRQ STM32L4_IRQ_I2C1_EV
|
|
||||||
#define PORT_1_ER_IRQ STM32L4_IRQ_I2C1_ER
|
|
||||||
#endif
|
|
||||||
|
|
||||||
IRQ_CONNECT(PORT_1_EV_IRQ, CONFIG_I2C_1_IRQ_PRI,
|
|
||||||
i2c_stm32lx_ev_isr, DEVICE_GET(i2c_stm32lx_1), 0);
|
|
||||||
irq_enable(PORT_1_EV_IRQ);
|
|
||||||
|
|
||||||
IRQ_CONNECT(PORT_1_ER_IRQ, CONFIG_I2C_1_IRQ_PRI,
|
|
||||||
i2c_stm32lx_er_isr, DEVICE_GET(i2c_stm32lx_1), 0);
|
|
||||||
irq_enable(PORT_1_ER_IRQ);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* CONFIG_I2C_1 */
|
|
||||||
|
|
||||||
#ifdef CONFIG_I2C_2
|
|
||||||
|
|
||||||
#ifdef CONFIG_I2C_STM32LX_INTERRUPT
|
|
||||||
static void i2c_stm32lx_irq_config_func_2(struct device *port);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const struct i2c_stm32lx_config i2c_stm32lx_cfg_2 = {
|
|
||||||
.base = (u8_t *)I2C2_BASE,
|
|
||||||
.pclken = { .bus = STM32_CLOCK_BUS_APB1,
|
|
||||||
.enr = LL_APB1_GRP1_PERIPH_I2C2 },
|
|
||||||
#ifdef CONFIG_I2C_STM32LX_INTERRUPT
|
|
||||||
.irq_config_func = i2c_stm32lx_irq_config_func_2,
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct i2c_stm32lx_data i2c_stm32lx_dev_data_2 = {
|
|
||||||
.dev_config.raw = CONFIG_I2C_2_DEFAULT_CFG,
|
|
||||||
};
|
|
||||||
|
|
||||||
DEVICE_AND_API_INIT(i2c_stm32lx_2, CONFIG_I2C_2_NAME, &i2c_stm32lx_init,
|
|
||||||
&i2c_stm32lx_dev_data_2, &i2c_stm32lx_cfg_2,
|
|
||||||
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
|
||||||
&api_funcs);
|
|
||||||
|
|
||||||
#ifdef CONFIG_I2C_STM32LX_INTERRUPT
|
|
||||||
static void i2c_stm32lx_irq_config_func_2(struct device *dev)
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_SOC_SERIES_STM32L4X
|
|
||||||
#define PORT_2_EV_IRQ STM32L4_IRQ_I2C2_EV
|
|
||||||
#define PORT_2_ER_IRQ STM32L4_IRQ_I2C2_ER
|
|
||||||
#endif
|
|
||||||
|
|
||||||
IRQ_CONNECT(PORT_2_EV_IRQ, CONFIG_I2C_2_IRQ_PRI,
|
|
||||||
i2c_stm32lx_ev_isr, DEVICE_GET(i2c_stm32lx_2), 0);
|
|
||||||
irq_enable(PORT_2_EV_IRQ);
|
|
||||||
|
|
||||||
IRQ_CONNECT(PORT_2_ER_IRQ, CONFIG_I2C_2_IRQ_PRI,
|
|
||||||
i2c_stm32lx_er_isr, DEVICE_GET(i2c_stm32lx_2), 0);
|
|
||||||
irq_enable(PORT_2_ER_IRQ);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* CONFIG_I2C_2 */
|
|
|
@ -1,209 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2016 BayLibre, SAS
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _STM32LX_I2C_H_
|
|
||||||
#define _STM32LX_I2C_H_
|
|
||||||
|
|
||||||
/* 35.6.1 Control register 1 (I2C_CR1) */
|
|
||||||
union __cr1 {
|
|
||||||
u32_t val;
|
|
||||||
struct {
|
|
||||||
u32_t pe :1 __packed;
|
|
||||||
u32_t txie :1 __packed;
|
|
||||||
u32_t rxie :1 __packed;
|
|
||||||
u32_t addrie :1 __packed;
|
|
||||||
u32_t nackie :1 __packed;
|
|
||||||
u32_t stopie :1 __packed;
|
|
||||||
u32_t tcie :1 __packed;
|
|
||||||
u32_t errie :1 __packed;
|
|
||||||
u32_t dnf :4 __packed;
|
|
||||||
u32_t anfoff :1 __packed;
|
|
||||||
u32_t rsvd__13 :1 __packed;
|
|
||||||
u32_t txdmaen :1 __packed;
|
|
||||||
u32_t rxdmaen :1 __packed;
|
|
||||||
u32_t sbc :1 __packed;
|
|
||||||
u32_t nostretch :1 __packed;
|
|
||||||
u32_t wupen :1 __packed;
|
|
||||||
u32_t gcen :1 __packed;
|
|
||||||
u32_t smbhen :1 __packed;
|
|
||||||
u32_t smbden :1 __packed;
|
|
||||||
u32_t alerten :1 __packed;
|
|
||||||
u32_t pecen :1 __packed;
|
|
||||||
u32_t rsvd__24_31 :8 __packed;
|
|
||||||
} bit;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* 35.6.2 Control register 2 (I2C_CR2) */
|
|
||||||
union __cr2 {
|
|
||||||
u32_t val;
|
|
||||||
struct {
|
|
||||||
u32_t sadd :10 __packed;
|
|
||||||
u32_t rd_wrn :1 __packed;
|
|
||||||
u32_t add10 :1 __packed;
|
|
||||||
u32_t head10r :1 __packed;
|
|
||||||
u32_t start :1 __packed;
|
|
||||||
u32_t stop :1 __packed;
|
|
||||||
u32_t nack :1 __packed;
|
|
||||||
u32_t nbytes :8 __packed;
|
|
||||||
u32_t reload :1 __packed;
|
|
||||||
u32_t autoend :1 __packed;
|
|
||||||
u32_t pecbyte :1 __packed;
|
|
||||||
u32_t rsvd__27_31 :5 __packed;
|
|
||||||
} bit;
|
|
||||||
};
|
|
||||||
|
|
||||||
union __oar1 {
|
|
||||||
u32_t val;
|
|
||||||
struct {
|
|
||||||
u32_t oa1 :10 __packed;
|
|
||||||
u32_t oa1mode :1 __packed;
|
|
||||||
u32_t rsvd__11_14 :4 __packed;
|
|
||||||
u32_t oa1en :1 __packed;
|
|
||||||
u32_t rsvd__16_31 :16 __packed;
|
|
||||||
} bit;
|
|
||||||
};
|
|
||||||
|
|
||||||
union __oar2 {
|
|
||||||
u32_t val;
|
|
||||||
struct {
|
|
||||||
u32_t rsvd__0 :1 __packed;
|
|
||||||
u32_t oa2 :7 __packed;
|
|
||||||
u32_t oa2msk :3 __packed;
|
|
||||||
u32_t rsvd__11_14 :4 __packed;
|
|
||||||
u32_t oa2en :1 __packed;
|
|
||||||
u32_t rsvd__16_31 :16 __packed;
|
|
||||||
} bit;
|
|
||||||
};
|
|
||||||
|
|
||||||
union __timingr {
|
|
||||||
u32_t val;
|
|
||||||
struct {
|
|
||||||
u32_t scll :8 __packed;
|
|
||||||
u32_t sclh :8 __packed;
|
|
||||||
u32_t sdadel :4 __packed;
|
|
||||||
u32_t scldel :4 __packed;
|
|
||||||
u32_t rsvd__24_27 :4 __packed;
|
|
||||||
u32_t presc :4 __packed;
|
|
||||||
} bit;
|
|
||||||
};
|
|
||||||
|
|
||||||
union __timeoutr {
|
|
||||||
u32_t val;
|
|
||||||
struct {
|
|
||||||
u32_t timeouta :12 __packed;
|
|
||||||
u32_t tidle :1 __packed;
|
|
||||||
u32_t rsvd__13_14 :1 __packed;
|
|
||||||
u32_t timouten :1 __packed;
|
|
||||||
u32_t timeoutb :12 __packed;
|
|
||||||
u32_t rsvd__28_30 :1 __packed;
|
|
||||||
u32_t texten :1 __packed;
|
|
||||||
} bit;
|
|
||||||
};
|
|
||||||
|
|
||||||
union __isr {
|
|
||||||
u32_t val;
|
|
||||||
struct {
|
|
||||||
u32_t txe :1 __packed;
|
|
||||||
u32_t txis :1 __packed;
|
|
||||||
u32_t rxne :1 __packed;
|
|
||||||
u32_t addr :1 __packed;
|
|
||||||
u32_t nackf :1 __packed;
|
|
||||||
u32_t stopf :1 __packed;
|
|
||||||
u32_t tc :1 __packed;
|
|
||||||
u32_t tcr :1 __packed;
|
|
||||||
u32_t berr :1 __packed;
|
|
||||||
u32_t arlo :1 __packed;
|
|
||||||
u32_t ovr :1 __packed;
|
|
||||||
u32_t pecerr :1 __packed;
|
|
||||||
u32_t timeout :1 __packed;
|
|
||||||
u32_t alert :1 __packed;
|
|
||||||
u32_t rsvd__14 :1 __packed;
|
|
||||||
u32_t busy :1 __packed;
|
|
||||||
u32_t dir :1 __packed;
|
|
||||||
u32_t addcode :7 __packed;
|
|
||||||
u32_t rsvd__24_31 :8 __packed;
|
|
||||||
} bit;
|
|
||||||
};
|
|
||||||
|
|
||||||
union __icr {
|
|
||||||
u32_t val;
|
|
||||||
struct {
|
|
||||||
u32_t rsvd__0_2 :3 __packed;
|
|
||||||
u32_t addr :1 __packed;
|
|
||||||
u32_t nack :1 __packed;
|
|
||||||
u32_t stop :1 __packed;
|
|
||||||
u32_t rsvd__6_7 :2 __packed;
|
|
||||||
u32_t berr :1 __packed;
|
|
||||||
u32_t arlo :1 __packed;
|
|
||||||
u32_t ovr :1 __packed;
|
|
||||||
u32_t pec :1 __packed;
|
|
||||||
u32_t timeout :1 __packed;
|
|
||||||
u32_t alert :1 __packed;
|
|
||||||
u32_t rsvd__14_31 :17 __packed;
|
|
||||||
} bit;
|
|
||||||
};
|
|
||||||
|
|
||||||
union __pecr {
|
|
||||||
u32_t val;
|
|
||||||
struct {
|
|
||||||
u32_t pec:8 __packed;
|
|
||||||
u32_t rsvd__9_31 :24 __packed;
|
|
||||||
} bit;
|
|
||||||
};
|
|
||||||
|
|
||||||
union __dr {
|
|
||||||
u32_t val;
|
|
||||||
struct {
|
|
||||||
u32_t data:8 __packed;
|
|
||||||
u32_t rsvd__9_31 :24 __packed;
|
|
||||||
} bit;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* 35.7.12 I2C register map */
|
|
||||||
struct i2c_stm32lx {
|
|
||||||
union __cr1 cr1;
|
|
||||||
union __cr2 cr2;
|
|
||||||
union __oar1 oar1;
|
|
||||||
union __oar2 oar2;
|
|
||||||
union __timingr timingr;
|
|
||||||
union __timeoutr timeoutr;
|
|
||||||
union __isr isr;
|
|
||||||
union __icr icr;
|
|
||||||
union __pecr pecr;
|
|
||||||
union __dr rxdr;
|
|
||||||
u32_t txdr;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef void (*irq_config_func_t)(struct device *port);
|
|
||||||
|
|
||||||
/* device config */
|
|
||||||
struct i2c_stm32lx_config {
|
|
||||||
void *base;
|
|
||||||
irq_config_func_t irq_config_func;
|
|
||||||
/* clock subsystem driving this peripheral */
|
|
||||||
struct stm32_pclken pclken;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* driver data */
|
|
||||||
struct i2c_stm32lx_data {
|
|
||||||
/* clock device */
|
|
||||||
struct device *clock;
|
|
||||||
/* Device config */
|
|
||||||
union dev_config dev_config;
|
|
||||||
/* ISR Sync */
|
|
||||||
struct k_sem device_sync_sem;
|
|
||||||
/* Current message data */
|
|
||||||
struct {
|
|
||||||
struct i2c_msg *msg;
|
|
||||||
unsigned int len;
|
|
||||||
u8_t *buf;
|
|
||||||
unsigned int is_err;
|
|
||||||
unsigned int is_nack;
|
|
||||||
unsigned int is_write;
|
|
||||||
} current;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _STM32LX_UART_H_ */
|
|
Loading…
Add table
Add a link
Reference in a new issue