/* * The unit for this value is (MBUS clock cycles / MBUS_TMR_PERIOD). When * MBUS_TMR_PERIOD is programmed to match the MBUS clock frequency in MHz, as * it is during DRAM init and during probe, the resulting unit is microseconds.
*/ staticint pmu_period = 50000;
module_param(pmu_period, int, 0644);
MODULE_PARM_DESC(pmu_period, "Bandwidth measurement period (microseconds)");
static u32 sun8i_a33_mbus_get_peak_bw(struct sun8i_a33_mbus *priv)
{ /* Returns the peak transfer (in KiB) during any single PMU period. */ return readl_relaxed(priv->reg_mbus + MBUS_TOTAL_BWCR);
}
opp = devfreq_recommended_opp(dev, freq, flags); if (IS_ERR(opp)) return PTR_ERR(opp);
dev_pm_opp_put(opp);
if (*freq == devfreq->previous_freq) return 0;
ret = sun8i_a33_mbus_set_dram_freq(priv, *freq); if (ret) {
dev_warn(dev, "failed to set DRAM frequency: %d\n", ret);
*freq = devfreq->previous_freq;
}
/* Choose tREFI and tRFC to match the configured DRAM type. */
mbus_cr = readl_relaxed(priv->reg_mbus + MBUS_CR); switch (MBUS_CR_GET_DRAM_TYPE(mbus_cr)) { case MBUS_CR_DRAM_TYPE_DDR2: case MBUS_CR_DRAM_TYPE_DDR3: case MBUS_CR_DRAM_TYPE_DDR4:
priv->tREFI_ns = 7800;
priv->tRFC_ns = 350; break; case MBUS_CR_DRAM_TYPE_LPDDR2: case MBUS_CR_DRAM_TYPE_LPDDR3:
priv->tREFI_ns = 3900;
priv->tRFC_ns = 210; break; default: return -EINVAL;
}
/* Save ODTMAP so it can be restored when raising the frequency. */
priv->odtmap = readl_relaxed(priv->reg_dram + DRAM_ODTMAP);
/* Compute the DRAM data bus width by counting enabled DATx8 blocks. */ for (i = 0; i < DRAM_DX_MAX; ++i) { void __iomem *reg = priv->reg_dram + DRAM_DXnGCR0(i);
if (!(readl_relaxed(reg) & DRAM_DXnGCR0_DXEN)) break;
}
priv->data_width = i;
/* Program MBUS_TMR such that the PMU period unit is microseconds. */
mbus_freq_mhz = clk_get_rate(priv->clk_mbus) / USEC_PER_SEC;
writel_relaxed(MBUS_TMR_PERIOD(mbus_freq_mhz),
priv->reg_mbus + MBUS_TMR);
/* "Master Ready Mask Register" bits must be set or MDFS will block. */
writel_relaxed(0xffffffff, priv->reg_mbus + MBUS_MDFSMRMR);
priv->reg_dram = devm_platform_ioremap_resource_byname(pdev, "dram"); if (IS_ERR(priv->reg_dram)) return PTR_ERR(priv->reg_dram);
priv->reg_mbus = devm_platform_ioremap_resource_byname(pdev, "mbus"); if (IS_ERR(priv->reg_mbus)) return PTR_ERR(priv->reg_mbus);
priv->clk_bus = devm_clk_get_enabled(dev, "bus"); if (IS_ERR(priv->clk_bus)) return dev_err_probe(dev, PTR_ERR(priv->clk_bus), "failed to get bus clock\n");
priv->clk_dram = devm_clk_get(dev, "dram"); if (IS_ERR(priv->clk_dram)) return dev_err_probe(dev, PTR_ERR(priv->clk_dram), "failed to get dram clock\n");
priv->clk_mbus = devm_clk_get(dev, "mbus"); if (IS_ERR(priv->clk_mbus)) return dev_err_probe(dev, PTR_ERR(priv->clk_mbus), "failed to get mbus clock\n");
/* Lock the DRAM clock rate to keep priv->nominal_bw in sync. */
ret = devm_clk_rate_exclusive_get(dev, priv->clk_dram); if (ret) return dev_err_probe(dev, ret, "failed to lock dram clock rate\n");
/* Lock the MBUS clock rate to keep MBUS_TMR_PERIOD in sync. */
ret = devm_clk_rate_exclusive_get(dev, priv->clk_mbus); if (ret) return dev_err_probe(dev, ret, "failed to lock mbus clock rate\n");
ret = devm_pm_opp_set_clkname(dev, "dram"); if (ret) return dev_err_probe(dev, ret, "failed to add OPP table\n");
base_freq = clk_get_rate(clk_get_parent(priv->clk_dram)); for (i = 0; i < max_state; ++i) { unsignedint div = variant->max_dram_divider - i;
priv->freq_table[i] = base_freq / div;
ret = dev_pm_opp_add(dev, priv->freq_table[i], 0); if (ret) {
err = "failed to add OPPs\n"; goto err_remove_opps;
}
}
ret = sun8i_a33_mbus_hw_init(dev, priv, priv->profile.initial_freq); if (ret) {
err = "failed to init hardware\n"; goto err_remove_opps;
}
priv->devfreq_dram = devfreq_add_device(dev, &priv->profile,
DEVFREQ_GOV_SIMPLE_ONDEMAND,
&priv->gov_data); if (IS_ERR(priv->devfreq_dram)) {
ret = PTR_ERR(priv->devfreq_dram);
err = "failed to add devfreq device\n"; goto err_remove_opps;
}
/* * This must be set manually after registering the devfreq device, * because there is no way to select a dynamic OPP as the suspend OPP.
*/
priv->devfreq_dram->suspend_freq = priv->freq_table[0];
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.