blob: 9e077a81956e94d078c0a33666724a000061ecf8 [file] [log] [blame]
/*
* Copyright (c) 2017 Linaro Ltd.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT arm_versatile_i2c
/**
* @file
* @brief Driver for ARM's SBCon 2-wire serial bus interface
*
* SBCon is a simple device which allows directly setting and getting the
* hardware state of two-bit serial interfaces like I2C.
*/
#include <zephyr/device.h>
#include <errno.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(i2c_sbcon, CONFIG_I2C_LOG_LEVEL);
#include "i2c-priv.h"
#include "i2c_bitbang.h"
/* SBCon hardware registers layout */
struct sbcon {
union {
volatile uint32_t SB_CONTROLS; /* Write to set pins high */
volatile uint32_t SB_CONTROL; /* Read for state of pins */
};
volatile uint32_t SB_CONTROLC; /* Write to set pins low */
};
/* Bits values for SCL and SDA lines in struct sbcon registers */
#define SCL BIT(0)
#define SDA BIT(1)
/* Driver config */
struct i2c_sbcon_config {
struct sbcon *sbcon; /* Address of hardware registers */
uint32_t bitrate; /* I2C bus speed in Hz */
};
/* Driver instance data */
struct i2c_sbcon_context {
struct i2c_bitbang bitbang; /* Bit-bang library data */
};
static void i2c_sbcon_set_scl(void *io_context, int state)
{
struct sbcon *sbcon = io_context;
if (state) {
sbcon->SB_CONTROLS = SCL;
} else {
sbcon->SB_CONTROLC = SCL;
}
}
static void i2c_sbcon_set_sda(void *io_context, int state)
{
struct sbcon *sbcon = io_context;
if (state) {
sbcon->SB_CONTROLS = SDA;
} else {
sbcon->SB_CONTROLC = SDA;
}
}
static int i2c_sbcon_get_sda(void *io_context)
{
struct sbcon *sbcon = io_context;
return sbcon->SB_CONTROL & SDA;
}
static const struct i2c_bitbang_io io_fns = {
.set_scl = &i2c_sbcon_set_scl,
.set_sda = &i2c_sbcon_set_sda,
.get_sda = &i2c_sbcon_get_sda,
};
static int i2c_sbcon_configure(const struct device *dev, uint32_t dev_config)
{
struct i2c_sbcon_context *context = dev->data;
return i2c_bitbang_configure(&context->bitbang, dev_config);
}
static int i2c_sbcon_get_config(const struct device *dev, uint32_t *config)
{
struct i2c_sbcon_context *context = dev->data;
return i2c_bitbang_get_config(&context->bitbang, config);
}
static int i2c_sbcon_transfer(const struct device *dev, struct i2c_msg *msgs,
uint8_t num_msgs, uint16_t slave_address)
{
struct i2c_sbcon_context *context = dev->data;
return i2c_bitbang_transfer(&context->bitbang, msgs, num_msgs,
slave_address);
}
static int i2c_sbcon_recover_bus(const struct device *dev)
{
struct i2c_sbcon_context *context = dev->data;
return i2c_bitbang_recover_bus(&context->bitbang);
}
static const struct i2c_driver_api api = {
.configure = i2c_sbcon_configure,
.get_config = i2c_sbcon_get_config,
.transfer = i2c_sbcon_transfer,
.recover_bus = i2c_sbcon_recover_bus,
};
static int i2c_sbcon_init(const struct device *dev)
{
struct i2c_sbcon_context *context = dev->data;
const struct i2c_sbcon_config *config = dev->config;
int ret;
i2c_bitbang_init(&context->bitbang, &io_fns, config->sbcon);
ret = i2c_bitbang_configure(&context->bitbang,
I2C_MODE_CONTROLLER | i2c_map_dt_bitrate(config->bitrate));
if (ret != 0) {
LOG_ERR("failed to configure I2C bitbang: %d", ret);
}
return ret;
}
#define DEFINE_I2C_SBCON(_num) \
\
static struct i2c_sbcon_context i2c_sbcon_dev_data_##_num; \
\
static const struct i2c_sbcon_config i2c_sbcon_dev_cfg_##_num = { \
.sbcon = (void *)DT_INST_REG_ADDR(_num), \
.bitrate = DT_INST_PROP(_num, clock_frequency), \
}; \
\
I2C_DEVICE_DT_INST_DEFINE(_num, \
i2c_sbcon_init, \
NULL, \
&i2c_sbcon_dev_data_##_num, \
&i2c_sbcon_dev_cfg_##_num, \
PRE_KERNEL_2, CONFIG_I2C_INIT_PRIORITY, &api);
DT_INST_FOREACH_STATUS_OKAY(DEFINE_I2C_SBCON)