xref: /kernel/linux/linux-5.10/drivers/nfc/st-nci/spi.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SPI Link Layer for ST NCI based Driver
48c2ecf20Sopenharmony_ci * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
118c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
128c2ecf20Sopenharmony_ci#include <linux/acpi.h>
138c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
148c2ecf20Sopenharmony_ci#include <linux/delay.h>
158c2ecf20Sopenharmony_ci#include <linux/nfc.h>
168c2ecf20Sopenharmony_ci#include <linux/of.h>
178c2ecf20Sopenharmony_ci#include <net/nfc/nci.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "st-nci.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define DRIVER_DESC "NCI NFC driver for ST_NCI"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/* ndlc header */
248c2ecf20Sopenharmony_ci#define ST_NCI_FRAME_HEADROOM	1
258c2ecf20Sopenharmony_ci#define ST_NCI_FRAME_TAILROOM	0
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define ST_NCI_SPI_MIN_SIZE 4   /* PCB(1) + NCI Packet header(3) */
288c2ecf20Sopenharmony_ci#define ST_NCI_SPI_MAX_SIZE 250 /* req 4.2.1 */
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define ST_NCI_DRIVER_NAME "st_nci"
318c2ecf20Sopenharmony_ci#define ST_NCI_SPI_DRIVER_NAME "st_nci_spi"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistruct st_nci_spi_phy {
348c2ecf20Sopenharmony_ci	struct spi_device *spi_dev;
358c2ecf20Sopenharmony_ci	struct llt_ndlc *ndlc;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	bool irq_active;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	struct gpio_desc *gpiod_reset;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	struct st_nci_se_status se_status;
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic int st_nci_spi_enable(void *phy_id)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	struct st_nci_spi_phy *phy = phy_id;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	gpiod_set_value(phy->gpiod_reset, 0);
498c2ecf20Sopenharmony_ci	usleep_range(10000, 15000);
508c2ecf20Sopenharmony_ci	gpiod_set_value(phy->gpiod_reset, 1);
518c2ecf20Sopenharmony_ci	usleep_range(80000, 85000);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	if (phy->ndlc->powered == 0 && phy->irq_active == 0) {
548c2ecf20Sopenharmony_ci		enable_irq(phy->spi_dev->irq);
558c2ecf20Sopenharmony_ci		phy->irq_active = true;
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	return 0;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic void st_nci_spi_disable(void *phy_id)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct st_nci_spi_phy *phy = phy_id;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	disable_irq_nosync(phy->spi_dev->irq);
668c2ecf20Sopenharmony_ci	phy->irq_active = false;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/*
708c2ecf20Sopenharmony_ci * Writing a frame must not return the number of written bytes.
718c2ecf20Sopenharmony_ci * It must return either zero for success, or <0 for error.
728c2ecf20Sopenharmony_ci * In addition, it must not alter the skb
738c2ecf20Sopenharmony_ci */
748c2ecf20Sopenharmony_cistatic int st_nci_spi_write(void *phy_id, struct sk_buff *skb)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	int r;
778c2ecf20Sopenharmony_ci	struct st_nci_spi_phy *phy = phy_id;
788c2ecf20Sopenharmony_ci	struct spi_device *dev = phy->spi_dev;
798c2ecf20Sopenharmony_ci	struct sk_buff *skb_rx;
808c2ecf20Sopenharmony_ci	u8 buf[ST_NCI_SPI_MAX_SIZE + NCI_DATA_HDR_SIZE +
818c2ecf20Sopenharmony_ci	       ST_NCI_FRAME_HEADROOM + ST_NCI_FRAME_TAILROOM];
828c2ecf20Sopenharmony_ci	struct spi_transfer spi_xfer = {
838c2ecf20Sopenharmony_ci		.tx_buf = skb->data,
848c2ecf20Sopenharmony_ci		.rx_buf = buf,
858c2ecf20Sopenharmony_ci		.len = skb->len,
868c2ecf20Sopenharmony_ci	};
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (phy->ndlc->hard_fault != 0)
898c2ecf20Sopenharmony_ci		return phy->ndlc->hard_fault;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	r = spi_sync_transfer(dev, &spi_xfer, 1);
928c2ecf20Sopenharmony_ci	/*
938c2ecf20Sopenharmony_ci	 * We may have received some valuable data on miso line.
948c2ecf20Sopenharmony_ci	 * Send them back in the ndlc state machine.
958c2ecf20Sopenharmony_ci	 */
968c2ecf20Sopenharmony_ci	if (!r) {
978c2ecf20Sopenharmony_ci		skb_rx = alloc_skb(skb->len, GFP_KERNEL);
988c2ecf20Sopenharmony_ci		if (!skb_rx) {
998c2ecf20Sopenharmony_ci			r = -ENOMEM;
1008c2ecf20Sopenharmony_ci			goto exit;
1018c2ecf20Sopenharmony_ci		}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci		skb_put(skb_rx, skb->len);
1048c2ecf20Sopenharmony_ci		memcpy(skb_rx->data, buf, skb->len);
1058c2ecf20Sopenharmony_ci		ndlc_recv(phy->ndlc, skb_rx);
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ciexit:
1098c2ecf20Sopenharmony_ci	return r;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/*
1138c2ecf20Sopenharmony_ci * Reads an ndlc frame and returns it in a newly allocated sk_buff.
1148c2ecf20Sopenharmony_ci * returns:
1158c2ecf20Sopenharmony_ci * 0 : if received frame is complete
1168c2ecf20Sopenharmony_ci * -EREMOTEIO : i2c read error (fatal)
1178c2ecf20Sopenharmony_ci * -EBADMSG : frame was incorrect and discarded
1188c2ecf20Sopenharmony_ci * -ENOMEM : cannot allocate skb, frame dropped
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_cistatic int st_nci_spi_read(struct st_nci_spi_phy *phy,
1218c2ecf20Sopenharmony_ci			struct sk_buff **skb)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	int r;
1248c2ecf20Sopenharmony_ci	u8 len;
1258c2ecf20Sopenharmony_ci	u8 buf[ST_NCI_SPI_MAX_SIZE];
1268c2ecf20Sopenharmony_ci	struct spi_device *dev = phy->spi_dev;
1278c2ecf20Sopenharmony_ci	struct spi_transfer spi_xfer = {
1288c2ecf20Sopenharmony_ci		.rx_buf = buf,
1298c2ecf20Sopenharmony_ci		.len = ST_NCI_SPI_MIN_SIZE,
1308c2ecf20Sopenharmony_ci	};
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	r = spi_sync_transfer(dev, &spi_xfer, 1);
1338c2ecf20Sopenharmony_ci	if (r < 0)
1348c2ecf20Sopenharmony_ci		return -EREMOTEIO;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	len = be16_to_cpu(*(__be16 *) (buf + 2));
1378c2ecf20Sopenharmony_ci	if (len > ST_NCI_SPI_MAX_SIZE) {
1388c2ecf20Sopenharmony_ci		nfc_err(&dev->dev, "invalid frame len\n");
1398c2ecf20Sopenharmony_ci		phy->ndlc->hard_fault = 1;
1408c2ecf20Sopenharmony_ci		return -EBADMSG;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	*skb = alloc_skb(ST_NCI_SPI_MIN_SIZE + len, GFP_KERNEL);
1448c2ecf20Sopenharmony_ci	if (*skb == NULL)
1458c2ecf20Sopenharmony_ci		return -ENOMEM;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	skb_reserve(*skb, ST_NCI_SPI_MIN_SIZE);
1488c2ecf20Sopenharmony_ci	skb_put(*skb, ST_NCI_SPI_MIN_SIZE);
1498c2ecf20Sopenharmony_ci	memcpy((*skb)->data, buf, ST_NCI_SPI_MIN_SIZE);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if (!len)
1528c2ecf20Sopenharmony_ci		return 0;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	spi_xfer.len = len;
1558c2ecf20Sopenharmony_ci	r = spi_sync_transfer(dev, &spi_xfer, 1);
1568c2ecf20Sopenharmony_ci	if (r < 0) {
1578c2ecf20Sopenharmony_ci		kfree_skb(*skb);
1588c2ecf20Sopenharmony_ci		return -EREMOTEIO;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	skb_put(*skb, len);
1628c2ecf20Sopenharmony_ci	memcpy((*skb)->data + ST_NCI_SPI_MIN_SIZE, buf, len);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return 0;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/*
1688c2ecf20Sopenharmony_ci * Reads an ndlc frame from the chip.
1698c2ecf20Sopenharmony_ci *
1708c2ecf20Sopenharmony_ci * On ST21NFCB, IRQ goes in idle state when read starts.
1718c2ecf20Sopenharmony_ci */
1728c2ecf20Sopenharmony_cistatic irqreturn_t st_nci_irq_thread_fn(int irq, void *phy_id)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct st_nci_spi_phy *phy = phy_id;
1758c2ecf20Sopenharmony_ci	struct spi_device *dev;
1768c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
1778c2ecf20Sopenharmony_ci	int r;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	if (!phy || !phy->ndlc || irq != phy->spi_dev->irq) {
1808c2ecf20Sopenharmony_ci		WARN_ON_ONCE(1);
1818c2ecf20Sopenharmony_ci		return IRQ_NONE;
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	dev = phy->spi_dev;
1858c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "IRQ\n");
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (phy->ndlc->hard_fault)
1888c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (!phy->ndlc->powered) {
1918c2ecf20Sopenharmony_ci		st_nci_spi_disable(phy);
1928c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	r = st_nci_spi_read(phy, &skb);
1968c2ecf20Sopenharmony_ci	if (r == -EREMOTEIO || r == -ENOMEM || r == -EBADMSG)
1978c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	ndlc_recv(phy->ndlc, skb);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic struct nfc_phy_ops spi_phy_ops = {
2058c2ecf20Sopenharmony_ci	.write = st_nci_spi_write,
2068c2ecf20Sopenharmony_ci	.enable = st_nci_spi_enable,
2078c2ecf20Sopenharmony_ci	.disable = st_nci_spi_disable,
2088c2ecf20Sopenharmony_ci};
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic const struct acpi_gpio_params reset_gpios = { 1, 0, false };
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic const struct acpi_gpio_mapping acpi_st_nci_gpios[] = {
2138c2ecf20Sopenharmony_ci	{ "reset-gpios", &reset_gpios, 1 },
2148c2ecf20Sopenharmony_ci	{},
2158c2ecf20Sopenharmony_ci};
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int st_nci_spi_probe(struct spi_device *dev)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct st_nci_spi_phy *phy;
2208c2ecf20Sopenharmony_ci	int r;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "%s\n", __func__);
2238c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "IRQ: %d\n", dev->irq);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	/* Check SPI platform functionnalities */
2268c2ecf20Sopenharmony_ci	if (!dev) {
2278c2ecf20Sopenharmony_ci		pr_debug("%s: dev is NULL. Device is not accessible.\n",
2288c2ecf20Sopenharmony_ci			__func__);
2298c2ecf20Sopenharmony_ci		return -ENODEV;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	phy = devm_kzalloc(&dev->dev, sizeof(struct st_nci_spi_phy),
2338c2ecf20Sopenharmony_ci			   GFP_KERNEL);
2348c2ecf20Sopenharmony_ci	if (!phy)
2358c2ecf20Sopenharmony_ci		return -ENOMEM;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	phy->spi_dev = dev;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	spi_set_drvdata(dev, phy);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	r = devm_acpi_dev_add_driver_gpios(&dev->dev, acpi_st_nci_gpios);
2428c2ecf20Sopenharmony_ci	if (r)
2438c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "Unable to add GPIO mapping table\n");
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	/* Get RESET GPIO */
2468c2ecf20Sopenharmony_ci	phy->gpiod_reset = devm_gpiod_get(&dev->dev, "reset", GPIOD_OUT_HIGH);
2478c2ecf20Sopenharmony_ci	if (IS_ERR(phy->gpiod_reset)) {
2488c2ecf20Sopenharmony_ci		nfc_err(&dev->dev, "Unable to get RESET GPIO\n");
2498c2ecf20Sopenharmony_ci		return PTR_ERR(phy->gpiod_reset);
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	phy->se_status.is_ese_present =
2538c2ecf20Sopenharmony_ci			device_property_read_bool(&dev->dev, "ese-present");
2548c2ecf20Sopenharmony_ci	phy->se_status.is_uicc_present =
2558c2ecf20Sopenharmony_ci			device_property_read_bool(&dev->dev, "uicc-present");
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	r = ndlc_probe(phy, &spi_phy_ops, &dev->dev,
2588c2ecf20Sopenharmony_ci			ST_NCI_FRAME_HEADROOM, ST_NCI_FRAME_TAILROOM,
2598c2ecf20Sopenharmony_ci			&phy->ndlc, &phy->se_status);
2608c2ecf20Sopenharmony_ci	if (r < 0) {
2618c2ecf20Sopenharmony_ci		nfc_err(&dev->dev, "Unable to register ndlc layer\n");
2628c2ecf20Sopenharmony_ci		return r;
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	phy->irq_active = true;
2668c2ecf20Sopenharmony_ci	r = devm_request_threaded_irq(&dev->dev, dev->irq, NULL,
2678c2ecf20Sopenharmony_ci				st_nci_irq_thread_fn,
2688c2ecf20Sopenharmony_ci				IRQF_ONESHOT,
2698c2ecf20Sopenharmony_ci				ST_NCI_SPI_DRIVER_NAME, phy);
2708c2ecf20Sopenharmony_ci	if (r < 0)
2718c2ecf20Sopenharmony_ci		nfc_err(&dev->dev, "Unable to register IRQ handler\n");
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return r;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int st_nci_spi_remove(struct spi_device *dev)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct st_nci_spi_phy *phy = spi_get_drvdata(dev);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "%s\n", __func__);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	ndlc_remove(phy->ndlc);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	return 0;
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistatic struct spi_device_id st_nci_spi_id_table[] = {
2888c2ecf20Sopenharmony_ci	{ST_NCI_SPI_DRIVER_NAME, 0},
2898c2ecf20Sopenharmony_ci	{}
2908c2ecf20Sopenharmony_ci};
2918c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, st_nci_spi_id_table);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic const struct acpi_device_id st_nci_spi_acpi_match[] = {
2948c2ecf20Sopenharmony_ci	{"SMO2101", 0},
2958c2ecf20Sopenharmony_ci	{}
2968c2ecf20Sopenharmony_ci};
2978c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, st_nci_spi_acpi_match);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic const struct of_device_id of_st_nci_spi_match[] = {
3008c2ecf20Sopenharmony_ci	{ .compatible = "st,st21nfcb-spi", },
3018c2ecf20Sopenharmony_ci	{}
3028c2ecf20Sopenharmony_ci};
3038c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_st_nci_spi_match);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic struct spi_driver st_nci_spi_driver = {
3068c2ecf20Sopenharmony_ci	.driver = {
3078c2ecf20Sopenharmony_ci		.name = ST_NCI_SPI_DRIVER_NAME,
3088c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(of_st_nci_spi_match),
3098c2ecf20Sopenharmony_ci		.acpi_match_table = ACPI_PTR(st_nci_spi_acpi_match),
3108c2ecf20Sopenharmony_ci	},
3118c2ecf20Sopenharmony_ci	.probe = st_nci_spi_probe,
3128c2ecf20Sopenharmony_ci	.id_table = st_nci_spi_id_table,
3138c2ecf20Sopenharmony_ci	.remove = st_nci_spi_remove,
3148c2ecf20Sopenharmony_ci};
3158c2ecf20Sopenharmony_cimodule_spi_driver(st_nci_spi_driver);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
3188c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
319