Horloge RTC gérée par le noyau Linux sur RaspberryPi

Vous savez sans nul doute que le RaspberryPi n’est pas équipé d’une horloge temps réel pour des raisons de coût. La distribution Raspian a fait le choix de récupérer l’heure sur Internet quand la carte est connecté sinon elle repart avec la dernière heure connu (fake_hwclock). Bien sur ce n’est pas satisfaisant…aussi vous trouverez sur le grand « n’Internet » nombre de site qui décrive comment rajouter une RTC (principalement en I2C). J’ai même fait quelques lignes la-dessus à la fin de l’article sur l’I2C sur Raspberry. Cependant je voudrais faire « encore mieux », en particulier que la partie « user space » (espace utilisateur) ne gère pas cette RTC mais que le noyau s’en charge de A à Z. Cette article décrit cette mise en oeuvre.

Pour être précis, c’est la lecture des articles de Christophe Blaess (article1, ressources) dans Gnu Linux France Magazine qui m’a titillé ! Aussi j’ai suivi son article sur la cross-compilation d’une chaîne de développement avec BuildRoot (version 2013.02) ainsi que la compilation d’un noyau avec cette même chaîne. L’installation, la compilation de la chaîne de cross-compilation puis la compilation d’un noyau se sont fait assez rapidement (une seule erreur au moment de la compilation de la uClibC : changer la locale de la ligne UCLIBC_BUILD_MINIMAL_LOCALES= »fr_FR » du fichier buildroot-2013.02/output/toolchain/uClibc-0.9.33.2/.config) et j’ai obtenu le fameux « Kernel panic… » signe que ca roule ;-). En faisant le tour de la configuration du noyau (make menuconfig), j’ai fouillé du coté de l’I2C et des pilotes pour RTC. Comme je dispose de deux platines RTC maison je me suis intéressé à leur intégration directement dans le noyau.

Les platines sont équipées de :

  • un DS1307 avec son quartz 32KHz et une pile bouton 3V : il faut alimenter ce circuit en 5V par l’intermédiaire de la broche du raspberry. Les lignes SDA et SCL sont toujours en 3,3V car ce sont des sorties à drain ouvert et les résistances de rappel des lignes SDA et SCL sont câblées sur le raspberry.
  • un DS3232 avec une pile bouton 3V : ce circuit dispose de son oscillateur interne compensé en température ce qui lui procure une grande précision et donc une dérive faible (de l’ordre de la minute sur un an). Cette platine est aussi équipé d’un capteur de température DS1621.

Ces trois circuits disposent de pilotes natifs dans le noyau Linux (drivers/rtc/rtc-ds1307.c et rtc-ds3232.c, drivers/hwmon/ds1621.c). Comme je veux les utiliser très tôt dans le processus de démarrage du noyau il faut que les pilotes soient inclus dans le noyau et pas sous forme de modules. Comme ces circuits sont tous utilisés sur le bus I2C, il faut donc aussi que celui-ci soit compilé dans le noyau. Enfin la gestion de la RTC par le noyau se fait en cochant l’option  « Device driver -> Real Time Clock -> Set system time from RTC on startup and resume ». Cependant il reste un problème : comment indiquer au pilote l’adresse de la RTC sur le bus I2C (ici 0x68) ainsi que le bus I2C actif (il y a deux bus i2c sur un raspberry qui n’ont pas la même numérotation suivant la version)…

Il faut lire la documentation du noyau sur « l’instantiation » de périphérique disponible sur https://www.kernel.org/doc/Documentation/i2c/instantiating-devices. La méthode 4 décrite dans ce document est celle utilisée par quasiment tout le monde :  on instantie un périphérique en passant son type et son adresse au bus i2c concerné géré par le noyau (Ex: echo ds1307 0×68 > /sys/class/i2c-adapter/i2c-1/new_device). A partir de ce moment vous pouvez utiliser la commande hwclock. Dans notre cas c’est un peu plus compliqué ! Il va falloir appliquer la méthode 1.

Le raspberryPi utilise un SoC Broadcom bcm2708. Il va donc falloir modifier le fichier d’initialisation de cette puce pour déclarer la RTC. Ce fichier est « arch/arm/mach-bcm2708/bcm2708.c ». Dans un premier temps il faut rajouter une structure :

// Ajout O. Dartois pour test RTC DS3232
static struct i2c_board_info __initdata bcm2708_i2c_devices[] = {
{
I2C_BOARD_INFO("rtc-ds1307", 0x68),
.type = "ds1307",
},
};

J’ai rajouté cette structure juste avant la fonction bcm2708_init(void). Puis toujours dans la fonction bcm2708_init(void), j’ai rajouté les lignes suivantes après l’enregistrement des busi2c :

bcm_register_device(&bcm2708_spi_device);
bcm_register_device(&bcm2708_bsc0_device);
bcm_register_device(&bcm2708_bsc1_device);
// Ajout O. Dartois
printk("Declaration RTC ...n");
i2c_register_board_info(1, bcm2708_i2c_devices, ARRAY_SIZE(bcm2708_i2c_devices));

On lance alors la compilation du noyau puis on copie le noyau zImage obtenu en kernel.img sur la carte SD du raspberryPi. On démarre la carte et on vérifie les messages du noyau (j’utilise le logiciel GrabSerial pour avoir une idée du temps de boot, c’est pour cette raison que vous avez des temps entre crochets au début de chaque ligne) :

[...]
[1.129414 0.000850] [ 0.077531] DMA: preallocated 4096 KiB pool for atomic coherent allocations
[1.148473 0.019059] [ 0.078721] bcm2708.uart_clock = 0
[1.149129 0.000656] [ 0.079882] Declaration RTC ...
[1.149740 0.000611] [ 0.080256] mailbox: Broadcom VideoCore Mailbox driver
[1.150716 0.000976] [ 0.080369] bcm2708_vcio: mailbox at f200b880
[...]
[2.072602 0.005752] [ 1.813068] i2c /dev entries driver
[2.086104 0.013502] [ 1.819306] bcm2708_i2c bcm2708_i2c.0: BSC0 Controller at 0x20205000 (irq 79) (baudrate 100k)
[2.097600 0.011496] [ 1.835187] rtc-ds1307 1-0068: rtc core: registered ds1307 as rtc0
[2.104376 0.006776] [ 1.843777] rtc-ds1307 1-0068: 56 bytes nvram
[2.117500 0.013124] [ 1.850403] bcm2708_i2c bcm2708_i2c.1: BSC1 Controller at 0x20804000 (irq 79) (baudrate 100k)
[...]
[2.413443 0.015522] [ 2.146339] rtc-ds1307 1-0068: setting system clock to 2013-05-06 06:25:21 UTC (1367821521)
[...]
pi@raspberrypi:~$ date
lundi 6 mai 2013, 08:30:00 (UTC+0200)
pi@raspberrypi:~$

Je vérifie la date à la fin du boot. La distribution utilisé ici est la Raspian, l’horloge RTC est en UTC, le système utilise le fuseau horaire de europe/paris. On est à l’heure d’été donc l’heure est bien cohérente (UTC+2).

Petite remarque supplémentaire : comme le bus i2c est intégré au noyau vous ne pouvez plus changer la vitesse du bus au moment du « insmod ». Pour changer dynamiquement la vitesse du bus i2c, il faut modifier la valeur de : /sys/module/i2c_bcm2708/parameters/baudrate

5 réflexions au sujet de « Horloge RTC gérée par le noyau Linux sur RaspberryPi »

  1. Ping : testez moi

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *