summaryrefslogtreecommitdiff
path: root/drivers/misc/issei/ham.c
blob: 674d9733d16cf12f44862b76c403ce77b504f936 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2023-2026 Intel Corporation */
#include <linux/dev_printk.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/string.h>

#include "dma.h"
#include "fw_client.h"
#include "ham.h"
#include "hw_msg.h"
#include "issei_dev.h"

static int __issei_ham_send_msg(struct issei_device *idev, u32 length, void *buf)
{
	struct issei_dma_data data = { };
	int ret;

	data.length = length;
	data.buf = buf;
	ret = issei_dma_write(idev, &data);
	if (ret)
		return ret;
	return idev->ops->irq_write_generate(idev);
}

/**
 * issei_ham_send_start_req - send start request to firmware
 * @idev: issei device object
 *
 * Return: 0 on success, <0 on failures
 */
int issei_ham_send_start_req(struct issei_device *idev)
{
	struct ham_start_message_req req;

	req.header.cmd = HAM_BUS_CMD_START_REQ;
	req.supported_version = ISSEI_SUPPORTED_PROTOCOL_VER;
	req.heci_capabilities_length = 0;

	return __issei_ham_send_msg(idev, sizeof(req), &req);
}

/**
 * issei_ham_send_clients_req - send clients request to firmware
 * @idev: issei device object
 *
 * Return: 0 on success, <0 on failures
 */
int issei_ham_send_clients_req(struct issei_device *idev)
{
	struct ham_get_clients_req req;

	req.header.cmd = HAM_BUS_CMD_CLIENT_REQ;

	return __issei_ham_send_msg(idev, sizeof(req), &req);
}

static int issei_ham_start_rsp(struct issei_device *idev, const u8 *buf, size_t length)
{
	struct ham_start_message_res *res = (struct ham_start_message_res *)buf;
	int ret;

	if (idev->rst_state != ISSEI_RST_STATE_START) {
		dev_err(&idev->dev, "Wrong state %d != %d\n",
			idev->rst_state, ISSEI_RST_STATE_START);
		return -EPROTO;
	}

	if (length < sizeof(*res)) {
		dev_err(&idev->dev, "Small start response size %zu < %zu\n",
			length, sizeof(*res));
		return -EPROTO;
	}

	if (length - sizeof(*res) != res->heci_capabilities_length) {
		dev_err(&idev->dev, "Wrong start response size %zu != %u\n",
			length - sizeof(*res), res->heci_capabilities_length);
		return -EPROTO;
	}

	memcpy(idev->fw_version, res->fw_version, sizeof(idev->fw_version));
	idev->fw_protocol_ver = res->supported_version;
	dev_dbg(&idev->dev, "FW protocol: %u FW version %u.%u.%u.%u", idev->fw_protocol_ver,
		idev->fw_version[0], idev->fw_version[1],
		idev->fw_version[2], idev->fw_version[3]);

	ret = issei_ham_send_clients_req(idev);
	if (ret == -EBUSY)
		ret = 0;

	return ret;
}

static int issei_ham_client_rsp(struct issei_device *idev, const u8 *buf, size_t length)
{
	struct ham_get_clients_res *res = (struct ham_get_clients_res *)buf;
	struct ham_client_properties *client;

	if (idev->rst_state != ISSEI_RST_STATE_CLIENT_ENUM) {
		dev_err(&idev->dev, "Wrong state %d != %d\n",
			idev->rst_state, ISSEI_RST_STATE_CLIENT_ENUM);
		return -EPROTO;
	}

	if (length < sizeof(*res)) {
		dev_err(&idev->dev, "Small response size %zu < %zu\n", length, sizeof(*res));
		return -EPROTO;
	}

	if (length - sizeof(*res) != res->client_count * sizeof(struct ham_client_properties)) {
		dev_err(&idev->dev, "Wrong response size %zu < %zu\n",
			length - sizeof(*res),
			res->client_count * sizeof(struct ham_client_properties));
		return -EPROTO;
	}

	guard(mutex)(&idev->client_lock);

	for (size_t i = 0; i < res->client_count; i++) {
		client = &res->clients_props[i];
		dev_dbg(&idev->dev, "client: id = %u ver = %u uuid = %pUb mtu = %u flags = %u",
			client->client_number, client->protocol_ver, &client->client_uuid,
			client->client_mtu, client->flags);
		issei_fw_cl_create(idev, client->client_number, client->protocol_ver,
				   &client->client_uuid, client->client_mtu, client->flags);
	}
	return 0;
}

static int __issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length)
{
	struct ham_bus_message *hdr = (struct ham_bus_message *)buf;

	if (length < sizeof(*hdr)) {
		dev_err(&idev->dev, "Small bus message size %zu < %zu\n",
			length, sizeof(*hdr));
		return -EPROTO;
	}

	switch (hdr->cmd) {
	case HAM_BUS_CMD_START_RSP:
		return issei_ham_start_rsp(idev, buf, length);

	case HAM_BUS_CMD_CLIENT_RSP:
		return issei_ham_client_rsp(idev, buf, length);

	default:
		dev_err(&idev->dev, "Unexpected command 0x%x", hdr->cmd);
		return -EPROTO;
	}
}

/**
 * issei_ham_process_ham_rsp - process response from firmware and release buffer
 * @idev: issei device object
 * @buf: response buffer
 * @length: response buffer length
 *
 * Return: 0 on success, <0 on failures
 */
int issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length)
{
	int ret;

	ret = __issei_ham_process_ham_rsp(idev, buf, length);
	kfree(buf);
	return ret;
}