summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0108-soc-aspeed-mctp-Add-initial-driver-for-ast2600-mctp.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0108-soc-aspeed-mctp-Add-initial-driver-for-ast2600-mctp.patch')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0108-soc-aspeed-mctp-Add-initial-driver-for-ast2600-mctp.patch1265
1 files changed, 0 insertions, 1265 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0108-soc-aspeed-mctp-Add-initial-driver-for-ast2600-mctp.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0108-soc-aspeed-mctp-Add-initial-driver-for-ast2600-mctp.patch
deleted file mode 100644
index bff271bfe..000000000
--- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0108-soc-aspeed-mctp-Add-initial-driver-for-ast2600-mctp.patch
+++ /dev/null
@@ -1,1265 +0,0 @@
-From cafc578414a664ecde96598838a3907a5d915416 Mon Sep 17 00:00:00 2001
-From: Iwona Winiarska <iwona.winiarska@intel.com>
-Date: Mon, 6 Apr 2020 22:00:46 +0200
-Subject: [PATCH 108/108] soc: aspeed: mctp: Add initial driver for
- ast2600-mctp
-
-Currently, there is no proper MCTP networking subsystem in Linux.
-Until we are able to work out the details of that, we are going to
-expose HW to userspace using raw read/write interface.
-Because of that, this driver is not intended to be submitted upstream.
-
-Here we are providing a simple device driver for AST2600 MCTP
-controller.
-
-Signed-off-by: Iwona Winiarska <iwona.winiarska@intel.com>
----
- drivers/soc/aspeed/Kconfig | 8 +
- drivers/soc/aspeed/Makefile | 1 +
- drivers/soc/aspeed/aspeed-mctp.c | 1126 ++++++++++++++++++++++++++++++
- include/uapi/linux/aspeed-mctp.h | 72 ++
- 4 files changed, 1207 insertions(+)
- create mode 100644 drivers/soc/aspeed/aspeed-mctp.c
- create mode 100644 include/uapi/linux/aspeed-mctp.h
-
-diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig
-index 13cd9fbabbf1..78d9140d822b 100644
---- a/drivers/soc/aspeed/Kconfig
-+++ b/drivers/soc/aspeed/Kconfig
-@@ -43,6 +43,14 @@ config ASPEED_LPC_SNOOP
- allows the BMC to listen on and save the data written by
- the host to an arbitrary LPC I/O port.
-
-+config ASPEED_MCTP
-+ tristate "Aspeed ast2600 MCTP Controller support"
-+ depends on SOC_ASPEED && REGMAP && MFD_SYSCON
-+ help
-+ Enable support for ast2600 MCTP Controller.
-+ The MCTP controller allows the BMC to communicate with devices on
-+ the host PCIe network.
-+
- config ASPEED_P2A_CTRL
- depends on SOC_ASPEED && REGMAP && MFD_SYSCON
- tristate "Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC bridge control"
-diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile
-index 875f0d9e7866..b3c143f9e24c 100644
---- a/drivers/soc/aspeed/Makefile
-+++ b/drivers/soc/aspeed/Makefile
-@@ -7,3 +7,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
- obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o
- obj-$(CONFIG_ASPEED_XDMA) += aspeed-xdma.o
- obj-$(CONFIG_ASPEED_VGA_SHAREDMEM) += aspeed-vga-sharedmem.o
-+obj-$(CONFIG_ASPEED_MCTP) += aspeed-mctp.o
-diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c
-new file mode 100644
-index 000000000000..addf96133abf
---- /dev/null
-+++ b/drivers/soc/aspeed/aspeed-mctp.c
-@@ -0,0 +1,1126 @@
-+// SPDX-License-Identifier: GPL-2.0
-+// Copyright (c) 2020, Intel Corporation.
-+
-+#include <linux/dma-mapping.h>
-+#include <linux/interrupt.h>
-+#include <linux/init.h>
-+#include <linux/io.h>
-+#include <linux/kernel.h>
-+#include <linux/list.h>
-+#include <linux/mfd/syscon.h>
-+#include <linux/miscdevice.h>
-+#include <linux/mm.h>
-+#include <linux/module.h>
-+#include <linux/of_platform.h>
-+#include <linux/pci.h>
-+#include <linux/poll.h>
-+#include <linux/ptr_ring.h>
-+#include <linux/regmap.h>
-+#include <linux/reset.h>
-+#include <linux/slab.h>
-+#include <linux/uaccess.h>
-+#include <linux/workqueue.h>
-+#include <uapi/linux/aspeed-mctp.h>
-+
-+/* AST2600 MCTP Controller registers */
-+#define ASPEED_MCTP_CTRL 0x000
-+#define TX_CMD_TRIGGER BIT(0)
-+#define RX_CMD_READY BIT(4)
-+#define MATCHING_EID BIT(9)
-+
-+#define ASPEED_MCTP_TX_CMD 0x004
-+#define ASPEED_MCTP_RX_CMD 0x008
-+
-+#define ASPEED_MCTP_INT_STS 0x00c
-+#define ASPEED_MCTP_INT_EN 0x010
-+#define TX_CMD_SENT_INT BIT(0)
-+#define TX_CMD_LAST_INT BIT(1)
-+#define TX_CMD_WRONG_INT BIT(2)
-+#define RX_CMD_RECEIVE_INT BIT(8)
-+#define RX_CMD_NO_MORE_INT BIT(9)
-+
-+#define ASPEED_MCTP_EID 0x014
-+#define ASPEED_MCTP_OBFF_CTRL 0x018
-+
-+#define ASPEED_MCTP_ENGINE_CTRL 0x01c
-+#define TX_MAX_PAYLOAD_SIZE_SHIFT 0
-+#define TX_MAX_PAYLOAD_SIZE_MASK GENMASK(1, TX_MAX_PAYLOAD_SIZE_SHIFT)
-+#define TX_MAX_PAYLOAD_SIZE(x) \
-+ (((x) << TX_MAX_PAYLOAD_SIZE_SHIFT) & TX_MAX_PAYLOAD_SIZE_MASK)
-+#define RX_MAX_PAYLOAD_SIZE_SHIFT 4
-+#define RX_MAX_PAYLOAD_SIZE_MASK GENMASK(5, RX_MAX_PAYLOAD_SIZE_SHIFT)
-+#define RX_MAX_PAYLOAD_SIZE(x) \
-+ (((x) << RX_MAX_PAYLOAD_SIZE_SHIFT) & RX_MAX_PAYLOAD_SIZE_MASK)
-+#define FIFO_LAYOUT_SHIFT 8
-+#define FIFO_LAYOUT_MASK GENMASK(9, FIFO_LAYOUT_SHIFT)
-+#define FIFO_LAYOUT(x) \
-+ (((x) << FIFO_LAYOUT_SHIFT) & FIFO_LAYOUT_MASK)
-+
-+#define ASPEED_MCTP_RX_BUF_ADDR 0x020
-+#define ASPEED_MCTP_RX_BUF_SIZE 0x024
-+#define ASPEED_MCTP_RX_BUF_RD_PTR 0x028
-+#define UPDATE_RX_RD_PTR BIT(31)
-+#define RX_BUFFER_RD_PTR GENMASK(11, 0)
-+#define ASPEED_MCTP_RX_BUF_WR_PTR 0x02c
-+#define RX_BUFFER_WR_PTR GENMASK(11, 0)
-+
-+#define ASPEED_MCTP_TX_BUF_ADDR 0x004
-+#define ASPEED_MCTP_TX_BUF_SIZE 0x034
-+#define ASPEED_MCTP_TX_BUF_RD_PTR 0x038
-+#define UPDATE_TX_RD_PTR BIT(31)
-+#define TX_BUFFER_RD_PTR GENMASK(11, 0)
-+#define ASPEED_MCTP_TX_BUF_WR_PTR 0x03c
-+#define TX_BUFFER_WR_PTR GENMASK(11, 0)
-+
-+#define ADDR_LEN (BIT(26) - 1)
-+#define DATA_ADDR(x) (((x) >> 4) & ADDR_LEN)
-+
-+/* TX command */
-+#define TX_LAST_CMD BIT(31)
-+#define TX_DATA_ADDR_SHIFT 4
-+#define TX_DATA_ADDR_MASK GENMASK(30, TX_DATA_ADDR_SHIFT)
-+#define TX_DATA_ADDR(x) \
-+ ((DATA_ADDR(x) << TX_DATA_ADDR_SHIFT) & TX_DATA_ADDR_MASK)
-+#define TX_RESERVED_1_MASK GENMASK(1, 0) /* must be 1 */
-+#define TX_RESERVED_1 1
-+#define TX_STOP_AFTER_CMD BIT(16)
-+#define TX_INTERRUPT_AFTER_CMD BIT(15)
-+#define TX_PACKET_SIZE_SHIFT 2
-+#define TX_PACKET_SIZE_MASK GENMASK(12, TX_PACKET_SIZE_SHIFT)
-+#define TX_PACKET_SIZE(x) \
-+ (((x) << TX_PACKET_SIZE_SHIFT) & TX_PACKET_SIZE_MASK)
-+#define TX_RESERVED_0_MASK GENMASK(1, 0) /* MBZ */
-+#define TX_RESERVED_0 0
-+
-+/* RX command */
-+#define RX_INTERRUPT_AFTER_CMD BIT(2)
-+#define RX_DATA_ADDR_SHIFT 4
-+#define RX_DATA_ADDR_MASK GENMASK(30, RX_DATA_ADDR_SHIFT)
-+#define RX_DATA_ADDR(x) \
-+ ((DATA_ADDR(x) << RX_DATA_ADDR_SHIFT) & RX_DATA_ADDR_MASK)
-+
-+/* Buffer sizes */
-+#define TX_CMD_COUNT 4
-+#define RX_CMD_COUNT 4
-+#define TX_MAX_CMD_COUNT SZ_4K
-+#define RX_MAX_CMD_COUNT SZ_4K
-+
-+/* PCIe Host Controller registers */
-+#define ASPEED_PCIE_MISC_STS_1 0x0c4
-+
-+/* PCI address definitions */
-+#define PCI_DEV_NUM_MASK GENMASK(4, 0)
-+#define PCI_BUS_NUM_SHIFT 5
-+#define PCI_BUS_NUM_MASK GENMASK(12, PCI_BUS_NUM_SHIFT)
-+#define GET_PCI_DEV_NUM(x) ((x) & PCI_DEV_NUM_MASK)
-+#define GET_PCI_BUS_NUM(x) (((x) & PCI_BUS_NUM_MASK) >> PCI_BUS_NUM_SHIFT)
-+
-+/* FIXME: ast2600 supports variable max transmission unit */
-+#define ASPEED_MCTP_MTU 64
-+
-+struct mctp_pcie_packet {
-+ struct {
-+ u32 hdr[4];
-+ u32 payload[16];
-+ } data;
-+ u32 size;
-+};
-+
-+struct aspeed_mctp_tx_cmd {
-+ u32 tx_lo;
-+ u32 tx_hi;
-+};
-+
-+struct mctp_buffer {
-+ void *vaddr;
-+ dma_addr_t dma_handle;
-+};
-+
-+struct mctp_channel {
-+ struct mctp_buffer data;
-+ struct mctp_buffer cmd;
-+ struct tasklet_struct tasklet;
-+ u32 rd_ptr;
-+ u32 wr_ptr;
-+};
-+
-+struct aspeed_mctp {
-+ struct device *dev;
-+ struct regmap *map;
-+ struct reset_control *reset;
-+ struct mctp_channel tx;
-+ struct mctp_channel rx;
-+ struct list_head clients;
-+ spinlock_t clients_lock; /* to protect clients list operations */
-+ wait_queue_head_t wait_queue;
-+ struct {
-+ struct regmap *map;
-+ struct delayed_work rst_dwork;
-+ bool need_uevent;
-+ u16 bdf;
-+ } pcie;
-+};
-+
-+struct mctp_client {
-+ struct kref ref;
-+ struct aspeed_mctp *priv;
-+ struct ptr_ring tx_queue;
-+ struct ptr_ring rx_queue;
-+ struct list_head link;
-+ bool disconnected;
-+};
-+
-+#define TX_CMD_BUF_SIZE \
-+ PAGE_ALIGN(TX_CMD_COUNT * sizeof(struct aspeed_mctp_tx_cmd))
-+#define TX_DATA_BUF_SIZE \
-+ PAGE_ALIGN(TX_CMD_COUNT * sizeof(struct mctp_pcie_packet))
-+#define RX_CMD_BUF_SIZE PAGE_ALIGN(RX_CMD_COUNT * sizeof(u32))
-+#define RX_DATA_BUF_SIZE \
-+ PAGE_ALIGN(RX_CMD_COUNT * sizeof(struct mctp_pcie_packet))
-+
-+struct kmem_cache *packet_cache;
-+
-+static void *packet_alloc(gfp_t flags)
-+{
-+ return kmem_cache_alloc(packet_cache, flags);
-+}
-+
-+static void packet_free(void *packet)
-+{
-+ kmem_cache_free(packet_cache, packet);
-+}
-+
-+static void aspeed_mctp_rx_trigger(struct mctp_channel *rx)
-+{
-+ struct aspeed_mctp *priv = container_of(rx, typeof(*priv), rx);
-+
-+ /*
-+ * Even though rx_buf_addr doesn't change, if we don't do the write
-+ * here, the HW doesn't trigger RX.
-+ * Also, note that we're writing 0 as wr_ptr. If we're writing other
-+ * value, the HW behaves in a bizzare way that's hard to explain...
-+ */
-+ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_ADDR, rx->cmd.dma_handle);
-+ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_WR_PTR, 0);
-+ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL, RX_CMD_READY,
-+ RX_CMD_READY);
-+}
-+
-+static void aspeed_mctp_tx_trigger(struct mctp_channel *tx)
-+{
-+ struct aspeed_mctp *priv = container_of(tx, typeof(*priv), tx);
-+
-+ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_ADDR,
-+ tx->cmd.dma_handle);
-+ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_WR_PTR, tx->wr_ptr);
-+ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL, TX_CMD_TRIGGER,
-+ TX_CMD_TRIGGER);
-+}
-+
-+static void aspeed_mctp_emit_tx_cmd(struct mctp_channel *tx,
-+ struct mctp_pcie_packet *packet, bool last)
-+{
-+ struct aspeed_mctp_tx_cmd *tx_cmd =
-+ (struct aspeed_mctp_tx_cmd *)tx->cmd.vaddr + tx->wr_ptr;
-+ u32 packet_sz_dw = packet->size / sizeof(u32) -
-+ sizeof(packet->data.hdr) / sizeof(u32);
-+ u32 offset = tx->wr_ptr * sizeof(packet->data);
-+
-+ memcpy(tx->data.vaddr + offset, &packet->data,
-+ sizeof(packet->data));
-+
-+ tx_cmd->tx_lo |= TX_PACKET_SIZE(packet_sz_dw);
-+ tx_cmd->tx_lo |= TX_STOP_AFTER_CMD;
-+ tx_cmd->tx_lo |= TX_INTERRUPT_AFTER_CMD;
-+ tx_cmd->tx_hi |= TX_RESERVED_1;
-+ tx_cmd->tx_hi |= TX_DATA_ADDR(tx->data.dma_handle + offset);
-+ if (last)
-+ tx_cmd->tx_hi |= TX_LAST_CMD;
-+
-+ tx->wr_ptr++;
-+}
-+
-+static struct mctp_client *aspeed_mctp_client_alloc(struct aspeed_mctp *priv)
-+{
-+ struct mctp_client *client;
-+
-+ client = kzalloc(sizeof(*client), GFP_KERNEL);
-+ if (!client)
-+ goto out;
-+
-+ kref_init(&client->ref);
-+ client->priv = priv;
-+ ptr_ring_init(&client->tx_queue, TX_CMD_COUNT, GFP_KERNEL);
-+ ptr_ring_init(&client->rx_queue, RX_CMD_COUNT, GFP_ATOMIC);
-+
-+out:
-+ return client;
-+}
-+
-+static void aspeed_mctp_client_free(struct kref *ref)
-+{
-+ struct mctp_client *client = container_of(ref, typeof(*client), ref);
-+
-+ ptr_ring_cleanup(&client->rx_queue, &packet_free);
-+ ptr_ring_cleanup(&client->tx_queue, &packet_free);
-+
-+ kfree(client);
-+}
-+
-+static void aspeed_mctp_client_get(struct mctp_client *client)
-+{
-+ lockdep_assert_held(&client->priv->clients_lock);
-+
-+ kref_get(&client->ref);
-+}
-+
-+static void aspeed_mctp_client_put(struct mctp_client *client)
-+{
-+ kref_put(&client->ref, &aspeed_mctp_client_free);
-+}
-+
-+static void aspeed_mctp_tx_tasklet(unsigned long data)
-+{
-+ struct mctp_channel *tx = (struct mctp_channel *)data;
-+ struct aspeed_mctp *priv = container_of(tx, typeof(*priv), tx);
-+ u32 rd_ptr = READ_ONCE(tx->rd_ptr);
-+ struct mctp_pcie_packet *packet;
-+ struct mctp_client *client;
-+ bool last, trigger = false;
-+
-+ /* we're called while there's still TX in progress */
-+ if (rd_ptr == 0 && tx->wr_ptr != 0)
-+ return;
-+
-+ spin_lock(&priv->clients_lock);
-+ client = list_first_entry_or_null(&priv->clients, typeof(*client),
-+ link);
-+ if (!client) {
-+ spin_unlock(&priv->clients_lock);
-+ return;
-+ }
-+ aspeed_mctp_client_get(client);
-+ spin_unlock(&priv->clients_lock);
-+
-+ /* last tx ended up with buffer size, meaning we now restart from 0 */
-+ if (rd_ptr == TX_CMD_COUNT) {
-+ WRITE_ONCE(tx->rd_ptr, 0);
-+ tx->wr_ptr = 0;
-+ }
-+
-+ while (tx->wr_ptr < TX_CMD_COUNT) {
-+ packet = ptr_ring_consume(&client->tx_queue);
-+ if (!packet)
-+ break;
-+
-+ last = !__ptr_ring_peek(&client->tx_queue);
-+
-+ aspeed_mctp_emit_tx_cmd(tx, packet, last);
-+ packet_free(packet);
-+
-+ trigger = true;
-+
-+ if (last)
-+ break;
-+ }
-+
-+ aspeed_mctp_client_put(client);
-+
-+ if (trigger)
-+ aspeed_mctp_tx_trigger(tx);
-+}
-+
-+static void aspeed_mctp_rx_tasklet(unsigned long data)
-+{
-+ struct mctp_channel *rx = (struct mctp_channel *)data;
-+ struct aspeed_mctp *priv = container_of(rx, typeof(*priv), rx);
-+ u32 rd_ptr = READ_ONCE(priv->rx.rd_ptr);
-+ struct mctp_pcie_packet *rx_packet;
-+ struct mctp_client *client;
-+ int ret;
-+
-+ spin_lock(&priv->clients_lock);
-+ client = list_first_entry_or_null(&priv->clients, typeof(*client),
-+ link);
-+ if (!client) {
-+ rx->wr_ptr = rd_ptr;
-+ spin_unlock(&priv->clients_lock);
-+ goto out_skip;
-+ }
-+ aspeed_mctp_client_get(client);
-+ spin_unlock(&priv->clients_lock);
-+
-+ while (rx->wr_ptr < rd_ptr) {
-+ rx_packet = packet_alloc(GFP_ATOMIC);
-+ if (!rx_packet) {
-+ dev_err(priv->dev, "Failed to allocate RX packet\n");
-+ goto out_put;
-+ }
-+
-+ memcpy(&rx_packet->data,
-+ rx->data.vaddr + rx->wr_ptr * sizeof(rx_packet->data),
-+ sizeof(rx_packet->data));
-+ rx->wr_ptr++;
-+
-+ ret = ptr_ring_produce(&client->rx_queue, rx_packet);
-+ if (ret) {
-+ dev_warn(priv->dev, "Failed to produce RX packet: %d\n",
-+ ret);
-+ packet_free(rx_packet);
-+ continue;
-+ }
-+ }
-+out_skip:
-+ if (rx->wr_ptr == RX_CMD_COUNT || (rx->wr_ptr == 0 && rd_ptr == 0)) {
-+ rx->wr_ptr = 0;
-+ if (client)
-+ aspeed_mctp_rx_trigger(rx);
-+ else
-+ WRITE_ONCE(rx->rd_ptr, 0);
-+ }
-+ wake_up_all(&priv->wait_queue);
-+
-+out_put:
-+ if (client)
-+ aspeed_mctp_client_put(client);
-+}
-+
-+static void aspeed_mctp_rx_chan_init(struct mctp_channel *rx)
-+{
-+ struct aspeed_mctp *priv = container_of(rx, typeof(*priv), rx);
-+ u32 *rx_cmd = (u32 *)rx->cmd.vaddr;
-+ u32 rx_data_addr = rx->data.dma_handle;
-+ struct mctp_pcie_packet packet;
-+ u32 data_size = sizeof(packet.data);
-+ u32 i;
-+
-+ for (i = 0; i < RX_CMD_COUNT; i++) {
-+ *rx_cmd |= RX_DATA_ADDR(rx_data_addr);
-+ *rx_cmd |= RX_INTERRUPT_AFTER_CMD;
-+ rx_data_addr += data_size;
-+ rx_cmd++;
-+ }
-+ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_SIZE, RX_CMD_COUNT);
-+}
-+
-+static void aspeed_mctp_tx_chan_init(struct mctp_channel *tx)
-+{
-+ struct aspeed_mctp *priv = container_of(tx, typeof(*priv), tx);
-+
-+ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_SIZE, TX_CMD_COUNT);
-+ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_WR_PTR, 0);
-+}
-+
-+static int aspeed_mctp_open(struct inode *inode, struct file *file)
-+{
-+ struct miscdevice *misc = file->private_data;
-+ struct platform_device *pdev = to_platform_device(misc->parent);
-+ struct aspeed_mctp *priv = platform_get_drvdata(pdev);
-+ struct mctp_client *client;
-+ int ret;
-+
-+ if (priv->pcie.bdf == 0) {
-+ ret = -ENODEV;
-+ goto out;
-+ }
-+
-+ client = aspeed_mctp_client_alloc(priv);
-+ if (!client) {
-+ ret = -ENOMEM;
-+ goto out;
-+ }
-+
-+ spin_lock_bh(&priv->clients_lock);
-+ /* TODO: Add support for multiple clients */
-+ if (!list_empty(&priv->clients)) {
-+ ret = -EBUSY;
-+ goto out_unlock;
-+ }
-+ list_add_tail(&client->link, &priv->clients);
-+ spin_unlock_bh(&priv->clients_lock);
-+
-+ /*
-+ * kick the tasklet to trigger rx
-+ * bh_disable/enable is just to make sure that the tasklet gets
-+ * scheduled immediately in process context without any unnecessary
-+ * delay
-+ */
-+ local_bh_disable();
-+ tasklet_hi_schedule(&priv->rx.tasklet);
-+ local_bh_enable();
-+
-+ file->private_data = client;
-+
-+ return 0;
-+out_unlock:
-+ spin_unlock_bh(&priv->clients_lock);
-+ aspeed_mctp_client_put(client);
-+out:
-+ return ret;
-+}
-+
-+static int aspeed_mctp_release(struct inode *inode, struct file *file)
-+{
-+ struct mctp_client *client = file->private_data;
-+ struct aspeed_mctp *priv = client->priv;
-+
-+ spin_lock_bh(&priv->clients_lock);
-+ list_del(&client->link);
-+ spin_unlock_bh(&priv->clients_lock);
-+
-+ /* Disable the tasklet to appease lockdep */
-+ local_bh_disable();
-+ aspeed_mctp_client_put(client);
-+ local_bh_enable();
-+
-+ return 0;
-+}
-+
-+static ssize_t aspeed_mctp_read(struct file *file, char __user *buf,
-+ size_t count, loff_t *ppos)
-+{
-+ struct mctp_client *client = file->private_data;
-+ struct aspeed_mctp *priv = client->priv;
-+ struct mctp_pcie_packet *rx_packet;
-+ size_t packet_sz = sizeof(rx_packet->data);
-+
-+ if (READ_ONCE(client->disconnected))
-+ return -EIO;
-+
-+ if (buf && count > 0) {
-+ if (count > packet_sz)
-+ count = packet_sz;
-+
-+ rx_packet = ptr_ring_consume_bh(&client->rx_queue);
-+ if (!rx_packet)
-+ return -EAGAIN;
-+
-+ if (copy_to_user(buf, &rx_packet->data, count)) {
-+ dev_err(priv->dev, "copy to user failed\n");
-+ packet_free(rx_packet);
-+ return -EFAULT;
-+ }
-+
-+ packet_free(rx_packet);
-+ }
-+
-+ return count;
-+}
-+
-+static ssize_t aspeed_mctp_write(struct file *file, const char __user *buf,
-+ size_t count, loff_t *ppos)
-+{
-+ struct mctp_client *client = file->private_data;
-+ struct aspeed_mctp *priv = client->priv;
-+ struct mctp_pcie_packet *tx_packet;
-+ int ret;
-+
-+ if (READ_ONCE(client->disconnected))
-+ return -EIO;
-+
-+ tx_packet = packet_alloc(GFP_KERNEL);
-+ if (!tx_packet) {
-+ ret = -ENOMEM;
-+ goto out;
-+ }
-+
-+ if (buf && count > 0) {
-+ if (count > sizeof(tx_packet->data)) {
-+ ret = -ENOSPC;
-+ goto out_packet;
-+ }
-+
-+ if (copy_from_user(&tx_packet->data, buf, count)) {
-+ dev_err(priv->dev, "copy from user failed\n");
-+ ret = -EFAULT;
-+ goto out_packet;
-+ }
-+ tx_packet->size = count;
-+
-+ ret = ptr_ring_produce_bh(&client->tx_queue, tx_packet);
-+ if (ret)
-+ goto out_packet;
-+
-+ tasklet_hi_schedule(&priv->tx.tasklet);
-+ }
-+
-+ return count;
-+
-+out_packet:
-+ packet_free(tx_packet);
-+out:
-+ return ret;
-+}
-+
-+static int
-+aspeed_mctp_filter_eid(struct aspeed_mctp *priv, void __user *userbuf)
-+{
-+ struct aspeed_mctp_filter_eid eid;
-+
-+ if (copy_from_user(&eid, userbuf, sizeof(eid))) {
-+ dev_err(priv->dev, "copy from user failed\n");
-+ return -EFAULT;
-+ }
-+
-+ if (eid.enable) {
-+ regmap_write(priv->map, ASPEED_MCTP_EID, eid.eid);
-+ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL,
-+ MATCHING_EID, MATCHING_EID);
-+ } else {
-+ regmap_update_bits(priv->map, ASPEED_MCTP_CTRL,
-+ MATCHING_EID, 0);
-+ }
-+ return 0;
-+}
-+
-+static int aspeed_mctp_get_bdf(struct aspeed_mctp *priv, void __user *userbuf)
-+{
-+ struct aspeed_mctp_get_bdf bdf = { priv->pcie.bdf };
-+
-+ if (copy_to_user(userbuf, &bdf, sizeof(bdf))) {
-+ dev_err(priv->dev, "copy to user failed\n");
-+ return -EFAULT;
-+ }
-+ return 0;
-+}
-+
-+static int
-+aspeed_mctp_get_medium_id(struct aspeed_mctp *priv, void __user *userbuf)
-+{
-+ struct aspeed_mctp_get_medium_id id = { 0x09 }; /* PCIe revision 2.0 */
-+
-+ if (copy_to_user(userbuf, &id, sizeof(id))) {
-+ dev_err(priv->dev, "copy to user failed\n");
-+ return -EFAULT;
-+ }
-+ return 0;
-+}
-+
-+static int
-+aspeed_mctp_get_mtu(struct aspeed_mctp *priv, void __user *userbuf)
-+{
-+ struct aspeed_mctp_get_mtu id = { ASPEED_MCTP_MTU };
-+
-+ if (copy_to_user(userbuf, &id, sizeof(id))) {
-+ dev_err(priv->dev, "copy to user failed\n");
-+ return -EFAULT;
-+ }
-+ return 0;
-+}
-+
-+static long
-+aspeed_mctp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-+{
-+ struct mctp_client *client = file->private_data;
-+ struct aspeed_mctp *priv = client->priv;
-+ void __user *userbuf = (void __user *)arg;
-+ int ret;
-+
-+ switch (cmd) {
-+ case ASPEED_MCTP_IOCTL_FILTER_EID:
-+ ret = aspeed_mctp_filter_eid(priv, userbuf);
-+ break;
-+
-+ case ASPEED_MCTP_IOCTL_GET_BDF:
-+ ret = aspeed_mctp_get_bdf(priv, userbuf);
-+ break;
-+
-+ case ASPEED_MCTP_IOCTL_GET_MEDIUM_ID:
-+ ret = aspeed_mctp_get_medium_id(priv, userbuf);
-+ break;
-+
-+ case ASPEED_MCTP_IOCTL_GET_MTU:
-+ ret = aspeed_mctp_get_mtu(priv, userbuf);
-+ break;
-+
-+ default:
-+ dev_err(priv->dev, "Command not found\n");
-+ ret = -EINVAL;
-+ }
-+
-+ return ret;
-+}
-+
-+static __poll_t aspeed_mctp_poll(struct file *file,
-+ struct poll_table_struct *pt)
-+{
-+ struct mctp_client *client = file->private_data;
-+ struct aspeed_mctp *priv = client->priv;
-+ __poll_t ret = 0;
-+
-+ poll_wait(file, &priv->wait_queue, pt);
-+
-+ if (!ptr_ring_full_bh(&client->tx_queue))
-+ ret |= EPOLLOUT;
-+
-+ if (__ptr_ring_peek(&client->rx_queue))
-+ ret |= EPOLLIN;
-+
-+ return ret;
-+}
-+
-+static const struct file_operations aspeed_mctp_fops = {
-+ .owner = THIS_MODULE,
-+ .open = aspeed_mctp_open,
-+ .release = aspeed_mctp_release,
-+ .read = aspeed_mctp_read,
-+ .write = aspeed_mctp_write,
-+ .unlocked_ioctl = aspeed_mctp_ioctl,
-+ .poll = aspeed_mctp_poll,
-+};
-+
-+static const struct regmap_config aspeed_mctp_regmap_cfg = {
-+ .reg_bits = 32,
-+ .reg_stride = 4,
-+ .val_bits = 32,
-+ .max_register = ASPEED_MCTP_TX_BUF_WR_PTR,
-+};
-+
-+struct device_type aspeed_mctp_type = {
-+ .name = "aspeed-mctp",
-+};
-+
-+static struct miscdevice aspeed_mctp_miscdev = {
-+ .minor = MISC_DYNAMIC_MINOR,
-+ .name = "aspeed-mctp",
-+ .fops = &aspeed_mctp_fops,
-+};
-+
-+static void aspeed_mctp_send_pcie_uevent(struct kobject *kobj, bool ready)
-+{
-+ char *pcie_not_ready_event[] = { ASPEED_MCTP_READY "=0", NULL };
-+ char *pcie_ready_event[] = { ASPEED_MCTP_READY "=1", NULL };
-+
-+ kobject_uevent_env(kobj, KOBJ_CHANGE,
-+ ready ? pcie_ready_event : pcie_not_ready_event);
-+}
-+
-+static void aspeed_mctp_pcie_setup(struct aspeed_mctp *priv)
-+{
-+ u32 reg;
-+
-+ regmap_read(priv->pcie.map, ASPEED_PCIE_MISC_STS_1, &reg);
-+
-+ priv->pcie.bdf = PCI_DEVID(GET_PCI_BUS_NUM(reg), GET_PCI_DEV_NUM(reg));
-+ if (priv->pcie.bdf != 0)
-+ cancel_delayed_work(&priv->pcie.rst_dwork);
-+ else
-+ schedule_delayed_work(&priv->pcie.rst_dwork,
-+ msecs_to_jiffies(1000));
-+}
-+
-+static void aspeed_mctp_irq_enable(struct aspeed_mctp *priv)
-+{
-+ u32 enable = TX_CMD_LAST_INT | TX_CMD_WRONG_INT |
-+ RX_CMD_RECEIVE_INT;
-+
-+ regmap_write(priv->map, ASPEED_MCTP_INT_EN, enable);
-+}
-+
-+static void aspeed_mctp_irq_disable(struct aspeed_mctp *priv)
-+{
-+ regmap_write(priv->map, ASPEED_MCTP_INT_EN, 0);
-+}
-+
-+static void aspeed_mctp_reset_work(struct work_struct *work)
-+{
-+ struct aspeed_mctp *priv = container_of(work, typeof(*priv),
-+ pcie.rst_dwork.work);
-+
-+ /*
-+ * Client "disconnection" is permanent to avoid forcing the user to use
-+ * uevents in order to monitor for reset. Even if no reads/writes are
-+ * issued during pci reset, userspace will still get -EIO afterwards,
-+ * forcing it to reopen and check the BDF (which may have potentially
-+ * changed after reset). Uevents can be used to avoid looping in open()
-+ */
-+ if (priv->pcie.need_uevent) {
-+ struct kobject *kobj = &aspeed_mctp_miscdev.this_device->kobj;
-+ struct mctp_client *client;
-+
-+ spin_lock_bh(&priv->clients_lock);
-+ client = list_first_entry_or_null(&priv->clients,
-+ typeof(*client), link);
-+ /*
-+ * Here, we're only updating the "disconnected" flag under
-+ * spinlock, meaning that we can skip taking the refcount
-+ */
-+ if (client)
-+ WRITE_ONCE(client->disconnected, true);
-+ spin_unlock_bh(&priv->clients_lock);
-+
-+ aspeed_mctp_send_pcie_uevent(kobj, false);
-+ priv->pcie.need_uevent = false;
-+ }
-+
-+ aspeed_mctp_pcie_setup(priv);
-+
-+ if (priv->pcie.bdf) {
-+ aspeed_mctp_send_pcie_uevent(&priv->dev->kobj, true);
-+ aspeed_mctp_irq_enable(priv);
-+ }
-+}
-+
-+static void aspeed_mctp_channels_init(struct aspeed_mctp *priv)
-+{
-+ aspeed_mctp_rx_chan_init(&priv->rx);
-+ aspeed_mctp_tx_chan_init(&priv->tx);
-+}
-+
-+static irqreturn_t aspeed_mctp_irq_handler(int irq, void *arg)
-+{
-+ struct aspeed_mctp *priv = arg;
-+ u32 handled = 0;
-+ u32 status;
-+
-+ regmap_read(priv->map, ASPEED_MCTP_INT_STS, &status);
-+ regmap_write(priv->map, ASPEED_MCTP_INT_STS, status);
-+
-+ if (status & TX_CMD_LAST_INT) {
-+ u32 rd_ptr;
-+
-+ regmap_write(priv->map, ASPEED_MCTP_TX_BUF_RD_PTR,
-+ UPDATE_RX_RD_PTR);
-+ regmap_read(priv->map, ASPEED_MCTP_TX_BUF_RD_PTR, &rd_ptr);
-+ rd_ptr &= TX_BUFFER_RD_PTR;
-+
-+ /*
-+ * rd_ptr on TX side seems to be busted...
-+ * Since we're always reading zeroes, let's trust that when
-+ * we're getting LAST_CMD irq, everything we previously
-+ * submitted was transmitted and start from 0
-+ */
-+ WRITE_ONCE(priv->tx.rd_ptr, TX_CMD_COUNT);
-+
-+ tasklet_hi_schedule(&priv->tx.tasklet);
-+
-+ wake_up_all(&priv->wait_queue);
-+
-+ handled |= TX_CMD_LAST_INT;
-+ }
-+
-+ if (status & TX_CMD_WRONG_INT) {
-+ /* TODO: print the actual command */
-+ dev_warn(priv->dev, "TX wrong");
-+
-+ handled |= TX_CMD_WRONG_INT;
-+ }
-+
-+ if (status & RX_CMD_RECEIVE_INT) {
-+ u32 rd_ptr;
-+
-+ regmap_write(priv->map, ASPEED_MCTP_RX_BUF_RD_PTR,
-+ UPDATE_RX_RD_PTR);
-+ regmap_read(priv->map, ASPEED_MCTP_RX_BUF_RD_PTR, &rd_ptr);
-+ rd_ptr &= RX_BUFFER_RD_PTR;
-+
-+ WRITE_ONCE(priv->rx.rd_ptr, rd_ptr > 0 ? rd_ptr : RX_CMD_COUNT);
-+
-+ tasklet_hi_schedule(&priv->rx.tasklet);
-+
-+ handled |= RX_CMD_RECEIVE_INT;
-+ }
-+
-+ if (!handled)
-+ return IRQ_NONE;
-+
-+ return IRQ_HANDLED;
-+}
-+
-+static irqreturn_t aspeed_mctp_pcie_rst_irq_handler(int irq, void *arg)
-+{
-+ struct aspeed_mctp *priv = arg;
-+
-+ aspeed_mctp_channels_init(priv);
-+
-+ priv->pcie.need_uevent = true;
-+ priv->pcie.bdf = 0;
-+
-+ schedule_delayed_work(&priv->pcie.rst_dwork, 0);
-+
-+ return IRQ_HANDLED;
-+}
-+
-+static void aspeed_mctp_drv_init(struct aspeed_mctp *priv)
-+{
-+ init_waitqueue_head(&priv->wait_queue);
-+ INIT_LIST_HEAD(&priv->clients);
-+
-+ spin_lock_init(&priv->clients_lock);
-+
-+ INIT_DELAYED_WORK(&priv->pcie.rst_dwork, aspeed_mctp_reset_work);
-+
-+ tasklet_init(&priv->tx.tasklet, aspeed_mctp_tx_tasklet,
-+ (unsigned long)&priv->tx);
-+ tasklet_init(&priv->rx.tasklet, aspeed_mctp_rx_tasklet,
-+ (unsigned long)&priv->rx);
-+}
-+
-+static void aspeed_mctp_drv_fini(struct aspeed_mctp *priv)
-+{
-+ tasklet_disable(&priv->tx.tasklet);
-+ tasklet_kill(&priv->tx.tasklet);
-+ tasklet_disable(&priv->rx.tasklet);
-+ tasklet_kill(&priv->rx.tasklet);
-+
-+ cancel_delayed_work_sync(&priv->pcie.rst_dwork);
-+}
-+
-+static int aspeed_mctp_resources_init(struct aspeed_mctp *priv)
-+{
-+ struct platform_device *pdev = to_platform_device(priv->dev);
-+ void __iomem *regs;
-+
-+ regs = devm_platform_ioremap_resource(pdev, 0);
-+ if (IS_ERR(regs)) {
-+ dev_err(priv->dev, "Failed to get regmap!\n");
-+ return PTR_ERR(regs);
-+ }
-+
-+ priv->map = devm_regmap_init_mmio(priv->dev, regs,
-+ &aspeed_mctp_regmap_cfg);
-+ if (IS_ERR(priv->map))
-+ return PTR_ERR(priv->map);
-+
-+ priv->reset = devm_reset_control_get(priv->dev, 0);
-+ if (IS_ERR(priv->reset)) {
-+ dev_err(priv->dev, "Failed to get reset!\n");
-+ return PTR_ERR(priv->reset);
-+ }
-+
-+ priv->pcie.map =
-+ syscon_regmap_lookup_by_phandle(priv->dev->of_node,
-+ "aspeed,pcieh");
-+ if (IS_ERR(priv->pcie.map)) {
-+ dev_err(priv->dev, "Failed to find PCIe Host regmap!\n");
-+ return PTR_ERR(priv->pcie.map);
-+ }
-+
-+ platform_set_drvdata(pdev, priv);
-+
-+ return 0;
-+}
-+
-+static int aspeed_mctp_dma_init(struct aspeed_mctp *priv)
-+{
-+ struct mctp_channel *tx = &priv->tx;
-+ struct mctp_channel *rx = &priv->rx;
-+ int ret = -ENOMEM;
-+
-+ BUILD_BUG_ON(TX_CMD_COUNT >= TX_MAX_CMD_COUNT);
-+ BUILD_BUG_ON(RX_CMD_COUNT >= RX_MAX_CMD_COUNT);
-+
-+ tx->cmd.vaddr = dma_alloc_coherent(priv->dev, TX_CMD_BUF_SIZE,
-+ &tx->cmd.dma_handle, GFP_KERNEL);
-+
-+ if (!tx->cmd.vaddr)
-+ return ret;
-+
-+ tx->data.vaddr = dma_alloc_coherent(priv->dev, TX_DATA_BUF_SIZE,
-+ &tx->data.dma_handle, GFP_KERNEL);
-+
-+ if (!tx->data.vaddr)
-+ goto out_tx_data;
-+
-+ rx->cmd.vaddr = dma_alloc_coherent(priv->dev, RX_CMD_BUF_SIZE,
-+ &rx->cmd.dma_handle, GFP_KERNEL);
-+
-+ if (!rx->cmd.vaddr)
-+ goto out_tx_cmd;
-+
-+ rx->data.vaddr = dma_alloc_coherent(priv->dev, RX_DATA_BUF_SIZE,
-+ &rx->data.dma_handle, GFP_KERNEL);
-+
-+ if (!rx->data.vaddr)
-+ goto out_rx_data;
-+
-+ return 0;
-+out_rx_data:
-+ dma_free_coherent(priv->dev, RX_CMD_BUF_SIZE, rx->cmd.vaddr,
-+ rx->cmd.dma_handle);
-+
-+out_tx_cmd:
-+ dma_free_coherent(priv->dev, TX_DATA_BUF_SIZE, tx->data.vaddr,
-+ tx->data.dma_handle);
-+
-+out_tx_data:
-+ dma_free_coherent(priv->dev, TX_CMD_BUF_SIZE, tx->cmd.vaddr,
-+ tx->cmd.dma_handle);
-+ return ret;
-+}
-+
-+static void aspeed_mctp_dma_fini(struct aspeed_mctp *priv)
-+{
-+ struct mctp_channel *tx = &priv->tx;
-+ struct mctp_channel *rx = &priv->rx;
-+
-+ dma_free_coherent(priv->dev, TX_CMD_BUF_SIZE, tx->cmd.vaddr,
-+ tx->cmd.dma_handle);
-+
-+ dma_free_coherent(priv->dev, RX_CMD_BUF_SIZE, rx->cmd.vaddr,
-+ rx->cmd.dma_handle);
-+
-+ dma_free_coherent(priv->dev, TX_DATA_BUF_SIZE, tx->data.vaddr,
-+ tx->data.dma_handle);
-+
-+ dma_free_coherent(priv->dev, RX_DATA_BUF_SIZE, rx->data.vaddr,
-+ rx->data.dma_handle);
-+}
-+
-+static int aspeed_mctp_irq_init(struct aspeed_mctp *priv)
-+{
-+ struct platform_device *pdev = to_platform_device(priv->dev);
-+ int irq, ret;
-+
-+ irq = platform_get_irq(pdev, 0);
-+ if (!irq)
-+ return -ENODEV;
-+
-+ ret = devm_request_irq(priv->dev, irq, aspeed_mctp_irq_handler,
-+ IRQF_SHARED, "aspeed-mctp", priv);
-+ if (ret)
-+ return ret;
-+
-+ irq = platform_get_irq(pdev, 1);
-+ if (!irq)
-+ return -ENODEV;
-+
-+ ret = devm_request_irq(priv->dev, irq,
-+ aspeed_mctp_pcie_rst_irq_handler,
-+ IRQF_SHARED, "aspeed-mctp", priv);
-+ if (ret)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static void aspeed_mctp_hw_reset(struct aspeed_mctp *priv)
-+{
-+ if (reset_control_assert(priv->reset) != 0)
-+ dev_warn(priv->dev, "Failed to assert reset\n");
-+
-+ if (reset_control_deassert(priv->reset) != 0)
-+ dev_warn(priv->dev, "Failed to deassert reset\n");
-+}
-+
-+static int aspeed_mctp_probe(struct platform_device *pdev)
-+{
-+ struct aspeed_mctp *priv;
-+ int ret;
-+
-+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
-+ if (!priv) {
-+ ret = -ENOMEM;
-+ goto out;
-+ }
-+ priv->dev = &pdev->dev;
-+
-+ aspeed_mctp_drv_init(priv);
-+
-+ ret = aspeed_mctp_resources_init(priv);
-+ if (ret) {
-+ dev_err(priv->dev, "Failed to init resources\n");
-+ goto out_drv;
-+ }
-+
-+ ret = aspeed_mctp_dma_init(priv);
-+ if (ret) {
-+ dev_err(priv->dev, "Failed to init DMA\n");
-+ goto out_drv;
-+ }
-+
-+ /*
-+ * FIXME: We need to verify how to make the reset work when we probe
-+ * multiple times. Currently calling reset more than once seems to make
-+ * the HW upset, however, we do need to reset after boot before we're
-+ * able to use the HW.
-+ */
-+ aspeed_mctp_hw_reset(priv);
-+
-+ aspeed_mctp_channels_init(priv);
-+
-+ aspeed_mctp_miscdev.parent = priv->dev;
-+ ret = misc_register(&aspeed_mctp_miscdev);
-+ if (ret) {
-+ dev_err(priv->dev, "Failed to register miscdev\n");
-+ goto out_dma;
-+ }
-+ aspeed_mctp_miscdev.this_device->type = &aspeed_mctp_type;
-+
-+ ret = aspeed_mctp_irq_init(priv);
-+ if (ret) {
-+ dev_err(priv->dev, "Failed to init IRQ!\n");
-+ goto out_dma;
-+ }
-+
-+ aspeed_mctp_irq_enable(priv);
-+
-+ aspeed_mctp_pcie_setup(priv);
-+
-+ return 0;
-+
-+out_dma:
-+ aspeed_mctp_dma_fini(priv);
-+out_drv:
-+ aspeed_mctp_drv_fini(priv);
-+out:
-+ dev_err(priv->dev, "Failed to probe Aspeed MCTP: %d\n", ret);
-+ return ret;
-+}
-+
-+static int aspeed_mctp_remove(struct platform_device *pdev)
-+{
-+ struct aspeed_mctp *priv = platform_get_drvdata(pdev);
-+
-+ misc_deregister(&aspeed_mctp_miscdev);
-+
-+ aspeed_mctp_irq_disable(priv);
-+
-+ aspeed_mctp_dma_fini(priv);
-+
-+ aspeed_mctp_drv_fini(priv);
-+
-+ return 0;
-+}
-+
-+static const struct of_device_id aspeed_mctp_match_table[] = {
-+ { .compatible = "aspeed,ast2600-mctp" },
-+ { }
-+};
-+
-+static struct platform_driver aspeed_mctp_driver = {
-+ .driver = {
-+ .name = "aspeed-mctp",
-+ .of_match_table = of_match_ptr(aspeed_mctp_match_table),
-+ },
-+ .probe = aspeed_mctp_probe,
-+ .remove = aspeed_mctp_remove,
-+};
-+
-+static int __init aspeed_mctp_init(void)
-+{
-+ packet_cache =
-+ kmem_cache_create_usercopy("mctp-packet",
-+ sizeof(struct mctp_pcie_packet),
-+ 0, 0, 0,
-+ sizeof(struct mctp_pcie_packet),
-+ NULL);
-+ if (!packet_cache)
-+ return -ENOMEM;
-+
-+ return platform_driver_register(&aspeed_mctp_driver);
-+}
-+
-+static void __exit aspeed_mctp_exit(void)
-+{
-+ platform_driver_unregister(&aspeed_mctp_driver);
-+ kmem_cache_destroy(packet_cache);
-+}
-+
-+module_init(aspeed_mctp_init)
-+module_exit(aspeed_mctp_exit)
-+
-+MODULE_DEVICE_TABLE(of, aspeed_mctp_match_table);
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>");
-+MODULE_DESCRIPTION("Aspeed AST2600 MCTP driver");
-diff --git a/include/uapi/linux/aspeed-mctp.h b/include/uapi/linux/aspeed-mctp.h
-new file mode 100644
-index 000000000000..e37fa3afde9d
---- /dev/null
-+++ b/include/uapi/linux/aspeed-mctp.h
-@@ -0,0 +1,72 @@
-+/* SPDX-License-Identifier: GPL-2.0 */
-+/* Copyright (c) 2020 Intel Corporation */
-+
-+#ifndef _UAPI_LINUX_ASPEED_MCTP_H
-+#define _UAPI_LINUX_ASPEED_MCTP_H
-+
-+#include <linux/ioctl.h>
-+#include <linux/types.h>
-+
-+/*
-+ * aspeed-mctp is a simple device driver exposing a read/write interface:
-+ * +----------------------+
-+ * | PCIe VDM Header | 16 bytes (Big Endian)
-+ * +----------------------+
-+ * | MCTP Message Payload | 64/128/256/512 bytes (Little Endian)
-+ * +----------------------+
-+ *
-+ * MCTP packet description can be found in DMTF DSP0238,
-+ * MCTP PCIe VDM Transport Specification.
-+ *
-+ * Note: AST2600 hardware follows MCTP Message Payload rules from earlier
-+ * hardware and we've decided to copy the packets directly, without swapping.
-+ * This means that it's userspace responsibility to provide the data in
-+ * a format that the hardware expects.
-+ */
-+
-+#define ASPEED_MCTP_PCIE_VDM_HDR_SIZE 16
-+
-+/*
-+ * uevents generated by aspeed-mctp driver
-+ */
-+#define ASPEED_MCTP_READY "PCIE_READY"
-+
-+/*
-+ * MCTP operations
-+ * @ASPEED_MCTP_IOCTL_FILTER_EID: enable/disable filter incoming packets based
-+ * on Endpoint ID (BROKEN)
-+ * @ASPEED_MCTP_IOCTL_GET_BDF: read PCI bus/device/function of MCTP Controller
-+ * @ASPEED_MCTP_IOCTL_GET_MEDIUM_ID: read MCTP physical medium identifier
-+ * related to PCIe revision
-+ * @ASPEED_MCTP_IOCTL_GET_MTU: read max transmission unit (in bytes)
-+ */
-+
-+struct aspeed_mctp_filter_eid {
-+ __u8 eid;
-+ bool enable;
-+};
-+
-+struct aspeed_mctp_get_bdf {
-+ __u16 bdf;
-+};
-+
-+struct aspeed_mctp_get_medium_id {
-+ __u8 medium_id;
-+};
-+
-+struct aspeed_mctp_get_mtu {
-+ __u8 mtu;
-+};
-+
-+#define ASPEED_MCTP_IOCTL_BASE 0x4d
-+
-+#define ASPEED_MCTP_IOCTL_FILTER_EID \
-+ _IOW(ASPEED_MCTP_IOCTL_BASE, 0, struct aspeed_mctp_filter_eid)
-+#define ASPEED_MCTP_IOCTL_GET_BDF \
-+ _IOR(ASPEED_MCTP_IOCTL_BASE, 1, struct aspeed_mctp_get_bdf)
-+#define ASPEED_MCTP_IOCTL_GET_MEDIUM_ID \
-+ _IOR(ASPEED_MCTP_IOCTL_BASE, 2, struct aspeed_mctp_get_medium_id)
-+#define ASPEED_MCTP_IOCTL_GET_MTU \
-+ _IOR(ASPEED_MCTP_IOCTL_BASE, 3, struct aspeed_mctp_get_mtu)
-+
-+#endif /* _UAPI_LINUX_ASPEED_MCTP_H */
---
-2.21.1
-