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