mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-07 04:12:04 -04:00
i2c: i2c-msm-geni: Add per session lock/unlock support in i2c
Add per session lock/unlock support in i2c. Change-Id: I1a7b8e5992aad473364a0c92a41986fa9680120f Signed-off-by: Vipin Deep Kaur <vkaur@codeaurora.org>
This commit is contained in:
parent
dfc8288102
commit
e9b202cb6d
2 changed files with 474 additions and 195 deletions
|
|
@ -134,6 +134,11 @@ enum EV_PRIORITY {
|
|||
#define STATE_IGNORE (U32_MAX)
|
||||
#define REQ_OF_DMA_ARGS (5) /* # of arguments required from client */
|
||||
|
||||
#define NOOP_TRE_MASK(link_rx, bei, ieot, ieob, ch) \
|
||||
((0x0 << 20) | (0x0 << 16) | (link_rx << 11) | (bei << 10) | \
|
||||
(ieot << 9) | (ieob << 8) | ch)
|
||||
#define NOOP_TRE (0x0 << 20 | 0x1 << 16)
|
||||
|
||||
struct __packed gpi_error_log_entry {
|
||||
u32 routine : 4;
|
||||
u32 type : 4;
|
||||
|
|
@ -445,6 +450,7 @@ struct gpi_dev {
|
|||
u32 ipc_log_lvl;
|
||||
u32 klog_lvl;
|
||||
struct dentry *dentry;
|
||||
bool is_le_vm;
|
||||
};
|
||||
|
||||
static struct gpi_dev *gpi_dev_dbg[5];
|
||||
|
|
@ -544,6 +550,8 @@ struct gpii_chan {
|
|||
u32 dir;
|
||||
struct gpi_ring ch_ring;
|
||||
struct gpi_client_info client_info;
|
||||
u32 lock_tre_set;
|
||||
u32 num_tre;
|
||||
};
|
||||
|
||||
struct gpii {
|
||||
|
|
@ -1663,7 +1671,8 @@ static void gpi_process_xfer_compl_event(struct gpii_chan *gpii_chan,
|
|||
return;
|
||||
}
|
||||
} else if (compl_event->code == MSM_GPI_TCE_EOB) {
|
||||
goto gpi_free_desc;
|
||||
if (!(gpii_chan->num_tre == 1 && gpii_chan->lock_tre_set))
|
||||
goto gpi_free_desc;
|
||||
}
|
||||
|
||||
tx_cb_param = vd->tx.callback_param;
|
||||
|
|
@ -2199,6 +2208,7 @@ int gpi_terminate_all(struct dma_chan *chan)
|
|||
{
|
||||
struct gpii_chan *gpii_chan = to_gpii_chan(chan);
|
||||
struct gpii *gpii = gpii_chan->gpii;
|
||||
struct gpi_dev *gpi_dev = gpii->gpi_dev;
|
||||
int schid, echid, i;
|
||||
int ret = 0;
|
||||
|
||||
|
|
@ -2213,21 +2223,22 @@ int gpi_terminate_all(struct dma_chan *chan)
|
|||
echid = (gpii->protocol == SE_PROTOCOL_UART) ? schid + 1 :
|
||||
MAX_CHANNELS_PER_GPII;
|
||||
|
||||
/* stop the channel */
|
||||
for (i = schid; i < echid; i++) {
|
||||
gpii_chan = &gpii->gpii_chan[i];
|
||||
if (!gpi_dev->is_le_vm) {
|
||||
/* stop the channel */
|
||||
for (i = schid; i < echid; i++) {
|
||||
gpii_chan = &gpii->gpii_chan[i];
|
||||
|
||||
/* disable ch state so no more TRE processing */
|
||||
write_lock_irq(&gpii->pm_lock);
|
||||
gpii_chan->pm_state = PREPARE_TERMINATE;
|
||||
write_unlock_irq(&gpii->pm_lock);
|
||||
/* disable ch state so no more TRE processing */
|
||||
write_lock_irq(&gpii->pm_lock);
|
||||
gpii_chan->pm_state = PREPARE_TERMINATE;
|
||||
write_unlock_irq(&gpii->pm_lock);
|
||||
|
||||
/* send command to Stop the channel */
|
||||
ret = gpi_send_cmd(gpii, gpii_chan, GPI_CH_CMD_STOP);
|
||||
if (ret)
|
||||
GPII_ERR(gpii, gpii_chan->chid,
|
||||
"Error Stopping Channel:%d resetting anyway\n",
|
||||
ret);
|
||||
/* send command to Stop the channel */
|
||||
ret = gpi_send_cmd(gpii, gpii_chan, GPI_CH_CMD_STOP);
|
||||
if (ret)
|
||||
GPII_ERR(gpii, gpii_chan->chid,
|
||||
"Error Stopping Chan:%d resetting\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
/* reset the channels (clears any pending tre) */
|
||||
|
|
@ -2267,6 +2278,44 @@ terminate_exit:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void gpi_noop_tre(struct gpii_chan *gpii_chan)
|
||||
{
|
||||
struct gpii *gpii = gpii_chan->gpii;
|
||||
struct gpi_ring *ch_ring = &gpii_chan->ch_ring;
|
||||
phys_addr_t local_rp, local_wp;
|
||||
void *cntxt_rp;
|
||||
u32 noop_mask, noop_tre;
|
||||
struct msm_gpi_tre *tre;
|
||||
|
||||
GPII_INFO(gpii, gpii_chan->chid, "Enter\n");
|
||||
|
||||
local_rp = to_physical(ch_ring, ch_ring->rp);
|
||||
local_wp = to_physical(ch_ring, ch_ring->wp);
|
||||
cntxt_rp = ch_ring->rp;
|
||||
|
||||
GPII_INFO(gpii, gpii_chan->chid,
|
||||
"local_rp:0x%0llx local_wp:0x%0llx\n", local_rp, local_wp);
|
||||
|
||||
noop_mask = NOOP_TRE_MASK(1, 0, 0, 0, 1);
|
||||
noop_tre = NOOP_TRE;
|
||||
|
||||
while (local_rp != local_wp) {
|
||||
tre = (struct msm_gpi_tre *)cntxt_rp;
|
||||
tre->dword[3] &= noop_mask;
|
||||
tre->dword[3] |= noop_tre;
|
||||
local_rp += ch_ring->el_size;
|
||||
cntxt_rp += ch_ring->el_size;
|
||||
if (cntxt_rp >= (ch_ring->base + ch_ring->len)) {
|
||||
cntxt_rp = ch_ring->base;
|
||||
local_rp = to_physical(ch_ring, ch_ring->base);
|
||||
}
|
||||
GPII_INFO(gpii, gpii_chan->chid,
|
||||
"local_rp:0x%0llx\n", local_rp);
|
||||
}
|
||||
|
||||
GPII_INFO(gpii, gpii_chan->chid, "exit\n");
|
||||
}
|
||||
|
||||
/* pause dma transfer for all channels */
|
||||
static int gpi_pause(struct dma_chan *chan)
|
||||
{
|
||||
|
|
@ -2277,38 +2326,41 @@ static int gpi_pause(struct dma_chan *chan)
|
|||
GPII_INFO(gpii, gpii_chan->chid, "Enter\n");
|
||||
mutex_lock(&gpii->ctrl_lock);
|
||||
|
||||
/*
|
||||
* pause/resume are per gpii not per channel, so
|
||||
* client needs to call pause only once
|
||||
*/
|
||||
if (gpii->pm_state == PAUSE_STATE) {
|
||||
GPII_INFO(gpii, gpii_chan->chid,
|
||||
"channel is already paused\n");
|
||||
mutex_unlock(&gpii->ctrl_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* send stop command to stop the channels */
|
||||
for (i = 0; i < MAX_CHANNELS_PER_GPII; i++) {
|
||||
ret = gpi_send_cmd(gpii, &gpii->gpii_chan[i], GPI_CH_CMD_STOP);
|
||||
gpii_chan = &gpii->gpii_chan[i];
|
||||
/* disable ch state so no more TRE processing */
|
||||
write_lock_irq(&gpii->pm_lock);
|
||||
gpii_chan->pm_state = PREPARE_TERMINATE;
|
||||
write_unlock_irq(&gpii->pm_lock);
|
||||
/* send command to Stop the channel */
|
||||
ret = gpi_send_cmd(gpii, gpii_chan, GPI_CH_CMD_STOP);
|
||||
if (ret) {
|
||||
GPII_ERR(gpii, gpii->gpii_chan[i].chid,
|
||||
"Error stopping chan, ret:%d\n", ret);
|
||||
mutex_unlock(&gpii->ctrl_lock);
|
||||
return -ECONNRESET;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_CHANNELS_PER_GPII; i++) {
|
||||
gpii_chan = &gpii->gpii_chan[i];
|
||||
gpi_noop_tre(gpii_chan);
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_CHANNELS_PER_GPII; i++) {
|
||||
gpii_chan = &gpii->gpii_chan[i];
|
||||
|
||||
ret = gpi_start_chan(gpii_chan);
|
||||
if (ret) {
|
||||
GPII_ERR(gpii, gpii_chan->chid,
|
||||
"Error Starting Channel ret:%d\n", ret);
|
||||
mutex_unlock(&gpii->ctrl_lock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
disable_irq(gpii->irq);
|
||||
|
||||
/* Wait for threads to complete out */
|
||||
tasklet_kill(&gpii->ev_task);
|
||||
|
||||
write_lock_irq(&gpii->pm_lock);
|
||||
gpii->pm_state = PAUSE_STATE;
|
||||
write_unlock_irq(&gpii->pm_lock);
|
||||
mutex_unlock(&gpii->ctrl_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2378,6 +2430,7 @@ struct dma_async_tx_descriptor *gpi_prep_slave_sg(struct dma_chan *chan,
|
|||
const gfp_t gfp = GFP_ATOMIC;
|
||||
struct gpi_desc *gpi_desc;
|
||||
u32 tre_type;
|
||||
gpii_chan->num_tre = sg_len;
|
||||
|
||||
GPII_VERB(gpii, gpii_chan->chid, "enter\n");
|
||||
|
||||
|
|
@ -2414,6 +2467,12 @@ struct dma_async_tx_descriptor *gpi_prep_slave_sg(struct dma_chan *chan,
|
|||
for_each_sg(sgl, sg, sg_len, i) {
|
||||
tre = sg_virt(sg);
|
||||
|
||||
if (sg_len == 1) {
|
||||
tre_type =
|
||||
MSM_GPI_TRE_TYPE(((struct msm_gpi_tre *)tre));
|
||||
gpii_chan->lock_tre_set =
|
||||
tre_type == MSM_GPI_TRE_LOCK ? true : false;
|
||||
}
|
||||
/* Check if last tre is an unlock tre */
|
||||
if (i == sg_len - 1) {
|
||||
tre_type =
|
||||
|
|
@ -2941,6 +3000,11 @@ static int gpi_probe(struct platform_device *pdev)
|
|||
if (!gpi_dev->gpiis)
|
||||
return -ENOMEM;
|
||||
|
||||
gpi_dev->is_le_vm = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,le-vm");
|
||||
if (gpi_dev->is_le_vm)
|
||||
GPI_LOG(gpi_dev, "LE-VM usecase\n");
|
||||
|
||||
/* setup all the supported gpii */
|
||||
INIT_LIST_HEAD(&gpi_dev->dma_device.channels);
|
||||
for (i = 0; i < gpi_dev->max_gpii; i++) {
|
||||
|
|
|
|||
|
|
@ -128,6 +128,9 @@ struct geni_i2c_dev {
|
|||
u32 dbg_num;
|
||||
struct dbg_buf_ctxt *dbg_buf_ptr;
|
||||
bool is_le_vm;
|
||||
bool req_chan;
|
||||
bool first_resume;
|
||||
bool gpi_reset;
|
||||
};
|
||||
|
||||
static struct geni_i2c_dev *gi2c_dev_dbg[MAX_SE];
|
||||
|
|
@ -457,29 +460,16 @@ static void gi2c_gsi_rx_cb(void *ptr)
|
|||
}
|
||||
}
|
||||
|
||||
static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
|
||||
int num)
|
||||
static int geni_i2c_gsi_request_channel(struct geni_i2c_dev *gi2c)
|
||||
{
|
||||
struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
|
||||
int i, ret = 0, timeout = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!gi2c->tx_c) {
|
||||
gi2c->tx_c = dma_request_slave_channel(gi2c->dev, "tx");
|
||||
if (!gi2c->tx_c) {
|
||||
GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
|
||||
"tx dma req slv chan ret :%d\n", ret);
|
||||
ret = -EIO;
|
||||
goto geni_i2c_gsi_xfer_out;
|
||||
}
|
||||
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) {
|
||||
GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
|
||||
"tx dma slave config ret :%d\n", ret);
|
||||
goto geni_i2c_gsi_xfer_out;
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -488,65 +478,320 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
|
|||
if (!gi2c->rx_c) {
|
||||
GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
|
||||
"rx dma req slv chan ret :%d\n", ret);
|
||||
ret = -EIO;
|
||||
goto geni_i2c_gsi_xfer_out;
|
||||
dma_release_channel(gi2c->tx_c);
|
||||
return -EIO;
|
||||
}
|
||||
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) {
|
||||
}
|
||||
|
||||
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) {
|
||||
GENI_SE_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) {
|
||||
GENI_SE_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)
|
||||
{
|
||||
struct dma_async_tx_descriptor *geni_desc = NULL;
|
||||
|
||||
if (chan->chan_id == 0) {
|
||||
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) {
|
||||
GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
|
||||
"rx dma slave config ret :%d\n", ret);
|
||||
goto geni_i2c_gsi_xfer_out;
|
||||
"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) {
|
||||
GENI_SE_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;
|
||||
|
||||
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);
|
||||
if (!gi2c->tx_desc) {
|
||||
gi2c->err = -ENOMEM;
|
||||
goto geni_i2c_err_lock_bus;
|
||||
}
|
||||
|
||||
/* Issue TX */
|
||||
tx_cookie = dmaengine_submit(gi2c->tx_desc);
|
||||
dma_async_issue_pending(gi2c->tx_c);
|
||||
|
||||
reinit_completion(&gi2c->xfer);
|
||||
timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
|
||||
if (!timeout) {
|
||||
GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
|
||||
"%s timedout\n", __func__);
|
||||
geni_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;
|
||||
|
||||
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);
|
||||
if (!gi2c->tx_desc) {
|
||||
gi2c->err = -ENOMEM;
|
||||
goto geni_i2c_err_unlock_bus;
|
||||
}
|
||||
|
||||
/* Issue TX */
|
||||
tx_cookie = dmaengine_submit(gi2c->tx_desc);
|
||||
dma_async_issue_pending(gi2c->tx_c);
|
||||
|
||||
reinit_completion(&gi2c->xfer);
|
||||
timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
|
||||
if (!timeout) {
|
||||
GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
|
||||
"%s failed\n", __func__);
|
||||
geni_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) {
|
||||
struct msm_gpi_tre *lock_t = &gi2c->lock_t;
|
||||
struct msm_gpi_tre *unlock_t = &gi2c->unlock_t;
|
||||
|
||||
/* lock */
|
||||
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;
|
||||
lock_t->dword[3] = MSM_GPI_LOCK_TRE_DWORD3(0, 0, 0, 0, 1);
|
||||
|
||||
/* 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);
|
||||
lock_t = setup_lock_tre(gi2c);
|
||||
unlock_t = setup_unlock_tre(gi2c);
|
||||
}
|
||||
|
||||
if (!gi2c->cfg_sent) {
|
||||
struct geni_i2c_clk_fld *itr = geni_i2c_clk_map +
|
||||
gi2c->clk_fld_idx;
|
||||
struct msm_gpi_tre *cfg0 = &gi2c->cfg0_t;
|
||||
|
||||
/* config0 */
|
||||
cfg0->dword[0] = MSM_GPI_I2C_CONFIG0_TRE_DWORD0(I2C_PACK_EN,
|
||||
itr->t_cycle,
|
||||
itr->t_high,
|
||||
itr->t_low);
|
||||
cfg0->dword[1] = MSM_GPI_I2C_CONFIG0_TRE_DWORD1(0, 0);
|
||||
cfg0->dword[2] = MSM_GPI_I2C_CONFIG0_TRE_DWORD2(0,
|
||||
itr->clk_div);
|
||||
cfg0->dword[3] = MSM_GPI_I2C_CONFIG0_TRE_DWORD3(0, 0, 0, 0, 1);
|
||||
|
||||
gi2c->tx_cb.userdata = gi2c;
|
||||
gi2c->rx_cb.userdata = 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;
|
||||
int stretch = (i < (num - 1));
|
||||
dma_cookie_t tx_cookie, rx_cookie;
|
||||
struct msm_gpi_tre *go_t = &gi2c->go_t;
|
||||
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;
|
||||
|
||||
|
|
@ -569,7 +814,7 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
|
|||
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++], &gi2c->lock_t,
|
||||
sg_set_buf(&gi2c->tx_sg[index++], lock_t,
|
||||
sizeof(gi2c->lock_t));
|
||||
} else {
|
||||
sg_init_table(gi2c->tx_sg, segs);
|
||||
|
|
@ -577,35 +822,13 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
|
|||
|
||||
/* Send cfg tre when cfg not sent already */
|
||||
if (!gi2c->cfg_sent) {
|
||||
sg_set_buf(&gi2c->tx_sg[index++], &gi2c->cfg0_t,
|
||||
sg_set_buf(&gi2c->tx_sg[index++], cfg0_t,
|
||||
sizeof(gi2c->cfg0_t));
|
||||
gi2c->cfg_sent = 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);
|
||||
}
|
||||
|
||||
sg_set_buf(&gi2c->tx_sg[index++], &gi2c->go_t,
|
||||
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) {
|
||||
|
|
@ -629,31 +852,17 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
|
|||
gi2c->dbg_buf_ptr[i].map_buf =
|
||||
(void *)&gi2c->rx_ph;
|
||||
}
|
||||
gi2c->rx_t.dword[0] =
|
||||
MSM_GPI_DMA_W_BUFFER_TRE_DWORD0(gi2c->rx_ph);
|
||||
gi2c->rx_t.dword[1] =
|
||||
MSM_GPI_DMA_W_BUFFER_TRE_DWORD1(gi2c->rx_ph);
|
||||
gi2c->rx_t.dword[2] =
|
||||
MSM_GPI_DMA_W_BUFFER_TRE_DWORD2(msgs[i].len);
|
||||
/* Set ieot for all Rx/Tx DMA tres */
|
||||
gi2c->rx_t.dword[3] =
|
||||
MSM_GPI_DMA_W_BUFFER_TRE_DWORD3(0, 0, 1, 0, 0);
|
||||
|
||||
sg_set_buf(&gi2c->rx_sg, &gi2c->rx_t,
|
||||
rx_t = setup_rx_tre(gi2c, msgs, i, num);
|
||||
sg_set_buf(&gi2c->rx_sg, rx_t,
|
||||
sizeof(gi2c->rx_t));
|
||||
gi2c->rx_desc = dmaengine_prep_slave_sg(gi2c->rx_c,
|
||||
&gi2c->rx_sg, 1,
|
||||
DMA_DEV_TO_MEM,
|
||||
(DMA_PREP_INTERRUPT |
|
||||
DMA_CTRL_ACK));
|
||||
|
||||
gi2c->rx_desc =
|
||||
geni_i2c_prep_desc(gi2c, gi2c->rx_c, segs);
|
||||
if (!gi2c->rx_desc) {
|
||||
GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
|
||||
"prep_slave_sg for rx failed\n");
|
||||
gi2c->err = -ENOMEM;
|
||||
goto geni_i2c_err_prep_sg;
|
||||
}
|
||||
gi2c->rx_desc->callback = gi2c_gsi_rx_cb;
|
||||
gi2c->rx_desc->callback_param = &gi2c->rx_cb;
|
||||
|
||||
/* Issue RX */
|
||||
rx_cookie = dmaengine_submit(gi2c->rx_desc);
|
||||
|
|
@ -679,45 +888,22 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
|
|||
(void *)&gi2c->tx_ph;
|
||||
}
|
||||
|
||||
gi2c->tx_t.dword[0] =
|
||||
MSM_GPI_DMA_W_BUFFER_TRE_DWORD0(gi2c->tx_ph);
|
||||
gi2c->tx_t.dword[1] =
|
||||
MSM_GPI_DMA_W_BUFFER_TRE_DWORD1(gi2c->tx_ph);
|
||||
gi2c->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.
|
||||
*/
|
||||
gi2c->tx_t.dword[3] =
|
||||
MSM_GPI_DMA_W_BUFFER_TRE_DWORD3(0, 0, 1, 0, 1);
|
||||
else
|
||||
gi2c->tx_t.dword[3] =
|
||||
MSM_GPI_DMA_W_BUFFER_TRE_DWORD3(0, 0, 1, 0, 0);
|
||||
|
||||
sg_set_buf(&gi2c->tx_sg[index++], &gi2c->tx_t,
|
||||
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++],
|
||||
&gi2c->unlock_t, sizeof(gi2c->unlock_t));
|
||||
unlock_t, sizeof(gi2c->unlock_t));
|
||||
}
|
||||
|
||||
gi2c->tx_desc = dmaengine_prep_slave_sg(gi2c->tx_c, gi2c->tx_sg,
|
||||
segs, DMA_MEM_TO_DEV,
|
||||
(DMA_PREP_INTERRUPT |
|
||||
DMA_CTRL_ACK));
|
||||
gi2c->tx_desc = geni_i2c_prep_desc(gi2c, gi2c->tx_c, segs);
|
||||
if (!gi2c->tx_desc) {
|
||||
GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
|
||||
"prep_slave_sg for tx failed\n");
|
||||
gi2c->err = -ENOMEM;
|
||||
goto geni_i2c_err_prep_sg;
|
||||
}
|
||||
gi2c->tx_desc->callback = gi2c_gsi_tx_cb;
|
||||
gi2c->tx_desc->callback_param = &gi2c->tx_cb;
|
||||
|
||||
/* Issue TX */
|
||||
tx_cookie = dmaengine_submit(gi2c->tx_desc);
|
||||
|
|
@ -736,8 +922,20 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
|
|||
}
|
||||
geni_i2c_err_prep_sg:
|
||||
if (gi2c->err) {
|
||||
dmaengine_terminate_all(gi2c->tx_c);
|
||||
gi2c->cfg_sent = 0;
|
||||
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;
|
||||
GENI_SE_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 */
|
||||
|
|
@ -777,21 +975,14 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
|
|||
return -EACCES;
|
||||
}
|
||||
|
||||
ret = pm_runtime_get_sync(gi2c->dev);
|
||||
if (ret < 0) {
|
||||
GENI_SE_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;
|
||||
}
|
||||
|
||||
if (gi2c->is_le_vm) {
|
||||
ret = geni_i2c_prepare(gi2c);
|
||||
if (ret) {
|
||||
dev_err(gi2c->dev, "I2C prepare failed\n");
|
||||
pm_runtime_put_sync_suspend(gi2c->dev);
|
||||
if (!gi2c->is_le_vm) {
|
||||
ret = pm_runtime_get_sync(gi2c->dev);
|
||||
if (ret < 0) {
|
||||
GENI_SE_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;
|
||||
}
|
||||
}
|
||||
|
|
@ -950,8 +1141,10 @@ geni_i2c_txn_ret:
|
|||
if (ret == 0)
|
||||
ret = num;
|
||||
|
||||
pm_runtime_mark_last_busy(gi2c->dev);
|
||||
pm_runtime_put_autosuspend(gi2c->dev);
|
||||
if (!gi2c->is_le_vm) {
|
||||
pm_runtime_mark_last_busy(gi2c->dev);
|
||||
pm_runtime_put_autosuspend(gi2c->dev);
|
||||
}
|
||||
gi2c->cur = NULL;
|
||||
gi2c->err = 0;
|
||||
GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev,
|
||||
|
|
@ -1013,6 +1206,7 @@ static int geni_i2c_probe(struct platform_device *pdev)
|
|||
|
||||
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");
|
||||
}
|
||||
|
||||
|
|
@ -1168,6 +1362,10 @@ static int geni_i2c_resume_noirq(struct device *device)
|
|||
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)
|
||||
{
|
||||
|
|
@ -1177,8 +1375,10 @@ static int geni_i2c_runtime_suspend(struct device *dev)
|
|||
disable_irq(gi2c->irq);
|
||||
|
||||
if (gi2c->is_le_vm) {
|
||||
/* Do not control clk/gpio/icb for LE-VM */
|
||||
return 0;
|
||||
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 */
|
||||
se_geni_clks_off(&gi2c->i2c_rsc);
|
||||
|
|
@ -1207,13 +1407,7 @@ static int geni_i2c_runtime_resume(struct device *dev)
|
|||
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* resume is called from probe so for LE,
|
||||
* geni_i2c_prepare can not be called inside
|
||||
* resume as clocks are not on during LE probe.
|
||||
* LA will provide clocks to LE so deferring
|
||||
* the below api call to first transfer in LE.
|
||||
*/
|
||||
|
||||
ret = geni_i2c_prepare(gi2c);
|
||||
if (ret) {
|
||||
dev_err(gi2c->dev, "I2C prepare failed\n");
|
||||
|
|
@ -1222,7 +1416,28 @@ static int geni_i2c_runtime_resume(struct device *dev)
|
|||
|
||||
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) {
|
||||
GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
|
||||
"%s failed: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, "%s\n", __func__);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
Loading…
Reference in a new issue