static int sun8i_a33_mbus_set_dram_freq()

in sun8i-a33-mbus.c [136:213]


static int sun8i_a33_mbus_set_dram_freq(struct sun8i_a33_mbus *priv,
					unsigned long freq)
{
	u32  ddr_freq_mhz = freq / USEC_PER_SEC; /* DDR */
	u32 dram_freq_mhz =    ddr_freq_mhz / 2; /* SDR */
	u32 mctl_freq_mhz =   dram_freq_mhz / 2; /* HDR */
	u32 dxodt, mdfscr, pwrctl, vtfcr;
	u32 i, tREFI_32ck, tRFC_ck;
	int ret;

	/* The rate change is not effective until the MDFS process runs. */
	ret = clk_set_rate(priv->clk_dram, freq);
	if (ret)
		return ret;

	/* Disable automatic self-refesh and VTF before starting MDFS. */
	pwrctl = readl_relaxed(priv->reg_dram + DRAM_PWRCTL) &
		 ~DRAM_PWRCTL_SELFREF_EN;
	writel_relaxed(pwrctl, priv->reg_dram + DRAM_PWRCTL);
	vtfcr = readl_relaxed(priv->reg_dram + DRAM_VTFCR);
	writel_relaxed(vtfcr & ~DRAM_VTFCR_VTF_ENABLE,
		       priv->reg_dram + DRAM_VTFCR);

	/* Set up MDFS and enable double buffering for timing registers. */
	mdfscr = MBUS_MDFSCR_MODE_DFS |
		 MBUS_MDFSCR_BYPASS |
		 MBUS_MDFSCR_PAD_HOLD |
		 MBUS_MDFSCR_BUFFER_TIMING;
	writel(mdfscr, priv->reg_mbus + MBUS_MDFSCR);

	/* Update the buffered copy of RFSHTMG. */
	tREFI_32ck = priv->tREFI_ns * mctl_freq_mhz / 1000 / 32;
	tRFC_ck = DIV_ROUND_UP(priv->tRFC_ns * mctl_freq_mhz, 1000);
	writel(DRAM_RFSHTMG_TREFI(tREFI_32ck) | DRAM_RFSHTMG_TRFC(tRFC_ck),
	       priv->reg_dram + DRAM_RFSHTMG);

	/* Enable ODT if needed, or disable it to save power. */
	if (priv->odtmap && dram_freq_mhz > priv->variant->odt_freq_mhz) {
		dxodt = DRAM_DXnGCR0_DXODT_DYNAMIC;
		writel(priv->odtmap, priv->reg_dram + DRAM_ODTMAP);
	} else {
		dxodt = DRAM_DXnGCR0_DXODT_DISABLED;
		writel(0, priv->reg_dram + DRAM_ODTMAP);
	}
	for (i = 0; i < DRAM_DX_MAX; ++i) {
		void __iomem *reg = priv->reg_dram + DRAM_DXnGCR0(i);

		writel((readl(reg) & ~DRAM_DXnGCR0_DXODT) | dxodt, reg);
	}

	dev_dbg(priv->devfreq_dram->dev.parent,
		"Setting DRAM to %u MHz, tREFI=%u, tRFC=%u, ODT=%s\n",
		dram_freq_mhz, tREFI_32ck, tRFC_ck,
		dxodt == DRAM_DXnGCR0_DXODT_DYNAMIC ? "dynamic" : "disabled");

	/* Trigger hardware MDFS. */
	writel(mdfscr | MBUS_MDFSCR_START, priv->reg_mbus + MBUS_MDFSCR);
	ret = readl_poll_timeout_atomic(priv->reg_mbus + MBUS_MDFSCR, mdfscr,
					!(mdfscr & MBUS_MDFSCR_START), 10, 1000);
	if (ret)
		return ret;

	/* Disable double buffering. */
	writel(0, priv->reg_mbus + MBUS_MDFSCR);

	/* Restore VTF configuration. */
	writel_relaxed(vtfcr, priv->reg_dram + DRAM_VTFCR);

	/* Enable automatic self-refresh at the lowest frequency only. */
	if (freq == priv->freq_table[0])
		pwrctl |= DRAM_PWRCTL_SELFREF_EN;
	writel_relaxed(pwrctl, priv->reg_dram + DRAM_PWRCTL);

	sun8i_a33_mbus_restart_pmu_counters(priv);
	sun8i_a33_mbus_update_nominal_bw(priv, ddr_freq_mhz);

	return 0;
}