| // 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/if_vlan.h> | |
| #include <linux/interrupt.h> | |
| #include <linux/irq.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/string_choices.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 - 2, 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_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 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; | |
| bool valid_cmd_received; | |
| #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); | |
| seq_printf(s, "Received valid CMD once : %s\n", | |
| str_yes_no(mses->valid_cmd_received)); | |
| 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); | |
| ret = -EIO; | |
| } else { | |
| memcpy(rxb, trx + 2, 2); | |
| mses->valid_cmd_received = true; | |
| } | |
| 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 = NULL; | |
| 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; | |
| 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++; | |
| } | |
| dev_kfree_skb(tskb); | |
| return ret; | |
| } | |
| static int mse102x_rx_frame_spi(struct mse102x_net *mse, u8 *buff, | |
| unsigned int frame_len, bool drop) | |
| { | |
| 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 (drop) { | |
| netdev_dbg(mse->ndev, "%s: Drop frame\n", __func__); | |
| ret = -EINVAL; | |
| } 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 irqreturn_t mse102x_rx_pkt_spi(struct mse102x_net *mse) | |
| { | |
| struct sk_buff *skb; | |
| unsigned int rxalign; | |
| unsigned int rxlen; | |
| bool drop = false; | |
| __be16 rx = 0; | |
| u16 cmd_resp; | |
| u8 *rxpkt; | |
| mse102x_tx_cmd_spi(mse, CMD_CTR); | |
| if (mse102x_rx_cmd_spi(mse, (u8 *)&rx)) { | |
| usleep_range(50, 100); | |
| return IRQ_NONE; | |
| } | |
| 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++; | |
| drop = true; | |
| goto drop; | |
| } | |
| rxlen = cmd_resp & LEN_MASK; | |
| if (rxlen < ETH_ZLEN || rxlen > VLAN_ETH_FRAME_LEN) { | |
| net_dbg_ratelimited("%s: Invalid frame length: %d\n", __func__, | |
| rxlen); | |
| mse->stats.invalid_len++; | |
| drop = true; | |
| } | |
| /* In case of a invalid CMD_RTS, the frame must be consumed anyway. | |
| * So assume the maximum possible frame length. | |
| */ | |
| drop: | |
| if (drop) | |
| rxlen = VLAN_ETH_FRAME_LEN; | |
| rxalign = ALIGN(rxlen + DET_SOF_LEN + DET_DFT_LEN, 4); | |
| skb = netdev_alloc_skb_ip_align(mse->ndev, rxalign); | |
| if (!skb) | |
| return IRQ_NONE; | |
| /* 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, drop)) { | |
| mse->ndev->stats.rx_errors++; | |
| dev_kfree_skb(skb); | |
| return IRQ_HANDLED; | |
| } | |
| 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; | |
| return IRQ_HANDLED; | |
| } | |
| 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 < ETH_ZLEN) | |
| pad = ETH_ZLEN - 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))) { | |
| unsigned int len = max_t(unsigned int, txb->len, ETH_ZLEN); | |
| 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 += len; | |
| mse->ndev->stats.tx_packets++; | |
| } | |
| dev_kfree_skb(txb); | |
| } | |
| if (ret == -ETIMEDOUT) { | |
| if (netif_msg_timer(mse)) | |
| netdev_err_once(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); | |
| dev_warn(ndev->dev.parent, "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); | |
| irqreturn_t ret; | |
| mutex_lock(&mses->lock); | |
| ret = mse102x_rx_pkt_spi(mse); | |
| mutex_unlock(&mses->lock); | |
| return ret; | |
| } | |
| static int mse102x_net_open(struct net_device *ndev) | |
| { | |
| struct irq_data *irq_data = irq_get_irq_data(ndev->irq); | |
| struct mse102x_net *mse = netdev_priv(ndev); | |
| struct mse102x_net_spi *mses = to_mse102x_spi(mse); | |
| int ret; | |
| if (!irq_data) { | |
| netdev_err(ndev, "Invalid IRQ: %d\n", ndev->irq); | |
| return -EINVAL; | |
| } | |
| switch (irqd_get_trigger_type(irq_data)) { | |
| case IRQ_TYPE_LEVEL_HIGH: | |
| case IRQ_TYPE_LEVEL_LOW: | |
| break; | |
| default: | |
| netdev_warn_once(ndev, "Only IRQ type level recommended, please update your device tree firmware.\n"); | |
| break; | |
| } | |
| 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); | |
| /* The SPI interrupt can stuck in case of pending packet(s). | |
| * So poll for possible packet(s) to re-arm the interrupt. | |
| */ | |
| mutex_lock(&mses->lock); | |
| if (mse102x_rx_pkt_spi(mse) == IRQ_NONE) | |
| mse102x_rx_pkt_spi(mse); | |
| mutex_unlock(&mses->lock); | |
| 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 */ | |
| 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; | |
| } | |
| static DEFINE_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->controller->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); | |
| 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 = pm_sleep_ptr(&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); |