Skip to content

Commit 3980351

Browse files
Jyothi Kumar Seerapuvinodkoul
authored andcommitted
i2c: i2c-qcom-geni: Add Block event interrupt support
The I2C driver gets an interrupt upon transfer completion. When handling multiple messages in a single transfer, this results in N interrupts for N messages, leading to significant software interrupt latency. To mitigate this latency, utilize Block Event Interrupt (BEI) mechanism. Enabling BEI instructs the hardware to prevent interrupt generation and BEI is disabled when an interrupt is necessary. Large I2C transfer can be divided into chunks of messages internally. Interrupts are not expected for the messages for which BEI bit set, only the last message triggers an interrupt, indicating the completion of N messages. This BEI mechanism enhances overall transfer efficiency. BEI optimizations are currently implemented for I2C write transfers only, as there is no use case for multiple I2C read messages in a single transfer at this time. Signed-off-by: Jyothi Kumar Seerapu <quic_jseerapu@quicinc.com> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> Acked-by: Andi Shyti <andi.shyti@kernel.org> Reviewed-by: Mukesh Savaliya <mukesh.savaliya@oss.qualcomm.com> Acked-by: Mukesh Savaliya <mukesh.savaliya@oss.qualcomm.com> Signed-off-by: Vinod Koul <vkoul@kernel.org>
1 parent 4e83313 commit 3980351

1 file changed

Lines changed: 224 additions & 24 deletions

File tree

drivers/i2c/busses/i2c-qcom-geni.c

Lines changed: 224 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,25 @@ enum geni_i2c_err_code {
7777
#define XFER_TIMEOUT HZ
7878
#define RST_TIMEOUT HZ
7979

80+
#define QCOM_I2C_MIN_NUM_OF_MSGS_MULTI_DESC 2
81+
82+
/**
83+
* struct geni_i2c_gpi_multi_desc_xfer - Structure for multi transfer support
84+
*
85+
* @msg_idx_cnt: Current message index being processed in the transfer
86+
* @unmap_msg_cnt: Number of messages that have been unmapped
87+
* @irq_cnt: Number of transfer completion interrupts received
88+
* @dma_buf: Array of virtual addresses for DMA-safe buffers
89+
* @dma_addr: Array of DMA addresses corresponding to the buffers
90+
*/
91+
struct geni_i2c_gpi_multi_desc_xfer {
92+
u32 msg_idx_cnt;
93+
u32 unmap_msg_cnt;
94+
u32 irq_cnt;
95+
void **dma_buf;
96+
dma_addr_t *dma_addr;
97+
};
98+
8099
struct geni_i2c_dev {
81100
struct geni_se se;
82101
u32 tx_wm;
@@ -99,6 +118,9 @@ struct geni_i2c_dev {
99118
struct dma_chan *rx_c;
100119
bool gpi_mode;
101120
bool abort_done;
121+
bool is_tx_multi_desc_xfer;
122+
u32 num_msgs;
123+
struct geni_i2c_gpi_multi_desc_xfer i2c_multi_desc_config;
102124
};
103125

104126
struct geni_i2c_desc {
@@ -499,6 +521,7 @@ static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
499521
static void i2c_gpi_cb_result(void *cb, const struct dmaengine_result *result)
500522
{
501523
struct geni_i2c_dev *gi2c = cb;
524+
struct geni_i2c_gpi_multi_desc_xfer *tx_multi_xfer;
502525

503526
if (result->result != DMA_TRANS_NOERROR) {
504527
dev_err(gi2c->se.dev, "DMA txn failed:%d\n", result->result);
@@ -507,6 +530,11 @@ static void i2c_gpi_cb_result(void *cb, const struct dmaengine_result *result)
507530
dev_dbg(gi2c->se.dev, "DMA xfer has pending: %d\n", result->residue);
508531
}
509532

533+
if (gi2c->is_tx_multi_desc_xfer) {
534+
tx_multi_xfer = &gi2c->i2c_multi_desc_config;
535+
tx_multi_xfer->irq_cnt++;
536+
}
537+
510538
complete(&gi2c->done);
511539
}
512540

@@ -525,7 +553,72 @@ static void geni_i2c_gpi_unmap(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
525553
}
526554
}
527555

528-
static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
556+
/**
557+
* geni_i2c_gpi_multi_desc_unmap() - Unmaps DMA buffers post multi message TX transfers
558+
* @gi2c: I2C dev handle
559+
* @msgs: Array of I2C messages
560+
* @peripheral: Pointer to gpi_i2c_config
561+
*/
562+
static void geni_i2c_gpi_multi_desc_unmap(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[],
563+
struct gpi_i2c_config *peripheral)
564+
{
565+
u32 msg_xfer_cnt, wr_idx = 0;
566+
struct geni_i2c_gpi_multi_desc_xfer *tx_multi_xfer = &gi2c->i2c_multi_desc_config;
567+
568+
msg_xfer_cnt = gi2c->err ? tx_multi_xfer->msg_idx_cnt : tx_multi_xfer->irq_cnt;
569+
570+
/* Unmap the processed DMA buffers based on the received interrupt count */
571+
for (; tx_multi_xfer->unmap_msg_cnt < msg_xfer_cnt; tx_multi_xfer->unmap_msg_cnt++) {
572+
wr_idx = tx_multi_xfer->unmap_msg_cnt;
573+
geni_i2c_gpi_unmap(gi2c, &msgs[wr_idx],
574+
tx_multi_xfer->dma_buf[wr_idx],
575+
tx_multi_xfer->dma_addr[wr_idx],
576+
NULL, 0);
577+
578+
if (tx_multi_xfer->unmap_msg_cnt == gi2c->num_msgs - 1) {
579+
kfree(tx_multi_xfer->dma_buf);
580+
kfree(tx_multi_xfer->dma_addr);
581+
break;
582+
}
583+
}
584+
}
585+
586+
/**
587+
* geni_i2c_gpi_multi_xfer_timeout_handler() - Handles multi message transfer timeout
588+
* @dev: Pointer to the corresponding dev node
589+
* @multi_xfer: Pointer to the geni_i2c_gpi_multi_desc_xfer
590+
* @transfer_timeout_msecs: Timeout value in milliseconds
591+
* @transfer_comp: Completion object of the transfer
592+
*
593+
* This function waits for the completion of each processed transfer messages
594+
* based on the interrupts generated upon transfer completion.
595+
*
596+
* Return: On success returns 0, -ETIMEDOUT on timeout.
597+
*/
598+
static int geni_i2c_gpi_multi_xfer_timeout_handler(struct device *dev,
599+
struct geni_i2c_gpi_multi_desc_xfer *multi_xfer,
600+
u32 transfer_timeout_msecs,
601+
struct completion *transfer_comp)
602+
{
603+
int i;
604+
u32 time_left;
605+
606+
for (i = 0; i < multi_xfer->msg_idx_cnt - 1; i++) {
607+
reinit_completion(transfer_comp);
608+
609+
if (multi_xfer->msg_idx_cnt != multi_xfer->irq_cnt) {
610+
time_left = wait_for_completion_timeout(transfer_comp,
611+
transfer_timeout_msecs);
612+
if (!time_left) {
613+
dev_err(dev, "%s: Transfer timeout\n", __func__);
614+
return -ETIMEDOUT;
615+
}
616+
}
617+
}
618+
return 0;
619+
}
620+
621+
static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[],
529622
struct dma_slave_config *config, dma_addr_t *dma_addr_p,
530623
void **buf, unsigned int op, struct dma_chan *dma_chan)
531624
{
@@ -537,26 +630,45 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
537630
enum dma_transfer_direction dma_dirn;
538631
struct dma_async_tx_descriptor *desc;
539632
int ret;
633+
struct geni_i2c_gpi_multi_desc_xfer *gi2c_gpi_xfer;
634+
dma_cookie_t cookie;
635+
u32 msg_idx;
540636

541637
peripheral = config->peripheral_config;
638+
gi2c_gpi_xfer = &gi2c->i2c_multi_desc_config;
639+
msg_idx = gi2c_gpi_xfer->msg_idx_cnt;
542640

543-
dma_buf = i2c_get_dma_safe_msg_buf(msg, 1);
544-
if (!dma_buf)
545-
return -ENOMEM;
641+
dma_buf = i2c_get_dma_safe_msg_buf(&msgs[msg_idx], 1);
642+
if (!dma_buf) {
643+
ret = -ENOMEM;
644+
goto out;
645+
}
546646

547647
if (op == I2C_WRITE)
548648
map_dirn = DMA_TO_DEVICE;
549649
else
550650
map_dirn = DMA_FROM_DEVICE;
551651

552-
addr = dma_map_single(gi2c->se.dev->parent, dma_buf, msg->len, map_dirn);
652+
addr = dma_map_single(gi2c->se.dev->parent, dma_buf,
653+
msgs[msg_idx].len, map_dirn);
553654
if (dma_mapping_error(gi2c->se.dev->parent, addr)) {
554-
i2c_put_dma_safe_msg_buf(dma_buf, msg, false);
555-
return -ENOMEM;
655+
i2c_put_dma_safe_msg_buf(dma_buf, &msgs[msg_idx], false);
656+
ret = -ENOMEM;
657+
goto out;
658+
}
659+
660+
if (gi2c->is_tx_multi_desc_xfer) {
661+
flags = DMA_CTRL_ACK;
662+
663+
/* BEI bit to be cleared for last TRE */
664+
if (msg_idx == gi2c->num_msgs - 1)
665+
flags |= DMA_PREP_INTERRUPT;
666+
} else {
667+
flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
556668
}
557669

558670
/* set the length as message for rx txn */
559-
peripheral->rx_len = msg->len;
671+
peripheral->rx_len = msgs[msg_idx].len;
560672
peripheral->op = op;
561673

562674
ret = dmaengine_slave_config(dma_chan, config);
@@ -567,14 +679,21 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
567679

568680
peripheral->set_config = 0;
569681
peripheral->multi_msg = true;
570-
flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
571682

572683
if (op == I2C_WRITE)
573684
dma_dirn = DMA_MEM_TO_DEV;
574685
else
575686
dma_dirn = DMA_DEV_TO_MEM;
576687

577-
desc = dmaengine_prep_slave_single(dma_chan, addr, msg->len, dma_dirn, flags);
688+
desc = dmaengine_prep_slave_single(dma_chan, addr, msgs[msg_idx].len,
689+
dma_dirn, flags);
690+
if (!desc && !(flags & DMA_PREP_INTERRUPT)) {
691+
/* Retry with interrupt if not enough TREs */
692+
flags |= DMA_PREP_INTERRUPT;
693+
desc = dmaengine_prep_slave_single(dma_chan, addr, msgs[msg_idx].len,
694+
dma_dirn, flags);
695+
}
696+
578697
if (!desc) {
579698
dev_err(gi2c->se.dev, "prep_slave_sg failed\n");
580699
ret = -EIO;
@@ -584,15 +703,48 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
584703
desc->callback_result = i2c_gpi_cb_result;
585704
desc->callback_param = gi2c;
586705

587-
dmaengine_submit(desc);
588-
*buf = dma_buf;
589-
*dma_addr_p = addr;
706+
if (!((msgs[msg_idx].flags & I2C_M_RD) && op == I2C_WRITE))
707+
gi2c_gpi_xfer->msg_idx_cnt++;
590708

709+
cookie = dmaengine_submit(desc);
710+
if (dma_submit_error(cookie)) {
711+
dev_err(gi2c->se.dev,
712+
"%s: dmaengine_submit failed (%d)\n", __func__, cookie);
713+
ret = -EINVAL;
714+
goto err_config;
715+
}
716+
717+
if (gi2c->is_tx_multi_desc_xfer) {
718+
gi2c_gpi_xfer->dma_buf[msg_idx] = dma_buf;
719+
gi2c_gpi_xfer->dma_addr[msg_idx] = addr;
720+
721+
dma_async_issue_pending(gi2c->tx_c);
722+
723+
if ((msg_idx == (gi2c->num_msgs - 1)) || flags & DMA_PREP_INTERRUPT) {
724+
ret = geni_i2c_gpi_multi_xfer_timeout_handler(gi2c->se.dev, gi2c_gpi_xfer,
725+
XFER_TIMEOUT, &gi2c->done);
726+
if (ret) {
727+
dev_err(gi2c->se.dev,
728+
"I2C multi write msg transfer timeout: %d\n",
729+
ret);
730+
gi2c->err = ret;
731+
return ret;
732+
}
733+
}
734+
} else {
735+
/* Non multi descriptor message transfer */
736+
*buf = dma_buf;
737+
*dma_addr_p = addr;
738+
}
591739
return 0;
592740

593741
err_config:
594-
dma_unmap_single(gi2c->se.dev->parent, addr, msg->len, map_dirn);
595-
i2c_put_dma_safe_msg_buf(dma_buf, msg, false);
742+
dma_unmap_single(gi2c->se.dev->parent, addr,
743+
msgs[msg_idx].len, map_dirn);
744+
i2c_put_dma_safe_msg_buf(dma_buf, &msgs[msg_idx], false);
745+
746+
out:
747+
gi2c->err = ret;
596748
return ret;
597749
}
598750

@@ -604,6 +756,7 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i
604756
unsigned long time_left;
605757
dma_addr_t tx_addr, rx_addr;
606758
void *tx_buf = NULL, *rx_buf = NULL;
759+
struct geni_i2c_gpi_multi_desc_xfer *tx_multi_xfer;
607760
const struct geni_i2c_clk_fld *itr = gi2c->clk_fld;
608761

609762
config.peripheral_config = &peripheral;
@@ -617,6 +770,41 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i
617770
peripheral.set_config = 1;
618771
peripheral.multi_msg = false;
619772

773+
gi2c->num_msgs = num;
774+
gi2c->is_tx_multi_desc_xfer = false;
775+
776+
tx_multi_xfer = &gi2c->i2c_multi_desc_config;
777+
memset(tx_multi_xfer, 0, sizeof(struct geni_i2c_gpi_multi_desc_xfer));
778+
779+
/*
780+
* If number of write messages are two and higher then
781+
* configure hardware for multi descriptor transfers with BEI.
782+
*/
783+
if (num >= QCOM_I2C_MIN_NUM_OF_MSGS_MULTI_DESC) {
784+
gi2c->is_tx_multi_desc_xfer = true;
785+
for (i = 0; i < num; i++) {
786+
if (msgs[i].flags & I2C_M_RD) {
787+
/*
788+
* Multi descriptor transfer with BEI
789+
* support is enabled for write transfers.
790+
* TODO: Add BEI optimization support for
791+
* read transfers later.
792+
*/
793+
gi2c->is_tx_multi_desc_xfer = false;
794+
break;
795+
}
796+
}
797+
}
798+
799+
if (gi2c->is_tx_multi_desc_xfer) {
800+
tx_multi_xfer->dma_buf = kcalloc(num, sizeof(void *), GFP_KERNEL);
801+
tx_multi_xfer->dma_addr = kcalloc(num, sizeof(dma_addr_t), GFP_KERNEL);
802+
if (!tx_multi_xfer->dma_buf || !tx_multi_xfer->dma_addr) {
803+
ret = -ENOMEM;
804+
goto err;
805+
}
806+
}
807+
620808
for (i = 0; i < num; i++) {
621809
gi2c->cur = &msgs[i];
622810
gi2c->err = 0;
@@ -627,33 +815,41 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i
627815
peripheral.stretch = 1;
628816

629817
peripheral.addr = msgs[i].addr;
818+
if (i > 0 && (!(msgs[i].flags & I2C_M_RD)))
819+
peripheral.multi_msg = false;
630820

631-
ret = geni_i2c_gpi(gi2c, &msgs[i], &config,
821+
ret = geni_i2c_gpi(gi2c, msgs, &config,
632822
&tx_addr, &tx_buf, I2C_WRITE, gi2c->tx_c);
633823
if (ret)
634824
goto err;
635825

636826
if (msgs[i].flags & I2C_M_RD) {
637-
ret = geni_i2c_gpi(gi2c, &msgs[i], &config,
827+
ret = geni_i2c_gpi(gi2c, msgs, &config,
638828
&rx_addr, &rx_buf, I2C_READ, gi2c->rx_c);
639829
if (ret)
640830
goto err;
641831

642832
dma_async_issue_pending(gi2c->rx_c);
643833
}
644834

645-
dma_async_issue_pending(gi2c->tx_c);
646-
647-
time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
648-
if (!time_left)
649-
gi2c->err = -ETIMEDOUT;
835+
if (!gi2c->is_tx_multi_desc_xfer) {
836+
dma_async_issue_pending(gi2c->tx_c);
837+
time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
838+
if (!time_left) {
839+
dev_err(gi2c->se.dev, "%s:I2C timeout\n", __func__);
840+
gi2c->err = -ETIMEDOUT;
841+
}
842+
}
650843

651844
if (gi2c->err) {
652845
ret = gi2c->err;
653846
goto err;
654847
}
655848

656-
geni_i2c_gpi_unmap(gi2c, &msgs[i], tx_buf, tx_addr, rx_buf, rx_addr);
849+
if (!gi2c->is_tx_multi_desc_xfer)
850+
geni_i2c_gpi_unmap(gi2c, &msgs[i], tx_buf, tx_addr, rx_buf, rx_addr);
851+
else if (tx_multi_xfer->unmap_msg_cnt != tx_multi_xfer->irq_cnt)
852+
geni_i2c_gpi_multi_desc_unmap(gi2c, msgs, &peripheral);
657853
}
658854

659855
return num;
@@ -662,7 +858,11 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i
662858
dev_err(gi2c->se.dev, "GPI transfer failed: %d\n", ret);
663859
dmaengine_terminate_sync(gi2c->rx_c);
664860
dmaengine_terminate_sync(gi2c->tx_c);
665-
geni_i2c_gpi_unmap(gi2c, &msgs[i], tx_buf, tx_addr, rx_buf, rx_addr);
861+
if (gi2c->is_tx_multi_desc_xfer)
862+
geni_i2c_gpi_multi_desc_unmap(gi2c, msgs, &peripheral);
863+
else
864+
geni_i2c_gpi_unmap(gi2c, &msgs[i], tx_buf, tx_addr, rx_buf, rx_addr);
865+
666866
return ret;
667867
}
668868

0 commit comments

Comments
 (0)