|
25 | 25 |
|
26 | 26 | #include <linux/firmware.h>
|
27 | 27 | #include <linux/pci.h>
|
| 28 | +#include <linux/i2c.h> |
28 | 29 | #include "amdgpu.h"
|
29 | 30 | #include "amdgpu_smu.h"
|
30 | 31 | #include "atomfirmware.h"
|
|
55 | 56 | #undef pr_info
|
56 | 57 | #undef pr_debug
|
57 | 58 |
|
| 59 | +#define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c)) |
| 60 | + |
58 | 61 | #define FEATURE_MASK(feature) (1ULL << feature)
|
59 | 62 | #define SMC_DPM_FEATURE ( \
|
60 | 63 | FEATURE_MASK(FEATURE_DPM_PREFETCHER_BIT) | \
|
@@ -460,6 +463,8 @@ static int navi10_tables_init(struct smu_context *smu)
|
460 | 463 | PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
|
461 | 464 | SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetrics_t),
|
462 | 465 | 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); |
463 | 468 | SMU_TABLE_INIT(tables, SMU_TABLE_OVERDRIVE, sizeof(OverDriveTable_t),
|
464 | 469 | PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
|
465 | 470 | 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)
|
2258 | 2263 | return navi10_dummy_pstate_control(smu, true);
|
2259 | 2264 | }
|
2260 | 2265 |
|
| 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 | + |
2261 | 2498 | static const struct pptable_funcs navi10_ppt_funcs = {
|
2262 | 2499 | .get_allowed_feature_mask = navi10_get_allowed_feature_mask,
|
2263 | 2500 | .set_default_dpm_table = navi10_set_default_dpm_table,
|
2264 | 2501 | .dpm_set_vcn_enable = navi10_dpm_set_vcn_enable,
|
2265 | 2502 | .dpm_set_jpeg_enable = navi10_dpm_set_jpeg_enable,
|
| 2503 | + .i2c_init = navi10_i2c_control_init, |
| 2504 | + .i2c_fini = navi10_i2c_control_fini, |
2266 | 2505 | .print_clk_levels = navi10_print_clk_levels,
|
2267 | 2506 | .force_clk_levels = navi10_force_clk_levels,
|
2268 | 2507 | .populate_umd_state_clk = navi10_populate_umd_state_clk,
|
|
0 commit comments