blob: a3af2850b975ae52ad862056d18315def3e24dfa [file] [log] [blame]
/*
* Copyright (c) 2020 Nuvoton Technology Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_I2C_I2C_NPCX_CONTROLLER_H_
#define ZEPHYR_DRIVERS_I2C_I2C_NPCX_CONTROLLER_H_
#include <zephyr/device.h>
#include "soc_miwu.h"
#ifdef __cplusplus
extern "C" {
#endif
/* I2C peripheral register mode */
#define NPCX_I2C_BANK_NORMAL 0
#define NPCX_I2C_BANK_FIFO 1
/* 32 bytes Tx FIFO and 32 bytes Rx FIFO. */
#define NPCX_I2C_FIFO_MAX_SIZE 32
/* Support 65535 bytes during DMA transaction */
#define NPCX_I2C_DMA_MAX_SIZE 65535
/* The delay for the I2C bus recovery bitbang in ~100K Hz */
#define I2C_RECOVER_BUS_DELAY_US 5
/*
* Internal SMBus Interface driver states values, which reflect events
* which occurred on the bus
*/
enum npcx_i2c_oper_state {
NPCX_I2C_IDLE,
NPCX_I2C_WAIT_START,
NPCX_I2C_WAIT_RESTART,
NPCX_I2C_WRITE_DATA,
NPCX_I2C_WRITE_SUSPEND,
NPCX_I2C_READ_DATA,
NPCX_I2C_READ_SUSPEND,
NPCX_I2C_WAIT_STOP,
NPCX_I2C_ERROR_RECOVERY,
};
enum npcx_i2c_flag {
NPCX_I2C_FLAG_TARGET1,
NPCX_I2C_FLAG_TARGET2,
NPCX_I2C_FLAG_TARGET3,
NPCX_I2C_FLAG_TARGET4,
NPCX_I2C_FLAG_TARGET5,
NPCX_I2C_FLAG_TARGET6,
NPCX_I2C_FLAG_TARGET7,
NPCX_I2C_FLAG_TARGET8,
NPCX_I2C_FLAG_COUNT,
};
enum i2c_pm_policy_state_flag {
I2C_PM_POLICY_STATE_FLAG_TGT,
I2C_PM_POLICY_STATE_FLAG_COUNT,
};
/* Device config */
struct i2c_ctrl_config {
uintptr_t base; /* i2c controller base address */
struct npcx_clk_cfg clk_cfg; /* clock configuration */
uint8_t irq; /* i2c controller irq */
#ifdef CONFIG_I2C_TARGET
/* i2c wake-up input source configuration */
const struct npcx_wui smb_wui;
bool wakeup_source;
#endif /* CONFIG_I2C_TARGET */
};
/* Driver data */
struct i2c_ctrl_data {
struct k_sem lock_sem; /* mutex of i2c controller */
struct k_sem sync_sem; /* semaphore used for synchronization */
uint32_t bus_freq; /* operation freq of i2c */
#if defined(CONFIG_I2C_NPCX_INVALID_STOP_WORKAROUND)
uint32_t stop_dealy_cycle_time; /* delay time in cycles before sending STOP */
#endif
enum npcx_i2c_oper_state oper_state; /* controller operation state */
int trans_err; /* error code during transaction */
struct i2c_msg *msg; /* cache msg for transaction state machine */
struct i2c_msg *msg_head;
int is_write; /* direction of current msg */
uint8_t *ptr_msg; /* current msg pointer for FIFO read/write */
uint16_t addr; /* slave address of transaction */
uint8_t msg_max_num;
uint8_t msg_curr_idx;
uint8_t port; /* current port used the controller */
bool is_configured; /* is port configured? */
const struct npcx_i2c_timing_cfg *ptr_speed_confs;
#ifdef CONFIG_I2C_TARGET
struct i2c_target_config *target_cfg[NPCX_I2C_FLAG_COUNT];
uint8_t target_idx; /* current target_cfg index */
atomic_t registered_target_mask;
/* i2c wake-up callback configuration */
struct miwu_callback smb_wk_cb;
#endif /* CONFIG_I2C_TARGET */
#if defined(CONFIG_PM) && defined(CONFIG_I2C_TARGET)
ATOMIC_DEFINE(pm_policy_state_flag, I2C_PM_POLICY_STATE_FLAG_COUNT);
#endif /* CONFIG_PM && CONFIG_I2C_TARGET */
};
/* Driver convenience defines */
#define HAL_I2C_INSTANCE(dev) \
((struct smb_reg *)((const struct i2c_ctrl_config *)(dev)->config)->base)
static inline void i2c_ctrl_start(const struct device *dev)
{
struct smb_reg *const inst = HAL_I2C_INSTANCE(dev);
inst->SMBCTL1 |= BIT(NPCX_SMBCTL1_START);
}
static inline void i2c_ctrl_data_write(const struct device *dev, uint8_t data)
{
struct smb_reg *const inst = HAL_I2C_INSTANCE(dev);
inst->SMBSDA = data;
}
static inline uint8_t i2c_ctrl_data_read(const struct device *dev)
{
struct smb_reg *const inst = HAL_I2C_INSTANCE(dev);
return inst->SMBSDA;
}
static inline void i2c_ctrl_irq_enable(const struct device *dev, int enable)
{
const struct i2c_ctrl_config *const config = dev->config;
if (enable) {
irq_enable(config->irq);
} else {
irq_disable(config->irq);
}
}
static inline void i2c_ctrl_notify(const struct device *dev, int error)
{
struct i2c_ctrl_data *const data = dev->data;
data->trans_err = error;
k_sem_give(&data->sync_sem);
}
static inline bool i2c_ctrl_is_scl_sda_both_high(const struct device *dev)
{
struct smb_reg *const inst = HAL_I2C_INSTANCE(dev);
if (IS_BIT_SET(inst->SMBCTL3, NPCX_SMBCTL3_SCL_LVL) &&
IS_BIT_SET(inst->SMBCTL3, NPCX_SMBCTL3_SDA_LVL)) {
return true;
}
return false;
}
static inline void i2c_ctrl_bank_sel(const struct device *dev, int bank)
{
struct smb_reg *const inst = HAL_I2C_INSTANCE(dev);
/* All DMA registers locate at bank 0 */
if (IS_ENABLED(CONFIG_I2C_NPCX_DMA_DRIVEN)) {
return;
}
if (bank) {
inst->SMBCTL3 |= BIT(NPCX_SMBCTL3_BNK_SEL);
} else {
inst->SMBCTL3 &= ~BIT(NPCX_SMBCTL3_BNK_SEL);
}
}
#if defined(CONFIG_I2C_NPCX_FIFO_DRIVEN)
static inline void i2c_ctrl_fifo_clear_status(const struct device *dev)
{
struct smb_reg *const inst = HAL_I2C_INSTANCE(dev);
inst->SMBFIF_CTS |= BIT(NPCX_SMBFIF_CTS_CLR_FIFO);
}
static inline void i2c_ctrl_fifo_rx_setup_threshold_nack(const struct device *dev, int threshold,
int last)
{
struct smb_reg *const inst = HAL_I2C_INSTANCE(dev);
uint8_t value = MIN(threshold, NPCX_I2C_FIFO_MAX_SIZE);
SET_FIELD(inst->SMBRXF_CTL, NPCX_SMBRXF_CTL_RX_THR, value);
/*
* Is it last received transaction? If so, set LAST bit. Then the
* hardware will generate NACK automatically when receiving last byte.
*/
if (last && (value == threshold)) {
inst->SMBRXF_CTL |= BIT(NPCX_SMBRXF_CTL_LAST);
}
}
#endif
#if defined(CONFIG_I2C_NPCX_DMA_DRIVEN)
static inline void i2c_ctrl_dma_clear_status(const struct device *dev)
{
struct smb_reg *const inst = HAL_I2C_INSTANCE(dev);
/* Clear DMA interrupt bit */
inst->DMA_CTRL |= BIT(NPCX_DMA_CTL_INTCLR);
}
#endif
/**
* @brief Lock the mutex of npcx i2c controller.
*
* @param i2c_dev Pointer to the device structure for i2c controller instance.
*/
void npcx_i2c_ctrl_mutex_lock(const struct device *i2c_dev);
/**
* @brief Unlock the mutex of npcx i2c controller.
*
* @param i2c_dev Pointer to the device structure for i2c controller instance.
*/
void npcx_i2c_ctrl_mutex_unlock(const struct device *i2c_dev);
/**
* @brief Configure operation of a npcx i2c controller.
*
* @param i2c_dev Pointer to the device structure for i2c controller instance.
* @param dev_config Bit-packed 32-bit value to the device runtime configuration
* for the I2C controller.
*
* @retval 0 If successful.
* @retval -EIO General input / output error, failed to configure device.
* @retval -ERANGE Out of supported i2c frequency.
*/
int npcx_i2c_ctrl_configure(const struct device *i2c_dev, uint32_t dev_config);
/**
* @brief Get I2C controller speed.
*
* @param i2c_dev Pointer to the device structure for i2c controller instance.
* @param speed Pointer to store the I2C speed.
*
* @retval 0 If successful.
* @retval -ERANGE Stored I2C frequency out of supported range.
* @retval -EIO Controller is not configured.
*/
int npcx_i2c_ctrl_get_speed(const struct device *i2c_dev, uint32_t *speed);
/**
* @brief Perform data transfer to via npcx i2c controller.
*
* @param i2c_dev Pointer to the device structure for i2c controller instance.
* @param msgs Array of messages to transfer.
* @param num_msgs Number of messages to transfer.
* @param addr Address of the I2C target device.
* @param port Port index of selected i2c port.
*
* @retval 0 If successful.
* @retval -EIO General input / output error.
* @retval -ENXIO No slave address match.
* @retval -ETIMEDOUT Timeout occurred for a i2c transaction.
*/
int npcx_i2c_ctrl_transfer(const struct device *i2c_dev, struct i2c_msg *msgs,
uint8_t num_msgs, uint16_t addr, uint8_t port);
/**
* @brief Toggle the SCL to generate maximum 9 clocks until the target release
* the SDA line and send a STOP condition.
*
* @param i2c_dev Pointer to the device structure for i2c controller instance.
*
* @retval 0 If successful.
* @retval -EBUSY fail to recover the bus.
*/
int npcx_i2c_ctrl_recover_bus(const struct device *dev);
/**
* @brief Registers the provided config as Target device of a npcx i2c controller.
*
* @param i2c_dev Pointer to the device structure for i2c controller instance.
* @param target_cfg Config struct used by the i2c target driver
* @param port Port index of selected i2c port.
*
* @retval 0 Is successful
* @retval -EBUSY If i2c transaction is proceeding.
*/
int npcx_i2c_ctrl_target_register(const struct device *i2c_dev,
struct i2c_target_config *target_cfg, uint8_t port);
/**
* @brief Unregisters the provided config as Target device of a npcx i2c controller.
*
* @param i2c_dev Pointer to the device structure for i2c controller instance.
* @param target_cfg Config struct used by the i2c target driver
* @param port Port index of selected i2c port.
*
* @retval 0 Is successful
* @retval -EBUSY If i2c transaction is proceeding.
* @retval -EINVAL If parameters are invalid
*/
int npcx_i2c_ctrl_target_unregister(const struct device *i2c_dev,
struct i2c_target_config *target_cfg, uint8_t port);
/* Common public fucntions */
bool i2c_ctrl_toggle_scls(const struct device *dev);
size_t i2c_ctrl_calculate_msg_remains(const struct device *dev);
void i2c_ctrl_handle_write_int_event(const struct device *dev);
void i2c_ctrl_handle_read_int_event(const struct device *dev);
void i2c_ctrl_stop(const struct device *dev);
/* FIFO-Driven public fucntions */
#if defined(CONFIG_I2C_NPCX_FIFO_DRIVEN)
void i2c_ctrl_fifo_hold_bus(const struct device *dev, int stall);
#endif
/* DMA-Driven public fucntions */
#if defined(CONFIG_I2C_NPCX_DMA_DRIVEN)
size_t i2c_ctrl_dma_proceed_write(const struct device *dev);
size_t i2c_ctrl_dma_proceed_read(const struct device *dev);
void i2c_ctrl_handle_write_dma_int_event(const struct device *dev);
void i2c_ctrl_handle_read_dma_int_event(const struct device *dev);
#endif
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_DRIVERS_I2C_I2C_NPCX_CONTROLLER_H_ */