blob: 4459595252813d5e4a5d4ef87ed892b5d2272142 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Callisto chip specific GXP MicroController Unit management.
*
* Copyright (C) 2022 Google LLC
*/
#include <linux/delay.h>
#include "gxp-internal.h"
#include "gxp-lpm.h"
#include "gxp-mcu.h"
#include "gxp-mcu-platform.h"
/* Setting bit 15 and 16 of GPOUT_LO_WRT register to 0 will hold MCU reset. */
#define GPOUT_LO_MCU_RESET (3u << 15)
#define GPOUT_LO_MCU_PSTATE (1u << 2)
#define GPOUT_LO_MCU_PREG (1u << 3)
#define GPIN_LO_MCU_PACCEPT (1u << 2)
#define GPIN_LO_MCU_PDENY (1u << 3)
int gxp_mcu_reset(struct gxp_dev *gxp, bool release_reset)
{
struct gxp_mcu *mcu = &to_mcu_dev(gxp)->mcu;
u32 gpout_lo_rd, gpin_lo_rd, orig;
int i, ret = 0;
/* 1. Read gpout_lo_rd register. */
orig = gpout_lo_rd =
lpm_read_32_psm(gxp, CORE_TO_PSM(GXP_MCU_CORE_ID), PSM_REG_GPOUT_LO_RD_OFFSET);
/* 2. Toggle bit 15 and 16 of this register to '0'. */
gpout_lo_rd &= ~GPOUT_LO_MCU_RESET;
/* 3. Set psm in debug mode with debug_cfg.en=1 and debug_cfg.gpout_override=1. */
lpm_write_32_psm(gxp, CORE_TO_PSM(GXP_MCU_CORE_ID), PSM_REG_DEBUG_CFG_OFFSET, 0b11);
/* 4. Write the modified value from step2 to gpout_lo_wrt register. */
lpm_write_32_psm(gxp, CORE_TO_PSM(GXP_MCU_CORE_ID), PSM_REG_GPOUT_LO_WRT_OFFSET,
gpout_lo_rd);
/*
* 5. Wait for MCU being reset.
*
* Basically, to verify the MCU reset, we should poll bit 0 of MCU_RESET_STATUS register
* (CORERESET_N) to become 0.
*
* However, as we cannot access the register for the security reason, there is no way to
* poll it. Based on the experiment, resetting MCU was already done when the step 4 above
* is finished which took under 5 us. Therefore, waiting 1~2 ms as a margin should be
* enough.
*/
usleep_range(1000, 2000);
gxp_mcu_reset_mailbox(mcu);
if (!release_reset)
return 0;
/*
* 6. Modify gpout_lo_wrt register locally to set bit [3:2]={1,0} to let MCU transit to
* RUN state.
*/
gpout_lo_rd = (gpout_lo_rd | GPOUT_LO_MCU_PREG) & ~GPOUT_LO_MCU_PSTATE;
lpm_write_32_psm(gxp, CORE_TO_PSM(GXP_MCU_CORE_ID), PSM_REG_GPOUT_LO_WRT_OFFSET,
gpout_lo_rd);
/* 7. Toggle bit 15 and 16 of gpout_lo_wrt register to '1' to release reset. */
gpout_lo_rd |= GPOUT_LO_MCU_RESET;
lpm_write_32_psm(gxp, CORE_TO_PSM(GXP_MCU_CORE_ID), PSM_REG_GPOUT_LO_WRT_OFFSET,
gpout_lo_rd);
/* 8. Poll gpin_lo_rd for one of bit 2 (paccept) and 3 (pdeny) becoming non-zero. */
for (i = 10000; i > 0; i--) {
gpin_lo_rd = lpm_read_32_psm(gxp, CORE_TO_PSM(GXP_MCU_CORE_ID),
PSM_REG_GPIN_LO_RD_OFFSET);
if (gpin_lo_rd & (GPIN_LO_MCU_PACCEPT | GPIN_LO_MCU_PDENY))
break;
udelay(GXP_TIME_DELAY_FACTOR);
}
if (!i) {
dev_warn(gxp->dev, "MCU is not responding to the power control");
ret = -ETIMEDOUT;
} else if (gpin_lo_rd & GPIN_LO_MCU_PDENY) {
dev_warn(gxp->dev, "MCU denied the power control for reset");
ret = -EAGAIN;
}
/* 9. Write gpout_lo_wrt the same as gpout_lo_rd of step 1. */
lpm_write_32_psm(gxp, CORE_TO_PSM(GXP_MCU_CORE_ID), PSM_REG_GPOUT_LO_WRT_OFFSET, orig);
/*
* 10. Move PSM back to func mode with gpout override disabled debug_cfg.en=0 and
* debug_cfg.gpout=0.
*/
lpm_write_32_psm(gxp, CORE_TO_PSM(GXP_MCU_CORE_ID), PSM_REG_DEBUG_CFG_OFFSET, 0);
return ret;
}