--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Management Controller Transport Protocol (MCTP)
+ *
+ * Copyright (c) 2021 Code Construct
+ * Copyright (c) 2021 Google
+ */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
+#include <linux/if_arp.h>
+#include <net/mctp.h>
+#include <net/mctpdevice.h>
+
+/* SMBus 3.0 allows 255 data bytes (plus PEC), but the
+ * first byte is taken for source slave address.
+ */
+#define MCTP_I2C_MAXBLOCK 255
+#define MCTP_I2C_MAXMTU (MCTP_I2C_MAXBLOCK - 1)
+#define MCTP_I2C_MINMTU (64 + 4)
+/* Allow space for address, command, byte_count, databytes, PEC */
+#define MCTP_I2C_RXBUFSZ (3 + MCTP_I2C_MAXBLOCK + 1)
+#define MCTP_I2C_MINLEN 8
+#define MCTP_I2C_COMMANDCODE 0x0f
+#define MCTP_I2C_TX_WORK_LEN 100
+// sufficient for 64kB at min mtu
+#define MCTP_I2C_TX_QUEUE_LEN 1100
+
+#define MCTP_I2C_OF_PROP "mctp-controller"
+
+enum {
+       MCTP_I2C_FLOW_STATE_NEW = 0,
+       MCTP_I2C_FLOW_STATE_ACTIVE,
+};
+
+static struct {
+       /* lock protects clients and also prevents adding/removing adapters
+        * during mctp_i2c_client probe/remove.
+        */
+       struct mutex lock;
+       // list of struct mctp_i2c_client
+       struct list_head clients;
+} mi_driver_state;
+
+struct mctp_i2c_client;
+
+// The netdev structure. One of these per I2C adapter.
+struct mctp_i2c_dev {
+       struct net_device *ndev;
+       struct i2c_adapter *adapter;
+       struct mctp_i2c_client *client;
+       struct list_head list; // for mctp_i2c_client.devs
+
+       size_t pos;
+       u8 buffer[MCTP_I2C_RXBUFSZ];
+
+       struct task_struct *tx_thread;
+       wait_queue_head_t tx_wq;
+       struct sk_buff_head tx_queue;
+
+       // a fake entry in our tx queue to perform an unlock operation
+       struct sk_buff unlock_marker;
+
+       spinlock_t flow_lock; // protects i2c_lock_count and release_count
+       int i2c_lock_count;
+       int release_count;
+};
+
+/* The i2c client structure. One per hardware i2c bus at the top of the
+ * mux tree, shared by multiple netdevs
+ */
+struct mctp_i2c_client {
+       struct i2c_client *client;
+       u8 lladdr;
+
+       struct mctp_i2c_dev *sel;
+       struct list_head devs;
+       spinlock_t curr_lock; // protects sel
+
+       struct list_head list; // for mi_driver_state.clients
+};
+
+// Header on the wire
+struct mctp_i2c_hdr {
+       u8 dest_slave;
+       u8 command;
+       u8 byte_count;
+       u8 source_slave;
+};
+
+static int mctp_i2c_recv(struct mctp_i2c_dev *midev);
+static int mctp_i2c_slave_cb(struct i2c_client *client,
+                            enum i2c_slave_event event, u8 *val);
+
+static struct i2c_adapter *mux_root_adapter(struct i2c_adapter *adap)
+{
+#if IS_ENABLED(CONFIG_I2C_MUX)
+       return i2c_root_adapter(&adap->dev);
+#else
+       /* In non-mux config all i2c adapters are root adapters */
+       return adap;
+#endif
+}
+
+static ssize_t mctp_current_mux_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct mctp_i2c_client *mcli = i2c_get_clientdata(to_i2c_client(dev));
+       struct net_device *ndev = NULL;
+       unsigned long flags;
+       ssize_t l;
+
+       spin_lock_irqsave(&mcli->curr_lock, flags);
+       if (mcli->sel) {
+               ndev = mcli->sel->ndev;
+               dev_hold(ndev);
+       }
+       spin_unlock_irqrestore(&mcli->curr_lock, flags);
+       l = scnprintf(buf, PAGE_SIZE, "%s\n", ndev ? ndev->name : "(none)");
+       if (ndev)
+               dev_put(ndev);
+       return l;
+}
+static DEVICE_ATTR_RO(mctp_current_mux);
+
+/* Creates a new i2c slave device attached to the root adapter.
+ * Sets up the slave callback.
+ * Must be called with a client on a root adapter.
+ */
+static struct mctp_i2c_client *mctp_i2c_new_client(struct i2c_client *client)
+{
+       struct mctp_i2c_client *mcli = NULL;
+       struct i2c_adapter *root = NULL;
+       int rc;
+
+       if (client->flags & I2C_CLIENT_TEN) {
+               dev_err(&client->dev, "%s failed, MCTP requires a 7-bit I2C address, addr=0x%x",
+                       __func__, client->addr);
+               rc = -EINVAL;
+               goto err;
+       }
+
+       root = mux_root_adapter(client->adapter);
+       if (!root) {
+               dev_err(&client->dev, "%s failed to find root adapter\n", __func__);
+               rc = -ENOENT;
+               goto err;
+       }
+       if (root != client->adapter) {
+               dev_err(&client->dev,
+                       "A mctp-i2c-controller client cannot be placed on an I2C mux adapter.\n"
+                       " It should be placed on the mux tree root adapter\n"
+                       " then set mctp-controller property on adapters to attach\n");
+               rc = -EINVAL;
+               goto err;
+       }
+
+       mcli = kzalloc(sizeof(*mcli), GFP_KERNEL);
+       if (!mcli) {
+               rc = -ENOMEM;
+               goto err;
+       }
+       spin_lock_init(&mcli->curr_lock);
+       INIT_LIST_HEAD(&mcli->devs);
+       INIT_LIST_HEAD(&mcli->list);
+       mcli->lladdr = client->addr & 0xff;
+       mcli->client = client;
+       i2c_set_clientdata(client, mcli);
+
+       rc = i2c_slave_register(mcli->client, mctp_i2c_slave_cb);
+       if (rc) {
+               dev_err(&client->dev, "%s i2c register failed %d\n", __func__, rc);
+               mcli->client = NULL;
+               i2c_set_clientdata(client, NULL);
+               goto err;
+       }
+
+       rc = device_create_file(&client->dev, &dev_attr_mctp_current_mux);
+       if (rc) {
+               dev_err(&client->dev, "%s adding sysfs \"%s\" failed %d\n", __func__,
+                       dev_attr_mctp_current_mux.attr.name, rc);
+               // continue anyway
+       }
+
+       return mcli;
+err:
+       if (mcli) {
+               if (mcli->client) {
+                       device_remove_file(&mcli->client->dev, &dev_attr_mctp_current_mux);
+                       i2c_unregister_device(mcli->client);
+               }
+               kfree(mcli);
+       }
+       return ERR_PTR(rc);
+}
+
+static void mctp_i2c_free_client(struct mctp_i2c_client *mcli)
+{
+       int rc;
+
+       WARN_ON(!mutex_is_locked(&mi_driver_state.lock));
+       WARN_ON(!list_empty(&mcli->devs));
+       WARN_ON(mcli->sel); // sanity check, no locking
+
+       device_remove_file(&mcli->client->dev, &dev_attr_mctp_current_mux);
+       rc = i2c_slave_unregister(mcli->client);
+       // leak if it fails, we can't propagate errors upwards
+       if (rc)
+               dev_err(&mcli->client->dev, "%s i2c unregister failed %d\n", __func__, rc);
+       else
+               kfree(mcli);
+}
+
+/* Switch the mctp i2c device to receive responses.
+ * Call with curr_lock held
+ */
+static void __mctp_i2c_device_select(struct mctp_i2c_client *mcli,
+                                    struct mctp_i2c_dev *midev)
+{
+       assert_spin_locked(&mcli->curr_lock);
+       if (midev)
+               dev_hold(midev->ndev);
+       if (mcli->sel)
+               dev_put(mcli->sel->ndev);
+       mcli->sel = midev;
+}
+
+// Switch the mctp i2c device to receive responses
+static void mctp_i2c_device_select(struct mctp_i2c_client *mcli,
+                                  struct mctp_i2c_dev *midev)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&mcli->curr_lock, flags);
+       __mctp_i2c_device_select(mcli, midev);
+       spin_unlock_irqrestore(&mcli->curr_lock, flags);
+}
+
+static int mctp_i2c_slave_cb(struct i2c_client *client,
+                            enum i2c_slave_event event, u8 *val)
+{
+       struct mctp_i2c_client *mcli = i2c_get_clientdata(client);
+       struct mctp_i2c_dev *midev = NULL;
+       unsigned long flags;
+       int rc = 0;
+
+       spin_lock_irqsave(&mcli->curr_lock, flags);
+       midev = mcli->sel;
+       if (midev)
+               dev_hold(midev->ndev);
+       spin_unlock_irqrestore(&mcli->curr_lock, flags);
+
+       if (!midev)
+               return 0;
+
+       switch (event) {
+       case I2C_SLAVE_WRITE_RECEIVED:
+               if (midev->pos < MCTP_I2C_RXBUFSZ) {
+                       midev->buffer[midev->pos] = *val;
+                       midev->pos++;
+               } else {
+                       midev->ndev->stats.rx_over_errors++;
+               }
+
+               break;
+       case I2C_SLAVE_WRITE_REQUESTED:
+               /* dest_slave as first byte */
+               midev->buffer[0] = mcli->lladdr << 1;
+               midev->pos = 1;
+               break;
+       case I2C_SLAVE_STOP:
+               rc = mctp_i2c_recv(midev);
+               break;
+       default:
+               break;
+       }
+
+       dev_put(midev->ndev);
+       return rc;
+}
+
+// Processes incoming data that has been accumulated by the slave cb
+static int mctp_i2c_recv(struct mctp_i2c_dev *midev)
+{
+       struct net_device *ndev = midev->ndev;
+       struct mctp_i2c_hdr *hdr;
+       struct mctp_skb_cb *cb;
+       struct sk_buff *skb;
+       u8 pec, calc_pec;
+       size_t recvlen;
+
+       /* + 1 for the PEC */
+       if (midev->pos < MCTP_I2C_MINLEN + 1) {
+               ndev->stats.rx_length_errors++;
+               return -EINVAL;
+       }
+       recvlen = midev->pos - 1;
+
+       hdr = (void *)midev->buffer;
+       if (hdr->command != MCTP_I2C_COMMANDCODE) {
+               ndev->stats.rx_dropped++;
+               return -EINVAL;
+       }
+
+       pec = midev->buffer[midev->pos - 1];
+       calc_pec = i2c_smbus_pec(0, midev->buffer, recvlen);
+       if (pec != calc_pec) {
+               ndev->stats.rx_crc_errors++;
+               return -EINVAL;
+       }
+
+       skb = netdev_alloc_skb(ndev, recvlen);
+       if (!skb) {
+               ndev->stats.rx_dropped++;
+               return -ENOMEM;
+       }
+
+       skb->protocol = htons(ETH_P_MCTP);
+       skb_put_data(skb, midev->buffer, recvlen);
+       skb_reset_mac_header(skb);
+       skb_pull(skb, sizeof(struct mctp_i2c_hdr));
+       skb_reset_network_header(skb);
+
+       cb = __mctp_cb(skb);
+       cb->halen = 1;
+       cb->haddr[0] = hdr->source_slave;
+
+       if (netif_rx(skb) == NET_RX_SUCCESS) {
+               ndev->stats.rx_packets++;
+               ndev->stats.rx_bytes += skb->len;
+       } else {
+               ndev->stats.rx_dropped++;
+       }
+       return 0;
+}
+
+enum mctp_i2c_flow_state {
+       MCTP_I2C_TX_FLOW_INVALID,
+       MCTP_I2C_TX_FLOW_NONE,
+       MCTP_I2C_TX_FLOW_NEW,
+       MCTP_I2C_TX_FLOW_EXISTING,
+};
+
+static enum mctp_i2c_flow_state
+mctp_i2c_get_tx_flow_state(struct mctp_i2c_dev *midev, struct sk_buff *skb)
+{
+       enum mctp_i2c_flow_state state;
+       struct mctp_sk_key *key;
+       struct mctp_flow *flow;
+       unsigned long flags;
+
+       flow = skb_ext_find(skb, SKB_EXT_MCTP);
+       if (!flow)
+               return MCTP_I2C_TX_FLOW_NONE;
+
+       key = flow->key;
+       if (!key)
+               return MCTP_I2C_TX_FLOW_NONE;
+
+       spin_lock_irqsave(&key->lock, flags);
+       /* if the key is present but invalid, we're unlikely to be able
+        * to handle the flow at all; just drop now
+        */
+       if (!key->valid) {
+               state = MCTP_I2C_TX_FLOW_INVALID;
+
+       } else if (key->dev_flow_state == MCTP_I2C_FLOW_STATE_NEW) {
+               key->dev_flow_state = MCTP_I2C_FLOW_STATE_ACTIVE;
+               state = MCTP_I2C_TX_FLOW_NEW;
+       } else {
+               state = MCTP_I2C_TX_FLOW_EXISTING;
+       }
+
+       spin_unlock_irqrestore(&key->lock, flags);
+
+       return state;
+}
+
+/* We're not contending with ourselves here; we only need to exclude other
+ * i2c clients from using the bus. refcounts are simply to prevent
+ * recursive locking.
+ */
+static void mctp_i2c_lock_nest(struct mctp_i2c_dev *midev)
+{
+       unsigned long flags;
+       bool lock;
+
+       spin_lock_irqsave(&midev->flow_lock, flags);
+       lock = midev->i2c_lock_count == 0;
+       midev->i2c_lock_count++;
+       spin_unlock_irqrestore(&midev->flow_lock, flags);
+
+       if (lock)
+               i2c_lock_bus(midev->adapter, I2C_LOCK_SEGMENT);
+}
+
+static void mctp_i2c_unlock_nest(struct mctp_i2c_dev *midev)
+{
+       unsigned long flags;
+       bool unlock;
+
+       spin_lock_irqsave(&midev->flow_lock, flags);
+       if (!WARN_ONCE(midev->i2c_lock_count == 0, "lock count underflow!"))
+               midev->i2c_lock_count--;
+       unlock = midev->i2c_lock_count == 0;
+       spin_unlock_irqrestore(&midev->flow_lock, flags);
+
+       if (unlock)
+               i2c_unlock_bus(midev->adapter, I2C_LOCK_SEGMENT);
+}
+
+static void mctp_i2c_xmit(struct mctp_i2c_dev *midev, struct sk_buff *skb)
+{
+       struct net_device_stats *stats = &midev->ndev->stats;
+       enum mctp_i2c_flow_state fs;
+       union i2c_smbus_data *data;
+       struct mctp_i2c_hdr *hdr;
+       unsigned int len;
+       u16 daddr;
+       int rc;
+
+       fs = mctp_i2c_get_tx_flow_state(midev, skb);
+
+       len = skb->len;
+       hdr = (void *)skb_mac_header(skb);
+       data = (void *)&hdr->byte_count;
+       daddr = hdr->dest_slave >> 1;
+
+       switch (fs) {
+       case MCTP_I2C_TX_FLOW_NONE:
+               /* no flow: full lock & unlock */
+               mctp_i2c_lock_nest(midev);
+               mctp_i2c_device_select(midev->client, midev);
+               rc = __i2c_smbus_xfer(midev->adapter, daddr, I2C_CLIENT_PEC,
+                                     I2C_SMBUS_WRITE, hdr->command,
+                                     I2C_SMBUS_BLOCK_DATA, data);
+               mctp_i2c_unlock_nest(midev);
+               break;
+
+       case MCTP_I2C_TX_FLOW_NEW:
+               /* new flow: lock, tx, but don't unlock; that will happen
+                * on flow release
+                */
+               mctp_i2c_lock_nest(midev);
+               mctp_i2c_device_select(midev->client, midev);
+               fallthrough;
+
+       case MCTP_I2C_TX_FLOW_EXISTING:
+               /* existing flow: we already have the lock; just tx */
+               rc = __i2c_smbus_xfer(midev->adapter, daddr, I2C_CLIENT_PEC,
+                                     I2C_SMBUS_WRITE, hdr->command,
+                                     I2C_SMBUS_BLOCK_DATA, data);
+               break;
+
+       case MCTP_I2C_TX_FLOW_INVALID:
+               return;
+       }
+
+       if (rc) {
+               dev_warn_ratelimited(&midev->adapter->dev,
+                                    "%s i2c_smbus_xfer failed %d", __func__, rc);
+               stats->tx_errors++;
+       } else {
+               stats->tx_bytes += len;
+               stats->tx_packets++;
+       }
+}
+
+static void mctp_i2c_flow_release(struct mctp_i2c_dev *midev)
+{
+       unsigned long flags;
+       bool unlock;
+
+       spin_lock_irqsave(&midev->flow_lock, flags);
+       if (midev->release_count > midev->i2c_lock_count) {
+               WARN_ONCE(1, "release count overflow");
+               midev->release_count = midev->i2c_lock_count;
+       }
+
+       midev->i2c_lock_count -= midev->release_count;
+       unlock = midev->i2c_lock_count == 0 && midev->release_count > 0;
+       midev->release_count = 0;
+       spin_unlock_irqrestore(&midev->flow_lock, flags);
+
+       if (unlock)
+               i2c_unlock_bus(midev->adapter, I2C_LOCK_SEGMENT);
+}
+
+static int mctp_i2c_header_create(struct sk_buff *skb, struct net_device *dev,
+                                 unsigned short type, const void *daddr,
+          const void *saddr, unsigned int len)
+{
+       struct mctp_i2c_hdr *hdr;
+       struct mctp_hdr *mhdr;
+       u8 lldst, llsrc;
+
+       lldst = *((u8 *)daddr);
+       llsrc = *((u8 *)saddr);
+
+       skb_push(skb, sizeof(struct mctp_i2c_hdr));
+       skb_reset_mac_header(skb);
+       hdr = (void *)skb_mac_header(skb);
+       mhdr = mctp_hdr(skb);
+       hdr->dest_slave = (lldst << 1) & 0xff;
+       hdr->command = MCTP_I2C_COMMANDCODE;
+       hdr->byte_count = len + 1;
+       if (hdr->byte_count > MCTP_I2C_MAXBLOCK)
+               return -EMSGSIZE;
+       hdr->source_slave = ((llsrc << 1) & 0xff) | 0x01;
+       mhdr->ver = 0x01;
+
+       return 0;
+}
+
+static int mctp_i2c_tx_thread(void *data)
+{
+       struct mctp_i2c_dev *midev = data;
+       struct sk_buff *skb;
+       unsigned long flags;
+
+       for (;;) {
+               if (kthread_should_stop())
+                       break;
+
+               spin_lock_irqsave(&midev->tx_queue.lock, flags);
+               skb = __skb_dequeue(&midev->tx_queue);
+               if (netif_queue_stopped(midev->ndev))
+                       netif_wake_queue(midev->ndev);
+               spin_unlock_irqrestore(&midev->tx_queue.lock, flags);
+
+               if (skb == &midev->unlock_marker) {
+                       mctp_i2c_flow_release(midev);
+
+               } else if (skb) {
+                       mctp_i2c_xmit(midev, skb);
+                       kfree_skb(skb);
+
+               } else {
+                       wait_event(midev->tx_wq,
+                                  !skb_queue_empty(&midev->tx_queue) ||
+                                  kthread_should_stop());
+               }
+       }
+
+       return 0;
+}
+
+static netdev_tx_t mctp_i2c_start_xmit(struct sk_buff *skb,
+                                      struct net_device *dev)
+{
+       struct mctp_i2c_dev *midev = netdev_priv(dev);
+       unsigned long flags;
+
+       spin_lock_irqsave(&midev->tx_queue.lock, flags);
+       if (skb_queue_len(&midev->tx_queue) >= MCTP_I2C_TX_WORK_LEN) {
+               netif_stop_queue(dev);
+               spin_unlock_irqrestore(&midev->tx_queue.lock, flags);
+               netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
+               return NETDEV_TX_BUSY;
+       }
+
+       __skb_queue_tail(&midev->tx_queue, skb);
+       if (skb_queue_len(&midev->tx_queue) == MCTP_I2C_TX_WORK_LEN)
+               netif_stop_queue(dev);
+       spin_unlock_irqrestore(&midev->tx_queue.lock, flags);
+
+       wake_up(&midev->tx_wq);
+       return NETDEV_TX_OK;
+}
+
+static void mctp_i2c_release_flow(struct mctp_dev *mdev,
+                                 struct mctp_sk_key *key)
+
+{
+       struct mctp_i2c_dev *midev = netdev_priv(mdev->dev);
+       unsigned long flags;
+
+       spin_lock_irqsave(&midev->flow_lock, flags);
+       midev->release_count++;
+       spin_unlock_irqrestore(&midev->flow_lock, flags);
+
+       /* Ensure we have a release operation queued, through the fake
+        * marker skb
+        */
+       spin_lock(&midev->tx_queue.lock);
+       if (!midev->unlock_marker.next)
+               __skb_queue_tail(&midev->tx_queue, &midev->unlock_marker);
+       spin_unlock(&midev->tx_queue.lock);
+
+       wake_up(&midev->tx_wq);
+}
+
+static const struct net_device_ops mctp_i2c_ops = {
+       .ndo_start_xmit = mctp_i2c_start_xmit,
+};
+
+static const struct header_ops mctp_i2c_headops = {
+       .create = mctp_i2c_header_create,
+};
+
+static const struct mctp_netdev_ops mctp_i2c_mctp_ops = {
+       .release_flow = mctp_i2c_release_flow,
+};
+
+static void mctp_i2c_net_setup(struct net_device *dev)
+{
+       dev->type = ARPHRD_MCTP;
+
+       dev->mtu = MCTP_I2C_MAXMTU;
+       dev->min_mtu = MCTP_I2C_MINMTU;
+       dev->max_mtu = MCTP_I2C_MAXMTU;
+       dev->tx_queue_len = MCTP_I2C_TX_QUEUE_LEN;
+
+       dev->hard_header_len = sizeof(struct mctp_i2c_hdr);
+       dev->addr_len = 1;
+
+       dev->netdev_ops         = &mctp_i2c_ops;
+       dev->header_ops         = &mctp_i2c_headops;
+       dev->needs_free_netdev  = true;
+}
+
+static int mctp_i2c_add_netdev(struct mctp_i2c_client *mcli,
+                              struct i2c_adapter *adap)
+{
+       unsigned long flags;
+       struct mctp_i2c_dev *midev = NULL;
+       struct net_device *ndev = NULL;
+       struct i2c_adapter *root;
+       char namebuf[30];
+       int rc;
+
+       root = mux_root_adapter(adap);
+       if (root != mcli->client->adapter) {
+               dev_err(&mcli->client->dev,
+                       "I2C adapter %s is not a child bus of %s",
+                       mcli->client->adapter->name, root->name);
+               return -EINVAL;
+       }
+
+       WARN_ON(!mutex_is_locked(&mi_driver_state.lock));
+       snprintf(namebuf, sizeof(namebuf), "mctpi2c%d", adap->nr);
+       ndev = alloc_netdev(sizeof(*midev), namebuf, NET_NAME_ENUM, mctp_i2c_net_setup);
+       if (!ndev) {
+               dev_err(&mcli->client->dev, "%s alloc netdev failed\n", __func__);
+               rc = -ENOMEM;
+               goto err;
+       }
+       dev_net_set(ndev, current->nsproxy->net_ns);
+       SET_NETDEV_DEV(ndev, &adap->dev);
+       ndev->dev_addr = &mcli->lladdr;
+
+       midev = netdev_priv(ndev);
+       skb_queue_head_init(&midev->tx_queue);
+       INIT_LIST_HEAD(&midev->list);
+       midev->adapter = adap;
+       midev->client = mcli;
+       spin_lock_init(&midev->flow_lock);
+       midev->i2c_lock_count = 0;
+       midev->release_count = 0;
+       /* Hold references */
+       get_device(&midev->adapter->dev);
+       get_device(&midev->client->client->dev);
+       midev->ndev = ndev;
+       init_waitqueue_head(&midev->tx_wq);
+       midev->tx_thread = kthread_create(mctp_i2c_tx_thread, midev,
+                                         "%s/tx", namebuf);
+       if (IS_ERR_OR_NULL(midev->tx_thread)) {
+               rc = -ENOMEM;
+               goto err_free;
+       }
+
+       rc = mctp_register_netdev(ndev, &mctp_i2c_mctp_ops);
+       if (rc) {
+               dev_err(&mcli->client->dev,
+                       "%s register netdev \"%s\" failed %d\n", __func__,
+                       ndev->name, rc);
+               goto err_stop_kthread;
+       }
+       spin_lock_irqsave(&mcli->curr_lock, flags);
+       list_add(&midev->list, &mcli->devs);
+       // Select a device by default
+       if (!mcli->sel)
+               __mctp_i2c_device_select(mcli, midev);
+       spin_unlock_irqrestore(&mcli->curr_lock, flags);
+
+       wake_up_process(midev->tx_thread);
+
+       return 0;
+
+err_stop_kthread:
+       kthread_stop(midev->tx_thread);
+
+err_free:
+       free_netdev(ndev);
+
+err:
+       return rc;
+}
+
+// Removes and unregisters a mctp-i2c netdev
+static void mctp_i2c_free_netdev(struct mctp_i2c_dev *midev)
+{
+       struct mctp_i2c_client *mcli = midev->client;
+       unsigned long flags;
+
+       netif_stop_queue(midev->ndev);
+       kthread_stop(midev->tx_thread);
+       skb_queue_purge(&midev->tx_queue);
+
+       /* Release references, used only for TX which has stopped */
+       put_device(&midev->adapter->dev);
+       put_device(&mcli->client->dev);
+
+       /* Remove it from the parent mcli */
+       spin_lock_irqsave(&mcli->curr_lock, flags);
+       list_del(&midev->list);
+       if (mcli->sel == midev) {
+               struct mctp_i2c_dev *first;
+
+               first = list_first_entry_or_null(&mcli->devs, struct mctp_i2c_dev, list);
+               __mctp_i2c_device_select(mcli, first);
+       }
+       spin_unlock_irqrestore(&mcli->curr_lock, flags);
+
+       /* Remove netdev. mctp_i2c_slave_cb() takes a dev_hold() so removing
+        * it now is safe. unregister_netdev() frees ndev and midev.
+        */
+       mctp_unregister_netdev(midev->ndev);
+}
+
+// Removes any netdev for adap. mcli is the parent root i2c client
+static void mctp_i2c_remove_netdev(struct mctp_i2c_client *mcli,
+                                  struct i2c_adapter *adap)
+{
+       unsigned long flags;
+       struct mctp_i2c_dev *midev = NULL, *m = NULL;
+
+       WARN_ON(!mutex_is_locked(&mi_driver_state.lock));
+       spin_lock_irqsave(&mcli->curr_lock, flags);
+       // list size is limited by number of MCTP netdevs on a single hardware bus
+       list_for_each_entry(m, &mcli->devs, list)
+               if (m->adapter == adap) {
+                       midev = m;
+                       break;
+               }
+       spin_unlock_irqrestore(&mcli->curr_lock, flags);
+
+       if (midev)
+               mctp_i2c_free_netdev(midev);
+}
+
+/* Determines whether a device is an i2c adapter.
+ * Optionally returns the root i2c_adapter
+ */
+static struct i2c_adapter *mctp_i2c_get_adapter(struct device *dev,
+                                               struct i2c_adapter **ret_root)
+{
+       struct i2c_adapter *root, *adap;
+
+       if (dev->type != &i2c_adapter_type)
+               return NULL;
+       adap = to_i2c_adapter(dev);
+       root = mux_root_adapter(adap);
+       WARN_ONCE(!root, "%s failed to find root adapter for %s\n",
+                 __func__, dev_name(dev));
+       if (!root)
+               return NULL;
+       if (ret_root)
+               *ret_root = root;
+       return adap;
+}
+
+/* Determines whether a device is an i2c adapter with the "mctp-controller"
+ * devicetree property set. If adap is not an OF node, returns match_no_of
+ */
+static bool mctp_i2c_adapter_match(struct i2c_adapter *adap, bool match_no_of)
+{
+       if (!adap->dev.of_node)
+               return match_no_of;
+       return of_property_read_bool(adap->dev.of_node, MCTP_I2C_OF_PROP);
+}
+
+/* Called for each existing i2c device (adapter or client) when a
+ * new mctp-i2c client is probed.
+ */
+static int mctp_i2c_client_try_attach(struct device *dev, void *data)
+{
+       struct i2c_adapter *adap = NULL, *root = NULL;
+       struct mctp_i2c_client *mcli = data;
+
+       adap = mctp_i2c_get_adapter(dev, &root);
+       if (!adap)
+               return 0;
+       if (mcli->client->adapter != root)
+               return 0;
+       // Must either have mctp-controller property on the adapter, or
+       // be a root adapter if it's non-devicetree
+       if (!mctp_i2c_adapter_match(adap, adap == root))
+               return 0;
+
+       return mctp_i2c_add_netdev(mcli, adap);
+}
+
+static void mctp_i2c_notify_add(struct device *dev)
+{
+       struct mctp_i2c_client *mcli = NULL, *m = NULL;
+       struct i2c_adapter *root = NULL, *adap = NULL;
+       int rc;
+
+       adap = mctp_i2c_get_adapter(dev, &root);
+       if (!adap)
+               return;
+       // Check for mctp-controller property on the adapter
+       if (!mctp_i2c_adapter_match(adap, false))
+               return;
+
+       /* Find an existing mcli for adap's root */
+       mutex_lock(&mi_driver_state.lock);
+       list_for_each_entry(m, &mi_driver_state.clients, list) {
+               if (m->client->adapter == root) {
+                       mcli = m;
+                       break;
+               }
+       }
+
+       if (mcli) {
+               rc = mctp_i2c_add_netdev(mcli, adap);
+               if (rc)
+                       dev_warn(dev, "%s Failed adding mctp-i2c device",
+                                __func__);
+       }
+       mutex_unlock(&mi_driver_state.lock);
+}
+
+static void mctp_i2c_notify_del(struct device *dev)
+{
+       struct i2c_adapter *root = NULL, *adap = NULL;
+       struct mctp_i2c_client *mcli = NULL;
+
+       adap = mctp_i2c_get_adapter(dev, &root);
+       if (!adap)
+               return;
+
+       mutex_lock(&mi_driver_state.lock);
+       list_for_each_entry(mcli, &mi_driver_state.clients, list) {
+               if (mcli->client->adapter == root) {
+                       mctp_i2c_remove_netdev(mcli, adap);
+                       break;
+               }
+       }
+       mutex_unlock(&mi_driver_state.lock);
+}
+
+static int mctp_i2c_probe(struct i2c_client *client)
+{
+       struct mctp_i2c_client *mcli = NULL;
+       int rc;
+
+       /* Check for >32 byte block support required for MCTP */
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_V3_BLOCK)) {
+               dev_err(&client->dev,
+                       "%s failed, I2C bus driver does not support 255 byte block transfer\n",
+                       __func__);
+               return -EOPNOTSUPP;
+       }
+
+       mutex_lock(&mi_driver_state.lock);
+       mcli = mctp_i2c_new_client(client);
+       if (IS_ERR(mcli)) {
+               rc = PTR_ERR(mcli);
+               mcli = NULL;
+               goto out;
+       } else {
+               list_add(&mcli->list, &mi_driver_state.clients);
+       }
+
+       // Add a netdev for adapters that have a 'mctp-controller' property
+       i2c_for_each_dev(mcli, mctp_i2c_client_try_attach);
+       rc = 0;
+out:
+       mutex_unlock(&mi_driver_state.lock);
+       return rc;
+}
+
+static int mctp_i2c_remove(struct i2c_client *client)
+{
+       struct mctp_i2c_client *mcli = i2c_get_clientdata(client);
+       struct mctp_i2c_dev *midev = NULL, *tmp = NULL;
+
+       mutex_lock(&mi_driver_state.lock);
+       list_del(&mcli->list);
+       // Remove all child adapter netdevs
+       list_for_each_entry_safe(midev, tmp, &mcli->devs, list)
+               mctp_i2c_free_netdev(midev);
+
+       mctp_i2c_free_client(mcli);
+       mutex_unlock(&mi_driver_state.lock);
+       // Callers ignore return code
+       return 0;
+}
+
+/* We look for a 'mctp-controller' property on I2C busses as they are
+ * added/deleted, creating/removing netdevs as required.
+ */
+static int mctp_i2c_notifier_call(struct notifier_block *nb,
+                                 unsigned long action, void *data)
+{
+       struct device *dev = data;
+
+       switch (action) {
+       case BUS_NOTIFY_ADD_DEVICE:
+               mctp_i2c_notify_add(dev);
+               break;
+       case BUS_NOTIFY_DEL_DEVICE:
+               mctp_i2c_notify_del(dev);
+               break;
+       }
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block mctp_i2c_notifier = {
+       .notifier_call = mctp_i2c_notifier_call,
+};
+
+static const struct i2c_device_id mctp_i2c_id[] = {
+       { "mctp-i2c", 0 },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, mctp_i2c_id);
+
+static const struct of_device_id mctp_i2c_of_match[] = {
+       { .compatible = "mctp-i2c-controller" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, mctp_i2c_of_match);
+
+static struct i2c_driver mctp_i2c_driver = {
+       .driver = {
+               .name = "mctp-i2c",
+               .of_match_table = mctp_i2c_of_match,
+       },
+       .probe_new = mctp_i2c_probe,
+       .remove = mctp_i2c_remove,
+       .id_table = mctp_i2c_id,
+};
+
+static __init int mctp_i2c_init(void)
+{
+       int rc;
+
+       INIT_LIST_HEAD(&mi_driver_state.clients);
+       mutex_init(&mi_driver_state.lock);
+       pr_info("MCTP SMBus/I2C transport driver\n");
+       rc = i2c_add_driver(&mctp_i2c_driver);
+       if (rc)
+               return rc;
+       rc = bus_register_notifier(&i2c_bus_type, &mctp_i2c_notifier);
+       if (rc) {
+               i2c_del_driver(&mctp_i2c_driver);
+               return rc;
+       }
+       return 0;
+}
+
+static __exit void mctp_i2c_exit(void)
+{
+       int rc;
+
+       rc = bus_unregister_notifier(&i2c_bus_type, &mctp_i2c_notifier);
+       if (rc)
+               pr_warn("%s Could not unregister notifier, %d", __func__, rc);
+       i2c_del_driver(&mctp_i2c_driver);
+}
+
+module_init(mctp_i2c_init);
+module_exit(mctp_i2c_exit);
+
+MODULE_DESCRIPTION("MCTP SMBus/I2C device");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Matt Johnston <matt@codeconstruct.com.au>");