xref: /openbmc/linux/drivers/net/mctp/mctp-i3c.c (revision 7eec1181)
17eec1181SMatt Johnston // SPDX-License-Identifier: GPL-2.0
27eec1181SMatt Johnston /*
37eec1181SMatt Johnston  * Implements DMTF specification
47eec1181SMatt Johnston  * "DSP0233 Management Component Transport Protocol (MCTP) I3C Transport
57eec1181SMatt Johnston  * Binding"
67eec1181SMatt Johnston  * https://www.dmtf.org/sites/default/files/standards/documents/DSP0233_1.0.0.pdf
77eec1181SMatt Johnston  *
87eec1181SMatt Johnston  * Copyright (c) 2023 Code Construct
97eec1181SMatt Johnston  */
107eec1181SMatt Johnston 
117eec1181SMatt Johnston #include <linux/module.h>
127eec1181SMatt Johnston #include <linux/netdevice.h>
137eec1181SMatt Johnston #include <linux/i3c/device.h>
147eec1181SMatt Johnston #include <linux/i3c/master.h>
157eec1181SMatt Johnston #include <linux/if_arp.h>
167eec1181SMatt Johnston #include <asm/unaligned.h>
177eec1181SMatt Johnston #include <net/mctp.h>
187eec1181SMatt Johnston #include <net/mctpdevice.h>
197eec1181SMatt Johnston 
207eec1181SMatt Johnston #define MCTP_I3C_MAXBUF 65536
217eec1181SMatt Johnston /* 48 bit Provisioned Id */
227eec1181SMatt Johnston #define PID_SIZE 6
237eec1181SMatt Johnston 
247eec1181SMatt Johnston /* 64 byte payload, 4 byte MCTP header */
257eec1181SMatt Johnston static const int MCTP_I3C_MINMTU = 64 + 4;
267eec1181SMatt Johnston /* One byte less to allow for the PEC */
277eec1181SMatt Johnston static const int MCTP_I3C_MAXMTU = MCTP_I3C_MAXBUF - 1;
287eec1181SMatt Johnston /* 4 byte MCTP header, no data, 1 byte PEC */
297eec1181SMatt Johnston static const int MCTP_I3C_MINLEN = 4 + 1;
307eec1181SMatt Johnston 
317eec1181SMatt Johnston /* Sufficient for 64kB at min mtu */
327eec1181SMatt Johnston static const int MCTP_I3C_TX_QUEUE_LEN = 1100;
337eec1181SMatt Johnston 
347eec1181SMatt Johnston /* Somewhat arbitrary */
357eec1181SMatt Johnston static const int MCTP_I3C_IBI_SLOTS = 8;
367eec1181SMatt Johnston 
377eec1181SMatt Johnston /* Mandatory Data Byte in an IBI, from DSP0233 */
387eec1181SMatt Johnston #define I3C_MDB_MCTP 0xAE
397eec1181SMatt Johnston /* From MIPI Device Characteristics Register (DCR) Assignments */
407eec1181SMatt Johnston #define I3C_DCR_MCTP 0xCC
417eec1181SMatt Johnston 
427eec1181SMatt Johnston static const char *MCTP_I3C_OF_PROP = "mctp-controller";
437eec1181SMatt Johnston 
447eec1181SMatt Johnston /* List of mctp_i3c_busdev */
457eec1181SMatt Johnston static LIST_HEAD(busdevs);
467eec1181SMatt Johnston /* Protects busdevs, as well as mctp_i3c_bus.devs lists */
477eec1181SMatt Johnston static DEFINE_MUTEX(busdevs_lock);
487eec1181SMatt Johnston 
497eec1181SMatt Johnston struct mctp_i3c_bus {
507eec1181SMatt Johnston 	struct net_device *ndev;
517eec1181SMatt Johnston 
527eec1181SMatt Johnston 	struct task_struct *tx_thread;
537eec1181SMatt Johnston 	wait_queue_head_t tx_wq;
547eec1181SMatt Johnston 	/* tx_lock protects tx_skb and devs */
557eec1181SMatt Johnston 	spinlock_t tx_lock;
567eec1181SMatt Johnston 	/* Next skb to transmit */
577eec1181SMatt Johnston 	struct sk_buff *tx_skb;
587eec1181SMatt Johnston 	/* Scratch buffer for xmit */
597eec1181SMatt Johnston 	u8 tx_scratch[MCTP_I3C_MAXBUF];
607eec1181SMatt Johnston 
617eec1181SMatt Johnston 	/* Element of busdevs */
627eec1181SMatt Johnston 	struct list_head list;
637eec1181SMatt Johnston 
647eec1181SMatt Johnston 	/* Provisioned ID of our controller */
657eec1181SMatt Johnston 	u64 pid;
667eec1181SMatt Johnston 
677eec1181SMatt Johnston 	struct i3c_bus *bus;
687eec1181SMatt Johnston 	/* Head of mctp_i3c_device.list. Protected by busdevs_lock */
697eec1181SMatt Johnston 	struct list_head devs;
707eec1181SMatt Johnston };
717eec1181SMatt Johnston 
727eec1181SMatt Johnston struct mctp_i3c_device {
737eec1181SMatt Johnston 	struct i3c_device *i3c;
747eec1181SMatt Johnston 	struct mctp_i3c_bus *mbus;
757eec1181SMatt Johnston 	struct list_head list; /* Element of mctp_i3c_bus.devs */
767eec1181SMatt Johnston 
777eec1181SMatt Johnston 	/* Held while tx_thread is using this device */
787eec1181SMatt Johnston 	struct mutex lock;
797eec1181SMatt Johnston 
807eec1181SMatt Johnston 	/* Whether BCR indicates MDB is present in IBI */
817eec1181SMatt Johnston 	bool have_mdb;
827eec1181SMatt Johnston 	/* I3C dynamic address */
837eec1181SMatt Johnston 	u8 addr;
847eec1181SMatt Johnston 	/* Maximum read length */
857eec1181SMatt Johnston 	u16 mrl;
867eec1181SMatt Johnston 	/* Maximum write length */
877eec1181SMatt Johnston 	u16 mwl;
887eec1181SMatt Johnston 	/* Provisioned ID */
897eec1181SMatt Johnston 	u64 pid;
907eec1181SMatt Johnston };
917eec1181SMatt Johnston 
927eec1181SMatt Johnston /* We synthesise a mac header using the Provisioned ID.
937eec1181SMatt Johnston  * Used to pass dest to mctp_i3c_start_xmit.
947eec1181SMatt Johnston  */
957eec1181SMatt Johnston struct mctp_i3c_internal_hdr {
967eec1181SMatt Johnston 	u8 dest[PID_SIZE];
977eec1181SMatt Johnston 	u8 source[PID_SIZE];
987eec1181SMatt Johnston } __packed;
997eec1181SMatt Johnston 
mctp_i3c_read(struct mctp_i3c_device * mi)1007eec1181SMatt Johnston static int mctp_i3c_read(struct mctp_i3c_device *mi)
1017eec1181SMatt Johnston {
1027eec1181SMatt Johnston 	struct i3c_priv_xfer xfer = { .rnw = 1, .len = mi->mrl };
1037eec1181SMatt Johnston 	struct net_device_stats *stats = &mi->mbus->ndev->stats;
1047eec1181SMatt Johnston 	struct mctp_i3c_internal_hdr *ihdr = NULL;
1057eec1181SMatt Johnston 	struct sk_buff *skb = NULL;
1067eec1181SMatt Johnston 	struct mctp_skb_cb *cb;
1077eec1181SMatt Johnston 	int net_status, rc;
1087eec1181SMatt Johnston 	u8 pec, addr;
1097eec1181SMatt Johnston 
1107eec1181SMatt Johnston 	skb = netdev_alloc_skb(mi->mbus->ndev,
1117eec1181SMatt Johnston 			       mi->mrl + sizeof(struct mctp_i3c_internal_hdr));
1127eec1181SMatt Johnston 	if (!skb) {
1137eec1181SMatt Johnston 		stats->rx_dropped++;
1147eec1181SMatt Johnston 		rc = -ENOMEM;
1157eec1181SMatt Johnston 		goto err;
1167eec1181SMatt Johnston 	}
1177eec1181SMatt Johnston 
1187eec1181SMatt Johnston 	skb->protocol = htons(ETH_P_MCTP);
1197eec1181SMatt Johnston 	/* Create a header for internal use */
1207eec1181SMatt Johnston 	skb_reset_mac_header(skb);
1217eec1181SMatt Johnston 	ihdr = skb_put(skb, sizeof(struct mctp_i3c_internal_hdr));
1227eec1181SMatt Johnston 	put_unaligned_be48(mi->pid, ihdr->source);
1237eec1181SMatt Johnston 	put_unaligned_be48(mi->mbus->pid, ihdr->dest);
1247eec1181SMatt Johnston 	skb_pull(skb, sizeof(struct mctp_i3c_internal_hdr));
1257eec1181SMatt Johnston 
1267eec1181SMatt Johnston 	xfer.data.in = skb_put(skb, mi->mrl);
1277eec1181SMatt Johnston 
1287eec1181SMatt Johnston 	rc = i3c_device_do_priv_xfers(mi->i3c, &xfer, 1);
1297eec1181SMatt Johnston 	if (rc < 0)
1307eec1181SMatt Johnston 		goto err;
1317eec1181SMatt Johnston 
1327eec1181SMatt Johnston 	if (WARN_ON_ONCE(xfer.len > mi->mrl)) {
1337eec1181SMatt Johnston 		/* Bad i3c bus driver */
1347eec1181SMatt Johnston 		rc = -EIO;
1357eec1181SMatt Johnston 		goto err;
1367eec1181SMatt Johnston 	}
1377eec1181SMatt Johnston 	if (xfer.len < MCTP_I3C_MINLEN) {
1387eec1181SMatt Johnston 		stats->rx_length_errors++;
1397eec1181SMatt Johnston 		rc = -EIO;
1407eec1181SMatt Johnston 		goto err;
1417eec1181SMatt Johnston 	}
1427eec1181SMatt Johnston 
1437eec1181SMatt Johnston 	/* check PEC, including address byte */
1447eec1181SMatt Johnston 	addr = mi->addr << 1 | 1;
1457eec1181SMatt Johnston 	pec = i2c_smbus_pec(0, &addr, 1);
1467eec1181SMatt Johnston 	pec = i2c_smbus_pec(pec, xfer.data.in, xfer.len - 1);
1477eec1181SMatt Johnston 	if (pec != ((u8 *)xfer.data.in)[xfer.len - 1]) {
1487eec1181SMatt Johnston 		stats->rx_crc_errors++;
1497eec1181SMatt Johnston 		rc = -EINVAL;
1507eec1181SMatt Johnston 		goto err;
1517eec1181SMatt Johnston 	}
1527eec1181SMatt Johnston 
1537eec1181SMatt Johnston 	/* Remove PEC */
1547eec1181SMatt Johnston 	skb_trim(skb, xfer.len - 1);
1557eec1181SMatt Johnston 
1567eec1181SMatt Johnston 	cb = __mctp_cb(skb);
1577eec1181SMatt Johnston 	cb->halen = PID_SIZE;
1587eec1181SMatt Johnston 	put_unaligned_be48(mi->pid, cb->haddr);
1597eec1181SMatt Johnston 
1607eec1181SMatt Johnston 	net_status = netif_rx(skb);
1617eec1181SMatt Johnston 
1627eec1181SMatt Johnston 	if (net_status == NET_RX_SUCCESS) {
1637eec1181SMatt Johnston 		stats->rx_packets++;
1647eec1181SMatt Johnston 		stats->rx_bytes += xfer.len - 1;
1657eec1181SMatt Johnston 	} else {
1667eec1181SMatt Johnston 		stats->rx_dropped++;
1677eec1181SMatt Johnston 	}
1687eec1181SMatt Johnston 
1697eec1181SMatt Johnston 	return 0;
1707eec1181SMatt Johnston err:
1717eec1181SMatt Johnston 	kfree_skb(skb);
1727eec1181SMatt Johnston 	return rc;
1737eec1181SMatt Johnston }
1747eec1181SMatt Johnston 
mctp_i3c_ibi_handler(struct i3c_device * i3c,const struct i3c_ibi_payload * payload)1757eec1181SMatt Johnston static void mctp_i3c_ibi_handler(struct i3c_device *i3c,
1767eec1181SMatt Johnston 				 const struct i3c_ibi_payload *payload)
1777eec1181SMatt Johnston {
1787eec1181SMatt Johnston 	struct mctp_i3c_device *mi = i3cdev_get_drvdata(i3c);
1797eec1181SMatt Johnston 
1807eec1181SMatt Johnston 	if (WARN_ON_ONCE(!mi))
1817eec1181SMatt Johnston 		return;
1827eec1181SMatt Johnston 
1837eec1181SMatt Johnston 	if (mi->have_mdb) {
1847eec1181SMatt Johnston 		if (payload->len > 0) {
1857eec1181SMatt Johnston 			if (((u8 *)payload->data)[0] != I3C_MDB_MCTP) {
1867eec1181SMatt Johnston 				/* Not a mctp-i3c interrupt, ignore it */
1877eec1181SMatt Johnston 				return;
1887eec1181SMatt Johnston 			}
1897eec1181SMatt Johnston 		} else {
1907eec1181SMatt Johnston 			/* The BCR advertised a Mandatory Data Byte but the
1917eec1181SMatt Johnston 			 * device didn't send one.
1927eec1181SMatt Johnston 			 */
1937eec1181SMatt Johnston 			dev_warn_once(i3cdev_to_dev(i3c), "IBI with missing MDB");
1947eec1181SMatt Johnston 		}
1957eec1181SMatt Johnston 	}
1967eec1181SMatt Johnston 
1977eec1181SMatt Johnston 	mctp_i3c_read(mi);
1987eec1181SMatt Johnston }
1997eec1181SMatt Johnston 
mctp_i3c_setup(struct mctp_i3c_device * mi)2007eec1181SMatt Johnston static int mctp_i3c_setup(struct mctp_i3c_device *mi)
2017eec1181SMatt Johnston {
2027eec1181SMatt Johnston 	const struct i3c_ibi_setup ibi = {
2037eec1181SMatt Johnston 		.max_payload_len = 1,
2047eec1181SMatt Johnston 		.num_slots = MCTP_I3C_IBI_SLOTS,
2057eec1181SMatt Johnston 		.handler = mctp_i3c_ibi_handler,
2067eec1181SMatt Johnston 	};
2077eec1181SMatt Johnston 	struct i3c_device_info info;
2087eec1181SMatt Johnston 	int rc;
2097eec1181SMatt Johnston 
2107eec1181SMatt Johnston 	i3c_device_get_info(mi->i3c, &info);
2117eec1181SMatt Johnston 	mi->have_mdb = info.bcr & BIT(2);
2127eec1181SMatt Johnston 	mi->addr = info.dyn_addr;
2137eec1181SMatt Johnston 	mi->mwl = info.max_write_len;
2147eec1181SMatt Johnston 	mi->mrl = info.max_read_len;
2157eec1181SMatt Johnston 	mi->pid = info.pid;
2167eec1181SMatt Johnston 
2177eec1181SMatt Johnston 	rc = i3c_device_request_ibi(mi->i3c, &ibi);
2187eec1181SMatt Johnston 	if (rc == -ENOTSUPP) {
2197eec1181SMatt Johnston 		/* This driver only supports In-Band Interrupt mode.
2207eec1181SMatt Johnston 		 * Support for Polling Mode could be added if required.
2217eec1181SMatt Johnston 		 * (ENOTSUPP is from the i3c layer, not EOPNOTSUPP).
2227eec1181SMatt Johnston 		 */
2237eec1181SMatt Johnston 		dev_warn(i3cdev_to_dev(mi->i3c),
2247eec1181SMatt Johnston 			 "Failed, bus driver doesn't support In-Band Interrupts");
2257eec1181SMatt Johnston 		goto err;
2267eec1181SMatt Johnston 	} else if (rc < 0) {
2277eec1181SMatt Johnston 		dev_err(i3cdev_to_dev(mi->i3c),
2287eec1181SMatt Johnston 			"Failed requesting IBI (%d)\n", rc);
2297eec1181SMatt Johnston 		goto err;
2307eec1181SMatt Johnston 	}
2317eec1181SMatt Johnston 
2327eec1181SMatt Johnston 	rc = i3c_device_enable_ibi(mi->i3c);
2337eec1181SMatt Johnston 	if (rc < 0) {
2347eec1181SMatt Johnston 		/* Assume a driver supporting request_ibi also
2357eec1181SMatt Johnston 		 * supports enable_ibi.
2367eec1181SMatt Johnston 		 */
2377eec1181SMatt Johnston 		dev_err(i3cdev_to_dev(mi->i3c), "Failed enabling IBI (%d)\n", rc);
2387eec1181SMatt Johnston 		goto err_free_ibi;
2397eec1181SMatt Johnston 	}
2407eec1181SMatt Johnston 
2417eec1181SMatt Johnston 	return 0;
2427eec1181SMatt Johnston 
2437eec1181SMatt Johnston err_free_ibi:
2447eec1181SMatt Johnston 	i3c_device_free_ibi(mi->i3c);
2457eec1181SMatt Johnston 
2467eec1181SMatt Johnston err:
2477eec1181SMatt Johnston 	return rc;
2487eec1181SMatt Johnston }
2497eec1181SMatt Johnston 
2507eec1181SMatt Johnston /* Adds a new MCTP i3c_device to a bus */
mctp_i3c_add_device(struct mctp_i3c_bus * mbus,struct i3c_device * i3c)2517eec1181SMatt Johnston static int mctp_i3c_add_device(struct mctp_i3c_bus *mbus,
2527eec1181SMatt Johnston 			       struct i3c_device *i3c)
2537eec1181SMatt Johnston __must_hold(&busdevs_lock)
2547eec1181SMatt Johnston {
2557eec1181SMatt Johnston 	struct mctp_i3c_device *mi = NULL;
2567eec1181SMatt Johnston 	int rc;
2577eec1181SMatt Johnston 
2587eec1181SMatt Johnston 	mi = kzalloc(sizeof(*mi), GFP_KERNEL);
2597eec1181SMatt Johnston 	if (!mi) {
2607eec1181SMatt Johnston 		rc = -ENOMEM;
2617eec1181SMatt Johnston 		goto err;
2627eec1181SMatt Johnston 	}
2637eec1181SMatt Johnston 	mi->mbus = mbus;
2647eec1181SMatt Johnston 	mi->i3c = i3c;
2657eec1181SMatt Johnston 	mutex_init(&mi->lock);
2667eec1181SMatt Johnston 	list_add(&mi->list, &mbus->devs);
2677eec1181SMatt Johnston 
2687eec1181SMatt Johnston 	i3cdev_set_drvdata(i3c, mi);
2697eec1181SMatt Johnston 	rc = mctp_i3c_setup(mi);
2707eec1181SMatt Johnston 	if (rc < 0)
2717eec1181SMatt Johnston 		goto err_free;
2727eec1181SMatt Johnston 
2737eec1181SMatt Johnston 	return 0;
2747eec1181SMatt Johnston 
2757eec1181SMatt Johnston err_free:
2767eec1181SMatt Johnston 	list_del(&mi->list);
2777eec1181SMatt Johnston 	kfree(mi);
2787eec1181SMatt Johnston 
2797eec1181SMatt Johnston err:
2807eec1181SMatt Johnston 	dev_warn(i3cdev_to_dev(i3c), "Error adding mctp-i3c device, %d\n", rc);
2817eec1181SMatt Johnston 	return rc;
2827eec1181SMatt Johnston }
2837eec1181SMatt Johnston 
mctp_i3c_probe(struct i3c_device * i3c)2847eec1181SMatt Johnston static int mctp_i3c_probe(struct i3c_device *i3c)
2857eec1181SMatt Johnston {
2867eec1181SMatt Johnston 	struct mctp_i3c_bus *b = NULL, *mbus = NULL;
2877eec1181SMatt Johnston 
2887eec1181SMatt Johnston 	/* Look for a known bus */
2897eec1181SMatt Johnston 	mutex_lock(&busdevs_lock);
2907eec1181SMatt Johnston 	list_for_each_entry(b, &busdevs, list)
2917eec1181SMatt Johnston 		if (b->bus == i3c->bus) {
2927eec1181SMatt Johnston 			mbus = b;
2937eec1181SMatt Johnston 			break;
2947eec1181SMatt Johnston 		}
2957eec1181SMatt Johnston 	mutex_unlock(&busdevs_lock);
2967eec1181SMatt Johnston 
2977eec1181SMatt Johnston 	if (!mbus) {
2987eec1181SMatt Johnston 		/* probably no "mctp-controller" property on the i3c bus */
2997eec1181SMatt Johnston 		return -ENODEV;
3007eec1181SMatt Johnston 	}
3017eec1181SMatt Johnston 
3027eec1181SMatt Johnston 	return mctp_i3c_add_device(mbus, i3c);
3037eec1181SMatt Johnston }
3047eec1181SMatt Johnston 
mctp_i3c_remove_device(struct mctp_i3c_device * mi)3057eec1181SMatt Johnston static void mctp_i3c_remove_device(struct mctp_i3c_device *mi)
3067eec1181SMatt Johnston __must_hold(&busdevs_lock)
3077eec1181SMatt Johnston {
3087eec1181SMatt Johnston 	/* Ensure the tx thread isn't using the device */
3097eec1181SMatt Johnston 	mutex_lock(&mi->lock);
3107eec1181SMatt Johnston 
3117eec1181SMatt Johnston 	/* Counterpart of mctp_i3c_setup */
3127eec1181SMatt Johnston 	i3c_device_disable_ibi(mi->i3c);
3137eec1181SMatt Johnston 	i3c_device_free_ibi(mi->i3c);
3147eec1181SMatt Johnston 
3157eec1181SMatt Johnston 	/* Counterpart of mctp_i3c_add_device */
3167eec1181SMatt Johnston 	i3cdev_set_drvdata(mi->i3c, NULL);
3177eec1181SMatt Johnston 	list_del(&mi->list);
3187eec1181SMatt Johnston 
3197eec1181SMatt Johnston 	/* Safe to unlock after removing from the list */
3207eec1181SMatt Johnston 	mutex_unlock(&mi->lock);
3217eec1181SMatt Johnston 	kfree(mi);
3227eec1181SMatt Johnston }
3237eec1181SMatt Johnston 
mctp_i3c_remove(struct i3c_device * i3c)3247eec1181SMatt Johnston static void mctp_i3c_remove(struct i3c_device *i3c)
3257eec1181SMatt Johnston {
3267eec1181SMatt Johnston 	struct mctp_i3c_device *mi = i3cdev_get_drvdata(i3c);
3277eec1181SMatt Johnston 
3287eec1181SMatt Johnston 	/* We my have received a Bus Remove notify prior to device remove,
3297eec1181SMatt Johnston 	 * so mi will already be removed.
3307eec1181SMatt Johnston 	 */
3317eec1181SMatt Johnston 	if (!mi)
3327eec1181SMatt Johnston 		return;
3337eec1181SMatt Johnston 
3347eec1181SMatt Johnston 	mutex_lock(&busdevs_lock);
3357eec1181SMatt Johnston 	mctp_i3c_remove_device(mi);
3367eec1181SMatt Johnston 	mutex_unlock(&busdevs_lock);
3377eec1181SMatt Johnston }
3387eec1181SMatt Johnston 
3397eec1181SMatt Johnston /* Returns the device for an address, with mi->lock held */
3407eec1181SMatt Johnston static struct mctp_i3c_device *
mctp_i3c_lookup(struct mctp_i3c_bus * mbus,u64 pid)3417eec1181SMatt Johnston mctp_i3c_lookup(struct mctp_i3c_bus *mbus, u64 pid)
3427eec1181SMatt Johnston {
3437eec1181SMatt Johnston 	struct mctp_i3c_device *mi = NULL, *ret = NULL;
3447eec1181SMatt Johnston 
3457eec1181SMatt Johnston 	mutex_lock(&busdevs_lock);
3467eec1181SMatt Johnston 	list_for_each_entry(mi, &mbus->devs, list)
3477eec1181SMatt Johnston 		if (mi->pid == pid) {
3487eec1181SMatt Johnston 			ret = mi;
3497eec1181SMatt Johnston 			mutex_lock(&mi->lock);
3507eec1181SMatt Johnston 			break;
3517eec1181SMatt Johnston 		}
3527eec1181SMatt Johnston 	mutex_unlock(&busdevs_lock);
3537eec1181SMatt Johnston 	return ret;
3547eec1181SMatt Johnston }
3557eec1181SMatt Johnston 
mctp_i3c_xmit(struct mctp_i3c_bus * mbus,struct sk_buff * skb)3567eec1181SMatt Johnston static void mctp_i3c_xmit(struct mctp_i3c_bus *mbus, struct sk_buff *skb)
3577eec1181SMatt Johnston {
3587eec1181SMatt Johnston 	struct net_device_stats *stats = &mbus->ndev->stats;
3597eec1181SMatt Johnston 	struct i3c_priv_xfer xfer = { .rnw = false };
3607eec1181SMatt Johnston 	struct mctp_i3c_internal_hdr *ihdr = NULL;
3617eec1181SMatt Johnston 	struct mctp_i3c_device *mi = NULL;
3627eec1181SMatt Johnston 	unsigned int data_len;
3637eec1181SMatt Johnston 	u8 *data = NULL;
3647eec1181SMatt Johnston 	u8 addr, pec;
3657eec1181SMatt Johnston 	int rc = 0;
3667eec1181SMatt Johnston 	u64 pid;
3677eec1181SMatt Johnston 
3687eec1181SMatt Johnston 	skb_pull(skb, sizeof(struct mctp_i3c_internal_hdr));
3697eec1181SMatt Johnston 	data_len = skb->len;
3707eec1181SMatt Johnston 
3717eec1181SMatt Johnston 	ihdr = (void *)skb_mac_header(skb);
3727eec1181SMatt Johnston 
3737eec1181SMatt Johnston 	pid = get_unaligned_be48(ihdr->dest);
3747eec1181SMatt Johnston 	mi = mctp_i3c_lookup(mbus, pid);
3757eec1181SMatt Johnston 	if (!mi) {
3767eec1181SMatt Johnston 		/* I3C endpoint went away after the packet was enqueued? */
3777eec1181SMatt Johnston 		stats->tx_dropped++;
3787eec1181SMatt Johnston 		goto out;
3797eec1181SMatt Johnston 	}
3807eec1181SMatt Johnston 
3817eec1181SMatt Johnston 	if (WARN_ON_ONCE(data_len + 1 > MCTP_I3C_MAXBUF))
3827eec1181SMatt Johnston 		goto out;
3837eec1181SMatt Johnston 
3847eec1181SMatt Johnston 	if (data_len + 1 > (unsigned int)mi->mwl) {
3857eec1181SMatt Johnston 		/* Route MTU was larger than supported by the endpoint */
3867eec1181SMatt Johnston 		stats->tx_dropped++;
3877eec1181SMatt Johnston 		goto out;
3887eec1181SMatt Johnston 	}
3897eec1181SMatt Johnston 
3907eec1181SMatt Johnston 	/* Need a linear buffer with space for the PEC */
3917eec1181SMatt Johnston 	xfer.len = data_len + 1;
3927eec1181SMatt Johnston 	if (skb_tailroom(skb) >= 1) {
3937eec1181SMatt Johnston 		skb_put(skb, 1);
3947eec1181SMatt Johnston 		data = skb->data;
3957eec1181SMatt Johnston 	} else {
3967eec1181SMatt Johnston 		/* Otherwise need to copy the buffer */
3977eec1181SMatt Johnston 		skb_copy_bits(skb, 0, mbus->tx_scratch, skb->len);
3987eec1181SMatt Johnston 		data = mbus->tx_scratch;
3997eec1181SMatt Johnston 	}
4007eec1181SMatt Johnston 
4017eec1181SMatt Johnston 	/* PEC calculation */
4027eec1181SMatt Johnston 	addr = mi->addr << 1;
4037eec1181SMatt Johnston 	pec = i2c_smbus_pec(0, &addr, 1);
4047eec1181SMatt Johnston 	pec = i2c_smbus_pec(pec, data, data_len);
4057eec1181SMatt Johnston 	data[data_len] = pec;
4067eec1181SMatt Johnston 
4077eec1181SMatt Johnston 	xfer.data.out = data;
4087eec1181SMatt Johnston 	rc = i3c_device_do_priv_xfers(mi->i3c, &xfer, 1);
4097eec1181SMatt Johnston 	if (rc == 0) {
4107eec1181SMatt Johnston 		stats->tx_bytes += data_len;
4117eec1181SMatt Johnston 		stats->tx_packets++;
4127eec1181SMatt Johnston 	} else {
4137eec1181SMatt Johnston 		stats->tx_errors++;
4147eec1181SMatt Johnston 	}
4157eec1181SMatt Johnston 
4167eec1181SMatt Johnston out:
4177eec1181SMatt Johnston 	if (mi)
4187eec1181SMatt Johnston 		mutex_unlock(&mi->lock);
4197eec1181SMatt Johnston }
4207eec1181SMatt Johnston 
mctp_i3c_tx_thread(void * data)4217eec1181SMatt Johnston static int mctp_i3c_tx_thread(void *data)
4227eec1181SMatt Johnston {
4237eec1181SMatt Johnston 	struct mctp_i3c_bus *mbus = data;
4247eec1181SMatt Johnston 	struct sk_buff *skb;
4257eec1181SMatt Johnston 
4267eec1181SMatt Johnston 	for (;;) {
4277eec1181SMatt Johnston 		if (kthread_should_stop())
4287eec1181SMatt Johnston 			break;
4297eec1181SMatt Johnston 
4307eec1181SMatt Johnston 		spin_lock_bh(&mbus->tx_lock);
4317eec1181SMatt Johnston 		skb = mbus->tx_skb;
4327eec1181SMatt Johnston 		mbus->tx_skb = NULL;
4337eec1181SMatt Johnston 		spin_unlock_bh(&mbus->tx_lock);
4347eec1181SMatt Johnston 
4357eec1181SMatt Johnston 		if (netif_queue_stopped(mbus->ndev))
4367eec1181SMatt Johnston 			netif_wake_queue(mbus->ndev);
4377eec1181SMatt Johnston 
4387eec1181SMatt Johnston 		if (skb) {
4397eec1181SMatt Johnston 			mctp_i3c_xmit(mbus, skb);
4407eec1181SMatt Johnston 			kfree_skb(skb);
4417eec1181SMatt Johnston 		} else {
4427eec1181SMatt Johnston 			wait_event_idle(mbus->tx_wq,
4437eec1181SMatt Johnston 					mbus->tx_skb || kthread_should_stop());
4447eec1181SMatt Johnston 		}
4457eec1181SMatt Johnston 	}
4467eec1181SMatt Johnston 
4477eec1181SMatt Johnston 	return 0;
4487eec1181SMatt Johnston }
4497eec1181SMatt Johnston 
mctp_i3c_start_xmit(struct sk_buff * skb,struct net_device * ndev)4507eec1181SMatt Johnston static netdev_tx_t mctp_i3c_start_xmit(struct sk_buff *skb,
4517eec1181SMatt Johnston 				       struct net_device *ndev)
4527eec1181SMatt Johnston {
4537eec1181SMatt Johnston 	struct mctp_i3c_bus *mbus = netdev_priv(ndev);
4547eec1181SMatt Johnston 	netdev_tx_t ret;
4557eec1181SMatt Johnston 
4567eec1181SMatt Johnston 	spin_lock(&mbus->tx_lock);
4577eec1181SMatt Johnston 	netif_stop_queue(ndev);
4587eec1181SMatt Johnston 	if (mbus->tx_skb) {
4597eec1181SMatt Johnston 		dev_warn_ratelimited(&ndev->dev, "TX with queue stopped");
4607eec1181SMatt Johnston 		ret = NETDEV_TX_BUSY;
4617eec1181SMatt Johnston 	} else {
4627eec1181SMatt Johnston 		mbus->tx_skb = skb;
4637eec1181SMatt Johnston 		ret = NETDEV_TX_OK;
4647eec1181SMatt Johnston 	}
4657eec1181SMatt Johnston 	spin_unlock(&mbus->tx_lock);
4667eec1181SMatt Johnston 
4677eec1181SMatt Johnston 	if (ret == NETDEV_TX_OK)
4687eec1181SMatt Johnston 		wake_up(&mbus->tx_wq);
4697eec1181SMatt Johnston 
4707eec1181SMatt Johnston 	return ret;
4717eec1181SMatt Johnston }
4727eec1181SMatt Johnston 
mctp_i3c_bus_free(struct mctp_i3c_bus * mbus)4737eec1181SMatt Johnston static void mctp_i3c_bus_free(struct mctp_i3c_bus *mbus)
4747eec1181SMatt Johnston __must_hold(&busdevs_lock)
4757eec1181SMatt Johnston {
4767eec1181SMatt Johnston 	struct mctp_i3c_device *mi = NULL, *tmp = NULL;
4777eec1181SMatt Johnston 
4787eec1181SMatt Johnston 	if (mbus->tx_thread) {
4797eec1181SMatt Johnston 		kthread_stop(mbus->tx_thread);
4807eec1181SMatt Johnston 		mbus->tx_thread = NULL;
4817eec1181SMatt Johnston 	}
4827eec1181SMatt Johnston 
4837eec1181SMatt Johnston 	/* Remove any child devices */
4847eec1181SMatt Johnston 	list_for_each_entry_safe(mi, tmp, &mbus->devs, list) {
4857eec1181SMatt Johnston 		mctp_i3c_remove_device(mi);
4867eec1181SMatt Johnston 	}
4877eec1181SMatt Johnston 
4887eec1181SMatt Johnston 	kfree_skb(mbus->tx_skb);
4897eec1181SMatt Johnston 	list_del(&mbus->list);
4907eec1181SMatt Johnston }
4917eec1181SMatt Johnston 
mctp_i3c_ndo_uninit(struct net_device * ndev)4927eec1181SMatt Johnston static void mctp_i3c_ndo_uninit(struct net_device *ndev)
4937eec1181SMatt Johnston {
4947eec1181SMatt Johnston 	struct mctp_i3c_bus *mbus = netdev_priv(ndev);
4957eec1181SMatt Johnston 
4967eec1181SMatt Johnston 	/* Perform cleanup here to ensure there are no remaining references */
4977eec1181SMatt Johnston 	mctp_i3c_bus_free(mbus);
4987eec1181SMatt Johnston }
4997eec1181SMatt Johnston 
mctp_i3c_header_create(struct sk_buff * skb,struct net_device * dev,unsigned short type,const void * daddr,const void * saddr,unsigned int len)5007eec1181SMatt Johnston static int mctp_i3c_header_create(struct sk_buff *skb, struct net_device *dev,
5017eec1181SMatt Johnston 				  unsigned short type, const void *daddr,
5027eec1181SMatt Johnston 	   const void *saddr, unsigned int len)
5037eec1181SMatt Johnston {
5047eec1181SMatt Johnston 	struct mctp_i3c_internal_hdr *ihdr;
5057eec1181SMatt Johnston 
5067eec1181SMatt Johnston 	skb_push(skb, sizeof(struct mctp_i3c_internal_hdr));
5077eec1181SMatt Johnston 	skb_reset_mac_header(skb);
5087eec1181SMatt Johnston 	ihdr = (void *)skb_mac_header(skb);
5097eec1181SMatt Johnston 	memcpy(ihdr->dest, daddr, PID_SIZE);
5107eec1181SMatt Johnston 	memcpy(ihdr->source, saddr, PID_SIZE);
5117eec1181SMatt Johnston 	return 0;
5127eec1181SMatt Johnston }
5137eec1181SMatt Johnston 
5147eec1181SMatt Johnston static const struct net_device_ops mctp_i3c_ops = {
5157eec1181SMatt Johnston 	.ndo_start_xmit = mctp_i3c_start_xmit,
5167eec1181SMatt Johnston 	.ndo_uninit = mctp_i3c_ndo_uninit,
5177eec1181SMatt Johnston };
5187eec1181SMatt Johnston 
5197eec1181SMatt Johnston static const struct header_ops mctp_i3c_headops = {
5207eec1181SMatt Johnston 	.create = mctp_i3c_header_create,
5217eec1181SMatt Johnston };
5227eec1181SMatt Johnston 
mctp_i3c_net_setup(struct net_device * dev)5237eec1181SMatt Johnston static void mctp_i3c_net_setup(struct net_device *dev)
5247eec1181SMatt Johnston {
5257eec1181SMatt Johnston 	dev->type = ARPHRD_MCTP;
5267eec1181SMatt Johnston 
5277eec1181SMatt Johnston 	dev->mtu = MCTP_I3C_MAXMTU;
5287eec1181SMatt Johnston 	dev->min_mtu = MCTP_I3C_MINMTU;
5297eec1181SMatt Johnston 	dev->max_mtu = MCTP_I3C_MAXMTU;
5307eec1181SMatt Johnston 	dev->tx_queue_len = MCTP_I3C_TX_QUEUE_LEN;
5317eec1181SMatt Johnston 
5327eec1181SMatt Johnston 	dev->hard_header_len = sizeof(struct mctp_i3c_internal_hdr);
5337eec1181SMatt Johnston 	dev->addr_len = PID_SIZE;
5347eec1181SMatt Johnston 
5357eec1181SMatt Johnston 	dev->netdev_ops	= &mctp_i3c_ops;
5367eec1181SMatt Johnston 	dev->header_ops	= &mctp_i3c_headops;
5377eec1181SMatt Johnston }
5387eec1181SMatt Johnston 
mctp_i3c_is_mctp_controller(struct i3c_bus * bus)5397eec1181SMatt Johnston static bool mctp_i3c_is_mctp_controller(struct i3c_bus *bus)
5407eec1181SMatt Johnston {
5417eec1181SMatt Johnston 	struct i3c_dev_desc *master = bus->cur_master;
5427eec1181SMatt Johnston 
5437eec1181SMatt Johnston 	if (!master)
5447eec1181SMatt Johnston 		return false;
5457eec1181SMatt Johnston 
5467eec1181SMatt Johnston 	return of_property_read_bool(master->common.master->dev.of_node,
5477eec1181SMatt Johnston 				     MCTP_I3C_OF_PROP);
5487eec1181SMatt Johnston }
5497eec1181SMatt Johnston 
5507eec1181SMatt Johnston /* Returns the Provisioned Id of a local bus master */
mctp_i3c_bus_local_pid(struct i3c_bus * bus,u64 * ret_pid)5517eec1181SMatt Johnston static int mctp_i3c_bus_local_pid(struct i3c_bus *bus, u64 *ret_pid)
5527eec1181SMatt Johnston {
5537eec1181SMatt Johnston 	struct i3c_dev_desc *master;
5547eec1181SMatt Johnston 
5557eec1181SMatt Johnston 	master = bus->cur_master;
5567eec1181SMatt Johnston 	if (WARN_ON_ONCE(!master))
5577eec1181SMatt Johnston 		return -ENOENT;
5587eec1181SMatt Johnston 	*ret_pid = master->info.pid;
5597eec1181SMatt Johnston 
5607eec1181SMatt Johnston 	return 0;
5617eec1181SMatt Johnston }
5627eec1181SMatt Johnston 
5637eec1181SMatt Johnston /* Returns an ERR_PTR on failure */
mctp_i3c_bus_add(struct i3c_bus * bus)5647eec1181SMatt Johnston static struct mctp_i3c_bus *mctp_i3c_bus_add(struct i3c_bus *bus)
5657eec1181SMatt Johnston __must_hold(&busdevs_lock)
5667eec1181SMatt Johnston {
5677eec1181SMatt Johnston 	struct mctp_i3c_bus *mbus = NULL;
5687eec1181SMatt Johnston 	struct net_device *ndev = NULL;
5697eec1181SMatt Johnston 	char namebuf[IFNAMSIZ];
5707eec1181SMatt Johnston 	u8 addr[PID_SIZE];
5717eec1181SMatt Johnston 	int rc;
5727eec1181SMatt Johnston 
5737eec1181SMatt Johnston 	if (!mctp_i3c_is_mctp_controller(bus))
5747eec1181SMatt Johnston 		return ERR_PTR(-ENOENT);
5757eec1181SMatt Johnston 
5767eec1181SMatt Johnston 	snprintf(namebuf, sizeof(namebuf), "mctpi3c%d", bus->id);
5777eec1181SMatt Johnston 	ndev = alloc_netdev(sizeof(*mbus), namebuf, NET_NAME_ENUM,
5787eec1181SMatt Johnston 			    mctp_i3c_net_setup);
5797eec1181SMatt Johnston 	if (!ndev) {
5807eec1181SMatt Johnston 		rc = -ENOMEM;
5817eec1181SMatt Johnston 		goto err;
5827eec1181SMatt Johnston 	}
5837eec1181SMatt Johnston 
5847eec1181SMatt Johnston 	mbus = netdev_priv(ndev);
5857eec1181SMatt Johnston 	mbus->ndev = ndev;
5867eec1181SMatt Johnston 	mbus->bus = bus;
5877eec1181SMatt Johnston 	INIT_LIST_HEAD(&mbus->devs);
5887eec1181SMatt Johnston 	list_add(&mbus->list, &busdevs);
5897eec1181SMatt Johnston 
5907eec1181SMatt Johnston 	rc = mctp_i3c_bus_local_pid(bus, &mbus->pid);
5917eec1181SMatt Johnston 	if (rc < 0) {
5927eec1181SMatt Johnston 		dev_err(&ndev->dev, "No I3C PID available\n");
5937eec1181SMatt Johnston 		goto err_free_uninit;
5947eec1181SMatt Johnston 	}
5957eec1181SMatt Johnston 	put_unaligned_be48(mbus->pid, addr);
5967eec1181SMatt Johnston 	dev_addr_set(ndev, addr);
5977eec1181SMatt Johnston 
5987eec1181SMatt Johnston 	init_waitqueue_head(&mbus->tx_wq);
5997eec1181SMatt Johnston 	spin_lock_init(&mbus->tx_lock);
6007eec1181SMatt Johnston 	mbus->tx_thread = kthread_run(mctp_i3c_tx_thread, mbus,
6017eec1181SMatt Johnston 				      "%s/tx", ndev->name);
6027eec1181SMatt Johnston 	if (IS_ERR(mbus->tx_thread)) {
6037eec1181SMatt Johnston 		dev_warn(&ndev->dev, "Error creating thread: %pe\n",
6047eec1181SMatt Johnston 			 mbus->tx_thread);
6057eec1181SMatt Johnston 		rc = PTR_ERR(mbus->tx_thread);
6067eec1181SMatt Johnston 		mbus->tx_thread = NULL;
6077eec1181SMatt Johnston 		goto err_free_uninit;
6087eec1181SMatt Johnston 	}
6097eec1181SMatt Johnston 
6107eec1181SMatt Johnston 	rc = mctp_register_netdev(ndev, NULL);
6117eec1181SMatt Johnston 	if (rc < 0) {
6127eec1181SMatt Johnston 		dev_warn(&ndev->dev, "netdev register failed: %d\n", rc);
6137eec1181SMatt Johnston 		goto err_free_netdev;
6147eec1181SMatt Johnston 	}
6157eec1181SMatt Johnston 	return mbus;
6167eec1181SMatt Johnston 
6177eec1181SMatt Johnston err_free_uninit:
6187eec1181SMatt Johnston 	/* uninit will not get called if a netdev has not been registered,
6197eec1181SMatt Johnston 	 * so we perform the same mbus cleanup manually.
6207eec1181SMatt Johnston 	 */
6217eec1181SMatt Johnston 	mctp_i3c_bus_free(mbus);
6227eec1181SMatt Johnston 
6237eec1181SMatt Johnston err_free_netdev:
6247eec1181SMatt Johnston 	free_netdev(ndev);
6257eec1181SMatt Johnston 
6267eec1181SMatt Johnston err:
6277eec1181SMatt Johnston 	return ERR_PTR(rc);
6287eec1181SMatt Johnston }
6297eec1181SMatt Johnston 
mctp_i3c_bus_remove(struct mctp_i3c_bus * mbus)6307eec1181SMatt Johnston static void mctp_i3c_bus_remove(struct mctp_i3c_bus *mbus)
6317eec1181SMatt Johnston __must_hold(&busdevs_lock)
6327eec1181SMatt Johnston {
6337eec1181SMatt Johnston 	/* Unregister calls through to ndo_uninit -> mctp_i3c_bus_free() */
6347eec1181SMatt Johnston 	mctp_unregister_netdev(mbus->ndev);
6357eec1181SMatt Johnston 
6367eec1181SMatt Johnston 	free_netdev(mbus->ndev);
6377eec1181SMatt Johnston 	/* mbus is deallocated */
6387eec1181SMatt Johnston }
6397eec1181SMatt Johnston 
6407eec1181SMatt Johnston /* Removes all mctp-i3c busses */
mctp_i3c_bus_remove_all(void)6417eec1181SMatt Johnston static void mctp_i3c_bus_remove_all(void)
6427eec1181SMatt Johnston {
6437eec1181SMatt Johnston 	struct mctp_i3c_bus *mbus = NULL, *tmp = NULL;
6447eec1181SMatt Johnston 
6457eec1181SMatt Johnston 	mutex_lock(&busdevs_lock);
6467eec1181SMatt Johnston 	list_for_each_entry_safe(mbus, tmp, &busdevs, list) {
6477eec1181SMatt Johnston 		mctp_i3c_bus_remove(mbus);
6487eec1181SMatt Johnston 	}
6497eec1181SMatt Johnston 	mutex_unlock(&busdevs_lock);
6507eec1181SMatt Johnston }
6517eec1181SMatt Johnston 
6527eec1181SMatt Johnston /* Adds a i3c_bus if it isn't already in the busdevs list.
6537eec1181SMatt Johnston  * Suitable as an i3c_for_each_bus_locked callback.
6547eec1181SMatt Johnston  */
mctp_i3c_bus_add_new(struct i3c_bus * bus,void * data)6557eec1181SMatt Johnston static int mctp_i3c_bus_add_new(struct i3c_bus *bus, void *data)
6567eec1181SMatt Johnston {
6577eec1181SMatt Johnston 	struct mctp_i3c_bus *mbus = NULL, *tmp = NULL;
6587eec1181SMatt Johnston 	bool exists = false;
6597eec1181SMatt Johnston 
6607eec1181SMatt Johnston 	mutex_lock(&busdevs_lock);
6617eec1181SMatt Johnston 	list_for_each_entry_safe(mbus, tmp, &busdevs, list)
6627eec1181SMatt Johnston 		if (mbus->bus == bus)
6637eec1181SMatt Johnston 			exists = true;
6647eec1181SMatt Johnston 
6657eec1181SMatt Johnston 	/* It is OK for a bus to already exist. That can occur due to
6667eec1181SMatt Johnston 	 * the race in mod_init between notifier and for_each_bus
6677eec1181SMatt Johnston 	 */
6687eec1181SMatt Johnston 	if (!exists)
6697eec1181SMatt Johnston 		mctp_i3c_bus_add(bus);
6707eec1181SMatt Johnston 	mutex_unlock(&busdevs_lock);
6717eec1181SMatt Johnston 	return 0;
6727eec1181SMatt Johnston }
6737eec1181SMatt Johnston 
mctp_i3c_notify_bus_remove(struct i3c_bus * bus)6747eec1181SMatt Johnston static void mctp_i3c_notify_bus_remove(struct i3c_bus *bus)
6757eec1181SMatt Johnston {
6767eec1181SMatt Johnston 	struct mctp_i3c_bus *mbus = NULL, *tmp;
6777eec1181SMatt Johnston 
6787eec1181SMatt Johnston 	mutex_lock(&busdevs_lock);
6797eec1181SMatt Johnston 	list_for_each_entry_safe(mbus, tmp, &busdevs, list)
6807eec1181SMatt Johnston 		if (mbus->bus == bus)
6817eec1181SMatt Johnston 			mctp_i3c_bus_remove(mbus);
6827eec1181SMatt Johnston 	mutex_unlock(&busdevs_lock);
6837eec1181SMatt Johnston }
6847eec1181SMatt Johnston 
mctp_i3c_notifier_call(struct notifier_block * nb,unsigned long action,void * data)6857eec1181SMatt Johnston static int mctp_i3c_notifier_call(struct notifier_block *nb,
6867eec1181SMatt Johnston 				  unsigned long action, void *data)
6877eec1181SMatt Johnston {
6887eec1181SMatt Johnston 	switch (action) {
6897eec1181SMatt Johnston 	case I3C_NOTIFY_BUS_ADD:
6907eec1181SMatt Johnston 		mctp_i3c_bus_add_new((struct i3c_bus *)data, NULL);
6917eec1181SMatt Johnston 		break;
6927eec1181SMatt Johnston 	case I3C_NOTIFY_BUS_REMOVE:
6937eec1181SMatt Johnston 		mctp_i3c_notify_bus_remove((struct i3c_bus *)data);
6947eec1181SMatt Johnston 		break;
6957eec1181SMatt Johnston 	}
6967eec1181SMatt Johnston 	return NOTIFY_DONE;
6977eec1181SMatt Johnston }
6987eec1181SMatt Johnston 
6997eec1181SMatt Johnston static struct notifier_block mctp_i3c_notifier = {
7007eec1181SMatt Johnston 	.notifier_call = mctp_i3c_notifier_call,
7017eec1181SMatt Johnston };
7027eec1181SMatt Johnston 
7037eec1181SMatt Johnston static const struct i3c_device_id mctp_i3c_ids[] = {
7047eec1181SMatt Johnston 	I3C_CLASS(I3C_DCR_MCTP, NULL),
7057eec1181SMatt Johnston 	{ 0 },
7067eec1181SMatt Johnston };
7077eec1181SMatt Johnston 
7087eec1181SMatt Johnston static struct i3c_driver mctp_i3c_driver = {
7097eec1181SMatt Johnston 	.driver = {
7107eec1181SMatt Johnston 		.name = "mctp-i3c",
7117eec1181SMatt Johnston 	},
7127eec1181SMatt Johnston 	.probe = mctp_i3c_probe,
7137eec1181SMatt Johnston 	.remove = mctp_i3c_remove,
7147eec1181SMatt Johnston 	.id_table = mctp_i3c_ids,
7157eec1181SMatt Johnston };
7167eec1181SMatt Johnston 
mctp_i3c_mod_init(void)7177eec1181SMatt Johnston static __init int mctp_i3c_mod_init(void)
7187eec1181SMatt Johnston {
7197eec1181SMatt Johnston 	int rc;
7207eec1181SMatt Johnston 
7217eec1181SMatt Johnston 	rc = i3c_register_notifier(&mctp_i3c_notifier);
7227eec1181SMatt Johnston 	if (rc < 0) {
7237eec1181SMatt Johnston 		i3c_driver_unregister(&mctp_i3c_driver);
7247eec1181SMatt Johnston 		return rc;
7257eec1181SMatt Johnston 	}
7267eec1181SMatt Johnston 
7277eec1181SMatt Johnston 	i3c_for_each_bus_locked(mctp_i3c_bus_add_new, NULL);
7287eec1181SMatt Johnston 
7297eec1181SMatt Johnston 	rc = i3c_driver_register(&mctp_i3c_driver);
7307eec1181SMatt Johnston 	if (rc < 0)
7317eec1181SMatt Johnston 		return rc;
7327eec1181SMatt Johnston 
7337eec1181SMatt Johnston 	return 0;
7347eec1181SMatt Johnston }
7357eec1181SMatt Johnston 
mctp_i3c_mod_exit(void)7367eec1181SMatt Johnston static __exit void mctp_i3c_mod_exit(void)
7377eec1181SMatt Johnston {
7387eec1181SMatt Johnston 	int rc;
7397eec1181SMatt Johnston 
7407eec1181SMatt Johnston 	i3c_driver_unregister(&mctp_i3c_driver);
7417eec1181SMatt Johnston 
7427eec1181SMatt Johnston 	rc = i3c_unregister_notifier(&mctp_i3c_notifier);
7437eec1181SMatt Johnston 	if (rc < 0)
7447eec1181SMatt Johnston 		pr_warn("MCTP I3C could not unregister notifier, %d\n", rc);
7457eec1181SMatt Johnston 
7467eec1181SMatt Johnston 	mctp_i3c_bus_remove_all();
7477eec1181SMatt Johnston }
7487eec1181SMatt Johnston 
7497eec1181SMatt Johnston module_init(mctp_i3c_mod_init);
7507eec1181SMatt Johnston module_exit(mctp_i3c_mod_exit);
7517eec1181SMatt Johnston 
7527eec1181SMatt Johnston MODULE_DEVICE_TABLE(i3c, mctp_i3c_ids);
7537eec1181SMatt Johnston MODULE_DESCRIPTION("MCTP I3C device");
7547eec1181SMatt Johnston MODULE_LICENSE("GPL");
7557eec1181SMatt Johnston MODULE_AUTHOR("Matt Johnston <matt@codeconstruct.com.au>");
756