diff options
author | Iwona Winiarska <iwona.winiarska@intel.com> | 2020-12-04 03:16:27 +0300 |
---|---|---|
committer | Iwona Winiarska <iwona.winiarska@intel.com> | 2020-12-17 12:19:12 +0300 |
commit | e25f94a28a950297018b3f4caebba41a60a46d61 (patch) | |
tree | 23224182b3a80679d87e6ca6a2f4affe0362503f | |
parent | fca274838c6d6292851f327418cace93616b5ee0 (diff) | |
download | linux-e25f94a28a950297018b3f4caebba41a60a46d61.tar.xz |
soc: aspeed: mctp: Expose internal kernel API
Some protocols that are already implemented in kernel can be
encapsulated in MCTP packets. To allow use aspeed-mctp internally in
kernel space, let's allow to use selected functions outside of
aspeed-mctp.
Change-Id: I543bfce975dfdd7cf8017899a139f69febbaca8d
Signed-off-by: Iwona Winiarska <iwona.winiarska@intel.com>
-rw-r--r-- | drivers/soc/aspeed/aspeed-mctp.c | 101 | ||||
-rw-r--r-- | include/linux/aspeed-mctp.h | 117 |
2 files changed, 179 insertions, 39 deletions
diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c index 2f2e15a5258c..85aed4db13e7 100644 --- a/drivers/soc/aspeed/aspeed-mctp.c +++ b/drivers/soc/aspeed/aspeed-mctp.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2020, Intel Corporation. +#include <linux/aspeed-mctp.h> #include <linux/bitfield.h> #include <linux/dma-mapping.h> #include <linux/interrupt.h> @@ -142,20 +143,6 @@ #define PCIE_VDM_HDR_REQUESTER_BDF_DW 1 #define PCIE_VDM_HDR_REQUESTER_BDF_MASK GENMASK(31, 16) -#define PCIE_VDM_HDR_SIZE_DW (ASPEED_MCTP_PCIE_VDM_HDR_SIZE / 4) -#define PCIE_VDM_DATA_SIZE_DW (ASPEED_MCTP_MTU / 4) - -#define PCIE_MCTP_MIN_PACKET_SIZE (ASPEED_MCTP_PCIE_VDM_HDR_SIZE + 4) - -struct mctp_pcie_packet_data { - u32 hdr[PCIE_VDM_HDR_SIZE_DW]; - u32 payload[PCIE_VDM_DATA_SIZE_DW]; -}; - -struct mctp_pcie_packet { - struct mctp_pcie_packet_data data; - u32 size; -}; struct aspeed_mctp_tx_cmd { u32 tx_lo; @@ -234,15 +221,17 @@ struct aspeed_mctp_endpoint { struct kmem_cache *packet_cache; -static void *packet_alloc(gfp_t flags) +void *aspeed_mctp_packet_alloc(gfp_t flags) { return kmem_cache_alloc(packet_cache, flags); } +EXPORT_SYMBOL_GPL(aspeed_mctp_packet_alloc); -static void packet_free(void *packet) +void aspeed_mctp_packet_free(void *packet) { kmem_cache_free(packet_cache, packet); } +EXPORT_SYMBOL_GPL(aspeed_mctp_packet_free); /* * HW produces and expects VDM header in little endian and payload in network order. @@ -336,8 +325,8 @@ 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); + ptr_ring_cleanup(&client->rx_queue, &aspeed_mctp_packet_free); + ptr_ring_cleanup(&client->tx_queue, &aspeed_mctp_packet_free); kfree(client); } @@ -416,14 +405,14 @@ static void aspeed_mctp_dispatch_packet(struct aspeed_mctp *priv, ret = ptr_ring_produce(&client->rx_queue, packet); if (ret) { dev_warn(priv->dev, "Failed to produce RX packet\n"); - packet_free(packet); + aspeed_mctp_packet_free(packet); } else { wake_up_all(&client->wait_queue); } aspeed_mctp_client_put(client); } else { dev_dbg(priv->dev, "Failed to dispatch RX packet\n"); - packet_free(packet); + aspeed_mctp_packet_free(packet); } } @@ -457,7 +446,7 @@ static void aspeed_mctp_tx_tasklet(unsigned long data) ptr_ring_consume(&client->tx_queue); aspeed_mctp_emit_tx_cmd(tx, packet); - packet_free(packet); + aspeed_mctp_packet_free(packet); trigger = true; } } @@ -485,7 +474,7 @@ static void aspeed_mctp_rx_tasklet(unsigned long data) hdr = (u32 *)&rx_buf[rx->wr_ptr]; while (*hdr != 0) { - rx_packet = packet_alloc(GFP_ATOMIC); + rx_packet = aspeed_mctp_packet_alloc(GFP_ATOMIC); if (!rx_packet) { dev_err(priv->dev, "Failed to allocate RX packet\n"); break; @@ -538,7 +527,7 @@ static void aspeed_mctp_tx_chan_init(struct mctp_channel *tx) regmap_write(priv->map, ASPEED_MCTP_TX_BUF_WR_PTR, 0); } -static struct mctp_client *aspeed_mctp_create_client(struct aspeed_mctp *priv) +struct mctp_client *aspeed_mctp_create_client(struct aspeed_mctp *priv) { struct mctp_client *client; @@ -564,6 +553,7 @@ static struct mctp_client *aspeed_mctp_create_client(struct aspeed_mctp *priv) return client; } +EXPORT_SYMBOL_GPL(aspeed_mctp_create_client); static int aspeed_mctp_open(struct inode *inode, struct file *file) { @@ -588,7 +578,7 @@ static int aspeed_mctp_open(struct inode *inode, struct file *file) return 0; } -static void aspeed_mctp_delete_client(struct mctp_client *client) +void aspeed_mctp_delete_client(struct mctp_client *client) { struct aspeed_mctp *priv = client->priv; struct mctp_type_handler *handler, *tmp; @@ -614,6 +604,7 @@ static void aspeed_mctp_delete_client(struct mctp_client *client) aspeed_mctp_client_put(client); local_bh_enable(); } +EXPORT_SYMBOL_GPL(aspeed_mctp_delete_client); static int aspeed_mctp_release(struct inode *inode, struct file *file) { @@ -624,6 +615,45 @@ static int aspeed_mctp_release(struct inode *inode, struct file *file) return 0; } +int aspeed_mctp_write_packet(struct mctp_client *client, + struct mctp_pcie_packet *tx_packet) +{ + struct aspeed_mctp *priv = client->priv; + int ret; + + if (priv->pcie.bdf == 0) + return -EIO; + + be32p_replace_bits(&tx_packet->data.hdr[PCIE_VDM_HDR_REQUESTER_BDF_DW], + priv->pcie.bdf, PCIE_VDM_HDR_REQUESTER_BDF_MASK); + + ret = ptr_ring_produce_bh(&client->tx_queue, tx_packet); + if (!ret) + tasklet_hi_schedule(&priv->tx.tasklet); + + return ret; +} +EXPORT_SYMBOL_GPL(aspeed_mctp_write_packet); + +struct mctp_pcie_packet *aspeed_mctp_read_packet(struct mctp_client *client, + unsigned long timeout) +{ + struct aspeed_mctp *priv = client->priv; + int ret; + + if (priv->pcie.bdf == 0) + return ERR_PTR(-EIO); + + ret = wait_event_interruptible_timeout(client->wait_queue, + __ptr_ring_peek(&client->rx_queue), + timeout); + if (ret == 0) + return ERR_PTR(-EAGAIN); + + return ptr_ring_consume_bh(&client->rx_queue); +} +EXPORT_SYMBOL_GPL(aspeed_mctp_read_packet); + static ssize_t aspeed_mctp_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -649,7 +679,7 @@ static ssize_t aspeed_mctp_read(struct file *file, char __user *buf, count = -EFAULT; } - packet_free(rx_packet); + aspeed_mctp_packet_free(rx_packet); return count; } @@ -668,10 +698,7 @@ static ssize_t aspeed_mctp_write(struct file *file, const char __user *buf, if (count > sizeof(tx_packet->data)) return -ENOSPC; - if (priv->pcie.bdf == 0) - return -EIO; - - tx_packet = packet_alloc(GFP_KERNEL); + tx_packet = aspeed_mctp_packet_alloc(GFP_KERNEL); if (!tx_packet) { ret = -ENOMEM; goto out; @@ -682,28 +709,23 @@ static ssize_t aspeed_mctp_write(struct file *file, const char __user *buf, ret = -EFAULT; goto out_packet; } - tx_packet->size = count; - be32p_replace_bits(&tx_packet->data.hdr[PCIE_VDM_HDR_REQUESTER_BDF_DW], - priv->pcie.bdf, PCIE_VDM_HDR_REQUESTER_BDF_MASK); + tx_packet->size = count; - ret = ptr_ring_produce_bh(&client->tx_queue, tx_packet); + ret = aspeed_mctp_write_packet(client, tx_packet); if (ret) goto out_packet; - tasklet_hi_schedule(&priv->tx.tasklet); - return count; out_packet: - packet_free(tx_packet); + aspeed_mctp_packet_free(tx_packet); out: return ret; } -int aspeed_mctp_add_type_handler(struct mctp_client *client, - u8 mctp_type, u16 pci_vendor_id, - u16 vdm_type, u16 vdm_mask) +int aspeed_mctp_add_type_handler(struct mctp_client *client, u8 mctp_type, + u16 pci_vendor_id, u16 vdm_type, u16 vdm_mask) { struct aspeed_mctp *priv = client->priv; struct mctp_type_handler *handler, *new_handler; @@ -747,6 +769,7 @@ out_unlock: return ret; } +EXPORT_SYMBOL_GPL(aspeed_mctp_add_type_handler); int aspeed_mctp_remove_type_handler(struct mctp_client *client, u8 mctp_type, u16 pci_vendor_id, diff --git a/include/linux/aspeed-mctp.h b/include/linux/aspeed-mctp.h new file mode 100644 index 000000000000..3cfa8ca927ca --- /dev/null +++ b/include/linux/aspeed-mctp.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2020 Intel Corporation */ + +#ifndef __LINUX_ASPEED_MCTP_H +#define __LINUX_ASPEED_MCTP_H + +#include <linux/types.h> + +struct mctp_client; +struct aspeed_mctp; + +struct pcie_transport_hdr { + u8 fmt_type; + u8 mbz; + u8 mbz_attr_len_hi; + u8 len_lo; + u16 requester; + u8 tag; + u8 code; + u16 target; + u16 vendor; +} __packed; + +struct mctp_protocol_hdr { + u8 ver; + u8 dest; + u8 src; + u8 flags_seq_tag; +} __packed; + +#define PCIE_VDM_HDR_SIZE 16 +#define MCTP_BTU_SIZE 64 +#define PCIE_VDM_DATA_SIZE_DW (MCTP_BTU_SIZE / 4) +#define PCIE_VDM_HDR_SIZE_DW (PCIE_VDM_HDR_SIZE / 4) + +#define PCIE_MCTP_MIN_PACKET_SIZE (PCIE_VDM_HDR_SIZE + 4) + +struct mctp_pcie_packet_data { + u32 hdr[PCIE_VDM_HDR_SIZE_DW]; + u32 payload[PCIE_VDM_DATA_SIZE_DW]; +}; + +struct mctp_pcie_packet { + struct mctp_pcie_packet_data data; + u32 size; +}; + +/** + * aspeed_mctp_add_type_handler() - register for the given MCTP message type + * @client: pointer to the existing mctp_client context + * @mctp_type: message type code according to DMTF DSP0239 spec. + * @pci_vendor_id: vendor ID (non-zero if msg_type is Vendor Defined PCI, + * otherwise it should be set to 0) + * @vdm_type: vendor defined message type (it should be set to 0 for non-Vendor + * Defined PCI message type) + * @vdm_mask: vendor defined message mask (it should be set to 0 for non-Vendor + * Defined PCI message type) + * + * Return: + * * 0 - success, + * * -EINVAL - arguments passed are incorrect, + * * -ENOMEM - cannot alloc a new handler, + * * -EBUSY - given message has already registered handler. + */ + +int aspeed_mctp_add_type_handler(struct mctp_client *client, u8 mctp_type, + u16 pci_vendor_id, u16 vdm_type, u16 vdm_mask); + +/** + * aspeed_mctp_create_client() - create mctp_client context + * @priv pointer to aspeed-mctp context + * + * Returns struct mctp_client or NULL. + */ +struct mctp_client *aspeed_mctp_create_client(struct aspeed_mctp *priv); + +/** + * aspeed_mctp_delete_client()- delete mctp_client context + * @client: pointer to existing mctp_client context + */ +void aspeed_mctp_delete_client(struct mctp_client *client); + +/** + * aspeed_mctp_write_packet() - send mctp_packet + * @client: pointer to existing mctp_client context + * @tx_packet: the allocated packet that needs to be send via aspeed-mctp + * + * After the function returns success, the packet is no longer owned by the + * caller, and as such, the caller should not attempt to free it. + * + * Return: + * * 0 - success, + * * -ENOSPC - failed to send packet due to lack of available space. + */ +int aspeed_mctp_write_packet(struct mctp_client *client, + struct mctp_pcie_packet *tx_packet); + +/** + * aspeed_mctp_read_packet() - receive mctp_packet + * @client: pointer to existing mctp_client context + * @timeout: timeout, in jiffies + * + * The function will sleep for up to @timeout if no packet is ready to read. + * + * After the function returns valid packet, the caller takes its ownership and + * is responsible for freeing it. + * + * Returns struct mctp_pcie_packet from or ERR_PTR in case of error or the + * @timeout elapsed. + */ +struct mctp_pcie_packet *aspeed_mctp_read_packet(struct mctp_client *client, + unsigned long timeout); + +void *aspeed_mctp_packet_alloc(gfp_t flags); +void aspeed_mctp_packet_free(void *packet); + +#endif /* __LINUX_ASPEED_MCTP_H */ |