drivers/cpufreq/cppc_cpufreq.c

Source file repositories/reference/linux-study-clean/drivers/cpufreq/cppc_cpufreq.c

File Facts

System
Linux kernel
Corpus path
drivers/cpufreq/cppc_cpufreq.c
Extension
.c
Size
28115 bytes
Lines
1078
Domain
Driver Families
Bucket
drivers/cpufreq
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 cppc_freq_invariance {
	int cpu;
	struct irq_work irq_work;
	struct kthread_work work;
	struct cppc_perf_fb_ctrs prev_perf_fb_ctrs;
	struct cppc_cpudata *cpu_data;
};

static DEFINE_PER_CPU(struct cppc_freq_invariance, cppc_freq_inv);
static struct kthread_worker *kworker_fie;

static int cppc_perf_from_fbctrs(u64 reference_perf,
				 struct cppc_perf_fb_ctrs *fb_ctrs_t0,
				 struct cppc_perf_fb_ctrs *fb_ctrs_t1);

/**
 * __cppc_scale_freq_tick - CPPC arch_freq_scale updater for frequency invariance
 * @cppc_fi: per-cpu CPPC FIE data.
 *
 * The CPPC driver registers itself with the topology core to provide its own
 * implementation (cppc_scale_freq_tick()) of topology_scale_freq_tick() which
 * gets called by the scheduler on every tick.
 *
 * Note that the arch specific counters have higher priority than CPPC counters,
 * if available, though the CPPC driver doesn't need to have any special
 * handling for that.
 */
static void __cppc_scale_freq_tick(struct cppc_freq_invariance *cppc_fi)
{
	struct cppc_perf_fb_ctrs fb_ctrs = {0};
	struct cppc_cpudata *cpu_data;
	unsigned long local_freq_scale;
	u64 perf, ref_perf;

	cpu_data = cppc_fi->cpu_data;

	if (cppc_get_perf_ctrs(cppc_fi->cpu, &fb_ctrs)) {
		pr_warn("%s: failed to read perf counters\n", __func__);
		return;
	}

	ref_perf = cpu_data->perf_caps.reference_perf;
	perf = cppc_perf_from_fbctrs(ref_perf,
				     &cppc_fi->prev_perf_fb_ctrs, &fb_ctrs);
	if (!perf)
		return;

	cppc_fi->prev_perf_fb_ctrs = fb_ctrs;

	perf <<= SCHED_CAPACITY_SHIFT;
	local_freq_scale = div64_u64(perf, cpu_data->perf_caps.highest_perf);

	/* This can happen due to counter's overflow */
	if (unlikely(local_freq_scale > 1024))
		local_freq_scale = 1024;

	per_cpu(arch_freq_scale, cppc_fi->cpu) = local_freq_scale;
}

static void cppc_scale_freq_tick(void)
{
	__cppc_scale_freq_tick(&per_cpu(cppc_freq_inv, smp_processor_id()));
}

static struct scale_freq_data cppc_sftd = {
	.source = SCALE_FREQ_SOURCE_CPPC,
	.set_freq_scale = cppc_scale_freq_tick,
};

static void cppc_scale_freq_workfn(struct kthread_work *work)
{
	struct cppc_freq_invariance *cppc_fi;

	cppc_fi = container_of(work, struct cppc_freq_invariance, work);
	__cppc_scale_freq_tick(cppc_fi);
}

static void cppc_irq_work(struct irq_work *irq_work)
{
	struct cppc_freq_invariance *cppc_fi;

	cppc_fi = container_of(irq_work, struct cppc_freq_invariance, irq_work);
	kthread_queue_work(kworker_fie, &cppc_fi->work);
}

/*
 * Reading perf counters may sleep if the CPC regs are in PCC.  Thus, we
 * schedule an irq work in scale_freq_tick (since we reach here from hard-irq
 * context), which then schedules a normal work item cppc_scale_freq_workfn()
 * that updates the per_cpu arch_freq_scale variable based on the counter

Annotation

Implementation Notes