mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [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®