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