root/drivers/net/ethernet/vertexcom/mse102x.c
// 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);