xref: /openbmc/linux/drivers/nfc/nfcmrvl/uart.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1be3d162aSKrzysztof Kozlowski // SPDX-License-Identifier: GPL-2.0-only
24dd649d1SAditya Srivastava /*
3e097dc62SVincent Cuissard  * Marvell NFC-over-UART driver
4e097dc62SVincent Cuissard  *
5e097dc62SVincent Cuissard  * Copyright (C) 2015, Marvell International Ltd.
6e097dc62SVincent Cuissard  */
7e097dc62SVincent Cuissard 
8e097dc62SVincent Cuissard #include <linux/module.h>
9e097dc62SVincent Cuissard #include <linux/delay.h>
10e097dc62SVincent Cuissard #include <linux/of_gpio.h>
11e097dc62SVincent Cuissard #include <net/nfc/nci.h>
12e097dc62SVincent Cuissard #include <net/nfc/nci_core.h>
13e097dc62SVincent Cuissard #include "nfcmrvl.h"
14e097dc62SVincent Cuissard 
15e097dc62SVincent Cuissard static unsigned int hci_muxed;
16e097dc62SVincent Cuissard static unsigned int flow_control;
17e097dc62SVincent Cuissard static unsigned int break_control;
18c3953a3cSJohan Hovold static int reset_n_io = -EINVAL;
19e097dc62SVincent Cuissard 
20e097dc62SVincent Cuissard /*
218f99528eSKrzysztof Kozlowski  * NFCMRVL NCI OPS
22e097dc62SVincent Cuissard  */
23e097dc62SVincent Cuissard 
nfcmrvl_uart_nci_open(struct nfcmrvl_private * priv)24e097dc62SVincent Cuissard static int nfcmrvl_uart_nci_open(struct nfcmrvl_private *priv)
25e097dc62SVincent Cuissard {
26e097dc62SVincent Cuissard 	return 0;
27e097dc62SVincent Cuissard }
28e097dc62SVincent Cuissard 
nfcmrvl_uart_nci_close(struct nfcmrvl_private * priv)29e097dc62SVincent Cuissard static int nfcmrvl_uart_nci_close(struct nfcmrvl_private *priv)
30e097dc62SVincent Cuissard {
31e097dc62SVincent Cuissard 	return 0;
32e097dc62SVincent Cuissard }
33e097dc62SVincent Cuissard 
nfcmrvl_uart_nci_send(struct nfcmrvl_private * priv,struct sk_buff * skb)34e097dc62SVincent Cuissard static int nfcmrvl_uart_nci_send(struct nfcmrvl_private *priv,
35e097dc62SVincent Cuissard 				 struct sk_buff *skb)
36e097dc62SVincent Cuissard {
37e097dc62SVincent Cuissard 	struct nci_uart *nu = priv->drv_data;
38e097dc62SVincent Cuissard 
39e097dc62SVincent Cuissard 	return nu->ops.send(nu, skb);
40e097dc62SVincent Cuissard }
41e097dc62SVincent Cuissard 
nfcmrvl_uart_nci_update_config(struct nfcmrvl_private * priv,const void * param)423194c687SVincent Cuissard static void nfcmrvl_uart_nci_update_config(struct nfcmrvl_private *priv,
433194c687SVincent Cuissard 					   const void *param)
443194c687SVincent Cuissard {
453194c687SVincent Cuissard 	struct nci_uart *nu = priv->drv_data;
463194c687SVincent Cuissard 	const struct nfcmrvl_fw_uart_config *config = param;
473194c687SVincent Cuissard 
483194c687SVincent Cuissard 	nci_uart_set_config(nu, le32_to_cpu(config->baudrate),
493194c687SVincent Cuissard 			    config->flow_control);
503194c687SVincent Cuissard }
513194c687SVincent Cuissard 
5226955037SKrzysztof Kozlowski static const struct nfcmrvl_if_ops uart_ops = {
53e097dc62SVincent Cuissard 	.nci_open = nfcmrvl_uart_nci_open,
54e097dc62SVincent Cuissard 	.nci_close = nfcmrvl_uart_nci_close,
55e097dc62SVincent Cuissard 	.nci_send = nfcmrvl_uart_nci_send,
563194c687SVincent Cuissard 	.nci_update_config = nfcmrvl_uart_nci_update_config
57e097dc62SVincent Cuissard };
58e097dc62SVincent Cuissard 
nfcmrvl_uart_parse_dt(struct device_node * node,struct nfcmrvl_platform_data * pdata)59e097dc62SVincent Cuissard static int nfcmrvl_uart_parse_dt(struct device_node *node,
60e097dc62SVincent Cuissard 				 struct nfcmrvl_platform_data *pdata)
61e097dc62SVincent Cuissard {
62e097dc62SVincent Cuissard 	struct device_node *matched_node;
63e097dc62SVincent Cuissard 	int ret;
64e097dc62SVincent Cuissard 
655bf59773SJohan Hovold 	matched_node = of_get_compatible_child(node, "marvell,nfc-uart");
66d8e018c0SVincent Cuissard 	if (!matched_node) {
675bf59773SJohan Hovold 		matched_node = of_get_compatible_child(node, "mrvl,nfc-uart");
68e097dc62SVincent Cuissard 		if (!matched_node)
69e097dc62SVincent Cuissard 			return -ENODEV;
70d8e018c0SVincent Cuissard 	}
71e097dc62SVincent Cuissard 
72e097dc62SVincent Cuissard 	ret = nfcmrvl_parse_dt(matched_node, pdata);
73e097dc62SVincent Cuissard 	if (ret < 0) {
74e097dc62SVincent Cuissard 		pr_err("Failed to get generic entries\n");
75d0607aa4SJohan Hovold 		of_node_put(matched_node);
76e097dc62SVincent Cuissard 		return ret;
77e097dc62SVincent Cuissard 	}
78e097dc62SVincent Cuissard 
79*cc6d85c1SRob Herring 	pdata->flow_control = of_property_read_bool(matched_node, "flow-control");
80*cc6d85c1SRob Herring 	pdata->break_control = of_property_read_bool(matched_node, "break-control");
81e097dc62SVincent Cuissard 
82d0607aa4SJohan Hovold 	of_node_put(matched_node);
83d0607aa4SJohan Hovold 
84e097dc62SVincent Cuissard 	return 0;
85e097dc62SVincent Cuissard }
86e097dc62SVincent Cuissard 
87e097dc62SVincent Cuissard /*
888f99528eSKrzysztof Kozlowski  * NCI UART OPS
89e097dc62SVincent Cuissard  */
90e097dc62SVincent Cuissard 
nfcmrvl_nci_uart_open(struct nci_uart * nu)91e097dc62SVincent Cuissard static int nfcmrvl_nci_uart_open(struct nci_uart *nu)
92e097dc62SVincent Cuissard {
93e097dc62SVincent Cuissard 	struct nfcmrvl_private *priv;
94e097dc62SVincent Cuissard 	struct nfcmrvl_platform_data config;
95fe53159fSKrzysztof Kozlowski 	const struct nfcmrvl_platform_data *pdata = NULL;
9615e0c59fSJohan Hovold 	struct device *dev = nu->tty->dev;
97e097dc62SVincent Cuissard 
98e097dc62SVincent Cuissard 	/*
99e097dc62SVincent Cuissard 	 * Platform data cannot be used here since usually it is already used
100e097dc62SVincent Cuissard 	 * by low level serial driver. We can try to retrieve serial device
101e097dc62SVincent Cuissard 	 * and check if DT entries were added.
102e097dc62SVincent Cuissard 	 */
103e097dc62SVincent Cuissard 
10415e0c59fSJohan Hovold 	if (dev && dev->parent && dev->parent->of_node)
10515e0c59fSJohan Hovold 		if (nfcmrvl_uart_parse_dt(dev->parent->of_node, &config) == 0)
106e097dc62SVincent Cuissard 			pdata = &config;
107e097dc62SVincent Cuissard 
108e097dc62SVincent Cuissard 	if (!pdata) {
109e097dc62SVincent Cuissard 		pr_info("No platform data / DT -> fallback to module params\n");
110e097dc62SVincent Cuissard 		config.hci_muxed = hci_muxed;
111e097dc62SVincent Cuissard 		config.reset_n_io = reset_n_io;
112e097dc62SVincent Cuissard 		config.flow_control = flow_control;
113e097dc62SVincent Cuissard 		config.break_control = break_control;
114e097dc62SVincent Cuissard 		pdata = &config;
115e097dc62SVincent Cuissard 	}
116e097dc62SVincent Cuissard 
11758d34aa6SVincent Cuissard 	priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_UART, nu, &uart_ops,
11815e0c59fSJohan Hovold 					dev, pdata);
119e097dc62SVincent Cuissard 	if (IS_ERR(priv))
120e097dc62SVincent Cuissard 		return PTR_ERR(priv);
121e097dc62SVincent Cuissard 
1223194c687SVincent Cuissard 	priv->support_fw_dnld = true;
123e097dc62SVincent Cuissard 
124e097dc62SVincent Cuissard 	nu->drv_data = priv;
125e097dc62SVincent Cuissard 	nu->ndev = priv->ndev;
126e097dc62SVincent Cuissard 
127e097dc62SVincent Cuissard 	return 0;
128e097dc62SVincent Cuissard }
129e097dc62SVincent Cuissard 
nfcmrvl_nci_uart_close(struct nci_uart * nu)130e097dc62SVincent Cuissard static void nfcmrvl_nci_uart_close(struct nci_uart *nu)
131e097dc62SVincent Cuissard {
132e097dc62SVincent Cuissard 	nfcmrvl_nci_unregister_dev((struct nfcmrvl_private *)nu->drv_data);
133e097dc62SVincent Cuissard }
134e097dc62SVincent Cuissard 
nfcmrvl_nci_uart_recv(struct nci_uart * nu,struct sk_buff * skb)135e097dc62SVincent Cuissard static int nfcmrvl_nci_uart_recv(struct nci_uart *nu, struct sk_buff *skb)
136e097dc62SVincent Cuissard {
137e097dc62SVincent Cuissard 	return nfcmrvl_nci_recv_frame((struct nfcmrvl_private *)nu->drv_data,
138e097dc62SVincent Cuissard 				      skb);
139e097dc62SVincent Cuissard }
140e097dc62SVincent Cuissard 
nfcmrvl_nci_uart_tx_start(struct nci_uart * nu)141e097dc62SVincent Cuissard static void nfcmrvl_nci_uart_tx_start(struct nci_uart *nu)
142e097dc62SVincent Cuissard {
143e097dc62SVincent Cuissard 	struct nfcmrvl_private *priv = (struct nfcmrvl_private *)nu->drv_data;
144e097dc62SVincent Cuissard 
145feacf002SVincent Cuissard 	if (priv->ndev->nfc_dev->fw_download_in_progress)
146feacf002SVincent Cuissard 		return;
147feacf002SVincent Cuissard 
148e097dc62SVincent Cuissard 	/* Remove BREAK to wake up the NFCC */
149e097dc62SVincent Cuissard 	if (priv->config.break_control && nu->tty->ops->break_ctl) {
150e097dc62SVincent Cuissard 		nu->tty->ops->break_ctl(nu->tty, 0);
151e097dc62SVincent Cuissard 		usleep_range(3000, 5000);
152e097dc62SVincent Cuissard 	}
153e097dc62SVincent Cuissard }
154e097dc62SVincent Cuissard 
nfcmrvl_nci_uart_tx_done(struct nci_uart * nu)155e097dc62SVincent Cuissard static void nfcmrvl_nci_uart_tx_done(struct nci_uart *nu)
156e097dc62SVincent Cuissard {
157e097dc62SVincent Cuissard 	struct nfcmrvl_private *priv = (struct nfcmrvl_private *)nu->drv_data;
158e097dc62SVincent Cuissard 
159feacf002SVincent Cuissard 	if (priv->ndev->nfc_dev->fw_download_in_progress)
160feacf002SVincent Cuissard 		return;
161feacf002SVincent Cuissard 
162e097dc62SVincent Cuissard 	/*
1638f99528eSKrzysztof Kozlowski 	 * To ensure that if the NFCC goes in DEEP SLEEP sate we can wake him
1648f99528eSKrzysztof Kozlowski 	 * up. we set BREAK. Once we will be ready to send again we will remove
1658f99528eSKrzysztof Kozlowski 	 * it.
166e097dc62SVincent Cuissard 	 */
167d2d2e645SVincent Cuissard 	if (priv->config.break_control && nu->tty->ops->break_ctl) {
168e097dc62SVincent Cuissard 		nu->tty->ops->break_ctl(nu->tty, -1);
169d2d2e645SVincent Cuissard 		usleep_range(1000, 3000);
170d2d2e645SVincent Cuissard 	}
171e097dc62SVincent Cuissard }
172e097dc62SVincent Cuissard 
173e097dc62SVincent Cuissard static struct nci_uart nfcmrvl_nci_uart = {
174e097dc62SVincent Cuissard 	.owner  = THIS_MODULE,
175e097dc62SVincent Cuissard 	.name   = "nfcmrvl_uart",
176e097dc62SVincent Cuissard 	.driver = NCI_UART_DRIVER_MARVELL,
177e097dc62SVincent Cuissard 	.ops	= {
178e097dc62SVincent Cuissard 		.open		= nfcmrvl_nci_uart_open,
179e097dc62SVincent Cuissard 		.close		= nfcmrvl_nci_uart_close,
180e097dc62SVincent Cuissard 		.recv		= nfcmrvl_nci_uart_recv,
181e097dc62SVincent Cuissard 		.tx_start	= nfcmrvl_nci_uart_tx_start,
182e097dc62SVincent Cuissard 		.tx_done	= nfcmrvl_nci_uart_tx_done,
183e097dc62SVincent Cuissard 	}
184e097dc62SVincent Cuissard };
185e3bf5531SKrzysztof Kozlowski module_driver(nfcmrvl_nci_uart, nci_uart_register, nci_uart_unregister);
186e097dc62SVincent Cuissard 
187e097dc62SVincent Cuissard MODULE_AUTHOR("Marvell International Ltd.");
188e097dc62SVincent Cuissard MODULE_DESCRIPTION("Marvell NFC-over-UART");
189e097dc62SVincent Cuissard MODULE_LICENSE("GPL v2");
190e097dc62SVincent Cuissard 
191e097dc62SVincent Cuissard module_param(flow_control, uint, 0);
192e097dc62SVincent Cuissard MODULE_PARM_DESC(flow_control, "Tell if UART needs flow control at init.");
193e097dc62SVincent Cuissard 
194e097dc62SVincent Cuissard module_param(break_control, uint, 0);
195e097dc62SVincent Cuissard MODULE_PARM_DESC(break_control, "Tell if UART driver must drive break signal.");
196e097dc62SVincent Cuissard 
197e097dc62SVincent Cuissard module_param(hci_muxed, uint, 0);
198e097dc62SVincent Cuissard MODULE_PARM_DESC(hci_muxed, "Tell if transport is muxed in HCI one.");
199e097dc62SVincent Cuissard 
200c3953a3cSJohan Hovold module_param(reset_n_io, int, 0);
201e097dc62SVincent Cuissard MODULE_PARM_DESC(reset_n_io, "GPIO that is wired to RESET_N signal.");
202