* [PATCH v5 0/4] nfc: st-nci: Fairphone 5 NFC bring-up (ST21NFCD)
@ 2026-10-05 20:51 Kristian Brox
2026-10-05 20:51 ` [PATCH v5 1/4] dt-bindings: net: nfc: add st,st21nfcd Kristian Brox
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Kristian Brox @ 2026-10-05 20:51 UTC (permalink / raw)
To: David Heidelberg, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, Konrad Dybcio
Cc: Krzysztof Kozlowski, Konrad Dybcio, Luca Weiss, Dmitry Baryshkov,
oe-linux-nfc, netdev, devicetree, linux-kernel, linux-arm-msm,
Kristian Brox, Krzysztof Kozlowski, Tim auf der Landwehr,
Abel Vesa
This adds NFC on the Fairphone 5 (qcm6490). The board uses an ST21NFCD
on I2C. That part speaks raw NCI; the current st-nci driver always
wraps NDLC, so using st,st21nfcb-i2c leaves the adapter unusable.
The series adds a st,st21nfcd compatible for the raw-NCI path and the
Fairphone 5 DT node. Boards that already use st21nfcb / st21nfcc keep
the NDLC path.
A later poll start that times out in "failed to set local general bytes"
is still unreproduced on this phone.
Signed-off-by: Kristian Brox <isyourbrainfoss@proton.me>
---
Link to v4:
https://lore.kernel.org/oe-linux-nfc/20260902-fp5-st21nfcd-v4-v4-0-ded2f1c501be@proton.me/
Changes in v5:
- Map proprietary RF protocol 0x90 to MIFARE (Tim). On this phone a
Classic 1K was accepted as 0x90 and was not dropped.
- Binding carries Reviewed-by from Krzysztof Kozlowski. DTS carries
Reviewed-by from Konrad Dybcio and Abel Vesa. The patch bodies are
the v4 patches. The raw-NCI driver change is unchanged.
- Tim's reference-clock restart and leading-0x7e check were tried on
this phone and left out.
---
Kristian Brox (3):
dt-bindings: net: nfc: add st,st21nfcd
nfc: st-nci: add raw NCI path for ST21NFCD
arm64: dts: qcom: qcm6490-fairphone-fp5: add ST21NFCD NFC
Tim auf der Landwehr (1):
nfc: st-nci: map proprietary RF protocol 0x90 to MIFARE
.../devicetree/bindings/net/nfc/st,st-nci.yaml | 49 +++++++++----
arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts | 34 ++++++++-
drivers/nfc/st-nci/core.c | 38 +++++++++-
drivers/nfc/st-nci/i2c.c | 82 ++++++++++++++++++++--
drivers/nfc/st-nci/ndlc.c | 27 +++++--
drivers/nfc/st-nci/ndlc.h | 5 +-
drivers/nfc/st-nci/se.c | 3 +
drivers/nfc/st-nci/spi.c | 2 +-
drivers/nfc/st-nci/st-nci.h | 2 +
9 files changed, 211 insertions(+), 31 deletions(-)
---
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
change-id: 20261005-fp5-st21nfcd-v5-1818d4776267
Best regards,
--
Kristian Brox <isyourbrainfoss@proton.me>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v5 1/4] dt-bindings: net: nfc: add st,st21nfcd
2026-10-05 20:51 [PATCH v5 0/4] nfc: st-nci: Fairphone 5 NFC bring-up (ST21NFCD) Kristian Brox
@ 2026-10-05 20:51 ` Kristian Brox
2026-10-05 20:51 ` [PATCH v5 2/4] nfc: st-nci: add raw NCI path for ST21NFCD Kristian Brox
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Kristian Brox @ 2026-10-05 20:51 UTC (permalink / raw)
To: David Heidelberg, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, Konrad Dybcio
Cc: Krzysztof Kozlowski, Konrad Dybcio, Luca Weiss, Dmitry Baryshkov,
oe-linux-nfc, netdev, devicetree, linux-kernel, linux-arm-msm,
Kristian Brox, Krzysztof Kozlowski
Add a compatible for the ST ST21NFCD NFC controller. Document optional
SYS_CLK (clocks) and VPS_IO (vdd-io-supply).
ST21NFCD is not I2C-only; the ST data brief lists I2C and SPI.
Split the spi-max-frequency if/else: forbid it for
st,st21nfcb-i2c and st,st21nfcc-i2c, require it for st,st21nfcb-spi,
and leave it optional for st,st21nfcd. Add clocks and vdd-io-supply to
the existing st,st21nfcb-i2c example instead of a second I2C example.
Signed-off-by: Kristian Brox <isyourbrainfoss@proton.me>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
.../devicetree/bindings/net/nfc/st,st-nci.yaml | 49 +++++++++++++++-------
1 file changed, 35 insertions(+), 14 deletions(-)
diff --git a/Documentation/devicetree/bindings/net/nfc/st,st-nci.yaml b/Documentation/devicetree/bindings/net/nfc/st,st-nci.yaml
index 1dcbddbc5..8abafb3d8 100644
--- a/Documentation/devicetree/bindings/net/nfc/st,st-nci.yaml
+++ b/Documentation/devicetree/bindings/net/nfc/st,st-nci.yaml
@@ -15,6 +15,7 @@ properties:
- st,st21nfcb-i2c
- st,st21nfcb-spi
- st,st21nfcc-i2c
+ - st,st21nfcd
reset-gpios:
description: Output GPIO pin used for resetting the controller
@@ -36,26 +37,43 @@ properties:
Specifies that the uicc swp signal can be physically connected to the
controller
+ clocks:
+ maxItems: 1
+ description:
+ External reference clock connected to SYS_CLK.
+
+ vdd-io-supply:
+ description:
+ Digital I/O supply (VPS_IO).
+
required:
- compatible
- interrupts
- reg
- reset-gpios
-if:
- properties:
- compatible:
- contains:
- enum:
- - st,st21nfcb-i2c
- - st,st21nfcc-i2c
-then:
- properties:
- spi-max-frequency: false
-else:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
- required:
- - spi-max-frequency
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - st,st21nfcb-i2c
+ - st,st21nfcc-i2c
+ then:
+ properties:
+ spi-max-frequency: false
+ else:
+ $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: st,st21nfcb-spi
+ then:
+ required:
+ - spi-max-frequency
unevaluatedProperties: false
@@ -76,6 +94,9 @@ examples:
interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
reset-gpios = <&gpio5 29 GPIO_ACTIVE_HIGH>;
+ clocks = <&clk>;
+ vdd-io-supply = <&vdd_io>;
+
ese-present;
uicc-present;
};
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v5 2/4] nfc: st-nci: add raw NCI path for ST21NFCD
2026-10-05 20:51 [PATCH v5 0/4] nfc: st-nci: Fairphone 5 NFC bring-up (ST21NFCD) Kristian Brox
2026-10-05 20:51 ` [PATCH v5 1/4] dt-bindings: net: nfc: add st,st21nfcd Kristian Brox
@ 2026-10-05 20:51 ` Kristian Brox
2026-10-05 20:51 ` [PATCH v5 3/4] nfc: st-nci: map proprietary RF protocol 0x90 to MIFARE Kristian Brox
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Kristian Brox @ 2026-10-05 20:51 UTC (permalink / raw)
To: David Heidelberg, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, Konrad Dybcio
Cc: Krzysztof Kozlowski, Konrad Dybcio, Luca Weiss, Dmitry Baryshkov,
oe-linux-nfc, netdev, devicetree, linux-kernel, linux-arm-msm,
Kristian Brox
ST21NFCD does not use NDLC. When the compatible is st,st21nfcd,
talk raw NCI:
- do not add or strip an NDLC PCB
- do not run the T1/T2 ACK timers
- I2C reads are a 3-byte NCI header plus payload
- skip proprietary SET_NFC_MODE and HCI SE discovery
- consume proprietary RF NTF 0xf02 (GID 0xf, OID 0x02)
Set the raw_nci flag before nci_register_device so ndlc_send does
not push an NDLC PCB into a 0-headroom skb. Enable optional vdd-io
(VPS_IO) and SYS_CLK before driving reset.
Reset is active-low. Existing boards describe it as GPIO_ACTIVE_HIGH
and the driver treats gpiod_set_value(reset, 1) as chip-running.
Keep that path so old DTS is unchanged. If the GPIO is active-low,
pulse logical 1 then 0 (assert, then deassert).
Existing st21nfcb / st21nfcc boards keep the NDLC path and do not
need those properties.
Tested on Fairphone 5: adapter powers up and reads an NTAG 215.
Signed-off-by: Kristian Brox <isyourbrainfoss@proton.me>
---
drivers/nfc/st-nci/core.c | 22 ++++++++++++
drivers/nfc/st-nci/i2c.c | 82 ++++++++++++++++++++++++++++++++++++++++++---
drivers/nfc/st-nci/ndlc.c | 27 +++++++++++----
drivers/nfc/st-nci/ndlc.h | 5 ++-
drivers/nfc/st-nci/se.c | 3 ++
drivers/nfc/st-nci/spi.c | 2 +-
drivers/nfc/st-nci/st-nci.h | 2 ++
7 files changed, 129 insertions(+), 14 deletions(-)
diff --git a/drivers/nfc/st-nci/core.c b/drivers/nfc/st-nci/core.c
index a367136d4..d7802434a 100644
--- a/drivers/nfc/st-nci/core.c
+++ b/drivers/nfc/st-nci/core.c
@@ -18,8 +18,13 @@
static int st_nci_init(struct nci_dev *ndev)
{
+ struct st_nci_info *info = nci_get_drvdata(ndev);
struct nci_mode_set_cmd cmd;
+ /* ST21NFCD has no NDLC proprietary SET_NFC_MODE */
+ if (info->ndlc->raw_nci)
+ return 0;
+
cmd.cmd_type = ST_NCI_SET_NFC_MODE;
cmd.mode = 1;
@@ -84,12 +89,29 @@ static int st_nci_prop_rsp_packet(struct nci_dev *ndev,
return 0;
}
+/*
+ * ST21NFCD emits proprietary NCI NTFs (GID 0xf, OID 0x02) on CORE_RESET
+ * and during each RF poll loop. The payload is an RF trace; tags are
+ * still reported with the standard RF_INTF_ACTIVATED_NTF. Consume the
+ * packet so nci_ntf_packet does not log "unsupported ntf opcode 0xf02".
+ */
+static int st_nci_prop_rf_ntf_packet(struct nci_dev *ndev,
+ struct sk_buff *skb)
+{
+ return 0;
+}
+
static const struct nci_driver_ops st_nci_prop_ops[] = {
{
.opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
ST_NCI_CORE_PROP),
.rsp = st_nci_prop_rsp_packet,
},
+ {
+ .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
+ ST_NCI_PROP_RF_NTF),
+ .ntf = st_nci_prop_rf_ntf_packet,
+ },
};
static const struct nci_ops st_nci_ops = {
diff --git a/drivers/nfc/st-nci/i2c.c b/drivers/nfc/st-nci/i2c.c
index 152c20b6b..2ec029f31 100644
--- a/drivers/nfc/st-nci/i2c.c
+++ b/drivers/nfc/st-nci/i2c.c
@@ -10,10 +10,13 @@
#include <linux/i2c.h>
#include <linux/gpio/consumer.h>
#include <linux/acpi.h>
+#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/nfc.h>
#include <linux/of.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
#include "st-nci.h"
@@ -22,10 +25,17 @@
/* ndlc header */
#define ST_NCI_FRAME_HEADROOM 1
#define ST_NCI_FRAME_TAILROOM 0
+#define ST_NCI_RAW_FRAME_HEADROOM 0
#define ST_NCI_I2C_MIN_SIZE 4 /* PCB(1) + NCI Packet header(3) */
+#define ST_NCI_NCI_HDR_SIZE 3 /* raw NCI: MT/PBF/GID + OID + len */
#define ST_NCI_I2C_MAX_SIZE 250 /* req 4.2.1 */
+enum st_nci_i2c_proto {
+ ST_NCI_I2C_PROTO_NDLC = 0,
+ ST_NCI_I2C_PROTO_RAW_NCI,
+};
+
#define ST_NCI_DRIVER_NAME "st_nci"
#define ST_NCI_I2C_DRIVER_NAME "st_nci_i2c"
@@ -34,6 +44,7 @@ struct st_nci_i2c_phy {
struct llt_ndlc *ndlc;
bool irq_active;
+ bool raw_nci;
struct gpio_desc *gpiod_reset;
@@ -44,9 +55,20 @@ static int st_nci_i2c_enable(void *phy_id)
{
struct st_nci_i2c_phy *phy = phy_id;
- gpiod_set_value(phy->gpiod_reset, 0);
- usleep_range(10000, 15000);
- gpiod_set_value(phy->gpiod_reset, 1);
+ /*
+ * Existing DTS uses GPIO_ACTIVE_HIGH and treats logical 1 as
+ * chip-running. GPIO_ACTIVE_LOW: logical 1 asserts reset
+ * (physical LOW).
+ */
+ if (gpiod_is_active_low(phy->gpiod_reset)) {
+ gpiod_set_value(phy->gpiod_reset, 1);
+ usleep_range(10000, 15000);
+ gpiod_set_value(phy->gpiod_reset, 0);
+ } else {
+ gpiod_set_value(phy->gpiod_reset, 0);
+ usleep_range(10000, 15000);
+ gpiod_set_value(phy->gpiod_reset, 1);
+ }
usleep_range(80000, 85000);
if (phy->ndlc->powered == 0 && phy->irq_active == 0) {
@@ -111,6 +133,42 @@ static int st_nci_i2c_read(struct st_nci_i2c_phy *phy,
u8 buf[ST_NCI_I2C_MAX_SIZE];
struct i2c_client *client = phy->i2c_dev;
+ if (phy->raw_nci) {
+ r = i2c_master_recv(client, buf, ST_NCI_NCI_HDR_SIZE);
+ if (r < 0) {
+ usleep_range(1000, 4000);
+ r = i2c_master_recv(client, buf, ST_NCI_NCI_HDR_SIZE);
+ }
+ if (r != ST_NCI_NCI_HDR_SIZE)
+ return -EREMOTEIO;
+
+ len = buf[2];
+ if (len > ST_NCI_I2C_MAX_SIZE) {
+ nfc_err(&client->dev, "invalid frame len\n");
+ return -EBADMSG;
+ }
+
+ *skb = alloc_skb(ST_NCI_NCI_HDR_SIZE + len, GFP_KERNEL);
+ if (!*skb)
+ return -ENOMEM;
+
+ skb_put(*skb, ST_NCI_NCI_HDR_SIZE);
+ memcpy((*skb)->data, buf, ST_NCI_NCI_HDR_SIZE);
+
+ if (!len)
+ return 0;
+
+ r = i2c_master_recv(client, buf, len);
+ if (r != len) {
+ kfree_skb(*skb);
+ return -EREMOTEIO;
+ }
+
+ skb_put(*skb, len);
+ memcpy((*skb)->data + ST_NCI_NCI_HDR_SIZE, buf, len);
+ return 0;
+ }
+
r = i2c_master_recv(client, buf, ST_NCI_I2C_MIN_SIZE);
if (r < 0) { /* Retry, chip was in standby */
usleep_range(1000, 4000);
@@ -211,6 +269,8 @@ static int st_nci_i2c_probe(struct i2c_client *client)
return -ENOMEM;
phy->i2c_dev = client;
+ phy->raw_nci = (uintptr_t)device_get_match_data(dev) ==
+ ST_NCI_I2C_PROTO_RAW_NCI;
i2c_set_clientdata(client, phy);
@@ -218,6 +278,14 @@ static int st_nci_i2c_probe(struct i2c_client *client)
if (r)
dev_dbg(dev, "Unable to add GPIO mapping table\n");
+ r = devm_regulator_get_enable_optional(dev, "vdd-io");
+ if (r && r != -ENODEV)
+ return dev_err_probe(dev, r, "failed to enable vdd-io\n");
+
+ r = PTR_ERR_OR_ZERO(devm_clk_get_optional_enabled(dev, NULL));
+ if (r)
+ return dev_err_probe(dev, r, "failed to enable clock\n");
+
/* Get RESET GPIO */
phy->gpiod_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(phy->gpiod_reset)) {
@@ -231,8 +299,10 @@ static int st_nci_i2c_probe(struct i2c_client *client)
device_property_read_bool(dev, "uicc-present");
r = ndlc_probe(phy, &i2c_phy_ops, &client->dev,
- ST_NCI_FRAME_HEADROOM, ST_NCI_FRAME_TAILROOM,
- &phy->ndlc, &phy->se_status);
+ phy->raw_nci ? ST_NCI_RAW_FRAME_HEADROOM :
+ ST_NCI_FRAME_HEADROOM,
+ ST_NCI_FRAME_TAILROOM,
+ &phy->ndlc, &phy->se_status, phy->raw_nci);
if (r < 0) {
nfc_err(&client->dev, "Unable to register ndlc layer\n");
return r;
@@ -273,6 +343,8 @@ static const struct of_device_id of_st_nci_i2c_match[] = {
{ .compatible = "st,st21nfcb-i2c" },
{ .compatible = "st,st21nfcb_i2c" },
{ .compatible = "st,st21nfcc-i2c" },
+ { .compatible = "st,st21nfcd",
+ .data = (void *)ST_NCI_I2C_PROTO_RAW_NCI },
{ }
};
MODULE_DEVICE_TABLE(of, of_st_nci_i2c_match);
diff --git a/drivers/nfc/st-nci/ndlc.c b/drivers/nfc/st-nci/ndlc.c
index be4808859..13026c4fa 100644
--- a/drivers/nfc/st-nci/ndlc.c
+++ b/drivers/nfc/st-nci/ndlc.c
@@ -62,8 +62,9 @@ void ndlc_close(struct llt_ndlc *ndlc)
/* toggle reset pin */
ndlc->ops->enable(ndlc->phy_id);
- nci_prop_cmd(ndlc->ndev, ST_NCI_CORE_PROP,
- sizeof(struct nci_mode_set_cmd), (__u8 *)&cmd);
+ if (!ndlc->raw_nci)
+ nci_prop_cmd(ndlc->ndev, ST_NCI_CORE_PROP,
+ sizeof(struct nci_mode_set_cmd), (__u8 *)&cmd);
ndlc->powered = 0;
ndlc->ops->disable(ndlc->phy_id);
@@ -72,11 +73,13 @@ EXPORT_SYMBOL(ndlc_close);
int ndlc_send(struct llt_ndlc *ndlc, struct sk_buff *skb)
{
- /* add ndlc header */
- u8 pcb = PCB_TYPE_DATAFRAME | PCB_DATAFRAME_RETRANSMIT_NO |
- PCB_FRAME_CRC_INFO_NOTPRESENT;
+ if (!ndlc->raw_nci) {
+ /* add ndlc header */
+ u8 pcb = PCB_TYPE_DATAFRAME | PCB_DATAFRAME_RETRANSMIT_NO |
+ PCB_FRAME_CRC_INFO_NOTPRESENT;
- *(u8 *)skb_push(skb, 1) = pcb;
+ *(u8 *)skb_push(skb, 1) = pcb;
+ }
skb_queue_tail(&ndlc->send_q, skb);
schedule_work(&ndlc->sm_work);
@@ -103,6 +106,10 @@ static void llt_ndlc_send_queue(struct llt_ndlc *ndlc)
ndlc->hard_fault = r;
break;
}
+ if (ndlc->raw_nci) {
+ kfree_skb(skb);
+ continue;
+ }
time_sent = jiffies;
*(unsigned long *)skb->cb = time_sent;
@@ -154,6 +161,10 @@ static void llt_ndlc_rcv_queue(struct llt_ndlc *ndlc)
pr_debug("rcvQlen=%d\n", ndlc->rcv_q.qlen);
while ((skb = skb_dequeue(&ndlc->rcv_q)) != NULL) {
+ if (ndlc->raw_nci) {
+ nci_recv_frame(ndlc->ndev, skb);
+ continue;
+ }
pcb = skb->data[0];
skb_pull(skb, 1);
if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_SUPERVISOR) {
@@ -251,7 +262,8 @@ static void ndlc_t2_timeout(struct timer_list *t)
int ndlc_probe(void *phy_id, const struct nfc_phy_ops *phy_ops,
struct device *dev, int phy_headroom, int phy_tailroom,
- struct llt_ndlc **ndlc_id, struct st_nci_se_status *se_status)
+ struct llt_ndlc **ndlc_id, struct st_nci_se_status *se_status,
+ bool raw_nci)
{
struct llt_ndlc *ndlc;
@@ -263,6 +275,7 @@ int ndlc_probe(void *phy_id, const struct nfc_phy_ops *phy_ops,
ndlc->phy_id = phy_id;
ndlc->dev = dev;
ndlc->powered = 0;
+ ndlc->raw_nci = raw_nci;
*ndlc_id = ndlc;
diff --git a/drivers/nfc/st-nci/ndlc.h b/drivers/nfc/st-nci/ndlc.h
index c24ce9b0d..0b12e12e4 100644
--- a/drivers/nfc/st-nci/ndlc.h
+++ b/drivers/nfc/st-nci/ndlc.h
@@ -39,6 +39,8 @@ struct llt_ndlc {
*/
int hard_fault;
int powered;
+ /* ST21NFCD: raw NCI on the wire, no NDLC PCB / ACK timers */
+ bool raw_nci;
};
int ndlc_open(struct llt_ndlc *ndlc);
@@ -47,6 +49,7 @@ int ndlc_send(struct llt_ndlc *ndlc, struct sk_buff *skb);
void ndlc_recv(struct llt_ndlc *ndlc, struct sk_buff *skb);
int ndlc_probe(void *phy_id, const struct nfc_phy_ops *phy_ops,
struct device *dev, int phy_headroom, int phy_tailroom,
- struct llt_ndlc **ndlc_id, struct st_nci_se_status *se_status);
+ struct llt_ndlc **ndlc_id, struct st_nci_se_status *se_status,
+ bool raw_nci);
void ndlc_remove(struct llt_ndlc *ndlc);
#endif /* __LOCAL_NDLC_H__ */
diff --git a/drivers/nfc/st-nci/se.c b/drivers/nfc/st-nci/se.c
index 607ec768e..44cc102bc 100644
--- a/drivers/nfc/st-nci/se.c
+++ b/drivers/nfc/st-nci/se.c
@@ -621,6 +621,9 @@ int st_nci_discover_se(struct nci_dev *ndev)
int se_count = 0;
struct st_nci_info *info = nci_get_drvdata(ndev);
+ if (info->ndlc->raw_nci)
+ return 0;
+
r = st_nci_hci_network_init(ndev);
if (r != 0)
return r;
diff --git a/drivers/nfc/st-nci/spi.c b/drivers/nfc/st-nci/spi.c
index 7948c7e0c..1ce80a85b 100644
--- a/drivers/nfc/st-nci/spi.c
+++ b/drivers/nfc/st-nci/spi.c
@@ -246,7 +246,7 @@ static int st_nci_spi_probe(struct spi_device *dev)
r = ndlc_probe(phy, &spi_phy_ops, &dev->dev,
ST_NCI_FRAME_HEADROOM, ST_NCI_FRAME_TAILROOM,
- &phy->ndlc, &phy->se_status);
+ &phy->ndlc, &phy->se_status, false);
if (r < 0) {
nfc_err(&dev->dev, "Unable to register ndlc layer\n");
return r;
diff --git a/drivers/nfc/st-nci/st-nci.h b/drivers/nfc/st-nci/st-nci.h
index 5286071e5..aa8782c95 100644
--- a/drivers/nfc/st-nci/st-nci.h
+++ b/drivers/nfc/st-nci/st-nci.h
@@ -15,6 +15,8 @@
#define ST_NCI_CORE_PROP 0x01
#define ST_NCI_SET_NFC_MODE 0x02
+/* ST21NFCD proprietary RF activity / poll-trace notification (GID 0xf) */
+#define ST_NCI_PROP_RF_NTF 0x02
/*
* ref ISO7816-3 chap 8.1. the initial character TS is followed by a
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v5 3/4] nfc: st-nci: map proprietary RF protocol 0x90 to MIFARE
2026-10-05 20:51 [PATCH v5 0/4] nfc: st-nci: Fairphone 5 NFC bring-up (ST21NFCD) Kristian Brox
2026-10-05 20:51 ` [PATCH v5 1/4] dt-bindings: net: nfc: add st,st21nfcd Kristian Brox
2026-10-05 20:51 ` [PATCH v5 2/4] nfc: st-nci: add raw NCI path for ST21NFCD Kristian Brox
@ 2026-10-05 20:51 ` Kristian Brox
2026-10-05 20:52 ` [PATCH v5 4/4] arm64: dts: qcom: qcm6490-fairphone-fp5: add ST21NFCD NFC Kristian Brox
2026-10-12 18:47 ` [PATCH v5 0/4] nfc: st-nci: Fairphone 5 NFC bring-up (ST21NFCD) Val Packett
4 siblings, 0 replies; 6+ messages in thread
From: Kristian Brox @ 2026-10-05 20:51 UTC (permalink / raw)
To: David Heidelberg, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, Konrad Dybcio
Cc: Krzysztof Kozlowski, Konrad Dybcio, Luca Weiss, Dmitry Baryshkov,
oe-linux-nfc, netdev, devicetree, linux-kernel, linux-arm-msm,
Kristian Brox, Tim auf der Landwehr
From: Tim auf der Landwehr <tadl-git@taufderl.de>
ST21NFCD reports MIFARE Classic with proprietary RF protocol 0x90
(a Classic 4K, and a Classic 1K on the Fairphone 5). That value is
not an NFC Forum protocol. st_nci_get_rfprotocol() returned 0 for it,
and nci_add_new_protocol() then drops the tag.
Map 0x90 to NFC_PROTO_MIFARE_MASK. ISO15693 (0x83) is unchanged.
Signed-off-by: Tim auf der Landwehr <tadl-git@taufderl.de>
Signed-off-by: Kristian Brox <isyourbrainfoss@proton.me>
---
drivers/nfc/st-nci/core.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/drivers/nfc/st-nci/core.c b/drivers/nfc/st-nci/core.c
index d7802434a..d83221566 100644
--- a/drivers/nfc/st-nci/core.c
+++ b/drivers/nfc/st-nci/core.c
@@ -15,6 +15,12 @@
#define DRIVER_DESC "NCI NFC driver for ST_NCI"
#define ST_NCI1_X_PROPRIETARY_ISO15693 0x83
+/*
+ * Proprietary RF protocol the controller reports for MIFARE Classic.
+ * Not an NFC Forum protocol, so it must be mapped here or
+ * nci_add_new_protocol drops the tag.
+ */
+#define ST_NCI1_X_PROPRIETARY_MIFARE 0x90
static int st_nci_init(struct nci_dev *ndev)
{
@@ -76,8 +82,14 @@ static int st_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
static __u32 st_nci_get_rfprotocol(struct nci_dev *ndev,
__u8 rf_protocol)
{
- return rf_protocol == ST_NCI1_X_PROPRIETARY_ISO15693 ?
- NFC_PROTO_ISO15693_MASK : 0;
+ switch (rf_protocol) {
+ case ST_NCI1_X_PROPRIETARY_ISO15693:
+ return NFC_PROTO_ISO15693_MASK;
+ case ST_NCI1_X_PROPRIETARY_MIFARE:
+ return NFC_PROTO_MIFARE_MASK;
+ default:
+ return 0;
+ }
}
static int st_nci_prop_rsp_packet(struct nci_dev *ndev,
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v5 4/4] arm64: dts: qcom: qcm6490-fairphone-fp5: add ST21NFCD NFC
2026-10-05 20:51 [PATCH v5 0/4] nfc: st-nci: Fairphone 5 NFC bring-up (ST21NFCD) Kristian Brox
` (2 preceding siblings ...)
2026-10-05 20:51 ` [PATCH v5 3/4] nfc: st-nci: map proprietary RF protocol 0x90 to MIFARE Kristian Brox
@ 2026-10-05 20:52 ` Kristian Brox
2026-10-12 18:47 ` [PATCH v5 0/4] nfc: st-nci: Fairphone 5 NFC bring-up (ST21NFCD) Val Packett
4 siblings, 0 replies; 6+ messages in thread
From: Kristian Brox @ 2026-10-05 20:52 UTC (permalink / raw)
To: David Heidelberg, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, Konrad Dybcio
Cc: Krzysztof Kozlowski, Konrad Dybcio, Luca Weiss, Dmitry Baryshkov,
oe-linux-nfc, netdev, devicetree, linux-kernel, linux-arm-msm,
Kristian Brox, Abel Vesa
Enable the ST21NFCD on i2c9 (0x08), IRQ TLMM 41, reset TLMM 38
active-low. Compatible is st,st21nfcd (raw NCI).
SYS_CLK is LN_BB_CLK2. VPS_IO is L18B (vreg_l18b). ese-present and
uicc-present follow the public schematic (NFC_SWP1/SWP2: SWP_SE to
SIM1, SWP_UICC to SIM2). Reader path is tested; SE/HCE is not.
Signed-off-by: Kristian Brox <isyourbrainfoss@proton.me>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Reviewed-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts | 34 +++++++++++++++++++++-
1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts b/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts
index f41ad446a..b028d1bb2 100644
--- a/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts
+++ b/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts
@@ -933,7 +933,23 @@ redriver_ss_in: endpoint {
&i2c9 {
status = "okay";
- /* ST21NFC NFC @ 28 */
+ nfc@8 {
+ compatible = "st,st21nfcd";
+ reg = <0x08>;
+
+ interrupts-extended = <&tlmm 41 IRQ_TYPE_LEVEL_HIGH>;
+ reset-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>;
+
+ pinctrl-0 = <&nfc_default>;
+ pinctrl-names = "default";
+
+ clocks = <&rpmhcc RPMH_LN_BB_CLK2>;
+ vdd-io-supply = <&vreg_l18b>;
+
+ ese-present;
+ uicc-present;
+ };
+
/* VL53L3 ToF @ 29 */
};
@@ -1462,6 +1478,22 @@ hall_sensor_default: hall-sensor-default-state {
drive-strength = <2>;
bias-pull-up;
};
+
+ nfc_default: nfc-default-state {
+ reset-pins {
+ pins = "gpio38";
+ function = "gpio";
+ drive-strength = <2>;
+ bias-disable;
+ };
+
+ int-pins {
+ pins = "gpio41";
+ function = "gpio";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
};
&uart5 {
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v5 0/4] nfc: st-nci: Fairphone 5 NFC bring-up (ST21NFCD)
2026-10-05 20:51 [PATCH v5 0/4] nfc: st-nci: Fairphone 5 NFC bring-up (ST21NFCD) Kristian Brox
` (3 preceding siblings ...)
2026-10-05 20:52 ` [PATCH v5 4/4] arm64: dts: qcom: qcm6490-fairphone-fp5: add ST21NFCD NFC Kristian Brox
@ 2026-10-12 18:47 ` Val Packett
4 siblings, 0 replies; 6+ messages in thread
From: Val Packett @ 2026-10-12 18:47 UTC (permalink / raw)
To: Kristian Brox, David Heidelberg, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
Konrad Dybcio
Cc: Krzysztof Kozlowski, Konrad Dybcio, Luca Weiss, Dmitry Baryshkov,
oe-linux-nfc, netdev, devicetree, linux-kernel, linux-arm-msm,
Krzysztof Kozlowski, Tim auf der Landwehr, Abel Vesa
On 10/5/26 5:51 PM, Kristian Brox wrote:
> This adds NFC on the Fairphone 5 (qcm6490). The board uses an ST21NFCD
> on I2C. That part speaks raw NCI; the current st-nci driver always
> wraps NDLC, so using st,st21nfcb-i2c leaves the adapter unusable.
>
> The series adds a st,st21nfcd compatible for the raw-NCI path and the
> Fairphone 5 DT node. Boards that already use st21nfcb / st21nfcc keep
> the NDLC path.
>
> A later poll start that times out in "failed to set local general bytes"
> is still unreproduced on this phone.
>
> Signed-off-by: Kristian Brox <isyourbrainfoss@proton.me>
> ---
> Link to v4:
> https://lore.kernel.org/oe-linux-nfc/20260902-fp5-st21nfcd-v4-v4-0-ded2f1c501be@proton.me/
>
> Changes in v5:
> - Map proprietary RF protocol 0x90 to MIFARE (Tim). On this phone a
> Classic 1K was accepted as 0x90 and was not dropped.
> - Binding carries Reviewed-by from Krzysztof Kozlowski. DTS carries
> Reviewed-by from Konrad Dybcio and Abel Vesa. The patch bodies are
> the v4 patches. The raw-NCI driver change is unchanged.
> - Tim's reference-clock restart and leading-0x7e check were tried on
> this phone and left out.
Hi, I've been trying to enable the st21nfcj on the Motorola Edge 30,
which uses the same protocol.
These mentioned left-out patches seemed like they would help, but they
didn't really. The leading-0x7e check does skip over the "7e 60 00" but
is not enough to untangle the desynced mess that starts with "79 3e 59 c0"..
sendQlen=1 unackQlen=0
ndlc frame written:
ndlc: 00000000: 20 00 01 01
incoming frame:
ndlc: 00000000: 7e 60 00
incoming frame:
ndlc: 00000000: 1f 01 01 20
rcvQlen=1
incoming frame:
ndlc: 00000000: 02 1a 04 04 01 20 92
rcvQlen=1
incoming frame:
ndlc: 00000000: 80 02 03 01 59 c0
rcvQlen=1
nci: nci_rx_work: unknown MT 0x4
incoming frame:
ndlc: 00000000: 79 3e 59 c0 47 75 00 00 00 01 00 00 61 66 02 7e
ndlc: 00000010: 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 40
ndlc: 00000020: 00 01 00 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e
ndlc: 00000030: 60 00 1f 02 01 20 02 1a 04 04 01 20 92 80 02 03
ndlc: 00000040: 01 59 c0 79 3e 59 c0 47 75 00 00 00 01 00 00 61
ndlc: 00000050: 66 02 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e
rcvQlen=1
nci: nci_ntf_packet: unknown ntf opcode 0x93e
nci: __nci_request: wait_for_completion_interruptible_timeout failed 0
I've tried changing things like interrupt trigger (with rising edge
only 1f 01 01 20 arrives, with falling edge nothing) but nothing seems
to help. I wonder if anyone has any idea of what might be the actual
cause of those strange frames. It's interesting that these two
workaround patches were found necessary on one phone but not on another..
Thanks,
~val
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-12 18:47 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-05 20:51 [PATCH v5 0/4] nfc: st-nci: Fairphone 5 NFC bring-up (ST21NFCD) Kristian Brox
2026-10-05 20:51 ` [PATCH v5 1/4] dt-bindings: net: nfc: add st,st21nfcd Kristian Brox
2026-10-05 20:51 ` [PATCH v5 2/4] nfc: st-nci: add raw NCI path for ST21NFCD Kristian Brox
2026-10-05 20:51 ` [PATCH v5 3/4] nfc: st-nci: map proprietary RF protocol 0x90 to MIFARE Kristian Brox
2026-10-05 20:52 ` [PATCH v5 4/4] arm64: dts: qcom: qcm6490-fairphone-fp5: add ST21NFCD NFC Kristian Brox
2026-10-12 18:47 ` [PATCH v5 0/4] nfc: st-nci: Fairphone 5 NFC bring-up (ST21NFCD) Val Packett
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®