xref: /linux/drivers/misc/issei/ham.c (revision 93e4b3076b5f2d853462b9777d083c77fc0b7b23)
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