diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-12 18:57:02 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-12 18:57:02 -0800 |
commit | aee3bfa3307cd0da2126bdc0ea359dabea5ee8f7 (patch) | |
tree | 3d35c69e8fa835098bb90f77f30abed120681651 /drivers/nfc | |
parent | c597b6bcd5c624534afc3df65cdc42bb05173bca (diff) | |
parent | 415b6f19e87e350b13585591859d4fdf50772229 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from Davic Miller:
1) Support busy polling generically, for all NAPI drivers. From Eric
Dumazet.
2) Add byte/packet counter support to nft_ct, from Floriani Westphal.
3) Add RSS/XPS support to mvneta driver, from Gregory Clement.
4) Implement IPV6_HDRINCL socket option for raw sockets, from Hannes
Frederic Sowa.
5) Add support for T6 adapter to cxgb4 driver, from Hariprasad Shenai.
6) Add support for VLAN device bridging to mlxsw switch driver, from
Ido Schimmel.
7) Add driver for Netronome NFP4000/NFP6000, from Jakub Kicinski.
8) Provide hwmon interface to mlxsw switch driver, from Jiri Pirko.
9) Reorganize wireless drivers into per-vendor directories just like we
do for ethernet drivers. From Kalle Valo.
10) Provide a way for administrators "destroy" connected sockets via the
SOCK_DESTROY socket netlink diag operation. From Lorenzo Colitti.
11) Add support to add/remove multicast routes via netlink, from Nikolay
Aleksandrov.
12) Make TCP keepalive settings per-namespace, from Nikolay Borisov.
13) Add forwarding and packet duplication facilities to nf_tables, from
Pablo Neira Ayuso.
14) Dead route support in MPLS, from Roopa Prabhu.
15) TSO support for thunderx chips, from Sunil Goutham.
16) Add driver for IBM's System i/p VNIC protocol, from Thomas Falcon.
17) Rationalize, consolidate, and more completely document the checksum
offloading facilities in the networking stack. From Tom Herbert.
18) Support aborting an ongoing scan in mac80211/cfg80211, from
Vidyullatha Kanchanapally.
19) Use per-bucket spinlock for bpf hash facility, from Tom Leiming.
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1375 commits)
net: bnxt: always return values from _bnxt_get_max_rings
net: bpf: reject invalid shifts
phonet: properly unshare skbs in phonet_rcv()
dwc_eth_qos: Fix dma address for multi-fragment skbs
phy: remove an unneeded condition
mdio: remove an unneed condition
mdio_bus: NULL dereference on allocation error
net: Fix typo in netdev_intersect_features
net: freescale: mac-fec: Fix build error from phy_device API change
net: freescale: ucc_geth: Fix build error from phy_device API change
bonding: Prevent IPv6 link local address on enslaved devices
IB/mlx5: Add flow steering support
net/mlx5_core: Export flow steering API
net/mlx5_core: Make ipv4/ipv6 location more clear
net/mlx5_core: Enable flow steering support for the IB driver
net/mlx5_core: Initialize namespaces only when supported by device
net/mlx5_core: Set priority attributes
net/mlx5_core: Connect flow tables
net/mlx5_core: Introduce modify flow table command
net/mlx5_core: Managing root flow table
...
Diffstat (limited to 'drivers/nfc')
-rw-r--r-- | drivers/nfc/Kconfig | 1 | ||||
-rw-r--r-- | drivers/nfc/Makefile | 1 | ||||
-rw-r--r-- | drivers/nfc/fdp/i2c.c | 12 | ||||
-rw-r--r-- | drivers/nfc/microread/i2c.c | 2 | ||||
-rw-r--r-- | drivers/nfc/nfcsim.c | 10 | ||||
-rw-r--r-- | drivers/nfc/nxp-nci/i2c.c | 34 | ||||
-rw-r--r-- | drivers/nfc/pn544/i2c.c | 46 | ||||
-rw-r--r-- | drivers/nfc/s3fwrn5/core.c | 2 | ||||
-rw-r--r-- | drivers/nfc/s3fwrn5/i2c.c | 2 | ||||
-rw-r--r-- | drivers/nfc/s3fwrn5/s3fwrn5.h | 4 | ||||
-rw-r--r-- | drivers/nfc/st-nci/Kconfig | 18 | ||||
-rw-r--r-- | drivers/nfc/st-nci/i2c.c | 80 | ||||
-rw-r--r-- | drivers/nfc/st-nci/ndlc.c | 1 | ||||
-rw-r--r-- | drivers/nfc/st-nci/se.c | 3 | ||||
-rw-r--r-- | drivers/nfc/st-nci/spi.c | 81 | ||||
-rw-r--r-- | drivers/nfc/st21nfca/Kconfig | 13 | ||||
-rw-r--r-- | drivers/nfc/st21nfca/i2c.c | 80 | ||||
-rw-r--r-- | drivers/nfc/st21nfca/se.c | 5 | ||||
-rw-r--r-- | drivers/nfc/st95hf/Kconfig | 10 | ||||
-rw-r--r-- | drivers/nfc/st95hf/Makefile | 6 | ||||
-rw-r--r-- | drivers/nfc/st95hf/core.c | 1273 | ||||
-rw-r--r-- | drivers/nfc/st95hf/spi.c | 167 | ||||
-rw-r--r-- | drivers/nfc/st95hf/spi.h | 64 | ||||
-rw-r--r-- | drivers/nfc/trf7970a.c | 8 |
24 files changed, 1748 insertions, 175 deletions
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index 0d6003dee3af..7437c9dfd8fc 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -76,4 +76,5 @@ source "drivers/nfc/st21nfca/Kconfig" source "drivers/nfc/st-nci/Kconfig" source "drivers/nfc/nxp-nci/Kconfig" source "drivers/nfc/s3fwrn5/Kconfig" +source "drivers/nfc/st95hf/Kconfig" endmenu diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index e3621416a48e..0a99e67daa10 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_NFC_ST21NFCA) += st21nfca/ obj-$(CONFIG_NFC_ST_NCI) += st-nci/ obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci/ obj-$(CONFIG_NFC_S3FWRN5) += s3fwrn5/ +obj-$(CONFIG_NFC_ST95HF) += st95hf/ diff --git a/drivers/nfc/fdp/i2c.c b/drivers/nfc/fdp/i2c.c index 532db28145c7..5e797d5c38ed 100644 --- a/drivers/nfc/fdp/i2c.c +++ b/drivers/nfc/fdp/i2c.c @@ -298,6 +298,12 @@ static int fdp_nci_i2c_probe(struct i2c_client *client, return -ENODEV; } + /* Checking if we have an irq */ + if (client->irq <= 0) { + nfc_err(dev, "IRQ not present\n"); + return -ENODEV; + } + phy = devm_kzalloc(dev, sizeof(struct fdp_i2c_phy), GFP_KERNEL); if (!phy) @@ -307,12 +313,6 @@ static int fdp_nci_i2c_probe(struct i2c_client *client, phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD; i2c_set_clientdata(client, phy); - /* Checking if we have an irq */ - if (client->irq <= 0) { - dev_err(dev, "IRQ not present\n"); - return -ENODEV; - } - r = request_threaded_irq(client->irq, NULL, fdp_nci_i2c_irq_thread_fn, IRQF_TRIGGER_RISING | IRQF_ONESHOT, FDP_I2C_DRIVER_NAME, phy); diff --git a/drivers/nfc/microread/i2c.c b/drivers/nfc/microread/i2c.c index daf352597ef8..918e8f2eac47 100644 --- a/drivers/nfc/microread/i2c.c +++ b/drivers/nfc/microread/i2c.c @@ -50,8 +50,6 @@ struct microread_i2c_phy { struct i2c_client *i2c_dev; struct nfc_hci_dev *hdev; - int irq; - int hard_fault; /* * < 0 if hardware error occured (e.g. i2c err) * and prevents normal operation. diff --git a/drivers/nfc/nfcsim.c b/drivers/nfc/nfcsim.c index 26ac9e5fa1ab..93aaca586858 100644 --- a/drivers/nfc/nfcsim.c +++ b/drivers/nfc/nfcsim.c @@ -32,6 +32,8 @@ #define NFCSIM_POLL_TARGET 2 #define NFCSIM_POLL_DUAL (NFCSIM_POLL_INITIATOR | NFCSIM_POLL_TARGET) +#define RX_DEFAULT_DELAY 5 + struct nfcsim { struct nfc_dev *nfc_dev; @@ -51,6 +53,8 @@ struct nfcsim { u8 initiator; + u32 rx_delay; + data_exchange_cb_t cb; void *cb_context; @@ -320,10 +324,9 @@ static int nfcsim_tx(struct nfc_dev *nfc_dev, struct nfc_target *target, * If packet transmission occurs immediately between them, we have a * non-stop flow of several tens of thousands SYMM packets per second * and a burning cpu. - * - * TODO: Add support for a sysfs entry to control this delay. */ - queue_delayed_work(wq, &peer->recv_work, msecs_to_jiffies(5)); + queue_delayed_work(wq, &peer->recv_work, + msecs_to_jiffies(dev->rx_delay)); mutex_unlock(&peer->lock); @@ -461,6 +464,7 @@ static struct nfcsim *nfcsim_init_dev(void) if (rc) goto free_nfc_dev; + dev->rx_delay = RX_DEFAULT_DELAY; return dev; free_nfc_dev: diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c index df4333c7ee0f..11520f472f98 100644 --- a/drivers/nfc/nxp-nci/i2c.c +++ b/drivers/nfc/nxp-nci/i2c.c @@ -52,7 +52,6 @@ struct nxp_nci_i2c_phy { unsigned int gpio_en; unsigned int gpio_fw; - unsigned int gpio_irq; int hard_fault; /* * < 0 if hardware error occurred (e.g. i2c err) @@ -85,7 +84,7 @@ static int nxp_nci_i2c_write(void *phy_id, struct sk_buff *skb) return phy->hard_fault; r = i2c_master_send(client, skb->data, skb->len); - if (r == -EREMOTEIO) { + if (r < 0) { /* Retry, chip was in standby */ usleep_range(110000, 120000); r = i2c_master_send(client, skb->data, skb->len); @@ -264,8 +263,6 @@ exit_irq_none: return IRQ_NONE; } -#ifdef CONFIG_OF - static int nxp_nci_i2c_parse_devtree(struct i2c_client *client) { struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client); @@ -294,48 +291,24 @@ static int nxp_nci_i2c_parse_devtree(struct i2c_client *client) } phy->gpio_fw = r; - r = irq_of_parse_and_map(pp, 0); - if (r < 0) { - nfc_err(&client->dev, "Unable to get irq, error: %d\n", r); - return r; - } - client->irq = r; - return 0; } -#else - -static int nxp_nci_i2c_parse_devtree(struct i2c_client *client) -{ - return -ENODEV; -} - -#endif - static int nxp_nci_i2c_acpi_config(struct nxp_nci_i2c_phy *phy) { struct i2c_client *client = phy->i2c_dev; - struct gpio_desc *gpiod_en, *gpiod_fw, *gpiod_irq; + struct gpio_desc *gpiod_en, *gpiod_fw; gpiod_en = devm_gpiod_get_index(&client->dev, NULL, 2, GPIOD_OUT_LOW); gpiod_fw = devm_gpiod_get_index(&client->dev, NULL, 1, GPIOD_OUT_LOW); - gpiod_irq = devm_gpiod_get_index(&client->dev, NULL, 0, GPIOD_IN); - if (IS_ERR(gpiod_en) || IS_ERR(gpiod_fw) || IS_ERR(gpiod_irq)) { + if (IS_ERR(gpiod_en) || IS_ERR(gpiod_fw)) { nfc_err(&client->dev, "No GPIOs\n"); return -EINVAL; } - client->irq = gpiod_to_irq(gpiod_irq); - if (client->irq < 0) { - nfc_err(&client->dev, "No IRQ\n"); - return -EINVAL; - } - phy->gpio_en = desc_to_gpio(gpiod_en); phy->gpio_fw = desc_to_gpio(gpiod_fw); - phy->gpio_irq = desc_to_gpio(gpiod_irq); return 0; } @@ -374,7 +347,6 @@ static int nxp_nci_i2c_probe(struct i2c_client *client, } else if (pdata) { phy->gpio_en = pdata->gpio_en; phy->gpio_fw = pdata->gpio_fw; - client->irq = pdata->irq; } else if (ACPI_HANDLE(&client->dev)) { r = nxp_nci_i2c_acpi_config(phy); if (r < 0) diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index fa75c53f3fa5..76c318444304 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -166,7 +166,6 @@ struct pn544_i2c_phy { struct nfc_hci_dev *hdev; unsigned int gpio_en; - unsigned int gpio_irq; unsigned int gpio_fw; unsigned int en_polarity; @@ -879,9 +878,8 @@ static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client) { struct pn544_i2c_phy *phy = i2c_get_clientdata(client); const struct acpi_device_id *id; - struct gpio_desc *gpiod_en, *gpiod_irq, *gpiod_fw; + struct gpio_desc *gpiod_en, *gpiod_fw; struct device *dev; - int ret; if (!client) return -EINVAL; @@ -914,32 +912,9 @@ static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client) phy->gpio_fw = desc_to_gpio(gpiod_fw); - /* Get IRQ GPIO */ - gpiod_irq = devm_gpiod_get_index(dev, PN544_GPIO_NAME_IRQ, 0, - GPIOD_IN); - if (IS_ERR(gpiod_irq)) { - nfc_err(dev, "Unable to get IRQ GPIO\n"); - return -ENODEV; - } - - phy->gpio_irq = desc_to_gpio(gpiod_irq); - - /* Map the pin to an IRQ */ - ret = gpiod_to_irq(gpiod_irq); - if (ret < 0) { - nfc_err(dev, "Fail pin IRQ mapping\n"); - return ret; - } - - nfc_info(dev, "GPIO resource, no:%d irq:%d\n", - desc_to_gpio(gpiod_irq), ret); - client->irq = ret; - return 0; } -#ifdef CONFIG_OF - static int pn544_hci_i2c_of_request_resources(struct i2c_client *client) { struct pn544_i2c_phy *phy = i2c_get_clientdata(client); @@ -996,15 +971,6 @@ static int pn544_hci_i2c_of_request_resources(struct i2c_client *client) goto err_gpio_fw; } - /* IRQ */ - ret = irq_of_parse_and_map(pp, 0); - if (ret < 0) { - nfc_err(&client->dev, - "Unable to get irq, error: %d\n", ret); - goto err_gpio_fw; - } - client->irq = ret; - return 0; err_gpio_fw: @@ -1015,15 +981,6 @@ err_dt: return ret; } -#else - -static int pn544_hci_i2c_of_request_resources(struct i2c_client *client) -{ - return -ENODEV; -} - -#endif - static int pn544_hci_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1076,7 +1033,6 @@ static int pn544_hci_i2c_probe(struct i2c_client *client, phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE); phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET); - phy->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ); /* Using ACPI */ } else if (ACPI_HANDLE(&client->dev)) { r = pn544_hci_i2c_acpi_request_resources(client); diff --git a/drivers/nfc/s3fwrn5/core.c b/drivers/nfc/s3fwrn5/core.c index 0d866ca295e3..9d9c8d57a042 100644 --- a/drivers/nfc/s3fwrn5/core.c +++ b/drivers/nfc/s3fwrn5/core.c @@ -147,7 +147,7 @@ static struct nci_ops s3fwrn5_nci_ops = { }; int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev, - struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload) + const struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload) { struct s3fwrn5_info *info; int ret; diff --git a/drivers/nfc/s3fwrn5/i2c.c b/drivers/nfc/s3fwrn5/i2c.c index c61d8a308da4..3ed0adf6479b 100644 --- a/drivers/nfc/s3fwrn5/i2c.c +++ b/drivers/nfc/s3fwrn5/i2c.c @@ -125,7 +125,7 @@ static int s3fwrn5_i2c_write(void *phy_id, struct sk_buff *skb) return 0; } -static struct s3fwrn5_phy_ops i2c_phy_ops = { +static const struct s3fwrn5_phy_ops i2c_phy_ops = { .set_wake = s3fwrn5_i2c_set_wake, .set_mode = s3fwrn5_i2c_set_mode, .get_mode = s3fwrn5_i2c_get_mode, diff --git a/drivers/nfc/s3fwrn5/s3fwrn5.h b/drivers/nfc/s3fwrn5/s3fwrn5.h index 89210d4828b8..7d5e516036fb 100644 --- a/drivers/nfc/s3fwrn5/s3fwrn5.h +++ b/drivers/nfc/s3fwrn5/s3fwrn5.h @@ -44,7 +44,7 @@ struct s3fwrn5_info { void *phy_id; struct device *pdev; - struct s3fwrn5_phy_ops *phy_ops; + const struct s3fwrn5_phy_ops *phy_ops; unsigned int max_payload; struct s3fwrn5_fw_info fw_info; @@ -90,7 +90,7 @@ static inline int s3fwrn5_write(struct s3fwrn5_info *info, struct sk_buff *skb) } int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev, - struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload); + const struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload); void s3fwrn5_remove(struct nci_dev *ndev); int s3fwrn5_recv_frame(struct nci_dev *ndev, struct sk_buff *skb, diff --git a/drivers/nfc/st-nci/Kconfig b/drivers/nfc/st-nci/Kconfig index e7c6db9c5860..dc9b777d78f6 100644 --- a/drivers/nfc/st-nci/Kconfig +++ b/drivers/nfc/st-nci/Kconfig @@ -1,19 +1,14 @@ config NFC_ST_NCI - tristate "STMicroelectronics ST NCI NFC driver" - depends on NFC_NCI - default n + tristate ---help--- STMicroelectronics NFC NCI chips core driver. It implements the chipset NCI logic and hooks into the NFC kernel APIs. Physical layers will register against it. - To compile this driver as a module, choose m here. The module will - be called st-nci. - Say N if unsure. - config NFC_ST_NCI_I2C - tristate "NFC ST NCI i2c support" - depends on NFC_ST_NCI && I2C + tristate "STMicroelectronics ST NCI NFC driver (I2C)" + depends on NFC_NCI && I2C + select NFC_ST_NCI ---help--- This module adds support for an I2C interface to the STMicroelectronics NFC NCI chips familly. @@ -23,8 +18,9 @@ config NFC_ST_NCI_I2C Say N if unsure. config NFC_ST_NCI_SPI - tristate "NFC ST NCI spi support" - depends on NFC_ST_NCI && SPI + tristate "STMicroelectronics ST NCI NFC driver (SPI)" + depends on NFC_NCI && SPI + select NFC_ST_NCI ---help--- This module adds support for an SPI interface to the STMicroelectronics NFC NCI chips familly. diff --git a/drivers/nfc/st-nci/i2c.c b/drivers/nfc/st-nci/i2c.c index 15e3ce2d274c..8a56b5c6e4c4 100644 --- a/drivers/nfc/st-nci/i2c.c +++ b/drivers/nfc/st-nci/i2c.c @@ -20,8 +20,10 @@ #include <linux/module.h> #include <linux/i2c.h> #include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/of_irq.h> #include <linux/of_gpio.h> +#include <linux/acpi.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/nfc.h> @@ -40,11 +42,7 @@ #define ST_NCI_I2C_DRIVER_NAME "st_nci_i2c" -static struct i2c_device_id st_nci_i2c_id_table[] = { - {ST_NCI_DRIVER_NAME, 0}, - {} -}; -MODULE_DEVICE_TABLE(i2c, st_nci_i2c_id_table); +#define ST_NCI_GPIO_NAME_RESET "clf_reset" struct st_nci_i2c_phy { struct i2c_client *i2c_dev; @@ -210,7 +208,43 @@ static struct nfc_phy_ops i2c_phy_ops = { .disable = st_nci_i2c_disable, }; -#ifdef CONFIG_OF +static int st_nci_i2c_acpi_request_resources(struct i2c_client *client) +{ + struct st_nci_i2c_phy *phy = i2c_get_clientdata(client); + const struct acpi_device_id *id; + struct gpio_desc *gpiod_reset; + struct device *dev; + + if (!client) + return -EINVAL; + + dev = &client->dev; + + /* Match the struct device against a given list of ACPI IDs */ + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + /* Get RESET GPIO from ACPI */ + gpiod_reset = devm_gpiod_get_index(dev, ST_NCI_GPIO_NAME_RESET, 1, + GPIOD_OUT_HIGH); + if (IS_ERR(gpiod_reset)) { + nfc_err(dev, "Unable to get RESET GPIO\n"); + return -ENODEV; + } + + phy->gpio_reset = desc_to_gpio(gpiod_reset); + + phy->irq_polarity = irq_get_trigger_type(client->irq); + + phy->se_status.is_ese_present = + device_property_present(dev, "ese-present"); + phy->se_status.is_uicc_present = + device_property_present(dev, "uicc-present"); + + return 0; +} + static int st_nci_i2c_of_request_resources(struct i2c_client *client) { struct st_nci_i2c_phy *phy = i2c_get_clientdata(client); @@ -232,7 +266,7 @@ static int st_nci_i2c_of_request_resources(struct i2c_client *client) /* GPIO request and configuration */ r = devm_gpio_request_one(&client->dev, gpio, - GPIOF_OUT_INIT_HIGH, "clf_reset"); + GPIOF_OUT_INIT_HIGH, ST_NCI_GPIO_NAME_RESET); if (r) { nfc_err(&client->dev, "Failed to request reset pin\n"); return r; @@ -248,12 +282,6 @@ static int st_nci_i2c_of_request_resources(struct i2c_client *client) return 0; } -#else -static int st_nci_i2c_of_request_resources(struct i2c_client *client) -{ - return -ENODEV; -} -#endif static int st_nci_i2c_request_resources(struct i2c_client *client) { @@ -272,7 +300,8 @@ static int st_nci_i2c_request_resources(struct i2c_client *client) phy->irq_polarity = pdata->irq_polarity; r = devm_gpio_request_one(&client->dev, - phy->gpio_reset, GPIOF_OUT_INIT_HIGH, "clf_reset"); + phy->gpio_reset, GPIOF_OUT_INIT_HIGH, + ST_NCI_GPIO_NAME_RESET); if (r) { pr_err("%s : reset gpio_request failed\n", __FILE__); return r; @@ -322,6 +351,12 @@ static int st_nci_i2c_probe(struct i2c_client *client, "Cannot get platform resources\n"); return r; } + } else if (ACPI_HANDLE(&client->dev)) { + r = st_nci_i2c_acpi_request_resources(client); + if (r) { + nfc_err(&client->dev, "Cannot get ACPI data\n"); + return r; + } } else { nfc_err(&client->dev, "st_nci platform resources not available\n"); @@ -358,7 +393,19 @@ static int st_nci_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_OF +static struct i2c_device_id st_nci_i2c_id_table[] = { + {ST_NCI_DRIVER_NAME, 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, st_nci_i2c_id_table); + +static const struct acpi_device_id st_nci_i2c_acpi_match[] = { + {"SMO2101"}, + {"SMO2102"}, + {} +}; +MODULE_DEVICE_TABLE(acpi, st_nci_i2c_acpi_match); + static const struct of_device_id of_st_nci_i2c_match[] = { { .compatible = "st,st21nfcb-i2c", }, { .compatible = "st,st21nfcb_i2c", }, @@ -366,19 +413,18 @@ static const struct of_device_id of_st_nci_i2c_match[] = { {} }; MODULE_DEVICE_TABLE(of, of_st_nci_i2c_match); -#endif static struct i2c_driver st_nci_i2c_driver = { .driver = { .owner = THIS_MODULE, .name = ST_NCI_I2C_DRIVER_NAME, .of_match_table = of_match_ptr(of_st_nci_i2c_match), + .acpi_match_table = ACPI_PTR(st_nci_i2c_acpi_match), }, .probe = st_nci_i2c_probe, .id_table = st_nci_i2c_id_table, .remove = st_nci_i2c_remove, }; - module_i2c_driver(st_nci_i2c_driver); MODULE_LICENSE("GPL"); diff --git a/drivers/nfc/st-nci/ndlc.c b/drivers/nfc/st-nci/ndlc.c index 0884b11001ef..50880d747b02 100644 --- a/drivers/nfc/st-nci/ndlc.c +++ b/drivers/nfc/st-nci/ndlc.c @@ -20,7 +20,6 @@ #include <net/nfc/nci_core.h> #include "st-nci.h" -#include "ndlc.h" #define NDLC_TIMER_T1 100 #define NDLC_TIMER_T1_WAIT 400 diff --git a/drivers/nfc/st-nci/se.c b/drivers/nfc/st-nci/se.c index dbab722a0654..a53e5df803eb 100644 --- a/drivers/nfc/st-nci/se.c +++ b/drivers/nfc/st-nci/se.c @@ -331,7 +331,7 @@ static int st_nci_hci_connectivity_event_received(struct nci_dev *ndev, switch (event) { case ST_NCI_EVT_CONNECTIVITY: - + r = nfc_se_connectivity(ndev->nfc_dev, host); break; case ST_NCI_EVT_TRANSACTION: /* According to specification etsi 102 622 @@ -392,7 +392,6 @@ void st_nci_hci_event_received(struct nci_dev *ndev, u8 pipe, } EXPORT_SYMBOL_GPL(st_nci_hci_event_received); - void st_nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd, struct sk_buff *skb) { diff --git a/drivers/nfc/st-nci/spi.c b/drivers/nfc/st-nci/spi.c index d6519bb9dba5..821dfa950fa8 100644 --- a/drivers/nfc/st-nci/spi.c +++ b/drivers/nfc/st-nci/spi.c @@ -20,8 +20,10 @@ #include <linux/module.h> #include <linux/spi/spi.h> #include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/of_irq.h> #include <linux/of_gpio.h> +#include <linux/acpi.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/nfc.h> @@ -34,18 +36,14 @@ /* ndlc header */ #define ST_NCI_FRAME_HEADROOM 1 -#define ST_NCI_FRAME_TAILROOM 0 +#define ST_NCI_FRAME_TAILROOM 0 #define ST_NCI_SPI_MIN_SIZE 4 /* PCB(1) + NCI Packet header(3) */ #define ST_NCI_SPI_MAX_SIZE 250 /* req 4.2.1 */ #define ST_NCI_SPI_DRIVER_NAME "st_nci_spi" -static struct spi_device_id st_nci_spi_id_table[] = { - {ST_NCI_SPI_DRIVER_NAME, 0}, - {} -}; -MODULE_DEVICE_TABLE(spi, st_nci_spi_id_table); +#define ST_NCI_GPIO_NAME_RESET "clf_reset" struct st_nci_spi_phy { struct spi_device *spi_dev; @@ -225,7 +223,43 @@ static struct nfc_phy_ops spi_phy_ops = { .disable = st_nci_spi_disable, }; -#ifdef CONFIG_OF +static int st_nci_spi_acpi_request_resources(struct spi_device *spi_dev) +{ + struct st_nci_spi_phy *phy = spi_get_drvdata(spi_dev); + const struct acpi_device_id *id; + struct gpio_desc *gpiod_reset; + struct device *dev; + + if (!spi_dev) + return -EINVAL; + + dev = &spi_dev->dev; + + /* Match the struct device against a given list of ACPI IDs */ + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + /* Get RESET GPIO from ACPI */ + gpiod_reset = devm_gpiod_get_index(dev, ST_NCI_GPIO_NAME_RESET, 1, + GPIOD_OUT_HIGH); + if (IS_ERR(gpiod_reset)) { + nfc_err(dev, "Unable to get RESET GPIO\n"); + return -ENODEV; + } + + phy->gpio_reset = desc_to_gpio(gpiod_reset); + + phy->irq_polarity = irq_get_trigger_type(spi_dev->irq); + + phy->se_status.is_ese_present = + device_property_present(dev, "ese-present"); + phy->se_status.is_uicc_present = + device_property_present(dev, "uicc-present"); + + return 0; +} + static int st_nci_spi_of_request_resources(struct spi_device *dev) { struct st_nci_spi_phy *phy = spi_get_drvdata(dev); @@ -247,7 +281,7 @@ static int st_nci_spi_of_request_resources(struct spi_device *dev) /* GPIO request and configuration */ r = devm_gpio_request_one(&dev->dev, gpio, - GPIOF_OUT_INIT_HIGH, "clf_reset"); + GPIOF_OUT_INIT_HIGH, ST_NCI_GPIO_NAME_RESET); if (r) { nfc_err(&dev->dev, "Failed to request reset pin\n"); return r; @@ -263,12 +297,6 @@ static int st_nci_spi_of_request_resources(struct spi_device *dev) return 0; } -#else -static int st_nci_spi_of_request_resources(struct spi_device *dev) -{ - return -ENODEV; -} -#endif static int st_nci_spi_request_resources(struct spi_device *dev) { @@ -287,7 +315,8 @@ static int st_nci_spi_request_resources(struct spi_device *dev) phy->irq_polarity = pdata->irq_polarity; r = devm_gpio_request_one(&dev->dev, - phy->gpio_reset, GPIOF_OUT_INIT_HIGH, "clf_reset"); + phy->gpio_reset, GPIOF_OUT_INIT_HIGH, + ST_NCI_GPIO_NAME_RESET); if (r) { pr_err("%s : reset gpio_request failed\n", __FILE__); return r; @@ -338,6 +367,12 @@ static int st_nci_spi_probe(struct spi_device *dev) "Cannot get platform resources\n"); return r; } + } else if (ACPI_HANDLE(&dev->dev)) { + r = st_nci_spi_acpi_request_resources(dev); + if (r) { + nfc_err(&dev->dev, "Cannot get ACPI data\n"); + return r; + } } else { nfc_err(&dev->dev, "st_nci platform resources not available\n"); @@ -374,24 +409,34 @@ static int st_nci_spi_remove(struct spi_device *dev) return 0; } -#ifdef CONFIG_OF +static struct spi_device_id st_nci_spi_id_table[] = { + {ST_NCI_SPI_DRIVER_NAME, 0}, + {} +}; +MODULE_DEVICE_TABLE(spi, st_nci_spi_id_table); + +static const struct acpi_device_id st_nci_spi_acpi_match[] = { + {"SMO2101", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, st_nci_spi_acpi_match); + static const struct of_device_id of_st_nci_spi_match[] = { { .compatible = "st,st21nfcb-spi", }, {} }; MODULE_DEVICE_TABLE(of, of_st_nci_spi_match); -#endif static struct spi_driver st_nci_spi_driver = { .driver = { .name = ST_NCI_SPI_DRIVER_NAME, .of_match_table = of_match_ptr(of_st_nci_spi_match), + .acpi_match_table = ACPI_PTR(st_nci_spi_acpi_match), }, .probe = st_nci_spi_probe, .id_table = st_nci_spi_id_table, .remove = st_nci_spi_remove, }; - module_spi_driver(st_nci_spi_driver); MODULE_LICENSE("GPL"); diff --git a/drivers/nfc/st21nfca/Kconfig b/drivers/nfc/st21nfca/Kconfig index ee459f066ade..cc3bd5658901 100644 --- a/drivers/nfc/st21nfca/Kconfig +++ b/drivers/nfc/st21nfca/Kconfig @@ -1,20 +1,15 @@ config NFC_ST21NFCA - tristate "STMicroelectronics ST21NFCA NFC driver" - depends on NFC_HCI + tristate select CRC_CCITT - default n ---help--- STMicroelectronics ST21NFCA core driver. It implements the chipset HCI logic and hooks into the NFC kernel APIs. Physical layers will register against it. - To compile this driver as a module, choose m here. The module will - be called st21nfca. - Say N if unsure. - config NFC_ST21NFCA_I2C - tristate "NFC ST21NFCA i2c support" - depends on NFC_ST21NFCA && I2C && NFC_SHDLC + tristate "STMicroelectronics ST21NFCA NFC driver (I2C)" + depends on NFC_HCI && I2C && NFC_SHDLC + select NFC_ST21NFCA ---help--- This module adds support for the STMicroelectronics st21nfca i2c interface. Select this if your platform is using the i2c bus. diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c index a98da33e680a..1f44a151d206 100644 --- a/drivers/nfc/st21nfca/i2c.c +++ b/drivers/nfc/st21nfca/i2c.c @@ -21,8 +21,10 @@ #include <linux/module.h> #include <linux/i2c.h> #include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/of_irq.h> #include <linux/of_gpio.h> +#include <linux/acpi.h> #include <linux/miscdevice.h> #include <linux/interrupt.h> #include <linux/delay.h> @@ -60,12 +62,7 @@ #define ST21NFCA_HCI_I2C_DRIVER_NAME "st21nfca_hci_i2c" -static struct i2c_device_id st21nfca_hci_i2c_id_table[] = { - {ST21NFCA_HCI_DRIVER_NAME, 0}, - {} -}; - -MODULE_DEVICE_TABLE(i2c, st21nfca_hci_i2c_id_table); +#define ST21NFCA_GPIO_NAME_EN "clf_enable" struct st21nfca_i2c_phy { struct i2c_client *i2c_dev; @@ -167,7 +164,6 @@ static void st21nfca_hci_i2c_disable(void *phy_id) { struct st21nfca_i2c_phy *phy = phy_id; - pr_info("\n"); gpio_set_value(phy->gpio_ena, 0); phy->powered = 0; @@ -210,7 +206,6 @@ static int st21nfca_hci_i2c_write(void *phy_id, struct sk_buff *skb) I2C_DUMP_SKB("st21nfca_hci_i2c_write", skb); - if (phy->hard_fault != 0) return phy->hard_fault; @@ -509,7 +504,41 @@ static struct nfc_phy_ops i2c_phy_ops = { .disable = st21nfca_hci_i2c_disable, }; -#ifdef CONFIG_OF +static int st21nfca_hci_i2c_acpi_request_resources(struct i2c_client *client) +{ + struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client); + const struct acpi_device_id *id; + struct gpio_desc *gpiod_ena; + struct device *dev; + + if (!client) + return -EINVAL; + + dev = &client->dev; + + /* Match the struct device against a given list of ACPI IDs */ + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + /* Get EN GPIO from ACPI */ + gpiod_ena = devm_gpiod_get_index(dev, ST21NFCA_GPIO_NAME_EN, 1, + GPIOD_OUT_LOW); + if (!IS_ERR(gpiod_ena)) + phy->gpio_ena = desc_to_gpio(gpiod_ena); + + phy->gpio_ena = desc_to_gpio(gpiod_ena); + + phy->irq_polarity = irq_get_trigger_type(client->irq); + + phy->se_status.is_ese_present = + device_property_present(dev, "ese-present"); + phy->se_status.is_uicc_present = + device_property_present(dev, "uicc-present"); + + return 0; +} + static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) { struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client); @@ -530,7 +559,7 @@ static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) /* GPIO request and configuration */ r = devm_gpio_request_one(&client->dev, gpio, GPIOF_OUT_INIT_HIGH, - "clf_enable"); + ST21NFCA_GPIO_NAME_EN); if (r) { nfc_err(&client->dev, "Failed to request enable pin\n"); return r; @@ -547,12 +576,6 @@ static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) return 0; } -#else -static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) -{ - return -ENODEV; -} -#endif static int st21nfca_hci_i2c_request_resources(struct i2c_client *client) { @@ -572,7 +595,8 @@ static int st21nfca_hci_i2c_request_resources(struct i2c_client *client) if (phy->gpio_ena > 0) { r = devm_gpio_request_one(&client->dev, phy->gpio_ena, - GPIOF_OUT_INIT_HIGH, "clf_enable"); + GPIOF_OUT_INIT_HIGH, + ST21NFCA_GPIO_NAME_EN); if (r) { pr_err("%s : ena gpio_request failed\n", __FILE__); return r; @@ -628,6 +652,12 @@ static int st21nfca_hci_i2c_probe(struct i2c_client *client, nfc_err(&client->dev, "Cannot get platform resources\n"); return r; } + } else if (ACPI_HANDLE(&client->dev)) { + r = st21nfca_hci_i2c_acpi_request_resources(client); + if (r) { + nfc_err(&client->dev, "Cannot get ACPI data\n"); + return r; + } } else { nfc_err(&client->dev, "st21nfca platform resources not available\n"); return -ENODEV; @@ -670,26 +700,36 @@ static int st21nfca_hci_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_OF +static struct i2c_device_id st21nfca_hci_i2c_id_table[] = { + {ST21NFCA_HCI_DRIVER_NAME, 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, st21nfca_hci_i2c_id_table); + +static const struct acpi_device_id st21nfca_hci_i2c_acpi_match[] = { + {"SMO2100", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, st21nfca_hci_i2c_acpi_match); + static const struct of_device_id of_st21nfca_i2c_match[] = { { .compatible = "st,st21nfca-i2c", }, { .compatible = "st,st21nfca_i2c", }, {} }; MODULE_DEVICE_TABLE(of, of_st21nfca_i2c_match); -#endif static struct i2c_driver st21nfca_hci_i2c_driver = { .driver = { .owner = THIS_MODULE, .name = ST21NFCA_HCI_I2C_DRIVER_NAME, .of_match_table = of_match_ptr(of_st21nfca_i2c_match), + .acpi_match_table = ACPI_PTR(st21nfca_hci_i2c_acpi_match), }, .probe = st21nfca_hci_i2c_probe, .id_table = st21nfca_hci_i2c_id_table, .remove = st21nfca_hci_i2c_remove, }; - module_i2c_driver(st21nfca_hci_i2c_driver); MODULE_LICENSE("GPL"); diff --git a/drivers/nfc/st21nfca/se.c b/drivers/nfc/st21nfca/se.c index c79d99b24c96..bd56a16e4007 100644 --- a/drivers/nfc/st21nfca/se.c +++ b/drivers/nfc/st21nfca/se.c @@ -312,7 +312,8 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, switch (event) { case ST21NFCA_EVT_CONNECTIVITY: - break; + r = nfc_se_connectivity(hdev->ndev, host); + break; case ST21NFCA_EVT_TRANSACTION: /* * According to specification etsi 102 622 @@ -342,7 +343,7 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, transaction->aid_len + 4, transaction->params_len); r = nfc_se_transaction(hdev->ndev, host, transaction); - break; + break; default: nfc_err(&hdev->ndev->dev, "Unexpected event on connectivity gate\n"); return 1; diff --git a/drivers/nfc/st95hf/Kconfig b/drivers/nfc/st95hf/Kconfig new file mode 100644 index 000000000000..224f266fdcb6 --- /dev/null +++ b/drivers/nfc/st95hf/Kconfig @@ -0,0 +1,10 @@ +config NFC_ST95HF + tristate "ST95HF NFC Transceiver driver" + depends on SPI && NFC_DIGITAL + help + This enables the ST NFC driver for ST95HF NFC transceiver. + This makes use of SPI framework to communicate with transceiver + and registered with NFC digital core to support Linux NFC framework. + + Say Y here to compile support for ST NFC transceiver ST95HF + linux driver into the kernel or say M to compile it as module. diff --git a/drivers/nfc/st95hf/Makefile b/drivers/nfc/st95hf/Makefile new file mode 100644 index 000000000000..00760b38ab7e --- /dev/null +++ b/drivers/nfc/st95hf/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for STMicroelectronics NFC transceiver ST95HF +# + +obj-$(CONFIG_NFC_ST95HF) += st95hf.o +st95hf-objs := spi.o core.o diff --git a/drivers/nfc/st95hf/core.c b/drivers/nfc/st95hf/core.c new file mode 100644 index 000000000000..c2840e412962 --- /dev/null +++ b/drivers/nfc/st95hf/core.c @@ -0,0 +1,1273 @@ +/* + * -------------------------------------------------------------------- + * Driver for ST NFC Transceiver ST95HF + * -------------------------------------------------------------------- + * Copyright (C) 2015 STMicroelectronics Pvt. Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/nfc.h> +#include <linux/of_gpio.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/wait.h> +#include <net/nfc/digital.h> +#include <net/nfc/nfc.h> + +#include "spi.h" + +/* supported protocols */ +#define ST95HF_SUPPORTED_PROT (NFC_PROTO_ISO14443_MASK | \ + NFC_PROTO_ISO14443_B_MASK | \ + NFC_PROTO_ISO15693_MASK) +/* driver capabilities */ +#define ST95HF_CAPABILITIES NFC_DIGITAL_DRV_CAPS_IN_CRC + +/* Command Send Interface */ +/* ST95HF_COMMAND_SEND CMD Ids */ +#define ECHO_CMD 0x55 +#define WRITE_REGISTER_CMD 0x9 +#define PROTOCOL_SELECT_CMD 0x2 +#define SEND_RECEIVE_CMD 0x4 + +/* Select protocol codes */ +#define ISO15693_PROTOCOL_CODE 0x1 +#define ISO14443A_PROTOCOL_CODE 0x2 +#define ISO14443B_PROTOCOL_CODE 0x3 + +/* + * head room len is 3 + * 1 byte for control byte + * 1 byte for cmd + * 1 byte for size + */ +#define ST95HF_HEADROOM_LEN 3 + +/* + * tailroom is 1 for ISO14443A + * and 0 for ISO14443B/ISO15693, + * hence the max value 1 should be + * taken. + */ +#define ST95HF_TAILROOM_LEN 1 + +/* Command Response interface */ +#define MAX_RESPONSE_BUFFER_SIZE 280 +#define ECHORESPONSE 0x55 +#define ST95HF_ERR_MASK 0xF +#define ST95HF_TIMEOUT_ERROR 0x87 +#define ST95HF_NFCA_CRC_ERR_MASK 0x20 +#define ST95HF_NFCB_CRC_ERR_MASK 0x01 + +/* ST95HF transmission flag values */ +#define TRFLAG_NFCA_SHORT_FRAME 0x07 +#define TRFLAG_NFCA_STD_FRAME 0x08 +#define TRFLAG_NFCA_STD_FRAME_CRC 0x28 + +/* Misc defs */ +#define HIGH 1 +#define LOW 0 +#define ISO14443A_RATS_REQ 0xE0 +#define RATS_TB1_PRESENT_MASK 0x20 +#define RATS_TA1_PRESENT_MASK 0x10 +#define TB1_FWI_MASK 0xF0 +#define WTX_REQ_FROM_TAG 0xF2 + +#define MAX_CMD_LEN 0x7 + +#define MAX_CMD_PARAMS 4 +struct cmd { + int cmd_len; + unsigned char cmd_id; + unsigned char no_cmd_params; + unsigned char cmd_params[MAX_CMD_PARAMS]; + enum req_type req; +}; + +struct param_list { + int param_offset; + int new_param_val; +}; + +/* + * List of top-level cmds to be used internally by the driver. + * All these commands are build on top of ST95HF basic commands + * such as SEND_RECEIVE_CMD, PROTOCOL_SELECT_CMD, etc. + * These top level cmds are used internally while implementing various ops of + * digital layer/driver probe or extending the digital framework layer for + * features that are not yet implemented there, for example, WTX cmd handling. + */ +enum st95hf_cmd_list { + CMD_ECHO, + CMD_ISO14443A_CONFIG, + CMD_ISO14443A_DEMOGAIN, + CMD_ISO14443B_DEMOGAIN, + CMD_ISO14443A_PROTOCOL_SELECT, + CMD_ISO14443B_PROTOCOL_SELECT, + CMD_WTX_RESPONSE, + CMD_FIELD_OFF, + CMD_ISO15693_PROTOCOL_SELECT, +}; + +static const struct cmd cmd_array[] = { + [CMD_ECHO] = { + .cmd_len = 0x2, + .cmd_id = ECHO_CMD, + .no_cmd_params = 0, + .req = SYNC, + }, + [CMD_ISO14443A_CONFIG] = { + .cmd_len = 0x7, + .cmd_id = WRITE_REGISTER_CMD, + .no_cmd_params = 0x4, + .cmd_params = {0x3A, 0x00, 0x5A, 0x04}, + .req = SYNC, + }, + [CMD_ISO14443A_DEMOGAIN] = { + .cmd_len = 0x7, + .cmd_id = WRITE_REGISTER_CMD, + .no_cmd_params = 0x4, + .cmd_params = {0x68, 0x01, 0x01, 0xDF}, + .req = SYNC, + }, + [CMD_ISO14443B_DEMOGAIN] = { + .cmd_len = 0x7, + .cmd_id = WRITE_REGISTER_CMD, + .no_cmd_params = 0x4, + .cmd_params = {0x68, 0x01, 0x01, 0x51}, + .req = SYNC, + }, + [CMD_ISO14443A_PROTOCOL_SELECT] = { + .cmd_len = 0x7, + .cmd_id = PROTOCOL_SELECT_CMD, + .no_cmd_params = 0x4, + .cmd_params = {ISO14443A_PROTOCOL_CODE, 0x00, 0x01, 0xA0}, + .req = SYNC, + }, + [CMD_ISO14443B_PROTOCOL_SELECT] = { + .cmd_len = 0x7, + .cmd_id = PROTOCOL_SELECT_CMD, + .no_cmd_params = 0x4, + .cmd_params = {ISO14443B_PROTOCOL_CODE, 0x01, 0x03, 0xFF}, + .req = SYNC, + }, + [CMD_WTX_RESPONSE] = { + .cmd_len = 0x6, + .cmd_id = SEND_RECEIVE_CMD, + .no_cmd_params = 0x3, + .cmd_params = {0xF2, 0x00, TRFLAG_NFCA_STD_FRAME_CRC}, + .req = ASYNC, + }, + [CMD_FIELD_OFF] = { + .cmd_len = 0x5, + .cmd_id = PROTOCOL_SELECT_CMD, + .no_cmd_params = 0x2, + .cmd_params = {0x0, 0x0}, + .req = SYNC, + }, + [CMD_ISO15693_PROTOCOL_SELECT] = { + .cmd_len = 0x5, + .cmd_id = PROTOCOL_SELECT_CMD, + .no_cmd_params = 0x2, + .cmd_params = {ISO15693_PROTOCOL_CODE, 0x0D}, + .req = SYNC, + }, +}; + +/* st95_digital_cmd_complete_arg stores client context */ +struct st95_digital_cmd_complete_arg { + struct sk_buff *skb_resp; + nfc_digital_cmd_complete_t complete_cb; + void *cb_usrarg; + bool rats; +}; + +/* + * structure containing ST95HF driver specific data. + * @spicontext: structure containing information required + * for spi communication between st95hf and host. + * @ddev: nfc digital device object. + * @nfcdev: nfc device object. + * @enable_gpio: gpio used to enable st95hf transceiver. + * @complete_cb_arg: structure to store various context information + * that is passed from nfc requesting thread to the threaded ISR. + * @st95hf_supply: regulator "consumer" for NFC device. + * @sendrcv_trflag: last byte of frame send by sendrecv command + * of st95hf. This byte contains transmission flag info. + * @exchange_lock: semaphore used for signaling the st95hf_remove + * function that the last outstanding async nfc request is finished. + * @rm_lock: mutex for ensuring safe access of nfc digital object + * from threaded ISR. Usage of this mutex avoids any race between + * deletion of the object from st95hf_remove() and its access from + * the threaded ISR. + * @nfcdev_free: flag to have the state of nfc device object. + * [alive | died] + * @current_protocol: current nfc protocol. + * @current_rf_tech: current rf technology. + * @fwi: frame waiting index, received in reply of RATS according to + * digital protocol. + */ +struct st95hf_context { + struct st95hf_spi_context spicontext; + struct nfc_digital_dev *ddev; + struct nfc_dev *nfcdev; + unsigned int enable_gpio; + struct st95_digital_cmd_complete_arg complete_cb_arg; + struct regulator *st95hf_supply; + unsigned char sendrcv_trflag; + struct semaphore exchange_lock; + struct mutex rm_lock; + bool nfcdev_free; + u8 current_protocol; + u8 current_rf_tech; + int fwi; +}; + +/* + * st95hf_send_recv_cmd() is for sending commands to ST95HF + * that are described in the cmd_array[]. It can optionally + * receive the response if the cmd request is of type + * SYNC. For that to happen caller must pass true to recv_res. + * For ASYNC request, recv_res is ignored and the + * function will never try to receive the response on behalf + * of the caller. + */ +static int st95hf_send_recv_cmd(struct st95hf_context *st95context, + enum st95hf_cmd_list cmd, + int no_modif, + struct param_list *list_array, + bool recv_res) +{ + unsigned char spi_cmd_buffer[MAX_CMD_LEN]; + int i, ret; + struct device *dev = &st95context->spicontext.spidev->dev; + + if (cmd_array[cmd].cmd_len > MAX_CMD_LEN) + return -EINVAL; + if (cmd_array[cmd].no_cmd_params < no_modif) + return -EINVAL; + if (no_modif && !list_array) + return -EINVAL; + + spi_cmd_buffer[0] = ST95HF_COMMAND_SEND; + spi_cmd_buffer[1] = cmd_array[cmd].cmd_id; + spi_cmd_buffer[2] = cmd_array[cmd].no_cmd_params; + + memcpy(&spi_cmd_buffer[3], cmd_array[cmd].cmd_params, + spi_cmd_buffer[2]); + + for (i = 0; i < no_modif; i++) { + if (list_array[i].param_offset >= cmd_array[cmd].no_cmd_params) + return -EINVAL; + spi_cmd_buffer[3 + list_array[i].param_offset] = + list_array[i].new_param_val; + } + + ret = st95hf_spi_send(&st95context->spicontext, + spi_cmd_buffer, + cmd_array[cmd].cmd_len, + cmd_array[cmd].req); + if (ret) { + dev_err(dev, "st95hf_spi_send failed with error %d\n", ret); + return ret; + } + + if (cmd_array[cmd].req == SYNC && recv_res) { + unsigned char st95hf_response_arr[2]; + + ret = st95hf_spi_recv_response(&st95context->spicontext, + st95hf_response_arr); + if (ret < 0) { + dev_err(dev, "spi error from st95hf_spi_recv_response(), err = 0x%x\n", + ret); + return ret; + } + + if (st95hf_response_arr[0]) { + dev_err(dev, "st95hf error from st95hf_spi_recv_response(), err = 0x%x\n", + st95hf_response_arr[0]); + return -EIO; + } + } + + return 0; +} + +static int st95hf_echo_command(struct st95hf_context *st95context) +{ + int result = 0; + unsigned char echo_response; + + result = st95hf_send_recv_cmd(st95context, CMD_ECHO, 0, NULL, false); + if (result) + return result; + + /* If control reached here, response can be taken */ + result = st95hf_spi_recv_echo_res(&st95context->spicontext, + &echo_response); + if (result) { + dev_err(&st95context->spicontext.spidev->dev, + "err: echo response receieve error = 0x%x\n", result); + return result; + } + + if (echo_response == ECHORESPONSE) + return 0; + + dev_err(&st95context->spicontext.spidev->dev, "err: echo res is 0x%x\n", + echo_response); + + return -EIO; +} + +static int secondary_configuration_type4a(struct st95hf_context *stcontext) +{ + int result = 0; + struct device *dev = &stcontext->nfcdev->dev; + + /* 14443A config setting after select protocol */ + result = st95hf_send_recv_cmd(stcontext, + CMD_ISO14443A_CONFIG, + 0, + NULL, + true); + if (result) { + dev_err(dev, "type a config cmd, err = 0x%x\n", result); + return result; + } + + /* 14443A demo gain setting */ + result = st95hf_send_recv_cmd(stcontext, + CMD_ISO14443A_DEMOGAIN, + 0, + NULL, + true); + if (result) + dev_err(dev, "type a demogain cmd, err = 0x%x\n", result); + + return result; +} + +static int secondary_configuration_type4b(struct st95hf_context *stcontext) +{ + int result = 0; + struct device *dev = &stcontext->nfcdev->dev; + + result = st95hf_send_recv_cmd(stcontext, + CMD_ISO14443B_DEMOGAIN, + 0, + NULL, + true); + if (result) + dev_err(dev, "type b demogain cmd, err = 0x%x\n", result); + + return result; +} + +static int st95hf_select_protocol(struct st95hf_context *stcontext, int type) +{ + int result = 0; + struct device *dev; + + dev = &stcontext->nfcdev->dev; + + switch (type) { + case NFC_DIGITAL_RF_TECH_106A: + stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_106A; + result = st95hf_send_recv_cmd(stcontext, + CMD_ISO14443A_PROTOCOL_SELECT, + 0, + NULL, + true); + if (result) { + dev_err(dev, "protocol sel, err = 0x%x\n", + result); + return result; + } + + /* secondary config. for 14443Type 4A after protocol select */ + result = secondary_configuration_type4a(stcontext); + if (result) { + dev_err(dev, "type a secondary config, err = 0x%x\n", + result); + return result; + } + break; + case NFC_DIGITAL_RF_TECH_106B: + stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_106B; + result = st95hf_send_recv_cmd(stcontext, + CMD_ISO14443B_PROTOCOL_SELECT, + 0, + NULL, + true); + if (result) { + dev_err(dev, "protocol sel send, err = 0x%x\n", + result); + return result; + } + + /* + * delay of 5-6 ms is required after select protocol + * command in case of ISO14443 Type B + */ + usleep_range(50000, 60000); + + /* secondary config. for 14443Type 4B after protocol select */ + result = secondary_configuration_type4b(stcontext); + if (result) { + dev_err(dev, "type b secondary config, err = 0x%x\n", + result); + return result; + } + break; + case NFC_DIGITAL_RF_TECH_ISO15693: + stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_ISO15693; + result = st95hf_send_recv_cmd(stcontext, + CMD_ISO15693_PROTOCOL_SELECT, + 0, + NULL, + true); + if (result) { + dev_err(dev, "protocol sel send, err = 0x%x\n", + result); + return result; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static void st95hf_send_st95enable_negativepulse(struct st95hf_context *st95con) +{ + /* First make irq_in pin high */ + gpio_set_value(st95con->enable_gpio, HIGH); + + /* wait for 1 milisecond */ + usleep_range(1000, 2000); + + /* Make irq_in pin low */ + gpio_set_value(st95con->enable_gpio, LOW); + + /* wait for minimum interrupt pulse to make st95 active */ + usleep_range(1000, 2000); + + /* At end make it high */ + gpio_set_value(st95con->enable_gpio, HIGH); +} + +/* + * Send a reset sequence over SPI bus (Reset command + wait 3ms + + * negative pulse on st95hf enable gpio + */ +static int st95hf_send_spi_reset_sequence(struct st95hf_context *st95context) +{ + int result = 0; + unsigned char reset_cmd = ST95HF_COMMAND_RESET; + + result = st95hf_spi_send(&st95context->spicontext, + &reset_cmd, + ST95HF_RESET_CMD_LEN, + ASYNC); + if (result) { + dev_err(&st95context->spicontext.spidev->dev, + "spi reset sequence cmd error = %d", result); + return result; + } + + /* wait for 3 milisecond to complete the controller reset process */ + usleep_range(3000, 4000); + + /* send negative pulse to make st95hf active */ + st95hf_send_st95enable_negativepulse(st95context); + + /* wait for 10 milisecond : HFO setup time */ + usleep_range(10000, 20000); + + return result; +} + +static int st95hf_por_sequence(struct st95hf_context *st95context) +{ + int nth_attempt = 1; + int result; + + st95hf_send_st95enable_negativepulse(st95context); + + usleep_range(5000, 6000); + do { + /* send an ECHO command and checks ST95HF response */ + result = st95hf_echo_command(st95context); + + dev_dbg(&st95context->spicontext.spidev->dev, + "response from echo function = 0x%x, attempt = %d\n", + result, nth_attempt); + + if (!result) + return 0; + + /* send an pulse on IRQ in case of the chip is on sleep state */ + if (nth_attempt == 2) + st95hf_send_st95enable_negativepulse(st95context); + else + st95hf_send_spi_reset_sequence(st95context); + + /* delay of 50 milisecond */ + usleep_range(50000, 51000); + } while (nth_attempt++ < 3); + + return -ETIMEDOUT; +} + +static int iso14443_config_fdt(struct st95hf_context *st95context, int wtxm) +{ + int result = 0; + struct device *dev = &st95context->spicontext.spidev->dev; + struct nfc_digital_dev *nfcddev = st95context->ddev; + unsigned char pp_typeb; + struct param_list new_params[2]; + + pp_typeb = cmd_array[CMD_ISO14443B_PROTOCOL_SELECT].cmd_params[2]; + + if (nfcddev->curr_protocol == NFC_PROTO_ISO14443 && + st95context->fwi < 4) + st95context->fwi = 4; + + new_params[0].param_offset = 2; + if (nfcddev->curr_protocol == NFC_PROTO_ISO14443) + new_params[0].new_param_val = st95context->fwi; + else if (nfcddev->curr_protocol == NFC_PROTO_ISO14443_B) + new_params[0].new_param_val = pp_typeb; + + new_params[1].param_offset = 3; + new_params[1].new_param_val = wtxm; + + switch (nfcddev->curr_protocol) { + case NFC_PROTO_ISO14443: + result = st95hf_send_recv_cmd(st95context, + CMD_ISO14443A_PROTOCOL_SELECT, + 2, + new_params, + true); + if (result) { + dev_err(dev, "WTX type a sel proto, err = 0x%x\n", + result); + return result; + } + + /* secondary config. for 14443Type 4A after protocol select */ + result = secondary_configuration_type4a(st95context); + if (result) { + dev_err(dev, "WTX type a second. config, err = 0x%x\n", + result); + return result; + } + break; + case NFC_PROTO_ISO14443_B: + result = st95hf_send_recv_cmd(st95context, + CMD_ISO14443B_PROTOCOL_SELECT, + 2, + new_params, + true); + if (result) { + dev_err(dev, "WTX type b sel proto, err = 0x%x\n", + result); + return result; + } + + /* secondary config. for 14443Type 4B after protocol select */ + result = secondary_configuration_type4b(st95context); + if (result) { + dev_err(dev, "WTX type b second. config, err = 0x%x\n", + result); + return result; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int st95hf_handle_wtx(struct st95hf_context *stcontext, + bool new_wtx, + int wtx_val) +{ + int result = 0; + unsigned char val_mm = 0; + struct param_list new_params[1]; + struct nfc_digital_dev *nfcddev = stcontext->ddev; + struct device *dev = &stcontext->nfcdev->dev; + + if (new_wtx) { + result = iso14443_config_fdt(stcontext, wtx_val & 0x3f); + if (result) { + dev_err(dev, "Config. setting error on WTX req, err = 0x%x\n", + result); + return result; + } + + /* Send response of wtx with ASYNC as no response expected */ + new_params[0].param_offset = 1; + new_params[0].new_param_val = wtx_val; + + result = st95hf_send_recv_cmd(stcontext, + CMD_WTX_RESPONSE, + 1, + new_params, + false); + if (result) + dev_err(dev, "WTX response send, err = 0x%x\n", result); + return result; + } + + /* if no new wtx, cofigure with default values */ + if (nfcddev->curr_protocol == NFC_PROTO_ISO14443) + val_mm = cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[3]; + else if (nfcddev->curr_protocol == NFC_PROTO_ISO14443_B) + val_mm = cmd_array[CMD_ISO14443B_PROTOCOL_SELECT].cmd_params[3]; + + result = iso14443_config_fdt(stcontext, val_mm); + if (result) + dev_err(dev, "Default config. setting error after WTX processing, err = 0x%x\n", + result); + + return result; +} + +static int st95hf_error_handling(struct st95hf_context *stcontext, + struct sk_buff *skb_resp, + int res_len) +{ + int result = 0; + unsigned char error_byte; + struct device *dev = &stcontext->nfcdev->dev; + + /* First check ST95HF specific error */ + if (skb_resp->data[0] & ST95HF_ERR_MASK) { + if (skb_resp->data[0] == ST95HF_TIMEOUT_ERROR) + result = -ETIMEDOUT; + else + result = -EIO; + return result; + } + + /* Check for CRC err only if CRC is present in the tag response */ + switch (stcontext->current_rf_tech) { + case NFC_DIGITAL_RF_TECH_106A: + if (stcontext->sendrcv_trflag == TRFLAG_NFCA_STD_FRAME_CRC) { + error_byte = skb_resp->data[res_len - 3]; + if (error_byte & ST95HF_NFCA_CRC_ERR_MASK) { + /* CRC error occurred */ + dev_err(dev, "CRC error, byte received = 0x%x\n", + error_byte); + result = -EIO; + } + } + break; + case NFC_DIGITAL_RF_TECH_106B: + case NFC_DIGITAL_RF_TECH_ISO15693: + error_byte = skb_resp->data[res_len - 1]; + if (error_byte & ST95HF_NFCB_CRC_ERR_MASK) { + /* CRC error occurred */ + dev_err(dev, "CRC error, byte received = 0x%x\n", + error_byte); + result = -EIO; + } + break; + } + + return result; +} + +static int st95hf_response_handler(struct st95hf_context *stcontext, + struct sk_buff *skb_resp, + int res_len) +{ + int result = 0; + int skb_len; + unsigned char val_mm; + struct nfc_digital_dev *nfcddev = stcontext->ddev; + struct device *dev = &stcontext->nfcdev->dev; + struct st95_digital_cmd_complete_arg *cb_arg; + + cb_arg = &stcontext->complete_cb_arg; + + /* Process the response */ + skb_put(skb_resp, res_len); + + /* Remove st95 header */ + skb_pull(skb_resp, 2); + + skb_len = skb_resp->len; + + /* check if it is case of RATS request reply & FWI is present */ + if (nfcddev->curr_protocol == NFC_PROTO_ISO14443 && cb_arg->rats && + (skb_resp->data[1] & RATS_TB1_PRESENT_MASK)) { + if (skb_resp->data[1] & RATS_TA1_PRESENT_MASK) + stcontext->fwi = + (skb_resp->data[3] & TB1_FWI_MASK) >> 4; + else + stcontext->fwi = + (skb_resp->data[2] & TB1_FWI_MASK) >> 4; + + val_mm = cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[3]; + + result = iso14443_config_fdt(stcontext, val_mm); + if (result) { + dev_err(dev, "error in config_fdt to handle fwi of ATS, error=%d\n", + result); + return result; + } + } + cb_arg->rats = false; + + /* Remove CRC bytes only if received frames data has an eod (CRC) */ + switch (stcontext->current_rf_tech) { + case NFC_DIGITAL_RF_TECH_106A: + if (stcontext->sendrcv_trflag == TRFLAG_NFCA_STD_FRAME_CRC) + skb_trim(skb_resp, (skb_len - 5)); + else + skb_trim(skb_resp, (skb_len - 3)); + break; + case NFC_DIGITAL_RF_TECH_106B: + case NFC_DIGITAL_RF_TECH_ISO15693: + skb_trim(skb_resp, (skb_len - 3)); + break; + } + + return result; +} + +static irqreturn_t st95hf_irq_handler(int irq, void *st95hfcontext) +{ + struct st95hf_context *stcontext = + (struct st95hf_context *)st95hfcontext; + + if (stcontext->spicontext.req_issync) { + complete(&stcontext->spicontext.done); + stcontext->spicontext.req_issync = false; + return IRQ_HANDLED; + } + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t st95hf_irq_thread_handler(int irq, void *st95hfcontext) +{ + int result = 0; + int res_len; + static bool wtx; + struct device *dev; + struct device *spidevice; + struct nfc_digital_dev *nfcddev; + struct sk_buff *skb_resp; + struct st95hf_context *stcontext = + (struct st95hf_context *)st95hfcontext; + struct st95_digital_cmd_complete_arg *cb_arg; + + spidevice = &stcontext->spicontext.spidev->dev; + + /* + * check semaphore, if not down() already, then we don't + * know in which context the ISR is called and surely it + * will be a bug. Note that down() of the semaphore is done + * in the corresponding st95hf_in_send_cmd() and then + * only this ISR should be called. ISR will up() the + * semaphore before leaving. Hence when the ISR is called + * the correct behaviour is down_trylock() should always + * return 1 (indicating semaphore cant be taken and hence no + * change in semaphore count). + * If not, then we up() the semaphore and crash on + * a BUG() ! + */ + if (!down_trylock(&stcontext->exchange_lock)) { + up(&stcontext->exchange_lock); + WARN(1, "unknown context in ST95HF ISR"); + return IRQ_NONE; + } + + cb_arg = &stcontext->complete_cb_arg; + skb_resp = cb_arg->skb_resp; + + mutex_lock(&stcontext->rm_lock); + res_len = st95hf_spi_recv_response(&stcontext->spicontext, + skb_resp->data); + if (res_len < 0) { + dev_err(spidevice, "TISR spi response err = 0x%x\n", res_len); + result = res_len; + goto end; + } + + /* if stcontext->nfcdev_free is true, it means remove already ran */ + if (stcontext->nfcdev_free) { + result = -ENODEV; + goto end; + } + + dev = &stcontext->nfcdev->dev; + nfcddev = stcontext->ddev; + if (skb_resp->data[2] == WTX_REQ_FROM_TAG) { + /* Request for new FWT from tag */ + result = st95hf_handle_wtx(stcontext, true, skb_resp->data[3]); + if (result) + goto end; + + wtx = true; + mutex_unlock(&stcontext->rm_lock); + return IRQ_HANDLED; + } + + result = st95hf_error_handling(stcontext, skb_resp, res_len); + if (result) + goto end; + + result = st95hf_response_handler(stcontext, skb_resp, res_len); + if (result) + goto end; + + /* + * If select protocol is done on wtx req. do select protocol + * again with default values + */ + if (wtx) { + wtx = false; + result = st95hf_handle_wtx(stcontext, false, 0); + if (result) + goto end; + } + + /* call digital layer callback */ + cb_arg->complete_cb(stcontext->ddev, cb_arg->cb_usrarg, skb_resp); + + /* up the semaphore before returning */ + up(&stcontext->exchange_lock); + mutex_unlock(&stcontext->rm_lock); + + return IRQ_HANDLED; + +end: + kfree_skb(skb_resp); + wtx = false; + cb_arg->rats = false; + skb_resp = ERR_PTR(result); + /* call of callback with error */ + cb_arg->complete_cb(stcontext->ddev, cb_arg->cb_usrarg, skb_resp); + /* up the semaphore before returning */ + up(&stcontext->exchange_lock); + mutex_unlock(&stcontext->rm_lock); + return IRQ_HANDLED; +} + +/* NFC ops functions definition */ +static int st95hf_in_configure_hw(struct nfc_digital_dev *ddev, + int type, + int param) +{ + struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev); + + if (type == NFC_DIGITAL_CONFIG_RF_TECH) + return st95hf_select_protocol(stcontext, param); + + if (type == NFC_DIGITAL_CONFIG_FRAMING) { + switch (param) { + case NFC_DIGITAL_FRAMING_NFCA_SHORT: + stcontext->sendrcv_trflag = TRFLAG_NFCA_SHORT_FRAME; + break; + case NFC_DIGITAL_FRAMING_NFCA_STANDARD: + stcontext->sendrcv_trflag = TRFLAG_NFCA_STD_FRAME; + break; + case NFC_DIGITAL_FRAMING_NFCA_T4T: + case NFC_DIGITAL_FRAMING_NFCA_NFC_DEP: + case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A: + stcontext->sendrcv_trflag = TRFLAG_NFCA_STD_FRAME_CRC; + break; + case NFC_DIGITAL_FRAMING_NFCB: + case NFC_DIGITAL_FRAMING_ISO15693_INVENTORY: + case NFC_DIGITAL_FRAMING_ISO15693_T5T: + break; + } + } + + return 0; +} + +static int rf_off(struct st95hf_context *stcontext) +{ + int rc; + struct device *dev; + + dev = &stcontext->nfcdev->dev; + + rc = st95hf_send_recv_cmd(stcontext, CMD_FIELD_OFF, 0, NULL, true); + if (rc) + dev_err(dev, "protocol sel send field off, err = 0x%x\n", rc); + + return rc; +} + +static int st95hf_in_send_cmd(struct nfc_digital_dev *ddev, + struct sk_buff *skb, + u16 timeout, + nfc_digital_cmd_complete_t cb, + void *arg) +{ + struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev); + int rc; + struct sk_buff *skb_resp; + int len_data_to_tag = 0; + + skb_resp = nfc_alloc_recv_skb(MAX_RESPONSE_BUFFER_SIZE, GFP_KERNEL); + if (!skb_resp) { + rc = -ENOMEM; + goto error; + } + + switch (stcontext->current_rf_tech) { + case NFC_DIGITAL_RF_TECH_106A: + len_data_to_tag = skb->len + 1; + *skb_put(skb, 1) = stcontext->sendrcv_trflag; + break; + case NFC_DIGITAL_RF_TECH_106B: + case NFC_DIGITAL_RF_TECH_ISO15693: + len_data_to_tag = skb->len; + break; + default: + rc = -EINVAL; + goto free_skb_resp; + } + + skb_push(skb, 3); + skb->data[0] = ST95HF_COMMAND_SEND; + skb->data[1] = SEND_RECEIVE_CMD; + skb->data[2] = len_data_to_tag; + + stcontext->complete_cb_arg.skb_resp = skb_resp; + stcontext->complete_cb_arg.cb_usrarg = arg; + stcontext->complete_cb_arg.complete_cb = cb; + + if ((skb->data[3] == ISO14443A_RATS_REQ) && + ddev->curr_protocol == NFC_PROTO_ISO14443) + stcontext->complete_cb_arg.rats = true; + + /* + * down the semaphore to indicate to remove func that an + * ISR is pending, note that it will not block here in any case. + * If found blocked, it is a BUG! + */ + rc = down_killable(&stcontext->exchange_lock); + if (rc) { + WARN(1, "Semaphore is not found up in st95hf_in_send_cmd\n"); + return rc; + } + + rc = st95hf_spi_send(&stcontext->spicontext, skb->data, + skb->len, + ASYNC); + if (rc) { + dev_err(&stcontext->nfcdev->dev, + "Error %d trying to perform data_exchange", rc); + /* up the semaphore since ISR will never come in this case */ + up(&stcontext->exchange_lock); + goto free_skb_resp; + } + + kfree_skb(skb); + + return rc; + +free_skb_resp: + kfree_skb(skb_resp); +error: + return rc; +} + +/* p2p will be supported in a later release ! */ +static int st95hf_tg_configure_hw(struct nfc_digital_dev *ddev, + int type, + int param) +{ + return 0; +} + +static int st95hf_tg_send_cmd(struct nfc_digital_dev *ddev, + struct sk_buff *skb, + u16 timeout, + nfc_digital_cmd_complete_t cb, + void *arg) +{ + return 0; +} + +static int st95hf_tg_listen(struct nfc_digital_dev *ddev, + u16 timeout, + nfc_digital_cmd_complete_t cb, + void *arg) +{ + return 0; +} + +static int st95hf_tg_get_rf_tech(struct nfc_digital_dev *ddev, u8 *rf_tech) +{ + return 0; +} + +static int st95hf_switch_rf(struct nfc_digital_dev *ddev, bool on) +{ + u8 rf_tech; + struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev); + + rf_tech = ddev->curr_rf_tech; + + if (on) + /* switch on RF field */ + return st95hf_select_protocol(stcontext, rf_tech); + + /* switch OFF RF field */ + return rf_off(stcontext); +} + +/* TODO st95hf_abort_cmd */ +static void st95hf_abort_cmd(struct nfc_digital_dev *ddev) +{ +} + +static struct nfc_digital_ops st95hf_nfc_digital_ops = { + .in_configure_hw = st95hf_in_configure_hw, + .in_send_cmd = st95hf_in_send_cmd, + + .tg_listen = st95hf_tg_listen, + .tg_configure_hw = st95hf_tg_configure_hw, + .tg_send_cmd = st95hf_tg_send_cmd, + .tg_get_rf_tech = st95hf_tg_get_rf_tech, + + .switch_rf = st95hf_switch_rf, + .abort_cmd = st95hf_abort_cmd, +}; + +static const struct spi_device_id st95hf_id[] = { + { "st95hf", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, st95hf_id); + +static int st95hf_probe(struct spi_device *nfc_spi_dev) +{ + int ret; + + struct st95hf_context *st95context; + struct st95hf_spi_context *spicontext; + + nfc_info(&nfc_spi_dev->dev, "ST95HF driver probe called.\n"); + + st95context = devm_kzalloc(&nfc_spi_dev->dev, + sizeof(struct st95hf_context), + GFP_KERNEL); + if (!st95context) + return -ENOMEM; + + spicontext = &st95context->spicontext; + + spicontext->spidev = nfc_spi_dev; + + st95context->fwi = + cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[2]; + + if (device_property_present(&nfc_spi_dev->dev, "st95hfvin")) { + st95context->st95hf_supply = + devm_regulator_get(&nfc_spi_dev->dev, + "st95hfvin"); + if (IS_ERR(st95context->st95hf_supply)) { + dev_err(&nfc_spi_dev->dev, "failed to acquire regulator\n"); + return PTR_ERR(st95context->st95hf_supply); + } + + ret = regulator_enable(st95context->st95hf_supply); + if (ret) { + dev_err(&nfc_spi_dev->dev, "failed to enable regulator\n"); + return ret; + } + } + + init_completion(&spicontext->done); + mutex_init(&spicontext->spi_lock); + + /* + * Store spicontext in spi device object for using it in + * remove function + */ + dev_set_drvdata(&nfc_spi_dev->dev, spicontext); + + st95context->enable_gpio = + of_get_named_gpio(nfc_spi_dev->dev.of_node, + "enable-gpio", + 0); + if (!gpio_is_valid(st95context->enable_gpio)) { + dev_err(&nfc_spi_dev->dev, "No valid enable gpio\n"); + ret = st95context->enable_gpio; + goto err_disable_regulator; + } + + ret = devm_gpio_request_one(&nfc_spi_dev->dev, st95context->enable_gpio, + GPIOF_DIR_OUT | GPIOF_INIT_HIGH, + "enable_gpio"); + if (ret) + goto err_disable_regulator; + + if (nfc_spi_dev->irq > 0) { + if (devm_request_threaded_irq(&nfc_spi_dev->dev, + nfc_spi_dev->irq, + st95hf_irq_handler, + st95hf_irq_thread_handler, + IRQF_TRIGGER_FALLING, + "st95hf", + (void *)st95context) < 0) { + dev_err(&nfc_spi_dev->dev, "err: irq request for st95hf is failed\n"); + ret = -EINVAL; + goto err_disable_regulator; + } + } else { + dev_err(&nfc_spi_dev->dev, "not a valid IRQ associated with ST95HF\n"); + ret = -EINVAL; + goto err_disable_regulator; + } + + /* + * First reset SPI to handle warm reset of the system. + * It will put the ST95HF device in Power ON state + * which make the state of device identical to state + * at the time of cold reset of the system. + */ + ret = st95hf_send_spi_reset_sequence(st95context); + if (ret) { + dev_err(&nfc_spi_dev->dev, "err: spi_reset_sequence failed\n"); + goto err_disable_regulator; + } + + /* call PowerOnReset sequence of ST95hf to activate it */ + ret = st95hf_por_sequence(st95context); + if (ret) { + dev_err(&nfc_spi_dev->dev, "err: por seq failed for st95hf\n"); + goto err_disable_regulator; + } + + /* create NFC dev object and register with NFC Subsystem */ + st95context->ddev = nfc_digital_allocate_device(&st95hf_nfc_digital_ops, + ST95HF_SUPPORTED_PROT, + ST95HF_CAPABILITIES, + ST95HF_HEADROOM_LEN, + ST95HF_TAILROOM_LEN); + if (!st95context->ddev) { + ret = -ENOMEM; + goto err_disable_regulator; + } + + st95context->nfcdev = st95context->ddev->nfc_dev; + nfc_digital_set_parent_dev(st95context->ddev, &nfc_spi_dev->dev); + + ret = nfc_digital_register_device(st95context->ddev); + if (ret) { + dev_err(&st95context->nfcdev->dev, "st95hf registration failed\n"); + goto err_free_digital_device; + } + + /* store st95context in nfc device object */ + nfc_digital_set_drvdata(st95context->ddev, st95context); + + sema_init(&st95context->exchange_lock, 1); + mutex_init(&st95context->rm_lock); + + return ret; + +err_free_digital_device: + nfc_digital_free_device(st95context->ddev); +err_disable_regulator: + if (st95context->st95hf_supply) + regulator_disable(st95context->st95hf_supply); + + return ret; +} + +static int st95hf_remove(struct spi_device *nfc_spi_dev) +{ + int result = 0; + unsigned char reset_cmd = ST95HF_COMMAND_RESET; + struct st95hf_spi_context *spictx = dev_get_drvdata(&nfc_spi_dev->dev); + + struct st95hf_context *stcontext = container_of(spictx, + struct st95hf_context, + spicontext); + + mutex_lock(&stcontext->rm_lock); + + nfc_digital_unregister_device(stcontext->ddev); + nfc_digital_free_device(stcontext->ddev); + stcontext->nfcdev_free = true; + + mutex_unlock(&stcontext->rm_lock); + + /* if last in_send_cmd's ISR is pending, wait for it to finish */ + result = down_killable(&stcontext->exchange_lock); + if (result == -EINTR) + dev_err(&spictx->spidev->dev, "sleep for semaphore interrupted by signal\n"); + + /* next reset the ST95HF controller */ + result = st95hf_spi_send(&stcontext->spicontext, + &reset_cmd, + ST95HF_RESET_CMD_LEN, + ASYNC); + if (result) { + dev_err(&spictx->spidev->dev, + "ST95HF reset failed in remove() err = %d\n", result); + return result; + } + + /* wait for 3 ms to complete the controller reset process */ + usleep_range(3000, 4000); + + /* disable regulator */ + if (stcontext->st95hf_supply) + regulator_disable(stcontext->st95hf_supply); + + return result; +} + +/* Register as SPI protocol driver */ +static struct spi_driver st95hf_driver = { + .driver = { + .name = "st95hf", + .owner = THIS_MODULE, + }, + .id_table = st95hf_id, + .probe = st95hf_probe, + .remove = st95hf_remove, +}; + +module_spi_driver(st95hf_driver); + +MODULE_AUTHOR("Shikha Singh <shikha.singh@st.com>"); +MODULE_DESCRIPTION("ST NFC Transceiver ST95HF driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/nfc/st95hf/spi.c b/drivers/nfc/st95hf/spi.c new file mode 100644 index 000000000000..e2d3bbcc8c34 --- /dev/null +++ b/drivers/nfc/st95hf/spi.c @@ -0,0 +1,167 @@ +/* + * ---------------------------------------------------------------------------- + * drivers/nfc/st95hf/spi.c function definitions for SPI communication + * ---------------------------------------------------------------------------- + * Copyright (C) 2015 STMicroelectronics Pvt. Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "spi.h" + +/* Function to send user provided buffer to ST95HF through SPI */ +int st95hf_spi_send(struct st95hf_spi_context *spicontext, + unsigned char *buffertx, + int datalen, + enum req_type reqtype) +{ + struct spi_message m; + int result = 0; + struct spi_device *spidev = spicontext->spidev; + struct spi_transfer tx_transfer = { + .tx_buf = buffertx, + .len = datalen, + }; + + mutex_lock(&spicontext->spi_lock); + + if (reqtype == SYNC) { + spicontext->req_issync = true; + reinit_completion(&spicontext->done); + } else { + spicontext->req_issync = false; + } + + spi_message_init(&m); + spi_message_add_tail(&tx_transfer, &m); + + result = spi_sync(spidev, &m); + if (result) { + dev_err(&spidev->dev, "error: sending cmd to st95hf using SPI = %d\n", + result); + mutex_unlock(&spicontext->spi_lock); + return result; + } + + /* return for asynchronous or no-wait case */ + if (reqtype == ASYNC) { + mutex_unlock(&spicontext->spi_lock); + return 0; + } + + result = wait_for_completion_timeout(&spicontext->done, + msecs_to_jiffies(1000)); + /* check for timeout or success */ + if (!result) { + dev_err(&spidev->dev, "error: response not ready timeout\n"); + result = -ETIMEDOUT; + } else { + result = 0; + } + + mutex_unlock(&spicontext->spi_lock); + + return result; +} +EXPORT_SYMBOL_GPL(st95hf_spi_send); + +/* Function to Receive command Response */ +int st95hf_spi_recv_response(struct st95hf_spi_context *spicontext, + unsigned char *receivebuff) +{ + int len = 0; + struct spi_transfer tx_takedata; + struct spi_message m; + struct spi_device *spidev = spicontext->spidev; + unsigned char readdata_cmd = ST95HF_COMMAND_RECEIVE; + struct spi_transfer t[2] = { + {.tx_buf = &readdata_cmd, .len = 1,}, + {.rx_buf = receivebuff, .len = 2, .cs_change = 1,}, + }; + + int ret = 0; + + memset(&tx_takedata, 0x0, sizeof(struct spi_transfer)); + + mutex_lock(&spicontext->spi_lock); + + /* First spi transfer to know the length of valid data */ + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + ret = spi_sync(spidev, &m); + if (ret) { + dev_err(&spidev->dev, "spi_recv_resp, data length error = %d\n", + ret); + mutex_unlock(&spicontext->spi_lock); + return ret; + } + + /* As 2 bytes are already read */ + len = 2; + + /* Support of long frame */ + if (receivebuff[0] & 0x60) + len += (((receivebuff[0] & 0x60) >> 5) << 8) | receivebuff[1]; + else + len += receivebuff[1]; + + /* Now make a transfer to read only relevant bytes */ + tx_takedata.rx_buf = &receivebuff[2]; + tx_takedata.len = len - 2; + + spi_message_init(&m); + spi_message_add_tail(&tx_takedata, &m); + + ret = spi_sync(spidev, &m); + + mutex_unlock(&spicontext->spi_lock); + if (ret) { + dev_err(&spidev->dev, "spi_recv_resp, data read error = %d\n", + ret); + return ret; + } + + return len; +} +EXPORT_SYMBOL_GPL(st95hf_spi_recv_response); + +int st95hf_spi_recv_echo_res(struct st95hf_spi_context *spicontext, + unsigned char *receivebuff) +{ + unsigned char readdata_cmd = ST95HF_COMMAND_RECEIVE; + struct spi_transfer t[2] = { + {.tx_buf = &readdata_cmd, .len = 1,}, + {.rx_buf = receivebuff, .len = 1,}, + }; + struct spi_message m; + struct spi_device *spidev = spicontext->spidev; + int ret = 0; + + mutex_lock(&spicontext->spi_lock); + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + ret = spi_sync(spidev, &m); + + mutex_unlock(&spicontext->spi_lock); + + if (ret) + dev_err(&spidev->dev, "recv_echo_res, data read error = %d\n", + ret); + + return ret; +} +EXPORT_SYMBOL_GPL(st95hf_spi_recv_echo_res); diff --git a/drivers/nfc/st95hf/spi.h b/drivers/nfc/st95hf/spi.h new file mode 100644 index 000000000000..552d220747cd --- /dev/null +++ b/drivers/nfc/st95hf/spi.h @@ -0,0 +1,64 @@ +/* + * --------------------------------------------------------------------------- + * drivers/nfc/st95hf/spi.h functions declarations for SPI communication + * --------------------------------------------------------------------------- + * Copyright (C) 2015 STMicroelectronics – All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __LINUX_ST95HF_SPI_H +#define __LINUX_ST95HF_SPI_H + +#include <linux/spi/spi.h> + +/* Basic ST95HF SPI CMDs */ +#define ST95HF_COMMAND_SEND 0x0 +#define ST95HF_COMMAND_RESET 0x1 +#define ST95HF_COMMAND_RECEIVE 0x2 + +#define ST95HF_RESET_CMD_LEN 0x1 + +/* + * structure to contain st95hf spi communication specific information. + * @req_issync: true for synchronous calls. + * @spidev: st95hf spi device object. + * @done: completion structure to wait for st95hf response + * for synchronous calls. + * @spi_lock: mutex to allow only one spi transfer at a time. + */ +struct st95hf_spi_context { + bool req_issync; + struct spi_device *spidev; + struct completion done; + struct mutex spi_lock; +}; + +/* flag to differentiate synchronous & asynchronous spi request */ +enum req_type { + SYNC, + ASYNC, +}; + +int st95hf_spi_send(struct st95hf_spi_context *spicontext, + unsigned char *buffertx, + int datalen, + enum req_type reqtype); + +int st95hf_spi_recv_response(struct st95hf_spi_context *spicontext, + unsigned char *receivebuff); + +int st95hf_spi_recv_echo_res(struct st95hf_spi_context *spicontext, + unsigned char *receivebuff); + +#endif diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c index f857feb2b573..10842b7051b3 100644 --- a/drivers/nfc/trf7970a.c +++ b/drivers/nfc/trf7970a.c @@ -2139,7 +2139,7 @@ static int trf7970a_remove(struct spi_device *spi) #ifdef CONFIG_PM_SLEEP static int trf7970a_suspend(struct device *dev) { - struct spi_device *spi = container_of(dev, struct spi_device, dev); + struct spi_device *spi = to_spi_device(dev); struct trf7970a *trf = spi_get_drvdata(spi); dev_dbg(dev, "Suspend\n"); @@ -2155,7 +2155,7 @@ static int trf7970a_suspend(struct device *dev) static int trf7970a_resume(struct device *dev) { - struct spi_device *spi = container_of(dev, struct spi_device, dev); + struct spi_device *spi = to_spi_device(dev); struct trf7970a *trf = spi_get_drvdata(spi); int ret; @@ -2174,7 +2174,7 @@ static int trf7970a_resume(struct device *dev) #ifdef CONFIG_PM static int trf7970a_pm_runtime_suspend(struct device *dev) { - struct spi_device *spi = container_of(dev, struct spi_device, dev); + struct spi_device *spi = to_spi_device(dev); struct trf7970a *trf = spi_get_drvdata(spi); int ret; @@ -2191,7 +2191,7 @@ static int trf7970a_pm_runtime_suspend(struct device *dev) static int trf7970a_pm_runtime_resume(struct device *dev) { - struct spi_device *spi = container_of(dev, struct spi_device, dev); + struct spi_device *spi = to_spi_device(dev); struct trf7970a *trf = spi_get_drvdata(spi); int ret; |