cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

i2c.c (6468B)


      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>");