drivers/devfreq/sun8i-a33-mbus.c

Source file repositories/reference/linux-study-clean/drivers/devfreq/sun8i-a33-mbus.c

File Facts

System
Linux kernel
Corpus path
drivers/devfreq/sun8i-a33-mbus.c
Extension
.c
Size
14453 bytes
Lines
490
Domain
Driver Families
Bucket
drivers/devfreq
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct sun8i_a33_mbus_variant {
	u32					min_dram_divider;
	u32					max_dram_divider;
	u32					odt_freq_mhz;
};

struct sun8i_a33_mbus {
	const struct sun8i_a33_mbus_variant	*variant;
	void __iomem				*reg_dram;
	void __iomem				*reg_mbus;
	struct clk				*clk_bus;
	struct clk				*clk_dram;
	struct clk				*clk_mbus;
	struct devfreq				*devfreq_dram;
	struct devfreq_simple_ondemand_data	gov_data;
	struct devfreq_dev_profile		profile;
	u32					data_width;
	u32					nominal_bw;
	u32					odtmap;
	u32					tREFI_ns;
	u32					tRFC_ns;
	unsigned long				freq_table[];
};

/*
 * 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.
 */
static int 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);
}

static void sun8i_a33_mbus_restart_pmu_counters(struct sun8i_a33_mbus *priv)
{
	u32 pmu_cfg = MBUS_PMU_CFG_PERIOD(pmu_period) | MBUS_PMU_CFG_UNIT_KB;

	/* All PMU counters are cleared on a disable->enable transition. */
	writel_relaxed(pmu_cfg,
		       priv->reg_mbus + MBUS_PMU_CFG);
	writel_relaxed(pmu_cfg | MBUS_PMU_CFG_ENABLE,
		       priv->reg_mbus + MBUS_PMU_CFG);

}

static void sun8i_a33_mbus_update_nominal_bw(struct sun8i_a33_mbus *priv,
					     u32 ddr_freq_mhz)
{
	/*
	 * Nominal bandwidth (KiB per PMU period):
	 *
	 *   DDR transfers   microseconds     KiB
	 *   ------------- * ------------ * --------
	 *    microsecond     PMU period    transfer
	 */
	priv->nominal_bw = ddr_freq_mhz * pmu_period * priv->data_width / 1024;
}

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 |

Annotation

Implementation Notes