xref: /kernel/linux/linux-6.6/drivers/nfc/st-nci/spi.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * SPI Link Layer for ST NCI based Driver
462306a36Sopenharmony_ci * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/spi/spi.h>
1162306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1262306a36Sopenharmony_ci#include <linux/acpi.h>
1362306a36Sopenharmony_ci#include <linux/interrupt.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/nfc.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci#include <net/nfc/nci.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "st-nci.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define DRIVER_DESC "NCI NFC driver for ST_NCI"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* ndlc header */
2462306a36Sopenharmony_ci#define ST_NCI_FRAME_HEADROOM	1
2562306a36Sopenharmony_ci#define ST_NCI_FRAME_TAILROOM	0
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define ST_NCI_SPI_MIN_SIZE 4   /* PCB(1) + NCI Packet header(3) */
2862306a36Sopenharmony_ci#define ST_NCI_SPI_MAX_SIZE 250 /* req 4.2.1 */
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define ST_NCI_DRIVER_NAME "st_nci"
3162306a36Sopenharmony_ci#define ST_NCI_SPI_DRIVER_NAME "st_nci_spi"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistruct st_nci_spi_phy {
3462306a36Sopenharmony_ci	struct spi_device *spi_dev;
3562306a36Sopenharmony_ci	struct llt_ndlc *ndlc;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	bool irq_active;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	struct gpio_desc *gpiod_reset;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	struct st_nci_se_status se_status;
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int st_nci_spi_enable(void *phy_id)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	struct st_nci_spi_phy *phy = phy_id;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	gpiod_set_value(phy->gpiod_reset, 0);
4962306a36Sopenharmony_ci	usleep_range(10000, 15000);
5062306a36Sopenharmony_ci	gpiod_set_value(phy->gpiod_reset, 1);
5162306a36Sopenharmony_ci	usleep_range(80000, 85000);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (phy->ndlc->powered == 0 && phy->irq_active == 0) {
5462306a36Sopenharmony_ci		enable_irq(phy->spi_dev->irq);
5562306a36Sopenharmony_ci		phy->irq_active = true;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return 0;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic void st_nci_spi_disable(void *phy_id)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct st_nci_spi_phy *phy = phy_id;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	disable_irq_nosync(phy->spi_dev->irq);
6662306a36Sopenharmony_ci	phy->irq_active = false;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * Writing a frame must not return the number of written bytes.
7162306a36Sopenharmony_ci * It must return either zero for success, or <0 for error.
7262306a36Sopenharmony_ci * In addition, it must not alter the skb
7362306a36Sopenharmony_ci */
7462306a36Sopenharmony_cistatic int st_nci_spi_write(void *phy_id, struct sk_buff *skb)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	int r;
7762306a36Sopenharmony_ci	struct st_nci_spi_phy *phy = phy_id;
7862306a36Sopenharmony_ci	struct spi_device *dev = phy->spi_dev;
7962306a36Sopenharmony_ci	struct sk_buff *skb_rx;
8062306a36Sopenharmony_ci	u8 buf[ST_NCI_SPI_MAX_SIZE + NCI_DATA_HDR_SIZE +
8162306a36Sopenharmony_ci	       ST_NCI_FRAME_HEADROOM + ST_NCI_FRAME_TAILROOM];
8262306a36Sopenharmony_ci	struct spi_transfer spi_xfer = {
8362306a36Sopenharmony_ci		.tx_buf = skb->data,
8462306a36Sopenharmony_ci		.rx_buf = buf,
8562306a36Sopenharmony_ci		.len = skb->len,
8662306a36Sopenharmony_ci	};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (phy->ndlc->hard_fault != 0)
8962306a36Sopenharmony_ci		return phy->ndlc->hard_fault;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	r = spi_sync_transfer(dev, &spi_xfer, 1);
9262306a36Sopenharmony_ci	/*
9362306a36Sopenharmony_ci	 * We may have received some valuable data on miso line.
9462306a36Sopenharmony_ci	 * Send them back in the ndlc state machine.
9562306a36Sopenharmony_ci	 */
9662306a36Sopenharmony_ci	if (!r) {
9762306a36Sopenharmony_ci		skb_rx = alloc_skb(skb->len, GFP_KERNEL);
9862306a36Sopenharmony_ci		if (!skb_rx)
9962306a36Sopenharmony_ci			return -ENOMEM;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		skb_put(skb_rx, skb->len);
10262306a36Sopenharmony_ci		memcpy(skb_rx->data, buf, skb->len);
10362306a36Sopenharmony_ci		ndlc_recv(phy->ndlc, skb_rx);
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return r;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/*
11062306a36Sopenharmony_ci * Reads an ndlc frame and returns it in a newly allocated sk_buff.
11162306a36Sopenharmony_ci * returns:
11262306a36Sopenharmony_ci * 0 : if received frame is complete
11362306a36Sopenharmony_ci * -EREMOTEIO : i2c read error (fatal)
11462306a36Sopenharmony_ci * -EBADMSG : frame was incorrect and discarded
11562306a36Sopenharmony_ci * -ENOMEM : cannot allocate skb, frame dropped
11662306a36Sopenharmony_ci */
11762306a36Sopenharmony_cistatic int st_nci_spi_read(struct st_nci_spi_phy *phy,
11862306a36Sopenharmony_ci			struct sk_buff **skb)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	int r;
12162306a36Sopenharmony_ci	u8 len;
12262306a36Sopenharmony_ci	u8 buf[ST_NCI_SPI_MAX_SIZE];
12362306a36Sopenharmony_ci	struct spi_device *dev = phy->spi_dev;
12462306a36Sopenharmony_ci	struct spi_transfer spi_xfer = {
12562306a36Sopenharmony_ci		.rx_buf = buf,
12662306a36Sopenharmony_ci		.len = ST_NCI_SPI_MIN_SIZE,
12762306a36Sopenharmony_ci	};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	r = spi_sync_transfer(dev, &spi_xfer, 1);
13062306a36Sopenharmony_ci	if (r < 0)
13162306a36Sopenharmony_ci		return -EREMOTEIO;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	len = be16_to_cpu(*(__be16 *) (buf + 2));
13462306a36Sopenharmony_ci	if (len > ST_NCI_SPI_MAX_SIZE) {
13562306a36Sopenharmony_ci		nfc_err(&dev->dev, "invalid frame len\n");
13662306a36Sopenharmony_ci		phy->ndlc->hard_fault = 1;
13762306a36Sopenharmony_ci		return -EBADMSG;
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	*skb = alloc_skb(ST_NCI_SPI_MIN_SIZE + len, GFP_KERNEL);
14162306a36Sopenharmony_ci	if (*skb == NULL)
14262306a36Sopenharmony_ci		return -ENOMEM;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	skb_reserve(*skb, ST_NCI_SPI_MIN_SIZE);
14562306a36Sopenharmony_ci	skb_put(*skb, ST_NCI_SPI_MIN_SIZE);
14662306a36Sopenharmony_ci	memcpy((*skb)->data, buf, ST_NCI_SPI_MIN_SIZE);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (!len)
14962306a36Sopenharmony_ci		return 0;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	spi_xfer.len = len;
15262306a36Sopenharmony_ci	r = spi_sync_transfer(dev, &spi_xfer, 1);
15362306a36Sopenharmony_ci	if (r < 0) {
15462306a36Sopenharmony_ci		kfree_skb(*skb);
15562306a36Sopenharmony_ci		return -EREMOTEIO;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	skb_put(*skb, len);
15962306a36Sopenharmony_ci	memcpy((*skb)->data + ST_NCI_SPI_MIN_SIZE, buf, len);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return 0;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci/*
16562306a36Sopenharmony_ci * Reads an ndlc frame from the chip.
16662306a36Sopenharmony_ci *
16762306a36Sopenharmony_ci * On ST21NFCB, IRQ goes in idle state when read starts.
16862306a36Sopenharmony_ci */
16962306a36Sopenharmony_cistatic irqreturn_t st_nci_irq_thread_fn(int irq, void *phy_id)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct st_nci_spi_phy *phy = phy_id;
17262306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
17362306a36Sopenharmony_ci	int r;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (!phy || !phy->ndlc || irq != phy->spi_dev->irq) {
17662306a36Sopenharmony_ci		WARN_ON_ONCE(1);
17762306a36Sopenharmony_ci		return IRQ_NONE;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (phy->ndlc->hard_fault)
18162306a36Sopenharmony_ci		return IRQ_HANDLED;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	if (!phy->ndlc->powered) {
18462306a36Sopenharmony_ci		st_nci_spi_disable(phy);
18562306a36Sopenharmony_ci		return IRQ_HANDLED;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	r = st_nci_spi_read(phy, &skb);
18962306a36Sopenharmony_ci	if (r == -EREMOTEIO || r == -ENOMEM || r == -EBADMSG)
19062306a36Sopenharmony_ci		return IRQ_HANDLED;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	ndlc_recv(phy->ndlc, skb);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return IRQ_HANDLED;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic const struct nfc_phy_ops spi_phy_ops = {
19862306a36Sopenharmony_ci	.write = st_nci_spi_write,
19962306a36Sopenharmony_ci	.enable = st_nci_spi_enable,
20062306a36Sopenharmony_ci	.disable = st_nci_spi_disable,
20162306a36Sopenharmony_ci};
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic const struct acpi_gpio_params reset_gpios = { 1, 0, false };
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic const struct acpi_gpio_mapping acpi_st_nci_gpios[] = {
20662306a36Sopenharmony_ci	{ "reset-gpios", &reset_gpios, 1 },
20762306a36Sopenharmony_ci	{},
20862306a36Sopenharmony_ci};
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic int st_nci_spi_probe(struct spi_device *dev)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct st_nci_spi_phy *phy;
21362306a36Sopenharmony_ci	int r;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* Check SPI platform functionnalities */
21662306a36Sopenharmony_ci	if (!dev) {
21762306a36Sopenharmony_ci		pr_debug("%s: dev is NULL. Device is not accessible.\n",
21862306a36Sopenharmony_ci			__func__);
21962306a36Sopenharmony_ci		return -ENODEV;
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	phy = devm_kzalloc(&dev->dev, sizeof(struct st_nci_spi_phy),
22362306a36Sopenharmony_ci			   GFP_KERNEL);
22462306a36Sopenharmony_ci	if (!phy)
22562306a36Sopenharmony_ci		return -ENOMEM;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	phy->spi_dev = dev;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	spi_set_drvdata(dev, phy);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	r = devm_acpi_dev_add_driver_gpios(&dev->dev, acpi_st_nci_gpios);
23262306a36Sopenharmony_ci	if (r)
23362306a36Sopenharmony_ci		dev_dbg(&dev->dev, "Unable to add GPIO mapping table\n");
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/* Get RESET GPIO */
23662306a36Sopenharmony_ci	phy->gpiod_reset = devm_gpiod_get(&dev->dev, "reset", GPIOD_OUT_HIGH);
23762306a36Sopenharmony_ci	if (IS_ERR(phy->gpiod_reset)) {
23862306a36Sopenharmony_ci		nfc_err(&dev->dev, "Unable to get RESET GPIO\n");
23962306a36Sopenharmony_ci		return PTR_ERR(phy->gpiod_reset);
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	phy->se_status.is_ese_present =
24362306a36Sopenharmony_ci			device_property_read_bool(&dev->dev, "ese-present");
24462306a36Sopenharmony_ci	phy->se_status.is_uicc_present =
24562306a36Sopenharmony_ci			device_property_read_bool(&dev->dev, "uicc-present");
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	r = ndlc_probe(phy, &spi_phy_ops, &dev->dev,
24862306a36Sopenharmony_ci			ST_NCI_FRAME_HEADROOM, ST_NCI_FRAME_TAILROOM,
24962306a36Sopenharmony_ci			&phy->ndlc, &phy->se_status);
25062306a36Sopenharmony_ci	if (r < 0) {
25162306a36Sopenharmony_ci		nfc_err(&dev->dev, "Unable to register ndlc layer\n");
25262306a36Sopenharmony_ci		return r;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	phy->irq_active = true;
25662306a36Sopenharmony_ci	r = devm_request_threaded_irq(&dev->dev, dev->irq, NULL,
25762306a36Sopenharmony_ci				st_nci_irq_thread_fn,
25862306a36Sopenharmony_ci				IRQF_ONESHOT,
25962306a36Sopenharmony_ci				ST_NCI_SPI_DRIVER_NAME, phy);
26062306a36Sopenharmony_ci	if (r < 0)
26162306a36Sopenharmony_ci		nfc_err(&dev->dev, "Unable to register IRQ handler\n");
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	return r;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic void st_nci_spi_remove(struct spi_device *dev)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	struct st_nci_spi_phy *phy = spi_get_drvdata(dev);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	ndlc_remove(phy->ndlc);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic struct spi_device_id st_nci_spi_id_table[] = {
27462306a36Sopenharmony_ci	{ST_NCI_SPI_DRIVER_NAME, 0},
27562306a36Sopenharmony_ci	{"st21nfcb-spi", 0},
27662306a36Sopenharmony_ci	{}
27762306a36Sopenharmony_ci};
27862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(spi, st_nci_spi_id_table);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic const struct acpi_device_id st_nci_spi_acpi_match[] __maybe_unused = {
28162306a36Sopenharmony_ci	{"SMO2101", 0},
28262306a36Sopenharmony_ci	{}
28362306a36Sopenharmony_ci};
28462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, st_nci_spi_acpi_match);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic const struct of_device_id of_st_nci_spi_match[] __maybe_unused = {
28762306a36Sopenharmony_ci	{ .compatible = "st,st21nfcb-spi", },
28862306a36Sopenharmony_ci	{}
28962306a36Sopenharmony_ci};
29062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_st_nci_spi_match);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic struct spi_driver st_nci_spi_driver = {
29362306a36Sopenharmony_ci	.driver = {
29462306a36Sopenharmony_ci		.name = ST_NCI_SPI_DRIVER_NAME,
29562306a36Sopenharmony_ci		.of_match_table = of_match_ptr(of_st_nci_spi_match),
29662306a36Sopenharmony_ci		.acpi_match_table = ACPI_PTR(st_nci_spi_acpi_match),
29762306a36Sopenharmony_ci	},
29862306a36Sopenharmony_ci	.probe = st_nci_spi_probe,
29962306a36Sopenharmony_ci	.id_table = st_nci_spi_id_table,
30062306a36Sopenharmony_ci	.remove = st_nci_spi_remove,
30162306a36Sopenharmony_ci};
30262306a36Sopenharmony_cimodule_spi_driver(st_nci_spi_driver);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
30562306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
306