Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

NFC: nfcmrvl: add UART driver

Add support of Marvell NFC chip controlled over UART

Signed-off-by: Vincent Cuissard <cuissard@marvell.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>

authored by

Vincent Cuissard and committed by
Samuel Ortiz
e097dc62 dc14bdef

+284 -1
+29
Documentation/devicetree/bindings/net/nfc/nfcmrvl.txt
··· 1 + * Marvell International Ltd. NCI NFC Controller 2 + 3 + Required properties: 4 + - compatible: Should be "mrvl,nfc-uart". 5 + 6 + Optional SoC specific properties: 7 + - pinctrl-names: Contains only one value - "default". 8 + - pintctrl-0: Specifies the pin control groups used for this controller. 9 + - reset-n-io: Output GPIO pin used to reset the chip (active low). 10 + - hci-muxed: Specifies that the chip is muxing NCI over HCI frames. 11 + 12 + Optional UART-based chip specific properties: 13 + - flow-control: Specifies that the chip is using RTS/CTS. 14 + - break-control: Specifies that the chip needs specific break management. 15 + 16 + Example (for ARM-based BeagleBoard Black with 88W8887 on UART5): 17 + 18 + &uart5 { 19 + status = "okay"; 20 + 21 + nfcmrvluart: nfcmrvluart@5 { 22 + compatible = "mrvl,nfc-uart"; 23 + 24 + reset-n-io = <&gpio3 16 0>; 25 + 26 + hci-muxed; 27 + flow-control; 28 + } 29 + };
+11
drivers/nfc/nfcmrvl/Kconfig
··· 21 21 22 22 Say Y here to compile support for Marvell NFC-over-USB driver 23 23 into the kernel or say M to compile it as module. 24 + 25 + config NFC_MRVL_UART 26 + tristate "Marvell NFC-over-UART driver" 27 + depends on NFC_MRVL && NFC_NCI_UART 28 + help 29 + Marvell NFC-over-UART driver. 30 + 31 + This driver provides support for Marvell NFC-over-UART devices 32 + 33 + Say Y here to compile support for Marvell NFC-over-UART driver 34 + into the kernel or say M to compile it as module.
+3
drivers/nfc/nfcmrvl/Makefile
··· 7 7 8 8 nfcmrvl_usb-y += usb.o 9 9 obj-$(CONFIG_NFC_MRVL_USB) += nfcmrvl_usb.o 10 + 11 + nfcmrvl_uart-y += uart.o 12 + obj-$(CONFIG_NFC_MRVL_UART) += nfcmrvl_uart.o
+7
drivers/nfc/nfcmrvl/nfcmrvl.h
··· 43 43 #define NFCMRVL_HCI_OGF 0x81 44 44 #define NFCMRVL_HCI_OCF 0xFE 45 45 46 + enum nfcmrvl_phy { 47 + NFCMRVL_PHY_USB = 0, 48 + NFCMRVL_PHY_UART = 1, 49 + }; 50 + 46 51 47 52 struct nfcmrvl_private { 48 53 ··· 66 61 void *drv_data; 67 62 /* PHY device */ 68 63 struct device *dev; 64 + /* PHY type */ 65 + enum nfcmrvl_phy phy; 69 66 /* Low level driver ops */ 70 67 struct nfcmrvl_if_ops *if_ops; 71 68 };
+225
drivers/nfc/nfcmrvl/uart.c
··· 1 + /** 2 + * Marvell NFC-over-UART driver 3 + * 4 + * Copyright (C) 2015, Marvell International Ltd. 5 + * 6 + * This software file (the "File") is distributed by Marvell International 7 + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 8 + * (the "License"). You may use, redistribute and/or modify this File in 9 + * accordance with the terms and conditions of the License, a copy of which 10 + * is available on the worldwide web at 11 + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 12 + * 13 + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE 14 + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 15 + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about 16 + * this warranty disclaimer. 17 + */ 18 + 19 + #include <linux/module.h> 20 + #include <linux/delay.h> 21 + #include <linux/of_gpio.h> 22 + #include <net/nfc/nci.h> 23 + #include <net/nfc/nci_core.h> 24 + #include "nfcmrvl.h" 25 + 26 + static unsigned int hci_muxed; 27 + static unsigned int flow_control; 28 + static unsigned int break_control; 29 + static unsigned int reset_n_io; 30 + 31 + /* 32 + ** NFCMRVL NCI OPS 33 + */ 34 + 35 + static int nfcmrvl_uart_nci_open(struct nfcmrvl_private *priv) 36 + { 37 + return 0; 38 + } 39 + 40 + static int nfcmrvl_uart_nci_close(struct nfcmrvl_private *priv) 41 + { 42 + return 0; 43 + } 44 + 45 + static int nfcmrvl_uart_nci_send(struct nfcmrvl_private *priv, 46 + struct sk_buff *skb) 47 + { 48 + struct nci_uart *nu = priv->drv_data; 49 + 50 + return nu->ops.send(nu, skb); 51 + } 52 + 53 + static struct nfcmrvl_if_ops uart_ops = { 54 + .nci_open = nfcmrvl_uart_nci_open, 55 + .nci_close = nfcmrvl_uart_nci_close, 56 + .nci_send = nfcmrvl_uart_nci_send, 57 + }; 58 + 59 + #ifdef CONFIG_OF 60 + 61 + static int nfcmrvl_uart_parse_dt(struct device_node *node, 62 + struct nfcmrvl_platform_data *pdata) 63 + { 64 + struct device_node *matched_node; 65 + int ret; 66 + 67 + matched_node = of_find_compatible_node(node, NULL, "mrvl,nfc-uart"); 68 + if (!matched_node) 69 + return -ENODEV; 70 + 71 + ret = nfcmrvl_parse_dt(matched_node, pdata); 72 + if (ret < 0) { 73 + pr_err("Failed to get generic entries\n"); 74 + return ret; 75 + } 76 + 77 + if (of_find_property(matched_node, "flow-control", NULL)) 78 + pdata->flow_control = 1; 79 + else 80 + pdata->flow_control = 0; 81 + 82 + if (of_find_property(matched_node, "break-control", NULL)) 83 + pdata->break_control = 1; 84 + else 85 + pdata->break_control = 0; 86 + 87 + return 0; 88 + } 89 + 90 + #else 91 + 92 + static int nfcmrvl_uart_parse_dt(struct device_node *node, 93 + struct nfcmrvl_platform_data *pdata) 94 + { 95 + return -ENODEV; 96 + } 97 + 98 + #endif 99 + 100 + /* 101 + ** NCI UART OPS 102 + */ 103 + 104 + static int nfcmrvl_nci_uart_open(struct nci_uart *nu) 105 + { 106 + struct nfcmrvl_private *priv; 107 + struct nfcmrvl_platform_data *pdata = NULL; 108 + struct nfcmrvl_platform_data config; 109 + 110 + /* 111 + * Platform data cannot be used here since usually it is already used 112 + * by low level serial driver. We can try to retrieve serial device 113 + * and check if DT entries were added. 114 + */ 115 + 116 + if (nu->tty->dev->parent && nu->tty->dev->parent->of_node) 117 + if (nfcmrvl_uart_parse_dt(nu->tty->dev->parent->of_node, 118 + &config) == 0) 119 + pdata = &config; 120 + 121 + if (!pdata) { 122 + pr_info("No platform data / DT -> fallback to module params\n"); 123 + config.hci_muxed = hci_muxed; 124 + config.reset_n_io = reset_n_io; 125 + config.flow_control = flow_control; 126 + config.break_control = break_control; 127 + pdata = &config; 128 + } 129 + 130 + priv = nfcmrvl_nci_register_dev(nu, &uart_ops, nu->tty->dev, pdata); 131 + if (IS_ERR(priv)) 132 + return PTR_ERR(priv); 133 + 134 + priv->phy = NFCMRVL_PHY_UART; 135 + 136 + nu->drv_data = priv; 137 + nu->ndev = priv->ndev; 138 + 139 + /* Set BREAK */ 140 + if (priv->config.break_control && nu->tty->ops->break_ctl) 141 + nu->tty->ops->break_ctl(nu->tty, -1); 142 + 143 + return 0; 144 + } 145 + 146 + static void nfcmrvl_nci_uart_close(struct nci_uart *nu) 147 + { 148 + nfcmrvl_nci_unregister_dev((struct nfcmrvl_private *)nu->drv_data); 149 + } 150 + 151 + static int nfcmrvl_nci_uart_recv(struct nci_uart *nu, struct sk_buff *skb) 152 + { 153 + return nfcmrvl_nci_recv_frame((struct nfcmrvl_private *)nu->drv_data, 154 + skb); 155 + } 156 + 157 + static void nfcmrvl_nci_uart_tx_start(struct nci_uart *nu) 158 + { 159 + struct nfcmrvl_private *priv = (struct nfcmrvl_private *)nu->drv_data; 160 + 161 + /* Remove BREAK to wake up the NFCC */ 162 + if (priv->config.break_control && nu->tty->ops->break_ctl) { 163 + nu->tty->ops->break_ctl(nu->tty, 0); 164 + usleep_range(3000, 5000); 165 + } 166 + } 167 + 168 + static void nfcmrvl_nci_uart_tx_done(struct nci_uart *nu) 169 + { 170 + struct nfcmrvl_private *priv = (struct nfcmrvl_private *)nu->drv_data; 171 + 172 + /* 173 + ** To ensure that if the NFCC goes in DEEP SLEEP sate we can wake him 174 + ** up. we set BREAK. Once we will be ready to send again we will remove 175 + ** it. 176 + */ 177 + if (priv->config.break_control && nu->tty->ops->break_ctl) 178 + nu->tty->ops->break_ctl(nu->tty, -1); 179 + } 180 + 181 + static struct nci_uart nfcmrvl_nci_uart = { 182 + .owner = THIS_MODULE, 183 + .name = "nfcmrvl_uart", 184 + .driver = NCI_UART_DRIVER_MARVELL, 185 + .ops = { 186 + .open = nfcmrvl_nci_uart_open, 187 + .close = nfcmrvl_nci_uart_close, 188 + .recv = nfcmrvl_nci_uart_recv, 189 + .tx_start = nfcmrvl_nci_uart_tx_start, 190 + .tx_done = nfcmrvl_nci_uart_tx_done, 191 + } 192 + }; 193 + 194 + /* 195 + ** Module init 196 + */ 197 + 198 + static int nfcmrvl_uart_init_module(void) 199 + { 200 + return nci_uart_register(&nfcmrvl_nci_uart); 201 + } 202 + 203 + static void nfcmrvl_uart_exit_module(void) 204 + { 205 + nci_uart_unregister(&nfcmrvl_nci_uart); 206 + } 207 + 208 + module_init(nfcmrvl_uart_init_module); 209 + module_exit(nfcmrvl_uart_exit_module); 210 + 211 + MODULE_AUTHOR("Marvell International Ltd."); 212 + MODULE_DESCRIPTION("Marvell NFC-over-UART"); 213 + MODULE_LICENSE("GPL v2"); 214 + 215 + module_param(flow_control, uint, 0); 216 + MODULE_PARM_DESC(flow_control, "Tell if UART needs flow control at init."); 217 + 218 + module_param(break_control, uint, 0); 219 + MODULE_PARM_DESC(break_control, "Tell if UART driver must drive break signal."); 220 + 221 + module_param(hci_muxed, uint, 0); 222 + MODULE_PARM_DESC(hci_muxed, "Tell if transport is muxed in HCI one."); 223 + 224 + module_param(reset_n_io, uint, 0); 225 + MODULE_PARM_DESC(reset_n_io, "GPIO that is wired to RESET_N signal.");
+9
include/linux/platform_data/nfcmrvl.h
··· 26 26 unsigned int reset_n_io; 27 27 /* Tell if transport is muxed in HCI one */ 28 28 unsigned int hci_muxed; 29 + 30 + /* 31 + * UART specific 32 + */ 33 + 34 + /* Tell if UART needs flow control at init */ 35 + unsigned int flow_control; 36 + /* Tell if firmware supports break control for power management */ 37 + unsigned int break_control; 29 38 }; 30 39 31 40 #endif /* _NFCMRVL_PTF_H_ */
-1
net/nfc/nci/uart.c
··· 12 12 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 13 13 * ARE EXPRESSLY DISCLAIMED. The License provides additional details about 14 14 * this warranty disclaimer. 15 - 16 15 */ 17 16 18 17 /* Inspired (hugely) by HCI LDISC implementation in Bluetooth.