Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Stefan Wahren | 3397 | 99.24% | 2 | 40.00% |
Wei Yongjun | 24 | 0.70% | 1 | 20.00% |
Sebastian Andrzej Siewior | 1 | 0.03% | 1 | 20.00% |
Uwe Kleine-König | 1 | 0.03% | 1 | 20.00% |
Total | 3423 | 5 |
// SPDX-License-Identifier: GPL-2.0-only /* Copyright (C) 2021 in-tech smart charging GmbH * * driver is based on micrel/ks8851_spi.c */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/interrupt.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> #include <linux/cache.h> #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/spi/spi.h> #include <linux/of_net.h> #define MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \ NETIF_MSG_TIMER) #define DRV_NAME "mse102x" #define DET_CMD 0x0001 #define DET_SOF 0x0002 #define DET_DFT 0x55AA #define CMD_SHIFT 12 #define CMD_RTS (0x1 << CMD_SHIFT) #define CMD_CTR (0x2 << CMD_SHIFT) #define CMD_MASK GENMASK(15, CMD_SHIFT) #define LEN_MASK GENMASK(CMD_SHIFT - 1, 0) #define DET_CMD_LEN 4 #define DET_SOF_LEN 2 #define DET_DFT_LEN 2 #define MIN_FREQ_HZ 6000000 #define MAX_FREQ_HZ 7142857 struct mse102x_stats { u64 xfer_err; u64 invalid_cmd; u64 invalid_ctr; u64 invalid_dft; u64 invalid_len; u64 invalid_rts; u64 invalid_sof; u64 tx_timeout; }; static const char mse102x_gstrings_stats[][ETH_GSTRING_LEN] = { "SPI transfer errors", "Invalid command", "Invalid CTR", "Invalid DFT", "Invalid frame length", "Invalid RTS", "Invalid SOF", "TX timeout", }; struct mse102x_net { struct net_device *ndev; u8 rxd[8]; u8 txd[8]; u32 msg_enable ____cacheline_aligned; struct sk_buff_head txq; struct mse102x_stats stats; }; struct mse102x_net_spi { struct mse102x_net mse102x; struct mutex lock; /* Protect SPI frame transfer */ struct work_struct tx_work; struct spi_device *spidev; struct spi_message spi_msg; struct spi_transfer spi_xfer; #ifdef CONFIG_DEBUG_FS struct dentry *device_root; #endif }; #define to_mse102x_spi(mse) container_of((mse), struct mse102x_net_spi, mse102x) #ifdef CONFIG_DEBUG_FS static int mse102x_info_show(struct seq_file *s, void *what) { struct mse102x_net_spi *mses = s->private; seq_printf(s, "TX ring size : %u\n", skb_queue_len(&mses->mse102x.txq)); seq_printf(s, "IRQ : %d\n", mses->spidev->irq); seq_printf(s, "SPI effective speed : %lu\n", (unsigned long)mses->spi_xfer.effective_speed_hz); seq_printf(s, "SPI mode : %x\n", mses->spidev->mode); return 0; } DEFINE_SHOW_ATTRIBUTE(mse102x_info); static void mse102x_init_device_debugfs(struct mse102x_net_spi *mses) { mses->device_root = debugfs_create_dir(dev_name(&mses->mse102x.ndev->dev), NULL); debugfs_create_file("info", S_IFREG | 0444, mses->device_root, mses, &mse102x_info_fops); } static void mse102x_remove_device_debugfs(struct mse102x_net_spi *mses) { debugfs_remove_recursive(mses->device_root); } #else /* CONFIG_DEBUG_FS */ static void mse102x_init_device_debugfs(struct mse102x_net_spi *mses) { } static void mse102x_remove_device_debugfs(struct mse102x_net_spi *mses) { } #endif /* SPI register read/write calls. * * All these calls issue SPI transactions to access the chip's registers. They * all require that the necessary lock is held to prevent accesses when the * chip is busy transferring packet data. */ static void mse102x_tx_cmd_spi(struct mse102x_net *mse, u16 cmd) { struct mse102x_net_spi *mses = to_mse102x_spi(mse); struct spi_transfer *xfer = &mses->spi_xfer; struct spi_message *msg = &mses->spi_msg; __be16 txb[2]; int ret; txb[0] = cpu_to_be16(DET_CMD); txb[1] = cpu_to_be16(cmd); xfer->tx_buf = txb; xfer->rx_buf = NULL; xfer->len = DET_CMD_LEN; ret = spi_sync(mses->spidev, msg); if (ret < 0) { netdev_err(mse->ndev, "%s: spi_sync() failed: %d\n", __func__, ret); mse->stats.xfer_err++; } } static int mse102x_rx_cmd_spi(struct mse102x_net *mse, u8 *rxb) { struct mse102x_net_spi *mses = to_mse102x_spi(mse); struct spi_transfer *xfer = &mses->spi_xfer; struct spi_message *msg = &mses->spi_msg; __be16 *txb = (__be16 *)mse->txd; __be16 *cmd = (__be16 *)mse->rxd; u8 *trx = mse->rxd; int ret; txb[0] = 0; txb[1] = 0; xfer->tx_buf = txb; xfer->rx_buf = trx; xfer->len = DET_CMD_LEN; ret = spi_sync(mses->spidev, msg); if (ret < 0) { netdev_err(mse->ndev, "%s: spi_sync() failed: %d\n", __func__, ret); mse->stats.xfer_err++; } else if (*cmd != cpu_to_be16(DET_CMD)) { net_dbg_ratelimited("%s: Unexpected response (0x%04x)\n", __func__, *cmd); mse->stats.invalid_cmd++; ret = -EIO; } else { memcpy(rxb, trx + 2, 2); } return ret; } static inline void mse102x_push_header(struct sk_buff *skb) { __be16 *header = skb_push(skb, DET_SOF_LEN); *header = cpu_to_be16(DET_SOF); } static inline void mse102x_put_footer(struct sk_buff *skb) { __be16 *footer = skb_put(skb, DET_DFT_LEN); *footer = cpu_to_be16(DET_DFT); } static int mse102x_tx_frame_spi(struct mse102x_net *mse, struct sk_buff *txp, unsigned int pad) { struct mse102x_net_spi *mses = to_mse102x_spi(mse); struct spi_transfer *xfer = &mses->spi_xfer; struct spi_message *msg = &mses->spi_msg; struct sk_buff *tskb; int ret; netif_dbg(mse, tx_queued, mse->ndev, "%s: skb %p, %d@%p\n", __func__, txp, txp->len, txp->data); if ((skb_headroom(txp) < DET_SOF_LEN) || (skb_tailroom(txp) < DET_DFT_LEN + pad)) { tskb = skb_copy_expand(txp, DET_SOF_LEN, DET_DFT_LEN + pad, GFP_KERNEL); if (!tskb) return -ENOMEM; dev_kfree_skb(txp); txp = tskb; } mse102x_push_header(txp); if (pad) skb_put_zero(txp, pad); mse102x_put_footer(txp); xfer->tx_buf = txp->data; xfer->rx_buf = NULL; xfer->len = txp->len; ret = spi_sync(mses->spidev, msg); if (ret < 0) { netdev_err(mse->ndev, "%s: spi_sync() failed: %d\n", __func__, ret); mse->stats.xfer_err++; } return ret; } static int mse102x_rx_frame_spi(struct mse102x_net *mse, u8 *buff, unsigned int frame_len) { struct mse102x_net_spi *mses = to_mse102x_spi(mse); struct spi_transfer *xfer = &mses->spi_xfer; struct spi_message *msg = &mses->spi_msg; __be16 *sof = (__be16 *)buff; __be16 *dft = (__be16 *)(buff + DET_SOF_LEN + frame_len); int ret; xfer->rx_buf = buff; xfer->tx_buf = NULL; xfer->len = DET_SOF_LEN + frame_len + DET_DFT_LEN; ret = spi_sync(mses->spidev, msg); if (ret < 0) { netdev_err(mse->ndev, "%s: spi_sync() failed: %d\n", __func__, ret); mse->stats.xfer_err++; } else if (*sof != cpu_to_be16(DET_SOF)) { netdev_dbg(mse->ndev, "%s: SPI start of frame is invalid (0x%04x)\n", __func__, *sof); mse->stats.invalid_sof++; ret = -EIO; } else if (*dft != cpu_to_be16(DET_DFT)) { netdev_dbg(mse->ndev, "%s: SPI frame tail is invalid (0x%04x)\n", __func__, *dft); mse->stats.invalid_dft++; ret = -EIO; } return ret; } static void mse102x_dump_packet(const char *msg, int len, const char *data) { printk(KERN_DEBUG ": %s - packet len:%d\n", msg, len); print_hex_dump(KERN_DEBUG, "pk data: ", DUMP_PREFIX_OFFSET, 16, 1, data, len, true); } static void mse102x_rx_pkt_spi(struct mse102x_net *mse) { struct sk_buff *skb; unsigned int rxalign; unsigned int rxlen; __be16 rx = 0; u16 cmd_resp; u8 *rxpkt; int ret; mse102x_tx_cmd_spi(mse, CMD_CTR); ret = mse102x_rx_cmd_spi(mse, (u8 *)&rx); cmd_resp = be16_to_cpu(rx); if (ret || ((cmd_resp & CMD_MASK) != CMD_RTS)) { usleep_range(50, 100); mse102x_tx_cmd_spi(mse, CMD_CTR); ret = mse102x_rx_cmd_spi(mse, (u8 *)&rx); if (ret) return; cmd_resp = be16_to_cpu(rx); if ((cmd_resp & CMD_MASK) != CMD_RTS) { net_dbg_ratelimited("%s: Unexpected response (0x%04x)\n", __func__, cmd_resp); mse->stats.invalid_rts++; return; } net_dbg_ratelimited("%s: Unexpected response to first CMD\n", __func__); } rxlen = cmd_resp & LEN_MASK; if (!rxlen) { net_dbg_ratelimited("%s: No frame length defined\n", __func__); mse->stats.invalid_len++; return; } rxalign = ALIGN(rxlen + DET_SOF_LEN + DET_DFT_LEN, 4); skb = netdev_alloc_skb_ip_align(mse->ndev, rxalign); if (!skb) return; /* 2 bytes Start of frame (before ethernet header) * 2 bytes Data frame tail (after ethernet frame) * They are copied, but ignored. */ rxpkt = skb_put(skb, rxlen) - DET_SOF_LEN; if (mse102x_rx_frame_spi(mse, rxpkt, rxlen)) { mse->ndev->stats.rx_errors++; dev_kfree_skb(skb); return; } if (netif_msg_pktdata(mse)) mse102x_dump_packet(__func__, skb->len, skb->data); skb->protocol = eth_type_trans(skb, mse->ndev); netif_rx(skb); mse->ndev->stats.rx_packets++; mse->ndev->stats.rx_bytes += rxlen; } static int mse102x_tx_pkt_spi(struct mse102x_net *mse, struct sk_buff *txb, unsigned long work_timeout) { unsigned int pad = 0; __be16 rx = 0; u16 cmd_resp; int ret; bool first = true; if (txb->len < 60) pad = 60 - txb->len; while (1) { mse102x_tx_cmd_spi(mse, CMD_RTS | (txb->len + pad)); ret = mse102x_rx_cmd_spi(mse, (u8 *)&rx); cmd_resp = be16_to_cpu(rx); if (!ret) { /* ready to send frame ? */ if (cmd_resp == CMD_CTR) break; net_dbg_ratelimited("%s: Unexpected response (0x%04x)\n", __func__, cmd_resp); mse->stats.invalid_ctr++; } /* It's not predictable how long / many retries it takes to * send at least one packet, so TX timeouts are possible. * That's the reason why the netdev watchdog is not used here. */ if (time_after(jiffies, work_timeout)) return -ETIMEDOUT; if (first) { /* throttle at first issue */ netif_stop_queue(mse->ndev); /* fast retry */ usleep_range(50, 100); first = false; } else { msleep(20); } } ret = mse102x_tx_frame_spi(mse, txb, pad); if (ret) net_dbg_ratelimited("%s: Failed to send (%d), drop frame\n", __func__, ret); return ret; } #define TX_QUEUE_MAX 10 static void mse102x_tx_work(struct work_struct *work) { /* Make sure timeout is sufficient to transfer TX_QUEUE_MAX frames */ unsigned long work_timeout = jiffies + msecs_to_jiffies(1000); struct mse102x_net_spi *mses; struct mse102x_net *mse; struct sk_buff *txb; int ret = 0; mses = container_of(work, struct mse102x_net_spi, tx_work); mse = &mses->mse102x; while ((txb = skb_dequeue(&mse->txq))) { mutex_lock(&mses->lock); ret = mse102x_tx_pkt_spi(mse, txb, work_timeout); mutex_unlock(&mses->lock); if (ret) { mse->ndev->stats.tx_dropped++; } else { mse->ndev->stats.tx_bytes += txb->len; mse->ndev->stats.tx_packets++; } dev_kfree_skb(txb); } if (ret == -ETIMEDOUT) { if (netif_msg_timer(mse)) netdev_err(mse->ndev, "tx work timeout\n"); mse->stats.tx_timeout++; } netif_wake_queue(mse->ndev); } static netdev_tx_t mse102x_start_xmit_spi(struct sk_buff *skb, struct net_device *ndev) { struct mse102x_net *mse = netdev_priv(ndev); struct mse102x_net_spi *mses = to_mse102x_spi(mse); netif_dbg(mse, tx_queued, ndev, "%s: skb %p, %d@%p\n", __func__, skb, skb->len, skb->data); skb_queue_tail(&mse->txq, skb); if (skb_queue_len(&mse->txq) >= TX_QUEUE_MAX) netif_stop_queue(ndev); schedule_work(&mses->tx_work); return NETDEV_TX_OK; } static void mse102x_init_mac(struct mse102x_net *mse, struct device_node *np) { struct net_device *ndev = mse->ndev; int ret = of_get_ethdev_address(np, ndev); if (ret) { eth_hw_addr_random(ndev); netdev_err(ndev, "Using random MAC address: %pM\n", ndev->dev_addr); } } /* Assumption: this is called for every incoming packet */ static irqreturn_t mse102x_irq(int irq, void *_mse) { struct mse102x_net *mse = _mse; struct mse102x_net_spi *mses = to_mse102x_spi(mse); mutex_lock(&mses->lock); mse102x_rx_pkt_spi(mse); mutex_unlock(&mses->lock); return IRQ_HANDLED; } static int mse102x_net_open(struct net_device *ndev) { struct mse102x_net *mse = netdev_priv(ndev); int ret; ret = request_threaded_irq(ndev->irq, NULL, mse102x_irq, IRQF_ONESHOT, ndev->name, mse); if (ret < 0) { netdev_err(ndev, "Failed to get irq: %d\n", ret); return ret; } netif_dbg(mse, ifup, ndev, "opening\n"); netif_start_queue(ndev); netif_carrier_on(ndev); netif_dbg(mse, ifup, ndev, "network device up\n"); return 0; } static int mse102x_net_stop(struct net_device *ndev) { struct mse102x_net *mse = netdev_priv(ndev); struct mse102x_net_spi *mses = to_mse102x_spi(mse); netif_info(mse, ifdown, ndev, "shutting down\n"); netif_carrier_off(mse->ndev); /* stop any outstanding work */ flush_work(&mses->tx_work); netif_stop_queue(ndev); skb_queue_purge(&mse->txq); free_irq(ndev->irq, mse); return 0; } static const struct net_device_ops mse102x_netdev_ops = { .ndo_open = mse102x_net_open, .ndo_stop = mse102x_net_stop, .ndo_start_xmit = mse102x_start_xmit_spi, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; /* ethtool support */ static void mse102x_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *di) { strscpy(di->driver, DRV_NAME, sizeof(di->driver)); strscpy(di->bus_info, dev_name(ndev->dev.parent), sizeof(di->bus_info)); } static u32 mse102x_get_msglevel(struct net_device *ndev) { struct mse102x_net *mse = netdev_priv(ndev); return mse->msg_enable; } static void mse102x_set_msglevel(struct net_device *ndev, u32 to) { struct mse102x_net *mse = netdev_priv(ndev); mse->msg_enable = to; } static void mse102x_get_ethtool_stats(struct net_device *ndev, struct ethtool_stats *estats, u64 *data) { struct mse102x_net *mse = netdev_priv(ndev); struct mse102x_stats *st = &mse->stats; memcpy(data, st, ARRAY_SIZE(mse102x_gstrings_stats) * sizeof(u64)); } static void mse102x_get_strings(struct net_device *ndev, u32 stringset, u8 *buf) { switch (stringset) { case ETH_SS_STATS: memcpy(buf, &mse102x_gstrings_stats, sizeof(mse102x_gstrings_stats)); break; default: WARN_ON(1); break; } } static int mse102x_get_sset_count(struct net_device *ndev, int sset) { switch (sset) { case ETH_SS_STATS: return ARRAY_SIZE(mse102x_gstrings_stats); default: return -EINVAL; } } static const struct ethtool_ops mse102x_ethtool_ops = { .get_drvinfo = mse102x_get_drvinfo, .get_link = ethtool_op_get_link, .get_msglevel = mse102x_get_msglevel, .set_msglevel = mse102x_set_msglevel, .get_ethtool_stats = mse102x_get_ethtool_stats, .get_strings = mse102x_get_strings, .get_sset_count = mse102x_get_sset_count, }; /* driver bus management functions */ #ifdef CONFIG_PM_SLEEP static int mse102x_suspend(struct device *dev) { struct mse102x_net *mse = dev_get_drvdata(dev); struct net_device *ndev = mse->ndev; if (netif_running(ndev)) { netif_device_detach(ndev); mse102x_net_stop(ndev); } return 0; } static int mse102x_resume(struct device *dev) { struct mse102x_net *mse = dev_get_drvdata(dev); struct net_device *ndev = mse->ndev; if (netif_running(ndev)) { mse102x_net_open(ndev); netif_device_attach(ndev); } return 0; } #endif static SIMPLE_DEV_PM_OPS(mse102x_pm_ops, mse102x_suspend, mse102x_resume); static int mse102x_probe_spi(struct spi_device *spi) { struct device *dev = &spi->dev; struct mse102x_net_spi *mses; struct net_device *ndev; struct mse102x_net *mse; int ret; spi->bits_per_word = 8; spi->mode |= SPI_MODE_3; /* enforce minimum speed to ensure device functionality */ spi->master->min_speed_hz = MIN_FREQ_HZ; if (!spi->max_speed_hz) spi->max_speed_hz = MAX_FREQ_HZ; if (spi->max_speed_hz < MIN_FREQ_HZ || spi->max_speed_hz > MAX_FREQ_HZ) { dev_err(&spi->dev, "SPI max frequency out of range (min: %u, max: %u)\n", MIN_FREQ_HZ, MAX_FREQ_HZ); return -EINVAL; } ret = spi_setup(spi); if (ret < 0) { dev_err(&spi->dev, "Unable to setup SPI device: %d\n", ret); return ret; } ndev = devm_alloc_etherdev(dev, sizeof(struct mse102x_net_spi)); if (!ndev) return -ENOMEM; ndev->needed_tailroom += ALIGN(DET_DFT_LEN, 4); ndev->needed_headroom += ALIGN(DET_SOF_LEN, 4); ndev->priv_flags &= ~IFF_TX_SKB_SHARING; ndev->tx_queue_len = 100; mse = netdev_priv(ndev); mses = to_mse102x_spi(mse); mses->spidev = spi; mutex_init(&mses->lock); INIT_WORK(&mses->tx_work, mse102x_tx_work); /* initialise pre-made spi transfer messages */ spi_message_init(&mses->spi_msg); spi_message_add_tail(&mses->spi_xfer, &mses->spi_msg); ndev->irq = spi->irq; mse->ndev = ndev; /* set the default message enable */ mse->msg_enable = netif_msg_init(-1, MSG_DEFAULT); skb_queue_head_init(&mse->txq); SET_NETDEV_DEV(ndev, dev); dev_set_drvdata(dev, mse); netif_carrier_off(mse->ndev); ndev->netdev_ops = &mse102x_netdev_ops; ndev->ethtool_ops = &mse102x_ethtool_ops; mse102x_init_mac(mse, dev->of_node); ret = register_netdev(ndev); if (ret) { dev_err(dev, "failed to register network device: %d\n", ret); return ret; } mse102x_init_device_debugfs(mses); return 0; } static void mse102x_remove_spi(struct spi_device *spi) { struct mse102x_net *mse = dev_get_drvdata(&spi->dev); struct mse102x_net_spi *mses = to_mse102x_spi(mse); if (netif_msg_drv(mse)) dev_info(&spi->dev, "remove\n"); mse102x_remove_device_debugfs(mses); unregister_netdev(mse->ndev); } static const struct of_device_id mse102x_match_table[] = { { .compatible = "vertexcom,mse1021" }, { .compatible = "vertexcom,mse1022" }, { } }; MODULE_DEVICE_TABLE(of, mse102x_match_table); static const struct spi_device_id mse102x_ids[] = { { "mse1021" }, { "mse1022" }, { } }; MODULE_DEVICE_TABLE(spi, mse102x_ids); static struct spi_driver mse102x_driver = { .driver = { .name = DRV_NAME, .of_match_table = mse102x_match_table, .pm = &mse102x_pm_ops, }, .probe = mse102x_probe_spi, .remove = mse102x_remove_spi, .id_table = mse102x_ids, }; module_spi_driver(mse102x_driver); MODULE_DESCRIPTION("MSE102x Network driver"); MODULE_AUTHOR("Stefan Wahren <stefan.wahren@chargebyte.com>"); MODULE_LICENSE("GPL"); MODULE_ALIAS("spi:" DRV_NAME);
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1