PN7120 NFC on ARM Slackware 14.2

28 July 2018
Back in July last year I bought a RaspberryPi with an NXP OM5577 which is a shield-hosted breakout board for the NXP PN7120 NFC reader/writer chip, although at the time I did not do much with it. This article details how to get the shield working with ARM Slackware, which involves rebuilding the kernel driver and libraries from source — for speed purposes this will be done via cross-compiling.

NXP provide a Linux image that has the NFC drivers and software pre-installed, but it is based on a dated version of Raspbian — a variant of Debian and I am not very fond of Debian as-is — and last time I checked the download link for OM5577-PN7120S_Rpi_Linux_demo_v1.2.zip was dead. After screwing up my installation of this image I decided to bite the bullet and get the NFC chip working with Slackware Linux, my preferred Linux distribution, but this was not helped by the generally poor documentation. This article is a mini-HowTo that is a write-up of my pains getting the shield working — I have put it in this section rather than the Electronics section because to me RaspberryPi is more of a mini-PC than what I would regard as a true embedded device.

Slackware 14.2 installation

I opted for Slackware 14.2 rather than slackware-current because the former is what I am used to using on other architectures, and the latter is the development branch which might have stability gotchas best bypassed when trying to get third-party software working. The instructions on the Slackware ARM on a Raspberry Pi website are mostly the standard Slackware installation procedure — main thing to keep an eye out for adding the initial FAT32 to the mount table as /boot, and rather than rebooting once the installation program has completed a few extra manual setup steps on the command-line need to be followed. The latter is mostly removing the stock Slackware kernel/firmware packages and installing ones made specially for RaspberryPi.

Once done it is advisable to clone the /boot partition — at ~100MB it is tiny by modern standards, and any likely big screwup involving overwritten files will be here rather than on the much larger root partition. I did also clone the whole 16GB SD card, but in hindsight this is only really worth doing if you expect to provision more than one RaspberryPi setup — if there are issues with the root partition, there is a good chance it is due to a dodgy or unsuitable SD card.

Building the kernel & driver module

Because of the relative speed and convenience of cross-compiling compared to building kernel sources directly on the RaspberryPi, the former way of building will be used. The instructions here are based on a those within the RaspberryPi kernel building documentation, so knowledge of what individual commands do is assumed, with the tools being stored under /opt/ARM/ — make modifications as-needed to suit your own setup.

Tool-clain setup

For cross-compilation, static builds of the tool-chain are available. Think these days 32-bit Linux is no longer mainstream, so will assume a 64-but build system:

git clone https://github.com/raspberrypi/tools /opt/ARM/tools PATH=$PATH:/opt/ARM/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/

Grabbing the sources

The RaspberryPi documentation suggests cloning using --depth=1 which only clones the latest commit — circa 480MB rather than 3GB for the whole lot — but to avoid headaches with subsequent changes this guide will explicitly use commit a06f9e522301 of the RapsberryPi kernel sources together with commit 5cabbc58ff17 of the NXP PN5xx drivers. The patch pn7120.patch wires up the Makefiles, kernel configuration, and Device Tree files:

git clone https://github.com/raspberrypi/linux cd linux git checkout a06f9e522301 cd drivers/misc git clone https://github.com/NXPNFCLinux/nxp-pn5xx.git cd nxp-pn5xx git checkout 5cabbc58ff17 cd ../../.. git apply -v pn7120.patch

If the git apply line fails, which is more likley if the git checkout commands above were omitted, a more robust alternative is to use:

patch -p1 < pn7120.patch

Kernel config

Even though my system is clearly a bcm2835 based RaspberryPi, bcm2709_defconfig seems to be the catch-all target chipset. Before compilation begins, kernel options need to be set. I'm not sure if make menuconfig actually needs the extra parameters, but it is simpler just to include them:

KERNEL=kernel7 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2709_defconfig KERNEL=kernel7 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

Within the menuconfig interface, the NXP driver needs to be enabled:

Device Drivers ---> Misc devices ---> NXP PN5XX based driver

For simplicity, I chose to build it into the kernel rather than build it as a module. Once kernel configuration is done, kick off the build process, which should take 20-30mins on a modern system (less if you are brave enough to use the -j make option).

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules dtbs

Kernel & module installation

For the installation of the kernel and its associated resources, I used a USB-based adapter that allows the SD card to be accessed directly from my desktop system. In my case fdisk -l had the following output for it:

Disk /dev/sdb: 15 GiB, 16106127360 bytes, 31457280 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0xc4c625a2 Device Boot Start End Sectors Size Id Type /dev/sdb1 * 32 205055 205024 100.1M c W95 FAT32 (LBA) /dev/sdb2 206848 1206271 999424 488M 82 Linux swap / Solaris /dev/sdb3 1206272 31457279 30251008 14.4G 83 Linux

The FAT32 partition /dev/sdb1 is the boot partition and the Linux partition /dev/sdb1 is the root. Mount these somewhere convenient:

mount /dev/sdb1 /mnt/boot/ mount /dev/sdb3 /mnt/root/

Use make modules_install with some extra parameters to copy across the kernel modules. You will need to make sure the cross-compiler parameters are there, as I vaguely recall things screwing up when they were omitted:

export PATH=$PATH:/opt/ARM/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=/mnt/root modules_install

The next stage is to copy across the kernel image, which here will have the destination name of nfc-kernel.img, using the mkknlimg script that attaches a trailer to the binary indicating that device trees should be used:

./scripts/mkknlimg arch/arm/boot/zImage /boot/nfc-kernel.img

According to the RespberryPi device tree information a device tree is mandatory since 4.4.y, so you might get away with a simple copy rather than using the mkknlimg script, but at time of writing I had yet to try this alternative approach. Finally the device tree and overlay files need to be copied to the boot partition:

cp arch/arm/boot/dts/*.dtb /mnt/boot/ cp arch/arm/boot/dts/overlays/*.dtb* /mnt/boot/overlays cp arch/arm/boot/dts/overlays/README /mnt/boot/overlays

In /boot/config.txt the line kernel=nfc-kernel.im will need to be added. Finishing up:

sync umount /mnt/boot/ umount /mnt/root/

Checking driver is working

Upon reboot, if the driver successfully recognises the NFC hardware, something much like the following will appear when the kernel logs are displayed using dmesg | grep pn54. If you see just the first line, then something has gone wrong:

[ 3.439139] pn54x_dev_init [ 3.442283] pn54x_probe [ 3.444721] pn544 1-0028: FIRM GPIO error getting from OF node [ 3.447210] pn544 1-0028: CLKREQ GPIO error getting from OF node [ 3.449819] pn544 1-0028: 1-0028 supply nxp,pn54x-pvdd not found, using dummy regulator [ 3.455138] pn544 1-0028: 1-0028 supply nxp,pn54x-vbat not found, using dummy regulator [ 3.460741] pn544 1-0028: 1-0028 supply nxp,pn54x-pmuvcc not found, using dummy regulator [ 3.466570] pn544 1-0028: 1-0028 supply nxp,pn54x-sevdd not found, using dummy regulator [ 3.472967] pn54x_probe: request irq_gpio 23 [ 3.475833] pn54x_probe: request ven_gpio 24 [ 3.478994] pn54x_probe : requesting IRQ 169

The other things to check for is the /dev/pn544 device - then it is safe to assume that the driver & hardware itself if working properly.

Allowing any user access to NFC

By default only the root user has access to the /dev/pn544 device. If you want non-root users to have access as well, one way of achieving this is to add the following udev rule — with Slackware, these live in the /etc/udev/rules.d directory:

ACTION=="add", KERNEL=="pn544", MODE="0666"

Building and using the NFC software

Since they are relatively quick to build, taking around 6-8 minutes, the NFC software will be built directly on the RaspberryPi itself. It is assumed that /opt/NFC is the installation directory, and the latest version of the software sources will be used:

git clone https://github.com/NXPNFCLinux/linux_libnfc-nci.git cd linux_libnfc-nci ./bootstrap ./configure --prefix=/opt/NFC --sysconfdir=/etc make && make install

For some reason the software has a subtle dependency on --sysconfdir=/etc and either omitting it or using an alternative directory results in errors such as NfcService Init Failed and SNEP Client Register Callback Failed when you try to run the sample app. For some reason having the I2C stuff enabled in /boot/config/ is not required. If all is successful, running the sample app should show the following:

root@Pi:~# /opt/NFC/sbin/nfcDemoApp poll ######################################################################################### ## NFC demo ## ######################################################################################### ## Poll mode activated ## ######################################################################################### ... press enter to quit ... Waiting for a Tag/Device...

..and bringing a tag near to the antenna:

NFC Tag Found Type : 'Type A - Mifare Ul' NFCID1 : '04 8C 98 D2 C6 48 80 ' Record Found : NDEF Content Max size : '137 bytes' NDEF Actual Content size : '0 bytes' ReadOnly : 'FALSE' Read NDEF Content Failed

Everything working :)

Identifying system version

The RaspberryPi model number is printed on top of the circuit board, as shown below below, although if you have a shield installed it will need to be removed in order to be able to see the model description:

From the command-line cat /proc/cpuinfo can be used to find out information on the RaspberryPi hardware, and the relevant values will be towards the bottom. For my board these are the values:

Hardware : BCM2835 Revision : a02082

Looking up the revision number my board is a RaspberryPi 3 Model B (revision-2) made by Sony UK, which corresponds with what is shown externally. Even though it claims to have a BCM2835 chipset, I suspect the chip itself is actually a BCM2837:

Device tree details

The part of NXP's technical note AN11697 on building the NXP NFC integration that deals with device nodes is horribly deficient in critical details. The device tree is one of two ways that the ARM Linux kernel can know about attached hardware, the other one being what the technical notes calls the platform data approach which to my knowledge is deprecated in kernels, and in both cases missing details include what I2C and GPIO parameters are appropriate for RaspberryPI. I only found out the correct details for the device tree approach from a YouTube video I came across by chance in a slide set, and for RaspberryPi this is the correct .dtsi snippet:

&i2c1{ status = "okay"; pn7120: pn7120@28 { compatible = "nxp,pn547"; reg = <0x28>; clock-frequency = <100000>; interrupt-parent = <&gpio>; interrupts = <23 0>; interrupt-gpios = <&gpio 23 0>; enable-gpios = <&gpio 24 0>; }; };

This needs to be included in the source code for the device tree blob appropriate for the Raspberry chipset, and the patch (see grabbing the sources above) to do this targets multiple chipsets because even with knowing what chip is in my RaspberryPi it was still not entirely clear which blob would be loaded. Even though my board is clearly a bcm2835 based one, bcm2709 seems to be the catch-all target chipset.