Commit | Line | Data |
---|---|---|
060e309a | 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause |
dfc768fb SW |
2 | /* |
3 | * Copyright (c) 2011, 2012, Qualcomm Atheros Communications Inc. | |
4 | * Copyright (c) 2017, I2SE GmbH | |
dfc768fb SW |
5 | */ |
6 | ||
7 | /* This module implements the Qualcomm Atheros UART protocol for | |
8 | * kernel-based UART device; it is essentially an Ethernet-to-UART | |
9 | * serial converter; | |
10 | */ | |
11 | ||
12 | #include <linux/device.h> | |
13 | #include <linux/errno.h> | |
14 | #include <linux/etherdevice.h> | |
15 | #include <linux/if_arp.h> | |
16 | #include <linux/if_ether.h> | |
17 | #include <linux/jiffies.h> | |
18 | #include <linux/kernel.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/netdevice.h> | |
21 | #include <linux/of.h> | |
dfc768fb SW |
22 | #include <linux/of_net.h> |
23 | #include <linux/sched.h> | |
24 | #include <linux/serdev.h> | |
25 | #include <linux/skbuff.h> | |
26 | #include <linux/types.h> | |
27 | ||
28 | #include "qca_7k_common.h" | |
29 | ||
30 | #define QCAUART_DRV_VERSION "0.1.0" | |
31 | #define QCAUART_DRV_NAME "qcauart" | |
32 | #define QCAUART_TX_TIMEOUT (1 * HZ) | |
33 | ||
34 | struct qcauart { | |
35 | struct net_device *net_dev; | |
36 | spinlock_t lock; /* transmit lock */ | |
37 | struct work_struct tx_work; /* Flushes transmit buffer */ | |
38 | ||
39 | struct serdev_device *serdev; | |
40 | struct qcafrm_handle frm_handle; | |
41 | struct sk_buff *rx_skb; | |
42 | ||
43 | unsigned char *tx_head; /* pointer to next XMIT byte */ | |
44 | int tx_left; /* bytes left in XMIT queue */ | |
45 | unsigned char *tx_buffer; | |
46 | }; | |
47 | ||
475fc6e2 JSS |
48 | static ssize_t |
49 | qca_tty_receive(struct serdev_device *serdev, const u8 *data, size_t count) | |
dfc768fb SW |
50 | { |
51 | struct qcauart *qca = serdev_device_get_drvdata(serdev); | |
52 | struct net_device *netdev = qca->net_dev; | |
53 | struct net_device_stats *n_stats = &netdev->stats; | |
54 | size_t i; | |
55 | ||
56 | if (!qca->rx_skb) { | |
57 | qca->rx_skb = netdev_alloc_skb_ip_align(netdev, | |
58 | netdev->mtu + | |
59 | VLAN_ETH_HLEN); | |
60 | if (!qca->rx_skb) { | |
61 | n_stats->rx_errors++; | |
62 | n_stats->rx_dropped++; | |
63 | return 0; | |
64 | } | |
65 | } | |
66 | ||
67 | for (i = 0; i < count; i++) { | |
68 | s32 retcode; | |
69 | ||
70 | retcode = qcafrm_fsm_decode(&qca->frm_handle, | |
71 | qca->rx_skb->data, | |
72 | skb_tailroom(qca->rx_skb), | |
73 | data[i]); | |
74 | ||
75 | switch (retcode) { | |
76 | case QCAFRM_GATHER: | |
77 | case QCAFRM_NOHEAD: | |
78 | break; | |
79 | case QCAFRM_NOTAIL: | |
80 | netdev_dbg(netdev, "recv: no RX tail\n"); | |
81 | n_stats->rx_errors++; | |
82 | n_stats->rx_dropped++; | |
83 | break; | |
84 | case QCAFRM_INVLEN: | |
85 | netdev_dbg(netdev, "recv: invalid RX length\n"); | |
86 | n_stats->rx_errors++; | |
87 | n_stats->rx_dropped++; | |
88 | break; | |
89 | default: | |
90 | n_stats->rx_packets++; | |
91 | n_stats->rx_bytes += retcode; | |
92 | skb_put(qca->rx_skb, retcode); | |
93 | qca->rx_skb->protocol = eth_type_trans( | |
94 | qca->rx_skb, qca->rx_skb->dev); | |
429205da | 95 | skb_checksum_none_assert(qca->rx_skb); |
90f77c1c | 96 | netif_rx(qca->rx_skb); |
dfc768fb SW |
97 | qca->rx_skb = netdev_alloc_skb_ip_align(netdev, |
98 | netdev->mtu + | |
99 | VLAN_ETH_HLEN); | |
100 | if (!qca->rx_skb) { | |
101 | netdev_dbg(netdev, "recv: out of RX resources\n"); | |
102 | n_stats->rx_errors++; | |
103 | return i; | |
104 | } | |
105 | } | |
106 | } | |
107 | ||
108 | return i; | |
109 | } | |
110 | ||
111 | /* Write out any remaining transmit buffer. Scheduled when tty is writable */ | |
112 | static void qcauart_transmit(struct work_struct *work) | |
113 | { | |
114 | struct qcauart *qca = container_of(work, struct qcauart, tx_work); | |
115 | struct net_device_stats *n_stats = &qca->net_dev->stats; | |
116 | int written; | |
117 | ||
118 | spin_lock_bh(&qca->lock); | |
119 | ||
120 | /* First make sure we're connected. */ | |
121 | if (!netif_running(qca->net_dev)) { | |
122 | spin_unlock_bh(&qca->lock); | |
123 | return; | |
124 | } | |
125 | ||
126 | if (qca->tx_left <= 0) { | |
127 | /* Now serial buffer is almost free & we can start | |
128 | * transmission of another packet | |
129 | */ | |
130 | n_stats->tx_packets++; | |
131 | spin_unlock_bh(&qca->lock); | |
132 | netif_wake_queue(qca->net_dev); | |
133 | return; | |
134 | } | |
135 | ||
136 | written = serdev_device_write_buf(qca->serdev, qca->tx_head, | |
137 | qca->tx_left); | |
138 | if (written > 0) { | |
139 | qca->tx_left -= written; | |
140 | qca->tx_head += written; | |
141 | } | |
142 | spin_unlock_bh(&qca->lock); | |
143 | } | |
144 | ||
145 | /* Called by the driver when there's room for more data. | |
146 | * Schedule the transmit. | |
147 | */ | |
148 | static void qca_tty_wakeup(struct serdev_device *serdev) | |
149 | { | |
150 | struct qcauart *qca = serdev_device_get_drvdata(serdev); | |
151 | ||
152 | schedule_work(&qca->tx_work); | |
153 | } | |
154 | ||
715d0871 | 155 | static const struct serdev_device_ops qca_serdev_ops = { |
dfc768fb SW |
156 | .receive_buf = qca_tty_receive, |
157 | .write_wakeup = qca_tty_wakeup, | |
158 | }; | |
159 | ||
160 | static int qcauart_netdev_open(struct net_device *dev) | |
161 | { | |
162 | struct qcauart *qca = netdev_priv(dev); | |
163 | ||
164 | netif_start_queue(qca->net_dev); | |
165 | ||
166 | return 0; | |
167 | } | |
168 | ||
169 | static int qcauart_netdev_close(struct net_device *dev) | |
170 | { | |
171 | struct qcauart *qca = netdev_priv(dev); | |
172 | ||
173 | netif_stop_queue(dev); | |
174 | flush_work(&qca->tx_work); | |
175 | ||
176 | spin_lock_bh(&qca->lock); | |
177 | qca->tx_left = 0; | |
178 | spin_unlock_bh(&qca->lock); | |
179 | ||
180 | return 0; | |
181 | } | |
182 | ||
183 | static netdev_tx_t | |
184 | qcauart_netdev_xmit(struct sk_buff *skb, struct net_device *dev) | |
185 | { | |
186 | struct net_device_stats *n_stats = &dev->stats; | |
187 | struct qcauart *qca = netdev_priv(dev); | |
188 | u8 pad_len = 0; | |
189 | int written; | |
190 | u8 *pos; | |
191 | ||
192 | spin_lock(&qca->lock); | |
193 | ||
194 | WARN_ON(qca->tx_left); | |
195 | ||
196 | if (!netif_running(dev)) { | |
197 | spin_unlock(&qca->lock); | |
198 | netdev_warn(qca->net_dev, "xmit: iface is down\n"); | |
199 | goto out; | |
200 | } | |
201 | ||
202 | pos = qca->tx_buffer; | |
203 | ||
204 | if (skb->len < QCAFRM_MIN_LEN) | |
205 | pad_len = QCAFRM_MIN_LEN - skb->len; | |
206 | ||
207 | pos += qcafrm_create_header(pos, skb->len + pad_len); | |
208 | ||
209 | memcpy(pos, skb->data, skb->len); | |
210 | pos += skb->len; | |
211 | ||
212 | if (pad_len) { | |
213 | memset(pos, 0, pad_len); | |
214 | pos += pad_len; | |
215 | } | |
216 | ||
217 | pos += qcafrm_create_footer(pos); | |
218 | ||
219 | netif_stop_queue(qca->net_dev); | |
220 | ||
221 | written = serdev_device_write_buf(qca->serdev, qca->tx_buffer, | |
222 | pos - qca->tx_buffer); | |
223 | if (written > 0) { | |
224 | qca->tx_left = (pos - qca->tx_buffer) - written; | |
225 | qca->tx_head = qca->tx_buffer + written; | |
226 | n_stats->tx_bytes += written; | |
227 | } | |
228 | spin_unlock(&qca->lock); | |
229 | ||
230 | netif_trans_update(dev); | |
231 | out: | |
232 | dev_kfree_skb_any(skb); | |
233 | return NETDEV_TX_OK; | |
234 | } | |
235 | ||
0290bd29 | 236 | static void qcauart_netdev_tx_timeout(struct net_device *dev, unsigned int txqueue) |
dfc768fb SW |
237 | { |
238 | struct qcauart *qca = netdev_priv(dev); | |
239 | ||
240 | netdev_info(qca->net_dev, "Transmit timeout at %ld, latency %ld\n", | |
241 | jiffies, dev_trans_start(dev)); | |
242 | dev->stats.tx_errors++; | |
243 | dev->stats.tx_dropped++; | |
244 | } | |
245 | ||
246 | static int qcauart_netdev_init(struct net_device *dev) | |
247 | { | |
248 | struct qcauart *qca = netdev_priv(dev); | |
249 | size_t len; | |
250 | ||
251 | /* Finish setting up the device info. */ | |
252 | dev->mtu = QCAFRM_MAX_MTU; | |
253 | dev->type = ARPHRD_ETHER; | |
254 | ||
255 | len = QCAFRM_HEADER_LEN + QCAFRM_MAX_LEN + QCAFRM_FOOTER_LEN; | |
256 | qca->tx_buffer = devm_kmalloc(&qca->serdev->dev, len, GFP_KERNEL); | |
257 | if (!qca->tx_buffer) | |
258 | return -ENOMEM; | |
259 | ||
260 | qca->rx_skb = netdev_alloc_skb_ip_align(qca->net_dev, | |
261 | qca->net_dev->mtu + | |
262 | VLAN_ETH_HLEN); | |
263 | if (!qca->rx_skb) | |
264 | return -ENOBUFS; | |
265 | ||
266 | return 0; | |
267 | } | |
268 | ||
269 | static void qcauart_netdev_uninit(struct net_device *dev) | |
270 | { | |
271 | struct qcauart *qca = netdev_priv(dev); | |
272 | ||
399e06a5 | 273 | dev_kfree_skb(qca->rx_skb); |
dfc768fb SW |
274 | } |
275 | ||
276 | static const struct net_device_ops qcauart_netdev_ops = { | |
277 | .ndo_init = qcauart_netdev_init, | |
278 | .ndo_uninit = qcauart_netdev_uninit, | |
279 | .ndo_open = qcauart_netdev_open, | |
280 | .ndo_stop = qcauart_netdev_close, | |
281 | .ndo_start_xmit = qcauart_netdev_xmit, | |
282 | .ndo_set_mac_address = eth_mac_addr, | |
283 | .ndo_tx_timeout = qcauart_netdev_tx_timeout, | |
284 | .ndo_validate_addr = eth_validate_addr, | |
285 | }; | |
286 | ||
287 | static void qcauart_netdev_setup(struct net_device *dev) | |
288 | { | |
289 | dev->netdev_ops = &qcauart_netdev_ops; | |
290 | dev->watchdog_timeo = QCAUART_TX_TIMEOUT; | |
291 | dev->priv_flags &= ~IFF_TX_SKB_SHARING; | |
292 | dev->tx_queue_len = 100; | |
293 | ||
294 | /* MTU range: 46 - 1500 */ | |
295 | dev->min_mtu = QCAFRM_MIN_MTU; | |
296 | dev->max_mtu = QCAFRM_MAX_MTU; | |
297 | } | |
298 | ||
299 | static const struct of_device_id qca_uart_of_match[] = { | |
300 | { | |
301 | .compatible = "qca,qca7000", | |
302 | }, | |
303 | {} | |
304 | }; | |
305 | MODULE_DEVICE_TABLE(of, qca_uart_of_match); | |
306 | ||
307 | static int qca_uart_probe(struct serdev_device *serdev) | |
308 | { | |
309 | struct net_device *qcauart_dev = alloc_etherdev(sizeof(struct qcauart)); | |
310 | struct qcauart *qca; | |
dfc768fb SW |
311 | u32 speed = 115200; |
312 | int ret; | |
313 | ||
314 | if (!qcauart_dev) | |
315 | return -ENOMEM; | |
316 | ||
317 | qcauart_netdev_setup(qcauart_dev); | |
318 | SET_NETDEV_DEV(qcauart_dev, &serdev->dev); | |
319 | ||
320 | qca = netdev_priv(qcauart_dev); | |
321 | if (!qca) { | |
322 | pr_err("qca_uart: Fail to retrieve private structure\n"); | |
323 | ret = -ENOMEM; | |
324 | goto free; | |
325 | } | |
326 | qca->net_dev = qcauart_dev; | |
327 | qca->serdev = serdev; | |
328 | qcafrm_fsm_init_uart(&qca->frm_handle); | |
329 | ||
330 | spin_lock_init(&qca->lock); | |
331 | INIT_WORK(&qca->tx_work, qcauart_transmit); | |
332 | ||
333 | of_property_read_u32(serdev->dev.of_node, "current-speed", &speed); | |
334 | ||
9ca01b25 | 335 | ret = of_get_ethdev_address(serdev->dev.of_node, qca->net_dev); |
83216e39 | 336 | if (ret) { |
dfc768fb SW |
337 | eth_hw_addr_random(qca->net_dev); |
338 | dev_info(&serdev->dev, "Using random MAC address: %pM\n", | |
339 | qca->net_dev->dev_addr); | |
340 | } | |
341 | ||
342 | netif_carrier_on(qca->net_dev); | |
343 | serdev_device_set_drvdata(serdev, qca); | |
344 | serdev_device_set_client_ops(serdev, &qca_serdev_ops); | |
345 | ||
346 | ret = serdev_device_open(serdev); | |
347 | if (ret) { | |
348 | dev_err(&serdev->dev, "Unable to open device %s\n", | |
349 | qcauart_dev->name); | |
350 | goto free; | |
351 | } | |
352 | ||
353 | speed = serdev_device_set_baudrate(serdev, speed); | |
354 | dev_info(&serdev->dev, "Using baudrate: %u\n", speed); | |
355 | ||
356 | serdev_device_set_flow_control(serdev, false); | |
357 | ||
358 | ret = register_netdev(qcauart_dev); | |
359 | if (ret) { | |
360 | dev_err(&serdev->dev, "Unable to register net device %s\n", | |
361 | qcauart_dev->name); | |
362 | serdev_device_close(serdev); | |
363 | cancel_work_sync(&qca->tx_work); | |
364 | goto free; | |
365 | } | |
366 | ||
367 | return 0; | |
368 | ||
369 | free: | |
370 | free_netdev(qcauart_dev); | |
371 | return ret; | |
372 | } | |
373 | ||
374 | static void qca_uart_remove(struct serdev_device *serdev) | |
375 | { | |
376 | struct qcauart *qca = serdev_device_get_drvdata(serdev); | |
377 | ||
378 | unregister_netdev(qca->net_dev); | |
379 | ||
380 | /* Flush any pending characters in the driver. */ | |
381 | serdev_device_close(serdev); | |
382 | cancel_work_sync(&qca->tx_work); | |
383 | ||
384 | free_netdev(qca->net_dev); | |
385 | } | |
386 | ||
387 | static struct serdev_device_driver qca_uart_driver = { | |
388 | .probe = qca_uart_probe, | |
389 | .remove = qca_uart_remove, | |
390 | .driver = { | |
391 | .name = QCAUART_DRV_NAME, | |
537a6b99 | 392 | .of_match_table = qca_uart_of_match, |
dfc768fb SW |
393 | }, |
394 | }; | |
395 | ||
396 | module_serdev_device_driver(qca_uart_driver); | |
397 | ||
398 | MODULE_DESCRIPTION("Qualcomm Atheros QCA7000 UART Driver"); | |
399 | MODULE_AUTHOR("Qualcomm Atheros Communications"); | |
d7d5f0fc | 400 | MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>"); |
dfc768fb SW |
401 | MODULE_LICENSE("Dual BSD/GPL"); |
402 | MODULE_VERSION(QCAUART_DRV_VERSION); |