drivers/clocksource/arm_global_timer.c

Source file repositories/reference/linux-study-clean/drivers/clocksource/arm_global_timer.c

File Facts

System
Linux kernel
Corpus path
drivers/clocksource/arm_global_timer.c
Extension
.c
Size
12277 bytes
Lines
473
Domain
Driver Families
Bucket
drivers/clocksource
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 gt_prescaler_config {
	const char *compatible;
	unsigned long prescaler;
};

static const struct gt_prescaler_config gt_prescaler_configs[] = {
	/*
	 * On am43 the global timer clock is a child of the clock used for CPU
	 * OPPs, so the initial prescaler has to be compatible with all OPPs
	 * which are 300, 600, 720, 800 and 1000 with a fixed divider of 2, this
	 * gives us a GCD of 10. Initial frequency is 1000, so the prescaler is
	 * 50.
	 */
	{ .compatible = "ti,am43", .prescaler = 50 },
	{ .compatible = "xlnx,zynq-7000", .prescaler = 2 },
	{ .compatible = NULL }
};

static unsigned long gt_get_initial_prescaler_value(struct device_node *np)
{
	const struct gt_prescaler_config *config;

	if (CONFIG_ARM_GT_INITIAL_PRESCALER_VAL != 0)
		return CONFIG_ARM_GT_INITIAL_PRESCALER_VAL;

	for (config = gt_prescaler_configs; config->compatible; config++) {
		if (of_machine_is_compatible(config->compatible))
			return config->prescaler;
	}

	return 1;
}

static int __init global_timer_of_register(struct device_node *np)
{
	struct clk *gt_clk;
	static unsigned long gt_clk_rate;
	int err;
	unsigned long psv;

	/*
	 * In A9 r2p0 the comparators for each processor with the global timer
	 * fire when the timer value is greater than or equal to. In previous
	 * revisions the comparators fired when the timer value was equal to.
	 */
	if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9
	    && (read_cpuid_id() & 0xf0000f) < 0x200000) {
		pr_warn("global-timer: non support for this cpu version.\n");
		return -ENOSYS;
	}

	gt_ppi = irq_of_parse_and_map(np, 0);
	if (!gt_ppi) {
		pr_warn("global-timer: unable to parse irq\n");
		return -EINVAL;
	}

	gt_base = of_iomap(np, 0);
	if (!gt_base) {
		pr_warn("global-timer: invalid base address\n");
		return -ENXIO;
	}

	gt_clk = of_clk_get(np, 0);
	if (!IS_ERR(gt_clk)) {
		err = clk_prepare_enable(gt_clk);
		if (err)
			goto out_unmap;
	} else {
		pr_warn("global-timer: clk not found\n");
		err = -EINVAL;
		goto out_unmap;
	}

	psv = gt_get_initial_prescaler_value(np);
	gt_clk_rate = clk_get_rate(gt_clk);
	gt_target_rate = gt_clk_rate / psv;
	gt_clk_rate_change_nb.notifier_call =
		gt_clk_rate_change_cb;
	err = clk_notifier_register(gt_clk, &gt_clk_rate_change_nb);
	if (err) {
		pr_warn("Unable to register clock notifier\n");
		goto out_clk;
	}

	gt_evt = alloc_percpu(struct clock_event_device);
	if (!gt_evt) {
		pr_warn("global-timer: can't allocate memory\n");
		err = -ENOMEM;
		goto out_clk_nb;

Annotation

Implementation Notes