From f097c09e94e4d3d63ccb7d9a670e9ffe095807a2 Mon Sep 17 00:00:00 2001 From: Oleksandr Shulzhenko Date: Fri, 20 May 2022 14:53:59 +0200 Subject: peci: busses: add PECI o. MCTP o. I3C platform driver Add PECI adapter that allows to send MCTP PCI VDM packets via i3c-mctp driver. Signed-off-by: Oleksandr Shulzhenko --- drivers/peci/busses/Kconfig | 13 +++ drivers/peci/busses/Makefile | 1 + drivers/peci/busses/peci-i3c.c | 254 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 drivers/peci/busses/peci-i3c.c diff --git a/drivers/peci/busses/Kconfig b/drivers/peci/busses/Kconfig index 20a1a7472d96..8e6a602c879d 100644 --- a/drivers/peci/busses/Kconfig +++ b/drivers/peci/busses/Kconfig @@ -44,4 +44,17 @@ config PECI_MCTP This support is also available as a module. If so, the module will be called peci-mctp. +config PECI_I3C + tristate "PECI over MCTP over I3C support" + depends on I3C + depends on PECI + depends on I3C_MCTP + + help + Say Y here if you want support for the Platform Environment Control + Interface (PECI) over MCTP over I3C bus adapter driver. + + This support is also available as a module. If so, the module + will be called peci-i3c + endmenu diff --git a/drivers/peci/busses/Makefile b/drivers/peci/busses/Makefile index ebf3fda9bedc..948578ff6bd1 100644 --- a/drivers/peci/busses/Makefile +++ b/drivers/peci/busses/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_PECI_ASPEED) += peci-aspeed.o obj-$(CONFIG_PECI_NPCM) += peci-npcm.o obj-$(CONFIG_PECI_MCTP) += peci-mctp.o +obj-$(CONFIG_PECI_I3C) += peci-i3c.o diff --git a/drivers/peci/busses/peci-i3c.c b/drivers/peci/busses/peci-i3c.c new file mode 100644 index 000000000000..aaf427ffe35a --- /dev/null +++ b/drivers/peci/busses/peci-i3c.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2022 Intel Corporation.*/ + +#include +#include +#include +#include +#include + +#include + +#include + +#define MSG_TAG_MASK GENMASK(2, 0) +#define MCTP_SET_MSG_TAG(x, val) ((x)->flags_seq_tag |= ((val) & MSG_TAG_MASK)) +#define MCTP_GET_MSG_TAG(x) ((x)->flags_seq_tag & MSG_TAG_MASK) +#define MCTP_HDR_VERSION 1 +#define REQUEST_FLAGS 0xc8 +#define RESPONSE_FLAGS 0xc0 +#define PECI_REQUEST 0x80 +#define PECI_RESPONSE 0 +#define PECI_PAYLOAD_SIZE 59 + +#define I3C_PECI_MCTP_TIMEOUT_VALUE_MS 800 + +struct mctp_peci_vdm_hdr { + u8 type; + __be16 vendor_id; + u8 instance_req_d; + u8 vendor_code; +} __packed; + +static const struct mctp_protocol_hdr mctp_protocol_hdr_template = { + .ver = MCTP_HDR_VERSION, + .flags_seq_tag = REQUEST_FLAGS +}; + +static const struct mctp_peci_vdm_hdr mctp_peci_vdm_hdr_template = { + .type = MCTP_MSG_TYPE_VDM_PCI, + .instance_req_d = PECI_REQUEST, + .vendor_code = MCTP_VDM_PCI_INTEL_PECI +}; + +struct i3c_peci { + struct peci_adapter *adapter; + struct device *dev; + struct i3c_device *i3cdev; + struct i3c_mctp_client *client; + u8 tag; +}; + +static void +prepare_tx_packet(struct i3c_mctp_packet *tx_packet, + u8 tx_len, u8 rx_len, u8 *tx_buf, u8 dest, u8 tag) +{ + struct mctp_protocol_hdr *mctp_protocol_hdr; + struct mctp_peci_vdm_hdr *mctp_peci_vdm_hdr; + u8 *peci_payload; + + mctp_protocol_hdr = (struct mctp_protocol_hdr *)&tx_packet->data.protocol_hdr; + *mctp_protocol_hdr = mctp_protocol_hdr_template; + mctp_protocol_hdr->dest = dest; + MCTP_SET_MSG_TAG(mctp_protocol_hdr, tag); + + mctp_peci_vdm_hdr = (struct mctp_peci_vdm_hdr *)&tx_packet->data.payload; + *mctp_peci_vdm_hdr = mctp_peci_vdm_hdr_template; + mctp_peci_vdm_hdr->vendor_id = cpu_to_be16(MCTP_VDM_PCI_INTEL_VENDOR_ID); + + peci_payload = (u8 *)(tx_packet->data.payload) + sizeof(struct mctp_peci_vdm_hdr); + + peci_payload[0] = tx_len; + peci_payload[1] = rx_len; + memcpy(&peci_payload[2], tx_buf, tx_len); + + tx_packet->size = I3C_MCTP_PACKET_SIZE; +} + +static int +verify_rx_packet(struct peci_adapter *adapter, struct i3c_mctp_packet *rx_packet, u8 tag) +{ + struct i3c_peci *priv = peci_get_adapdata(adapter); + bool invalid_packet = false; + struct mctp_protocol_hdr *mctp_protocol_hdr; + struct mctp_peci_vdm_hdr *mctp_message_hdr; + u8 expected_flags; + + expected_flags = (RESPONSE_FLAGS | (tag & MSG_TAG_MASK)); + + mctp_protocol_hdr = (struct mctp_protocol_hdr *)&rx_packet->data.protocol_hdr; + mctp_message_hdr = (struct mctp_peci_vdm_hdr *)&rx_packet->data.payload; + + if (mctp_protocol_hdr->flags_seq_tag != expected_flags) { + dev_dbg(priv->dev, + "mismatch in mctp flags: expected: 0x%.2x, got: 0x%.2x", + expected_flags, mctp_protocol_hdr->flags_seq_tag); + invalid_packet = true; + } + + if (mctp_message_hdr->instance_req_d != PECI_RESPONSE) { + dev_dbg(priv->dev, + "mismatch in PECI response code: expected: 0x%.2x, got: 0x%.2x", + PECI_RESPONSE, mctp_message_hdr->instance_req_d); + invalid_packet = true; + } + + if (invalid_packet) { + dev_warn_ratelimited(priv->dev, "unexpected peci response found\n"); + return -EIO; + } + + return 0; +} + +static struct i3c_mctp_packet *i3c_peci_send_receive(struct peci_adapter *adapter, + struct i3c_device *i3cdev, + u8 tx_len, u8 rx_len, u8 *tx_buf, u8 dest_eid) +{ + unsigned long timeout = msecs_to_jiffies(I3C_PECI_MCTP_TIMEOUT_VALUE_MS); + struct i3c_peci *priv = peci_get_adapdata(adapter); + struct i3c_mctp_packet *tx_packet; + struct i3c_mctp_packet *rx_packet; + u8 tag = priv->tag; + int ret; + + tx_packet = i3c_mctp_packet_alloc(GFP_KERNEL); + if (!tx_packet) + return ERR_PTR(-ENOMEM); + + prepare_tx_packet(tx_packet, tx_len, rx_len, tx_buf, dest_eid, tag); + + print_hex_dump_bytes("TX : ", DUMP_PREFIX_NONE, &tx_packet->data, tx_packet->size); + + ret = i3c_mctp_send_packet(i3cdev, tx_packet); + if (ret) { + i3c_mctp_packet_free(tx_packet); + return ERR_PTR(ret); + } + + i3c_mctp_packet_free(tx_packet); + priv->tag++; + rx_packet = i3c_mctp_receive_packet(priv->client, timeout); + if (IS_ERR(rx_packet)) + return rx_packet; + + ret = verify_rx_packet(adapter, rx_packet, tag); + if (ret) { + i3c_mctp_packet_free(rx_packet); + return ERR_PTR(ret); + } + + print_hex_dump_bytes("RX : ", DUMP_PREFIX_NONE, &rx_packet->data, rx_packet->size); + + return rx_packet; +} + +static int +i3c_peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg) +{ + struct i3c_peci *priv = peci_get_adapdata(adapter); + struct i3c_mctp_packet *rx_packet; + u8 domain_id = 0; + u8 dest_eid; + int ret; + + if (msg->tx_len > PECI_PAYLOAD_SIZE || msg->rx_len > PECI_PAYLOAD_SIZE) + return -EINVAL; + + if (msg->tx_len > 2) + domain_id = msg->tx_buf[1] >> 1; + + ret = i3c_mctp_get_eid(priv->client, domain_id, &dest_eid); + if (ret) + return -ENODEV; + + rx_packet = i3c_peci_send_receive(adapter, priv->i3cdev, + msg->tx_len, msg->rx_len, msg->tx_buf, dest_eid); + if (IS_ERR(rx_packet)) + return PTR_ERR(rx_packet); + + memcpy(msg->rx_buf, (u8 *)(rx_packet->data.payload) + sizeof(struct mctp_peci_vdm_hdr), + msg->rx_len); + + i3c_mctp_packet_free(rx_packet); + + return 0; +} + +static int i3c_peci_probe(struct platform_device *pdev) +{ + struct peci_adapter *adapter; + struct i3c_peci *priv; + int ret; + + adapter = peci_alloc_adapter(&pdev->dev, sizeof(*priv)); + if (!adapter) + return -ENOMEM; + + priv = peci_get_adapdata(adapter); + + platform_set_drvdata(pdev, priv); + + priv->i3cdev = dev_to_i3cdev(pdev->dev.parent); + priv->dev = &pdev->dev; + + adapter->owner = THIS_MODULE; + strscpy(adapter->name, pdev->name, sizeof(adapter->name)); + + adapter->xfer = i3c_peci_xfer; + adapter->peci_revision = 0x41; + + priv->adapter = adapter; + + priv->client = i3c_mctp_add_peci_client(priv->i3cdev); + if (IS_ERR(priv->client)) { + ret = -ENOMEM; + goto out_put_device; + } + + ret = peci_add_adapter(adapter); + if (ret) + goto out_del_client; + + return 0; + +out_del_client: + i3c_mctp_remove_peci_client(priv->client); +out_put_device: + put_device(&adapter->dev); + return ret; +} + +static int i3c_peci_remove(struct platform_device *pdev) +{ + struct i3c_peci *priv = platform_get_drvdata(pdev); + + peci_del_adapter(priv->adapter); + + i3c_mctp_remove_peci_client(priv->client); + + return 0; +} + +static struct platform_driver i3c_peci_driver = { + .probe = i3c_peci_probe, + .remove = i3c_peci_remove, + .driver = { + .name = "peci-i3c", + }, +}; +module_platform_driver(i3c_peci_driver); + +MODULE_AUTHOR("Oleksandr Shulzhenko "); +MODULE_DESCRIPTION("I3C PECI driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3