[LINUX] Lire l'implémentation de la minuterie globale ARM

Maintenant que nous avons lu jusqu'ici, lisons petit à petit l'implémentation de la minuterie globale d'ARM.


https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/clocksource/arm_global_timer.c

path: root/drivers/clocksource/arm_global_timer.c

TIMER_OF_DECLARE()

arm_global_timer.c


/* Only tested on r2p2 and r3p0  */
TIMER_OF_DECLARE(arm_gt, "arm,cortex-a9-global-timer",
			global_timer_of_register);

cortex-a9-global-timer est enregistré dans la ligne suivante.

global_timer_of_register()

CPU rivision check

Uniquement disponible après Cortex A9 r2p0.

global_timer_of_register()@arm_global_timer.c


static int __init global_timer_of_register(struct device_node *np)
{
	struct clk *gt_clk;
	int err = 0;
	/*
	 * 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;
	}

Reportez-vous aux paramètres de l'arborescence des appareils.

Les paramètres auxquels il faut se référer sont les suivants.

No. Nom Objectif
1 gt_ppi Pour la détection d'interruption de minuterie
2 gt_base Pour le contrôle du registre
3 gt_clk Pour obtenir la fréquence de la minuterie

global_timer_of_register()@arm_global_timer.c


	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;
	}

Enregistrement de chaque interruption CPU

--Calculer la fréquence d'horloge à partir de l'horloge obtenue précédemment.

global_timer_of_register()@arm_global_timer.c


	gt_clk_rate = clk_get_rate(gt_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;
	}

	err = request_percpu_irq(gt_ppi, gt_clockevent_interrupt,
				 "gt", gt_evt);
	if (err) {
		pr_warn("global-timer: can't register interrupt %d (%d)\n",
			gt_ppi, err);
		goto out_free;
	}

Réglez la minuterie globale de la CPU au démarrage.

global_timer_of_register()@arm_global_timer.c


	/* Register and immediately configure the timer on the boot CPU */
	err = gt_clocksource_init();
	if (err)
		goto out_irq;
	
	err = cpuhp_setup_state(CPUHP_AP_ARM_GLOBAL_TIMER_STARTING,
				"clockevents/arm/global_timer:starting",
				gt_starting_cpu, gt_dying_cpu);
	if (err)
		goto out_irq;

	gt_delay_timer_init();

	return 0;

out_irq:
	free_percpu_irq(gt_ppi, gt_evt);
out_free:
	free_percpu(gt_evt);
out_clk:
	clk_disable_unprepare(gt_clk);
out_unmap:
	iounmap(gt_base);
	WARN(err, "ARM Global timer register failed (%d)\n", err);

	return err;
}

gt_clockevent_interrupt()

gt_clockevent_interrupt()@arm_global_timer.c


static irqreturn_t gt_clockevent_interrupt(int irq, void *dev_id)
{
	struct clock_event_device *evt = dev_id;

	if (!(readl_relaxed(gt_base + GT_INT_STATUS) &
				GT_INT_STATUS_EVENT_FLAG))
		return IRQ_NONE;

	/**
	 * ERRATA 740657( Global Timer can send 2 interrupts for
	 * the same event in single-shot mode)
	 * Workaround:
	 *	Either disable single-shot mode.
	 *	Or
	 *	Modify the Interrupt Handler to avoid the
	 *	offending sequence. This is achieved by clearing
	 *	the Global Timer flag _after_ having incremented
	 *	the Comparator register	value to a higher value.
	 */
	if (clockevent_state_oneshot(evt))
		gt_compare_set(ULONG_MAX, 0);

	writel_relaxed(GT_INT_STATUS_EVENT_FLAG, gt_base + GT_INT_STATUS);
	evt->event_handler(evt);

	return IRQ_HANDLED;
}

gt_clocksource_init()

gt_clocksource_init()@arm_global_timer.c


static int __init gt_clocksource_init(void)
{
	writel(0, gt_base + GT_CONTROL);
	writel(0, gt_base + GT_COUNTER0);
	writel(0, gt_base + GT_COUNTER1);
	/* enables timer on all the cores */
	writel(GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL);

#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
	sched_clock_register(gt_sched_clock_read, 64, gt_clk_rate);
#endif
	return clocksource_register_hz(&gt_clocksource, gt_clk_rate);
}

Les structures suivantes sont enregistrées dans sched et clocksource.

arm_global_timer.c


static struct clocksource gt_clocksource = {
	.name	= "arm_global_timer",
	.rating	= 300,
	.read	= gt_clocksource_read,
	.mask	= CLOCKSOURCE_MASK(64),
	.flags	= CLOCK_SOURCE_IS_CONTINUOUS,
	.resume = gt_resume,
};

#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
static u64 notrace gt_sched_clock_read(void)
{
	return _gt_counter_read();
}
#endif

gt_clocksource_read() / gt_resume()

--gt_clocksource_read () est l'alias de gt_counter_read (). --gt_resume () réactive le temporisateur global lors de la reprise.

gt_clocksource_read()/gt_resume()@arm_global_timer.c



static u64 gt_clocksource_read(struct clocksource *cs)
{
	return gt_counter_read();
}

static void gt_resume(struct clocksource *cs)
{
	unsigned long ctrl;

	ctrl = readl(gt_base + GT_CONTROL);
	if (!(ctrl & GT_CONTROL_TIMER_ENABLE))
		/* re-enable timer on resume */
		writel(GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL);
}

gt_counter_read()

Lisez la valeur du compteur.

--Lisez les 32 bits supérieurs.

gt_counter_read()@arm_global_timer.c


/*
 * To get the value from the Global Timer Counter register proceed as follows:
 * 1. Read the upper 32-bit timer counter register
 * 2. Read the lower 32-bit timer counter register
 * 3. Read the upper 32-bit timer counter register again. If the value is
 *  different to the 32-bit upper value read previously, go back to step 2.
 *  Otherwise the 64-bit timer counter value is correct.
 */
static u64 notrace _gt_counter_read(void)
{
	u64 counter;
	u32 lower;
	u32 upper, old_upper;

	upper = readl_relaxed(gt_base + GT_COUNTER1);
	do {
		old_upper = upper;
		lower = readl_relaxed(gt_base + GT_COUNTER0);
		upper = readl_relaxed(gt_base + GT_COUNTER1);
	} while (upper != old_upper);

	counter = upper;
	counter <<= 32;
	counter |= lower;
	return counter;
}

static u64 gt_counter_read(void)
{
	return _gt_counter_read();
}

gt_starting_cpu(), gt_dying_cpu() Processus d'enregistrement d'un événement d'horloge sur la CPU au démarrage.

gt_starting_cpu()/gt_dying_cpu()@arm_global_timer.c


tatic int gt_starting_cpu(unsigned int cpu)
{
	struct clock_event_device *clk = this_cpu_ptr(gt_evt);

	clk->name = "arm_global_timer";
	clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
		CLOCK_EVT_FEAT_PERCPU;
	clk->set_state_shutdown = gt_clockevent_shutdown;
	clk->set_state_periodic = gt_clockevent_set_periodic;
	clk->set_state_oneshot = gt_clockevent_shutdown;
	clk->set_state_oneshot_stopped = gt_clockevent_shutdown;
	clk->set_next_event = gt_clockevent_set_next_event;
	clk->cpumask = cpumask_of(cpu);
	clk->rating = 300;
	clk->irq = gt_ppi;
	clockevents_config_and_register(clk, gt_clk_rate,
					1, 0xffffffff);
	enable_percpu_irq(clk->irq, IRQ_TYPE_NONE);
	return 0;
}

static int gt_dying_cpu(unsigned int cpu)
{
	struct clock_event_device *clk = this_cpu_ptr(gt_evt);

	gt_clockevent_shutdown(clk);
	disable_percpu_irq(clk->irq);
	return 0;
}

gt_clockevent_shutdown()

gt_clockevent_shutdown()@arm_global_timer.c


static int gt_clockevent_shutdown(struct clock_event_device *evt)
{
	unsigned long ctrl;

	ctrl = readl(gt_base + GT_CONTROL);
	ctrl &= ~(GT_CONTROL_COMP_ENABLE | GT_CONTROL_IRQ_ENABLE |
		  GT_CONTROL_AUTO_INC);
	writel(ctrl, gt_base + GT_CONTROL);
	return 0;
}

gt_compare_set()/gt_clockevent_set_periodic()/gt_clockevent_set_next_event

gt_compare_set()/gt_clockevent_set_periodic()/gt_clockevent_set_next_event@arm_global_timer.c


/**
 * To ensure that updates to comparator value register do not set the
 * Interrupt Status Register proceed as follows:
 * 1. Clear the Comp Enable bit in the Timer Control Register.
 * 2. Write the lower 32-bit Comparator Value Register.
 * 3. Write the upper 32-bit Comparator Value Register.
 * 4. Set the Comp Enable bit and, if necessary, the IRQ enable bit.
 */
static void gt_compare_set(unsigned long delta, int periodic)
{
	u64 counter = gt_counter_read();
	unsigned long ctrl;

	counter += delta;
	ctrl = GT_CONTROL_TIMER_ENABLE;
	writel_relaxed(ctrl, gt_base + GT_CONTROL);
	writel_relaxed(lower_32_bits(counter), gt_base + GT_COMP0);
	writel_relaxed(upper_32_bits(counter), gt_base + GT_COMP1);

	if (periodic) {
		writel_relaxed(delta, gt_base + GT_AUTO_INC);
		ctrl |= GT_CONTROL_AUTO_INC;
	}

	ctrl |= GT_CONTROL_COMP_ENABLE | GT_CONTROL_IRQ_ENABLE;
	writel_relaxed(ctrl, gt_base + GT_CONTROL);
}

static int gt_clockevent_set_periodic(struct clock_event_device *evt)
{
	gt_compare_set(DIV_ROUND_CLOSEST(gt_clk_rate, HZ), 1);
	return 0;
}

static int gt_clockevent_set_next_event(unsigned long evt,
					struct clock_event_device *unused)
{
	gt_compare_set(evt, 0);
	return 0;
}

gt_delay_timer_init()

--Spécifiez la fréquence d'horloge --Enregistrement de la minuterie de retard

gt_delay_timer_init()@arm_global_timer.c


static unsigned long gt_read_long(void)
{
	return readl_relaxed(gt_base + GT_COUNTER0);
}

static struct delay_timer gt_delay_timer = {
	.read_current_timer = gt_read_long,
};

static void __init gt_delay_timer_init(void)
{
	gt_delay_timer.freq = gt_clk_rate;
	register_current_timer_delay(&gt_delay_timer);
}

Résumé

Il semble être grossièrement divisé en contenu suivant.

Eh bien, peut-être que vous pouvez créer votre propre minuterie ...? J'en suis venu à la simple impression.

Recommended Posts

Lire l'implémentation de la minuterie globale ARM
J'ai lu l'implémentation de range (Objects / rangeobject.c)
Lisez également le contenu de arch / arm / kernel / swp_emulate.c
Othello-De la troisième ligne de "Implementation Deep Learning" (3)
L'histoire de la manipulation des variables globales Python
Lire tout le contenu de proc / [pid]
Othello-De la troisième ligne de "Implementation Deep Learning" (2)
[Python] Lire le code source de Bottle Part 2
Pourquoi l'implémentation Python d'ISUCON 5 a utilisé Bottle
[Python] Lire le code source de Bottle Part 1
J'ai lu et implémenté les variantes de UKR
Modèle de script python pour lire le contenu du fichier
Jouez avec l'implémentation de l'interface utilisateur de Pythonista 3 [Super Super Primer]
J'ai suivi la mise en place de la commande du (première moitié)
À propos des tests dans la mise en œuvre de modèles d'apprentissage automatique
J'ai suivi la mise en place de la commande du (seconde moitié)
DataNitro, implémentation de la fonction de lecture des données de feuille
Lisez la puissance du compteur intelligent avec M5StickC (édition BP35C0-J11-T01)
Comment accéder à la variable globale du module importé
Un mémorandum sur la mise en œuvre des recommandations en Python
Le début de cif2cell
Le sens de soi
Lire la documentation OpenCV
le zen de Python
Implémentation de la séquence de Fibonacci
La vengeance des types: la vengeance des types
Augmentez la vitesse de la méthode Monte Carlo de l'implémentation de découpage Cython.
Une implémentation Python simple de la méthode k-voisinage (k-NN)
Lire le fichier GRIB2 de l'Agence météorologique sur pygrib