diff options
author | Jason M. Bills <jason.m.bills@linux.intel.com> | 2020-06-15 21:00:52 +0300 |
---|---|---|
committer | Jason M. Bills <jason.m.bills@linux.intel.com> | 2020-06-15 21:34:52 +0300 |
commit | 057594a8bb8d062d72244196170c2d78947d318a (patch) | |
tree | bd0ce34dac9b401ce5ac6919f1cd85239c526b82 /meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0108-soc-aspeed-mctp-Add-initial-driver-for-ast2600-mctp.patch | |
parent | 0ccb93d91de41b7db333efccc1aa8d03db1ef63f (diff) | |
download | openbmc-057594a8bb8d062d72244196170c2d78947d318a.tar.xz |
Update to internal 0.59
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, 1265 insertions, 0 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 new file mode 100644 index 000000000..bff271bfe --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0108-soc-aspeed-mctp-Add-initial-driver-for-ast2600-mctp.patch @@ -0,0 +1,1265 @@ +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 + |