162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * UART Link Layer for S3FWRN82 NCI based Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015 Samsung Electronics
662306a36Sopenharmony_ci * Robert Baldyga <r.baldyga@samsung.com>
762306a36Sopenharmony_ci * Copyright (C) 2020 Samsung Electronics
862306a36Sopenharmony_ci * Bongsu Jeon <bongsu.jeon@samsung.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/device.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/nfc.h>
1562306a36Sopenharmony_ci#include <linux/netdevice.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci#include <linux/serdev.h>
1862306a36Sopenharmony_ci#include <linux/gpio.h>
1962306a36Sopenharmony_ci#include <linux/of_gpio.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "phy_common.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define S3FWRN82_NCI_HEADER 3
2462306a36Sopenharmony_ci#define S3FWRN82_NCI_IDX 2
2562306a36Sopenharmony_ci#define NCI_SKB_BUFF_LEN 258
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct s3fwrn82_uart_phy {
2862306a36Sopenharmony_ci	struct phy_common common;
2962306a36Sopenharmony_ci	struct serdev_device *ser_dev;
3062306a36Sopenharmony_ci	struct sk_buff *recv_skb;
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic int s3fwrn82_uart_write(void *phy_id, struct sk_buff *out)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct s3fwrn82_uart_phy *phy = phy_id;
3662306a36Sopenharmony_ci	int err;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	err = serdev_device_write(phy->ser_dev,
3962306a36Sopenharmony_ci				  out->data, out->len,
4062306a36Sopenharmony_ci				  MAX_SCHEDULE_TIMEOUT);
4162306a36Sopenharmony_ci	if (err < 0)
4262306a36Sopenharmony_ci		return err;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return 0;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic const struct s3fwrn5_phy_ops uart_phy_ops = {
4862306a36Sopenharmony_ci	.set_wake = s3fwrn5_phy_set_wake,
4962306a36Sopenharmony_ci	.set_mode = s3fwrn5_phy_set_mode,
5062306a36Sopenharmony_ci	.get_mode = s3fwrn5_phy_get_mode,
5162306a36Sopenharmony_ci	.write = s3fwrn82_uart_write,
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic int s3fwrn82_uart_read(struct serdev_device *serdev,
5562306a36Sopenharmony_ci			      const unsigned char *data,
5662306a36Sopenharmony_ci			      size_t count)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev);
5962306a36Sopenharmony_ci	size_t i;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
6262306a36Sopenharmony_ci		skb_put_u8(phy->recv_skb, *data++);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci		if (phy->recv_skb->len < S3FWRN82_NCI_HEADER)
6562306a36Sopenharmony_ci			continue;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci		if ((phy->recv_skb->len - S3FWRN82_NCI_HEADER)
6862306a36Sopenharmony_ci				< phy->recv_skb->data[S3FWRN82_NCI_IDX])
6962306a36Sopenharmony_ci			continue;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		s3fwrn5_recv_frame(phy->common.ndev, phy->recv_skb,
7262306a36Sopenharmony_ci				   phy->common.mode);
7362306a36Sopenharmony_ci		phy->recv_skb = alloc_skb(NCI_SKB_BUFF_LEN, GFP_KERNEL);
7462306a36Sopenharmony_ci		if (!phy->recv_skb)
7562306a36Sopenharmony_ci			return 0;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return i;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic const struct serdev_device_ops s3fwrn82_serdev_ops = {
8262306a36Sopenharmony_ci	.receive_buf = s3fwrn82_uart_read,
8362306a36Sopenharmony_ci	.write_wakeup = serdev_device_write_wakeup,
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic const struct of_device_id s3fwrn82_uart_of_match[] = {
8762306a36Sopenharmony_ci	{ .compatible = "samsung,s3fwrn82", },
8862306a36Sopenharmony_ci	{},
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, s3fwrn82_uart_of_match);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic int s3fwrn82_uart_parse_dt(struct serdev_device *serdev)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev);
9562306a36Sopenharmony_ci	struct device_node *np = serdev->dev.of_node;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (!np)
9862306a36Sopenharmony_ci		return -ENODEV;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	phy->common.gpio_en = of_get_named_gpio(np, "en-gpios", 0);
10162306a36Sopenharmony_ci	if (!gpio_is_valid(phy->common.gpio_en))
10262306a36Sopenharmony_ci		return -ENODEV;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	phy->common.gpio_fw_wake = of_get_named_gpio(np, "wake-gpios", 0);
10562306a36Sopenharmony_ci	if (!gpio_is_valid(phy->common.gpio_fw_wake))
10662306a36Sopenharmony_ci		return -ENODEV;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return 0;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic int s3fwrn82_uart_probe(struct serdev_device *serdev)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct s3fwrn82_uart_phy *phy;
11462306a36Sopenharmony_ci	int ret = -ENOMEM;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	phy = devm_kzalloc(&serdev->dev, sizeof(*phy), GFP_KERNEL);
11762306a36Sopenharmony_ci	if (!phy)
11862306a36Sopenharmony_ci		goto err_exit;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	phy->recv_skb = alloc_skb(NCI_SKB_BUFF_LEN, GFP_KERNEL);
12162306a36Sopenharmony_ci	if (!phy->recv_skb)
12262306a36Sopenharmony_ci		goto err_exit;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	mutex_init(&phy->common.mutex);
12562306a36Sopenharmony_ci	phy->common.mode = S3FWRN5_MODE_COLD;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	phy->ser_dev = serdev;
12862306a36Sopenharmony_ci	serdev_device_set_drvdata(serdev, phy);
12962306a36Sopenharmony_ci	serdev_device_set_client_ops(serdev, &s3fwrn82_serdev_ops);
13062306a36Sopenharmony_ci	ret = serdev_device_open(serdev);
13162306a36Sopenharmony_ci	if (ret) {
13262306a36Sopenharmony_ci		dev_err(&serdev->dev, "Unable to open device\n");
13362306a36Sopenharmony_ci		goto err_skb;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	ret = serdev_device_set_baudrate(serdev, 115200);
13762306a36Sopenharmony_ci	if (ret != 115200) {
13862306a36Sopenharmony_ci		ret = -EINVAL;
13962306a36Sopenharmony_ci		goto err_serdev;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	serdev_device_set_flow_control(serdev, false);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	ret = s3fwrn82_uart_parse_dt(serdev);
14562306a36Sopenharmony_ci	if (ret < 0)
14662306a36Sopenharmony_ci		goto err_serdev;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	ret = devm_gpio_request_one(&phy->ser_dev->dev, phy->common.gpio_en,
14962306a36Sopenharmony_ci				    GPIOF_OUT_INIT_HIGH, "s3fwrn82_en");
15062306a36Sopenharmony_ci	if (ret < 0)
15162306a36Sopenharmony_ci		goto err_serdev;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	ret = devm_gpio_request_one(&phy->ser_dev->dev,
15462306a36Sopenharmony_ci				    phy->common.gpio_fw_wake,
15562306a36Sopenharmony_ci				    GPIOF_OUT_INIT_LOW, "s3fwrn82_fw_wake");
15662306a36Sopenharmony_ci	if (ret < 0)
15762306a36Sopenharmony_ci		goto err_serdev;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	ret = s3fwrn5_probe(&phy->common.ndev, phy, &phy->ser_dev->dev,
16062306a36Sopenharmony_ci			    &uart_phy_ops);
16162306a36Sopenharmony_ci	if (ret < 0)
16262306a36Sopenharmony_ci		goto err_serdev;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return ret;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cierr_serdev:
16762306a36Sopenharmony_ci	serdev_device_close(serdev);
16862306a36Sopenharmony_cierr_skb:
16962306a36Sopenharmony_ci	kfree_skb(phy->recv_skb);
17062306a36Sopenharmony_cierr_exit:
17162306a36Sopenharmony_ci	return ret;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic void s3fwrn82_uart_remove(struct serdev_device *serdev)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	s3fwrn5_remove(phy->common.ndev);
17962306a36Sopenharmony_ci	serdev_device_close(serdev);
18062306a36Sopenharmony_ci	kfree_skb(phy->recv_skb);
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic struct serdev_device_driver s3fwrn82_uart_driver = {
18462306a36Sopenharmony_ci	.probe = s3fwrn82_uart_probe,
18562306a36Sopenharmony_ci	.remove = s3fwrn82_uart_remove,
18662306a36Sopenharmony_ci	.driver = {
18762306a36Sopenharmony_ci		.name = "s3fwrn82_uart",
18862306a36Sopenharmony_ci		.of_match_table = s3fwrn82_uart_of_match,
18962306a36Sopenharmony_ci	},
19062306a36Sopenharmony_ci};
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cimodule_serdev_device_driver(s3fwrn82_uart_driver);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
19562306a36Sopenharmony_ciMODULE_DESCRIPTION("UART driver for Samsung NFC");
19662306a36Sopenharmony_ciMODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>");
197