|
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"
|
|
57 | 58 | #undef pr_info
|
58 | 59 | #undef pr_debug
|
59 | 60 |
|
| 61 | +#define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c)) |
| 62 | + |
60 | 63 | #define FEATURE_MASK(feature) (1ULL << feature)
|
61 | 64 | #define SMC_DPM_FEATURE ( \
|
62 | 65 | FEATURE_MASK(FEATURE_DPM_PREFETCHER_BIT) | \
|
@@ -367,6 +370,8 @@ static int sienna_cichlid_tables_init(struct smu_context *smu)
|
367 | 370 | PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
|
368 | 371 | SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetrics_t),
|
369 | 372 | PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
|
| 373 | + SMU_TABLE_INIT(tables, SMU_TABLE_I2C_COMMANDS, sizeof(SwI2cRequest_t), |
| 374 | + PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM); |
370 | 375 | SMU_TABLE_INIT(tables, SMU_TABLE_OVERDRIVE, sizeof(OverDriveTable_t),
|
371 | 376 | PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
|
372 | 377 | SMU_TABLE_INIT(tables, SMU_TABLE_PMSTATUSLOG, SMU11_TOOL_SIZE,
|
@@ -2425,11 +2430,245 @@ static void sienna_cichlid_dump_pptable(struct smu_context *smu)
|
2425 | 2430 | dev_info(smu->adev->dev, "MmHubPadding[7] = 0x%x\n", pptable->MmHubPadding[7]);
|
2426 | 2431 | }
|
2427 | 2432 |
|
| 2433 | +static void sienna_cichlid_fill_i2c_req(SwI2cRequest_t *req, bool write, |
| 2434 | + uint8_t address, uint32_t numbytes, |
| 2435 | + uint8_t *data) |
| 2436 | +{ |
| 2437 | + int i; |
| 2438 | + |
| 2439 | + BUG_ON(numbytes > MAX_SW_I2C_COMMANDS); |
| 2440 | + |
| 2441 | + req->I2CcontrollerPort = 0; |
| 2442 | + req->I2CSpeed = 2; |
| 2443 | + req->SlaveAddress = address; |
| 2444 | + req->NumCmds = numbytes; |
| 2445 | + |
| 2446 | + for (i = 0; i < numbytes; i++) { |
| 2447 | + SwI2cCmd_t *cmd = &req->SwI2cCmds[i]; |
| 2448 | + |
| 2449 | + /* First 2 bytes are always write for lower 2b EEPROM address */ |
| 2450 | + if (i < 2) |
| 2451 | + cmd->CmdConfig = CMDCONFIG_READWRITE_MASK; |
| 2452 | + else |
| 2453 | + cmd->CmdConfig = write ? CMDCONFIG_READWRITE_MASK : 0; |
| 2454 | + |
| 2455 | + |
| 2456 | + /* Add RESTART for read after address filled */ |
| 2457 | + cmd->CmdConfig |= (i == 2 && !write) ? CMDCONFIG_RESTART_MASK : 0; |
| 2458 | + |
| 2459 | + /* Add STOP in the end */ |
| 2460 | + cmd->CmdConfig |= (i == (numbytes - 1)) ? CMDCONFIG_STOP_MASK : 0; |
| 2461 | + |
| 2462 | + /* Fill with data regardless if read or write to simplify code */ |
| 2463 | + cmd->ReadWriteData = data[i]; |
| 2464 | + } |
| 2465 | +} |
| 2466 | + |
| 2467 | +static int sienna_cichlid_i2c_read_data(struct i2c_adapter *control, |
| 2468 | + uint8_t address, |
| 2469 | + uint8_t *data, |
| 2470 | + uint32_t numbytes) |
| 2471 | +{ |
| 2472 | + uint32_t i, ret = 0; |
| 2473 | + SwI2cRequest_t req; |
| 2474 | + struct amdgpu_device *adev = to_amdgpu_device(control); |
| 2475 | + struct smu_table_context *smu_table = &adev->smu.smu_table; |
| 2476 | + struct smu_table *table = &smu_table->driver_table; |
| 2477 | + |
| 2478 | + memset(&req, 0, sizeof(req)); |
| 2479 | + sienna_cichlid_fill_i2c_req(&req, false, address, numbytes, data); |
| 2480 | + |
| 2481 | + mutex_lock(&adev->smu.mutex); |
| 2482 | + /* Now read data starting with that address */ |
| 2483 | + ret = smu_cmn_update_table(&adev->smu, SMU_TABLE_I2C_COMMANDS, 0, &req, |
| 2484 | + true); |
| 2485 | + mutex_unlock(&adev->smu.mutex); |
| 2486 | + |
| 2487 | + if (!ret) { |
| 2488 | + SwI2cRequest_t *res = (SwI2cRequest_t *)table->cpu_addr; |
| 2489 | + |
| 2490 | + /* Assume SMU fills res.SwI2cCmds[i].Data with read bytes */ |
| 2491 | + for (i = 0; i < numbytes; i++) |
| 2492 | + data[i] = res->SwI2cCmds[i].ReadWriteData; |
| 2493 | + |
| 2494 | + dev_dbg(adev->dev, "sienna_cichlid_i2c_read_data, address = %x, bytes = %d, data :", |
| 2495 | + (uint16_t)address, numbytes); |
| 2496 | + |
| 2497 | + print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, |
| 2498 | + 8, 1, data, numbytes, false); |
| 2499 | + } else |
| 2500 | + dev_err(adev->dev, "sienna_cichlid_i2c_read_data - error occurred :%x", ret); |
| 2501 | + |
| 2502 | + return ret; |
| 2503 | +} |
| 2504 | + |
| 2505 | +static int sienna_cichlid_i2c_write_data(struct i2c_adapter *control, |
| 2506 | + uint8_t address, |
| 2507 | + uint8_t *data, |
| 2508 | + uint32_t numbytes) |
| 2509 | +{ |
| 2510 | + uint32_t ret; |
| 2511 | + SwI2cRequest_t req; |
| 2512 | + struct amdgpu_device *adev = to_amdgpu_device(control); |
| 2513 | + |
| 2514 | + memset(&req, 0, sizeof(req)); |
| 2515 | + sienna_cichlid_fill_i2c_req(&req, true, address, numbytes, data); |
| 2516 | + |
| 2517 | + mutex_lock(&adev->smu.mutex); |
| 2518 | + ret = smu_cmn_update_table(&adev->smu, SMU_TABLE_I2C_COMMANDS, 0, &req, true); |
| 2519 | + mutex_unlock(&adev->smu.mutex); |
| 2520 | + |
| 2521 | + if (!ret) { |
| 2522 | + dev_dbg(adev->dev, "sienna_cichlid_i2c_write(), address = %x, bytes = %d , data: ", |
| 2523 | + (uint16_t)address, numbytes); |
| 2524 | + |
| 2525 | + print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, |
| 2526 | + 8, 1, data, numbytes, false); |
| 2527 | + /* |
| 2528 | + * According to EEPROM spec there is a MAX of 10 ms required for |
| 2529 | + * EEPROM to flush internal RX buffer after STOP was issued at the |
| 2530 | + * end of write transaction. During this time the EEPROM will not be |
| 2531 | + * responsive to any more commands - so wait a bit more. |
| 2532 | + */ |
| 2533 | + msleep(10); |
| 2534 | + |
| 2535 | + } else |
| 2536 | + dev_err(adev->dev, "sienna_cichlid_i2c_write- error occurred :%x", ret); |
| 2537 | + |
| 2538 | + return ret; |
| 2539 | +} |
| 2540 | + |
| 2541 | +static int sienna_cichlid_i2c_xfer(struct i2c_adapter *i2c_adap, |
| 2542 | + struct i2c_msg *msgs, int num) |
| 2543 | +{ |
| 2544 | + uint32_t i, j, ret, data_size, data_chunk_size, next_eeprom_addr = 0; |
| 2545 | + uint8_t *data_ptr, data_chunk[MAX_SW_I2C_COMMANDS] = { 0 }; |
| 2546 | + |
| 2547 | + for (i = 0; i < num; i++) { |
| 2548 | + /* |
| 2549 | + * SMU interface allows at most MAX_SW_I2C_COMMANDS bytes of data at |
| 2550 | + * once and hence the data needs to be spliced into chunks and sent each |
| 2551 | + * chunk separately |
| 2552 | + */ |
| 2553 | + data_size = msgs[i].len - 2; |
| 2554 | + data_chunk_size = MAX_SW_I2C_COMMANDS - 2; |
| 2555 | + next_eeprom_addr = (msgs[i].buf[0] << 8 & 0xff00) | (msgs[i].buf[1] & 0xff); |
| 2556 | + data_ptr = msgs[i].buf + 2; |
| 2557 | + |
| 2558 | + for (j = 0; j < data_size / data_chunk_size; j++) { |
| 2559 | + /* Insert the EEPROM dest addess, bits 0-15 */ |
| 2560 | + data_chunk[0] = ((next_eeprom_addr >> 8) & 0xff); |
| 2561 | + data_chunk[1] = (next_eeprom_addr & 0xff); |
| 2562 | + |
| 2563 | + if (msgs[i].flags & I2C_M_RD) { |
| 2564 | + ret = sienna_cichlid_i2c_read_data(i2c_adap, |
| 2565 | + (uint8_t)msgs[i].addr, |
| 2566 | + data_chunk, MAX_SW_I2C_COMMANDS); |
| 2567 | + |
| 2568 | + memcpy(data_ptr, data_chunk + 2, data_chunk_size); |
| 2569 | + } else { |
| 2570 | + |
| 2571 | + memcpy(data_chunk + 2, data_ptr, data_chunk_size); |
| 2572 | + |
| 2573 | + ret = sienna_cichlid_i2c_write_data(i2c_adap, |
| 2574 | + (uint8_t)msgs[i].addr, |
| 2575 | + data_chunk, MAX_SW_I2C_COMMANDS); |
| 2576 | + } |
| 2577 | + |
| 2578 | + if (ret) { |
| 2579 | + num = -EIO; |
| 2580 | + goto fail; |
| 2581 | + } |
| 2582 | + |
| 2583 | + next_eeprom_addr += data_chunk_size; |
| 2584 | + data_ptr += data_chunk_size; |
| 2585 | + } |
| 2586 | + |
| 2587 | + if (data_size % data_chunk_size) { |
| 2588 | + data_chunk[0] = ((next_eeprom_addr >> 8) & 0xff); |
| 2589 | + data_chunk[1] = (next_eeprom_addr & 0xff); |
| 2590 | + |
| 2591 | + if (msgs[i].flags & I2C_M_RD) { |
| 2592 | + ret = sienna_cichlid_i2c_read_data(i2c_adap, |
| 2593 | + (uint8_t)msgs[i].addr, |
| 2594 | + data_chunk, (data_size % data_chunk_size) + 2); |
| 2595 | + |
| 2596 | + memcpy(data_ptr, data_chunk + 2, data_size % data_chunk_size); |
| 2597 | + } else { |
| 2598 | + memcpy(data_chunk + 2, data_ptr, data_size % data_chunk_size); |
| 2599 | + |
| 2600 | + ret = sienna_cichlid_i2c_write_data(i2c_adap, |
| 2601 | + (uint8_t)msgs[i].addr, |
| 2602 | + data_chunk, (data_size % data_chunk_size) + 2); |
| 2603 | + } |
| 2604 | + |
| 2605 | + if (ret) { |
| 2606 | + num = -EIO; |
| 2607 | + goto fail; |
| 2608 | + } |
| 2609 | + } |
| 2610 | + } |
| 2611 | + |
| 2612 | +fail: |
| 2613 | + return num; |
| 2614 | +} |
| 2615 | + |
| 2616 | +static u32 sienna_cichlid_i2c_func(struct i2c_adapter *adap) |
| 2617 | +{ |
| 2618 | + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; |
| 2619 | +} |
| 2620 | + |
| 2621 | + |
| 2622 | +static const struct i2c_algorithm sienna_cichlid_i2c_algo = { |
| 2623 | + .master_xfer = sienna_cichlid_i2c_xfer, |
| 2624 | + .functionality = sienna_cichlid_i2c_func, |
| 2625 | +}; |
| 2626 | + |
| 2627 | +static bool sienna_cichlid_i2c_adapter_is_added(struct i2c_adapter *control) |
| 2628 | +{ |
| 2629 | + struct amdgpu_device *adev = to_amdgpu_device(control); |
| 2630 | + |
| 2631 | + return control->dev.parent == &adev->pdev->dev; |
| 2632 | +} |
| 2633 | + |
| 2634 | +static int sienna_cichlid_i2c_control_init(struct smu_context *smu, struct i2c_adapter *control) |
| 2635 | +{ |
| 2636 | + struct amdgpu_device *adev = to_amdgpu_device(control); |
| 2637 | + int res; |
| 2638 | + |
| 2639 | + /* smu_i2c_eeprom_init may be called twice in sriov */ |
| 2640 | + if (sienna_cichlid_i2c_adapter_is_added(control)) |
| 2641 | + return 0; |
| 2642 | + |
| 2643 | + control->owner = THIS_MODULE; |
| 2644 | + control->class = I2C_CLASS_SPD; |
| 2645 | + control->dev.parent = &adev->pdev->dev; |
| 2646 | + control->algo = &sienna_cichlid_i2c_algo; |
| 2647 | + snprintf(control->name, sizeof(control->name), "AMDGPU SMU"); |
| 2648 | + |
| 2649 | + res = i2c_add_adapter(control); |
| 2650 | + if (res) |
| 2651 | + DRM_ERROR("Failed to register hw i2c, err: %d\n", res); |
| 2652 | + |
| 2653 | + return res; |
| 2654 | +} |
| 2655 | + |
| 2656 | +static void sienna_cichlid_i2c_control_fini(struct smu_context *smu, struct i2c_adapter *control) |
| 2657 | +{ |
| 2658 | + if (!sienna_cichlid_i2c_adapter_is_added(control)) |
| 2659 | + return; |
| 2660 | + |
| 2661 | + i2c_del_adapter(control); |
| 2662 | +} |
| 2663 | + |
| 2664 | + |
2428 | 2665 | static const struct pptable_funcs sienna_cichlid_ppt_funcs = {
|
2429 | 2666 | .get_allowed_feature_mask = sienna_cichlid_get_allowed_feature_mask,
|
2430 | 2667 | .set_default_dpm_table = sienna_cichlid_set_default_dpm_table,
|
2431 | 2668 | .dpm_set_vcn_enable = sienna_cichlid_dpm_set_vcn_enable,
|
2432 | 2669 | .dpm_set_jpeg_enable = sienna_cichlid_dpm_set_jpeg_enable,
|
| 2670 | + .i2c_init = sienna_cichlid_i2c_control_init, |
| 2671 | + .i2c_fini = sienna_cichlid_i2c_control_fini, |
2433 | 2672 | .print_clk_levels = sienna_cichlid_print_clk_levels,
|
2434 | 2673 | .force_clk_levels = sienna_cichlid_force_clk_levels,
|
2435 | 2674 | .populate_umd_state_clk = sienna_cichlid_populate_umd_state_clk,
|
|
0 commit comments