Suite à un petit commentaire de Yoann Sculo concernant l’avant dernier article, j’ai eu envie de détailler un peu le fonctionnement du multiplexage des GPIO sur un processeur OMAP (comme celui de la Pandaboard), et les paramètres auxquels nous pouvons avoir accès par l’intermédiaire du système de fichiers debugfs
.
Multiplexage des GPIO de l’OMAP
Pour obtenir des informations détaillées sur le fonctionnement de l’OMAP 4430 (celui utilisé dans la Pandaboard), il ne faut pas hésiter à se reporter au TRM (Technical Reference Manual) diffusé par Texas Instruments quitte à prendre un peu de temps pour se familiariser avec l’organisation de ses 5554 pages !
Les processeurs de la famille OMAP proposent un nombre beaucoup plus important de lignes logiques de GPIO que le nombre de broches physiquement disponibles sur leurs boîtiers. Aussi un multiplexage est-il nécessaire pour sélectionner quelle fonctionnalité est affectée à chaque broche à un instant donné.
Prenons l’exemple de la broche 10 du connecteur J3 que nous avons déjà employée à plusieurs reprises. En examinant la documentation de la Pandaboard, nous avons vu que trois fonctions lui sont attribuées : MCSPI1_CS
(Chip Select du bus SPI1), UART1_RX
(ligne de réception du port série numéro 1) et GPIO_138
. C’est cette dernière fonction que nous avons employée dans les articles précédents.
Le choix entre ces trois fonctions est fait tout d’abord par le bootloader (U-boot), comme nous l’avons vu dans l’avant-dernier article. Ensuite, le noyau Linux lui-même affecte les broches qui le concernent. Toutefois, il nous est possible d’intervenir sur le paramétrage depuis l’espace utilisateur.
Chaque broche peut théoriquement avoir jusqu’à huit fonctions. À un moment donné, une broche est configurée suivant un mode, et le processeur OMAP propose donc les modes 0 à 7. Par exemple, sur la Pandaboard la broche 10 du connecteur J3 peut être configurée avec les modes suivants :
- Mode 0 :
MCSPI1_CS
- Mode 1 :
UART1_RX
- Mode 2 : Non utilisé
- Mode 3 :
GPIO_138
- Mode 4 à 6 : Non utilisés
- Mode 7 : Safe mode : la broche est isolée, les contacts éventuels avec d’autres broches ne provoqueront pas de court-circuit.
Consultation avec debugfs
Le système de fichiers debugfs
est, comme son nom l’indique, destiné à aider à la mise au point du noyau. Il s’agit d’un système de fichiers virtuel, comme ceux que l’on monte sur /proc
ou /sys
, mais les règles d’utilisation sont beaucoup plus souples qu’avec ces derniers (/proc
est réservé aux processus et aux paramètres de fonctionnement du noyau, /sys
contient la représentation du système vu du kernel en fournissant une unique valeur par fichier).
Bien que l’on puisse choisir un autre répertoire, on convient généralement de monter (dans un script de démarrage ou dans le fichier /etc/fstab
) le système debugfs
sur le répertoire /sys/kernel/debug
.
En voici un exemple sur la Pandaboard :
[Panda]# mount none /sys/kernel/debug/ -t debugfs [Panda]# cd /sys/kernel/debug/ [Panda]# ls bdi gpio ieee80211 mmc0 regulator tracing voltage bluetooth hid memblock omap_mux sched_features usb [Panda]#
Nous pouvons examiner dans le fichier gpio
la configuration actuelle des GPIO employés par le système.
[Panda]# cat gpio GPIOs 0-31, gpio: gpio-1 (hub_power ) out hi gpio-7 (pandaboard::status1 ) out lo gpio-8 (pandaboard::status2 ) out lo GPIOs 32-63, gpio: gpio-62 (hub_nreset ) out hi GPIOs 64-95, gpio: GPIOs 96-127, gpio: GPIOs 128-159, gpio: GPIOs 160-191, gpio: [Panda]#
Nous voyons qu’il y a six contrôleurs, chacun gérant 32 GPIO, ce qui est confirmé par le Technical Reference Manual de l’OMAP. Ceci correspond aux entrées gpiochip
que nous trouvons dans le répertoire /sys/class/gpio
.
[Panda]# ls /sys/class/gpio/ export gpio62 gpiochip128 gpiochip32 gpiochip96 gpio1 gpiochip0 gpiochip160 gpiochip64 unexport [Panda]#
Le GPIO 1 correspond à l’alimentation du bloc Ethernet + USB de la Pandaboard, ainsi une commande.
[Panda]# echo 0 > /sys/class/gpio/gpio1/value
éteint-elle ce hub (et coupe par la même occasion ma connexion sur la Pandaboard puisque j’y accède par ssh, ce qui m’oblige à réinitialiser la carte !).
Les GPIO 7 et 8 sont utilisés pour les LEDs d’état de la Pandaboard (voir cet article), et le 62 permet de réinitialiser le contrôleur USB/Ethernet.
Examinons le contenu du répertoire omap_mux
, qui concerne le multiplexage des sorties de l’OMAP.
[Panda]# cd omap_mux/ [Panda]# ls abe_clks csi22_dy1 gpmc_a19 gpmc_ncs7 kpd_row2 sim_pwrctrl usbb1_ulpitll_clk abe_dmic_clk1 dpm_emu0 gpmc_a20 gpmc_noe kpd_row3 sim_reset usbb1_ulpitll_dat0 abe_dmic_din1 dpm_emu1 gpmc_a21 gpmc_nwe kpd_row4 sr_scl usbb1_ulpitll_dat1 abe_dmic_din2 dpm_emu10 gpmc_a22 gpmc_nwp kpd_row5 sr_sda usbb1_ulpitll_dat2 abe_dmic_din3 dpm_emu11 gpmc_a23 gpmc_wait0 mcspi1_clk sys_32k usbb1_ulpitll_dat3 abe_mcbsp1_clkx dpm_emu12 gpmc_a24 gpmc_wait1 mcspi1_cs0 sys_boot0 usbb1_ulpitll_dat4 abe_mcbsp1_dr dpm_emu13 gpmc_a25 gpmc_wait2 mcspi1_cs1 sys_boot1 usbb1_ulpitll_dat5 abe_mcbsp1_dx dpm_emu14 gpmc_ad0 hdmi_cec mcspi1_cs2 sys_boot2 usbb1_ulpitll_dat6 abe_mcbsp1_fsx dpm_emu15 gpmc_ad1 hdmi_ddc_scl mcspi1_cs3 sys_boot3 usbb1_ulpitll_dat7 abe_mcbsp2_clkx dpm_emu16 gpmc_ad10 hdmi_ddc_sda mcspi1_simo sys_boot4 usbb1_ulpitll_dir abe_mcbsp2_dr dpm_emu17 gpmc_ad11 hdmi_hpd mcspi1_somi sys_boot5 usbb1_ulpitll_nxt abe_mcbsp2_dx dpm_emu18 gpmc_ad12 hdq_sio mcspi4_clk sys_boot6 usbb1_ulpitll_stp abe_mcbsp2_fsx dpm_emu19 gpmc_ad13 i2c1_scl mcspi4_cs0 sys_boot7 usbb2_hsic_data abe_pdm_dl_data dpm_emu2 gpmc_ad14 i2c1_sda mcspi4_simo sys_nirq1 usbb2_hsic_strobe abe_pdm_frame dpm_emu3 gpmc_ad15 i2c2_scl mcspi4_somi sys_nirq2 usbb2_ulpitll_clk abe_pdm_lb_clk dpm_emu4 gpmc_ad2 i2c2_sda sdmmc1_clk sys_nrespwron usbb2_ulpitll_dat0 abe_pdm_ul_data dpm_emu5 gpmc_ad3 i2c3_scl sdmmc1_cmd sys_nreswarm usbb2_ulpitll_dat1 board dpm_emu6 gpmc_ad4 i2c3_sda sdmmc1_dat0 sys_pwr_req usbb2_ulpitll_dat2 cam_globalreset dpm_emu7 gpmc_ad5 i2c4_scl sdmmc1_dat1 sys_pwron_reset_out usbb2_ulpitll_dat3 cam_shutter dpm_emu8 gpmc_ad6 i2c4_sda sdmmc1_dat2 uart2_cts usbb2_ulpitll_dat4 cam_strobe dpm_emu9 gpmc_ad7 jtag_ntrst sdmmc1_dat3 uart2_rts usbb2_ulpitll_dat5 csi21_dx0 fref_clk0_out gpmc_ad8 jtag_rtck sdmmc1_dat4 uart2_rx usbb2_ulpitll_dat6 csi21_dx1 fref_clk1_out gpmc_ad9 jtag_tck sdmmc1_dat5 uart2_tx usbb2_ulpitll_dat7 csi21_dx2 fref_clk2_out gpmc_clk jtag_tdi sdmmc1_dat6 uart3_cts_rctx usbb2_ulpitll_dir csi21_dx3 fref_clk3_out gpmc_nadv_ale jtag_tdo sdmmc1_dat7 uart3_rts_sd usbb2_ulpitll_nxt csi21_dx4 fref_clk3_req gpmc_nbe0_cle jtag_tms_tmsc sdmmc5_clk uart3_rx_irrx usbb2_ulpitll_stp csi21_dy0 fref_clk4_out gpmc_nbe1 kpd_col0 sdmmc5_cmd uart3_tx_irtx usbc1_icusb_dm csi21_dy1 fref_clk4_req gpmc_ncs0 kpd_col1 sdmmc5_dat0 uart4_rx usbc1_icusb_dp csi21_dy2 fref_clk_ioreq gpmc_ncs1 kpd_col2 sdmmc5_dat1 uart4_tx csi21_dy3 fref_slicer_in gpmc_ncs2 kpd_col3 sdmmc5_dat2 usba0_otg_ce csi21_dy4 fref_xtal_in gpmc_ncs3 kpd_col4 sdmmc5_dat3 usba0_otg_dm csi22_dx0 gpmc_a16 gpmc_ncs4 kpd_col5 sim_cd usba0_otg_dp csi22_dx1 gpmc_a17 gpmc_ncs5 kpd_row0 sim_clk usbb1_hsic_data csi22_dy0 gpmc_a18 gpmc_ncs6 kpd_row1 sim_io usbb1_hsic_strobe [Panda]#
Par convention, les ports du processeur OMAP sont nommés en utilisant leur fonction première, celle du Mode 0. Ainsi la broche 10 de connecteur J3 est-elle nommée mcspi1_cs1
.
[Panda]# cat mcspi1_cs1 name: mcspi1_cs1.gpio_138 (0x4a10013a/0x13a = 0x010b), b af23, t NA mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE3 signals: mcspi1_cs1 | uart1_rx | NA | gpio_138 | NA | NA | NA | safe_mode [Panda]#
Les signaux possibles pour cette broche sont décrits sur la ligne « signals:
» et correspondent à la liste observée plus haut. La ligne « mode:
» indique la configuration actuelle. Ici, le mode sélectionné est Mode_3 (gpio_138
) configuré en entrée avec une résistance de rappel à la masse (pulldown).
Paramétrage depuis l’espace utilisateur
Nous pouvons modifier les paramètres présentés par debugfs
. Pour cela il faut écrire dans le fichier une valeur sur deux octets qui se compose ainsi.
- Bits 0, 1 et 2 : numéro de mode
- Bit 3 : activation de la résistance de rappel en entrée
- Bit 4 : résistance de rappel vers le haut (pull-up).
- Bit 5-7 : inutilisés
- Bit 8 : broche en entrée
- Bit 9-15 : configuration de l’état de la broche lors de la mise en sommeil (non détaillée ici)
Modifions ces paramètres, en conservant le mode 3 (celui du GPIO).
Basculement en sortie
Il faut désactiver les bits Input_enable
, Pull_enable
et calculer le mode désiré.
Nous allons donc écrire la valeur 0x0003
.
[Panda]# echo 0x0003 > mcspi1_cs1 [Panda]# cat mcspi1_cs1 name: mcspi1_cs1.gpio_138 (0x4a10013a/0x13a = 0x0003), b af23, t NA mode: OMAP_PIN_OUTPUT | OMAP_MUX_MODE3 signals: mcspi1_cs1 | uart1_rx | NA | gpio_138 | NA | NA | NA | safe_mode [Panda]#
La ligne « mode:
» a bien été modifiée pour afficher OMAP_PIN_OUTPUT
.
Basculement en entrée simple
Pour ramener notre broche en entrée, nous activons le bit Input_enable
.
[Panda]#echo 0x0103 > mcspi1_cs1
[Panda]#cat mcspi1_cs1
name: mcspi1_cs1.gpio_138 (0x4a10013a/0x13a = 0x0103), b af23, t NA mode: OMAP_PIN_INPUT | OMAP_MUX_MODE3 signals: mcspi1_cs1 | uart1_rx | NA | gpio_138 | NA | NA | NA | safe_mode [Panda]#
Aucun pull-up ou pull-down n’étant activé, la valeur lue en entrée peut fluctuer si elle n’est pas connectée. Par exemple les lignes suivantes
[Panda]# echo 138 > /sys/class/gpio/export [Panda]# while true; do cat /sys/class/gpio/gpio138/value ; done
affichent une alternance de valeurs 0 et 1 qui évoluent si on effleure du doigt la broche 10 (ce que je ne conseille pas, des tensions élevées engendrées par l’électricité statique pouvant endommager le processeur).
Basculement en entrée avec rappel à la masse (pull-down)
Pour activer un pull-down, il suffit d’activer le bit Pull_enable
en laissant le bit Pull_up
désactivé. Il s’agit de la configuration fixée par défaut par le bootloader U-boot.
La valeur à écrire est 0x010B
[Panda]# echo 0x010B > mcspi1_cs1 [Panda]# cat mcspi1_cs1 name: mcspi1_cs1.gpio_138 (0x4a10013a/0x13a = 0x010b), b af23, t NA mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE3 signals: mcspi1_cs1 | uart1_rx | NA | gpio_138 | NA | NA | NA | safe_mode [Panda]#
Nous pouvons vérifier l’état de l’entrée. Cette fois la valeur est figée, même si l’entrée est laissée non connectée et si on l’effleure du doigt.
[Panda]# cat /sys/class/gpio/gpio138/value 0 [Panda]#
Basculement en entrée avec tirage à la tension d’alimentation (pull-up)
Enfin, nous allons modifier la résistance de rappel en activant un pull-up vers la tension d’alimentation grâce au bit Pull_up
.
[Panda]# echo 0x011B > mcspi1_cs1 [Panda]# cat mcspi1_cs1 name: mcspi1_cs1.gpio_138 (0x4a10013a/0x13a = 0x011b), b af23, t NA mode: OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE3 signals: mcspi1_cs1 | uart1_rx | NA | gpio_138 | NA | NA | NA | safe_mode [Panda]#
À présent l’entrée (toujours non connectée) ne présente plus la même valeur.
[Panda]# cat /sys/class/gpio/gpio138/value 1 [Panda]#
Changement de mode sur une broche
Nous pouvons choisir de modifier le mode de fonctionnement d’une broche du port d’extension A. Il n’est pas facile de savoir comment les broches sont configurées par défaut, car la documentation de la Pandaboard est un peu floue sur ce sujet. Voici donc un petit script shell qui nous affiche pour chaque broche (identifiée par son numéro et son nom officiel) la configuration actuelle ainsi que la liste des configurations possibles.
/root/liste-j3.sh: #/bin/sh cd /sys/kernel/debug/omap_mux num=3 for name in gpmc_ad7 mcspi1_cs3 gpmc_ad6 uart4_tx gpmc_ad5 uart4_rx gpmc_ad4 mcspi1_cs1 gpmc_ad3 mcspi1_simo gpmc_ad2 mcspi1_cs2 gpmc_ad1 mcspi1_cs0 gpmc_ad0 mcspi1_somi gpmc_nwe mcspi1_clk gpmc_noe gpmc_ad15 i2c4_sda i2c4_scl do mode=$(grep "^mode:" "${name}" | sed 's/.*OMAP_MUX_MODE//') list=$(grep "^signals:" "${name}" | sed 's/^signals://; s/|//g') item=0 for i in ${list} do if [ ${item} -eq ${mode} ] then selected_function=$i break; fi item=$((item + 1)) done echo "${num} ${name} ${mode} ${selected_function} (${list})" num=$((num + 1)) done
A l’exécution, nous obtenons une liste un peu compliquée à lire qui contient pour chaque broche : son numéro, son nom, le numéro du mode sélectionné, la fonction sélectionnée et la liste des fonctions disponibles. Ci-dessous j’ai aligné manuellement les colonnes pour améliorer la lisibilité.
[Panda]# /root/liste-j3.sh 3 gpmc_ad7 1 sdmmc2_dat7 ( gpmc_ad7 sdmmc2_dat7 sdmmc2_clk_fdbk NA NA NA NA NA ) 4 mcspi1_cs3 3 gpio_140 ( mcspi1_cs3 uart1_rts slimbus2_data gpio_140 NA NA NA safe_mode ) 5 gpmc_ad6 1 sdmmc2_dat6 ( gpmc_ad6 sdmmc2_dat6 sdmmc2_dir_cmd NA NA NA NA NA ) 6 uart4_tx 0 uart4_tx ( uart4_tx sdmmc4_dat1 kpd_col8 gpio_156 NA NA NA safe_mode ) 7 gpmc_ad5 1 sdmmc2_dat5 ( gpmc_ad5 sdmmc2_dat5 sdmmc2_dir_dat1 NA NA NA NA NA ) 8 uart4_rx 0 uart4_rx ( uart4_rx sdmmc4_dat2 kpd_row8 gpio_155 NA NA NA safe_mode ) 9 gpmc_ad4 1 sdmmc2_dat4 ( gpmc_ad4 sdmmc2_dat4 sdmmc2_dir_dat0 NA NA NA NA NA ) 10 mcspi1_cs1 3 gpio_138 ( mcspi1_cs1 uart1_rx NA gpio_138 NA NA NA safe_mode ) 11 gpmc_ad3 1 sdmmc2_dat3 ( gpmc_ad3 sdmmc2_dat3 NA NA NA NA NA NA ) 12 mcspi1_simo 0 mcspi1_simo ( mcspi1_simo NA NA gpio_136 NA NA NA safe_mode ) 13 gpmc_ad2 1 sdmmc2_dat2 ( gpmc_ad2 sdmmc2_dat2 NA NA NA NA NA NA ) 14 mcspi1_cs2 3 gpio_139 ( mcspi1_cs2 uart1_cts slimbus2_clock gpio_139 NA NA NA safe_mode ) 15 gpmc_ad1 1 sdmmc2_dat1 ( gpmc_ad1 sdmmc2_dat1 NA NA NA NA NA NA ) 16 mcspi1_cs0 0 mcspi1_cs0 ( mcspi1_cs0 NA NA gpio_137 NA NA NA safe_mode ) 17 gpmc_ad0 1 sdmmc2_dat0 ( gpmc_ad0 sdmmc2_dat0 NA NA NA NA NA NA ) 18 mcspi1_somi 0 mcspi1_somi ( mcspi1_somi NA NA gpio_135 NA NA NA safe_mode ) 19 gpmc_nwe 1 sdmmc2_cmd ( gpmc_nwe sdmmc2_cmd NA NA NA NA NA NA ) 20 mcspi1_clk 0 mcspi1_clk ( mcspi1_clk NA NA gpio_134 NA NA NA safe_mode ) 21 gpmc_noe 1 sdmmc2_clk ( gpmc_noe sdmmc2_clk NA NA NA NA NA NA ) 22 gpmc_ad15 3 gpio_39 ( gpmc_ad15 kpd_col3 c2c_data8 gpio_39 NA sdmmc1_dat7 NA NA ) 23 i2c4_sda 0 i2c4_sda ( i2c4_sda NA NA gpio_133 NA NA NA safe_mode ) 24 i2c4_scl 0 i2c4_scl ( i2c4_scl NA NA gpio_132 NA NA NA safe_mode) [Panda]#
Prenons la ligne correspondant à la broche numéro 6 par exemple. Son nom est « uart4_tx
» et il s’agit également de sa fonction par défaut car son mode initial est zéro. Nous voyons dans la liste des fonctions possibles – entre parenthèses – qu’elle peut être commutée sur le GPIO 156 (mode 3). Nous allons essayer.
Petite parenthèse : une légère erreur dans le script ci-dessus avait initialement décalé les numéros de broches et j’ai passé un long moment à essayer de chercher la fonction uart4_tx
et le gpio_156
sur la broche 7 au lieu de 6. Cette fois l’erreur était inoffensive mais dans d’autres circonstances un tel décalage peut être fatal au processeur (cela m’est arrivé récemment…) Soyez donc très prudents dans les manipulations des GPIO de ce type de carte, les broches sont directement reliées aux pattes du processeur sans plus de protection.
Pour commencer, relions une sonde d’oscilloscope à la broche 6, et l’autre à la masse.
Le signal est immobile, fixé sur 1,8V. Nous allons demander au kernel d’envoyer des données sur la ligne série ttyO3
, ce qui se traduira par une activité sur la ligne d’émission (TX) de l’UART numéro 4.
[Panda]# cat /bin/ls > /dev/ttyO3
Alors que le noyau émet le contenu du fichier exécutable /bin/ls
sur sa sortie série, nous observons sur la broche 6 une activité incessante qui est typique d’un transfert sur liaison série.
Après avoir arreté l’émission (avec Contrôle-C) nous basculons la broche 6 sur le GPIO 156 en modifiant le mode.
[Panda]# echo 0x0003 > uart4_tx [Panda]#
Plus aucun signal n’est visible sur l’oscilloscope. Activons la sortie GPIO 156.
[Panda]# echo 156 > /sys/class/gpio/export [Panda]# echo out > /sys/class/gpio/gpio156/direction
Puis basculons régulèrement l’état du GPIO 156 pour vérifier si nous le voyons à l’oscilloscope.
[Panda]# while true; do echo 0 > /sys/class/gpio/gpio156/value ; usleep 100; echo 1 > /sys/class/gpio/gpio156/value; usleep 100; done
Le signal carré est alors clairement visible.
Nous avons réussi à basculer notre broche 6 du connecteur J3 de la fonction TX de l’UART4 à la fonction GPIO 156.
Conclusion
Nous voyons que le pseudo système de fichiers debugfs
peut s’avérer très utile pour consulter ou modifier les fonctions attribuées aux broches de sortie du processeur OMAP. Il ne faut toutefois pas oublier que son interface est avant tout destinée aux développeurs du kernel, aussi n’y a-t-il aucune garantie de perénité des paramètres configurables au gré des futures évolutions du noyau.