drivers/devfreq/imx8m-ddrc.c

Source file repositories/reference/linux-study-clean/drivers/devfreq/imx8m-ddrc.c

File Facts

System
Linux kernel
Corpus path
drivers/devfreq/imx8m-ddrc.c
Extension
.c
Size
12219 bytes
Lines
458
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 imx8m_ddrc_freq {
	unsigned long rate;
	unsigned long smcarg;
	int dram_core_parent_index;
	int dram_alt_parent_index;
	int dram_apb_parent_index;
};

/* Hardware limitation */
#define IMX8M_DDRC_MAX_FREQ_COUNT 4

/*
 * i.MX8M DRAM Controller clocks have the following structure (abridged):
 *
 * +----------+       |\            +------+
 * | dram_pll |-------|M| dram_core |      |
 * +----------+       |U|---------->| D    |
 *                 /--|X|           |  D   |
 *   dram_alt_root |  |/            |   R  |
 *                 |                |    C |
 *            +---------+           |      |
 *            |FIX DIV/4|           |      |
 *            +---------+           |      |
 *  composite:     |                |      |
 * +----------+    |                |      |
 * | dram_alt |----/                |      |
 * +----------+                     |      |
 * | dram_apb |-------------------->|      |
 * +----------+                     +------+
 *
 * The dram_pll is used for higher rates and dram_alt is used for lower rates.
 *
 * Frequency switching is implemented in TF-A (via SMC call) and can change the
 * configuration of the clocks, including mux parents. The dram_alt and
 * dram_apb clocks are "imx composite" and their parent can change too.
 *
 * We need to prepare/enable the new mux parents head of switching and update
 * their information afterwards.
 */
struct imx8m_ddrc {
	struct devfreq_dev_profile profile;
	struct devfreq *devfreq;

	/* For frequency switching: */
	struct clk *dram_core;
	struct clk *dram_pll;
	struct clk *dram_alt;
	struct clk *dram_apb;

	int freq_count;
	struct imx8m_ddrc_freq freq_table[IMX8M_DDRC_MAX_FREQ_COUNT];
};

static struct imx8m_ddrc_freq *imx8m_ddrc_find_freq(struct imx8m_ddrc *priv,
						    unsigned long rate)
{
	struct imx8m_ddrc_freq *freq;
	int i;

	/*
	 * Firmware reports values in MT/s, so we round-down from Hz
	 * Rounding is extra generous to ensure a match.
	 */
	rate = DIV_ROUND_CLOSEST(rate, 250000);
	for (i = 0; i < priv->freq_count; ++i) {
		freq = &priv->freq_table[i];
		if (freq->rate == rate ||
				freq->rate + 1 == rate ||
				freq->rate - 1 == rate)
			return freq;
	}

	return NULL;
}

static void imx8m_ddrc_smc_set_freq(int target_freq)
{
	struct arm_smccc_res res;
	u32 online_cpus = 0;
	int cpu;

	local_irq_disable();

	for_each_online_cpu(cpu)
		online_cpus |= (1 << (cpu * 8));

	/* change the ddr freqency */
	arm_smccc_smc(IMX_SIP_DDR_DVFS, target_freq, online_cpus,
			0, 0, 0, 0, 0, &res);

Annotation

Implementation Notes