/* * Copyright 2019 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. *
*/
/* The T_I2C_POLL_US is defined as follows: * * "Define a timer interval (t_i2c_poll) equal to 10 times the * signalling period for the highest I2C transfer speed used in the * system and supported by DW_apb_i2c. For instance, if the highest * I2C data transfer mode is 400 kb/s, then t_i2c_poll is 25 us." -- * DesignWare DW_apb_i2c Databook, Version 1.21a, section 3.8.3.1, * page 56, with grammar and syntax corrections. * * Vcc for our device is at 1.8V which puts it at 400 kHz, * see Atmel AT24CM02 datasheet, section 8.3 DC Characteristics table, page 14. * * The procedure to disable the IP block is described in section * 3.8.3 Disabling DW_apb_i2c on page 56.
*/ #define I2C_SPEED_MODE_FAST 2 #define T_I2C_POLL_US 25 #define I2C_MAX_T_POLL_COUNT 1000
/* * Standard mode speed, These values are taken from SMUIO MAS, * but are different from what is given is * Synopsys spec. The values here are based on assumption * that refclock is 100MHz * * Configuration for standard mode; Speed = 100kbps * Scale linearly, for now only support standard speed clock * This will work only with 100M ref clock * * TBD:Change the calculation to take into account ref clock values also.
*/
/* The IC_TAR::IC_TAR field is 10-bits wide. * It takes a 7-bit or 10-bit addresses as an address, * i.e. no read/write bit--no wire format, just the address.
*/
WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TAR, address & 0x3FF);
}
/* If slave is not present */ if (REG_GET_FIELD(reg_c_tx_abrt_source,
CKSVII2C_IC_TX_ABRT_SOURCE,
ABRT_7B_ADDR_NOACK) == 1) {
ret |= I2C_NAK_7B_ADDR_NOACK;
smu_v11_0_i2c_clear_status(control);
} else { /* wait till some data is there in RXFIFO */ /* Poll for some byte in RXFIFO */ unsignedlong timeout_counter = jiffies + msecs_to_jiffies(20);
do { if (time_after(jiffies, timeout_counter)) {
ret |= I2C_SW_TIMEOUT; break;
}
} while (REG_GET_FIELD(reg_ic_status, CKSVII2C_IC_STATUS, RFNE) == 0);
}
return ret;
}
/** * smu_v11_0_i2c_transmit - Send a block of data over the I2C bus to a slave device. * * @control: I2C adapter reference * @address: The I2C address of the slave device. * @data: The data to transmit over the bus. * @numbytes: The amount of data to transmit. * @i2c_flag: Flags for transmission * * Returns 0 on success or error.
*/ static uint32_t smu_v11_0_i2c_transmit(struct i2c_adapter *control,
u16 address, u8 *data,
u32 numbytes, u32 i2c_flag)
{ struct amdgpu_smu_i2c_bus *smu_i2c = i2c_get_adapdata(control); struct amdgpu_device *adev = smu_i2c->adev;
u32 bytes_sent, reg, ret = I2C_OK; unsignedlong timeout_counter;
/* Set the I2C slave address */
smu_v11_0_i2c_set_address(control, address); /* Enable I2C */
smu_v11_0_i2c_enable(control, true);
/* Clear status bits */
smu_v11_0_i2c_clear_status(control);
timeout_counter = jiffies + msecs_to_jiffies(20);
while (numbytes > 0) {
reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS); if (!REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFNF)) { /* * We waited for too long for the transmission * FIFO to become not-full. Exit the loop * with error.
*/ if (time_after(jiffies, timeout_counter)) {
ret |= I2C_SW_TIMEOUT; goto Err;
}
} else {
reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT,
data[bytes_sent]);
/* Final message, final byte, must generate a * STOP to release the bus, i.e. don't hold * SCL low.
*/ if (numbytes == 1 && i2c_flag & I2C_M_STOP)
reg = REG_SET_FIELD(reg,
CKSVII2C_IC_DATA_CMD,
STOP, 1);
/* Record that the bytes were transmitted */
bytes_sent++;
numbytes--;
}
}
ret = smu_v11_0_i2c_poll_tx_status(control);
Err: /* Any error, no point in proceeding */ if (ret != I2C_OK) { if (ret & I2C_SW_TIMEOUT)
DRM_ERROR("TIMEOUT ERROR !!!");
if (ret & I2C_NAK_7B_ADDR_NOACK)
DRM_ERROR("Received I2C_NAK_7B_ADDR_NOACK !!!");
if (ret & I2C_NAK_TXDATA_NOACK)
DRM_ERROR("Received I2C_NAK_TXDATA_NOACK !!!");
}
return ret;
}
/** * smu_v11_0_i2c_receive - Receive a block of data over the I2C bus from a slave device. * * @control: I2C adapter reference * @address: The I2C address of the slave device. * @data: Placeholder to store received data. * @numbytes: The amount of data to transmit. * @i2c_flag: Flags for transmission * * Returns 0 on success or error.
*/ static uint32_t smu_v11_0_i2c_receive(struct i2c_adapter *control,
u16 address, u8 *data,
u32 numbytes, u32 i2c_flag)
{ struct amdgpu_smu_i2c_bus *smu_i2c = i2c_get_adapdata(control); struct amdgpu_device *adev = smu_i2c->adev;
uint32_t bytes_received, ret = I2C_OK;
bytes_received = 0;
/* Set the I2C slave address */
smu_v11_0_i2c_set_address(control, address);
/* Final message, final byte, must generate a STOP * to release the bus, i.e. don't hold SCL low.
*/ if (numbytes == 1 && i2c_flag & I2C_M_STOP)
reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD,
STOP, 1);
res = smu_v11_0_i2c_enable(control, false); if (res != I2C_OK) {
status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
enable = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE);
en_stat = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE_STATUS);
/* Nobody is using the I2C engine, yet it remains * active, possibly because someone missed to send * STOP.
*/
DRM_DEBUG_DRIVER("Aborting from fini: status:0x%08x " "enable:0x%08x enable_stat:0x%08x",
status, enable, en_stat);
smu_v11_0_i2c_abort(control);
}
/* Restore clock gating */
/* * TODO Reenabling clock gating seems to break subsequent SMU operation * on the I2C bus. My guess is that SMU doesn't disable clock gating like * we do here before working with the bus. So for now just don't restore * it but later work with SMU to see if they have this issue and can * update their code appropriately
*/ /* smu_v11_0_i2c_set_clock_gating(control, true); */
mutex_lock(&smu_i2c->mutex); if (!smu_v11_0_i2c_bus_lock(i2c))
DRM_ERROR("Failed to lock the bus from SMU"); else
adev->pm.bus_locked = true;
}
staticint trylock_bus(struct i2c_adapter *i2c, unsignedint flags)
{
WARN_ONCE(1, "This operation not supposed to run in atomic context!"); returnfalse;
}
staticint smu_v11_0_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msg, int num)
{ int i, ret;
u16 addr, dir;
smu_v11_0_i2c_init(i2c_adap);
/* From the client's point of view, this sequence of * messages-- the array i2c_msg *msg, is a single transaction * 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 (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,
msg + i,
i2c_flag); else
ret = smu_v11_0_i2c_write_data(i2c_adap,
msg + i,
i2c_flag);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.