xref: /linux/drivers/misc/issei/ham.c (revision 93e4b3076b5f2d853462b9777d083c77fc0b7b23)
17bd4b999SAlexander Usyskin // SPDX-License-Identifier: GPL-2.0
27bd4b999SAlexander Usyskin /* Copyright (C) 2023-2026 Intel Corporation */
37bd4b999SAlexander Usyskin #include <linux/dev_printk.h>
47bd4b999SAlexander Usyskin #include <linux/errno.h>
57bd4b999SAlexander Usyskin #include <linux/slab.h>
67bd4b999SAlexander Usyskin #include <linux/string.h>
77bd4b999SAlexander Usyskin 
87bd4b999SAlexander Usyskin #include "dma.h"
97bd4b999SAlexander Usyskin #include "fw_client.h"
107bd4b999SAlexander Usyskin #include "ham.h"
117bd4b999SAlexander Usyskin #include "hw_msg.h"
127bd4b999SAlexander Usyskin #include "issei_dev.h"
137bd4b999SAlexander Usyskin 
147bd4b999SAlexander Usyskin static int __issei_ham_send_msg(struct issei_device *idev, u32 length, void *buf)
157bd4b999SAlexander Usyskin {
167bd4b999SAlexander Usyskin 	struct issei_dma_data data = { };
177bd4b999SAlexander Usyskin 	int ret;
187bd4b999SAlexander Usyskin 
197bd4b999SAlexander Usyskin 	data.length = length;
207bd4b999SAlexander Usyskin 	data.buf = buf;
217bd4b999SAlexander Usyskin 	ret = issei_dma_write(idev, &data);
227bd4b999SAlexander Usyskin 	if (ret)
237bd4b999SAlexander Usyskin 		return ret;
247bd4b999SAlexander Usyskin 	return idev->ops->irq_write_generate(idev);
257bd4b999SAlexander Usyskin }
267bd4b999SAlexander Usyskin 
277bd4b999SAlexander Usyskin /**
287bd4b999SAlexander Usyskin  * issei_ham_send_start_req - send start request to firmware
297bd4b999SAlexander Usyskin  * @idev: issei device object
307bd4b999SAlexander Usyskin  *
317bd4b999SAlexander Usyskin  * Return: 0 on success, <0 on failures
327bd4b999SAlexander Usyskin  */
337bd4b999SAlexander Usyskin int issei_ham_send_start_req(struct issei_device *idev)
347bd4b999SAlexander Usyskin {
357bd4b999SAlexander Usyskin 	struct ham_start_message_req req;
367bd4b999SAlexander Usyskin 
377bd4b999SAlexander Usyskin 	req.header.cmd = HAM_BUS_CMD_START_REQ;
387bd4b999SAlexander Usyskin 	req.supported_version = ISSEI_SUPPORTED_PROTOCOL_VER;
397bd4b999SAlexander Usyskin 	req.heci_capabilities_length = 0;
407bd4b999SAlexander Usyskin 
417bd4b999SAlexander Usyskin 	return __issei_ham_send_msg(idev, sizeof(req), &req);
427bd4b999SAlexander Usyskin }
437bd4b999SAlexander Usyskin 
447bd4b999SAlexander Usyskin /**
457bd4b999SAlexander Usyskin  * issei_ham_send_clients_req - send clients request to firmware
467bd4b999SAlexander Usyskin  * @idev: issei device object
477bd4b999SAlexander Usyskin  *
487bd4b999SAlexander Usyskin  * Return: 0 on success, <0 on failures
497bd4b999SAlexander Usyskin  */
507bd4b999SAlexander Usyskin int issei_ham_send_clients_req(struct issei_device *idev)
517bd4b999SAlexander Usyskin {
527bd4b999SAlexander Usyskin 	struct ham_get_clients_req req;
537bd4b999SAlexander Usyskin 
547bd4b999SAlexander Usyskin 	req.header.cmd = HAM_BUS_CMD_CLIENT_REQ;
557bd4b999SAlexander Usyskin 
567bd4b999SAlexander Usyskin 	return __issei_ham_send_msg(idev, sizeof(req), &req);
577bd4b999SAlexander Usyskin }
587bd4b999SAlexander Usyskin 
597bd4b999SAlexander Usyskin static int issei_ham_start_rsp(struct issei_device *idev, const u8 *buf, size_t length)
607bd4b999SAlexander Usyskin {
617bd4b999SAlexander Usyskin 	struct ham_start_message_res *res = (struct ham_start_message_res *)buf;
627bd4b999SAlexander Usyskin 	int ret;
637bd4b999SAlexander Usyskin 
647bd4b999SAlexander Usyskin 	if (idev->rst_state != ISSEI_RST_STATE_START) {
657bd4b999SAlexander Usyskin 		dev_err(&idev->dev, "Wrong state %d != %d\n",
667bd4b999SAlexander Usyskin 			idev->rst_state, ISSEI_RST_STATE_START);
677bd4b999SAlexander Usyskin 		return -EPROTO;
687bd4b999SAlexander Usyskin 	}
697bd4b999SAlexander Usyskin 
707bd4b999SAlexander Usyskin 	if (length < sizeof(*res)) {
717bd4b999SAlexander Usyskin 		dev_err(&idev->dev, "Small start response size %zu < %zu\n",
727bd4b999SAlexander Usyskin 			length, sizeof(*res));
737bd4b999SAlexander Usyskin 		return -EPROTO;
747bd4b999SAlexander Usyskin 	}
757bd4b999SAlexander Usyskin 
767bd4b999SAlexander Usyskin 	if (length - sizeof(*res) != res->heci_capabilities_length) {
777bd4b999SAlexander Usyskin 		dev_err(&idev->dev, "Wrong start response size %zu != %u\n",
787bd4b999SAlexander Usyskin 			length - sizeof(*res), res->heci_capabilities_length);
797bd4b999SAlexander Usyskin 		return -EPROTO;
807bd4b999SAlexander Usyskin 	}
817bd4b999SAlexander Usyskin 
827bd4b999SAlexander Usyskin 	memcpy(idev->fw_version, res->fw_version, sizeof(idev->fw_version));
837bd4b999SAlexander Usyskin 	idev->fw_protocol_ver = res->supported_version;
847bd4b999SAlexander Usyskin 	dev_dbg(&idev->dev, "FW protocol: %u FW version %u.%u.%u.%u", idev->fw_protocol_ver,
857bd4b999SAlexander Usyskin 		idev->fw_version[0], idev->fw_version[1],
867bd4b999SAlexander Usyskin 		idev->fw_version[2], idev->fw_version[3]);
877bd4b999SAlexander Usyskin 
887bd4b999SAlexander Usyskin 	ret = issei_ham_send_clients_req(idev);
897bd4b999SAlexander Usyskin 	if (ret == -EBUSY)
907bd4b999SAlexander Usyskin 		ret = 0;
917bd4b999SAlexander Usyskin 
927bd4b999SAlexander Usyskin 	return ret;
937bd4b999SAlexander Usyskin }
947bd4b999SAlexander Usyskin 
957bd4b999SAlexander Usyskin static int issei_ham_client_rsp(struct issei_device *idev, const u8 *buf, size_t length)
967bd4b999SAlexander Usyskin {
977bd4b999SAlexander Usyskin 	struct ham_get_clients_res *res = (struct ham_get_clients_res *)buf;
987bd4b999SAlexander Usyskin 	struct ham_client_properties *client;
997bd4b999SAlexander Usyskin 
1007bd4b999SAlexander Usyskin 	if (idev->rst_state != ISSEI_RST_STATE_CLIENT_ENUM) {
1017bd4b999SAlexander Usyskin 		dev_err(&idev->dev, "Wrong state %d != %d\n",
1027bd4b999SAlexander Usyskin 			idev->rst_state, ISSEI_RST_STATE_CLIENT_ENUM);
1037bd4b999SAlexander Usyskin 		return -EPROTO;
1047bd4b999SAlexander Usyskin 	}
1057bd4b999SAlexander Usyskin 
1067bd4b999SAlexander Usyskin 	if (length < sizeof(*res)) {
1077bd4b999SAlexander Usyskin 		dev_err(&idev->dev, "Small response size %zu < %zu\n", length, sizeof(*res));
1087bd4b999SAlexander Usyskin 		return -EPROTO;
1097bd4b999SAlexander Usyskin 	}
1107bd4b999SAlexander Usyskin 
1117bd4b999SAlexander Usyskin 	if (length - sizeof(*res) != res->client_count * sizeof(struct ham_client_properties)) {
1127bd4b999SAlexander Usyskin 		dev_err(&idev->dev, "Wrong response size %zu < %zu\n",
1137bd4b999SAlexander Usyskin 			length - sizeof(*res),
1147bd4b999SAlexander Usyskin 			res->client_count * sizeof(struct ham_client_properties));
1157bd4b999SAlexander Usyskin 		return -EPROTO;
1167bd4b999SAlexander Usyskin 	}
1177bd4b999SAlexander Usyskin 
1187bd4b999SAlexander Usyskin 	guard(mutex)(&idev->client_lock);
1197bd4b999SAlexander Usyskin 
1207bd4b999SAlexander Usyskin 	for (size_t i = 0; i < res->client_count; i++) {
1217bd4b999SAlexander Usyskin 		client = &res->clients_props[i];
1227bd4b999SAlexander Usyskin 		dev_dbg(&idev->dev, "client: id = %u ver = %u uuid = %pUb mtu = %u flags = %u",
1237bd4b999SAlexander Usyskin 			client->client_number, client->protocol_ver, &client->client_uuid,
1247bd4b999SAlexander Usyskin 			client->client_mtu, client->flags);
1257bd4b999SAlexander Usyskin 		issei_fw_cl_create(idev, client->client_number, client->protocol_ver,
1267bd4b999SAlexander Usyskin 				   &client->client_uuid, client->client_mtu, client->flags);
1277bd4b999SAlexander Usyskin 	}
1287bd4b999SAlexander Usyskin 	return 0;
1297bd4b999SAlexander Usyskin }
1307bd4b999SAlexander Usyskin 
1317bd4b999SAlexander Usyskin static int __issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length)
1327bd4b999SAlexander Usyskin {
1337bd4b999SAlexander Usyskin 	struct ham_bus_message *hdr = (struct ham_bus_message *)buf;
1347bd4b999SAlexander Usyskin 
135*220190f9SLinmao Li 	if (length < sizeof(*hdr)) {
136*220190f9SLinmao Li 		dev_err(&idev->dev, "Small bus message size %zu < %zu\n",
137*220190f9SLinmao Li 			length, sizeof(*hdr));
138*220190f9SLinmao Li 		return -EPROTO;
139*220190f9SLinmao Li 	}
140*220190f9SLinmao Li 
1417bd4b999SAlexander Usyskin 	switch (hdr->cmd) {
1427bd4b999SAlexander Usyskin 	case HAM_BUS_CMD_START_RSP:
1437bd4b999SAlexander Usyskin 		return issei_ham_start_rsp(idev, buf, length);
1447bd4b999SAlexander Usyskin 
1457bd4b999SAlexander Usyskin 	case HAM_BUS_CMD_CLIENT_RSP:
1467bd4b999SAlexander Usyskin 		return issei_ham_client_rsp(idev, buf, length);
1477bd4b999SAlexander Usyskin 
1487bd4b999SAlexander Usyskin 	default:
1497bd4b999SAlexander Usyskin 		dev_err(&idev->dev, "Unexpected command 0x%x", hdr->cmd);
1507bd4b999SAlexander Usyskin 		return -EPROTO;
1517bd4b999SAlexander Usyskin 	}
1527bd4b999SAlexander Usyskin }
1537bd4b999SAlexander Usyskin 
1547bd4b999SAlexander Usyskin /**
1557bd4b999SAlexander Usyskin  * issei_ham_process_ham_rsp - process response from firmware and release buffer
1567bd4b999SAlexander Usyskin  * @idev: issei device object
1577bd4b999SAlexander Usyskin  * @buf: response buffer
1587bd4b999SAlexander Usyskin  * @length: response buffer length
1597bd4b999SAlexander Usyskin  *
1607bd4b999SAlexander Usyskin  * Return: 0 on success, <0 on failures
1617bd4b999SAlexander Usyskin  */
1627bd4b999SAlexander Usyskin int issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length)
1637bd4b999SAlexander Usyskin {
1647bd4b999SAlexander Usyskin 	int ret;
1657bd4b999SAlexander Usyskin 
1667bd4b999SAlexander Usyskin 	ret = __issei_ham_process_ham_rsp(idev, buf, length);
1677bd4b999SAlexander Usyskin 	kfree(buf);
1687bd4b999SAlexander Usyskin 	return ret;
1697bd4b999SAlexander Usyskin }
170