ASoC: Intel: haswell: Power transition refactor
Update D0 <-> D3 sequence to correctly transition hardware and DSP core from and to D3. On top of that, set SHIM registers to their recommended defaults during D0 and D3 proceduces as HW does not reset registers for us. Connected to: [alsa-devel][BUG] bdw-rt5650 DSP boot timeout https://mailman.alsa-project.org/pipermail/alsa-devel/2019-July/153098.html Github issue ticket reference: https://github.com/thesofproject/linux/pull/1842 Tested on: - BDW-Y RVP with rt286 - SAMUS with rt5677 Proposed solution (both in July 2019 and on github): 'Revert "ASoC: Intel: Work around to fix HW d3 potential crash issue"' is NAKed as it only covers the problem up and actually brings back the undefined behavior: some registers (e.g.: APLLSE) are describing LPT offsets rather than WPT ones. In consequence, during power-transitions driver issues incorrect writes and leaves the regs of interest alone. Existing patch - the non-revert - does not resolve the HW D3 issue at all as it ignores the recommended sequence and does not initialize hardware registers as expected. And thus, leaving things as are is also unacceptable. Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com> Tested-by: Ross Zwisler <zwisler@google.com> Link: https://lore.kernel.org/r/20200330194520.13253-1-cezary.rojewski@intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Родитель
b938b25f49
Коммит
8ec7d60432
|
@ -243,45 +243,92 @@ static irqreturn_t hsw_irq(int irq, void *context)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define CSR_DEFAULT_VALUE 0x8480040E
|
||||||
|
#define ISC_DEFAULT_VALUE 0x0
|
||||||
|
#define ISD_DEFAULT_VALUE 0x0
|
||||||
|
#define IMC_DEFAULT_VALUE 0x7FFF0003
|
||||||
|
#define IMD_DEFAULT_VALUE 0x7FFF0003
|
||||||
|
#define IPCC_DEFAULT_VALUE 0x0
|
||||||
|
#define IPCD_DEFAULT_VALUE 0x0
|
||||||
|
#define CLKCTL_DEFAULT_VALUE 0x7FF
|
||||||
|
#define CSR2_DEFAULT_VALUE 0x0
|
||||||
|
#define LTR_CTRL_DEFAULT_VALUE 0x0
|
||||||
|
#define HMD_CTRL_DEFAULT_VALUE 0x0
|
||||||
|
|
||||||
|
static void hsw_set_shim_defaults(struct sst_dsp *sst)
|
||||||
|
{
|
||||||
|
sst_dsp_shim_write_unlocked(sst, SST_CSR, CSR_DEFAULT_VALUE);
|
||||||
|
sst_dsp_shim_write_unlocked(sst, SST_ISRX, ISC_DEFAULT_VALUE);
|
||||||
|
sst_dsp_shim_write_unlocked(sst, SST_ISRD, ISD_DEFAULT_VALUE);
|
||||||
|
sst_dsp_shim_write_unlocked(sst, SST_IMRX, IMC_DEFAULT_VALUE);
|
||||||
|
sst_dsp_shim_write_unlocked(sst, SST_IMRD, IMD_DEFAULT_VALUE);
|
||||||
|
sst_dsp_shim_write_unlocked(sst, SST_IPCX, IPCC_DEFAULT_VALUE);
|
||||||
|
sst_dsp_shim_write_unlocked(sst, SST_IPCD, IPCD_DEFAULT_VALUE);
|
||||||
|
sst_dsp_shim_write_unlocked(sst, SST_CLKCTL, CLKCTL_DEFAULT_VALUE);
|
||||||
|
sst_dsp_shim_write_unlocked(sst, SST_CSR2, CSR2_DEFAULT_VALUE);
|
||||||
|
sst_dsp_shim_write_unlocked(sst, SST_LTRC, LTR_CTRL_DEFAULT_VALUE);
|
||||||
|
sst_dsp_shim_write_unlocked(sst, SST_HMDC, HMD_CTRL_DEFAULT_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* all clock-gating minus DCLCGE and DTCGE */
|
||||||
|
#define SST_VDRTCL2_CG_OTHER 0xB7D
|
||||||
|
|
||||||
static void hsw_set_dsp_D3(struct sst_dsp *sst)
|
static void hsw_set_dsp_D3(struct sst_dsp *sst)
|
||||||
{
|
{
|
||||||
u32 val;
|
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
/* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
|
/* disable clock core gating */
|
||||||
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
|
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||||
reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
|
reg &= ~(SST_VDRTCL2_DCLCGE);
|
||||||
writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
|
writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||||
|
|
||||||
/* enable power gating and switch off DRAM & IRAM blocks */
|
/* stall, reset and set 24MHz XOSC */
|
||||||
val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
|
sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
|
||||||
val |= SST_VDRTCL0_DSRAMPGE_MASK |
|
SST_CSR_24MHZ_LPCS | SST_CSR_STALL | SST_CSR_RST,
|
||||||
SST_VDRTCL0_ISRAMPGE_MASK;
|
SST_CSR_24MHZ_LPCS | SST_CSR_STALL | SST_CSR_RST);
|
||||||
val &= ~(SST_VDRTCL0_D3PGD | SST_VDRTCL0_D3SRAMPGD);
|
|
||||||
writel(val, sst->addr.pci_cfg + SST_VDRTCTL0);
|
|
||||||
|
|
||||||
/* switch off audio PLL */
|
/* DRAM power gating all */
|
||||||
val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
|
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
|
||||||
val |= SST_VDRTCL2_APLLSE_MASK;
|
reg |= SST_VDRTCL0_ISRAMPGE_MASK |
|
||||||
writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
|
SST_VDRTCL0_DSRAMPGE_MASK;
|
||||||
|
reg &= ~(SST_VDRTCL0_D3SRAMPGD);
|
||||||
|
reg |= SST_VDRTCL0_D3PGD;
|
||||||
|
writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
|
||||||
|
udelay(50);
|
||||||
|
|
||||||
/* disable MCLK(clkctl.smos = 0) */
|
/* PLL shutdown enable */
|
||||||
|
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||||
|
reg |= SST_VDRTCL2_APLLSE_MASK;
|
||||||
|
writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||||
|
|
||||||
|
/* disable MCLK */
|
||||||
sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
|
sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
|
||||||
SST_CLKCTL_MASK, 0);
|
SST_CLKCTL_MASK, 0);
|
||||||
|
|
||||||
/* Set D3 state, delay 50 us */
|
/* switch clock gating */
|
||||||
val = readl(sst->addr.pci_cfg + SST_PMCS);
|
|
||||||
val |= SST_PMCS_PS_MASK;
|
|
||||||
writel(val, sst->addr.pci_cfg + SST_PMCS);
|
|
||||||
udelay(50);
|
|
||||||
|
|
||||||
/* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
|
|
||||||
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
|
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||||
reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
|
reg |= SST_VDRTCL2_CG_OTHER;
|
||||||
|
reg &= ~(SST_VDRTCL2_DTCGE);
|
||||||
|
writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||||
|
/* enable DTCGE separatelly */
|
||||||
|
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||||
|
reg |= SST_VDRTCL2_DTCGE;
|
||||||
writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
|
writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||||
|
|
||||||
|
/* set shim defaults */
|
||||||
|
hsw_set_shim_defaults(sst);
|
||||||
|
|
||||||
|
/* set D3 */
|
||||||
|
reg = readl(sst->addr.pci_cfg + SST_PMCS);
|
||||||
|
reg |= SST_PMCS_PS_MASK;
|
||||||
|
writel(reg, sst->addr.pci_cfg + SST_PMCS);
|
||||||
udelay(50);
|
udelay(50);
|
||||||
|
|
||||||
|
/* enable clock core gating */
|
||||||
|
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||||
|
reg |= SST_VDRTCL2_DCLCGE;
|
||||||
|
writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||||
|
udelay(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hsw_reset(struct sst_dsp *sst)
|
static void hsw_reset(struct sst_dsp *sst)
|
||||||
|
@ -299,75 +346,62 @@ static void hsw_reset(struct sst_dsp *sst)
|
||||||
SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
|
SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* recommended CSR state for power-up */
|
||||||
|
#define SST_CSR_D0_MASK (0x18A09C0C | SST_CSR_DCS_MASK)
|
||||||
|
|
||||||
static int hsw_set_dsp_D0(struct sst_dsp *sst)
|
static int hsw_set_dsp_D0(struct sst_dsp *sst)
|
||||||
{
|
{
|
||||||
int tries = 10;
|
u32 reg;
|
||||||
u32 reg, fw_dump_bit;
|
|
||||||
|
|
||||||
/* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
|
/* disable clock core gating */
|
||||||
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
|
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||||
reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
|
reg &= ~(SST_VDRTCL2_DCLCGE);
|
||||||
writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
|
writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||||
|
|
||||||
/* Disable D3PG (VDRTCTL0.D3PGD = 1) */
|
/* switch clock gating */
|
||||||
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
|
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||||
reg |= SST_VDRTCL0_D3PGD;
|
reg |= SST_VDRTCL2_CG_OTHER;
|
||||||
writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
|
reg &= ~(SST_VDRTCL2_DTCGE);
|
||||||
|
writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||||
|
|
||||||
/* Set D0 state */
|
/* set D0 */
|
||||||
reg = readl(sst->addr.pci_cfg + SST_PMCS);
|
reg = readl(sst->addr.pci_cfg + SST_PMCS);
|
||||||
reg &= ~SST_PMCS_PS_MASK;
|
reg &= ~(SST_PMCS_PS_MASK);
|
||||||
writel(reg, sst->addr.pci_cfg + SST_PMCS);
|
writel(reg, sst->addr.pci_cfg + SST_PMCS);
|
||||||
|
|
||||||
/* check that ADSP shim is enabled */
|
/* DRAM power gating none */
|
||||||
while (tries--) {
|
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
|
||||||
reg = readl(sst->addr.pci_cfg + SST_PMCS) & SST_PMCS_PS_MASK;
|
reg &= ~(SST_VDRTCL0_ISRAMPGE_MASK |
|
||||||
if (reg == 0)
|
SST_VDRTCL0_DSRAMPGE_MASK);
|
||||||
goto finish;
|
reg |= SST_VDRTCL0_D3SRAMPGD;
|
||||||
|
reg |= SST_VDRTCL0_D3PGD;
|
||||||
|
writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
|
||||||
|
mdelay(10);
|
||||||
|
|
||||||
msleep(1);
|
/* set shim defaults */
|
||||||
}
|
hsw_set_shim_defaults(sst);
|
||||||
|
|
||||||
return -ENODEV;
|
/* restore MCLK */
|
||||||
|
|
||||||
finish:
|
|
||||||
/* select SSP1 19.2MHz base clock, SSP clock 0, turn off Low Power Clock */
|
|
||||||
sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
|
|
||||||
SST_CSR_S1IOCS | SST_CSR_SBCS1 | SST_CSR_LPCS, 0x0);
|
|
||||||
|
|
||||||
/* stall DSP core, set clk to 192/96Mhz */
|
|
||||||
sst_dsp_shim_update_bits_unlocked(sst,
|
|
||||||
SST_CSR, SST_CSR_STALL | SST_CSR_DCS_MASK,
|
|
||||||
SST_CSR_STALL | SST_CSR_DCS(4));
|
|
||||||
|
|
||||||
/* Set 24MHz MCLK, prevent local clock gating, enable SSP0 clock */
|
|
||||||
sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
|
sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
|
||||||
SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0,
|
SST_CLKCTL_MASK, SST_CLKCTL_MASK);
|
||||||
SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0);
|
|
||||||
|
|
||||||
/* Stall and reset core, set CSR */
|
/* PLL shutdown disable */
|
||||||
hsw_reset(sst);
|
|
||||||
|
|
||||||
/* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
|
|
||||||
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
|
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||||
reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
|
reg &= ~(SST_VDRTCL2_APLLSE_MASK);
|
||||||
writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
|
writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||||
|
|
||||||
|
sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
|
||||||
|
SST_CSR_D0_MASK, SST_CSR_SBCS0 | SST_CSR_SBCS1 |
|
||||||
|
SST_CSR_STALL | SST_CSR_DCS(4));
|
||||||
udelay(50);
|
udelay(50);
|
||||||
|
|
||||||
/* switch on audio PLL */
|
/* enable clock core gating */
|
||||||
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
|
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||||
reg &= ~SST_VDRTCL2_APLLSE_MASK;
|
reg |= SST_VDRTCL2_DCLCGE;
|
||||||
writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
|
writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||||
|
|
||||||
/* set default power gating control, enable power gating control for all blocks. that is,
|
/* clear reset */
|
||||||
can't be accessed, please enable each block before accessing. */
|
sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, SST_CSR_RST, 0);
|
||||||
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
|
|
||||||
reg |= SST_VDRTCL0_DSRAMPGE_MASK | SST_VDRTCL0_ISRAMPGE_MASK;
|
|
||||||
/* for D0, always enable the block(DSRAM[0]) used for FW dump */
|
|
||||||
fw_dump_bit = 1 << SST_VDRTCL0_DSRAMPGE_SHIFT;
|
|
||||||
writel(reg & ~fw_dump_bit, sst->addr.pci_cfg + SST_VDRTCTL0);
|
|
||||||
|
|
||||||
|
|
||||||
/* disable DMA finish function for SSP0 & SSP1 */
|
/* disable DMA finish function for SSP0 & SSP1 */
|
||||||
sst_dsp_shim_update_bits_unlocked(sst, SST_CSR2, SST_CSR2_SDFD_SSP1,
|
sst_dsp_shim_update_bits_unlocked(sst, SST_CSR2, SST_CSR2_SDFD_SSP1,
|
||||||
|
@ -384,12 +418,6 @@ finish:
|
||||||
sst_dsp_shim_update_bits(sst, SST_IMRD, (SST_IMRD_DONE | SST_IMRD_BUSY |
|
sst_dsp_shim_update_bits(sst, SST_IMRD, (SST_IMRD_DONE | SST_IMRD_BUSY |
|
||||||
SST_IMRD_SSP0 | SST_IMRD_DMAC), 0x0);
|
SST_IMRD_SSP0 | SST_IMRD_DMAC), 0x0);
|
||||||
|
|
||||||
/* clear IPC registers */
|
|
||||||
sst_dsp_shim_write(sst, SST_IPCX, 0x0);
|
|
||||||
sst_dsp_shim_write(sst, SST_IPCD, 0x0);
|
|
||||||
sst_dsp_shim_write(sst, 0x80, 0x6);
|
|
||||||
sst_dsp_shim_write(sst, 0xe0, 0x300a);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,11 +443,6 @@ static void hsw_sleep(struct sst_dsp *sst)
|
||||||
{
|
{
|
||||||
dev_dbg(sst->dev, "HSW_PM dsp runtime suspend\n");
|
dev_dbg(sst->dev, "HSW_PM dsp runtime suspend\n");
|
||||||
|
|
||||||
/* put DSP into reset and stall */
|
|
||||||
sst_dsp_shim_update_bits(sst, SST_CSR,
|
|
||||||
SST_CSR_24MHZ_LPCS | SST_CSR_RST | SST_CSR_STALL,
|
|
||||||
SST_CSR_RST | SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
|
|
||||||
|
|
||||||
hsw_set_dsp_D3(sst);
|
hsw_set_dsp_D3(sst);
|
||||||
dev_dbg(sst->dev, "HSW_PM dsp runtime suspend exit\n");
|
dev_dbg(sst->dev, "HSW_PM dsp runtime suspend exit\n");
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче