Skip to content

Commit 1bc7347

Browse files
committed
drm/amdgpu/navi1x: add SMU i2c support (v2)
Enable SMU i2c bus access for navi1x asics. v2: add missing implementation Reviewed-by: Andrey Grodzovsky <[email protected]> Signed-off-by: Alex Deucher <[email protected]>
1 parent 0e0e11e commit 1bc7347

File tree

1 file changed

+239
-0
lines changed

1 file changed

+239
-0
lines changed

drivers/gpu/drm/amd/powerplay/navi10_ppt.c

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include <linux/firmware.h>
2727
#include <linux/pci.h>
28+
#include <linux/i2c.h>
2829
#include "amdgpu.h"
2930
#include "amdgpu_smu.h"
3031
#include "atomfirmware.h"
@@ -55,6 +56,8 @@
5556
#undef pr_info
5657
#undef pr_debug
5758

59+
#define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c))
60+
5861
#define FEATURE_MASK(feature) (1ULL << feature)
5962
#define SMC_DPM_FEATURE ( \
6063
FEATURE_MASK(FEATURE_DPM_PREFETCHER_BIT) | \
@@ -460,6 +463,8 @@ static int navi10_tables_init(struct smu_context *smu)
460463
PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
461464
SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetrics_t),
462465
PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
466+
SMU_TABLE_INIT(tables, SMU_TABLE_I2C_COMMANDS, sizeof(SwI2cRequest_t),
467+
PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
463468
SMU_TABLE_INIT(tables, SMU_TABLE_OVERDRIVE, sizeof(OverDriveTable_t),
464469
PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
465470
SMU_TABLE_INIT(tables, SMU_TABLE_PMSTATUSLOG, SMU11_TOOL_SIZE,
@@ -2258,11 +2263,245 @@ static int navi10_disable_umc_cdr_12gbps_workaround(struct smu_context *smu)
22582263
return navi10_dummy_pstate_control(smu, true);
22592264
}
22602265

2266+
static void navi10_fill_i2c_req(SwI2cRequest_t *req, bool write,
2267+
uint8_t address, uint32_t numbytes,
2268+
uint8_t *data)
2269+
{
2270+
int i;
2271+
2272+
BUG_ON(numbytes > MAX_SW_I2C_COMMANDS);
2273+
2274+
req->I2CcontrollerPort = 0;
2275+
req->I2CSpeed = 2;
2276+
req->SlaveAddress = address;
2277+
req->NumCmds = numbytes;
2278+
2279+
for (i = 0; i < numbytes; i++) {
2280+
SwI2cCmd_t *cmd = &req->SwI2cCmds[i];
2281+
2282+
/* First 2 bytes are always write for lower 2b EEPROM address */
2283+
if (i < 2)
2284+
cmd->Cmd = 1;
2285+
else
2286+
cmd->Cmd = write;
2287+
2288+
2289+
/* Add RESTART for read after address filled */
2290+
cmd->CmdConfig |= (i == 2 && !write) ? CMDCONFIG_RESTART_MASK : 0;
2291+
2292+
/* Add STOP in the end */
2293+
cmd->CmdConfig |= (i == (numbytes - 1)) ? CMDCONFIG_STOP_MASK : 0;
2294+
2295+
/* Fill with data regardless if read or write to simplify code */
2296+
cmd->RegisterAddr = data[i];
2297+
}
2298+
}
2299+
2300+
static int navi10_i2c_read_data(struct i2c_adapter *control,
2301+
uint8_t address,
2302+
uint8_t *data,
2303+
uint32_t numbytes)
2304+
{
2305+
uint32_t i, ret = 0;
2306+
SwI2cRequest_t req;
2307+
struct amdgpu_device *adev = to_amdgpu_device(control);
2308+
struct smu_table_context *smu_table = &adev->smu.smu_table;
2309+
struct smu_table *table = &smu_table->driver_table;
2310+
2311+
memset(&req, 0, sizeof(req));
2312+
navi10_fill_i2c_req(&req, false, address, numbytes, data);
2313+
2314+
mutex_lock(&adev->smu.mutex);
2315+
/* Now read data starting with that address */
2316+
ret = smu_cmn_update_table(&adev->smu, SMU_TABLE_I2C_COMMANDS, 0, &req,
2317+
true);
2318+
mutex_unlock(&adev->smu.mutex);
2319+
2320+
if (!ret) {
2321+
SwI2cRequest_t *res = (SwI2cRequest_t *)table->cpu_addr;
2322+
2323+
/* Assume SMU fills res.SwI2cCmds[i].Data with read bytes */
2324+
for (i = 0; i < numbytes; i++)
2325+
data[i] = res->SwI2cCmds[i].Data;
2326+
2327+
dev_dbg(adev->dev, "navi10_i2c_read_data, address = %x, bytes = %d, data :",
2328+
(uint16_t)address, numbytes);
2329+
2330+
print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE,
2331+
8, 1, data, numbytes, false);
2332+
} else
2333+
dev_err(adev->dev, "navi10_i2c_read_data - error occurred :%x", ret);
2334+
2335+
return ret;
2336+
}
2337+
2338+
static int navi10_i2c_write_data(struct i2c_adapter *control,
2339+
uint8_t address,
2340+
uint8_t *data,
2341+
uint32_t numbytes)
2342+
{
2343+
uint32_t ret;
2344+
SwI2cRequest_t req;
2345+
struct amdgpu_device *adev = to_amdgpu_device(control);
2346+
2347+
memset(&req, 0, sizeof(req));
2348+
navi10_fill_i2c_req(&req, true, address, numbytes, data);
2349+
2350+
mutex_lock(&adev->smu.mutex);
2351+
ret = smu_cmn_update_table(&adev->smu, SMU_TABLE_I2C_COMMANDS, 0, &req, true);
2352+
mutex_unlock(&adev->smu.mutex);
2353+
2354+
if (!ret) {
2355+
dev_dbg(adev->dev, "navi10_i2c_write(), address = %x, bytes = %d , data: ",
2356+
(uint16_t)address, numbytes);
2357+
2358+
print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE,
2359+
8, 1, data, numbytes, false);
2360+
/*
2361+
* According to EEPROM spec there is a MAX of 10 ms required for
2362+
* EEPROM to flush internal RX buffer after STOP was issued at the
2363+
* end of write transaction. During this time the EEPROM will not be
2364+
* responsive to any more commands - so wait a bit more.
2365+
*/
2366+
msleep(10);
2367+
2368+
} else
2369+
dev_err(adev->dev, "navi10_i2c_write- error occurred :%x", ret);
2370+
2371+
return ret;
2372+
}
2373+
2374+
static int navi10_i2c_xfer(struct i2c_adapter *i2c_adap,
2375+
struct i2c_msg *msgs, int num)
2376+
{
2377+
uint32_t i, j, ret, data_size, data_chunk_size, next_eeprom_addr = 0;
2378+
uint8_t *data_ptr, data_chunk[MAX_SW_I2C_COMMANDS] = { 0 };
2379+
2380+
for (i = 0; i < num; i++) {
2381+
/*
2382+
* SMU interface allows at most MAX_SW_I2C_COMMANDS bytes of data at
2383+
* once and hence the data needs to be spliced into chunks and sent each
2384+
* chunk separately
2385+
*/
2386+
data_size = msgs[i].len - 2;
2387+
data_chunk_size = MAX_SW_I2C_COMMANDS - 2;
2388+
next_eeprom_addr = (msgs[i].buf[0] << 8 & 0xff00) | (msgs[i].buf[1] & 0xff);
2389+
data_ptr = msgs[i].buf + 2;
2390+
2391+
for (j = 0; j < data_size / data_chunk_size; j++) {
2392+
/* Insert the EEPROM dest addess, bits 0-15 */
2393+
data_chunk[0] = ((next_eeprom_addr >> 8) & 0xff);
2394+
data_chunk[1] = (next_eeprom_addr & 0xff);
2395+
2396+
if (msgs[i].flags & I2C_M_RD) {
2397+
ret = navi10_i2c_read_data(i2c_adap,
2398+
(uint8_t)msgs[i].addr,
2399+
data_chunk, MAX_SW_I2C_COMMANDS);
2400+
2401+
memcpy(data_ptr, data_chunk + 2, data_chunk_size);
2402+
} else {
2403+
2404+
memcpy(data_chunk + 2, data_ptr, data_chunk_size);
2405+
2406+
ret = navi10_i2c_write_data(i2c_adap,
2407+
(uint8_t)msgs[i].addr,
2408+
data_chunk, MAX_SW_I2C_COMMANDS);
2409+
}
2410+
2411+
if (ret) {
2412+
num = -EIO;
2413+
goto fail;
2414+
}
2415+
2416+
next_eeprom_addr += data_chunk_size;
2417+
data_ptr += data_chunk_size;
2418+
}
2419+
2420+
if (data_size % data_chunk_size) {
2421+
data_chunk[0] = ((next_eeprom_addr >> 8) & 0xff);
2422+
data_chunk[1] = (next_eeprom_addr & 0xff);
2423+
2424+
if (msgs[i].flags & I2C_M_RD) {
2425+
ret = navi10_i2c_read_data(i2c_adap,
2426+
(uint8_t)msgs[i].addr,
2427+
data_chunk, (data_size % data_chunk_size) + 2);
2428+
2429+
memcpy(data_ptr, data_chunk + 2, data_size % data_chunk_size);
2430+
} else {
2431+
memcpy(data_chunk + 2, data_ptr, data_size % data_chunk_size);
2432+
2433+
ret = navi10_i2c_write_data(i2c_adap,
2434+
(uint8_t)msgs[i].addr,
2435+
data_chunk, (data_size % data_chunk_size) + 2);
2436+
}
2437+
2438+
if (ret) {
2439+
num = -EIO;
2440+
goto fail;
2441+
}
2442+
}
2443+
}
2444+
2445+
fail:
2446+
return num;
2447+
}
2448+
2449+
static u32 navi10_i2c_func(struct i2c_adapter *adap)
2450+
{
2451+
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
2452+
}
2453+
2454+
2455+
static const struct i2c_algorithm navi10_i2c_algo = {
2456+
.master_xfer = navi10_i2c_xfer,
2457+
.functionality = navi10_i2c_func,
2458+
};
2459+
2460+
static bool navi10_i2c_adapter_is_added(struct i2c_adapter *control)
2461+
{
2462+
struct amdgpu_device *adev = to_amdgpu_device(control);
2463+
2464+
return control->dev.parent == &adev->pdev->dev;
2465+
}
2466+
2467+
static int navi10_i2c_control_init(struct smu_context *smu, struct i2c_adapter *control)
2468+
{
2469+
struct amdgpu_device *adev = to_amdgpu_device(control);
2470+
int res;
2471+
2472+
/* smu_i2c_eeprom_init may be called twice in sriov */
2473+
if (navi10_i2c_adapter_is_added(control))
2474+
return 0;
2475+
2476+
control->owner = THIS_MODULE;
2477+
control->class = I2C_CLASS_SPD;
2478+
control->dev.parent = &adev->pdev->dev;
2479+
control->algo = &navi10_i2c_algo;
2480+
snprintf(control->name, sizeof(control->name), "AMDGPU SMU");
2481+
2482+
res = i2c_add_adapter(control);
2483+
if (res)
2484+
DRM_ERROR("Failed to register hw i2c, err: %d\n", res);
2485+
2486+
return res;
2487+
}
2488+
2489+
static void navi10_i2c_control_fini(struct smu_context *smu, struct i2c_adapter *control)
2490+
{
2491+
if (!navi10_i2c_adapter_is_added(control))
2492+
return;
2493+
2494+
i2c_del_adapter(control);
2495+
}
2496+
2497+
22612498
static const struct pptable_funcs navi10_ppt_funcs = {
22622499
.get_allowed_feature_mask = navi10_get_allowed_feature_mask,
22632500
.set_default_dpm_table = navi10_set_default_dpm_table,
22642501
.dpm_set_vcn_enable = navi10_dpm_set_vcn_enable,
22652502
.dpm_set_jpeg_enable = navi10_dpm_set_jpeg_enable,
2503+
.i2c_init = navi10_i2c_control_init,
2504+
.i2c_fini = navi10_i2c_control_fini,
22662505
.print_clk_levels = navi10_print_clk_levels,
22672506
.force_clk_levels = navi10_force_clk_levels,
22682507
.populate_umd_state_clk = navi10_populate_umd_state_clk,

0 commit comments

Comments
 (0)