162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright (C) 2021 in-tech smart charging GmbH 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * driver is based on micrel/ks8851_spi.c 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/interrupt.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/netdevice.h> 1362306a36Sopenharmony_ci#include <linux/etherdevice.h> 1462306a36Sopenharmony_ci#include <linux/ethtool.h> 1562306a36Sopenharmony_ci#include <linux/cache.h> 1662306a36Sopenharmony_ci#include <linux/debugfs.h> 1762306a36Sopenharmony_ci#include <linux/seq_file.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/spi/spi.h> 2062306a36Sopenharmony_ci#include <linux/of_net.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \ 2362306a36Sopenharmony_ci NETIF_MSG_TIMER) 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define DRV_NAME "mse102x" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define DET_CMD 0x0001 2862306a36Sopenharmony_ci#define DET_SOF 0x0002 2962306a36Sopenharmony_ci#define DET_DFT 0x55AA 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define CMD_SHIFT 12 3262306a36Sopenharmony_ci#define CMD_RTS (0x1 << CMD_SHIFT) 3362306a36Sopenharmony_ci#define CMD_CTR (0x2 << CMD_SHIFT) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define CMD_MASK GENMASK(15, CMD_SHIFT) 3662306a36Sopenharmony_ci#define LEN_MASK GENMASK(CMD_SHIFT - 1, 0) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define DET_CMD_LEN 4 3962306a36Sopenharmony_ci#define DET_SOF_LEN 2 4062306a36Sopenharmony_ci#define DET_DFT_LEN 2 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define MIN_FREQ_HZ 6000000 4362306a36Sopenharmony_ci#define MAX_FREQ_HZ 7142857 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistruct mse102x_stats { 4662306a36Sopenharmony_ci u64 xfer_err; 4762306a36Sopenharmony_ci u64 invalid_cmd; 4862306a36Sopenharmony_ci u64 invalid_ctr; 4962306a36Sopenharmony_ci u64 invalid_dft; 5062306a36Sopenharmony_ci u64 invalid_len; 5162306a36Sopenharmony_ci u64 invalid_rts; 5262306a36Sopenharmony_ci u64 invalid_sof; 5362306a36Sopenharmony_ci u64 tx_timeout; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic const char mse102x_gstrings_stats[][ETH_GSTRING_LEN] = { 5762306a36Sopenharmony_ci "SPI transfer errors", 5862306a36Sopenharmony_ci "Invalid command", 5962306a36Sopenharmony_ci "Invalid CTR", 6062306a36Sopenharmony_ci "Invalid DFT", 6162306a36Sopenharmony_ci "Invalid frame length", 6262306a36Sopenharmony_ci "Invalid RTS", 6362306a36Sopenharmony_ci "Invalid SOF", 6462306a36Sopenharmony_ci "TX timeout", 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistruct mse102x_net { 6862306a36Sopenharmony_ci struct net_device *ndev; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci u8 rxd[8]; 7162306a36Sopenharmony_ci u8 txd[8]; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci u32 msg_enable ____cacheline_aligned; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci struct sk_buff_head txq; 7662306a36Sopenharmony_ci struct mse102x_stats stats; 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistruct mse102x_net_spi { 8062306a36Sopenharmony_ci struct mse102x_net mse102x; 8162306a36Sopenharmony_ci struct mutex lock; /* Protect SPI frame transfer */ 8262306a36Sopenharmony_ci struct work_struct tx_work; 8362306a36Sopenharmony_ci struct spi_device *spidev; 8462306a36Sopenharmony_ci struct spi_message spi_msg; 8562306a36Sopenharmony_ci struct spi_transfer spi_xfer; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 8862306a36Sopenharmony_ci struct dentry *device_root; 8962306a36Sopenharmony_ci#endif 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define to_mse102x_spi(mse) container_of((mse), struct mse102x_net_spi, mse102x) 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int mse102x_info_show(struct seq_file *s, void *what) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct mse102x_net_spi *mses = s->private; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci seq_printf(s, "TX ring size : %u\n", 10162306a36Sopenharmony_ci skb_queue_len(&mses->mse102x.txq)); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci seq_printf(s, "IRQ : %d\n", 10462306a36Sopenharmony_ci mses->spidev->irq); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci seq_printf(s, "SPI effective speed : %lu\n", 10762306a36Sopenharmony_ci (unsigned long)mses->spi_xfer.effective_speed_hz); 10862306a36Sopenharmony_ci seq_printf(s, "SPI mode : %x\n", 10962306a36Sopenharmony_ci mses->spidev->mode); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(mse102x_info); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic void mse102x_init_device_debugfs(struct mse102x_net_spi *mses) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci mses->device_root = debugfs_create_dir(dev_name(&mses->mse102x.ndev->dev), 11862306a36Sopenharmony_ci NULL); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci debugfs_create_file("info", S_IFREG | 0444, mses->device_root, mses, 12162306a36Sopenharmony_ci &mse102x_info_fops); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic void mse102x_remove_device_debugfs(struct mse102x_net_spi *mses) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci debugfs_remove_recursive(mses->device_root); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci#else /* CONFIG_DEBUG_FS */ 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void mse102x_init_device_debugfs(struct mse102x_net_spi *mses) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void mse102x_remove_device_debugfs(struct mse102x_net_spi *mses) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#endif 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* SPI register read/write calls. 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci * All these calls issue SPI transactions to access the chip's registers. They 14462306a36Sopenharmony_ci * all require that the necessary lock is held to prevent accesses when the 14562306a36Sopenharmony_ci * chip is busy transferring packet data. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void mse102x_tx_cmd_spi(struct mse102x_net *mse, u16 cmd) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct mse102x_net_spi *mses = to_mse102x_spi(mse); 15162306a36Sopenharmony_ci struct spi_transfer *xfer = &mses->spi_xfer; 15262306a36Sopenharmony_ci struct spi_message *msg = &mses->spi_msg; 15362306a36Sopenharmony_ci __be16 txb[2]; 15462306a36Sopenharmony_ci int ret; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci txb[0] = cpu_to_be16(DET_CMD); 15762306a36Sopenharmony_ci txb[1] = cpu_to_be16(cmd); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci xfer->tx_buf = txb; 16062306a36Sopenharmony_ci xfer->rx_buf = NULL; 16162306a36Sopenharmony_ci xfer->len = DET_CMD_LEN; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci ret = spi_sync(mses->spidev, msg); 16462306a36Sopenharmony_ci if (ret < 0) { 16562306a36Sopenharmony_ci netdev_err(mse->ndev, "%s: spi_sync() failed: %d\n", 16662306a36Sopenharmony_ci __func__, ret); 16762306a36Sopenharmony_ci mse->stats.xfer_err++; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int mse102x_rx_cmd_spi(struct mse102x_net *mse, u8 *rxb) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct mse102x_net_spi *mses = to_mse102x_spi(mse); 17462306a36Sopenharmony_ci struct spi_transfer *xfer = &mses->spi_xfer; 17562306a36Sopenharmony_ci struct spi_message *msg = &mses->spi_msg; 17662306a36Sopenharmony_ci __be16 *txb = (__be16 *)mse->txd; 17762306a36Sopenharmony_ci __be16 *cmd = (__be16 *)mse->rxd; 17862306a36Sopenharmony_ci u8 *trx = mse->rxd; 17962306a36Sopenharmony_ci int ret; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci txb[0] = 0; 18262306a36Sopenharmony_ci txb[1] = 0; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci xfer->tx_buf = txb; 18562306a36Sopenharmony_ci xfer->rx_buf = trx; 18662306a36Sopenharmony_ci xfer->len = DET_CMD_LEN; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci ret = spi_sync(mses->spidev, msg); 18962306a36Sopenharmony_ci if (ret < 0) { 19062306a36Sopenharmony_ci netdev_err(mse->ndev, "%s: spi_sync() failed: %d\n", 19162306a36Sopenharmony_ci __func__, ret); 19262306a36Sopenharmony_ci mse->stats.xfer_err++; 19362306a36Sopenharmony_ci } else if (*cmd != cpu_to_be16(DET_CMD)) { 19462306a36Sopenharmony_ci net_dbg_ratelimited("%s: Unexpected response (0x%04x)\n", 19562306a36Sopenharmony_ci __func__, *cmd); 19662306a36Sopenharmony_ci mse->stats.invalid_cmd++; 19762306a36Sopenharmony_ci ret = -EIO; 19862306a36Sopenharmony_ci } else { 19962306a36Sopenharmony_ci memcpy(rxb, trx + 2, 2); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return ret; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic inline void mse102x_push_header(struct sk_buff *skb) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci __be16 *header = skb_push(skb, DET_SOF_LEN); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci *header = cpu_to_be16(DET_SOF); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic inline void mse102x_put_footer(struct sk_buff *skb) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci __be16 *footer = skb_put(skb, DET_DFT_LEN); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci *footer = cpu_to_be16(DET_DFT); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic int mse102x_tx_frame_spi(struct mse102x_net *mse, struct sk_buff *txp, 22062306a36Sopenharmony_ci unsigned int pad) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct mse102x_net_spi *mses = to_mse102x_spi(mse); 22362306a36Sopenharmony_ci struct spi_transfer *xfer = &mses->spi_xfer; 22462306a36Sopenharmony_ci struct spi_message *msg = &mses->spi_msg; 22562306a36Sopenharmony_ci struct sk_buff *tskb; 22662306a36Sopenharmony_ci int ret; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci netif_dbg(mse, tx_queued, mse->ndev, "%s: skb %p, %d@%p\n", 22962306a36Sopenharmony_ci __func__, txp, txp->len, txp->data); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if ((skb_headroom(txp) < DET_SOF_LEN) || 23262306a36Sopenharmony_ci (skb_tailroom(txp) < DET_DFT_LEN + pad)) { 23362306a36Sopenharmony_ci tskb = skb_copy_expand(txp, DET_SOF_LEN, DET_DFT_LEN + pad, 23462306a36Sopenharmony_ci GFP_KERNEL); 23562306a36Sopenharmony_ci if (!tskb) 23662306a36Sopenharmony_ci return -ENOMEM; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci dev_kfree_skb(txp); 23962306a36Sopenharmony_ci txp = tskb; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci mse102x_push_header(txp); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (pad) 24562306a36Sopenharmony_ci skb_put_zero(txp, pad); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci mse102x_put_footer(txp); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci xfer->tx_buf = txp->data; 25062306a36Sopenharmony_ci xfer->rx_buf = NULL; 25162306a36Sopenharmony_ci xfer->len = txp->len; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci ret = spi_sync(mses->spidev, msg); 25462306a36Sopenharmony_ci if (ret < 0) { 25562306a36Sopenharmony_ci netdev_err(mse->ndev, "%s: spi_sync() failed: %d\n", 25662306a36Sopenharmony_ci __func__, ret); 25762306a36Sopenharmony_ci mse->stats.xfer_err++; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return ret; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int mse102x_rx_frame_spi(struct mse102x_net *mse, u8 *buff, 26462306a36Sopenharmony_ci unsigned int frame_len) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct mse102x_net_spi *mses = to_mse102x_spi(mse); 26762306a36Sopenharmony_ci struct spi_transfer *xfer = &mses->spi_xfer; 26862306a36Sopenharmony_ci struct spi_message *msg = &mses->spi_msg; 26962306a36Sopenharmony_ci __be16 *sof = (__be16 *)buff; 27062306a36Sopenharmony_ci __be16 *dft = (__be16 *)(buff + DET_SOF_LEN + frame_len); 27162306a36Sopenharmony_ci int ret; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci xfer->rx_buf = buff; 27462306a36Sopenharmony_ci xfer->tx_buf = NULL; 27562306a36Sopenharmony_ci xfer->len = DET_SOF_LEN + frame_len + DET_DFT_LEN; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci ret = spi_sync(mses->spidev, msg); 27862306a36Sopenharmony_ci if (ret < 0) { 27962306a36Sopenharmony_ci netdev_err(mse->ndev, "%s: spi_sync() failed: %d\n", 28062306a36Sopenharmony_ci __func__, ret); 28162306a36Sopenharmony_ci mse->stats.xfer_err++; 28262306a36Sopenharmony_ci } else if (*sof != cpu_to_be16(DET_SOF)) { 28362306a36Sopenharmony_ci netdev_dbg(mse->ndev, "%s: SPI start of frame is invalid (0x%04x)\n", 28462306a36Sopenharmony_ci __func__, *sof); 28562306a36Sopenharmony_ci mse->stats.invalid_sof++; 28662306a36Sopenharmony_ci ret = -EIO; 28762306a36Sopenharmony_ci } else if (*dft != cpu_to_be16(DET_DFT)) { 28862306a36Sopenharmony_ci netdev_dbg(mse->ndev, "%s: SPI frame tail is invalid (0x%04x)\n", 28962306a36Sopenharmony_ci __func__, *dft); 29062306a36Sopenharmony_ci mse->stats.invalid_dft++; 29162306a36Sopenharmony_ci ret = -EIO; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return ret; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic void mse102x_dump_packet(const char *msg, int len, const char *data) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci printk(KERN_DEBUG ": %s - packet len:%d\n", msg, len); 30062306a36Sopenharmony_ci print_hex_dump(KERN_DEBUG, "pk data: ", DUMP_PREFIX_OFFSET, 16, 1, 30162306a36Sopenharmony_ci data, len, true); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic void mse102x_rx_pkt_spi(struct mse102x_net *mse) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct sk_buff *skb; 30762306a36Sopenharmony_ci unsigned int rxalign; 30862306a36Sopenharmony_ci unsigned int rxlen; 30962306a36Sopenharmony_ci __be16 rx = 0; 31062306a36Sopenharmony_ci u16 cmd_resp; 31162306a36Sopenharmony_ci u8 *rxpkt; 31262306a36Sopenharmony_ci int ret; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci mse102x_tx_cmd_spi(mse, CMD_CTR); 31562306a36Sopenharmony_ci ret = mse102x_rx_cmd_spi(mse, (u8 *)&rx); 31662306a36Sopenharmony_ci cmd_resp = be16_to_cpu(rx); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (ret || ((cmd_resp & CMD_MASK) != CMD_RTS)) { 31962306a36Sopenharmony_ci usleep_range(50, 100); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci mse102x_tx_cmd_spi(mse, CMD_CTR); 32262306a36Sopenharmony_ci ret = mse102x_rx_cmd_spi(mse, (u8 *)&rx); 32362306a36Sopenharmony_ci if (ret) 32462306a36Sopenharmony_ci return; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci cmd_resp = be16_to_cpu(rx); 32762306a36Sopenharmony_ci if ((cmd_resp & CMD_MASK) != CMD_RTS) { 32862306a36Sopenharmony_ci net_dbg_ratelimited("%s: Unexpected response (0x%04x)\n", 32962306a36Sopenharmony_ci __func__, cmd_resp); 33062306a36Sopenharmony_ci mse->stats.invalid_rts++; 33162306a36Sopenharmony_ci return; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci net_dbg_ratelimited("%s: Unexpected response to first CMD\n", 33562306a36Sopenharmony_ci __func__); 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci rxlen = cmd_resp & LEN_MASK; 33962306a36Sopenharmony_ci if (!rxlen) { 34062306a36Sopenharmony_ci net_dbg_ratelimited("%s: No frame length defined\n", __func__); 34162306a36Sopenharmony_ci mse->stats.invalid_len++; 34262306a36Sopenharmony_ci return; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci rxalign = ALIGN(rxlen + DET_SOF_LEN + DET_DFT_LEN, 4); 34662306a36Sopenharmony_ci skb = netdev_alloc_skb_ip_align(mse->ndev, rxalign); 34762306a36Sopenharmony_ci if (!skb) 34862306a36Sopenharmony_ci return; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* 2 bytes Start of frame (before ethernet header) 35162306a36Sopenharmony_ci * 2 bytes Data frame tail (after ethernet frame) 35262306a36Sopenharmony_ci * They are copied, but ignored. 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_ci rxpkt = skb_put(skb, rxlen) - DET_SOF_LEN; 35562306a36Sopenharmony_ci if (mse102x_rx_frame_spi(mse, rxpkt, rxlen)) { 35662306a36Sopenharmony_ci mse->ndev->stats.rx_errors++; 35762306a36Sopenharmony_ci dev_kfree_skb(skb); 35862306a36Sopenharmony_ci return; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (netif_msg_pktdata(mse)) 36262306a36Sopenharmony_ci mse102x_dump_packet(__func__, skb->len, skb->data); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, mse->ndev); 36562306a36Sopenharmony_ci netif_rx(skb); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci mse->ndev->stats.rx_packets++; 36862306a36Sopenharmony_ci mse->ndev->stats.rx_bytes += rxlen; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic int mse102x_tx_pkt_spi(struct mse102x_net *mse, struct sk_buff *txb, 37262306a36Sopenharmony_ci unsigned long work_timeout) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci unsigned int pad = 0; 37562306a36Sopenharmony_ci __be16 rx = 0; 37662306a36Sopenharmony_ci u16 cmd_resp; 37762306a36Sopenharmony_ci int ret; 37862306a36Sopenharmony_ci bool first = true; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (txb->len < 60) 38162306a36Sopenharmony_ci pad = 60 - txb->len; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci while (1) { 38462306a36Sopenharmony_ci mse102x_tx_cmd_spi(mse, CMD_RTS | (txb->len + pad)); 38562306a36Sopenharmony_ci ret = mse102x_rx_cmd_spi(mse, (u8 *)&rx); 38662306a36Sopenharmony_ci cmd_resp = be16_to_cpu(rx); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (!ret) { 38962306a36Sopenharmony_ci /* ready to send frame ? */ 39062306a36Sopenharmony_ci if (cmd_resp == CMD_CTR) 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci net_dbg_ratelimited("%s: Unexpected response (0x%04x)\n", 39462306a36Sopenharmony_ci __func__, cmd_resp); 39562306a36Sopenharmony_ci mse->stats.invalid_ctr++; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* It's not predictable how long / many retries it takes to 39962306a36Sopenharmony_ci * send at least one packet, so TX timeouts are possible. 40062306a36Sopenharmony_ci * That's the reason why the netdev watchdog is not used here. 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ci if (time_after(jiffies, work_timeout)) 40362306a36Sopenharmony_ci return -ETIMEDOUT; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (first) { 40662306a36Sopenharmony_ci /* throttle at first issue */ 40762306a36Sopenharmony_ci netif_stop_queue(mse->ndev); 40862306a36Sopenharmony_ci /* fast retry */ 40962306a36Sopenharmony_ci usleep_range(50, 100); 41062306a36Sopenharmony_ci first = false; 41162306a36Sopenharmony_ci } else { 41262306a36Sopenharmony_ci msleep(20); 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci ret = mse102x_tx_frame_spi(mse, txb, pad); 41762306a36Sopenharmony_ci if (ret) 41862306a36Sopenharmony_ci net_dbg_ratelimited("%s: Failed to send (%d), drop frame\n", 41962306a36Sopenharmony_ci __func__, ret); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return ret; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci#define TX_QUEUE_MAX 10 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic void mse102x_tx_work(struct work_struct *work) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci /* Make sure timeout is sufficient to transfer TX_QUEUE_MAX frames */ 42962306a36Sopenharmony_ci unsigned long work_timeout = jiffies + msecs_to_jiffies(1000); 43062306a36Sopenharmony_ci struct mse102x_net_spi *mses; 43162306a36Sopenharmony_ci struct mse102x_net *mse; 43262306a36Sopenharmony_ci struct sk_buff *txb; 43362306a36Sopenharmony_ci int ret = 0; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci mses = container_of(work, struct mse102x_net_spi, tx_work); 43662306a36Sopenharmony_ci mse = &mses->mse102x; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci while ((txb = skb_dequeue(&mse->txq))) { 43962306a36Sopenharmony_ci mutex_lock(&mses->lock); 44062306a36Sopenharmony_ci ret = mse102x_tx_pkt_spi(mse, txb, work_timeout); 44162306a36Sopenharmony_ci mutex_unlock(&mses->lock); 44262306a36Sopenharmony_ci if (ret) { 44362306a36Sopenharmony_ci mse->ndev->stats.tx_dropped++; 44462306a36Sopenharmony_ci } else { 44562306a36Sopenharmony_ci mse->ndev->stats.tx_bytes += txb->len; 44662306a36Sopenharmony_ci mse->ndev->stats.tx_packets++; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci dev_kfree_skb(txb); 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (ret == -ETIMEDOUT) { 45362306a36Sopenharmony_ci if (netif_msg_timer(mse)) 45462306a36Sopenharmony_ci netdev_err(mse->ndev, "tx work timeout\n"); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci mse->stats.tx_timeout++; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci netif_wake_queue(mse->ndev); 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic netdev_tx_t mse102x_start_xmit_spi(struct sk_buff *skb, 46362306a36Sopenharmony_ci struct net_device *ndev) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci struct mse102x_net *mse = netdev_priv(ndev); 46662306a36Sopenharmony_ci struct mse102x_net_spi *mses = to_mse102x_spi(mse); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci netif_dbg(mse, tx_queued, ndev, 46962306a36Sopenharmony_ci "%s: skb %p, %d@%p\n", __func__, skb, skb->len, skb->data); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci skb_queue_tail(&mse->txq, skb); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (skb_queue_len(&mse->txq) >= TX_QUEUE_MAX) 47462306a36Sopenharmony_ci netif_stop_queue(ndev); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci schedule_work(&mses->tx_work); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return NETDEV_TX_OK; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic void mse102x_init_mac(struct mse102x_net *mse, struct device_node *np) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct net_device *ndev = mse->ndev; 48462306a36Sopenharmony_ci int ret = of_get_ethdev_address(np, ndev); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (ret) { 48762306a36Sopenharmony_ci eth_hw_addr_random(ndev); 48862306a36Sopenharmony_ci netdev_err(ndev, "Using random MAC address: %pM\n", 48962306a36Sopenharmony_ci ndev->dev_addr); 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci/* Assumption: this is called for every incoming packet */ 49462306a36Sopenharmony_cistatic irqreturn_t mse102x_irq(int irq, void *_mse) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct mse102x_net *mse = _mse; 49762306a36Sopenharmony_ci struct mse102x_net_spi *mses = to_mse102x_spi(mse); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci mutex_lock(&mses->lock); 50062306a36Sopenharmony_ci mse102x_rx_pkt_spi(mse); 50162306a36Sopenharmony_ci mutex_unlock(&mses->lock); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci return IRQ_HANDLED; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic int mse102x_net_open(struct net_device *ndev) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci struct mse102x_net *mse = netdev_priv(ndev); 50962306a36Sopenharmony_ci int ret; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci ret = request_threaded_irq(ndev->irq, NULL, mse102x_irq, IRQF_ONESHOT, 51262306a36Sopenharmony_ci ndev->name, mse); 51362306a36Sopenharmony_ci if (ret < 0) { 51462306a36Sopenharmony_ci netdev_err(ndev, "Failed to get irq: %d\n", ret); 51562306a36Sopenharmony_ci return ret; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci netif_dbg(mse, ifup, ndev, "opening\n"); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci netif_start_queue(ndev); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci netif_carrier_on(ndev); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci netif_dbg(mse, ifup, ndev, "network device up\n"); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci return 0; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic int mse102x_net_stop(struct net_device *ndev) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct mse102x_net *mse = netdev_priv(ndev); 53262306a36Sopenharmony_ci struct mse102x_net_spi *mses = to_mse102x_spi(mse); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci netif_info(mse, ifdown, ndev, "shutting down\n"); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci netif_carrier_off(mse->ndev); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* stop any outstanding work */ 53962306a36Sopenharmony_ci flush_work(&mses->tx_work); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci netif_stop_queue(ndev); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci skb_queue_purge(&mse->txq); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci free_irq(ndev->irq, mse); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci return 0; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic const struct net_device_ops mse102x_netdev_ops = { 55162306a36Sopenharmony_ci .ndo_open = mse102x_net_open, 55262306a36Sopenharmony_ci .ndo_stop = mse102x_net_stop, 55362306a36Sopenharmony_ci .ndo_start_xmit = mse102x_start_xmit_spi, 55462306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 55562306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 55662306a36Sopenharmony_ci}; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci/* ethtool support */ 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic void mse102x_get_drvinfo(struct net_device *ndev, 56162306a36Sopenharmony_ci struct ethtool_drvinfo *di) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci strscpy(di->driver, DRV_NAME, sizeof(di->driver)); 56462306a36Sopenharmony_ci strscpy(di->bus_info, dev_name(ndev->dev.parent), sizeof(di->bus_info)); 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic u32 mse102x_get_msglevel(struct net_device *ndev) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct mse102x_net *mse = netdev_priv(ndev); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return mse->msg_enable; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic void mse102x_set_msglevel(struct net_device *ndev, u32 to) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct mse102x_net *mse = netdev_priv(ndev); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci mse->msg_enable = to; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic void mse102x_get_ethtool_stats(struct net_device *ndev, 58262306a36Sopenharmony_ci struct ethtool_stats *estats, u64 *data) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci struct mse102x_net *mse = netdev_priv(ndev); 58562306a36Sopenharmony_ci struct mse102x_stats *st = &mse->stats; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci memcpy(data, st, ARRAY_SIZE(mse102x_gstrings_stats) * sizeof(u64)); 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic void mse102x_get_strings(struct net_device *ndev, u32 stringset, u8 *buf) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci switch (stringset) { 59362306a36Sopenharmony_ci case ETH_SS_STATS: 59462306a36Sopenharmony_ci memcpy(buf, &mse102x_gstrings_stats, 59562306a36Sopenharmony_ci sizeof(mse102x_gstrings_stats)); 59662306a36Sopenharmony_ci break; 59762306a36Sopenharmony_ci default: 59862306a36Sopenharmony_ci WARN_ON(1); 59962306a36Sopenharmony_ci break; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic int mse102x_get_sset_count(struct net_device *ndev, int sset) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci switch (sset) { 60662306a36Sopenharmony_ci case ETH_SS_STATS: 60762306a36Sopenharmony_ci return ARRAY_SIZE(mse102x_gstrings_stats); 60862306a36Sopenharmony_ci default: 60962306a36Sopenharmony_ci return -EINVAL; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic const struct ethtool_ops mse102x_ethtool_ops = { 61462306a36Sopenharmony_ci .get_drvinfo = mse102x_get_drvinfo, 61562306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 61662306a36Sopenharmony_ci .get_msglevel = mse102x_get_msglevel, 61762306a36Sopenharmony_ci .set_msglevel = mse102x_set_msglevel, 61862306a36Sopenharmony_ci .get_ethtool_stats = mse102x_get_ethtool_stats, 61962306a36Sopenharmony_ci .get_strings = mse102x_get_strings, 62062306a36Sopenharmony_ci .get_sset_count = mse102x_get_sset_count, 62162306a36Sopenharmony_ci}; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci/* driver bus management functions */ 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic int mse102x_suspend(struct device *dev) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci struct mse102x_net *mse = dev_get_drvdata(dev); 63062306a36Sopenharmony_ci struct net_device *ndev = mse->ndev; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci if (netif_running(ndev)) { 63362306a36Sopenharmony_ci netif_device_detach(ndev); 63462306a36Sopenharmony_ci mse102x_net_stop(ndev); 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci return 0; 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic int mse102x_resume(struct device *dev) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci struct mse102x_net *mse = dev_get_drvdata(dev); 64362306a36Sopenharmony_ci struct net_device *ndev = mse->ndev; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (netif_running(ndev)) { 64662306a36Sopenharmony_ci mse102x_net_open(ndev); 64762306a36Sopenharmony_ci netif_device_attach(ndev); 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return 0; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci#endif 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(mse102x_pm_ops, mse102x_suspend, mse102x_resume); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic int mse102x_probe_spi(struct spi_device *spi) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci struct device *dev = &spi->dev; 65962306a36Sopenharmony_ci struct mse102x_net_spi *mses; 66062306a36Sopenharmony_ci struct net_device *ndev; 66162306a36Sopenharmony_ci struct mse102x_net *mse; 66262306a36Sopenharmony_ci int ret; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci spi->bits_per_word = 8; 66562306a36Sopenharmony_ci spi->mode |= SPI_MODE_3; 66662306a36Sopenharmony_ci /* enforce minimum speed to ensure device functionality */ 66762306a36Sopenharmony_ci spi->master->min_speed_hz = MIN_FREQ_HZ; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (!spi->max_speed_hz) 67062306a36Sopenharmony_ci spi->max_speed_hz = MAX_FREQ_HZ; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci if (spi->max_speed_hz < MIN_FREQ_HZ || 67362306a36Sopenharmony_ci spi->max_speed_hz > MAX_FREQ_HZ) { 67462306a36Sopenharmony_ci dev_err(&spi->dev, "SPI max frequency out of range (min: %u, max: %u)\n", 67562306a36Sopenharmony_ci MIN_FREQ_HZ, MAX_FREQ_HZ); 67662306a36Sopenharmony_ci return -EINVAL; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci ret = spi_setup(spi); 68062306a36Sopenharmony_ci if (ret < 0) { 68162306a36Sopenharmony_ci dev_err(&spi->dev, "Unable to setup SPI device: %d\n", ret); 68262306a36Sopenharmony_ci return ret; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci ndev = devm_alloc_etherdev(dev, sizeof(struct mse102x_net_spi)); 68662306a36Sopenharmony_ci if (!ndev) 68762306a36Sopenharmony_ci return -ENOMEM; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci ndev->needed_tailroom += ALIGN(DET_DFT_LEN, 4); 69062306a36Sopenharmony_ci ndev->needed_headroom += ALIGN(DET_SOF_LEN, 4); 69162306a36Sopenharmony_ci ndev->priv_flags &= ~IFF_TX_SKB_SHARING; 69262306a36Sopenharmony_ci ndev->tx_queue_len = 100; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci mse = netdev_priv(ndev); 69562306a36Sopenharmony_ci mses = to_mse102x_spi(mse); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci mses->spidev = spi; 69862306a36Sopenharmony_ci mutex_init(&mses->lock); 69962306a36Sopenharmony_ci INIT_WORK(&mses->tx_work, mse102x_tx_work); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci /* initialise pre-made spi transfer messages */ 70262306a36Sopenharmony_ci spi_message_init(&mses->spi_msg); 70362306a36Sopenharmony_ci spi_message_add_tail(&mses->spi_xfer, &mses->spi_msg); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci ndev->irq = spi->irq; 70662306a36Sopenharmony_ci mse->ndev = ndev; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* set the default message enable */ 70962306a36Sopenharmony_ci mse->msg_enable = netif_msg_init(-1, MSG_DEFAULT); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci skb_queue_head_init(&mse->txq); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci SET_NETDEV_DEV(ndev, dev); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci dev_set_drvdata(dev, mse); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci netif_carrier_off(mse->ndev); 71862306a36Sopenharmony_ci ndev->netdev_ops = &mse102x_netdev_ops; 71962306a36Sopenharmony_ci ndev->ethtool_ops = &mse102x_ethtool_ops; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci mse102x_init_mac(mse, dev->of_node); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci ret = register_netdev(ndev); 72462306a36Sopenharmony_ci if (ret) { 72562306a36Sopenharmony_ci dev_err(dev, "failed to register network device: %d\n", ret); 72662306a36Sopenharmony_ci return ret; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci mse102x_init_device_debugfs(mses); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci return 0; 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic void mse102x_remove_spi(struct spi_device *spi) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct mse102x_net *mse = dev_get_drvdata(&spi->dev); 73762306a36Sopenharmony_ci struct mse102x_net_spi *mses = to_mse102x_spi(mse); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (netif_msg_drv(mse)) 74062306a36Sopenharmony_ci dev_info(&spi->dev, "remove\n"); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci mse102x_remove_device_debugfs(mses); 74362306a36Sopenharmony_ci unregister_netdev(mse->ndev); 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic const struct of_device_id mse102x_match_table[] = { 74762306a36Sopenharmony_ci { .compatible = "vertexcom,mse1021" }, 74862306a36Sopenharmony_ci { .compatible = "vertexcom,mse1022" }, 74962306a36Sopenharmony_ci { } 75062306a36Sopenharmony_ci}; 75162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mse102x_match_table); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic const struct spi_device_id mse102x_ids[] = { 75462306a36Sopenharmony_ci { "mse1021" }, 75562306a36Sopenharmony_ci { "mse1022" }, 75662306a36Sopenharmony_ci { } 75762306a36Sopenharmony_ci}; 75862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(spi, mse102x_ids); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic struct spi_driver mse102x_driver = { 76162306a36Sopenharmony_ci .driver = { 76262306a36Sopenharmony_ci .name = DRV_NAME, 76362306a36Sopenharmony_ci .of_match_table = mse102x_match_table, 76462306a36Sopenharmony_ci .pm = &mse102x_pm_ops, 76562306a36Sopenharmony_ci }, 76662306a36Sopenharmony_ci .probe = mse102x_probe_spi, 76762306a36Sopenharmony_ci .remove = mse102x_remove_spi, 76862306a36Sopenharmony_ci .id_table = mse102x_ids, 76962306a36Sopenharmony_ci}; 77062306a36Sopenharmony_cimodule_spi_driver(mse102x_driver); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ciMODULE_DESCRIPTION("MSE102x Network driver"); 77362306a36Sopenharmony_ciMODULE_AUTHOR("Stefan Wahren <stefan.wahren@chargebyte.com>"); 77462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 77562306a36Sopenharmony_ciMODULE_ALIAS("spi:" DRV_NAME); 776