diff options
author | Jason M. Bills <jason.m.bills@linux.intel.com> | 2020-12-08 00:45:20 +0300 |
---|---|---|
committer | Jason M. Bills <jason.m.bills@linux.intel.com> | 2020-12-10 01:15:05 +0300 |
commit | 82dbc15a05125a812c140a3c8cff81c366482229 (patch) | |
tree | 9c8f1ad262a2e281f20340cf8646aca6f8596044 /meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0108-soc-aspeed-mctp-Add-initial-driver-for-ast2600-mctp.patch | |
parent | 8d6ae7f2a817751fad151168fa10ce28ee0869d8 (diff) | |
download | openbmc-82dbc15a05125a812c140a3c8cff81c366482229.tar.xz |
Update to internal 0.26
Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
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.patch | 1265 |
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, ®); -+ -+ 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 - |