12f207cbfSStefan Wahren // SPDX-License-Identifier: GPL-2.0-only 22f207cbfSStefan Wahren /* Copyright (C) 2021 in-tech smart charging GmbH 32f207cbfSStefan Wahren * 42f207cbfSStefan Wahren * driver is based on micrel/ks8851_spi.c 52f207cbfSStefan Wahren */ 62f207cbfSStefan Wahren 72f207cbfSStefan Wahren #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 82f207cbfSStefan Wahren 92f207cbfSStefan Wahren #include <linux/interrupt.h> 102f207cbfSStefan Wahren #include <linux/module.h> 112f207cbfSStefan Wahren #include <linux/kernel.h> 122f207cbfSStefan Wahren #include <linux/netdevice.h> 132f207cbfSStefan Wahren #include <linux/etherdevice.h> 142f207cbfSStefan Wahren #include <linux/ethtool.h> 152f207cbfSStefan Wahren #include <linux/cache.h> 162f207cbfSStefan Wahren #include <linux/debugfs.h> 172f207cbfSStefan Wahren #include <linux/seq_file.h> 182f207cbfSStefan Wahren 192f207cbfSStefan Wahren #include <linux/spi/spi.h> 202f207cbfSStefan Wahren #include <linux/of_net.h> 212f207cbfSStefan Wahren 222f207cbfSStefan Wahren #define MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \ 232f207cbfSStefan Wahren NETIF_MSG_TIMER) 242f207cbfSStefan Wahren 252f207cbfSStefan Wahren #define DRV_NAME "mse102x" 262f207cbfSStefan Wahren 272f207cbfSStefan Wahren #define DET_CMD 0x0001 282f207cbfSStefan Wahren #define DET_SOF 0x0002 292f207cbfSStefan Wahren #define DET_DFT 0x55AA 302f207cbfSStefan Wahren 312f207cbfSStefan Wahren #define CMD_SHIFT 12 322f207cbfSStefan Wahren #define CMD_RTS (0x1 << CMD_SHIFT) 332f207cbfSStefan Wahren #define CMD_CTR (0x2 << CMD_SHIFT) 342f207cbfSStefan Wahren 352f207cbfSStefan Wahren #define CMD_MASK GENMASK(15, CMD_SHIFT) 362f207cbfSStefan Wahren #define LEN_MASK GENMASK(CMD_SHIFT - 1, 0) 372f207cbfSStefan Wahren 382f207cbfSStefan Wahren #define DET_CMD_LEN 4 392f207cbfSStefan Wahren #define DET_SOF_LEN 2 402f207cbfSStefan Wahren #define DET_DFT_LEN 2 412f207cbfSStefan Wahren 422f207cbfSStefan Wahren #define MIN_FREQ_HZ 6000000 432f207cbfSStefan Wahren #define MAX_FREQ_HZ 7142857 442f207cbfSStefan Wahren 452f207cbfSStefan Wahren struct mse102x_stats { 462f207cbfSStefan Wahren u64 xfer_err; 472f207cbfSStefan Wahren u64 invalid_cmd; 482f207cbfSStefan Wahren u64 invalid_ctr; 492f207cbfSStefan Wahren u64 invalid_dft; 502f207cbfSStefan Wahren u64 invalid_len; 512f207cbfSStefan Wahren u64 invalid_rts; 522f207cbfSStefan Wahren u64 invalid_sof; 532f207cbfSStefan Wahren u64 tx_timeout; 542f207cbfSStefan Wahren }; 552f207cbfSStefan Wahren 562f207cbfSStefan Wahren static const char mse102x_gstrings_stats[][ETH_GSTRING_LEN] = { 572f207cbfSStefan Wahren "SPI transfer errors", 582f207cbfSStefan Wahren "Invalid command", 592f207cbfSStefan Wahren "Invalid CTR", 602f207cbfSStefan Wahren "Invalid DFT", 612f207cbfSStefan Wahren "Invalid frame length", 622f207cbfSStefan Wahren "Invalid RTS", 632f207cbfSStefan Wahren "Invalid SOF", 642f207cbfSStefan Wahren "TX timeout", 652f207cbfSStefan Wahren }; 662f207cbfSStefan Wahren 672f207cbfSStefan Wahren struct mse102x_net { 682f207cbfSStefan Wahren struct net_device *ndev; 692f207cbfSStefan Wahren 702f207cbfSStefan Wahren u8 rxd[8]; 712f207cbfSStefan Wahren u8 txd[8]; 722f207cbfSStefan Wahren 732f207cbfSStefan Wahren u32 msg_enable ____cacheline_aligned; 742f207cbfSStefan Wahren 752f207cbfSStefan Wahren struct sk_buff_head txq; 762f207cbfSStefan Wahren struct mse102x_stats stats; 772f207cbfSStefan Wahren }; 782f207cbfSStefan Wahren 792f207cbfSStefan Wahren struct mse102x_net_spi { 802f207cbfSStefan Wahren struct mse102x_net mse102x; 812f207cbfSStefan Wahren struct mutex lock; /* Protect SPI frame transfer */ 822f207cbfSStefan Wahren struct work_struct tx_work; 832f207cbfSStefan Wahren struct spi_device *spidev; 842f207cbfSStefan Wahren struct spi_message spi_msg; 852f207cbfSStefan Wahren struct spi_transfer spi_xfer; 862f207cbfSStefan Wahren 872f207cbfSStefan Wahren #ifdef CONFIG_DEBUG_FS 882f207cbfSStefan Wahren struct dentry *device_root; 892f207cbfSStefan Wahren #endif 902f207cbfSStefan Wahren }; 912f207cbfSStefan Wahren 922f207cbfSStefan Wahren #define to_mse102x_spi(mse) container_of((mse), struct mse102x_net_spi, mse102x) 932f207cbfSStefan Wahren 942f207cbfSStefan Wahren #ifdef CONFIG_DEBUG_FS 952f207cbfSStefan Wahren 962f207cbfSStefan Wahren static int mse102x_info_show(struct seq_file *s, void *what) 972f207cbfSStefan Wahren { 982f207cbfSStefan Wahren struct mse102x_net_spi *mses = s->private; 992f207cbfSStefan Wahren 1002f207cbfSStefan Wahren seq_printf(s, "TX ring size : %u\n", 1012f207cbfSStefan Wahren skb_queue_len(&mses->mse102x.txq)); 1022f207cbfSStefan Wahren 1032f207cbfSStefan Wahren seq_printf(s, "IRQ : %d\n", 1042f207cbfSStefan Wahren mses->spidev->irq); 1052f207cbfSStefan Wahren 1062f207cbfSStefan Wahren seq_printf(s, "SPI effective speed : %lu\n", 1072f207cbfSStefan Wahren (unsigned long)mses->spi_xfer.effective_speed_hz); 1082f207cbfSStefan Wahren seq_printf(s, "SPI mode : %x\n", 1092f207cbfSStefan Wahren mses->spidev->mode); 1102f207cbfSStefan Wahren 1112f207cbfSStefan Wahren return 0; 1122f207cbfSStefan Wahren } 1132f207cbfSStefan Wahren DEFINE_SHOW_ATTRIBUTE(mse102x_info); 1142f207cbfSStefan Wahren 1152f207cbfSStefan Wahren static void mse102x_init_device_debugfs(struct mse102x_net_spi *mses) 1162f207cbfSStefan Wahren { 1172f207cbfSStefan Wahren mses->device_root = debugfs_create_dir(dev_name(&mses->mse102x.ndev->dev), 1182f207cbfSStefan Wahren NULL); 1192f207cbfSStefan Wahren 1202f207cbfSStefan Wahren debugfs_create_file("info", S_IFREG | 0444, mses->device_root, mses, 1212f207cbfSStefan Wahren &mse102x_info_fops); 1222f207cbfSStefan Wahren } 1232f207cbfSStefan Wahren 1242f207cbfSStefan Wahren static void mse102x_remove_device_debugfs(struct mse102x_net_spi *mses) 1252f207cbfSStefan Wahren { 1262f207cbfSStefan Wahren debugfs_remove_recursive(mses->device_root); 1272f207cbfSStefan Wahren } 1282f207cbfSStefan Wahren 1292f207cbfSStefan Wahren #else /* CONFIG_DEBUG_FS */ 1302f207cbfSStefan Wahren 1312f207cbfSStefan Wahren static void mse102x_init_device_debugfs(struct mse102x_net_spi *mses) 1322f207cbfSStefan Wahren { 1332f207cbfSStefan Wahren } 1342f207cbfSStefan Wahren 1352f207cbfSStefan Wahren static void mse102x_remove_device_debugfs(struct mse102x_net_spi *mses) 1362f207cbfSStefan Wahren { 1372f207cbfSStefan Wahren } 1382f207cbfSStefan Wahren 1392f207cbfSStefan Wahren #endif 1402f207cbfSStefan Wahren 1412f207cbfSStefan Wahren /* SPI register read/write calls. 1422f207cbfSStefan Wahren * 1432f207cbfSStefan Wahren * All these calls issue SPI transactions to access the chip's registers. They 1442f207cbfSStefan Wahren * all require that the necessary lock is held to prevent accesses when the 1452f207cbfSStefan Wahren * chip is busy transferring packet data. 1462f207cbfSStefan Wahren */ 1472f207cbfSStefan Wahren 1482f207cbfSStefan Wahren static void mse102x_tx_cmd_spi(struct mse102x_net *mse, u16 cmd) 1492f207cbfSStefan Wahren { 1502f207cbfSStefan Wahren struct mse102x_net_spi *mses = to_mse102x_spi(mse); 1512f207cbfSStefan Wahren struct spi_transfer *xfer = &mses->spi_xfer; 1522f207cbfSStefan Wahren struct spi_message *msg = &mses->spi_msg; 1532f207cbfSStefan Wahren __be16 txb[2]; 1542f207cbfSStefan Wahren int ret; 1552f207cbfSStefan Wahren 1562f207cbfSStefan Wahren txb[0] = cpu_to_be16(DET_CMD); 1572f207cbfSStefan Wahren txb[1] = cpu_to_be16(cmd); 1582f207cbfSStefan Wahren 1592f207cbfSStefan Wahren xfer->tx_buf = txb; 1602f207cbfSStefan Wahren xfer->rx_buf = NULL; 1612f207cbfSStefan Wahren xfer->len = DET_CMD_LEN; 1622f207cbfSStefan Wahren 1632f207cbfSStefan Wahren ret = spi_sync(mses->spidev, msg); 1642f207cbfSStefan Wahren if (ret < 0) { 1652f207cbfSStefan Wahren netdev_err(mse->ndev, "%s: spi_sync() failed: %d\n", 1662f207cbfSStefan Wahren __func__, ret); 1672f207cbfSStefan Wahren mse->stats.xfer_err++; 1682f207cbfSStefan Wahren } 1692f207cbfSStefan Wahren } 1702f207cbfSStefan Wahren 1712f207cbfSStefan Wahren static int mse102x_rx_cmd_spi(struct mse102x_net *mse, u8 *rxb) 1722f207cbfSStefan Wahren { 1732f207cbfSStefan Wahren struct mse102x_net_spi *mses = to_mse102x_spi(mse); 1742f207cbfSStefan Wahren struct spi_transfer *xfer = &mses->spi_xfer; 1752f207cbfSStefan Wahren struct spi_message *msg = &mses->spi_msg; 1762f207cbfSStefan Wahren __be16 *txb = (__be16 *)mse->txd; 1772f207cbfSStefan Wahren __be16 *cmd = (__be16 *)mse->rxd; 1782f207cbfSStefan Wahren u8 *trx = mse->rxd; 1792f207cbfSStefan Wahren int ret; 1802f207cbfSStefan Wahren 1812f207cbfSStefan Wahren txb[0] = 0; 1822f207cbfSStefan Wahren txb[1] = 0; 1832f207cbfSStefan Wahren 1842f207cbfSStefan Wahren xfer->tx_buf = txb; 1852f207cbfSStefan Wahren xfer->rx_buf = trx; 1862f207cbfSStefan Wahren xfer->len = DET_CMD_LEN; 1872f207cbfSStefan Wahren 1882f207cbfSStefan Wahren ret = spi_sync(mses->spidev, msg); 1892f207cbfSStefan Wahren if (ret < 0) { 1902f207cbfSStefan Wahren netdev_err(mse->ndev, "%s: spi_sync() failed: %d\n", 1912f207cbfSStefan Wahren __func__, ret); 1922f207cbfSStefan Wahren mse->stats.xfer_err++; 1932f207cbfSStefan Wahren } else if (*cmd != cpu_to_be16(DET_CMD)) { 1942f207cbfSStefan Wahren net_dbg_ratelimited("%s: Unexpected response (0x%04x)\n", 1952f207cbfSStefan Wahren __func__, *cmd); 1962f207cbfSStefan Wahren mse->stats.invalid_cmd++; 1972f207cbfSStefan Wahren ret = -EIO; 1982f207cbfSStefan Wahren } else { 1992f207cbfSStefan Wahren memcpy(rxb, trx + 2, 2); 2002f207cbfSStefan Wahren } 2012f207cbfSStefan Wahren 2022f207cbfSStefan Wahren return ret; 2032f207cbfSStefan Wahren } 2042f207cbfSStefan Wahren 2052f207cbfSStefan Wahren static inline void mse102x_push_header(struct sk_buff *skb) 2062f207cbfSStefan Wahren { 2072f207cbfSStefan Wahren __be16 *header = skb_push(skb, DET_SOF_LEN); 2082f207cbfSStefan Wahren 2092f207cbfSStefan Wahren *header = cpu_to_be16(DET_SOF); 2102f207cbfSStefan Wahren } 2112f207cbfSStefan Wahren 2122f207cbfSStefan Wahren static inline void mse102x_put_footer(struct sk_buff *skb) 2132f207cbfSStefan Wahren { 2142f207cbfSStefan Wahren __be16 *footer = skb_put(skb, DET_DFT_LEN); 2152f207cbfSStefan Wahren 2162f207cbfSStefan Wahren *footer = cpu_to_be16(DET_DFT); 2172f207cbfSStefan Wahren } 2182f207cbfSStefan Wahren 2192f207cbfSStefan Wahren static int mse102x_tx_frame_spi(struct mse102x_net *mse, struct sk_buff *txp, 2202f207cbfSStefan Wahren unsigned int pad) 2212f207cbfSStefan Wahren { 2222f207cbfSStefan Wahren struct mse102x_net_spi *mses = to_mse102x_spi(mse); 2232f207cbfSStefan Wahren struct spi_transfer *xfer = &mses->spi_xfer; 2242f207cbfSStefan Wahren struct spi_message *msg = &mses->spi_msg; 2251325e838SStefan Wahren struct sk_buff *tskb = NULL; 2262f207cbfSStefan Wahren int ret; 2272f207cbfSStefan Wahren 2282f207cbfSStefan Wahren netif_dbg(mse, tx_queued, mse->ndev, "%s: skb %p, %d@%p\n", 2292f207cbfSStefan Wahren __func__, txp, txp->len, txp->data); 2302f207cbfSStefan Wahren 2312f207cbfSStefan Wahren if ((skb_headroom(txp) < DET_SOF_LEN) || 2322f207cbfSStefan Wahren (skb_tailroom(txp) < DET_DFT_LEN + pad)) { 2332f207cbfSStefan Wahren tskb = skb_copy_expand(txp, DET_SOF_LEN, DET_DFT_LEN + pad, 2342f207cbfSStefan Wahren GFP_KERNEL); 2352f207cbfSStefan Wahren if (!tskb) 2362f207cbfSStefan Wahren return -ENOMEM; 2372f207cbfSStefan Wahren 2382f207cbfSStefan Wahren txp = tskb; 2392f207cbfSStefan Wahren } 2402f207cbfSStefan Wahren 2412f207cbfSStefan Wahren mse102x_push_header(txp); 2422f207cbfSStefan Wahren 2432f207cbfSStefan Wahren if (pad) 2442f207cbfSStefan Wahren skb_put_zero(txp, pad); 2452f207cbfSStefan Wahren 2462f207cbfSStefan Wahren mse102x_put_footer(txp); 2472f207cbfSStefan Wahren 2482f207cbfSStefan Wahren xfer->tx_buf = txp->data; 2492f207cbfSStefan Wahren xfer->rx_buf = NULL; 2502f207cbfSStefan Wahren xfer->len = txp->len; 2512f207cbfSStefan Wahren 2522f207cbfSStefan Wahren ret = spi_sync(mses->spidev, msg); 2532f207cbfSStefan Wahren if (ret < 0) { 2542f207cbfSStefan Wahren netdev_err(mse->ndev, "%s: spi_sync() failed: %d\n", 2552f207cbfSStefan Wahren __func__, ret); 2562f207cbfSStefan Wahren mse->stats.xfer_err++; 2572f207cbfSStefan Wahren } 2582f207cbfSStefan Wahren 2591325e838SStefan Wahren dev_kfree_skb(tskb); 2601325e838SStefan Wahren 2612f207cbfSStefan Wahren return ret; 2622f207cbfSStefan Wahren } 2632f207cbfSStefan Wahren 2642f207cbfSStefan Wahren static int mse102x_rx_frame_spi(struct mse102x_net *mse, u8 *buff, 2652f207cbfSStefan Wahren unsigned int frame_len) 2662f207cbfSStefan Wahren { 2672f207cbfSStefan Wahren struct mse102x_net_spi *mses = to_mse102x_spi(mse); 2682f207cbfSStefan Wahren struct spi_transfer *xfer = &mses->spi_xfer; 2692f207cbfSStefan Wahren struct spi_message *msg = &mses->spi_msg; 2702f207cbfSStefan Wahren __be16 *sof = (__be16 *)buff; 2712f207cbfSStefan Wahren __be16 *dft = (__be16 *)(buff + DET_SOF_LEN + frame_len); 2722f207cbfSStefan Wahren int ret; 2732f207cbfSStefan Wahren 2742f207cbfSStefan Wahren xfer->rx_buf = buff; 2752f207cbfSStefan Wahren xfer->tx_buf = NULL; 2762f207cbfSStefan Wahren xfer->len = DET_SOF_LEN + frame_len + DET_DFT_LEN; 2772f207cbfSStefan Wahren 2782f207cbfSStefan Wahren ret = spi_sync(mses->spidev, msg); 2792f207cbfSStefan Wahren if (ret < 0) { 2802f207cbfSStefan Wahren netdev_err(mse->ndev, "%s: spi_sync() failed: %d\n", 2812f207cbfSStefan Wahren __func__, ret); 2822f207cbfSStefan Wahren mse->stats.xfer_err++; 2832f207cbfSStefan Wahren } else if (*sof != cpu_to_be16(DET_SOF)) { 2842f207cbfSStefan Wahren netdev_dbg(mse->ndev, "%s: SPI start of frame is invalid (0x%04x)\n", 2852f207cbfSStefan Wahren __func__, *sof); 2862f207cbfSStefan Wahren mse->stats.invalid_sof++; 2872f207cbfSStefan Wahren ret = -EIO; 2882f207cbfSStefan Wahren } else if (*dft != cpu_to_be16(DET_DFT)) { 2892f207cbfSStefan Wahren netdev_dbg(mse->ndev, "%s: SPI frame tail is invalid (0x%04x)\n", 2902f207cbfSStefan Wahren __func__, *dft); 2912f207cbfSStefan Wahren mse->stats.invalid_dft++; 2922f207cbfSStefan Wahren ret = -EIO; 2932f207cbfSStefan Wahren } 2942f207cbfSStefan Wahren 2952f207cbfSStefan Wahren return ret; 2962f207cbfSStefan Wahren } 2972f207cbfSStefan Wahren 2982f207cbfSStefan Wahren static void mse102x_dump_packet(const char *msg, int len, const char *data) 2992f207cbfSStefan Wahren { 3002f207cbfSStefan Wahren printk(KERN_DEBUG ": %s - packet len:%d\n", msg, len); 3012f207cbfSStefan Wahren print_hex_dump(KERN_DEBUG, "pk data: ", DUMP_PREFIX_OFFSET, 16, 1, 3022f207cbfSStefan Wahren data, len, true); 3032f207cbfSStefan Wahren } 3042f207cbfSStefan Wahren 3052f207cbfSStefan Wahren static void mse102x_rx_pkt_spi(struct mse102x_net *mse) 3062f207cbfSStefan Wahren { 3072f207cbfSStefan Wahren struct sk_buff *skb; 3082f207cbfSStefan Wahren unsigned int rxalign; 3092f207cbfSStefan Wahren unsigned int rxlen; 3102f207cbfSStefan Wahren __be16 rx = 0; 3112f207cbfSStefan Wahren u16 cmd_resp; 3122f207cbfSStefan Wahren u8 *rxpkt; 3132f207cbfSStefan Wahren int ret; 3142f207cbfSStefan Wahren 3152f207cbfSStefan Wahren mse102x_tx_cmd_spi(mse, CMD_CTR); 3162f207cbfSStefan Wahren ret = mse102x_rx_cmd_spi(mse, (u8 *)&rx); 3172f207cbfSStefan Wahren cmd_resp = be16_to_cpu(rx); 3182f207cbfSStefan Wahren 3192f207cbfSStefan Wahren if (ret || ((cmd_resp & CMD_MASK) != CMD_RTS)) { 3202f207cbfSStefan Wahren usleep_range(50, 100); 3212f207cbfSStefan Wahren 3222f207cbfSStefan Wahren mse102x_tx_cmd_spi(mse, CMD_CTR); 3232f207cbfSStefan Wahren ret = mse102x_rx_cmd_spi(mse, (u8 *)&rx); 3242f207cbfSStefan Wahren if (ret) 3252f207cbfSStefan Wahren return; 3262f207cbfSStefan Wahren 3272f207cbfSStefan Wahren cmd_resp = be16_to_cpu(rx); 3282f207cbfSStefan Wahren if ((cmd_resp & CMD_MASK) != CMD_RTS) { 3292f207cbfSStefan Wahren net_dbg_ratelimited("%s: Unexpected response (0x%04x)\n", 3302f207cbfSStefan Wahren __func__, cmd_resp); 3312f207cbfSStefan Wahren mse->stats.invalid_rts++; 3322f207cbfSStefan Wahren return; 3332f207cbfSStefan Wahren } 3342f207cbfSStefan Wahren 3352f207cbfSStefan Wahren net_dbg_ratelimited("%s: Unexpected response to first CMD\n", 3362f207cbfSStefan Wahren __func__); 3372f207cbfSStefan Wahren } 3382f207cbfSStefan Wahren 3392f207cbfSStefan Wahren rxlen = cmd_resp & LEN_MASK; 3402f207cbfSStefan Wahren if (!rxlen) { 3412f207cbfSStefan Wahren net_dbg_ratelimited("%s: No frame length defined\n", __func__); 3422f207cbfSStefan Wahren mse->stats.invalid_len++; 3432f207cbfSStefan Wahren return; 3442f207cbfSStefan Wahren } 3452f207cbfSStefan Wahren 3462f207cbfSStefan Wahren rxalign = ALIGN(rxlen + DET_SOF_LEN + DET_DFT_LEN, 4); 3472f207cbfSStefan Wahren skb = netdev_alloc_skb_ip_align(mse->ndev, rxalign); 3482f207cbfSStefan Wahren if (!skb) 3492f207cbfSStefan Wahren return; 3502f207cbfSStefan Wahren 3512f207cbfSStefan Wahren /* 2 bytes Start of frame (before ethernet header) 3522f207cbfSStefan Wahren * 2 bytes Data frame tail (after ethernet frame) 3532f207cbfSStefan Wahren * They are copied, but ignored. 3542f207cbfSStefan Wahren */ 3552f207cbfSStefan Wahren rxpkt = skb_put(skb, rxlen) - DET_SOF_LEN; 3562f207cbfSStefan Wahren if (mse102x_rx_frame_spi(mse, rxpkt, rxlen)) { 3572f207cbfSStefan Wahren mse->ndev->stats.rx_errors++; 3582f207cbfSStefan Wahren dev_kfree_skb(skb); 3592f207cbfSStefan Wahren return; 3602f207cbfSStefan Wahren } 3612f207cbfSStefan Wahren 3622f207cbfSStefan Wahren if (netif_msg_pktdata(mse)) 3632f207cbfSStefan Wahren mse102x_dump_packet(__func__, skb->len, skb->data); 3642f207cbfSStefan Wahren 3652f207cbfSStefan Wahren skb->protocol = eth_type_trans(skb, mse->ndev); 36690f77c1cSSebastian Andrzej Siewior netif_rx(skb); 3672f207cbfSStefan Wahren 3682f207cbfSStefan Wahren mse->ndev->stats.rx_packets++; 3692f207cbfSStefan Wahren mse->ndev->stats.rx_bytes += rxlen; 3702f207cbfSStefan Wahren } 3712f207cbfSStefan Wahren 3722f207cbfSStefan Wahren static int mse102x_tx_pkt_spi(struct mse102x_net *mse, struct sk_buff *txb, 3732f207cbfSStefan Wahren unsigned long work_timeout) 3742f207cbfSStefan Wahren { 3752f207cbfSStefan Wahren unsigned int pad = 0; 3762f207cbfSStefan Wahren __be16 rx = 0; 3772f207cbfSStefan Wahren u16 cmd_resp; 3782f207cbfSStefan Wahren int ret; 3792f207cbfSStefan Wahren bool first = true; 3802f207cbfSStefan Wahren 3812f207cbfSStefan Wahren if (txb->len < 60) 3822f207cbfSStefan Wahren pad = 60 - txb->len; 3832f207cbfSStefan Wahren 3842f207cbfSStefan Wahren while (1) { 3852f207cbfSStefan Wahren mse102x_tx_cmd_spi(mse, CMD_RTS | (txb->len + pad)); 3862f207cbfSStefan Wahren ret = mse102x_rx_cmd_spi(mse, (u8 *)&rx); 3872f207cbfSStefan Wahren cmd_resp = be16_to_cpu(rx); 3882f207cbfSStefan Wahren 3892f207cbfSStefan Wahren if (!ret) { 3902f207cbfSStefan Wahren /* ready to send frame ? */ 3912f207cbfSStefan Wahren if (cmd_resp == CMD_CTR) 3922f207cbfSStefan Wahren break; 3932f207cbfSStefan Wahren 3942f207cbfSStefan Wahren net_dbg_ratelimited("%s: Unexpected response (0x%04x)\n", 3952f207cbfSStefan Wahren __func__, cmd_resp); 3962f207cbfSStefan Wahren mse->stats.invalid_ctr++; 3972f207cbfSStefan Wahren } 3982f207cbfSStefan Wahren 3992f207cbfSStefan Wahren /* It's not predictable how long / many retries it takes to 4002f207cbfSStefan Wahren * send at least one packet, so TX timeouts are possible. 4012f207cbfSStefan Wahren * That's the reason why the netdev watchdog is not used here. 4022f207cbfSStefan Wahren */ 4032f207cbfSStefan Wahren if (time_after(jiffies, work_timeout)) 4042f207cbfSStefan Wahren return -ETIMEDOUT; 4052f207cbfSStefan Wahren 4062f207cbfSStefan Wahren if (first) { 4072f207cbfSStefan Wahren /* throttle at first issue */ 4082f207cbfSStefan Wahren netif_stop_queue(mse->ndev); 4092f207cbfSStefan Wahren /* fast retry */ 4102f207cbfSStefan Wahren usleep_range(50, 100); 4112f207cbfSStefan Wahren first = false; 4122f207cbfSStefan Wahren } else { 4132f207cbfSStefan Wahren msleep(20); 4142f207cbfSStefan Wahren } 415431b9b4dSYang Li } 4162f207cbfSStefan Wahren 4172f207cbfSStefan Wahren ret = mse102x_tx_frame_spi(mse, txb, pad); 4182f207cbfSStefan Wahren if (ret) 4192f207cbfSStefan Wahren net_dbg_ratelimited("%s: Failed to send (%d), drop frame\n", 4202f207cbfSStefan Wahren __func__, ret); 4212f207cbfSStefan Wahren 4222f207cbfSStefan Wahren return ret; 4232f207cbfSStefan Wahren } 4242f207cbfSStefan Wahren 4252f207cbfSStefan Wahren #define TX_QUEUE_MAX 10 4262f207cbfSStefan Wahren 4272f207cbfSStefan Wahren static void mse102x_tx_work(struct work_struct *work) 4282f207cbfSStefan Wahren { 4292f207cbfSStefan Wahren /* Make sure timeout is sufficient to transfer TX_QUEUE_MAX frames */ 4302f207cbfSStefan Wahren unsigned long work_timeout = jiffies + msecs_to_jiffies(1000); 4312f207cbfSStefan Wahren struct mse102x_net_spi *mses; 4322f207cbfSStefan Wahren struct mse102x_net *mse; 4332f207cbfSStefan Wahren struct sk_buff *txb; 4342f207cbfSStefan Wahren int ret = 0; 4352f207cbfSStefan Wahren 4362f207cbfSStefan Wahren mses = container_of(work, struct mse102x_net_spi, tx_work); 4372f207cbfSStefan Wahren mse = &mses->mse102x; 4382f207cbfSStefan Wahren 4392f207cbfSStefan Wahren while ((txb = skb_dequeue(&mse->txq))) { 440*fa8d2d14SStefan Wahren unsigned int len = max_t(unsigned int, txb->len, ETH_ZLEN); 441*fa8d2d14SStefan Wahren 4422f207cbfSStefan Wahren mutex_lock(&mses->lock); 4432f207cbfSStefan Wahren ret = mse102x_tx_pkt_spi(mse, txb, work_timeout); 4442f207cbfSStefan Wahren mutex_unlock(&mses->lock); 4452f207cbfSStefan Wahren if (ret) { 4462f207cbfSStefan Wahren mse->ndev->stats.tx_dropped++; 4472f207cbfSStefan Wahren } else { 448*fa8d2d14SStefan Wahren mse->ndev->stats.tx_bytes += len; 4492f207cbfSStefan Wahren mse->ndev->stats.tx_packets++; 4502f207cbfSStefan Wahren } 4512f207cbfSStefan Wahren 4522f207cbfSStefan Wahren dev_kfree_skb(txb); 4532f207cbfSStefan Wahren } 4542f207cbfSStefan Wahren 4552f207cbfSStefan Wahren if (ret == -ETIMEDOUT) { 4562f207cbfSStefan Wahren if (netif_msg_timer(mse)) 4572f207cbfSStefan Wahren netdev_err(mse->ndev, "tx work timeout\n"); 4582f207cbfSStefan Wahren 4592f207cbfSStefan Wahren mse->stats.tx_timeout++; 4602f207cbfSStefan Wahren } 4612f207cbfSStefan Wahren 4622f207cbfSStefan Wahren netif_wake_queue(mse->ndev); 4632f207cbfSStefan Wahren } 4642f207cbfSStefan Wahren 4652f207cbfSStefan Wahren static netdev_tx_t mse102x_start_xmit_spi(struct sk_buff *skb, 4662f207cbfSStefan Wahren struct net_device *ndev) 4672f207cbfSStefan Wahren { 4682f207cbfSStefan Wahren struct mse102x_net *mse = netdev_priv(ndev); 4692f207cbfSStefan Wahren struct mse102x_net_spi *mses = to_mse102x_spi(mse); 4702f207cbfSStefan Wahren 4712f207cbfSStefan Wahren netif_dbg(mse, tx_queued, ndev, 4722f207cbfSStefan Wahren "%s: skb %p, %d@%p\n", __func__, skb, skb->len, skb->data); 4732f207cbfSStefan Wahren 4742f207cbfSStefan Wahren skb_queue_tail(&mse->txq, skb); 4752f207cbfSStefan Wahren 4762f207cbfSStefan Wahren if (skb_queue_len(&mse->txq) >= TX_QUEUE_MAX) 4772f207cbfSStefan Wahren netif_stop_queue(ndev); 4782f207cbfSStefan Wahren 4792f207cbfSStefan Wahren schedule_work(&mses->tx_work); 4802f207cbfSStefan Wahren 4812f207cbfSStefan Wahren return NETDEV_TX_OK; 4822f207cbfSStefan Wahren } 4832f207cbfSStefan Wahren 4842f207cbfSStefan Wahren static void mse102x_init_mac(struct mse102x_net *mse, struct device_node *np) 4852f207cbfSStefan Wahren { 4862f207cbfSStefan Wahren struct net_device *ndev = mse->ndev; 4872f207cbfSStefan Wahren int ret = of_get_ethdev_address(np, ndev); 4882f207cbfSStefan Wahren 4892f207cbfSStefan Wahren if (ret) { 4902f207cbfSStefan Wahren eth_hw_addr_random(ndev); 4912f207cbfSStefan Wahren netdev_err(ndev, "Using random MAC address: %pM\n", 4922f207cbfSStefan Wahren ndev->dev_addr); 4932f207cbfSStefan Wahren } 4942f207cbfSStefan Wahren } 4952f207cbfSStefan Wahren 4962f207cbfSStefan Wahren /* Assumption: this is called for every incoming packet */ 4972f207cbfSStefan Wahren static irqreturn_t mse102x_irq(int irq, void *_mse) 4982f207cbfSStefan Wahren { 4992f207cbfSStefan Wahren struct mse102x_net *mse = _mse; 5002f207cbfSStefan Wahren struct mse102x_net_spi *mses = to_mse102x_spi(mse); 5012f207cbfSStefan Wahren 5022f207cbfSStefan Wahren mutex_lock(&mses->lock); 5032f207cbfSStefan Wahren mse102x_rx_pkt_spi(mse); 5042f207cbfSStefan Wahren mutex_unlock(&mses->lock); 5052f207cbfSStefan Wahren 5062f207cbfSStefan Wahren return IRQ_HANDLED; 5072f207cbfSStefan Wahren } 5082f207cbfSStefan Wahren 5092f207cbfSStefan Wahren static int mse102x_net_open(struct net_device *ndev) 5102f207cbfSStefan Wahren { 5112f207cbfSStefan Wahren struct mse102x_net *mse = netdev_priv(ndev); 5122f207cbfSStefan Wahren int ret; 5132f207cbfSStefan Wahren 5142f207cbfSStefan Wahren ret = request_threaded_irq(ndev->irq, NULL, mse102x_irq, IRQF_ONESHOT, 5152f207cbfSStefan Wahren ndev->name, mse); 5162f207cbfSStefan Wahren if (ret < 0) { 5172f207cbfSStefan Wahren netdev_err(ndev, "Failed to get irq: %d\n", ret); 5182f207cbfSStefan Wahren return ret; 5192f207cbfSStefan Wahren } 5202f207cbfSStefan Wahren 5212f207cbfSStefan Wahren netif_dbg(mse, ifup, ndev, "opening\n"); 5222f207cbfSStefan Wahren 5232f207cbfSStefan Wahren netif_start_queue(ndev); 5242f207cbfSStefan Wahren 5252f207cbfSStefan Wahren netif_carrier_on(ndev); 5262f207cbfSStefan Wahren 5272f207cbfSStefan Wahren netif_dbg(mse, ifup, ndev, "network device up\n"); 5282f207cbfSStefan Wahren 5292f207cbfSStefan Wahren return 0; 5302f207cbfSStefan Wahren } 5312f207cbfSStefan Wahren 5322f207cbfSStefan Wahren static int mse102x_net_stop(struct net_device *ndev) 5332f207cbfSStefan Wahren { 5342f207cbfSStefan Wahren struct mse102x_net *mse = netdev_priv(ndev); 5352f207cbfSStefan Wahren struct mse102x_net_spi *mses = to_mse102x_spi(mse); 5362f207cbfSStefan Wahren 5372f207cbfSStefan Wahren netif_info(mse, ifdown, ndev, "shutting down\n"); 5382f207cbfSStefan Wahren 5392f207cbfSStefan Wahren netif_carrier_off(mse->ndev); 5402f207cbfSStefan Wahren 5412f207cbfSStefan Wahren /* stop any outstanding work */ 5422f207cbfSStefan Wahren flush_work(&mses->tx_work); 5432f207cbfSStefan Wahren 5442f207cbfSStefan Wahren netif_stop_queue(ndev); 5452f207cbfSStefan Wahren 5462f207cbfSStefan Wahren skb_queue_purge(&mse->txq); 5472f207cbfSStefan Wahren 5482f207cbfSStefan Wahren free_irq(ndev->irq, mse); 5492f207cbfSStefan Wahren 5502f207cbfSStefan Wahren return 0; 5512f207cbfSStefan Wahren } 5522f207cbfSStefan Wahren 5532f207cbfSStefan Wahren static const struct net_device_ops mse102x_netdev_ops = { 5542f207cbfSStefan Wahren .ndo_open = mse102x_net_open, 5552f207cbfSStefan Wahren .ndo_stop = mse102x_net_stop, 5562f207cbfSStefan Wahren .ndo_start_xmit = mse102x_start_xmit_spi, 5572f207cbfSStefan Wahren .ndo_set_mac_address = eth_mac_addr, 5582f207cbfSStefan Wahren .ndo_validate_addr = eth_validate_addr, 5592f207cbfSStefan Wahren }; 5602f207cbfSStefan Wahren 5612f207cbfSStefan Wahren /* ethtool support */ 5622f207cbfSStefan Wahren 5632f207cbfSStefan Wahren static void mse102x_get_drvinfo(struct net_device *ndev, 5642f207cbfSStefan Wahren struct ethtool_drvinfo *di) 5652f207cbfSStefan Wahren { 5662f207cbfSStefan Wahren strscpy(di->driver, DRV_NAME, sizeof(di->driver)); 5672f207cbfSStefan Wahren strscpy(di->bus_info, dev_name(ndev->dev.parent), sizeof(di->bus_info)); 5682f207cbfSStefan Wahren } 5692f207cbfSStefan Wahren 5702f207cbfSStefan Wahren static u32 mse102x_get_msglevel(struct net_device *ndev) 5712f207cbfSStefan Wahren { 5722f207cbfSStefan Wahren struct mse102x_net *mse = netdev_priv(ndev); 5732f207cbfSStefan Wahren 5742f207cbfSStefan Wahren return mse->msg_enable; 5752f207cbfSStefan Wahren } 5762f207cbfSStefan Wahren 5772f207cbfSStefan Wahren static void mse102x_set_msglevel(struct net_device *ndev, u32 to) 5782f207cbfSStefan Wahren { 5792f207cbfSStefan Wahren struct mse102x_net *mse = netdev_priv(ndev); 5802f207cbfSStefan Wahren 5812f207cbfSStefan Wahren mse->msg_enable = to; 5822f207cbfSStefan Wahren } 5832f207cbfSStefan Wahren 5842f207cbfSStefan Wahren static void mse102x_get_ethtool_stats(struct net_device *ndev, 5852f207cbfSStefan Wahren struct ethtool_stats *estats, u64 *data) 5862f207cbfSStefan Wahren { 5872f207cbfSStefan Wahren struct mse102x_net *mse = netdev_priv(ndev); 5882f207cbfSStefan Wahren struct mse102x_stats *st = &mse->stats; 5892f207cbfSStefan Wahren 5902f207cbfSStefan Wahren memcpy(data, st, ARRAY_SIZE(mse102x_gstrings_stats) * sizeof(u64)); 5912f207cbfSStefan Wahren } 5922f207cbfSStefan Wahren 5932f207cbfSStefan Wahren static void mse102x_get_strings(struct net_device *ndev, u32 stringset, u8 *buf) 5942f207cbfSStefan Wahren { 5952f207cbfSStefan Wahren switch (stringset) { 5962f207cbfSStefan Wahren case ETH_SS_STATS: 5972f207cbfSStefan Wahren memcpy(buf, &mse102x_gstrings_stats, 5982f207cbfSStefan Wahren sizeof(mse102x_gstrings_stats)); 5992f207cbfSStefan Wahren break; 6002f207cbfSStefan Wahren default: 6012f207cbfSStefan Wahren WARN_ON(1); 6022f207cbfSStefan Wahren break; 6032f207cbfSStefan Wahren } 6042f207cbfSStefan Wahren } 6052f207cbfSStefan Wahren 6062f207cbfSStefan Wahren static int mse102x_get_sset_count(struct net_device *ndev, int sset) 6072f207cbfSStefan Wahren { 6082f207cbfSStefan Wahren switch (sset) { 6092f207cbfSStefan Wahren case ETH_SS_STATS: 6102f207cbfSStefan Wahren return ARRAY_SIZE(mse102x_gstrings_stats); 6112f207cbfSStefan Wahren default: 6122f207cbfSStefan Wahren return -EINVAL; 6132f207cbfSStefan Wahren } 6142f207cbfSStefan Wahren } 6152f207cbfSStefan Wahren 6162f207cbfSStefan Wahren static const struct ethtool_ops mse102x_ethtool_ops = { 6172f207cbfSStefan Wahren .get_drvinfo = mse102x_get_drvinfo, 6182f207cbfSStefan Wahren .get_link = ethtool_op_get_link, 6192f207cbfSStefan Wahren .get_msglevel = mse102x_get_msglevel, 6202f207cbfSStefan Wahren .set_msglevel = mse102x_set_msglevel, 6212f207cbfSStefan Wahren .get_ethtool_stats = mse102x_get_ethtool_stats, 6222f207cbfSStefan Wahren .get_strings = mse102x_get_strings, 6232f207cbfSStefan Wahren .get_sset_count = mse102x_get_sset_count, 6242f207cbfSStefan Wahren }; 6252f207cbfSStefan Wahren 6262f207cbfSStefan Wahren /* driver bus management functions */ 6272f207cbfSStefan Wahren 6282f207cbfSStefan Wahren #ifdef CONFIG_PM_SLEEP 6292f207cbfSStefan Wahren 6302f207cbfSStefan Wahren static int mse102x_suspend(struct device *dev) 6312f207cbfSStefan Wahren { 6322f207cbfSStefan Wahren struct mse102x_net *mse = dev_get_drvdata(dev); 6332f207cbfSStefan Wahren struct net_device *ndev = mse->ndev; 6342f207cbfSStefan Wahren 6352f207cbfSStefan Wahren if (netif_running(ndev)) { 6362f207cbfSStefan Wahren netif_device_detach(ndev); 6372f207cbfSStefan Wahren mse102x_net_stop(ndev); 6382f207cbfSStefan Wahren } 6392f207cbfSStefan Wahren 6402f207cbfSStefan Wahren return 0; 6412f207cbfSStefan Wahren } 6422f207cbfSStefan Wahren 6432f207cbfSStefan Wahren static int mse102x_resume(struct device *dev) 6442f207cbfSStefan Wahren { 6452f207cbfSStefan Wahren struct mse102x_net *mse = dev_get_drvdata(dev); 6462f207cbfSStefan Wahren struct net_device *ndev = mse->ndev; 6472f207cbfSStefan Wahren 6482f207cbfSStefan Wahren if (netif_running(ndev)) { 6492f207cbfSStefan Wahren mse102x_net_open(ndev); 6502f207cbfSStefan Wahren netif_device_attach(ndev); 6512f207cbfSStefan Wahren } 6522f207cbfSStefan Wahren 6532f207cbfSStefan Wahren return 0; 6542f207cbfSStefan Wahren } 6552f207cbfSStefan Wahren #endif 6562f207cbfSStefan Wahren 6572f207cbfSStefan Wahren static SIMPLE_DEV_PM_OPS(mse102x_pm_ops, mse102x_suspend, mse102x_resume); 6582f207cbfSStefan Wahren 6592f207cbfSStefan Wahren static int mse102x_probe_spi(struct spi_device *spi) 6602f207cbfSStefan Wahren { 6612f207cbfSStefan Wahren struct device *dev = &spi->dev; 6622f207cbfSStefan Wahren struct mse102x_net_spi *mses; 6632f207cbfSStefan Wahren struct net_device *ndev; 6642f207cbfSStefan Wahren struct mse102x_net *mse; 6652f207cbfSStefan Wahren int ret; 6662f207cbfSStefan Wahren 6672f207cbfSStefan Wahren spi->bits_per_word = 8; 6682f207cbfSStefan Wahren spi->mode |= SPI_MODE_3; 6692f207cbfSStefan Wahren /* enforce minimum speed to ensure device functionality */ 6702f207cbfSStefan Wahren spi->master->min_speed_hz = MIN_FREQ_HZ; 6712f207cbfSStefan Wahren 6722f207cbfSStefan Wahren if (!spi->max_speed_hz) 6732f207cbfSStefan Wahren spi->max_speed_hz = MAX_FREQ_HZ; 6742f207cbfSStefan Wahren 6752f207cbfSStefan Wahren if (spi->max_speed_hz < MIN_FREQ_HZ || 6762f207cbfSStefan Wahren spi->max_speed_hz > MAX_FREQ_HZ) { 6772f207cbfSStefan Wahren dev_err(&spi->dev, "SPI max frequency out of range (min: %u, max: %u)\n", 6782f207cbfSStefan Wahren MIN_FREQ_HZ, MAX_FREQ_HZ); 6792f207cbfSStefan Wahren return -EINVAL; 6802f207cbfSStefan Wahren } 6812f207cbfSStefan Wahren 6822f207cbfSStefan Wahren ret = spi_setup(spi); 6832f207cbfSStefan Wahren if (ret < 0) { 6842f207cbfSStefan Wahren dev_err(&spi->dev, "Unable to setup SPI device: %d\n", ret); 6852f207cbfSStefan Wahren return ret; 6862f207cbfSStefan Wahren } 6872f207cbfSStefan Wahren 6882f207cbfSStefan Wahren ndev = devm_alloc_etherdev(dev, sizeof(struct mse102x_net_spi)); 6892f207cbfSStefan Wahren if (!ndev) 6902f207cbfSStefan Wahren return -ENOMEM; 6912f207cbfSStefan Wahren 6922f207cbfSStefan Wahren ndev->needed_tailroom += ALIGN(DET_DFT_LEN, 4); 6932f207cbfSStefan Wahren ndev->needed_headroom += ALIGN(DET_SOF_LEN, 4); 6942f207cbfSStefan Wahren ndev->priv_flags &= ~IFF_TX_SKB_SHARING; 6952f207cbfSStefan Wahren ndev->tx_queue_len = 100; 6962f207cbfSStefan Wahren 6972f207cbfSStefan Wahren mse = netdev_priv(ndev); 6982f207cbfSStefan Wahren mses = to_mse102x_spi(mse); 6992f207cbfSStefan Wahren 7002f207cbfSStefan Wahren mses->spidev = spi; 7012f207cbfSStefan Wahren mutex_init(&mses->lock); 7022f207cbfSStefan Wahren INIT_WORK(&mses->tx_work, mse102x_tx_work); 7032f207cbfSStefan Wahren 7042f207cbfSStefan Wahren /* initialise pre-made spi transfer messages */ 7052f207cbfSStefan Wahren spi_message_init(&mses->spi_msg); 7062f207cbfSStefan Wahren spi_message_add_tail(&mses->spi_xfer, &mses->spi_msg); 7072f207cbfSStefan Wahren 7082f207cbfSStefan Wahren ndev->irq = spi->irq; 7092f207cbfSStefan Wahren mse->ndev = ndev; 7102f207cbfSStefan Wahren 7112f207cbfSStefan Wahren /* set the default message enable */ 7122f207cbfSStefan Wahren mse->msg_enable = netif_msg_init(-1, MSG_DEFAULT); 7132f207cbfSStefan Wahren 7142f207cbfSStefan Wahren skb_queue_head_init(&mse->txq); 7152f207cbfSStefan Wahren 7162f207cbfSStefan Wahren SET_NETDEV_DEV(ndev, dev); 7172f207cbfSStefan Wahren 7182f207cbfSStefan Wahren dev_set_drvdata(dev, mse); 7192f207cbfSStefan Wahren 7202f207cbfSStefan Wahren netif_carrier_off(mse->ndev); 7212f207cbfSStefan Wahren ndev->netdev_ops = &mse102x_netdev_ops; 7222f207cbfSStefan Wahren ndev->ethtool_ops = &mse102x_ethtool_ops; 7232f207cbfSStefan Wahren 7242f207cbfSStefan Wahren mse102x_init_mac(mse, dev->of_node); 7252f207cbfSStefan Wahren 7262f207cbfSStefan Wahren ret = register_netdev(ndev); 7272f207cbfSStefan Wahren if (ret) { 7282f207cbfSStefan Wahren dev_err(dev, "failed to register network device: %d\n", ret); 7292f207cbfSStefan Wahren return ret; 7302f207cbfSStefan Wahren } 7312f207cbfSStefan Wahren 7322f207cbfSStefan Wahren mse102x_init_device_debugfs(mses); 7332f207cbfSStefan Wahren 7342f207cbfSStefan Wahren return 0; 7352f207cbfSStefan Wahren } 7362f207cbfSStefan Wahren 737a0386bbaSUwe Kleine-König static void mse102x_remove_spi(struct spi_device *spi) 7382f207cbfSStefan Wahren { 7392f207cbfSStefan Wahren struct mse102x_net *mse = dev_get_drvdata(&spi->dev); 7402f207cbfSStefan Wahren struct mse102x_net_spi *mses = to_mse102x_spi(mse); 7412f207cbfSStefan Wahren 7422f207cbfSStefan Wahren if (netif_msg_drv(mse)) 7432f207cbfSStefan Wahren dev_info(&spi->dev, "remove\n"); 7442f207cbfSStefan Wahren 7452f207cbfSStefan Wahren mse102x_remove_device_debugfs(mses); 7462f207cbfSStefan Wahren unregister_netdev(mse->ndev); 7472f207cbfSStefan Wahren } 7482f207cbfSStefan Wahren 7492f207cbfSStefan Wahren static const struct of_device_id mse102x_match_table[] = { 7502f207cbfSStefan Wahren { .compatible = "vertexcom,mse1021" }, 7512f207cbfSStefan Wahren { .compatible = "vertexcom,mse1022" }, 7522f207cbfSStefan Wahren { } 7532f207cbfSStefan Wahren }; 7542f207cbfSStefan Wahren MODULE_DEVICE_TABLE(of, mse102x_match_table); 7552f207cbfSStefan Wahren 7561bba1998SWei Yongjun static const struct spi_device_id mse102x_ids[] = { 7571bba1998SWei Yongjun { "mse1021" }, 7581bba1998SWei Yongjun { "mse1022" }, 7591bba1998SWei Yongjun { } 7601bba1998SWei Yongjun }; 7611bba1998SWei Yongjun MODULE_DEVICE_TABLE(spi, mse102x_ids); 7621bba1998SWei Yongjun 7632f207cbfSStefan Wahren static struct spi_driver mse102x_driver = { 7642f207cbfSStefan Wahren .driver = { 7652f207cbfSStefan Wahren .name = DRV_NAME, 7662f207cbfSStefan Wahren .of_match_table = mse102x_match_table, 7672f207cbfSStefan Wahren .pm = &mse102x_pm_ops, 7682f207cbfSStefan Wahren }, 7692f207cbfSStefan Wahren .probe = mse102x_probe_spi, 7702f207cbfSStefan Wahren .remove = mse102x_remove_spi, 7711bba1998SWei Yongjun .id_table = mse102x_ids, 7722f207cbfSStefan Wahren }; 7732f207cbfSStefan Wahren module_spi_driver(mse102x_driver); 7742f207cbfSStefan Wahren 7752f207cbfSStefan Wahren MODULE_DESCRIPTION("MSE102x Network driver"); 77656cb6a59SStefan Wahren MODULE_AUTHOR("Stefan Wahren <stefan.wahren@chargebyte.com>"); 7772f207cbfSStefan Wahren MODULE_LICENSE("GPL"); 7782f207cbfSStefan Wahren MODULE_ALIAS("spi:" DRV_NAME); 779