PN7120 NFC on ARM Slackware 14.2
28 July 2018Back 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 abcm2835
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 casefdisk -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 usingdmesg | 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
The other things to check for is the
ACTION=="add", KERNEL=="pn544", MODE="0666"
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
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 :)
From the command-line
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
&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 /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:
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:
--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:
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:
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:
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:
bcm2835
based one, bcm2709
seems to be the catch-all target chipset.