Skip to content

Commit 63ef02d

Browse files
Aryan SrivastavaAndi Shyti
authored andcommitted
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>
1 parent e1902d6 commit 63ef02d

3 files changed

Lines changed: 175 additions & 7 deletions

File tree

drivers/i2c/busses/i2c-octeon-core.c

Lines changed: 160 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,32 @@ static void octeon_i2c_hlc_disable(struct octeon_i2c *i2c)
135135
octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB);
136136
}
137137

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+
138164
/**
139165
* octeon_i2c_hlc_wait - wait for an HLC operation to complete
140166
* @i2c: The struct octeon_i2c
@@ -281,6 +307,7 @@ static int octeon_i2c_start(struct octeon_i2c *i2c)
281307
u8 stat;
282308

283309
octeon_i2c_hlc_disable(i2c);
310+
octeon_i2c_block_disable(i2c);
284311

285312
octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STA);
286313
ret = octeon_i2c_wait(i2c);
@@ -604,6 +631,125 @@ static int octeon_i2c_hlc_comp_write(struct octeon_i2c *i2c, struct i2c_msg *msg
604631
return ret;
605632
}
606633

634+
/**
635+
* octeon_i2c_hlc_block_comp_read - high-level-controller composite block read
636+
* @i2c: The struct octeon_i2c
637+
* @msgs: msg[0] contains address, place read data into msg[1]
638+
*
639+
* i2c core command is constructed and written into the SW_TWSI register.
640+
* The execution of the command will result in requested data being
641+
* placed into a FIFO buffer, ready to be read.
642+
* Used in the case where the i2c xfer is for greater than 8 bytes of read data.
643+
*
644+
* Returns: 0 on success, otherwise a negative errno.
645+
*/
646+
static int octeon_i2c_hlc_block_comp_read(struct octeon_i2c *i2c, struct i2c_msg *msgs)
647+
{
648+
int ret;
649+
u16 len, i;
650+
u64 cmd;
651+
652+
octeon_i2c_hlc_enable(i2c);
653+
octeon_i2c_block_enable(i2c);
654+
655+
/* Write (size - 1) into block control register */
656+
len = msgs[1].len - 1;
657+
octeon_i2c_writeq_flush((u64)len, i2c->twsi_base + OCTEON_REG_BLOCK_CTL(i2c));
658+
659+
/* Prepare core command */
660+
cmd = SW_TWSI_V | SW_TWSI_R | SW_TWSI_SOVR | SW_TWSI_OP_7_IA;
661+
cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT;
662+
663+
/* Send core command */
664+
ret = octeon_i2c_hlc_read_cmd(i2c, msgs[0], cmd);
665+
if (ret)
666+
goto err;
667+
668+
cmd = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c));
669+
if ((cmd & SW_TWSI_R) == 0) {
670+
octeon_i2c_block_disable(i2c);
671+
return octeon_i2c_check_status(i2c, false);
672+
}
673+
674+
/* read data in FIFO */
675+
octeon_i2c_writeq_flush(TWSX_BLOCK_STS_RESET_PTR,
676+
i2c->twsi_base + OCTEON_REG_BLOCK_STS(i2c));
677+
for (i = 0; i <= len; i += 8) {
678+
/* Byte-swap FIFO data and copy into msg buffer */
679+
__be64 rd = cpu_to_be64(__raw_readq(i2c->twsi_base + OCTEON_REG_BLOCK_FIFO(i2c)));
680+
681+
memcpy(&msgs[1].buf[i], &rd, min(8, msgs[1].len - i));
682+
}
683+
684+
err:
685+
octeon_i2c_block_disable(i2c);
686+
return ret;
687+
}
688+
689+
/**
690+
* octeon_i2c_hlc_block_comp_write - high-level-controller composite block write
691+
* @i2c: The struct octeon_i2c
692+
* @msgs: msg[0] contains address, msg[1] contains data to be written
693+
*
694+
* i2c core command is constructed and write data is written into the FIFO buffer.
695+
* The execution of the command will result in HW write, using the data in FIFO.
696+
* Used in the case where the i2c xfer is for greater than 8 bytes of write data.
697+
*
698+
* Returns: 0 on success, otherwise a negative errno.
699+
*/
700+
static int octeon_i2c_hlc_block_comp_write(struct octeon_i2c *i2c, struct i2c_msg *msgs)
701+
{
702+
bool set_ext;
703+
int ret;
704+
u16 len, i;
705+
u64 cmd, ext = 0;
706+
707+
octeon_i2c_hlc_enable(i2c);
708+
octeon_i2c_block_enable(i2c);
709+
710+
/* Write (size - 1) into block control register */
711+
len = msgs[1].len - 1;
712+
octeon_i2c_writeq_flush((u64)len, i2c->twsi_base + OCTEON_REG_BLOCK_CTL(i2c));
713+
714+
/* Prepare core command */
715+
cmd = SW_TWSI_V | SW_TWSI_SOVR | SW_TWSI_OP_7_IA;
716+
cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT;
717+
718+
/* Set parameters for extended message (if required) */
719+
set_ext = octeon_i2c_hlc_ext(i2c, msgs[0], &cmd, &ext);
720+
721+
/* Write msg into FIFO buffer */
722+
octeon_i2c_writeq_flush(TWSX_BLOCK_STS_RESET_PTR,
723+
i2c->twsi_base + OCTEON_REG_BLOCK_STS(i2c));
724+
for (i = 0; i <= len; i += 8) {
725+
__be64 buf = 0;
726+
727+
/* Copy 8 bytes or remaining bytes from message buffer */
728+
memcpy(&buf, &msgs[1].buf[i], min(8, msgs[1].len - i));
729+
730+
/* Byte-swap message data and write into FIFO */
731+
buf = cpu_to_be64(buf);
732+
octeon_i2c_writeq_flush((u64)buf, i2c->twsi_base + OCTEON_REG_BLOCK_FIFO(i2c));
733+
}
734+
if (set_ext)
735+
octeon_i2c_writeq_flush(ext, i2c->twsi_base + OCTEON_REG_SW_TWSI_EXT(i2c));
736+
737+
/* Send command to core (send data in FIFO) */
738+
ret = octeon_i2c_hlc_cmd_send(i2c, cmd);
739+
if (ret)
740+
goto err;
741+
742+
cmd = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c));
743+
if ((cmd & SW_TWSI_R) == 0) {
744+
octeon_i2c_block_disable(i2c);
745+
return octeon_i2c_check_status(i2c, false);
746+
}
747+
748+
err:
749+
octeon_i2c_block_disable(i2c);
750+
return ret;
751+
}
752+
607753
/**
608754
* octeon_i2c_xfer - The driver's xfer function
609755
* @adap: Pointer to the i2c_adapter structure
@@ -630,13 +776,21 @@ int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
630776
if ((msgs[0].flags & I2C_M_RD) == 0 &&
631777
(msgs[1].flags & I2C_M_RECV_LEN) == 0 &&
632778
msgs[0].len > 0 && msgs[0].len <= 2 &&
633-
msgs[1].len > 0 && msgs[1].len <= 8 &&
779+
msgs[1].len > 0 &&
634780
msgs[0].addr == msgs[1].addr) {
635-
if (msgs[1].flags & I2C_M_RD)
636-
ret = octeon_i2c_hlc_comp_read(i2c, msgs);
637-
else
638-
ret = octeon_i2c_hlc_comp_write(i2c, msgs);
639-
goto out;
781+
if (msgs[1].len <= 8) {
782+
if (msgs[1].flags & I2C_M_RD)
783+
ret = octeon_i2c_hlc_comp_read(i2c, msgs);
784+
else
785+
ret = octeon_i2c_hlc_comp_write(i2c, msgs);
786+
goto out;
787+
} else if (msgs[1].len <= 1024 && OCTEON_REG_BLOCK_CTL(i2c)) {
788+
if (msgs[1].flags & I2C_M_RD)
789+
ret = octeon_i2c_hlc_block_comp_read(i2c, msgs);
790+
else
791+
ret = octeon_i2c_hlc_block_comp_write(i2c, msgs);
792+
goto out;
793+
}
640794
}
641795
}
642796
}

drivers/i2c/busses/i2c-octeon-core.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,18 +96,28 @@ struct octeon_i2c_reg_offset {
9696
unsigned int twsi_int;
9797
unsigned int sw_twsi_ext;
9898
unsigned int mode;
99+
unsigned int block_ctl;
100+
unsigned int block_sts;
101+
unsigned int block_fifo;
99102
};
100103

101104
#define OCTEON_REG_SW_TWSI(x) ((x)->roff.sw_twsi)
102105
#define OCTEON_REG_TWSI_INT(x) ((x)->roff.twsi_int)
103106
#define OCTEON_REG_SW_TWSI_EXT(x) ((x)->roff.sw_twsi_ext)
104107
#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)
105111

106-
/* Set REFCLK_SRC and HS_MODE in TWSX_MODE register */
112+
/* TWSX_MODE register */
107113
#define TWSX_MODE_REFCLK_SRC BIT(4)
114+
#define TWSX_MODE_BLOCK_MODE BIT(2)
108115
#define TWSX_MODE_HS_MODE BIT(0)
109116
#define TWSX_MODE_HS_MASK (TWSX_MODE_REFCLK_SRC | TWSX_MODE_HS_MODE)
110117

118+
/* TWSX_BLOCK_STS register */
119+
#define TWSX_BLOCK_STS_RESET_PTR BIT(0)
120+
111121
/* Set BUS_MON_RST to reset bus monitor */
112122
#define BUS_MON_RST_MASK BIT(3)
113123

@@ -123,6 +133,7 @@ struct octeon_i2c {
123133
void __iomem *twsi_base;
124134
struct device *dev;
125135
bool hlc_enabled;
136+
bool block_enabled;
126137
bool broken_irq_mode;
127138
bool broken_irq_check;
128139
void (*int_enable)(struct octeon_i2c *);

drivers/i2c/busses/i2c-thunderx-pcidrv.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ static int thunder_i2c_probe_pci(struct pci_dev *pdev,
168168
i2c->roff.twsi_int = 0x1010;
169169
i2c->roff.sw_twsi_ext = 0x1018;
170170
i2c->roff.mode = 0x1038;
171+
i2c->roff.block_ctl = 0x1048;
172+
i2c->roff.block_sts = 0x1050;
173+
i2c->roff.block_fifo = 0x1058;
171174

172175
i2c->dev = dev;
173176
pci_set_drvdata(pdev, i2c);

0 commit comments

Comments
 (0)