18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * I2C Link Layer for Samsung S3FWRN5 NCI based Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Samsung Electrnoics 68c2ecf20Sopenharmony_ci * Robert Baldyga <r.baldyga@samsung.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/i2c.h> 108c2ecf20Sopenharmony_ci#include <linux/gpio.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> 138c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <net/nfc/nfc.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "s3fwrn5.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define S3FWRN5_I2C_DRIVER_NAME "s3fwrn5_i2c" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define S3FWRN5_I2C_MAX_PAYLOAD 32 238c2ecf20Sopenharmony_ci#define S3FWRN5_EN_WAIT_TIME 150 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct s3fwrn5_i2c_phy { 268c2ecf20Sopenharmony_ci struct i2c_client *i2c_dev; 278c2ecf20Sopenharmony_ci struct nci_dev *ndev; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci int gpio_en; 308c2ecf20Sopenharmony_ci int gpio_fw_wake; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci struct mutex mutex; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci enum s3fwrn5_mode mode; 358c2ecf20Sopenharmony_ci unsigned int irq_skip:1; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic void s3fwrn5_i2c_set_wake(void *phy_id, bool wake) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct s3fwrn5_i2c_phy *phy = phy_id; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci mutex_lock(&phy->mutex); 438c2ecf20Sopenharmony_ci gpio_set_value(phy->gpio_fw_wake, wake); 448c2ecf20Sopenharmony_ci msleep(S3FWRN5_EN_WAIT_TIME/2); 458c2ecf20Sopenharmony_ci mutex_unlock(&phy->mutex); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void s3fwrn5_i2c_set_mode(void *phy_id, enum s3fwrn5_mode mode) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct s3fwrn5_i2c_phy *phy = phy_id; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci mutex_lock(&phy->mutex); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (phy->mode == mode) 558c2ecf20Sopenharmony_ci goto out; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci phy->mode = mode; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci gpio_set_value(phy->gpio_en, 1); 608c2ecf20Sopenharmony_ci gpio_set_value(phy->gpio_fw_wake, 0); 618c2ecf20Sopenharmony_ci if (mode == S3FWRN5_MODE_FW) 628c2ecf20Sopenharmony_ci gpio_set_value(phy->gpio_fw_wake, 1); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (mode != S3FWRN5_MODE_COLD) { 658c2ecf20Sopenharmony_ci msleep(S3FWRN5_EN_WAIT_TIME); 668c2ecf20Sopenharmony_ci gpio_set_value(phy->gpio_en, 0); 678c2ecf20Sopenharmony_ci msleep(S3FWRN5_EN_WAIT_TIME/2); 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci phy->irq_skip = true; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ciout: 738c2ecf20Sopenharmony_ci mutex_unlock(&phy->mutex); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic enum s3fwrn5_mode s3fwrn5_i2c_get_mode(void *phy_id) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct s3fwrn5_i2c_phy *phy = phy_id; 798c2ecf20Sopenharmony_ci enum s3fwrn5_mode mode; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci mutex_lock(&phy->mutex); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci mode = phy->mode; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci mutex_unlock(&phy->mutex); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return mode; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int s3fwrn5_i2c_write(void *phy_id, struct sk_buff *skb) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct s3fwrn5_i2c_phy *phy = phy_id; 938c2ecf20Sopenharmony_ci int ret; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci mutex_lock(&phy->mutex); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci phy->irq_skip = false; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci ret = i2c_master_send(phy->i2c_dev, skb->data, skb->len); 1008c2ecf20Sopenharmony_ci if (ret == -EREMOTEIO) { 1018c2ecf20Sopenharmony_ci /* Retry, chip was in standby */ 1028c2ecf20Sopenharmony_ci usleep_range(110000, 120000); 1038c2ecf20Sopenharmony_ci ret = i2c_master_send(phy->i2c_dev, skb->data, skb->len); 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci mutex_unlock(&phy->mutex); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (ret < 0) 1098c2ecf20Sopenharmony_ci return ret; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (ret != skb->len) 1128c2ecf20Sopenharmony_ci return -EREMOTEIO; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return 0; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic const struct s3fwrn5_phy_ops i2c_phy_ops = { 1188c2ecf20Sopenharmony_ci .set_wake = s3fwrn5_i2c_set_wake, 1198c2ecf20Sopenharmony_ci .set_mode = s3fwrn5_i2c_set_mode, 1208c2ecf20Sopenharmony_ci .get_mode = s3fwrn5_i2c_get_mode, 1218c2ecf20Sopenharmony_ci .write = s3fwrn5_i2c_write, 1228c2ecf20Sopenharmony_ci}; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int s3fwrn5_i2c_read(struct s3fwrn5_i2c_phy *phy) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct sk_buff *skb; 1278c2ecf20Sopenharmony_ci size_t hdr_size; 1288c2ecf20Sopenharmony_ci size_t data_len; 1298c2ecf20Sopenharmony_ci char hdr[4]; 1308c2ecf20Sopenharmony_ci int ret; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci hdr_size = (phy->mode == S3FWRN5_MODE_NCI) ? 1338c2ecf20Sopenharmony_ci NCI_CTRL_HDR_SIZE : S3FWRN5_FW_HDR_SIZE; 1348c2ecf20Sopenharmony_ci ret = i2c_master_recv(phy->i2c_dev, hdr, hdr_size); 1358c2ecf20Sopenharmony_ci if (ret < 0) 1368c2ecf20Sopenharmony_ci return ret; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (ret < hdr_size) 1398c2ecf20Sopenharmony_ci return -EBADMSG; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci data_len = (phy->mode == S3FWRN5_MODE_NCI) ? 1428c2ecf20Sopenharmony_ci ((struct nci_ctrl_hdr *)hdr)->plen : 1438c2ecf20Sopenharmony_ci ((struct s3fwrn5_fw_header *)hdr)->len; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci skb = alloc_skb(hdr_size + data_len, GFP_KERNEL); 1468c2ecf20Sopenharmony_ci if (!skb) 1478c2ecf20Sopenharmony_ci return -ENOMEM; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci skb_put_data(skb, hdr, hdr_size); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (data_len == 0) 1528c2ecf20Sopenharmony_ci goto out; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci ret = i2c_master_recv(phy->i2c_dev, skb_put(skb, data_len), data_len); 1558c2ecf20Sopenharmony_ci if (ret != data_len) { 1568c2ecf20Sopenharmony_ci kfree_skb(skb); 1578c2ecf20Sopenharmony_ci return -EBADMSG; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ciout: 1618c2ecf20Sopenharmony_ci return s3fwrn5_recv_frame(phy->ndev, skb, phy->mode); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic irqreturn_t s3fwrn5_i2c_irq_thread_fn(int irq, void *phy_id) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct s3fwrn5_i2c_phy *phy = phy_id; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (!phy || !phy->ndev) { 1698c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 1708c2ecf20Sopenharmony_ci return IRQ_NONE; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci mutex_lock(&phy->mutex); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (phy->irq_skip) 1768c2ecf20Sopenharmony_ci goto out; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci switch (phy->mode) { 1798c2ecf20Sopenharmony_ci case S3FWRN5_MODE_NCI: 1808c2ecf20Sopenharmony_ci case S3FWRN5_MODE_FW: 1818c2ecf20Sopenharmony_ci s3fwrn5_i2c_read(phy); 1828c2ecf20Sopenharmony_ci break; 1838c2ecf20Sopenharmony_ci case S3FWRN5_MODE_COLD: 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ciout: 1888c2ecf20Sopenharmony_ci mutex_unlock(&phy->mutex); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int s3fwrn5_i2c_parse_dt(struct i2c_client *client) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct s3fwrn5_i2c_phy *phy = i2c_get_clientdata(client); 1968c2ecf20Sopenharmony_ci struct device_node *np = client->dev.of_node; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (!np) 1998c2ecf20Sopenharmony_ci return -ENODEV; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci phy->gpio_en = of_get_named_gpio(np, "en-gpios", 0); 2028c2ecf20Sopenharmony_ci if (!gpio_is_valid(phy->gpio_en)) { 2038c2ecf20Sopenharmony_ci /* Support also deprecated property */ 2048c2ecf20Sopenharmony_ci phy->gpio_en = of_get_named_gpio(np, "s3fwrn5,en-gpios", 0); 2058c2ecf20Sopenharmony_ci if (!gpio_is_valid(phy->gpio_en)) 2068c2ecf20Sopenharmony_ci return -ENODEV; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci phy->gpio_fw_wake = of_get_named_gpio(np, "wake-gpios", 0); 2108c2ecf20Sopenharmony_ci if (!gpio_is_valid(phy->gpio_fw_wake)) { 2118c2ecf20Sopenharmony_ci /* Support also deprecated property */ 2128c2ecf20Sopenharmony_ci phy->gpio_fw_wake = of_get_named_gpio(np, "s3fwrn5,fw-gpios", 0); 2138c2ecf20Sopenharmony_ci if (!gpio_is_valid(phy->gpio_fw_wake)) 2148c2ecf20Sopenharmony_ci return -ENODEV; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic int s3fwrn5_i2c_probe(struct i2c_client *client, 2218c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct s3fwrn5_i2c_phy *phy; 2248c2ecf20Sopenharmony_ci int ret; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci phy = devm_kzalloc(&client->dev, sizeof(*phy), GFP_KERNEL); 2278c2ecf20Sopenharmony_ci if (!phy) 2288c2ecf20Sopenharmony_ci return -ENOMEM; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci mutex_init(&phy->mutex); 2318c2ecf20Sopenharmony_ci phy->mode = S3FWRN5_MODE_COLD; 2328c2ecf20Sopenharmony_ci phy->irq_skip = true; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci phy->i2c_dev = client; 2358c2ecf20Sopenharmony_ci i2c_set_clientdata(client, phy); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci ret = s3fwrn5_i2c_parse_dt(client); 2388c2ecf20Sopenharmony_ci if (ret < 0) 2398c2ecf20Sopenharmony_ci return ret; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci ret = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_en, 2428c2ecf20Sopenharmony_ci GPIOF_OUT_INIT_HIGH, "s3fwrn5_en"); 2438c2ecf20Sopenharmony_ci if (ret < 0) 2448c2ecf20Sopenharmony_ci return ret; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci ret = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_fw_wake, 2478c2ecf20Sopenharmony_ci GPIOF_OUT_INIT_LOW, "s3fwrn5_fw_wake"); 2488c2ecf20Sopenharmony_ci if (ret < 0) 2498c2ecf20Sopenharmony_ci return ret; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci ret = s3fwrn5_probe(&phy->ndev, phy, &phy->i2c_dev->dev, &i2c_phy_ops, 2528c2ecf20Sopenharmony_ci S3FWRN5_I2C_MAX_PAYLOAD); 2538c2ecf20Sopenharmony_ci if (ret < 0) 2548c2ecf20Sopenharmony_ci return ret; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&client->dev, phy->i2c_dev->irq, NULL, 2578c2ecf20Sopenharmony_ci s3fwrn5_i2c_irq_thread_fn, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, 2588c2ecf20Sopenharmony_ci S3FWRN5_I2C_DRIVER_NAME, phy); 2598c2ecf20Sopenharmony_ci if (ret) 2608c2ecf20Sopenharmony_ci s3fwrn5_remove(phy->ndev); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return ret; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int s3fwrn5_i2c_remove(struct i2c_client *client) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct s3fwrn5_i2c_phy *phy = i2c_get_clientdata(client); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci s3fwrn5_remove(phy->ndev); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic const struct i2c_device_id s3fwrn5_i2c_id_table[] = { 2758c2ecf20Sopenharmony_ci {S3FWRN5_I2C_DRIVER_NAME, 0}, 2768c2ecf20Sopenharmony_ci {} 2778c2ecf20Sopenharmony_ci}; 2788c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, s3fwrn5_i2c_id_table); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic const struct of_device_id of_s3fwrn5_i2c_match[] = { 2818c2ecf20Sopenharmony_ci { .compatible = "samsung,s3fwrn5-i2c", }, 2828c2ecf20Sopenharmony_ci {} 2838c2ecf20Sopenharmony_ci}; 2848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_s3fwrn5_i2c_match); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic struct i2c_driver s3fwrn5_i2c_driver = { 2878c2ecf20Sopenharmony_ci .driver = { 2888c2ecf20Sopenharmony_ci .name = S3FWRN5_I2C_DRIVER_NAME, 2898c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(of_s3fwrn5_i2c_match), 2908c2ecf20Sopenharmony_ci }, 2918c2ecf20Sopenharmony_ci .probe = s3fwrn5_i2c_probe, 2928c2ecf20Sopenharmony_ci .remove = s3fwrn5_i2c_remove, 2938c2ecf20Sopenharmony_ci .id_table = s3fwrn5_i2c_id_table, 2948c2ecf20Sopenharmony_ci}; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cimodule_i2c_driver(s3fwrn5_i2c_driver); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2998c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("I2C driver for Samsung S3FWRN5"); 3008c2ecf20Sopenharmony_ciMODULE_AUTHOR("Robert Baldyga <r.baldyga@samsung.com>"); 301