1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (C) 2023-2026 Intel Corporation */ 3 #include <linux/dev_printk.h> 4 #include <linux/errno.h> 5 #include <linux/slab.h> 6 #include <linux/string.h> 7 8 #include "dma.h" 9 #include "fw_client.h" 10 #include "ham.h" 11 #include "hw_msg.h" 12 #include "issei_dev.h" 13 14 static int __issei_ham_send_msg(struct issei_device *idev, u32 length, void *buf) 15 { 16 struct issei_dma_data data = { }; 17 int ret; 18 19 data.length = length; 20 data.buf = buf; 21 ret = issei_dma_write(idev, &data); 22 if (ret) 23 return ret; 24 return idev->ops->irq_write_generate(idev); 25 } 26 27 /** 28 * issei_ham_send_start_req - send start request to firmware 29 * @idev: issei device object 30 * 31 * Return: 0 on success, <0 on failures 32 */ 33 int issei_ham_send_start_req(struct issei_device *idev) 34 { 35 struct ham_start_message_req req; 36 37 req.header.cmd = HAM_BUS_CMD_START_REQ; 38 req.supported_version = ISSEI_SUPPORTED_PROTOCOL_VER; 39 req.heci_capabilities_length = 0; 40 41 return __issei_ham_send_msg(idev, sizeof(req), &req); 42 } 43 44 /** 45 * issei_ham_send_clients_req - send clients request to firmware 46 * @idev: issei device object 47 * 48 * Return: 0 on success, <0 on failures 49 */ 50 int issei_ham_send_clients_req(struct issei_device *idev) 51 { 52 struct ham_get_clients_req req; 53 54 req.header.cmd = HAM_BUS_CMD_CLIENT_REQ; 55 56 return __issei_ham_send_msg(idev, sizeof(req), &req); 57 } 58 59 static int issei_ham_start_rsp(struct issei_device *idev, const u8 *buf, size_t length) 60 { 61 struct ham_start_message_res *res = (struct ham_start_message_res *)buf; 62 int ret; 63 64 if (idev->rst_state != ISSEI_RST_STATE_START) { 65 dev_err(&idev->dev, "Wrong state %d != %d\n", 66 idev->rst_state, ISSEI_RST_STATE_START); 67 return -EPROTO; 68 } 69 70 if (length < sizeof(*res)) { 71 dev_err(&idev->dev, "Small start response size %zu < %zu\n", 72 length, sizeof(*res)); 73 return -EPROTO; 74 } 75 76 if (length - sizeof(*res) != res->heci_capabilities_length) { 77 dev_err(&idev->dev, "Wrong start response size %zu != %u\n", 78 length - sizeof(*res), res->heci_capabilities_length); 79 return -EPROTO; 80 } 81 82 memcpy(idev->fw_version, res->fw_version, sizeof(idev->fw_version)); 83 idev->fw_protocol_ver = res->supported_version; 84 dev_dbg(&idev->dev, "FW protocol: %u FW version %u.%u.%u.%u", idev->fw_protocol_ver, 85 idev->fw_version[0], idev->fw_version[1], 86 idev->fw_version[2], idev->fw_version[3]); 87 88 ret = issei_ham_send_clients_req(idev); 89 if (ret == -EBUSY) 90 ret = 0; 91 92 return ret; 93 } 94 95 static int issei_ham_client_rsp(struct issei_device *idev, const u8 *buf, size_t length) 96 { 97 struct ham_get_clients_res *res = (struct ham_get_clients_res *)buf; 98 struct ham_client_properties *client; 99 100 if (idev->rst_state != ISSEI_RST_STATE_CLIENT_ENUM) { 101 dev_err(&idev->dev, "Wrong state %d != %d\n", 102 idev->rst_state, ISSEI_RST_STATE_CLIENT_ENUM); 103 return -EPROTO; 104 } 105 106 if (length < sizeof(*res)) { 107 dev_err(&idev->dev, "Small response size %zu < %zu\n", length, sizeof(*res)); 108 return -EPROTO; 109 } 110 111 if (length - sizeof(*res) != res->client_count * sizeof(struct ham_client_properties)) { 112 dev_err(&idev->dev, "Wrong response size %zu < %zu\n", 113 length - sizeof(*res), 114 res->client_count * sizeof(struct ham_client_properties)); 115 return -EPROTO; 116 } 117 118 guard(mutex)(&idev->client_lock); 119 120 for (size_t i = 0; i < res->client_count; i++) { 121 client = &res->clients_props[i]; 122 dev_dbg(&idev->dev, "client: id = %u ver = %u uuid = %pUb mtu = %u flags = %u", 123 client->client_number, client->protocol_ver, &client->client_uuid, 124 client->client_mtu, client->flags); 125 issei_fw_cl_create(idev, client->client_number, client->protocol_ver, 126 &client->client_uuid, client->client_mtu, client->flags); 127 } 128 return 0; 129 } 130 131 static int __issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length) 132 { 133 struct ham_bus_message *hdr = (struct ham_bus_message *)buf; 134 135 if (length < sizeof(*hdr)) { 136 dev_err(&idev->dev, "Small bus message size %zu < %zu\n", 137 length, sizeof(*hdr)); 138 return -EPROTO; 139 } 140 141 switch (hdr->cmd) { 142 case HAM_BUS_CMD_START_RSP: 143 return issei_ham_start_rsp(idev, buf, length); 144 145 case HAM_BUS_CMD_CLIENT_RSP: 146 return issei_ham_client_rsp(idev, buf, length); 147 148 default: 149 dev_err(&idev->dev, "Unexpected command 0x%x", hdr->cmd); 150 return -EPROTO; 151 } 152 } 153 154 /** 155 * issei_ham_process_ham_rsp - process response from firmware and release buffer 156 * @idev: issei device object 157 * @buf: response buffer 158 * @length: response buffer length 159 * 160 * Return: 0 on success, <0 on failures 161 */ 162 int issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length) 163 { 164 int ret; 165 166 ret = __issei_ham_process_ham_rsp(idev, buf, length); 167 kfree(buf); 168 return ret; 169 } 170