brintos

brintos / linux-shallow public Read only

0
0
Text · 9.3 KiB · 37efb1e Raw
403 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause2/*3 *   Copyright (c) 2011, 2012, Qualcomm Atheros Communications Inc.4 *   Copyright (c) 2017, I2SE GmbH5 */6 7/*   This module implements the Qualcomm Atheros UART protocol for8 *   kernel-based UART device; it is essentially an Ethernet-to-UART9 *   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 34struct 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 48static size_t49qca_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(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);95			skb_checksum_none_assert(qca->rx_skb);96			netif_rx(qca->rx_skb);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 */112static 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 start128		 * transmission of another packet129		 */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 */148static 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 155static const struct serdev_device_ops qca_serdev_ops = {156	.receive_buf = qca_tty_receive,157	.write_wakeup = qca_tty_wakeup,158};159 160static 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 169static 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 183static netdev_tx_t184qcauart_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);231out:232	dev_kfree_skb_any(skb);233	return NETDEV_TX_OK;234}235 236static void qcauart_netdev_tx_timeout(struct net_device *dev, unsigned int txqueue)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 246static 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 269static 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 276static 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 287static 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 299static const struct of_device_id qca_uart_of_match[] = {300	{301	 .compatible = "qca,qca7000",302	},303	{}304};305MODULE_DEVICE_TABLE(of, qca_uart_of_match);306 307static 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(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 335	ret = of_get_ethdev_address(serdev->dev.of_node, qca->net_dev);336	if (ret) {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 369free:370	free_netdev(qcauart_dev);371	return ret;372}373 374static 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 387static 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 396module_serdev_device_driver(qca_uart_driver);397 398MODULE_DESCRIPTION("Qualcomm Atheros QCA7000 UART Driver");399MODULE_AUTHOR("Qualcomm Atheros Communications");400MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>");401MODULE_LICENSE("Dual BSD/GPL");402MODULE_VERSION(QCAUART_DRV_VERSION);403