Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

i2c: octeon: add block-mode i2c operations

Add functions to perform block read and write operations. This applies
for cases where the requested operation is for >8 bytes of data.

When not using the block mode transfer, the driver will attempt a series
of 8 byte i2c operations until it reaches the desired total. For
example, for a 40 byte request the driver will complete 5 separate
transactions. This results in large transactions taking a significant
amount of time to process.

Add block mode such that the driver can request larger transactions, up
to 1024 bytes per transfer.

Many aspects of the block mode transfer is common with the regular 8
byte operations. Use generic functions for parts of the message
construction and sending the message. The key difference for the block
mode is the usage of separate FIFO buffer to store data.

Write to this buffer in the case of a write (before command send).
Read from this buffer in the case of a read (after command send).

Data is written into this buffer by placing data into the MSB onwards.
This means the bottom 8 bits of the data will match the top 8 bits, and
so on and so forth.

Set specific bits in message for block mode, enable block mode transfers
from global i2c management registers, construct message, send message,
read or write from FIFO buffer as required.

The block-mode transactions result in a significant speed increase in
large i2c requests.

Signed-off-by: Aryan Srivastava <aryan.srivastava@alliedtelesis.co.nz>
Link: https://lore.kernel.org/r/20250324192946.3078712-2-aryan.srivastava@alliedtelesis.co.nz
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>

authored by

Aryan Srivastava and committed by
Andi Shyti
63ef02da e1902d65

+175 -7
+160 -6
drivers/i2c/busses/i2c-octeon-core.c
··· 135 135 octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); 136 136 } 137 137 138 + static void octeon_i2c_block_enable(struct octeon_i2c *i2c) 139 + { 140 + u64 mode; 141 + 142 + if (i2c->block_enabled || !OCTEON_REG_BLOCK_CTL(i2c)) 143 + return; 144 + 145 + i2c->block_enabled = true; 146 + mode = __raw_readq(i2c->twsi_base + OCTEON_REG_MODE(i2c)); 147 + mode |= TWSX_MODE_BLOCK_MODE; 148 + octeon_i2c_writeq_flush(mode, i2c->twsi_base + OCTEON_REG_MODE(i2c)); 149 + } 150 + 151 + static void octeon_i2c_block_disable(struct octeon_i2c *i2c) 152 + { 153 + u64 mode; 154 + 155 + if (!i2c->block_enabled || !OCTEON_REG_BLOCK_CTL(i2c)) 156 + return; 157 + 158 + i2c->block_enabled = false; 159 + mode = __raw_readq(i2c->twsi_base + OCTEON_REG_MODE(i2c)); 160 + mode &= ~TWSX_MODE_BLOCK_MODE; 161 + octeon_i2c_writeq_flush(mode, i2c->twsi_base + OCTEON_REG_MODE(i2c)); 162 + } 163 + 138 164 /** 139 165 * octeon_i2c_hlc_wait - wait for an HLC operation to complete 140 166 * @i2c: The struct octeon_i2c ··· 307 281 u8 stat; 308 282 309 283 octeon_i2c_hlc_disable(i2c); 284 + octeon_i2c_block_disable(i2c); 310 285 311 286 octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STA); 312 287 ret = octeon_i2c_wait(i2c); ··· 632 605 } 633 606 634 607 /** 608 + * octeon_i2c_hlc_block_comp_read - high-level-controller composite block read 609 + * @i2c: The struct octeon_i2c 610 + * @msgs: msg[0] contains address, place read data into msg[1] 611 + * 612 + * i2c core command is constructed and written into the SW_TWSI register. 613 + * The execution of the command will result in requested data being 614 + * placed into a FIFO buffer, ready to be read. 615 + * Used in the case where the i2c xfer is for greater than 8 bytes of read data. 616 + * 617 + * Returns: 0 on success, otherwise a negative errno. 618 + */ 619 + static int octeon_i2c_hlc_block_comp_read(struct octeon_i2c *i2c, struct i2c_msg *msgs) 620 + { 621 + int ret; 622 + u16 len, i; 623 + u64 cmd; 624 + 625 + octeon_i2c_hlc_enable(i2c); 626 + octeon_i2c_block_enable(i2c); 627 + 628 + /* Write (size - 1) into block control register */ 629 + len = msgs[1].len - 1; 630 + octeon_i2c_writeq_flush((u64)len, i2c->twsi_base + OCTEON_REG_BLOCK_CTL(i2c)); 631 + 632 + /* Prepare core command */ 633 + cmd = SW_TWSI_V | SW_TWSI_R | SW_TWSI_SOVR | SW_TWSI_OP_7_IA; 634 + cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT; 635 + 636 + /* Send core command */ 637 + ret = octeon_i2c_hlc_read_cmd(i2c, msgs[0], cmd); 638 + if (ret) 639 + goto err; 640 + 641 + cmd = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c)); 642 + if ((cmd & SW_TWSI_R) == 0) { 643 + octeon_i2c_block_disable(i2c); 644 + return octeon_i2c_check_status(i2c, false); 645 + } 646 + 647 + /* read data in FIFO */ 648 + octeon_i2c_writeq_flush(TWSX_BLOCK_STS_RESET_PTR, 649 + i2c->twsi_base + OCTEON_REG_BLOCK_STS(i2c)); 650 + for (i = 0; i <= len; i += 8) { 651 + /* Byte-swap FIFO data and copy into msg buffer */ 652 + __be64 rd = cpu_to_be64(__raw_readq(i2c->twsi_base + OCTEON_REG_BLOCK_FIFO(i2c))); 653 + 654 + memcpy(&msgs[1].buf[i], &rd, min(8, msgs[1].len - i)); 655 + } 656 + 657 + err: 658 + octeon_i2c_block_disable(i2c); 659 + return ret; 660 + } 661 + 662 + /** 663 + * octeon_i2c_hlc_block_comp_write - high-level-controller composite block write 664 + * @i2c: The struct octeon_i2c 665 + * @msgs: msg[0] contains address, msg[1] contains data to be written 666 + * 667 + * i2c core command is constructed and write data is written into the FIFO buffer. 668 + * The execution of the command will result in HW write, using the data in FIFO. 669 + * Used in the case where the i2c xfer is for greater than 8 bytes of write data. 670 + * 671 + * Returns: 0 on success, otherwise a negative errno. 672 + */ 673 + static int octeon_i2c_hlc_block_comp_write(struct octeon_i2c *i2c, struct i2c_msg *msgs) 674 + { 675 + bool set_ext; 676 + int ret; 677 + u16 len, i; 678 + u64 cmd, ext = 0; 679 + 680 + octeon_i2c_hlc_enable(i2c); 681 + octeon_i2c_block_enable(i2c); 682 + 683 + /* Write (size - 1) into block control register */ 684 + len = msgs[1].len - 1; 685 + octeon_i2c_writeq_flush((u64)len, i2c->twsi_base + OCTEON_REG_BLOCK_CTL(i2c)); 686 + 687 + /* Prepare core command */ 688 + cmd = SW_TWSI_V | SW_TWSI_SOVR | SW_TWSI_OP_7_IA; 689 + cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT; 690 + 691 + /* Set parameters for extended message (if required) */ 692 + set_ext = octeon_i2c_hlc_ext(i2c, msgs[0], &cmd, &ext); 693 + 694 + /* Write msg into FIFO buffer */ 695 + octeon_i2c_writeq_flush(TWSX_BLOCK_STS_RESET_PTR, 696 + i2c->twsi_base + OCTEON_REG_BLOCK_STS(i2c)); 697 + for (i = 0; i <= len; i += 8) { 698 + __be64 buf = 0; 699 + 700 + /* Copy 8 bytes or remaining bytes from message buffer */ 701 + memcpy(&buf, &msgs[1].buf[i], min(8, msgs[1].len - i)); 702 + 703 + /* Byte-swap message data and write into FIFO */ 704 + buf = cpu_to_be64(buf); 705 + octeon_i2c_writeq_flush((u64)buf, i2c->twsi_base + OCTEON_REG_BLOCK_FIFO(i2c)); 706 + } 707 + if (set_ext) 708 + octeon_i2c_writeq_flush(ext, i2c->twsi_base + OCTEON_REG_SW_TWSI_EXT(i2c)); 709 + 710 + /* Send command to core (send data in FIFO) */ 711 + ret = octeon_i2c_hlc_cmd_send(i2c, cmd); 712 + if (ret) 713 + goto err; 714 + 715 + cmd = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c)); 716 + if ((cmd & SW_TWSI_R) == 0) { 717 + octeon_i2c_block_disable(i2c); 718 + return octeon_i2c_check_status(i2c, false); 719 + } 720 + 721 + err: 722 + octeon_i2c_block_disable(i2c); 723 + return ret; 724 + } 725 + 726 + /** 635 727 * octeon_i2c_xfer - The driver's xfer function 636 728 * @adap: Pointer to the i2c_adapter structure 637 729 * @msgs: Pointer to the messages to be processed ··· 776 630 if ((msgs[0].flags & I2C_M_RD) == 0 && 777 631 (msgs[1].flags & I2C_M_RECV_LEN) == 0 && 778 632 msgs[0].len > 0 && msgs[0].len <= 2 && 779 - msgs[1].len > 0 && msgs[1].len <= 8 && 633 + msgs[1].len > 0 && 780 634 msgs[0].addr == msgs[1].addr) { 781 - if (msgs[1].flags & I2C_M_RD) 782 - ret = octeon_i2c_hlc_comp_read(i2c, msgs); 783 - else 784 - ret = octeon_i2c_hlc_comp_write(i2c, msgs); 785 - goto out; 635 + if (msgs[1].len <= 8) { 636 + if (msgs[1].flags & I2C_M_RD) 637 + ret = octeon_i2c_hlc_comp_read(i2c, msgs); 638 + else 639 + ret = octeon_i2c_hlc_comp_write(i2c, msgs); 640 + goto out; 641 + } else if (msgs[1].len <= 1024 && OCTEON_REG_BLOCK_CTL(i2c)) { 642 + if (msgs[1].flags & I2C_M_RD) 643 + ret = octeon_i2c_hlc_block_comp_read(i2c, msgs); 644 + else 645 + ret = octeon_i2c_hlc_block_comp_write(i2c, msgs); 646 + goto out; 647 + } 786 648 } 787 649 } 788 650 }
+12 -1
drivers/i2c/busses/i2c-octeon-core.h
··· 96 96 unsigned int twsi_int; 97 97 unsigned int sw_twsi_ext; 98 98 unsigned int mode; 99 + unsigned int block_ctl; 100 + unsigned int block_sts; 101 + unsigned int block_fifo; 99 102 }; 100 103 101 104 #define OCTEON_REG_SW_TWSI(x) ((x)->roff.sw_twsi) 102 105 #define OCTEON_REG_TWSI_INT(x) ((x)->roff.twsi_int) 103 106 #define OCTEON_REG_SW_TWSI_EXT(x) ((x)->roff.sw_twsi_ext) 104 107 #define OCTEON_REG_MODE(x) ((x)->roff.mode) 108 + #define OCTEON_REG_BLOCK_CTL(x) ((x)->roff.block_ctl) 109 + #define OCTEON_REG_BLOCK_STS(x) ((x)->roff.block_sts) 110 + #define OCTEON_REG_BLOCK_FIFO(x) ((x)->roff.block_fifo) 105 111 106 - /* Set REFCLK_SRC and HS_MODE in TWSX_MODE register */ 112 + /* TWSX_MODE register */ 107 113 #define TWSX_MODE_REFCLK_SRC BIT(4) 114 + #define TWSX_MODE_BLOCK_MODE BIT(2) 108 115 #define TWSX_MODE_HS_MODE BIT(0) 109 116 #define TWSX_MODE_HS_MASK (TWSX_MODE_REFCLK_SRC | TWSX_MODE_HS_MODE) 117 + 118 + /* TWSX_BLOCK_STS register */ 119 + #define TWSX_BLOCK_STS_RESET_PTR BIT(0) 110 120 111 121 /* Set BUS_MON_RST to reset bus monitor */ 112 122 #define BUS_MON_RST_MASK BIT(3) ··· 133 123 void __iomem *twsi_base; 134 124 struct device *dev; 135 125 bool hlc_enabled; 126 + bool block_enabled; 136 127 bool broken_irq_mode; 137 128 bool broken_irq_check; 138 129 void (*int_enable)(struct octeon_i2c *);
+3
drivers/i2c/busses/i2c-thunderx-pcidrv.c
··· 168 168 i2c->roff.twsi_int = 0x1010; 169 169 i2c->roff.sw_twsi_ext = 0x1018; 170 170 i2c->roff.mode = 0x1038; 171 + i2c->roff.block_ctl = 0x1048; 172 + i2c->roff.block_sts = 0x1050; 173 + i2c->roff.block_fifo = 0x1058; 171 174 172 175 i2c->dev = dev; 173 176 pci_set_drvdata(pdev, i2c);