1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause |
2 | /* |
3 | * Copyright (c) 2011, 2012, Qualcomm Atheros Communications Inc. |
4 | * Copyright (c) 2017, I2SE GmbH |
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> |
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 | |
48 | static size_t |
49 | qca_tty_receive(struct serdev_device *serdev, const u8 *data, size_t count) |
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(dev: netdev, |
58 | length: 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(handle: &qca->frm_handle, |
71 | buf: qca->rx_skb->data, |
72 | buf_len: skb_tailroom(skb: qca->rx_skb), |
73 | recv_byte: 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(skb: qca->rx_skb, len: retcode); |
93 | qca->rx_skb->protocol = eth_type_trans( |
94 | skb: qca->rx_skb, dev: qca->rx_skb->dev); |
95 | skb_checksum_none_assert(skb: qca->rx_skb); |
96 | netif_rx(skb: qca->rx_skb); |
97 | qca->rx_skb = netdev_alloc_skb_ip_align(dev: netdev, |
98 | length: 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(lock: &qca->lock); |
119 | |
120 | /* First make sure we're connected. */ |
121 | if (!netif_running(dev: qca->net_dev)) { |
122 | spin_unlock_bh(lock: &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(lock: &qca->lock); |
132 | netif_wake_queue(dev: 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(lock: &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(work: &qca->tx_work); |
153 | } |
154 | |
155 | static const struct serdev_device_ops qca_serdev_ops = { |
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(dev: 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(work: &qca->tx_work); |
175 | |
176 | spin_lock_bh(lock: &qca->lock); |
177 | qca->tx_left = 0; |
178 | spin_unlock_bh(lock: &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(lock: &qca->lock); |
193 | |
194 | WARN_ON(qca->tx_left); |
195 | |
196 | if (!netif_running(dev)) { |
197 | spin_unlock(lock: &qca->lock); |
198 | netdev_warn(dev: qca->net_dev, format: "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(buf: pos, len: 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(buf: pos); |
218 | |
219 | netif_stop_queue(dev: 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(lock: &qca->lock); |
229 | |
230 | netif_trans_update(dev); |
231 | out: |
232 | dev_kfree_skb_any(skb); |
233 | return NETDEV_TX_OK; |
234 | } |
235 | |
236 | static void qcauart_netdev_tx_timeout(struct net_device *dev, unsigned int txqueue) |
237 | { |
238 | struct qcauart *qca = netdev_priv(dev); |
239 | |
240 | netdev_info(dev: qca->net_dev, format: "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(dev: &qca->serdev->dev, size: len, GFP_KERNEL); |
257 | if (!qca->tx_buffer) |
258 | return -ENOMEM; |
259 | |
260 | qca->rx_skb = netdev_alloc_skb_ip_align(dev: qca->net_dev, |
261 | length: 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 | |
273 | dev_kfree_skb(qca->rx_skb); |
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; |
311 | u32 speed = 115200; |
312 | int ret; |
313 | |
314 | if (!qcauart_dev) |
315 | return -ENOMEM; |
316 | |
317 | qcauart_netdev_setup(dev: qcauart_dev); |
318 | SET_NETDEV_DEV(qcauart_dev, &serdev->dev); |
319 | |
320 | qca = netdev_priv(dev: 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(handle: &qca->frm_handle); |
329 | |
330 | spin_lock_init(&qca->lock); |
331 | INIT_WORK(&qca->tx_work, qcauart_transmit); |
332 | |
333 | of_property_read_u32(np: serdev->dev.of_node, propname: "current-speed" , out_value: &speed); |
334 | |
335 | ret = of_get_ethdev_address(np: serdev->dev.of_node, dev: qca->net_dev); |
336 | if (ret) { |
337 | eth_hw_addr_random(dev: 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(dev: qca->net_dev); |
343 | serdev_device_set_drvdata(serdev, data: qca); |
344 | serdev_device_set_client_ops(serdev, ops: &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(dev: 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(work: &qca->tx_work); |
364 | goto free; |
365 | } |
366 | |
367 | return 0; |
368 | |
369 | free: |
370 | free_netdev(dev: 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(dev: qca->net_dev); |
379 | |
380 | /* Flush any pending characters in the driver. */ |
381 | serdev_device_close(serdev); |
382 | cancel_work_sync(work: &qca->tx_work); |
383 | |
384 | free_netdev(dev: 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, |
392 | .of_match_table = qca_uart_of_match, |
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" ); |
400 | MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>" ); |
401 | MODULE_LICENSE("Dual BSD/GPL" ); |
402 | MODULE_VERSION(QCAUART_DRV_VERSION); |
403 | |