blob: bba5e18f9496f3ca315fd90d68408497d7b36d5b [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Rio EdgeTPU power management support
*
* Copyright (C) 2021 Google, Inc.
*/
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <soc/google/bcl.h>
#include "edgetpu-config.h"
#include "edgetpu-internal.h"
#include "edgetpu-mobile-platform.h"
#include "edgetpu-pm.h"
#include "edgetpu-soc.h"
#include "mobile-soc-gsx01.h"
#include "mobile-pm.h"
#include "mobile-pm.c"
#define SHUTDOWN_DELAY_US_MIN 200
#define SHUTDOWN_DELAY_US_MAX 200
#define BOOTUP_DELAY_US_MIN 100
#define BOOTUP_DELAY_US_MAX 150
#define SHUTDOWN_MAX_DELAY_COUNT 20
#define EDGETPU_PSM0_CFG 0x1c1700
#define EDGETPU_PSM0_START 0x1c1704
#define EDGETPU_PSM0_STATUS 0x1c1708
#define EDGETPU_LPM_CONTROL_CSR 0x1d0020
#define EDGETPU_LPM_CORE_CSR 0x1d0028
#define EDGETPU_LPM_CLUSTER_CSR0 0x1d0030
#define EDGETPU_LPM_CLUSTER_CSR1 0x1d0038
#define EDGETPU_TOP_CLOCK_GATE_CONTROL_CSR 0x1d0068
#define EDGETPU_LPM_CHANGE_TIMEOUT 30000
#define EDGETPU_LPM_IMEM_OPS_SIZE 0x4
#define EDGETPU_LPM_IMEM_OPS(n) (0x1c0800 + (EDGETPU_LPM_IMEM_OPS_SIZE * (n)))
#define EDGETPU_LPM_IMEM_OPS_SET(etdev, n, value) \
edgetpu_dev_write_32_sync(etdev, EDGETPU_LPM_IMEM_OPS(n), value)
static void rio_lpm_down(struct edgetpu_dev *etdev)
{
int timeout_cnt = 0;
u32 val;
do {
/* Manually delay 200us per retry till LPM shutdown finished */
usleep_range(SHUTDOWN_DELAY_US_MIN, SHUTDOWN_DELAY_US_MAX);
val = edgetpu_dev_read_32_sync(etdev, EDGETPU_REG_LPM_CONTROL);
if ((val & 0x100) || (val == 0))
break;
timeout_cnt++;
} while (timeout_cnt < SHUTDOWN_MAX_DELAY_COUNT);
if (timeout_cnt == SHUTDOWN_MAX_DELAY_COUNT)
// Log the issue then continue to perform the shutdown forcefully.
etdev_warn(etdev, "LPM shutdown failure, continuing BLK shutdown\n");
}
static void rio_patch_lpm(struct edgetpu_dev *etdev)
{
/* Pchannel handshake fix for b/210907864. */
EDGETPU_LPM_IMEM_OPS_SET(etdev, 190, 0x11061104);
/* FRC retention fix for cl/412125437. */
EDGETPU_LPM_IMEM_OPS_SET(etdev, 3, 0x10051101);
EDGETPU_LPM_IMEM_OPS_SET(etdev, 13, 0x11051001);
EDGETPU_LPM_IMEM_OPS_SET(etdev, 22, 0x22001005);
EDGETPU_LPM_IMEM_OPS_SET(etdev, 28, 0x21261004);
EDGETPU_LPM_IMEM_OPS_SET(etdev, 32, 0x100b1104);
EDGETPU_LPM_IMEM_OPS_SET(etdev, 33, 0x100f2126);
EDGETPU_LPM_IMEM_OPS_SET(etdev, 41, 0x10090011);
EDGETPU_LPM_IMEM_OPS_SET(etdev, 135, 0x110e100b);
EDGETPU_LPM_IMEM_OPS_SET(etdev, 138, 0x21251110);
EDGETPU_LPM_IMEM_OPS_SET(etdev, 164, 0x100b1109);
EDGETPU_LPM_IMEM_OPS_SET(etdev, 174, 0x22b32125);
/* DVFS fix for b/211411986. */
EDGETPU_LPM_IMEM_OPS_SET(etdev, 197, 0x00241002);
EDGETPU_LPM_IMEM_OPS_SET(etdev, 198, 0x0bc75301);
EDGETPU_LPM_IMEM_OPS_SET(etdev, 199, 0x00241102);
EDGETPU_LPM_IMEM_OPS_SET(etdev, 200, 0x0bc95001);
EDGETPU_LPM_IMEM_OPS_SET(etdev, 201, 0x14171018);
EDGETPU_LPM_IMEM_OPS_SET(etdev, 202, 0x02001118);
/* psm_1_state_table_0_trans_1_next_state */
edgetpu_dev_write_32_sync(etdev, 0x1c2020, 0x00000000);
/* psm_1_state_table_0_trans_1_seq_addr */
edgetpu_dev_write_32_sync(etdev, 0x1c2024, 0x000000c5);
/* psm_1_state_table_0_trans_1_trigger_num */
edgetpu_dev_write_32_sync(etdev, 0x1c2030, 0x00000005);
/* psm_1_state_table_0_trans_1_trigger_en */
edgetpu_dev_write_32_sync(etdev, 0x1c2034, 0x00000001);
/* trigger_csr_events_en_5_hi */
edgetpu_dev_write_32_sync(etdev, 0x1c012c, 0x00000003);
/*
* FRC clocking fix for b/287661979.
*
* Increases the delay between cluster clock enablement and logic
* retention/restore activation.
*/
EDGETPU_LPM_IMEM_OPS_SET(etdev, 3, 0x21261101);
EDGETPU_LPM_IMEM_OPS_SET(etdev, 4, 0x11111005);
}
static int rio_lpm_up(struct edgetpu_dev *etdev)
{
int ret, i;
u32 val;
rio_patch_lpm(etdev);
/* TODO(b/225804117) : Move core and cluster PSM setup to fw. */
/* set coreNewPwrState = 0x4, coreNewPowerStateReq = 0x1 */
val = edgetpu_dev_read_32_sync(etdev, EDGETPU_LPM_CORE_CSR);
val = val | (4 << 3);
val = val | (1 << 6);
edgetpu_dev_write_32_sync(etdev, EDGETPU_LPM_CORE_CSR, val);
/* set clusterNewPwrState = 0x1 */
edgetpu_dev_write_32_sync(etdev, EDGETPU_LPM_CLUSTER_CSR0, 1 << 1);
edgetpu_dev_write_32_sync(etdev, EDGETPU_LPM_CLUSTER_CSR1, 1 << 1);
/* If lpmCtlOffState is set, clear tpuPowerOff and leave */
val = edgetpu_dev_read_32_sync(etdev, EDGETPU_LPM_CONTROL_CSR);
if (val & (1 << 8)) {
edgetpu_dev_write_32_sync(etdev, EDGETPU_LPM_CONTROL_CSR, val & ~(1 << 5));
return 0;
}
for (i = 0; i < 4; i++) {
val = edgetpu_dev_read_32_sync(etdev, EDGETPU_PSM0_STATUS + i * 0x1000);
/* only bring up LPM when it's in an invalid state */
if ((val & 0x10) == 0)
edgetpu_dev_write_32_sync(etdev, EDGETPU_PSM0_START + i * 0x1000, 1);
}
for (i = 0; i < 4; i++) {
ret = readl_poll_timeout(etdev->regs.mem + EDGETPU_PSM0_STATUS + i * 0x1000, val,
val & 0x80, 5, EDGETPU_LPM_CHANGE_TIMEOUT);
if (ret) {
etdev_err(etdev, "Set PSM%d failed: %d\n", i, ret);
return ret;
}
}
for (i = 0; i < 4; i++)
edgetpu_dev_write_32_sync(etdev, EDGETPU_PSM0_CFG + i * 0x1000, 0);
/* set clockGateAllowed = 0x1 */
edgetpu_dev_write_32_sync(etdev, EDGETPU_LPM_CONTROL_CSR, 1 << 3);
/* set axiReorderClockGateEn = 0x1, tpuTopClockGateEn = 0x1 */
edgetpu_dev_write_32_sync(etdev, EDGETPU_TOP_CLOCK_GATE_CONTROL_CSR, 3);
return 0;
}
static bool rio_is_block_down(struct edgetpu_dev *etdev)
{
int timeout_cnt = 0;
int curr_state;
do {
/* Delay 20us per retry till blk shutdown finished */
usleep_range(SHUTDOWN_DELAY_US_MIN, SHUTDOWN_DELAY_US_MAX);
curr_state = readl(etdev->pmu_status);
if (!curr_state)
return true;
timeout_cnt++;
} while (timeout_cnt < SHUTDOWN_MAX_DELAY_COUNT);
return false;
}
static void rio_post_fw_start(struct edgetpu_dev *etdev)
{
if (etdev->soc_data->bcl_dev)
google_init_tpu_ratio(etdev->soc_data->bcl_dev);
}
int edgetpu_chip_pm_create(struct edgetpu_dev *etdev)
{
struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
struct edgetpu_mobile_platform_pwr *platform_pwr = &etmdev->platform_pwr;
platform_pwr->lpm_up = rio_lpm_up;
platform_pwr->lpm_down = rio_lpm_down;
if (etdev->pmu_status)
platform_pwr->is_block_down = rio_is_block_down;
platform_pwr->post_fw_start = rio_post_fw_start;
etdev->soc_data->bcl_dev = google_retrieve_bcl_handle();
return edgetpu_mobile_pm_create(etdev);
}