xref: /linux/drivers/misc/issei/hw_heci.c (revision 93e4b3076b5f2d853462b9777d083c77fc0b7b23)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (C) 2023-2026 Intel Corporation */
3 #include <linux/align.h>
4 #include <linux/bitfield.h>
5 #include <linux/bits.h>
6 #include <linux/cleanup.h>
7 #include <linux/dev_printk.h>
8 #include <linux/err.h>
9 #include <linux/io.h>
10 #include <linux/pci.h>
11 #include <linux/sizes.h>
12 #include <linux/spinlock.h>
13 #include <linux/types.h>
14 
15 #include "hw_heci.h"
16 #include "hw_heci_regs.h"
17 #include "hw_msg.h"
18 
19 /**
20  * heci_reg_read - Reads 32bit data from the issei heci device
21  * @hw: the heci hardware structure
22  * @offset: offset from which to read the data
23  *
24  * Return: register value (u32)
25  */
26 static inline u32 heci_reg_read(const struct issei_heci_hw *hw, unsigned long offset)
27 {
28 	return ioread32(hw->mem_addr + offset);
29 }
30 
31 /**
32  * heci_reg_write - Writes 32bit data to the issei heci device
33  *
34  * @hw: the heci hardware structure
35  * @offset: offset from which to write the data
36  * @value: register value to write (u32)
37  */
38 static inline void heci_reg_write(const struct issei_heci_hw *hw, unsigned long offset, u32 value)
39 {
40 	iowrite32(value, hw->mem_addr + offset);
41 }
42 
43 /**
44  * heci_fwcbrw_read - Reads 32bit data from heci circular buffer
45  * @hw: the heci hardware structure
46  *
47  * Return: FW_CB_RW register value (u32)
48  */
49 static inline u32 heci_fwcbrw_read(const struct issei_heci_hw *hw)
50 {
51 	return heci_reg_read(hw, FW_CB_RW);
52 }
53 
54 /**
55  * heci_hcbww_write - write 32bit data to the host circular buffer
56  * @hw: the heci hardware structure
57  * @data: 32bit data to be written to the host circular buffer
58  */
59 static inline void heci_hcbww_write(const struct issei_heci_hw *hw, u32 data)
60 {
61 	heci_reg_write(hw, H_CB_WW, data);
62 }
63 
64 /**
65  * heci_irq_src - Filters IRQ source bits from the host CSR
66  * @hcsr: host CSR register value
67  *
68  * Return: interrupt source bits of host CSR
69  */
70 static inline u32 heci_irq_src(u32 hcsr)
71 {
72 	return hcsr & H_CSR_IS;
73 }
74 
75 /**
76  * heci_hcsr_read - Reads 32bit data from the host CSR
77  * @idev: the device structure
78  *
79  * Return: H_CSR register value (u32)
80  */
81 static inline u32 heci_hcsr_read(const struct issei_device *idev)
82 {
83 	return heci_reg_read(to_heci_hw(idev), H_CSR);
84 }
85 
86 /**
87  * heci_hcsr_write - writes H_CSR register to device
88  * @idev: the device structure
89  * @reg: new register value
90  */
91 static inline void heci_hcsr_write(struct issei_device *idev, u32 reg)
92 {
93 	heci_reg_write(to_heci_hw(idev), H_CSR, reg);
94 }
95 
96 /**
97  * heci_hcsr_set - writes H_CSR register to the heci device
98  * @idev: the device structure
99  * @reg: new register value
100  *
101  * Writes H_CSR register to the heci device
102  * and ignores the H_IS bit for it is write-one-to-zero.
103  *
104  */
105 static inline void heci_hcsr_set(struct issei_device *idev, u32 reg)
106 {
107 	reg &= ~H_CSR_IS;
108 	heci_hcsr_write(idev, reg);
109 }
110 
111 /**
112  * heci_hcsr_set_hig - set host interrupt (set H_CSR_IG)
113  * @idev: the device structure
114  */
115 static inline void heci_hcsr_set_hig(struct issei_device *idev)
116 {
117 	u32 reg;
118 
119 	reg = heci_hcsr_read(idev) | H_CSR_IG;
120 	heci_hcsr_set(idev, reg);
121 }
122 
123 /**
124  * heci_fwcsr_read - Reads 32bit data from the FW CSR
125  * @idev: the device structure
126  *
127  * Return: FW_CSR_HA register value (u32)
128  */
129 static inline u32 heci_fwcsr_read(const struct issei_device *idev)
130 {
131 	return heci_reg_read(to_heci_hw(idev), FW_CSR_HA);
132 }
133 
134 /**
135  * heci_count_full_read_slots - counts read full slots.
136  * @idev: the device structure
137  *
138  * Return: -EOVERFLOW if overflow, otherwise filled slots count
139  */
140 static int heci_count_full_read_slots(struct issei_device *idev)
141 {
142 	u8 buffer_depth, filled_slots;
143 	u8 read_ptr, write_ptr;
144 	u32 reg;
145 
146 	reg = heci_fwcsr_read(idev);
147 	buffer_depth = (u8)FIELD_GET(FW_CSR_CBD, reg);
148 	read_ptr = (u8)FIELD_GET(FW_CSR_CBRP, reg);
149 	write_ptr = (u8)FIELD_GET(FW_CSR_CBWP, reg);
150 	filled_slots = write_ptr - read_ptr;
151 
152 	/* check for overflow */
153 	if (filled_slots > buffer_depth)
154 		return -EOVERFLOW;
155 
156 	dev_dbg(&idev->dev, "filled_slots = %08x\n", filled_slots);
157 	return filled_slots;
158 }
159 
160 /**
161  * heci_irq_disable - disables heci device interrupts
162  * @idev: the device structure
163  * @reg: supplied hcsr register value
164  *
165  * disables heci device interrupts using supplied hcsr register value.
166  */
167 static inline void heci_irq_disable(struct issei_device *idev, u32 reg)
168 {
169 	reg &= ~H_CSR_IE;
170 	heci_hcsr_set(idev, reg);
171 }
172 
173 /**
174  * heci_irq_clear - clear and stop interrupts
175  * @idev: the device structure
176  * @reg: supplied hcsr register value
177  */
178 static inline void heci_irq_clear(struct issei_device *idev, u32 reg)
179 {
180 	if (heci_irq_src(reg))
181 		heci_hcsr_write(idev, reg);
182 }
183 
184 /**
185  * issei_heci_irq_clear - clear and stop interrupts
186  * @idev: the device structure
187  */
188 static void issei_heci_irq_clear(struct issei_device *idev)
189 {
190 	u32 reg = heci_hcsr_read(idev);
191 
192 	heci_irq_clear(idev, reg);
193 }
194 
195 /**
196  * issei_heci_irq_enable - enables heci device interrupts
197  * @idev: the device structure
198  */
199 static void issei_heci_irq_enable(struct issei_device *idev)
200 {
201 	u32 reg;
202 
203 	reg = heci_hcsr_read(idev) | H_CSR_IE;
204 	heci_hcsr_set(idev, reg);
205 }
206 
207 /**
208  * issei_heci_irq_disable - disables heci device interrupts
209  * @idev: the device structure
210  */
211 static void issei_heci_irq_disable(struct issei_device *idev)
212 {
213 	u32 reg = heci_hcsr_read(idev);
214 
215 	heci_irq_disable(idev, reg);
216 }
217 
218 static void issei_heci_irq_sync(struct issei_device *idev)
219 {
220 	synchronize_irq(to_heci_hw(idev)->irq);
221 }
222 
223 /**
224  * issei_heci_hw_reset_release - release device from the reset
225  * @idev: the device structure
226  */
227 static void issei_heci_hw_reset_release(struct issei_device *idev)
228 {
229 	u32 reg = heci_hcsr_read(idev);
230 
231 	reg |= H_CSR_IG;
232 	reg &= ~H_CSR_RST;
233 	heci_hcsr_set(idev, reg);
234 }
235 
236 /**
237  * heci_hw_is_ready - check whether the hw has turned ready
238  * @idev: the device structure
239  *
240  * Return: bool
241  */
242 static bool heci_hw_is_ready(struct issei_device *idev)
243 {
244 	u32 reg = heci_fwcsr_read(idev);
245 
246 	return reg & FW_CSR_RDY;
247 }
248 
249 /**
250  * heci_hw_is_resetting - check whether the hw is in reset
251  * @idev: the device structure
252  *
253  * Return: bool
254  */
255 static bool heci_hw_is_resetting(struct issei_device *idev)
256 {
257 	u32 reg = heci_fwcsr_read(idev);
258 
259 	return reg & FW_CSR_RST;
260 }
261 
262 /**
263  * issei_heci_host_set_ready - enable device
264  * @idev: the device structure
265  */
266 static void issei_heci_host_set_ready(struct issei_device *idev)
267 {
268 	u32 reg = heci_hcsr_read(idev);
269 
270 	reg |= H_CSR_IE | H_CSR_IG | H_CSR_RDY;
271 	heci_hcsr_set(idev, reg);
272 }
273 
274 /**
275  * issei_heci_hw_reset - resets fw via heci csr register.
276  * @idev: the device structure
277  * @enable: if interrupt should be enabled after reset.
278  *
279  * Return: 0 on success an error code otherwise
280  */
281 static int issei_heci_hw_reset(struct issei_device *idev, bool enable)
282 {
283 	u32 reg;
284 
285 	if (enable)
286 		issei_heci_irq_enable(idev);
287 
288 	reg = heci_hcsr_read(idev);
289 	/*
290 	 * H_CSR_RST may be found lit before reset is started,
291 	 * for example if preceding reset flow hasn't completed.
292 	 * In that case asserting H_CSR_RST will be ignored, therefore
293 	 * we need to clean H_CSR_RST bit to start a successful reset sequence.
294 	 */
295 	if (reg & H_CSR_RST) {
296 		dev_warn(&idev->dev, "H_CSR_RST is set = 0x%08X", reg);
297 		reg &= ~H_CSR_RST;
298 		heci_hcsr_set(idev, reg);
299 		reg = heci_hcsr_read(idev);
300 	}
301 
302 	reg |= H_CSR_RST | H_CSR_IG | H_CSR_IS;
303 
304 	if (!enable)
305 		reg &= ~H_CSR_IE;
306 
307 	heci_hcsr_write(idev, reg);
308 
309 	/*
310 	 * Host reads the H_CSR once to ensure that the
311 	 * posted write to H_CSR completes.
312 	 */
313 	reg = heci_hcsr_read(idev);
314 
315 	if (!(reg & H_CSR_RST))
316 		dev_warn(&idev->dev, "H_CSR_RST is not set = 0x%08X", reg);
317 
318 	if (reg & H_CSR_RDY)
319 		dev_warn(&idev->dev, "H_CSR_RDY is not cleared 0x%08X", reg);
320 
321 	if (!enable)
322 		issei_heci_hw_reset_release(idev);
323 	return 0;
324 }
325 
326 /**
327  * issei_heci_irq_write_generate - generate interrupt to signal write completion.
328  * @idev: the device structure
329  *
330  * Return: 0 on success, -EIO if hardware is not ready and requires reset
331  */
332 static int issei_heci_irq_write_generate(struct issei_device *idev)
333 {
334 	struct issei_heci_hw *hw = to_heci_hw(idev);
335 
336 	scoped_guard(spinlock_irqsave, &hw->access_lock)
337 		heci_hcsr_set_hig(idev);
338 	if (!heci_hw_is_ready(idev))
339 		return -EIO;
340 	return 0;
341 }
342 
343 /**
344  * issei_heci_hw_config - initial hardware configuration.
345  * @idev: the device structure
346  *
347  * Return: 0 always
348  */
349 static int issei_heci_hw_config(struct issei_device *idev)
350 {
351 	struct issei_heci_hw *hw = to_heci_hw(idev);
352 	u32 reg;
353 
354 	/* Doesn't change in runtime */
355 	reg = heci_hcsr_read(idev);
356 	hw->hbuf_depth = FIELD_GET(H_CSR_CBD, reg);
357 
358 	return 0;
359 }
360 
361 /**
362  * heci_write_hbuf - write to hardware buffer
363  * @idev: the device structure
364  * @data: data to write
365  * @data_len: data size
366  *
367  * Return: 0 on success, <0 on error
368  */
369 static int heci_write_hbuf(struct issei_device *idev, const void *data, size_t data_len)
370 {
371 	struct issei_heci_hw *hw = to_heci_hw(idev);
372 
373 	if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) {
374 		dev_err(&idev->dev, "Data size %zu not aligned to slot size %zu\n",
375 			data_len, CB_SLOT_SIZE);
376 		return -EINVAL;
377 	}
378 
379 	scoped_guard(spinlock_irqsave, &hw->access_lock) {
380 		const u32 *reg_buf = data;
381 		size_t i;
382 
383 		for (i = 0; i < data_len / CB_SLOT_SIZE; i++)
384 			heci_hcbww_write(hw, reg_buf[i]);
385 	}
386 
387 	return issei_heci_irq_write_generate(idev);
388 }
389 
390 /**
391  * heci_read_hbuf - read data from hardware buffer
392  * @idev: the device structure
393  * @data: buffer to store read data
394  * @data_len: buffer size
395  *
396  * Return: 0 on success, <0 on error
397  */
398 static int heci_read_hbuf(struct issei_device *idev, void *data, size_t data_len)
399 {
400 	struct issei_heci_hw *hw = to_heci_hw(idev);
401 	u32 *reg_buf = data;
402 
403 	if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) {
404 		dev_err(&idev->dev, "Data size %zu not aligned to slot size %zu\n",
405 			data_len, CB_SLOT_SIZE);
406 		return -EINVAL;
407 	}
408 
409 	scoped_guard(spinlock_irqsave, &hw->access_lock) {
410 		for (; data_len >= CB_SLOT_SIZE; data_len -= CB_SLOT_SIZE)
411 			*reg_buf++ = heci_fwcbrw_read(hw);
412 	}
413 	return 0;
414 }
415 
416 /**
417  * issei_heci_setup_message_send - send setup message to firmware
418  * @idev: the device structure
419  *
420  * Return: 0 on success, <0 on error
421  */
422 static int issei_heci_setup_message_send(struct issei_device *idev)
423 {
424 	struct ham_setup_shared_memory_req req = {
425 		.msg_id = HAM_CB_MESSAGE_ID_REQ,
426 		.ver = HAM_CB_MESSAGE_VER,
427 		.reserved = 0,
428 		.buffer_physical_address = idev->dma.daddr,
429 		.host_to_fw_section_length = idev->dma.length.h2f,
430 		.fw_to_host_section_length = idev->dma.length.f2h,
431 		.control_length = idev->dma.length.ctl
432 	};
433 	int ret;
434 
435 	ret = heci_write_hbuf(idev, &req, sizeof(req));
436 	if (ret)
437 		dev_err(&idev->dev, "Shared memory req write failed ret = %d\n", ret);
438 
439 	return ret;
440 }
441 
442 /**
443  * issei_heci_setup_message_recv - receive setup message response from firmware
444  * @idev: the device structure
445  *
446  * Return: 0 on success, <0 on error
447  */
448 static int issei_heci_setup_message_recv(struct issei_device *idev)
449 {
450 	struct ham_setup_shared_memory_res res;
451 	int ret;
452 
453 	if (heci_count_full_read_slots(idev) != sizeof(res) / CB_SLOT_SIZE) {
454 		dev_dbg(&idev->dev, "Setup response is not fully received\n");
455 		return -ENODATA;
456 	}
457 
458 	ret = heci_read_hbuf(idev, &res, sizeof(res));
459 	if (ret) {
460 		dev_err(&idev->dev, "Shared memory res read failed ret = %d\n", ret);
461 		return ret;
462 	}
463 
464 	if (res.msg_id != HAM_CB_MESSAGE_ID_RES) {
465 		dev_err(&idev->dev, "Shared memory res header 0x%x != 0x%x\n",
466 			res.msg_id, HAM_CB_MESSAGE_ID_RES);
467 		return -EPROTO;
468 	}
469 	if (res.status != 0) {
470 		dev_err(&idev->dev, "Shared memory res status %d != 0\n", res.status);
471 		return -EPROTO;
472 	}
473 
474 	return 0;
475 }
476 
477 static const struct issei_hw_ops hw_heci_ops = {
478 	.irq_clear = issei_heci_irq_clear,
479 	.irq_enable = issei_heci_irq_enable,
480 	.irq_disable = issei_heci_irq_disable,
481 	.irq_sync = issei_heci_irq_sync,
482 
483 	.hw_reset = issei_heci_hw_reset,
484 	.hw_config = issei_heci_hw_config,
485 	.hw_reset_release = issei_heci_hw_reset_release,
486 	.host_set_ready = issei_heci_host_set_ready,
487 
488 	.hw_is_ready = heci_hw_is_ready,
489 
490 	.setup_message_send = issei_heci_setup_message_send,
491 	.setup_message_recv = issei_heci_setup_message_recv,
492 	.irq_write_generate = issei_heci_irq_write_generate,
493 };
494 
495 const struct issei_hw_ops *issei_heci_get_ops(void)
496 {
497 	return &hw_heci_ops;
498 }
499 
500 irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id)
501 {
502 	struct issei_device *idev = dev_id;
503 	struct issei_heci_hw *hw = to_heci_hw(idev);
504 	u32 reg;
505 
506 	reg = heci_hcsr_read(idev);
507 	if (!heci_irq_src(reg))
508 		return IRQ_NONE;
509 
510 	scoped_guard(spinlock_irqsave, &hw->access_lock) {
511 		reg = heci_hcsr_read(idev);
512 		heci_irq_clear(idev, reg);
513 
514 		if (heci_hw_is_resetting(idev))
515 			heci_hcsr_set_hig(idev);
516 	}
517 
518 	dev_dbg(&idev->dev, "interrupt source 0x%08X\n", heci_irq_src(reg));
519 
520 	issei_poke_process_thread(idev);
521 
522 	return IRQ_HANDLED;
523 }
524 
525 static const struct hw_heci_cfg hw_heci_pch_cfg = {
526 	.dma_length.h2f = SZ_64K,
527 	.dma_length.f2h = SZ_64K,
528 	.dma_length.ctl = SZ_4K,
529 };
530 
531 const struct hw_heci_cfg *issei_heci_get_cfg(kernel_ulong_t idx)
532 {
533 	return &hw_heci_pch_cfg;
534 }
535 
536 /**
537  * issei_heci_dev_init - initializes the issei device structure with hw_heci
538  * @idev: device structure
539  * @mem_addr: memory address on bar
540  * @cfg: per device generation config
541  */
542 void issei_heci_dev_init(struct issei_device *idev,
543 			 void __iomem *mem_addr, const struct hw_heci_cfg *cfg)
544 {
545 	struct issei_heci_hw *hw = to_heci_hw(idev);
546 
547 	spin_lock_init(&hw->access_lock);
548 	hw->mem_addr = mem_addr;
549 	hw->cfg = cfg;
550 }
551