1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * I2C Link Layer for Samsung S3FWRN5 NCI based Driver 4 * 5 * Copyright (C) 2015 Samsung Electrnoics 6 * Robert Baldyga <r.baldyga@samsung.com> 7 */ 8 9 #include <linux/i2c.h> 10 #include <linux/gpio.h> 11 #include <linux/delay.h> 12 #include <linux/of_gpio.h> 13 #include <linux/of_irq.h> 14 #include <linux/module.h> 15 16 #include <net/nfc/nfc.h> 17 18 #include "s3fwrn5.h" 19 20 #define S3FWRN5_I2C_DRIVER_NAME "s3fwrn5_i2c" 21 22 #define S3FWRN5_EN_WAIT_TIME 150 23 24 struct s3fwrn5_i2c_phy { 25 struct i2c_client *i2c_dev; 26 struct nci_dev *ndev; 27 28 unsigned int gpio_en; 29 unsigned int gpio_fw_wake; 30 31 struct mutex mutex; 32 33 enum s3fwrn5_mode mode; 34 unsigned int irq_skip:1; 35 }; 36 37 static void s3fwrn5_i2c_set_wake(void *phy_id, bool wake) 38 { 39 struct s3fwrn5_i2c_phy *phy = phy_id; 40 41 mutex_lock(&phy->mutex); 42 gpio_set_value(phy->gpio_fw_wake, wake); 43 msleep(S3FWRN5_EN_WAIT_TIME/2); 44 mutex_unlock(&phy->mutex); 45 } 46 47 static void s3fwrn5_i2c_set_mode(void *phy_id, enum s3fwrn5_mode mode) 48 { 49 struct s3fwrn5_i2c_phy *phy = phy_id; 50 51 mutex_lock(&phy->mutex); 52 53 if (phy->mode == mode) 54 goto out; 55 56 phy->mode = mode; 57 58 gpio_set_value(phy->gpio_en, 1); 59 gpio_set_value(phy->gpio_fw_wake, 0); 60 if (mode == S3FWRN5_MODE_FW) 61 gpio_set_value(phy->gpio_fw_wake, 1); 62 63 if (mode != S3FWRN5_MODE_COLD) { 64 msleep(S3FWRN5_EN_WAIT_TIME); 65 gpio_set_value(phy->gpio_en, 0); 66 msleep(S3FWRN5_EN_WAIT_TIME/2); 67 } 68 69 phy->irq_skip = true; 70 71 out: 72 mutex_unlock(&phy->mutex); 73 } 74 75 static enum s3fwrn5_mode s3fwrn5_i2c_get_mode(void *phy_id) 76 { 77 struct s3fwrn5_i2c_phy *phy = phy_id; 78 enum s3fwrn5_mode mode; 79 80 mutex_lock(&phy->mutex); 81 82 mode = phy->mode; 83 84 mutex_unlock(&phy->mutex); 85 86 return mode; 87 } 88 89 static int s3fwrn5_i2c_write(void *phy_id, struct sk_buff *skb) 90 { 91 struct s3fwrn5_i2c_phy *phy = phy_id; 92 int ret; 93 94 mutex_lock(&phy->mutex); 95 96 phy->irq_skip = false; 97 98 ret = i2c_master_send(phy->i2c_dev, skb->data, skb->len); 99 if (ret == -EREMOTEIO) { 100 /* Retry, chip was in standby */ 101 usleep_range(110000, 120000); 102 ret = i2c_master_send(phy->i2c_dev, skb->data, skb->len); 103 } 104 105 mutex_unlock(&phy->mutex); 106 107 if (ret < 0) 108 return ret; 109 110 if (ret != skb->len) 111 return -EREMOTEIO; 112 113 return 0; 114 } 115 116 static const struct s3fwrn5_phy_ops i2c_phy_ops = { 117 .set_wake = s3fwrn5_i2c_set_wake, 118 .set_mode = s3fwrn5_i2c_set_mode, 119 .get_mode = s3fwrn5_i2c_get_mode, 120 .write = s3fwrn5_i2c_write, 121 }; 122 123 static int s3fwrn5_i2c_read(struct s3fwrn5_i2c_phy *phy) 124 { 125 struct sk_buff *skb; 126 size_t hdr_size; 127 size_t data_len; 128 char hdr[4]; 129 int ret; 130 131 hdr_size = (phy->mode == S3FWRN5_MODE_NCI) ? 132 NCI_CTRL_HDR_SIZE : S3FWRN5_FW_HDR_SIZE; 133 ret = i2c_master_recv(phy->i2c_dev, hdr, hdr_size); 134 if (ret < 0) 135 return ret; 136 137 if (ret < hdr_size) 138 return -EBADMSG; 139 140 data_len = (phy->mode == S3FWRN5_MODE_NCI) ? 141 ((struct nci_ctrl_hdr *)hdr)->plen : 142 ((struct s3fwrn5_fw_header *)hdr)->len; 143 144 skb = alloc_skb(hdr_size + data_len, GFP_KERNEL); 145 if (!skb) 146 return -ENOMEM; 147 148 skb_put_data(skb, hdr, hdr_size); 149 150 if (data_len == 0) 151 goto out; 152 153 ret = i2c_master_recv(phy->i2c_dev, skb_put(skb, data_len), data_len); 154 if (ret != data_len) { 155 kfree_skb(skb); 156 return -EBADMSG; 157 } 158 159 out: 160 return s3fwrn5_recv_frame(phy->ndev, skb, phy->mode); 161 } 162 163 static irqreturn_t s3fwrn5_i2c_irq_thread_fn(int irq, void *phy_id) 164 { 165 struct s3fwrn5_i2c_phy *phy = phy_id; 166 167 if (!phy || !phy->ndev) { 168 WARN_ON_ONCE(1); 169 return IRQ_NONE; 170 } 171 172 mutex_lock(&phy->mutex); 173 174 if (phy->irq_skip) 175 goto out; 176 177 switch (phy->mode) { 178 case S3FWRN5_MODE_NCI: 179 case S3FWRN5_MODE_FW: 180 s3fwrn5_i2c_read(phy); 181 break; 182 case S3FWRN5_MODE_COLD: 183 break; 184 } 185 186 out: 187 mutex_unlock(&phy->mutex); 188 189 return IRQ_HANDLED; 190 } 191 192 static int s3fwrn5_i2c_parse_dt(struct i2c_client *client) 193 { 194 struct s3fwrn5_i2c_phy *phy = i2c_get_clientdata(client); 195 struct device_node *np = client->dev.of_node; 196 197 if (!np) 198 return -ENODEV; 199 200 phy->gpio_en = of_get_named_gpio(np, "en-gpios", 0); 201 if (!gpio_is_valid(phy->gpio_en)) { 202 /* Support also deprecated property */ 203 phy->gpio_en = of_get_named_gpio(np, "s3fwrn5,en-gpios", 0); 204 if (!gpio_is_valid(phy->gpio_en)) 205 return -ENODEV; 206 } 207 208 phy->gpio_fw_wake = of_get_named_gpio(np, "wake-gpios", 0); 209 if (!gpio_is_valid(phy->gpio_fw_wake)) { 210 /* Support also deprecated property */ 211 phy->gpio_fw_wake = of_get_named_gpio(np, "s3fwrn5,fw-gpios", 0); 212 if (!gpio_is_valid(phy->gpio_fw_wake)) 213 return -ENODEV; 214 } 215 216 return 0; 217 } 218 219 static int s3fwrn5_i2c_probe(struct i2c_client *client, 220 const struct i2c_device_id *id) 221 { 222 struct s3fwrn5_i2c_phy *phy; 223 int ret; 224 225 phy = devm_kzalloc(&client->dev, sizeof(*phy), GFP_KERNEL); 226 if (!phy) 227 return -ENOMEM; 228 229 mutex_init(&phy->mutex); 230 phy->mode = S3FWRN5_MODE_COLD; 231 phy->irq_skip = true; 232 233 phy->i2c_dev = client; 234 i2c_set_clientdata(client, phy); 235 236 ret = s3fwrn5_i2c_parse_dt(client); 237 if (ret < 0) 238 return ret; 239 240 ret = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_en, 241 GPIOF_OUT_INIT_HIGH, "s3fwrn5_en"); 242 if (ret < 0) 243 return ret; 244 245 ret = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_fw_wake, 246 GPIOF_OUT_INIT_LOW, "s3fwrn5_fw_wake"); 247 if (ret < 0) 248 return ret; 249 250 ret = s3fwrn5_probe(&phy->ndev, phy, &phy->i2c_dev->dev, &i2c_phy_ops); 251 if (ret < 0) 252 return ret; 253 254 ret = devm_request_threaded_irq(&client->dev, phy->i2c_dev->irq, NULL, 255 s3fwrn5_i2c_irq_thread_fn, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, 256 S3FWRN5_I2C_DRIVER_NAME, phy); 257 if (ret) 258 s3fwrn5_remove(phy->ndev); 259 260 return ret; 261 } 262 263 static int s3fwrn5_i2c_remove(struct i2c_client *client) 264 { 265 struct s3fwrn5_i2c_phy *phy = i2c_get_clientdata(client); 266 267 s3fwrn5_remove(phy->ndev); 268 269 return 0; 270 } 271 272 static const struct i2c_device_id s3fwrn5_i2c_id_table[] = { 273 {S3FWRN5_I2C_DRIVER_NAME, 0}, 274 {} 275 }; 276 MODULE_DEVICE_TABLE(i2c, s3fwrn5_i2c_id_table); 277 278 static const struct of_device_id of_s3fwrn5_i2c_match[] = { 279 { .compatible = "samsung,s3fwrn5-i2c", }, 280 {} 281 }; 282 MODULE_DEVICE_TABLE(of, of_s3fwrn5_i2c_match); 283 284 static struct i2c_driver s3fwrn5_i2c_driver = { 285 .driver = { 286 .name = S3FWRN5_I2C_DRIVER_NAME, 287 .of_match_table = of_match_ptr(of_s3fwrn5_i2c_match), 288 }, 289 .probe = s3fwrn5_i2c_probe, 290 .remove = s3fwrn5_i2c_remove, 291 .id_table = s3fwrn5_i2c_id_table, 292 }; 293 294 module_i2c_driver(s3fwrn5_i2c_driver); 295 296 MODULE_LICENSE("GPL"); 297 MODULE_DESCRIPTION("I2C driver for Samsung S3FWRN5"); 298 MODULE_AUTHOR("Robert Baldyga <r.baldyga@samsung.com>"); 299