171 lines
4.4 KiB
C
171 lines
4.4 KiB
C
/*
|
|
* Copyright (c) 2019 Antmicro <www.antmicro.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT litex_i2c
|
|
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/drivers/i2c.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(i2c_litex, CONFIG_I2C_LOG_LEVEL);
|
|
|
|
#include "i2c-priv.h"
|
|
#include "i2c_bitbang.h"
|
|
|
|
#include <soc.h>
|
|
|
|
#define SCL_BIT_POS 0
|
|
#define SDA_DIR_BIT_POS 1
|
|
#define SDA_BIT_W_POS 2
|
|
#define SDA_BIT_R_POS 0
|
|
|
|
#define SDA_DIR_OUTPUT 1
|
|
#define SDA_DIR_INPUT 0
|
|
|
|
#define HIGH_STATE_ON_I2C_LINES 0x7
|
|
|
|
struct i2c_litex_cfg {
|
|
uint32_t write_addr;
|
|
uint32_t read_addr;
|
|
uint32_t bitrate;
|
|
};
|
|
|
|
#define GET_I2C_CFG(dev) \
|
|
((const struct i2c_litex_cfg *) dev->config)
|
|
|
|
#define GET_I2C_BITBANG(dev) \
|
|
((struct i2c_bitbang *) dev->data)
|
|
|
|
static inline void set_bit(uint32_t addr, uint32_t bit, uint32_t val)
|
|
{
|
|
uint32_t mask = BIT(bit);
|
|
|
|
if (val) {
|
|
litex_write8(litex_read8(addr) | mask, addr);
|
|
} else {
|
|
litex_write8(litex_read8(addr) & ~mask, addr);
|
|
}
|
|
}
|
|
|
|
static inline int get_bit(uint32_t addr, uint32_t bit)
|
|
{
|
|
uint32_t mask = BIT(bit);
|
|
|
|
return !!(litex_read8(addr) & mask);
|
|
}
|
|
|
|
static void i2c_litex_bitbang_set_scl(void *context, int state)
|
|
{
|
|
const struct i2c_litex_cfg *config =
|
|
(const struct i2c_litex_cfg *) context;
|
|
|
|
set_bit(config->write_addr, SCL_BIT_POS, state);
|
|
}
|
|
|
|
static void i2c_litex_bitbang_set_sda(void *context, int state)
|
|
{
|
|
const struct i2c_litex_cfg *config =
|
|
(const struct i2c_litex_cfg *) context;
|
|
|
|
set_bit(config->write_addr, SDA_DIR_BIT_POS, SDA_DIR_OUTPUT);
|
|
set_bit(config->write_addr, SDA_BIT_W_POS, state);
|
|
}
|
|
|
|
static int i2c_litex_bitbang_get_sda(void *context)
|
|
{
|
|
const struct i2c_litex_cfg *config =
|
|
(const struct i2c_litex_cfg *) context;
|
|
|
|
set_bit(config->write_addr, SDA_DIR_BIT_POS, SDA_DIR_INPUT);
|
|
return get_bit(config->read_addr, SDA_BIT_R_POS);
|
|
}
|
|
|
|
static const struct i2c_bitbang_io i2c_litex_bitbang_io = {
|
|
.set_scl = i2c_litex_bitbang_set_scl,
|
|
.set_sda = i2c_litex_bitbang_set_sda,
|
|
.get_sda = i2c_litex_bitbang_get_sda,
|
|
};
|
|
|
|
static int i2c_litex_init(const struct device *dev)
|
|
{
|
|
const struct i2c_litex_cfg *config = GET_I2C_CFG(dev);
|
|
struct i2c_bitbang *bitbang = GET_I2C_BITBANG(dev);
|
|
int ret;
|
|
|
|
litex_write8(litex_read8(config->write_addr) | HIGH_STATE_ON_I2C_LINES, config->write_addr);
|
|
i2c_bitbang_init(bitbang, &i2c_litex_bitbang_io, (void *)config);
|
|
|
|
ret = i2c_bitbang_configure(bitbang,
|
|
I2C_MODE_CONTROLLER | i2c_map_dt_bitrate(config->bitrate));
|
|
if (ret != 0) {
|
|
LOG_ERR("failed to configure I2C bitbang: %d", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int i2c_litex_configure(const struct device *dev, uint32_t dev_config)
|
|
{
|
|
struct i2c_bitbang *bitbang = GET_I2C_BITBANG(dev);
|
|
|
|
return i2c_bitbang_configure(bitbang, dev_config);
|
|
}
|
|
|
|
static int i2c_litex_get_config(const struct device *dev, uint32_t *config)
|
|
{
|
|
struct i2c_bitbang *bitbang = GET_I2C_BITBANG(dev);
|
|
|
|
return i2c_bitbang_get_config(bitbang, config);
|
|
}
|
|
|
|
static int i2c_litex_transfer(const struct device *dev, struct i2c_msg *msgs,
|
|
uint8_t num_msgs, uint16_t addr)
|
|
{
|
|
struct i2c_bitbang *bitbang = GET_I2C_BITBANG(dev);
|
|
|
|
return i2c_bitbang_transfer(bitbang, msgs, num_msgs, addr);
|
|
}
|
|
|
|
static int i2c_litex_recover_bus(const struct device *dev)
|
|
{
|
|
struct i2c_bitbang *bitbang = GET_I2C_BITBANG(dev);
|
|
|
|
return i2c_bitbang_recover_bus(bitbang);
|
|
}
|
|
|
|
static const struct i2c_driver_api i2c_litex_driver_api = {
|
|
.configure = i2c_litex_configure,
|
|
.get_config = i2c_litex_get_config,
|
|
.transfer = i2c_litex_transfer,
|
|
.recover_bus = i2c_litex_recover_bus,
|
|
#ifdef CONFIG_I2C_RTIO
|
|
.iodev_submit = i2c_iodev_submit_fallback,
|
|
#endif
|
|
};
|
|
|
|
/* Device Instantiation */
|
|
|
|
#define I2C_LITEX_INIT(n) \
|
|
static const struct i2c_litex_cfg i2c_litex_cfg_##n = { \
|
|
.write_addr = DT_INST_REG_ADDR_BY_NAME(n, write), \
|
|
.read_addr = DT_INST_REG_ADDR_BY_NAME(n, read), \
|
|
.bitrate = DT_INST_PROP(n, clock_frequency), \
|
|
}; \
|
|
\
|
|
static struct i2c_bitbang i2c_bitbang_##n; \
|
|
\
|
|
I2C_DEVICE_DT_INST_DEFINE(n, \
|
|
i2c_litex_init, \
|
|
NULL, \
|
|
&i2c_bitbang_##n, \
|
|
&i2c_litex_cfg_##n, \
|
|
POST_KERNEL, \
|
|
CONFIG_I2C_INIT_PRIORITY, \
|
|
&i2c_litex_driver_api \
|
|
);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(I2C_LITEX_INIT)
|