Files
msm-5.15/drivers/i2c/busses/i2c-msm-geni.c
aveshala da6f10d4e4 i2c: i2c-msm-geni: i2c-hub support for kalama
I2C master hub is a core which has a group of serial engines and
io_macros, which have reduced capabilities compared to qupv3 serial
engines and IO macros. It supports only I2C protocol and doesn't
support QUP related capabilities like GSI, DMA mode and AHB interface.

This change adds required code changes for i2c hub into existing GENI
i2c driver and takes care of eliminating not supported functions.

Change-Id: I40d3f99068e98ce6930786e0d990f7cc956d151f
Signed-off-by: aveshala <quic_aveshala@quicinc.com>
2022-01-28 05:39:28 -08:00

1668 lines
43 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <linux/qcom-geni-se.h>
#include <linux/qcom-geni-se-common.h>
#include <linux/ipc_logging.h>
#include <linux/dmaengine.h>
#include <linux/msm_gpi.h>
#include <linux/ioctl.h>
#include <linux/pinctrl/consumer.h>
#include <linux/slab.h>
#define SE_I2C_TX_TRANS_LEN (0x26C)
#define SE_I2C_RX_TRANS_LEN (0x270)
#define SE_I2C_SCL_COUNTERS (0x278)
#define SE_I2C_ERR (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |\
M_GP_IRQ_1_EN | M_GP_IRQ_3_EN | M_GP_IRQ_4_EN)
#define SE_I2C_ABORT (1U << 1)
/* M_CMD OP codes for I2C */
#define I2C_WRITE (0x1)
#define I2C_READ (0x2)
#define I2C_WRITE_READ (0x3)
#define I2C_ADDR_ONLY (0x4)
#define I2C_BUS_CLEAR (0x6)
#define I2C_STOP_ON_BUS (0x7)
/* M_CMD params for I2C */
#define PRE_CMD_DELAY (BIT(0))
#define TIMESTAMP_BEFORE (BIT(1))
#define STOP_STRETCH (BIT(2))
#define TIMESTAMP_AFTER (BIT(3))
#define POST_COMMAND_DELAY (BIT(4))
#define IGNORE_ADD_NACK (BIT(6))
#define READ_FINISHED_WITH_ACK (BIT(7))
#define BYPASS_ADDR_PHASE (BIT(8))
#define SLV_ADDR_MSK (GENMASK(15, 9))
#define SLV_ADDR_SHFT (9)
#define I2C_PACK_EN (BIT(0) | BIT(1))
#define GP_IRQ0 0
#define GP_IRQ1 1
#define GP_IRQ2 2
#define GP_IRQ3 3
#define GP_IRQ4 4
#define GP_IRQ5 5
#define GENI_OVERRUN 6
#define GENI_ILLEGAL_CMD 7
#define GENI_ABORT_DONE 8
#define GENI_TIMEOUT 9
#define GENI_HW_PARAM 0x50
#define I2C_NACK GP_IRQ1
#define I2C_BUS_PROTO GP_IRQ3
#define I2C_ARB_LOST GP_IRQ4
#define DM_I2C_CB_ERR ((BIT(GP_IRQ1) | BIT(GP_IRQ3) | BIT(GP_IRQ4)) \
<< 5)
#define I2C_MASTER_HUB (BIT(0))
#define KHz(freq) (1000 * freq)
#define I2C_AUTO_SUSPEND_DELAY 250
#define I2C_TIMEOUT_SAFETY_COEFFICIENT 10
#define I2C_TIMEOUT_MIN_USEC 500000
#define MAX_SE 20
#define I2C_LOG_DBG(log_ctx, print, dev, x...) do { \
if (dev) \
i2c_trace_log(dev, x); \
} while (0)
#define I2C_LOG_ERR(log_ctx, print, dev, x...) do { \
if (dev) \
i2c_trace_log(dev, x); \
} while (0)
#define CREATE_TRACE_POINTS
#include "i2c-qup-trace.h"
/* FTRACE Logging */
void i2c_trace_log(struct device *dev, const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
va_start(args, fmt);
vaf.va = &args;
trace_i2c_log_info(dev_name(dev), &vaf);
va_end(args);
}
enum i2c_se_mode {
UNINITIALIZED,
FIFO_SE_DMA,
GSI_ONLY,
};
struct dbg_buf_ctxt {
void *virt_buf;
void *map_buf;
};
struct geni_i2c_dev {
struct device *dev;
void __iomem *base;
unsigned int tx_wm;
int irq;
int err;
u32 xfer_timeout;
struct i2c_adapter adap;
struct completion xfer;
struct i2c_msg *cur;
struct geni_se i2c_rsc;
struct clk *m_ahb_clk;
struct clk *s_ahb_clk;
int cur_wr;
int cur_rd;
struct device *wrapper_dev;
void *ipcl;
int clk_fld_idx;
struct dma_chan *tx_c;
struct dma_chan *rx_c;
struct msm_gpi_tre lock_t;
struct msm_gpi_tre unlock_t;
struct msm_gpi_tre cfg0_t;
struct msm_gpi_tre go_t;
struct msm_gpi_tre tx_t;
struct msm_gpi_tre rx_t;
dma_addr_t tx_ph;
dma_addr_t rx_ph;
struct msm_gpi_ctrl tx_ev;
struct msm_gpi_ctrl rx_ev;
struct scatterlist tx_sg[5]; /* lock, cfg0, go, TX, unlock */
struct scatterlist rx_sg;
int cfg_sent;
int clk_freq_out;
struct dma_async_tx_descriptor *tx_desc;
struct dma_async_tx_descriptor *rx_desc;
struct msm_gpi_dma_async_tx_cb_param tx_cb;
struct msm_gpi_dma_async_tx_cb_param rx_cb;
enum i2c_se_mode se_mode;
bool cmd_done;
bool is_shared;
u32 dbg_num;
struct dbg_buf_ctxt *dbg_buf_ptr;
bool is_le_vm;
bool req_chan;
bool first_resume;
bool gpi_reset;
bool is_i2c_hub;
};
static struct geni_i2c_dev *gi2c_dev_dbg[MAX_SE];
static int arr_idx;
struct geni_i2c_err_log {
int err;
const char *msg;
};
static struct geni_i2c_err_log gi2c_log[] = {
[GP_IRQ0] = {-EINVAL, "Unknown I2C err GP_IRQ0"},
[I2C_NACK] = {-ENOTCONN,
"NACK: slv unresponsive, check its power/reset-ln"},
[GP_IRQ2] = {-EINVAL, "Unknown I2C err GP IRQ2"},
[I2C_BUS_PROTO] = {-EPROTO,
"Bus proto err, noisy/unepxected start/stop"},
[I2C_ARB_LOST] = {-EBUSY,
"Bus arbitration lost, clock line undriveable"},
[GP_IRQ5] = {-EINVAL, "Unknown I2C err GP IRQ5"},
[GENI_OVERRUN] = {-EIO, "Cmd overrun, check GENI cmd-state machine"},
[GENI_ILLEGAL_CMD] = {-EILSEQ,
"Illegal cmd, check GENI cmd-state machine"},
[GENI_ABORT_DONE] = {-ETIMEDOUT, "Abort after timeout successful"},
[GENI_TIMEOUT] = {-ETIMEDOUT, "I2C TXN timed out"},
};
struct geni_i2c_clk_fld {
u32 clk_freq_out;
u8 clk_div;
u8 t_high;
u8 t_low;
u8 t_cycle;
};
static struct geni_i2c_clk_fld geni_i2c_clk_map[] = {
{KHz(100), 7, 10, 11, 26},
{KHz(400), 2, 7, 10, 24},
{KHz(1000), 1, 3, 9, 18},
};
static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
{
int i;
int ret = 0;
bool clk_map_present = false;
struct geni_i2c_clk_fld *itr = geni_i2c_clk_map;
for (i = 0; i < ARRAY_SIZE(geni_i2c_clk_map); i++, itr++) {
if (itr->clk_freq_out == gi2c->clk_freq_out) {
clk_map_present = true;
break;
}
}
if (clk_map_present)
gi2c->clk_fld_idx = i;
else
ret = -EINVAL;
return ret;
}
/**
* geni_i2c_se_dump_dbg_regs() - Print relevant registers that capture most
* accurately the state of an SE.
* @se: Pointer to the concerned serial engine.
* @iomem: Base address of the SE's register space.
* @ipc: IPC log context handle.
*
* This function is used to print out all the registers that capture the state
* of an SE to help debug any errors.
*
* Return: None
*/
void geni_i2c_se_dump_dbg_regs(struct geni_se *se, void __iomem *base,
void *ipc)
{
u32 m_cmd0 = 0;
u32 m_irq_status = 0;
u32 s_cmd0 = 0;
u32 s_irq_status = 0;
u32 geni_status = 0;
u32 geni_ios = 0;
u32 dma_rx_irq = 0;
u32 dma_tx_irq = 0;
u32 rx_fifo_status = 0;
u32 tx_fifo_status = 0;
u32 se_dma_dbg = 0;
u32 m_cmd_ctrl = 0;
u32 se_dma_rx_len = 0;
u32 se_dma_rx_len_in = 0;
u32 se_dma_tx_len = 0;
u32 se_dma_tx_len_in = 0;
u32 geni_m_irq_en = 0;
u32 geni_s_irq_en = 0;
u32 geni_dma_tx_irq_en = 0;
u32 geni_dma_rx_irq_en = 0;
m_cmd0 = geni_read_reg(base, SE_GENI_M_CMD0);
m_irq_status = geni_read_reg(base, SE_GENI_M_IRQ_STATUS);
s_cmd0 = geni_read_reg(base, SE_GENI_S_CMD0);
s_irq_status = geni_read_reg(base, SE_GENI_S_IRQ_STATUS);
geni_status = geni_read_reg(base, SE_GENI_STATUS);
geni_ios = geni_read_reg(base, SE_GENI_IOS);
dma_tx_irq = geni_read_reg(base, SE_DMA_TX_IRQ_STAT);
dma_rx_irq = geni_read_reg(base, SE_DMA_RX_IRQ_STAT);
rx_fifo_status = geni_read_reg(base, SE_GENI_RX_FIFO_STATUS);
tx_fifo_status = geni_read_reg(base, SE_GENI_TX_FIFO_STATUS);
se_dma_dbg = geni_read_reg(base, SE_DMA_DEBUG_REG0);
m_cmd_ctrl = geni_read_reg(base, SE_GENI_M_CMD_CTRL_REG);
se_dma_rx_len = geni_read_reg(base, SE_DMA_RX_LEN);
se_dma_rx_len_in = geni_read_reg(base, SE_DMA_RX_LEN_IN);
se_dma_tx_len = geni_read_reg(base, SE_DMA_TX_LEN);
se_dma_tx_len_in = geni_read_reg(base, SE_DMA_TX_LEN_IN);
geni_m_irq_en = geni_read_reg(base, SE_GENI_M_IRQ_EN);
geni_s_irq_en = geni_read_reg(base, SE_GENI_S_IRQ_EN);
geni_dma_tx_irq_en = geni_read_reg(base, SE_DMA_TX_IRQ_EN);
geni_dma_rx_irq_en = geni_read_reg(base, SE_DMA_RX_IRQ_EN);
I2C_LOG_DBG(ipc, false, se->dev,
"%s: m_cmd0:0x%x, m_irq_status:0x%x, geni_status:0x%x, geni_ios:0x%x\n",
__func__, m_cmd0, m_irq_status, geni_status, geni_ios);
I2C_LOG_DBG(ipc, false, se->dev,
"dma_rx_irq:0x%x, dma_tx_irq:0x%x, rx_fifo_sts:0x%x, tx_fifo_sts:0x%x\n",
dma_rx_irq, dma_tx_irq, rx_fifo_status, tx_fifo_status);
I2C_LOG_DBG(ipc, false, se->dev,
"se_dma_dbg:0x%x, m_cmd_ctrl:0x%x, dma_rxlen:0x%x, dma_rxlen_in:0x%x\n",
se_dma_dbg, m_cmd_ctrl, se_dma_rx_len, se_dma_rx_len_in);
I2C_LOG_DBG(ipc, false, se->dev,
"dma_txlen:0x%x, dma_txlen_in:0x%x s_irq_status:0x%x\n",
se_dma_tx_len, se_dma_tx_len_in, s_irq_status);
I2C_LOG_DBG(ipc, false, se->dev,
"dma_txirq_en:0x%x, dma_rxirq_en:0x%x geni_m_irq_en:0x%x geni_s_irq_en:0x%x\n",
geni_dma_tx_irq_en, geni_dma_rx_irq_en, geni_m_irq_en,
geni_s_irq_en);
}
static inline void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c, int dfs)
{
struct geni_i2c_clk_fld *itr = geni_i2c_clk_map + gi2c->clk_fld_idx;
geni_write_reg(dfs, gi2c->base, SE_GENI_CLK_SEL);
geni_write_reg((itr->clk_div << 4) | 1, gi2c->base, GENI_SER_M_CLK_CFG);
geni_write_reg(((itr->t_high << 20) | (itr->t_low << 10) |
itr->t_cycle), gi2c->base, SE_I2C_SCL_COUNTERS);
/*
* Ensure Clk config completes before return.
*/
mb();
}
static inline void qcom_geni_i2c_calc_timeout(struct geni_i2c_dev *gi2c)
{
struct geni_i2c_clk_fld *clk_itr = geni_i2c_clk_map + gi2c->clk_fld_idx;
size_t bit_cnt = gi2c->cur->len*9;
size_t bit_usec = (bit_cnt*USEC_PER_SEC)/clk_itr->clk_freq_out;
size_t xfer_max_usec = (bit_usec*I2C_TIMEOUT_SAFETY_COEFFICIENT) +
I2C_TIMEOUT_MIN_USEC;
gi2c->xfer_timeout = usecs_to_jiffies(xfer_max_usec);
}
static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
{
if (err == I2C_NACK || err == GENI_ABORT_DONE) {
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev, "%s\n",
gi2c_log[err].msg);
goto err_ret;
} else {
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev, "%s\n",
gi2c_log[err].msg);
}
geni_i2c_se_dump_dbg_regs(&gi2c->i2c_rsc, gi2c->base, gi2c->ipcl);
err_ret:
gi2c->err = gi2c_log[err].err;
}
static int geni_i2c_prepare(struct geni_i2c_dev *gi2c)
{
if (gi2c->se_mode == UNINITIALIZED) {
int proto = geni_se_read_proto(&gi2c->i2c_rsc);
u32 se_mode;
if (proto != GENI_SE_I2C) {
dev_err(gi2c->dev, "Invalid proto %d\n", proto);
if (!gi2c->is_le_vm) {
geni_se_resources_off(&gi2c->i2c_rsc);
geni_icc_disable(&gi2c->i2c_rsc);
}
return -ENXIO;
}
se_mode = readl_relaxed(gi2c->base +
GENI_IF_DISABLE_RO);
if (se_mode) {
gi2c->se_mode = GSI_ONLY;
geni_se_select_mode(&gi2c->i2c_rsc, GENI_GPI_DMA);
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev,
"i2c in GSI ONLY mode\n");
} else {
int gi2c_tx_depth;
if (!gi2c->is_i2c_hub)
gi2c_tx_depth = geni_se_get_tx_fifo_depth(&gi2c->i2c_rsc);
else
gi2c_tx_depth = 16; /* i2c hub depth is fixed to 16 */
gi2c->se_mode = FIFO_SE_DMA;
gi2c->tx_wm = gi2c_tx_depth - 1;
geni_se_init(&gi2c->i2c_rsc, gi2c->tx_wm, gi2c_tx_depth);
qcom_geni_i2c_conf(gi2c, 0);
geni_se_config_packing(&gi2c->i2c_rsc, 8, 4, true, true, true);
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev,
"i2c fifo/se-dma mode. fifo depth:%d\n",
gi2c_tx_depth);
}
}
return 0;
}
static irqreturn_t geni_i2c_irq(int irq, void *dev)
{
struct geni_i2c_dev *gi2c = dev;
int i, j;
u32 m_stat = readl_relaxed(gi2c->base + SE_GENI_M_IRQ_STATUS);
u32 rx_st = readl_relaxed(gi2c->base + SE_GENI_RX_FIFO_STATUS);
u32 dm_tx_st = readl_relaxed(gi2c->base + SE_DMA_TX_IRQ_STAT);
u32 dm_rx_st = readl_relaxed(gi2c->base + SE_DMA_RX_IRQ_STAT);
u32 dma = readl_relaxed(gi2c->base + SE_GENI_DMA_MODE_EN);
struct i2c_msg *cur = gi2c->cur;
if (!cur) {
geni_i2c_se_dump_dbg_regs(&gi2c->i2c_rsc, gi2c->base, gi2c->ipcl);
I2C_LOG_ERR(gi2c->ipcl, false, gi2c->dev, "Spurious irq\n");
goto irqret;
}
if ((m_stat & M_CMD_FAILURE_EN) ||
(dm_rx_st & (DM_I2C_CB_ERR)) ||
(m_stat & M_CMD_CANCEL_EN) ||
(m_stat & M_CMD_ABORT_EN)) {
if (m_stat & M_GP_IRQ_1_EN)
geni_i2c_err(gi2c, I2C_NACK);
if (m_stat & M_GP_IRQ_3_EN)
geni_i2c_err(gi2c, I2C_BUS_PROTO);
if (m_stat & M_GP_IRQ_4_EN)
geni_i2c_err(gi2c, I2C_ARB_LOST);
if (m_stat & M_CMD_OVERRUN_EN)
geni_i2c_err(gi2c, GENI_OVERRUN);
if (m_stat & M_ILLEGAL_CMD_EN)
geni_i2c_err(gi2c, GENI_ILLEGAL_CMD);
if (m_stat & M_CMD_ABORT_EN)
geni_i2c_err(gi2c, GENI_ABORT_DONE);
if (m_stat & M_GP_IRQ_0_EN)
geni_i2c_err(gi2c, GP_IRQ0);
if (!dma)
writel_relaxed(0, (gi2c->base +
SE_GENI_TX_WATERMARK_REG));
gi2c->cmd_done = true;
goto irqret;
}
if (((m_stat & M_RX_FIFO_WATERMARK_EN) ||
(m_stat & M_RX_FIFO_LAST_EN)) && (cur->flags & I2C_M_RD)) {
u32 rxcnt = rx_st & RX_FIFO_WC_MSK;
for (j = 0; j < rxcnt; j++) {
u32 temp;
int p;
temp = readl_relaxed(gi2c->base + SE_GENI_RX_FIFOn);
for (i = gi2c->cur_rd, p = 0; (i < cur->len && p < 4);
i++, p++)
cur->buf[i] = (u8) ((temp >> (p * 8)) & 0xff);
gi2c->cur_rd = i;
if (gi2c->cur_rd == cur->len) {
dev_dbg(gi2c->dev, "FIFO i:%d,read 0x%x\n",
i, temp);
break;
}
}
} else if ((m_stat & M_TX_FIFO_WATERMARK_EN) &&
!(cur->flags & I2C_M_RD)) {
for (j = 0; j < gi2c->tx_wm; j++) {
u32 temp = 0;
int p;
for (i = gi2c->cur_wr, p = 0; (i < cur->len && p < 4);
i++, p++)
temp |= (((u32)(cur->buf[i]) << (p * 8)));
writel_relaxed(temp, gi2c->base + SE_GENI_TX_FIFOn);
gi2c->cur_wr = i;
dev_dbg(gi2c->dev, "FIFO i:%d,wrote 0x%x\n", i, temp);
if (gi2c->cur_wr == cur->len) {
dev_dbg(gi2c->dev, "FIFO i2c bytes done writing\n");
writel_relaxed(0,
(gi2c->base + SE_GENI_TX_WATERMARK_REG));
break;
}
}
}
irqret:
if (m_stat)
writel_relaxed(m_stat, gi2c->base + SE_GENI_M_IRQ_CLEAR);
if (dma) {
if (dm_tx_st)
writel_relaxed(dm_tx_st, gi2c->base +
SE_DMA_TX_IRQ_CLR);
if (dm_rx_st)
writel_relaxed(dm_rx_st, gi2c->base +
SE_DMA_RX_IRQ_CLR);
/* Ensure all writes are done before returning from ISR. */
wmb();
if ((dm_tx_st & TX_DMA_DONE) || (dm_rx_st & RX_DMA_DONE))
gi2c->cmd_done = true;
}
else if (m_stat & M_CMD_DONE_EN)
gi2c->cmd_done = true;
if (gi2c->cmd_done) {
gi2c->cmd_done = false;
complete(&gi2c->xfer);
}
return IRQ_HANDLED;
}
static void gi2c_ev_cb(struct dma_chan *ch, struct msm_gpi_cb const *cb_str,
void *ptr)
{
struct geni_i2c_dev *gi2c = ptr;
u32 m_stat = cb_str->status;
switch (cb_str->cb_event) {
case MSM_GPI_QUP_ERROR:
case MSM_GPI_QUP_SW_ERROR:
case MSM_GPI_QUP_MAX_EVENT:
case MSM_GPI_QUP_CH_ERROR:
case MSM_GPI_QUP_FW_ERROR:
case MSM_GPI_QUP_PENDING_EVENT:
case MSM_GPI_QUP_EOT_DESC_MISMATCH:
break;
case MSM_GPI_QUP_NOTIFY:
if (m_stat & M_GP_IRQ_1_EN)
geni_i2c_err(gi2c, I2C_NACK);
if (m_stat & M_GP_IRQ_3_EN)
geni_i2c_err(gi2c, I2C_BUS_PROTO);
if (m_stat & M_GP_IRQ_4_EN)
geni_i2c_err(gi2c, I2C_ARB_LOST);
complete(&gi2c->xfer);
break;
default:
break;
}
if (cb_str->cb_event != MSM_GPI_QUP_NOTIFY)
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"GSI QN err:0x%x, status:0x%x, err:%d\n",
cb_str->error_log.error_code,
m_stat, cb_str->cb_event);
}
static void gi2c_gsi_cb_err(struct msm_gpi_dma_async_tx_cb_param *cb,
char *xfer)
{
struct geni_i2c_dev *gi2c = cb->userdata;
if (cb->status & DM_I2C_CB_ERR) {
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev,
"%s TCE Unexpected Err, stat:0x%x\n",
xfer, cb->status);
if (cb->status & (BIT(GP_IRQ1) << 5))
geni_i2c_err(gi2c, I2C_NACK);
if (cb->status & (BIT(GP_IRQ3) << 5))
geni_i2c_err(gi2c, I2C_BUS_PROTO);
if (cb->status & (BIT(GP_IRQ4) << 5))
geni_i2c_err(gi2c, I2C_ARB_LOST);
}
}
static void gi2c_gsi_tx_cb(void *ptr)
{
struct msm_gpi_dma_async_tx_cb_param *tx_cb = ptr;
struct geni_i2c_dev *gi2c = tx_cb->userdata;
gi2c_gsi_cb_err(tx_cb, "TX");
complete(&gi2c->xfer);
}
static void gi2c_gsi_rx_cb(void *ptr)
{
struct msm_gpi_dma_async_tx_cb_param *rx_cb = ptr;
struct geni_i2c_dev *gi2c = rx_cb->userdata;
if (gi2c->cur->flags & I2C_M_RD) {
gi2c_gsi_cb_err(rx_cb, "RX");
complete(&gi2c->xfer);
}
}
static int geni_i2c_gsi_request_channel(struct geni_i2c_dev *gi2c)
{
int ret = 0;
if (!gi2c->tx_c) {
gi2c->tx_c = dma_request_slave_channel(gi2c->dev, "tx");
if (!gi2c->tx_c) {
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"tx dma req slv chan ret :%d\n", ret);
return -EIO;
}
}
if (!gi2c->rx_c) {
gi2c->rx_c = dma_request_slave_channel(gi2c->dev, "rx");
if (!gi2c->rx_c) {
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"rx dma req slv chan ret :%d\n", ret);
dma_release_channel(gi2c->tx_c);
return -EIO;
}
}
gi2c->tx_ev.init.callback = gi2c_ev_cb;
gi2c->tx_ev.init.cb_param = gi2c;
gi2c->tx_ev.cmd = MSM_GPI_INIT;
gi2c->tx_c->private = &gi2c->tx_ev;
ret = dmaengine_slave_config(gi2c->tx_c, NULL);
if (ret) {
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"tx dma slave config ret :%d\n", ret);
goto dmaengine_slave_config_fail;
}
gi2c->rx_ev.init.cb_param = gi2c;
gi2c->rx_ev.init.callback = gi2c_ev_cb;
gi2c->rx_ev.cmd = MSM_GPI_INIT;
gi2c->rx_c->private = &gi2c->rx_ev;
ret = dmaengine_slave_config(gi2c->rx_c, NULL);
if (ret) {
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"rx dma slave config ret :%d\n", ret);
goto dmaengine_slave_config_fail;
}
gi2c->tx_cb.userdata = gi2c;
gi2c->rx_cb.userdata = gi2c;
gi2c->req_chan = true;
return ret;
dmaengine_slave_config_fail:
dma_release_channel(gi2c->tx_c);
dma_release_channel(gi2c->rx_c);
gi2c->tx_c = NULL;
gi2c->rx_c = NULL;
return ret;
}
static struct msm_gpi_tre *setup_lock_tre(struct geni_i2c_dev *gi2c)
{
struct msm_gpi_tre *lock_t = &gi2c->lock_t;
/* lock: chain bit set */
lock_t->dword[0] = MSM_GPI_LOCK_TRE_DWORD0;
lock_t->dword[1] = MSM_GPI_LOCK_TRE_DWORD1;
lock_t->dword[2] = MSM_GPI_LOCK_TRE_DWORD2;
/* ieob for le-vm and chain for shared se */
if (gi2c->is_shared)
lock_t->dword[3] = MSM_GPI_LOCK_TRE_DWORD3(0, 0, 0, 0, 1);
else if (gi2c->is_le_vm)
lock_t->dword[3] = MSM_GPI_LOCK_TRE_DWORD3(0, 0, 0, 1, 0);
return lock_t;
}
static struct msm_gpi_tre *setup_cfg0_tre(struct geni_i2c_dev *gi2c)
{
struct geni_i2c_clk_fld *itr = geni_i2c_clk_map +
gi2c->clk_fld_idx;
struct msm_gpi_tre *cfg0_t = &gi2c->cfg0_t;
/* config0 */
cfg0_t->dword[0] = MSM_GPI_I2C_CONFIG0_TRE_DWORD0(I2C_PACK_EN,
itr->t_cycle, itr->t_high, itr->t_low);
cfg0_t->dword[1] = MSM_GPI_I2C_CONFIG0_TRE_DWORD1(0, 0);
cfg0_t->dword[2] = MSM_GPI_I2C_CONFIG0_TRE_DWORD2(0,
itr->clk_div);
cfg0_t->dword[3] = MSM_GPI_I2C_CONFIG0_TRE_DWORD3(0, 0, 0, 0, 1);
return cfg0_t;
}
static struct msm_gpi_tre *setup_go_tre(struct geni_i2c_dev *gi2c,
struct i2c_msg msgs[], int i, int num)
{
struct msm_gpi_tre *go_t = &gi2c->go_t;
u8 op = (msgs[i].flags & I2C_M_RD) ? 2 : 1;
int stretch = (i < (num - 1));
go_t->dword[0] = MSM_GPI_I2C_GO_TRE_DWORD0((stretch << 2),
msgs[i].addr, op);
go_t->dword[1] = MSM_GPI_I2C_GO_TRE_DWORD1;
if (msgs[i].flags & I2C_M_RD) {
go_t->dword[2] = MSM_GPI_I2C_GO_TRE_DWORD2(msgs[i].len);
/*
* For Rx Go tre: Set ieob for non-shared se and for all
* but last transfer in shared se
*/
if (!gi2c->is_shared || (gi2c->is_shared && i != num-1))
go_t->dword[3] = MSM_GPI_I2C_GO_TRE_DWORD3(1, 0,
0, 1, 0);
else
go_t->dword[3] = MSM_GPI_I2C_GO_TRE_DWORD3(1, 0,
0, 0, 0);
} else {
/* For Tx Go tre: ieob is not set, chain bit is set */
go_t->dword[2] = MSM_GPI_I2C_GO_TRE_DWORD2(0);
go_t->dword[3] = MSM_GPI_I2C_GO_TRE_DWORD3(0, 0, 0, 0,
1);
}
return go_t;
}
static struct msm_gpi_tre *setup_rx_tre(struct geni_i2c_dev *gi2c,
struct i2c_msg msgs[], int i, int num)
{
struct msm_gpi_tre *rx_t = &gi2c->rx_t;
rx_t->dword[0] = MSM_GPI_DMA_W_BUFFER_TRE_DWORD0(gi2c->rx_ph);
rx_t->dword[1] = MSM_GPI_DMA_W_BUFFER_TRE_DWORD1(gi2c->rx_ph);
rx_t->dword[2] = MSM_GPI_DMA_W_BUFFER_TRE_DWORD2(msgs[i].len);
/* Set ieot for all Rx/Tx DMA tres */
rx_t->dword[3] = MSM_GPI_DMA_W_BUFFER_TRE_DWORD3(0, 0, 1, 0, 0);
return rx_t;
}
static struct msm_gpi_tre *setup_tx_tre(struct geni_i2c_dev *gi2c,
struct i2c_msg msgs[], int i, int num)
{
struct msm_gpi_tre *tx_t = &gi2c->tx_t;
tx_t->dword[0] = MSM_GPI_DMA_W_BUFFER_TRE_DWORD0(gi2c->tx_ph);
tx_t->dword[1] = MSM_GPI_DMA_W_BUFFER_TRE_DWORD1(gi2c->tx_ph);
tx_t->dword[2] = MSM_GPI_DMA_W_BUFFER_TRE_DWORD2(msgs[i].len);
if (gi2c->is_shared && i == num-1)
/*
* For Tx: unlock tre is send for last transfer
* so set chain bit for last transfer DMA tre.
*/
tx_t->dword[3] =
MSM_GPI_DMA_W_BUFFER_TRE_DWORD3(0, 0, 1, 0, 1);
else
tx_t->dword[3] =
MSM_GPI_DMA_W_BUFFER_TRE_DWORD3(0, 0, 1, 0, 0);
return tx_t;
}
static struct msm_gpi_tre *setup_unlock_tre(struct geni_i2c_dev *gi2c)
{
struct msm_gpi_tre *unlock_t = &gi2c->unlock_t;
/* unlock tre: ieob set */
unlock_t->dword[0] = MSM_GPI_UNLOCK_TRE_DWORD0;
unlock_t->dword[1] = MSM_GPI_UNLOCK_TRE_DWORD1;
unlock_t->dword[2] = MSM_GPI_UNLOCK_TRE_DWORD2;
unlock_t->dword[3] = MSM_GPI_UNLOCK_TRE_DWORD3(0, 0, 0, 1, 0);
return unlock_t;
}
static struct dma_async_tx_descriptor *geni_i2c_prep_desc
(struct geni_i2c_dev *gi2c, struct dma_chan *chan, int segs, bool tx_chan)
{
struct dma_async_tx_descriptor *geni_desc = NULL;
if (tx_chan) {
geni_desc = dmaengine_prep_slave_sg(gi2c->tx_c, gi2c->tx_sg,
segs, DMA_MEM_TO_DEV,
(DMA_PREP_INTERRUPT | DMA_CTRL_ACK));
if (!geni_desc) {
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"prep_slave_sg for tx failed\n");
gi2c->err = -ENOMEM;
return NULL;
}
geni_desc->callback = gi2c_gsi_tx_cb;
geni_desc->callback_param = &gi2c->tx_cb;
} else {
geni_desc = dmaengine_prep_slave_sg(gi2c->rx_c,
&gi2c->rx_sg, 1, DMA_DEV_TO_MEM,
(DMA_PREP_INTERRUPT | DMA_CTRL_ACK));
if (!geni_desc) {
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"prep_slave_sg for rx failed\n");
gi2c->err = -ENOMEM;
return NULL;
}
geni_desc->callback = gi2c_gsi_rx_cb;
geni_desc->callback_param = &gi2c->rx_cb;
}
return geni_desc;
}
static int geni_i2c_lock_bus(struct geni_i2c_dev *gi2c)
{
struct msm_gpi_tre *lock_t = NULL;
int ret = 0, timeout = 0;
dma_cookie_t tx_cookie;
bool tx_chan = true;
if (!gi2c->req_chan) {
ret = geni_i2c_gsi_request_channel(gi2c);
if (ret)
return ret;
}
lock_t = setup_lock_tre(gi2c);
sg_init_table(gi2c->tx_sg, 1);
sg_set_buf(&gi2c->tx_sg[0], lock_t,
sizeof(gi2c->lock_t));
gi2c->tx_desc = geni_i2c_prep_desc(gi2c, gi2c->tx_c, 1, tx_chan);
if (!gi2c->tx_desc) {
gi2c->err = -ENOMEM;
goto geni_i2c_err_lock_bus;
}
reinit_completion(&gi2c->xfer);
/* Issue TX */
tx_cookie = dmaengine_submit(gi2c->tx_desc);
dma_async_issue_pending(gi2c->tx_c);
timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
if (!timeout) {
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"%s timedout\n", __func__);
geni_i2c_se_dump_dbg_regs(&gi2c->i2c_rsc, gi2c->base,
gi2c->ipcl);
gi2c->err = -ETIMEDOUT;
goto geni_i2c_err_lock_bus;
}
return 0;
geni_i2c_err_lock_bus:
if (gi2c->err) {
dmaengine_terminate_all(gi2c->tx_c);
gi2c->cfg_sent = 0;
}
return gi2c->err;
}
static void geni_i2c_unlock_bus(struct geni_i2c_dev *gi2c)
{
struct msm_gpi_tre *unlock_t = NULL;
int timeout = 0;
dma_cookie_t tx_cookie;
bool tx_chan = true;
if (gi2c->gpi_reset)
goto geni_i2c_err_unlock_bus;
unlock_t = setup_unlock_tre(gi2c);
sg_init_table(gi2c->tx_sg, 1);
sg_set_buf(&gi2c->tx_sg[0], unlock_t,
sizeof(gi2c->unlock_t));
gi2c->tx_desc = geni_i2c_prep_desc(gi2c, gi2c->tx_c, 1, tx_chan);
if (!gi2c->tx_desc) {
gi2c->err = -ENOMEM;
goto geni_i2c_err_unlock_bus;
}
reinit_completion(&gi2c->xfer);
/* Issue TX */
tx_cookie = dmaengine_submit(gi2c->tx_desc);
dma_async_issue_pending(gi2c->tx_c);
timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
if (!timeout) {
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"%s failed\n", __func__);
geni_i2c_se_dump_dbg_regs(&gi2c->i2c_rsc, gi2c->base,
gi2c->ipcl);
gi2c->err = -ETIMEDOUT;
goto geni_i2c_err_unlock_bus;
}
geni_i2c_err_unlock_bus:
if (gi2c->gpi_reset || gi2c->err) {
dmaengine_terminate_all(gi2c->tx_c);
gi2c->cfg_sent = 0;
gi2c->err = 0;
gi2c->gpi_reset = false;
}
}
static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
int num)
{
struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
int i, ret = 0, timeout = 0;
struct msm_gpi_tre *lock_t = NULL;
struct msm_gpi_tre *unlock_t = NULL;
struct msm_gpi_tre *cfg0_t = NULL;
if (!gi2c->req_chan) {
ret = geni_i2c_gsi_request_channel(gi2c);
if (ret)
return ret;
}
if (gi2c->is_shared) {
lock_t = setup_lock_tre(gi2c);
unlock_t = setup_unlock_tre(gi2c);
}
if (!gi2c->cfg_sent)
cfg0_t = setup_cfg0_tre(gi2c);
for (i = 0; i < num; i++) {
u8 op = (msgs[i].flags & I2C_M_RD) ? 2 : 1;
int segs = 3 - op;
int index = 0;
u8 *dma_buf = NULL;
dma_cookie_t tx_cookie, rx_cookie;
struct msm_gpi_tre *go_t = NULL;
struct msm_gpi_tre *rx_t = NULL;
struct msm_gpi_tre *tx_t = NULL;
struct device *rx_dev = gi2c->wrapper_dev;
struct device *tx_dev = gi2c->wrapper_dev;
bool tx_chan = true;
reinit_completion(&gi2c->xfer);
gi2c->cur = &msgs[i];
dma_buf = i2c_get_dma_safe_msg_buf(&msgs[i], 1);
if (!dma_buf) {
ret = -ENOMEM;
goto geni_i2c_gsi_xfer_out;
}
qcom_geni_i2c_calc_timeout(gi2c);
if (!gi2c->cfg_sent)
segs++;
if (gi2c->is_shared && (i == 0 || i == num-1)) {
segs++;
if (num == 1)
segs++;
sg_init_table(gi2c->tx_sg, segs);
if (i == 0)
/* Send lock tre for first transfer in a msg */
sg_set_buf(&gi2c->tx_sg[index++], lock_t,
sizeof(gi2c->lock_t));
} else {
sg_init_table(gi2c->tx_sg, segs);
}
/* Send cfg tre when cfg not sent already */
if (!gi2c->cfg_sent) {
sg_set_buf(&gi2c->tx_sg[index++], cfg0_t,
sizeof(gi2c->cfg0_t));
gi2c->cfg_sent = 1;
}
go_t = setup_go_tre(gi2c, msgs, i, num);
sg_set_buf(&gi2c->tx_sg[index++], go_t,
sizeof(gi2c->go_t));
if (msgs[i].flags & I2C_M_RD) {
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev,
"msg[%d].len:%d R\n", i, gi2c->cur->len);
sg_init_table(&gi2c->rx_sg, 1);
ret = geni_se_common_iommu_map_buf(rx_dev, &gi2c->rx_ph,
dma_buf, msgs[i].len,
DMA_FROM_DEVICE);
if (ret) {
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"geni_se_common_iommu_map_buf for rx failed :%d\n",
ret);
i2c_put_dma_safe_msg_buf(dma_buf, &msgs[i],
false);
goto geni_i2c_gsi_xfer_out;
} else if (gi2c->dbg_buf_ptr) {
gi2c->dbg_buf_ptr[i].virt_buf =
(void *)dma_buf;
gi2c->dbg_buf_ptr[i].map_buf =
(void *)&gi2c->rx_ph;
}
rx_t = setup_rx_tre(gi2c, msgs, i, num);
sg_set_buf(&gi2c->rx_sg, rx_t,
sizeof(gi2c->rx_t));
gi2c->rx_desc =
geni_i2c_prep_desc(gi2c, gi2c->rx_c, segs, !tx_chan);
if (!gi2c->rx_desc) {
gi2c->err = -ENOMEM;
goto geni_i2c_err_prep_sg;
}
/* Issue RX */
rx_cookie = dmaengine_submit(gi2c->rx_desc);
dma_async_issue_pending(gi2c->rx_c);
} else {
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev,
"msg[%d].len:%d W\n", i, gi2c->cur->len);
ret = geni_se_common_iommu_map_buf(tx_dev, &gi2c->tx_ph,
dma_buf, msgs[i].len,
DMA_TO_DEVICE);
if (ret) {
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"geni_se_common_iommu_map_buf for tx failed :%d\n",
ret);
i2c_put_dma_safe_msg_buf(dma_buf, &msgs[i],
false);
goto geni_i2c_gsi_xfer_out;
} else if (gi2c->dbg_buf_ptr) {
gi2c->dbg_buf_ptr[i].virt_buf =
(void *)dma_buf;
gi2c->dbg_buf_ptr[i].map_buf =
(void *)&gi2c->tx_ph;
}
tx_t = setup_tx_tre(gi2c, msgs, i, num);
sg_set_buf(&gi2c->tx_sg[index++], tx_t,
sizeof(gi2c->tx_t));
}
if (gi2c->is_shared && i == num-1) {
/* Send unlock tre at the end of last transfer */
sg_set_buf(&gi2c->tx_sg[index++],
unlock_t, sizeof(gi2c->unlock_t));
}
gi2c->tx_desc =
geni_i2c_prep_desc(gi2c, gi2c->tx_c, segs, tx_chan);
if (!gi2c->tx_desc) {
gi2c->err = -ENOMEM;
goto geni_i2c_err_prep_sg;
}
/* Issue TX */
tx_cookie = dmaengine_submit(gi2c->tx_desc);
dma_async_issue_pending(gi2c->tx_c);
timeout = wait_for_completion_timeout(&gi2c->xfer,
gi2c->xfer_timeout);
if (!timeout) {
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"I2C gsi xfer timeout:%u flags:%d addr:0x%x\n",
gi2c->xfer_timeout, gi2c->cur->flags,
gi2c->cur->addr);
geni_i2c_se_dump_dbg_regs(&gi2c->i2c_rsc, gi2c->base,
gi2c->ipcl);
gi2c->err = -ETIMEDOUT;
}
geni_i2c_err_prep_sg:
if (gi2c->err) {
if (!gi2c->is_le_vm) {
dmaengine_terminate_all(gi2c->tx_c);
gi2c->cfg_sent = 0;
} else {
/* Stop channel in case of error in LE-VM */
ret = dmaengine_pause(gi2c->tx_c);
if (ret) {
gi2c->gpi_reset = true;
gi2c->err = ret;
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"Channel cancel failed\n");
goto geni_i2c_gsi_xfer_out;
}
}
}
if (gi2c->is_shared)
/* Resend cfg tre for every new message on shared se */
gi2c->cfg_sent = 0;
if (msgs[i].flags & I2C_M_RD)
geni_se_common_iommu_unmap_buf(rx_dev, &gi2c->rx_ph,
msgs[i].len, DMA_FROM_DEVICE);
else
geni_se_common_iommu_unmap_buf(tx_dev, &gi2c->tx_ph,
msgs[i].len, DMA_TO_DEVICE);
i2c_put_dma_safe_msg_buf(dma_buf, &msgs[i], !gi2c->err);
if (gi2c->err)
goto geni_i2c_gsi_xfer_out;
}
geni_i2c_gsi_xfer_out:
if (!ret && gi2c->err)
ret = gi2c->err;
return ret;
}
static int geni_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg msgs[],
int num)
{
struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
int i, ret = 0, timeout = 0;
gi2c->err = 0;
/* Client to respect system suspend */
if (!pm_runtime_enabled(gi2c->dev)) {
I2C_LOG_ERR(gi2c->ipcl, false, gi2c->dev,
"%s: System suspended\n", __func__);
return -EACCES;
}
if (!gi2c->is_le_vm) {
ret = pm_runtime_get_sync(gi2c->dev);
if (ret < 0) {
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"error turning SE resources:%d\n", ret);
pm_runtime_put_noidle(gi2c->dev);
/* Set device in suspended since resume failed */
pm_runtime_set_suspended(gi2c->dev);
return ret;
}
}
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev,
"n:%d addr:0x%x\n", num, msgs[0].addr);
gi2c->dbg_num = num;
kfree(gi2c->dbg_buf_ptr);
gi2c->dbg_buf_ptr =
kcalloc(num, sizeof(struct dbg_buf_ctxt), GFP_KERNEL);
if (!gi2c->dbg_buf_ptr)
I2C_LOG_ERR(gi2c->ipcl, false, gi2c->dev,
"Buf logging pointer not available\n");
if (gi2c->se_mode == GSI_ONLY) {
ret = geni_i2c_gsi_xfer(adap, msgs, num);
goto geni_i2c_txn_ret;
} else {
/* Don't set shared flag in non-GSI mode */
gi2c->is_shared = false;
}
for (i = 0; i < num; i++) {
int stretch = (i < (num - 1));
u32 m_param = 0;
u32 m_cmd = 0;
u8 *dma_buf = NULL;
dma_addr_t tx_dma = 0;
dma_addr_t rx_dma = 0;
enum geni_se_xfer_mode mode = GENI_SE_FIFO;
reinit_completion(&gi2c->xfer);
m_param |= (stretch ? STOP_STRETCH : 0);
m_param |= ((msgs[i].addr & 0x7F) << SLV_ADDR_SHFT);
gi2c->cur = &msgs[i];
qcom_geni_i2c_calc_timeout(gi2c);
if (!gi2c->is_i2c_hub)
mode = msgs[i].len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
else
mode = GENI_SE_FIFO; /* i2c hub has only FIFO mode */
geni_se_select_mode(&gi2c->i2c_rsc, mode);
if (mode == GENI_SE_DMA) {
dma_buf = i2c_get_dma_safe_msg_buf(&msgs[i], 1);
if (!dma_buf) {
ret = -ENOMEM;
goto geni_i2c_txn_ret;
}
}
if (msgs[i].flags & I2C_M_RD) {
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev,
"msgs[%d].len:%d R\n", i, msgs[i].len);
geni_write_reg(msgs[i].len,
gi2c->base, SE_I2C_RX_TRANS_LEN);
m_cmd = I2C_READ;
geni_se_setup_m_cmd(&gi2c->i2c_rsc, m_cmd, m_param);
if (mode == GENI_SE_DMA) {
ret = geni_se_rx_dma_prep(&gi2c->i2c_rsc,
dma_buf, msgs[i].len,
&rx_dma);
if (ret) {
i2c_put_dma_safe_msg_buf(dma_buf,
&msgs[i], false);
mode = GENI_SE_FIFO;
geni_se_select_mode(&gi2c->i2c_rsc,
mode);
} else if (gi2c->dbg_buf_ptr) {
gi2c->dbg_buf_ptr[i].virt_buf =
(void *)dma_buf;
gi2c->dbg_buf_ptr[i].map_buf =
(void *)&rx_dma;
}
}
} else {
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev,
"msgs[%d].len:%d W\n", i, msgs[i].len);
geni_write_reg(msgs[i].len, gi2c->base,
SE_I2C_TX_TRANS_LEN);
m_cmd = I2C_WRITE;
geni_se_setup_m_cmd(&gi2c->i2c_rsc, m_cmd, m_param);
if (mode == GENI_SE_DMA) {
ret = geni_se_tx_dma_prep(&gi2c->i2c_rsc,
dma_buf, msgs[i].len,
&tx_dma);
if (ret) {
i2c_put_dma_safe_msg_buf(dma_buf,
&msgs[i], false);
mode = GENI_SE_FIFO;
geni_se_select_mode(&gi2c->i2c_rsc,
mode);
} else if (gi2c->dbg_buf_ptr) {
gi2c->dbg_buf_ptr[i].virt_buf =
(void *)dma_buf;
gi2c->dbg_buf_ptr[i].map_buf =
(void *)&tx_dma;
}
}
if (mode == GENI_SE_FIFO) /* Get FIFO IRQ */
geni_write_reg(1, gi2c->base,
SE_GENI_TX_WATERMARK_REG);
}
/* Ensure FIFO write go through before waiting for Done evet */
mb();
timeout = wait_for_completion_timeout(&gi2c->xfer,
gi2c->xfer_timeout);
if (!timeout) {
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"I2C xfer timeout: %d\n", gi2c->xfer_timeout);
geni_i2c_err(gi2c, GENI_TIMEOUT);
}
if (gi2c->err) {
reinit_completion(&gi2c->xfer);
geni_se_cancel_m_cmd(&gi2c->i2c_rsc);
timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
if (!timeout) {
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"Abort\n");
geni_se_abort_m_cmd(&gi2c->i2c_rsc);
}
}
gi2c->cur_wr = 0;
gi2c->cur_rd = 0;
if (mode == GENI_SE_DMA) {
if (gi2c->err) {
reinit_completion(&gi2c->xfer);
if (msgs[i].flags != I2C_M_RD)
writel_relaxed(1, gi2c->base +
SE_DMA_TX_FSM_RST);
else
writel_relaxed(1, gi2c->base +
SE_DMA_RX_FSM_RST);
wait_for_completion_timeout(&gi2c->xfer, HZ);
}
if (rx_dma)
geni_se_rx_dma_unprep(&gi2c->i2c_rsc, rx_dma,
msgs[i].len);
if (tx_dma)
geni_se_tx_dma_unprep(&gi2c->i2c_rsc, tx_dma,
msgs[i].len);
i2c_put_dma_safe_msg_buf(dma_buf, &msgs[i], !gi2c->err);
}
ret = gi2c->err;
if (gi2c->err) {
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"i2c error :%d\n", gi2c->err);
break;
}
}
geni_i2c_txn_ret:
if (ret == 0)
ret = num;
if (!gi2c->is_le_vm) {
pm_runtime_mark_last_busy(gi2c->dev);
pm_runtime_put_autosuspend(gi2c->dev);
}
gi2c->cur = NULL;
gi2c->err = 0;
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev,
"i2c txn ret:%d\n", ret);
return ret;
}
static u32 geni_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
}
static const struct i2c_algorithm geni_i2c_algo = {
.master_xfer = geni_i2c_xfer,
.functionality = geni_i2c_func,
};
static int get_geni_se_i2c_hub(struct geni_i2c_dev *gi2c)
{
int ret = 0;
int geni_hw_param;
ret = geni_se_common_clks_on(gi2c->i2c_rsc.clk, gi2c->m_ahb_clk, gi2c->s_ahb_clk);
if (ret) {
dev_err(gi2c->dev, "%s: Err in geni_se_clks_on %d\n", __func__, ret);
return ret;
}
geni_hw_param = geni_read_reg(gi2c->base, GENI_HW_PARAM);
if (geni_hw_param & I2C_MASTER_HUB)
gi2c->is_i2c_hub = true;
else
gi2c->is_i2c_hub = false;
geni_se_common_clks_off(gi2c->i2c_rsc.clk, gi2c->m_ahb_clk, gi2c->s_ahb_clk);
return ret;
}
static int geni_i2c_probe(struct platform_device *pdev)
{
struct geni_i2c_dev *gi2c;
struct resource *res;
int ret;
struct device *dev = &pdev->dev;
gi2c = devm_kzalloc(&pdev->dev, sizeof(*gi2c), GFP_KERNEL);
if (!gi2c)
return -ENOMEM;
if (arr_idx < MAX_SE)
/* Debug purpose */
gi2c_dev_dbg[arr_idx++] = gi2c;
gi2c->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
gi2c->base = devm_ioremap_resource(gi2c->dev, res);
if (IS_ERR(gi2c->base))
return PTR_ERR(gi2c->base);
if (of_property_read_bool(pdev->dev.of_node, "qcom,le-vm")) {
gi2c->is_le_vm = true;
gi2c->first_resume = true;
dev_info(&pdev->dev, "LE-VM usecase\n");
}
gi2c->i2c_rsc.dev = dev;
gi2c->i2c_rsc.wrapper = dev_get_drvdata(dev->parent);
gi2c->i2c_rsc.base = gi2c->base;
gi2c->wrapper_dev = dev->parent;
if (of_property_read_u32(pdev->dev.of_node, "qcom,clk-freq-out",
&gi2c->clk_freq_out))
gi2c->clk_freq_out = KHz(400);
dev_info(&pdev->dev, "Bus frequency is set to %dHz.\n",
gi2c->clk_freq_out);
ret = geni_i2c_clk_map_idx(gi2c);
if (ret) {
dev_err(gi2c->dev, "Invalid clk frequency %d KHz: %d\n",
gi2c->clk_freq_out, ret);
return ret;
}
/*
* For LE, clocks, gpio and icb voting will be provided by
* LA. The I2C operates in GSI mode only for LE usecase,
* se irq not required. Below properties will not be present
* in I2C LE dt.
*/
if (!gi2c->is_le_vm) {
gi2c->i2c_rsc.clk = devm_clk_get(&pdev->dev, "se-clk");
if (IS_ERR(gi2c->i2c_rsc.clk)) {
ret = PTR_ERR(gi2c->i2c_rsc.clk);
dev_err(&pdev->dev, "Err getting SE Core clk %d\n",
ret);
return ret;
}
gi2c->m_ahb_clk = devm_clk_get(dev->parent, "m-ahb");
if (IS_ERR(gi2c->m_ahb_clk)) {
ret = PTR_ERR(gi2c->m_ahb_clk);
dev_err(&pdev->dev, "Err getting M AHB clk %d\n", ret);
return ret;
}
gi2c->s_ahb_clk = devm_clk_get(dev->parent, "s-ahb");
if (IS_ERR(gi2c->s_ahb_clk)) {
ret = PTR_ERR(gi2c->s_ahb_clk);
dev_err(&pdev->dev, "Err getting S AHB clk %d\n", ret);
return ret;
}
ret = get_geni_se_i2c_hub(gi2c);
if (ret) {
dev_err(&pdev->dev, "failing at get_geni_se_i2c_hub %d\n", ret);
return ret;
}
/*
* For I2C_HUB, qup-ddr voting not required.
*/
if (gi2c->is_i2c_hub) {
ret = geni_icc_get(&gi2c->i2c_rsc, NULL);
if (ret) {
dev_err(&pdev->dev, "%s: Error - geni_icc_get ret:%d\n",
__func__, ret);
return ret;
}
gi2c->i2c_rsc.icc_paths[GENI_TO_CORE].avg_bw = GENI_DEFAULT_BW;
gi2c->i2c_rsc.icc_paths[CPU_TO_GENI].avg_bw = GENI_DEFAULT_BW;
} else {
ret = geni_se_common_resources_init(&gi2c->i2c_rsc,
GENI_DEFAULT_BW, GENI_DEFAULT_BW,
Bps_to_icc(gi2c->clk_freq_out));
if (ret) {
dev_err(&pdev->dev, "%s: Error - resources_init ret:%d\n",
__func__, ret);
return ret;
}
}
gi2c->irq = platform_get_irq(pdev, 0);
if (gi2c->irq < 0)
return gi2c->irq;
irq_set_status_flags(gi2c->irq, IRQ_NOAUTOEN);
ret = devm_request_irq(gi2c->dev, gi2c->irq, geni_i2c_irq,
0, "i2c_geni", gi2c);
if (ret) {
dev_err(gi2c->dev, "Request_irq failed:%d: err:%d\n",
gi2c->irq, ret);
return ret;
}
}
if (of_property_read_bool(pdev->dev.of_node, "qcom,shared")) {
gi2c->is_shared = true;
dev_info(&pdev->dev, "Multi-EE usecase\n");
}
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (ret) {
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(&pdev->dev, "could not set DMA mask\n");
return ret;
}
}
gi2c->adap.algo = &geni_i2c_algo;
init_completion(&gi2c->xfer);
platform_set_drvdata(pdev, gi2c);
i2c_set_adapdata(&gi2c->adap, gi2c);
gi2c->adap.dev.parent = gi2c->dev;
gi2c->adap.dev.of_node = pdev->dev.of_node;
strscpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));
pm_runtime_set_suspended(gi2c->dev);
pm_runtime_set_autosuspend_delay(gi2c->dev, I2C_AUTO_SUSPEND_DELAY);
pm_runtime_use_autosuspend(gi2c->dev);
pm_runtime_enable(gi2c->dev);
ret = i2c_add_adapter(&gi2c->adap);
if (ret) {
dev_err(gi2c->dev, "Add adapter failed, ret=%d\n", ret);
return ret;
}
dev_info(gi2c->dev, "I2C probed\n");
return 0;
}
static int geni_i2c_remove(struct platform_device *pdev)
{
struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev);
int i;
pm_runtime_disable(gi2c->dev);
i2c_del_adapter(&gi2c->adap);
for (i = 0; i < arr_idx; i++)
gi2c_dev_dbg[i] = NULL;
arr_idx = 0;
if (gi2c->ipcl)
ipc_log_context_destroy(gi2c->ipcl);
return 0;
}
static int geni_i2c_resume_early(struct device *device)
{
struct geni_i2c_dev *gi2c = dev_get_drvdata(device);
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev, "%s ret=%d\n", __func__, true);
return 0;
}
/*
* get sync/put sync in LA-VM -> do resources on/off
* get sync/put sync in LE-VM -> do lock/unlock gpii
*/
#if IS_ENABLED(CONFIG_PM)
static int geni_i2c_runtime_suspend(struct device *dev)
{
int ret;
struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
if (gi2c->se_mode == FIFO_SE_DMA)
disable_irq(gi2c->irq);
if (gi2c->is_le_vm) {
if (!gi2c->first_resume)
geni_i2c_unlock_bus(gi2c);
else
gi2c->first_resume = false;
} else if (gi2c->is_shared) {
/* Do not unconfigure GPIOs if shared se */
geni_se_common_clks_off(gi2c->i2c_rsc.clk, gi2c->m_ahb_clk, gi2c->s_ahb_clk);
ret = geni_icc_disable(&gi2c->i2c_rsc);
if (ret)
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev,
"%s failing at geni_icc_disable ret=%d\n", __func__, ret);
} else {
geni_se_resources_off(&gi2c->i2c_rsc);
ret = geni_icc_disable(&gi2c->i2c_rsc);
if (ret)
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev,
"%s failing at geni_icc_disable ret=%d\n", __func__, ret);
}
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev, "%s ret=%d\n", __func__, ret);
return 0;
}
static int geni_i2c_runtime_resume(struct device *dev)
{
int ret;
struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
if (!gi2c->ipcl) {
char ipc_name[I2C_NAME_SIZE];
snprintf(ipc_name, I2C_NAME_SIZE, "%s", dev_name(gi2c->dev));
gi2c->ipcl = ipc_log_context_create(2, ipc_name, 0);
}
if (!gi2c->is_le_vm) {
ret = geni_icc_enable(&gi2c->i2c_rsc);
if (ret) {
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev,
"%s failing at geni icc enable ret=%d\n", __func__, ret);
return ret;
}
/* Do not control clk/gpio/icb for LE-VM */
ret = geni_se_resources_on(&gi2c->i2c_rsc);
if (ret)
return ret;
ret = geni_i2c_prepare(gi2c);
if (ret) {
dev_err(gi2c->dev, "I2C prepare failed\n");
return ret;
}
if (gi2c->se_mode == FIFO_SE_DMA)
enable_irq(gi2c->irq);
} else if (!gi2c->first_resume) {
/*
* For first resume call in le, do nothing, and in
* corresponding first suspend, set the first_resume
* flag to false, to enable lock/unlock per resume/suspend
* session.
*/
ret = geni_i2c_prepare(gi2c);
if (ret) {
dev_err(gi2c->dev, "I2C prepare failed\n");
return ret;
}
ret = geni_i2c_lock_bus(gi2c);
if (ret) {
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"%s failed: %d\n", __func__, ret);
return ret;
}
}
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev, "%s ret=%d\n", __func__, ret);
return 0;
}
static int geni_i2c_suspend_late(struct device *device)
{
struct geni_i2c_dev *gi2c = dev_get_drvdata(device);
int ret;
/* Make sure no transactions are pending */
ret = i2c_trylock_bus(&gi2c->adap, I2C_LOCK_SEGMENT);
if (!ret) {
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
"late I2C transaction request\n");
return -EBUSY;
}
if (!pm_runtime_status_suspended(device)) {
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev,
"%s: Force suspend\n", __func__);
geni_i2c_runtime_suspend(device);
pm_runtime_disable(device);
pm_runtime_set_suspended(device);
pm_runtime_enable(device);
}
i2c_unlock_bus(&gi2c->adap, I2C_LOCK_SEGMENT);
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev, "%s ret=%d\n", __func__);
return 0;
}
#else
static int geni_i2c_runtime_suspend(struct device *dev)
{
return 0;
}
static int geni_i2c_runtime_resume(struct device *dev)
{
return 0;
}
static int geni_i2c_suspend_late(struct device *device)
{
return 0;
}
#endif
static const struct dev_pm_ops geni_i2c_pm_ops = {
.suspend_late = geni_i2c_suspend_late,
.resume_early = geni_i2c_resume_early,
.runtime_suspend = geni_i2c_runtime_suspend,
.runtime_resume = geni_i2c_runtime_resume,
};
static const struct of_device_id geni_i2c_dt_match[] = {
{ .compatible = "qcom,i2c-geni" },
{}
};
MODULE_DEVICE_TABLE(of, geni_i2c_dt_match);
static struct platform_driver geni_i2c_driver = {
.probe = geni_i2c_probe,
.remove = geni_i2c_remove,
.driver = {
.name = "i2c_geni",
.pm = &geni_i2c_pm_ops,
.of_match_table = geni_i2c_dt_match,
},
};
static int __init i2c_dev_init(void)
{
return platform_driver_register(&geni_i2c_driver);
}
static void __exit i2c_dev_exit(void)
{
platform_driver_unregister(&geni_i2c_driver);
}
module_init(i2c_dev_init);
module_exit(i2c_dev_exit);
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:i2c_geni");