drm/amdgpu: Fix Vega20 I2C to be agnostic (v2)

Teach Vega20 I2C to be agnostic. Allow addressing
different devices while the master holds the bus.
Set STOP as per the controller's specification.

v2: Qualify generating ReSTART before the 1st byte
    of the message, when set by the caller, as
    those functions are separated, as caught by
    Andrey G.

Cc: Jean Delvare <jdelvare@suse.de>
Cc: Alexander Deucher <Alexander.Deucher@amd.com>
Cc: Andrey Grodzovsky <Andrey.Grodzovsky@amd.com>
Cc: Lijo Lazar <Lijo.Lazar@amd.com>
Cc: Stanley Yang <Stanley.Yang@amd.com>
Cc: Hawking Zhang <Hawking.Zhang@amd.com>
Signed-off-by: Luben Tuikov <luben.tuikov@amd.com>
Acked-by: Alexander Deucher <Alexander.Deucher@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Luben Tuikov 2021-01-26 15:56:45 -05:00 committed by Alex Deucher
parent 35ed27032c
commit daaa75fd98
2 changed files with 70 additions and 41 deletions

View File

@ -41,10 +41,10 @@ int amdgpu_eeprom_xfer(struct i2c_adapter *i2c_adap,
}, },
{ {
.addr = slave_addr, .addr = slave_addr,
.flags = read ? I2C_M_RD: 0, .flags = read ? I2C_M_RD : 0,
.len = bytes, .len = bytes,
.buf = eeprom_buf, .buf = eeprom_buf,
} },
}; };
int r; int r;

View File

@ -41,9 +41,7 @@
#define I2C_SW_TIMEOUT 8 #define I2C_SW_TIMEOUT 8
#define I2C_ABORT 0x10 #define I2C_ABORT 0x10
/* I2C transaction flags */ #define I2C_X_RESTART BIT(31)
#define I2C_NO_STOP 1
#define I2C_RESTART 2
#define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c)) #define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c))
@ -205,9 +203,6 @@ static uint32_t smu_v11_0_i2c_poll_rx_status(struct i2c_adapter *control)
return ret; return ret;
} }
/** /**
* smu_v11_0_i2c_transmit - Send a block of data over the I2C bus to a slave device. * smu_v11_0_i2c_transmit - Send a block of data over the I2C bus to a slave device.
* *
@ -252,21 +247,22 @@ static uint32_t smu_v11_0_i2c_transmit(struct i2c_adapter *control,
reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS); reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
if (REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFNF)) { if (REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFNF)) {
do { do {
reg = 0;
/*
* Prepare transaction, no need to set RESTART. I2C engine will send
* START as soon as it sees data in TXFIFO
*/
if (bytes_sent == 0)
reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, RESTART,
(i2c_flag & I2C_RESTART) ? 1 : 0);
reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT, data[bytes_sent]); reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT, data[bytes_sent]);
/* determine if we need to send STOP bit or not */ /* Final message, final byte, must
if (numbytes == 1) * generate a STOP, to release the
/* Final transaction, so send stop unless I2C_NO_STOP */ * bus, i.e. don't hold SCL low.
reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, STOP, */
(i2c_flag & I2C_NO_STOP) ? 0 : 1); if (numbytes == 1 && i2c_flag & I2C_M_STOP)
reg = REG_SET_FIELD(reg,
CKSVII2C_IC_DATA_CMD,
STOP, 1);
if (bytes_sent == 0 && i2c_flag & I2C_X_RESTART)
reg = REG_SET_FIELD(reg,
CKSVII2C_IC_DATA_CMD,
RESTART, 1);
/* Write */ /* Write */
reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, CMD, 0); reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, CMD, 0);
WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD, reg); WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD, reg);
@ -341,23 +337,21 @@ static uint32_t smu_v11_0_i2c_receive(struct i2c_adapter *control,
smu_v11_0_i2c_clear_status(control); smu_v11_0_i2c_clear_status(control);
/* Prepare transaction */ /* Prepare transaction */
/* Each time we disable I2C, so this is not a restart */
if (bytes_received == 0)
reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, RESTART,
(i2c_flag & I2C_RESTART) ? 1 : 0);
reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT, 0); reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT, 0);
/* Read */ /* Read */
reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, CMD, 1); reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, CMD, 1);
/* Transmitting last byte */ /* Final message, final byte, must generate a STOP
if (numbytes == 1) * to release the bus, i.e. don't hold SCL low.
/* Final transaction, so send stop if requested */ */
reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, STOP, if (numbytes == 1 && i2c_flag & I2C_M_STOP)
(i2c_flag & I2C_NO_STOP) ? 0 : 1); reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD,
STOP, 1);
if (bytes_received == 0 && i2c_flag & I2C_X_RESTART)
reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD,
RESTART, 1);
WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD, reg); WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD, reg);
@ -591,23 +585,59 @@ static const struct i2c_lock_operations smu_v11_0_i2c_i2c_lock_ops = {
}; };
static int smu_v11_0_i2c_xfer(struct i2c_adapter *i2c_adap, static int smu_v11_0_i2c_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg *msgs, int num) struct i2c_msg *msg, int num)
{ {
int i, ret; int i, ret;
u16 addr, dir;
smu_v11_0_i2c_init(i2c_adap); smu_v11_0_i2c_init(i2c_adap);
for (i = 0; i < num; i++) { /* From the client's point of view, this sequence of
uint32_t i2c_flag = ((msgs[i].flags & I2C_M_NOSTART) ? 0 : I2C_RESTART) || * messages-- the array i2c_msg *msg, is a single transaction
(((msgs[i].flags & I2C_M_STOP) ? 0 : I2C_NO_STOP)); * on the bus, starting with START and ending with STOP.
*
* The client is welcome to send any sequence of messages in
* this array, as processing under this function here is
* striving to be agnostic.
*
* Record the first address and direction we see. If either
* changes for a subsequent message, generate ReSTART. The
* DW_apb_i2c databook, v1.21a, specifies that ReSTART is
* generated when the direction changes, with the default IP
* block parameter settings, but it doesn't specify if ReSTART
* is generated when the address changes (possibly...). We
* don't rely on the default IP block parameter settings as
* the block is shared and they may change.
*/
if (num > 0) {
addr = msg[0].addr;
dir = msg[0].flags & I2C_M_RD;
}
if (msgs[i].flags & I2C_M_RD) for (i = 0; i < num; i++) {
u32 i2c_flag = 0;
if (msg[i].addr != addr || (msg[i].flags ^ dir) & I2C_M_RD) {
addr = msg[i].addr;
dir = msg[i].flags & I2C_M_RD;
i2c_flag |= I2C_X_RESTART;
}
if (i == num - 1) {
/* Set the STOP bit on the last message, so
* that the IP block generates a STOP after
* the last byte of the message.
*/
i2c_flag |= I2C_M_STOP;
}
if (msg[i].flags & I2C_M_RD)
ret = smu_v11_0_i2c_read_data(i2c_adap, ret = smu_v11_0_i2c_read_data(i2c_adap,
msgs + i, msg + i,
i2c_flag); i2c_flag);
else else
ret = smu_v11_0_i2c_write_data(i2c_adap, ret = smu_v11_0_i2c_write_data(i2c_adap,
msgs + i, msg + i,
i2c_flag); i2c_flag);
if (ret != I2C_OK) { if (ret != I2C_OK) {
@ -625,7 +655,6 @@ static u32 smu_v11_0_i2c_func(struct i2c_adapter *adap)
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
} }
static const struct i2c_algorithm smu_v11_0_i2c_algo = { static const struct i2c_algorithm smu_v11_0_i2c_algo = {
.master_xfer = smu_v11_0_i2c_xfer, .master_xfer = smu_v11_0_i2c_xfer,
.functionality = smu_v11_0_i2c_func, .functionality = smu_v11_0_i2c_func,