drivers: i2c: i2c_dw: Added intel lpss dma support for I2C
Enabled intel LPSS DMA interface using dw common to support usage of internal DMA in LPSS I2C to transfer and receive data. Signed-off-by: Bindu S <bindu.s@intel.com>
This commit is contained in:
parent
292ca93795
commit
877208dc78
4 changed files with 224 additions and 2 deletions
|
@ -12,3 +12,12 @@ config I2C_DW_CLOCK_SPEED
|
|||
int "Set the clock speed for I2C"
|
||||
depends on I2C_DW
|
||||
default 32
|
||||
|
||||
config I2C_DW_LPSS_DMA
|
||||
bool "Use I2C integrated DMA for asynchronous transfer"
|
||||
select DMA
|
||||
select DMA_INTEL_LPSS
|
||||
help
|
||||
This option enables I2C DMA feature to be used for asynchrounous
|
||||
data transfers. All Tx operaton are done using dma channel 0 and
|
||||
all Rx operations are done using dma channel 1.
|
||||
|
|
|
@ -30,6 +30,11 @@
|
|||
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
#if defined(CONFIG_I2C_DW_LPSS_DMA)
|
||||
#include <zephyr/drivers/dma.h>
|
||||
#include <zephyr/drivers/dma/dma_intel_lpss.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IOAPIC
|
||||
#include <zephyr/drivers/interrupt_controller/ioapic.h>
|
||||
#endif
|
||||
|
@ -48,6 +53,147 @@ static inline uint32_t get_regs(const struct device *dev)
|
|||
return (uint32_t)DEVICE_MMIO_GET(dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_I2C_DW_LPSS_DMA
|
||||
void i2c_dw_enable_idma(const struct device *dev, bool enable)
|
||||
{
|
||||
uint32_t reg;
|
||||
uint32_t reg_base = get_regs(dev);
|
||||
|
||||
if (enable) {
|
||||
write_dma_cr(DW_IC_DMA_ENABLE, reg_base);
|
||||
reg = sys_read32(reg_base + DW_IC_REG_DMA_CR);
|
||||
} else {
|
||||
reg = read_dma_cr(reg_base);
|
||||
reg &= ~DW_IC_DMA_ENABLE;
|
||||
write_dma_cr(reg, reg_base);
|
||||
reg = sys_read32(reg_base + DW_IC_REG_DMA_CR);
|
||||
}
|
||||
}
|
||||
|
||||
void cb_i2c_idma_transfer(const struct device *dma, void *user_data,
|
||||
uint32_t channel, int status)
|
||||
{
|
||||
const struct device *dev = (const struct device *)user_data;
|
||||
struct i2c_dw_dev_config *const dw = dev->data;
|
||||
|
||||
dma_stop(dw->dma_dev, channel);
|
||||
i2c_dw_enable_idma(dev, false);
|
||||
|
||||
if (status) {
|
||||
dw->xfr_status = true;
|
||||
} else {
|
||||
dw->xfr_status = false;
|
||||
}
|
||||
}
|
||||
|
||||
void i2c_dw_set_fifo_th(const struct device *dev, uint8_t fifo_depth)
|
||||
{
|
||||
uint32_t reg_base = get_regs(dev);
|
||||
|
||||
write_tdlr(fifo_depth, reg_base);
|
||||
write_rdlr(fifo_depth - 1, reg_base);
|
||||
}
|
||||
|
||||
inline void *i2c_dw_dr_phy_addr(const struct device *dev)
|
||||
{
|
||||
struct i2c_dw_dev_config *const dw = dev->data;
|
||||
|
||||
return (void *) (dw->phy_addr + DW_IC_REG_DATA_CMD);
|
||||
}
|
||||
|
||||
int32_t i2c_dw_idma_rx_transfer(const struct device *dev)
|
||||
{
|
||||
struct i2c_dw_dev_config *const dw = dev->data;
|
||||
|
||||
struct dma_config dma_cfg = { 0 };
|
||||
struct dma_block_config dma_block_cfg = { 0 };
|
||||
|
||||
if (!device_is_ready(dw->dma_dev)) {
|
||||
LOG_DBG("DMA device is not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dma_cfg.dma_slot = 1U;
|
||||
dma_cfg.channel_direction = PERIPHERAL_TO_MEMORY;
|
||||
dma_cfg.source_data_size = 1U;
|
||||
dma_cfg.dest_data_size = 1U;
|
||||
dma_cfg.source_burst_length = 1U;
|
||||
dma_cfg.dest_burst_length = 1U;
|
||||
dma_cfg.dma_callback = cb_i2c_idma_transfer;
|
||||
dma_cfg.user_data = (void *)dev;
|
||||
dma_cfg.complete_callback_en = 0U;
|
||||
dma_cfg.error_callback_en = 1U;
|
||||
dma_cfg.block_count = 1U;
|
||||
dma_cfg.head_block = &dma_block_cfg;
|
||||
|
||||
dma_block_cfg.block_size = dw->xfr_len;
|
||||
dma_block_cfg.dest_address = (uint64_t)&dw->xfr_buf[0];
|
||||
dma_block_cfg.source_address = (uint64_t)i2c_dw_dr_phy_addr(dev);
|
||||
dw->xfr_status = false;
|
||||
|
||||
if (dma_config(dw->dma_dev, DMA_INTEL_LPSS_RX_CHAN, &dma_cfg)) {
|
||||
LOG_DBG("Error transfer");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (dma_start(dw->dma_dev, DMA_INTEL_LPSS_RX_CHAN)) {
|
||||
LOG_DBG("Error transfer");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
i2c_dw_enable_idma(dev, true);
|
||||
i2c_dw_set_fifo_th(dev, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t i2c_dw_idma_tx_transfer(const struct device *dev,
|
||||
uint64_t data)
|
||||
{
|
||||
struct i2c_dw_dev_config *const dw = dev->data;
|
||||
|
||||
struct dma_config dma_cfg = { 0 };
|
||||
struct dma_block_config dma_block_cfg = { 0 };
|
||||
|
||||
if (!device_is_ready(dw->dma_dev)) {
|
||||
LOG_DBG("DMA device is not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dma_cfg.dma_slot = 0U;
|
||||
dma_cfg.channel_direction = MEMORY_TO_PERIPHERAL;
|
||||
dma_cfg.source_data_size = 1U;
|
||||
dma_cfg.dest_data_size = 1U;
|
||||
dma_cfg.source_burst_length = 1U;
|
||||
dma_cfg.dest_burst_length = 1U;
|
||||
dma_cfg.dma_callback = cb_i2c_idma_transfer;
|
||||
dma_cfg.user_data = (void *)dev;
|
||||
dma_cfg.complete_callback_en = 0U;
|
||||
dma_cfg.error_callback_en = 1U;
|
||||
dma_cfg.block_count = 1U;
|
||||
dma_cfg.head_block = &dma_block_cfg;
|
||||
|
||||
dma_block_cfg.block_size = 1;
|
||||
dma_block_cfg.source_address = (uint64_t)&data;
|
||||
dma_block_cfg.dest_address = (uint64_t)i2c_dw_dr_phy_addr(dev);
|
||||
dw->xfr_status = false;
|
||||
|
||||
if (dma_config(dw->dma_dev, DMA_INTEL_LPSS_TX_CHAN, &dma_cfg)) {
|
||||
LOG_DBG("Error transfer");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (dma_start(dw->dma_dev, DMA_INTEL_LPSS_TX_CHAN)) {
|
||||
LOG_DBG("Error trnasfer");
|
||||
return -EIO;
|
||||
}
|
||||
i2c_dw_enable_idma(dev, true);
|
||||
i2c_dw_set_fifo_th(dev, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void i2c_dw_data_ask(const struct device *dev)
|
||||
{
|
||||
struct i2c_dw_dev_config * const dw = dev->data;
|
||||
|
@ -119,6 +265,13 @@ static void i2c_dw_data_read(const struct device *dev)
|
|||
struct i2c_dw_dev_config * const dw = dev->data;
|
||||
uint32_t reg_base = get_regs(dev);
|
||||
|
||||
#ifdef CONFIG_I2C_DW_LPSS_DMA
|
||||
if (test_bit_status_rfne(reg_base) && (dw->xfr_len > 0)) {
|
||||
i2c_dw_idma_rx_transfer(dev);
|
||||
dw->xfr_len = 0;
|
||||
dw->rx_pending = 0;
|
||||
}
|
||||
#else
|
||||
while (test_bit_status_rfne(reg_base) && (dw->xfr_len > 0)) {
|
||||
dw->xfr_buf[0] = (uint8_t)read_cmd_data(reg_base);
|
||||
|
||||
|
@ -130,7 +283,7 @@ static void i2c_dw_data_read(const struct device *dev)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
/* Nothing to receive anymore */
|
||||
if (dw->xfr_len == 0U) {
|
||||
dw->state &= ~I2C_DW_CMD_RECV;
|
||||
|
@ -169,8 +322,11 @@ static int i2c_dw_data_send(const struct device *dev)
|
|||
data |= IC_DATA_CMD_STOP;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_I2C_DW_LPSS_DMA
|
||||
i2c_dw_idma_tx_transfer(dev, data);
|
||||
#else
|
||||
write_cmd_data(data, reg_base);
|
||||
|
||||
#endif
|
||||
dw->xfr_len--;
|
||||
dw->xfr_buf++;
|
||||
|
||||
|
@ -230,6 +386,16 @@ static void i2c_dw_isr(const struct device *port)
|
|||
|
||||
/* Check if we are configured as a master device */
|
||||
if (test_bit_con_master_mode(reg_base)) {
|
||||
#ifdef CONFIG_I2C_DW_LPSS_DMA
|
||||
uint32_t stat = sys_read32(reg_base + IDMA_REG_INTR_STS);
|
||||
|
||||
if (stat & IDMA_TX_RX_CHAN_MASK) {
|
||||
/* Handle the DMA interrupt */
|
||||
dma_intel_lpss_isr(dw->dma_dev);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Bail early if there is any error. */
|
||||
if ((DW_INTR_STAT_TX_ABRT | DW_INTR_STAT_TX_OVER |
|
||||
DW_INTR_STAT_RX_OVER | DW_INTR_STAT_RX_UNDER) &
|
||||
|
@ -879,6 +1045,26 @@ static int i2c_dw_initialize(const struct device *dev)
|
|||
|
||||
device_map(DEVICE_MMIO_RAM_PTR(dev), mbar.phys_addr,
|
||||
mbar.size, K_MEM_CACHE_NONE);
|
||||
|
||||
pcie_set_cmd(rom->pcie->bdf, PCIE_CONF_CMDSTAT_MASTER, true);
|
||||
|
||||
#ifdef CONFIG_I2C_DW_LPSS_DMA
|
||||
size_t nhdls = 0;
|
||||
const device_handle_t *hdls;
|
||||
|
||||
hdls = device_supported_handles_get(dev, &nhdls);
|
||||
dw->dma_dev = device_from_handle(*hdls);
|
||||
|
||||
/* Assign physical & virtual address to dma instance */
|
||||
dw->phy_addr = mbar.phys_addr;
|
||||
dw->base_addr = (uint32_t)(DEVICE_MMIO_GET(dev) + DMA_INTEL_LPSS_OFFSET);
|
||||
sys_write32((uint32_t)dw->phy_addr,
|
||||
DEVICE_MMIO_GET(dev) + DMA_INTEL_LPSS_REMAP_LOW);
|
||||
sys_write32((uint32_t)(dw->phy_addr >> DMA_INTEL_LPSS_ADDR_RIGHT_SHIFT),
|
||||
DEVICE_MMIO_GET(dev) + DMA_INTEL_LPSS_REMAP_HI);
|
||||
LOG_DBG("i2c instance physical addr: [0x%lx], virtual addr: [0x%lx]",
|
||||
dw->phy_addr, dw->base_addr);
|
||||
#endif
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
|
@ -889,6 +1075,7 @@ static int i2c_dw_initialize(const struct device *dev)
|
|||
k_mutex_init(&dw->bus_mutex);
|
||||
|
||||
uint32_t reg_base = get_regs(dev);
|
||||
|
||||
clear_bit_enable_en(reg_base);
|
||||
|
||||
/* verify that we have a valid DesignWare register first */
|
||||
|
|
|
@ -123,6 +123,13 @@ struct i2c_dw_dev_config {
|
|||
uint8_t request_bytes;
|
||||
uint8_t xfr_flags;
|
||||
bool support_hs_mode;
|
||||
#ifdef CONFIG_I2C_DW_LPSS_DMA
|
||||
const struct device *dma_dev;
|
||||
uintptr_t phy_addr;
|
||||
uintptr_t base_addr;
|
||||
/* For dma transfer */
|
||||
bool xfr_status;
|
||||
#endif
|
||||
|
||||
struct i2c_target_config *slave_cfg;
|
||||
};
|
||||
|
|
|
@ -144,14 +144,25 @@ union ic_comp_param_1_register {
|
|||
#define DW_IC_REG_STATUS (0x70)
|
||||
#define DW_IC_REG_TXFLR (0x74)
|
||||
#define DW_IC_REG_RXFLR (0x78)
|
||||
#define DW_IC_REG_DMA_CR (0x88)
|
||||
#define DW_IC_REG_TDLR (0x8C)
|
||||
#define DW_IC_REG_RDLR (0x90)
|
||||
#define DW_IC_REG_FS_SPKLEN (0xA0)
|
||||
#define DW_IC_REG_HS_SPKLEN (0xA4)
|
||||
#define DW_IC_REG_COMP_PARAM_1 (0xF4)
|
||||
#define DW_IC_REG_COMP_TYPE (0xFC)
|
||||
|
||||
#define IDMA_REG_INTR_STS 0xAE8
|
||||
#define IDMA_TX_RX_CHAN_MASK 0x3
|
||||
|
||||
/* CON Bit */
|
||||
#define DW_IC_CON_MASTER_MODE_BIT (0)
|
||||
|
||||
/* DMA control bits */
|
||||
#define DW_IC_DMA_RX_ENABLE BIT(0)
|
||||
#define DW_IC_DMA_TX_ENABLE BIT(1)
|
||||
#define DW_IC_DMA_ENABLE (BIT(0) | BIT(1))
|
||||
|
||||
DEFINE_TEST_BIT_OP(con_master_mode, DW_IC_REG_CON, DW_IC_CON_MASTER_MODE_BIT)
|
||||
DEFINE_MM_REG_WRITE(con, DW_IC_REG_CON, 32)
|
||||
DEFINE_MM_REG_READ(con, DW_IC_REG_CON, 32)
|
||||
|
@ -209,6 +220,14 @@ DEFINE_TEST_BIT_OP(status_rfne, DW_IC_REG_STATUS, DW_IC_STATUS_RFNE_BIT)
|
|||
DEFINE_MM_REG_READ(txflr, DW_IC_REG_TXFLR, 32)
|
||||
DEFINE_MM_REG_READ(rxflr, DW_IC_REG_RXFLR, 32)
|
||||
|
||||
DEFINE_MM_REG_READ(dma_cr, DW_IC_REG_DMA_CR, 32)
|
||||
DEFINE_MM_REG_WRITE(dma_cr, DW_IC_REG_DMA_CR, 32)
|
||||
|
||||
DEFINE_MM_REG_READ(tdlr, DW_IC_REG_TDLR, 32)
|
||||
DEFINE_MM_REG_WRITE(tdlr, DW_IC_REG_TDLR, 32)
|
||||
DEFINE_MM_REG_READ(rdlr, DW_IC_REG_RDLR, 32)
|
||||
DEFINE_MM_REG_WRITE(rdlr, DW_IC_REG_RDLR, 32)
|
||||
|
||||
DEFINE_MM_REG_READ(fs_spklen, DW_IC_REG_FS_SPKLEN, 32)
|
||||
DEFINE_MM_REG_READ(hs_spklen, DW_IC_REG_HS_SPKLEN, 32)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue