xref: /linux/drivers/misc/issei/hw_heci.c (revision 93e4b3076b5f2d853462b9777d083c77fc0b7b23)
18bf5e849SAlexander Usyskin // SPDX-License-Identifier: GPL-2.0
28bf5e849SAlexander Usyskin /* Copyright (C) 2023-2026 Intel Corporation */
38bf5e849SAlexander Usyskin #include <linux/align.h>
48bf5e849SAlexander Usyskin #include <linux/bitfield.h>
58bf5e849SAlexander Usyskin #include <linux/bits.h>
68bf5e849SAlexander Usyskin #include <linux/cleanup.h>
78bf5e849SAlexander Usyskin #include <linux/dev_printk.h>
88bf5e849SAlexander Usyskin #include <linux/err.h>
98bf5e849SAlexander Usyskin #include <linux/io.h>
108bf5e849SAlexander Usyskin #include <linux/pci.h>
118bf5e849SAlexander Usyskin #include <linux/sizes.h>
128bf5e849SAlexander Usyskin #include <linux/spinlock.h>
138bf5e849SAlexander Usyskin #include <linux/types.h>
148bf5e849SAlexander Usyskin 
158bf5e849SAlexander Usyskin #include "hw_heci.h"
168bf5e849SAlexander Usyskin #include "hw_heci_regs.h"
178bf5e849SAlexander Usyskin #include "hw_msg.h"
188bf5e849SAlexander Usyskin 
198bf5e849SAlexander Usyskin /**
208bf5e849SAlexander Usyskin  * heci_reg_read - Reads 32bit data from the issei heci device
218bf5e849SAlexander Usyskin  * @hw: the heci hardware structure
228bf5e849SAlexander Usyskin  * @offset: offset from which to read the data
238bf5e849SAlexander Usyskin  *
248bf5e849SAlexander Usyskin  * Return: register value (u32)
258bf5e849SAlexander Usyskin  */
268bf5e849SAlexander Usyskin static inline u32 heci_reg_read(const struct issei_heci_hw *hw, unsigned long offset)
278bf5e849SAlexander Usyskin {
288bf5e849SAlexander Usyskin 	return ioread32(hw->mem_addr + offset);
298bf5e849SAlexander Usyskin }
308bf5e849SAlexander Usyskin 
318bf5e849SAlexander Usyskin /**
328bf5e849SAlexander Usyskin  * heci_reg_write - Writes 32bit data to the issei heci device
338bf5e849SAlexander Usyskin  *
348bf5e849SAlexander Usyskin  * @hw: the heci hardware structure
358bf5e849SAlexander Usyskin  * @offset: offset from which to write the data
368bf5e849SAlexander Usyskin  * @value: register value to write (u32)
378bf5e849SAlexander Usyskin  */
388bf5e849SAlexander Usyskin static inline void heci_reg_write(const struct issei_heci_hw *hw, unsigned long offset, u32 value)
398bf5e849SAlexander Usyskin {
408bf5e849SAlexander Usyskin 	iowrite32(value, hw->mem_addr + offset);
418bf5e849SAlexander Usyskin }
428bf5e849SAlexander Usyskin 
438bf5e849SAlexander Usyskin /**
448bf5e849SAlexander Usyskin  * heci_fwcbrw_read - Reads 32bit data from heci circular buffer
458bf5e849SAlexander Usyskin  * @hw: the heci hardware structure
468bf5e849SAlexander Usyskin  *
478bf5e849SAlexander Usyskin  * Return: FW_CB_RW register value (u32)
488bf5e849SAlexander Usyskin  */
498bf5e849SAlexander Usyskin static inline u32 heci_fwcbrw_read(const struct issei_heci_hw *hw)
508bf5e849SAlexander Usyskin {
518bf5e849SAlexander Usyskin 	return heci_reg_read(hw, FW_CB_RW);
528bf5e849SAlexander Usyskin }
538bf5e849SAlexander Usyskin 
548bf5e849SAlexander Usyskin /**
558bf5e849SAlexander Usyskin  * heci_hcbww_write - write 32bit data to the host circular buffer
568bf5e849SAlexander Usyskin  * @hw: the heci hardware structure
578bf5e849SAlexander Usyskin  * @data: 32bit data to be written to the host circular buffer
588bf5e849SAlexander Usyskin  */
598bf5e849SAlexander Usyskin static inline void heci_hcbww_write(const struct issei_heci_hw *hw, u32 data)
608bf5e849SAlexander Usyskin {
618bf5e849SAlexander Usyskin 	heci_reg_write(hw, H_CB_WW, data);
628bf5e849SAlexander Usyskin }
638bf5e849SAlexander Usyskin 
648bf5e849SAlexander Usyskin /**
658bf5e849SAlexander Usyskin  * heci_irq_src - Filters IRQ source bits from the host CSR
668bf5e849SAlexander Usyskin  * @hcsr: host CSR register value
678bf5e849SAlexander Usyskin  *
688bf5e849SAlexander Usyskin  * Return: interrupt source bits of host CSR
698bf5e849SAlexander Usyskin  */
708bf5e849SAlexander Usyskin static inline u32 heci_irq_src(u32 hcsr)
718bf5e849SAlexander Usyskin {
728bf5e849SAlexander Usyskin 	return hcsr & H_CSR_IS;
738bf5e849SAlexander Usyskin }
748bf5e849SAlexander Usyskin 
758bf5e849SAlexander Usyskin /**
768bf5e849SAlexander Usyskin  * heci_hcsr_read - Reads 32bit data from the host CSR
778bf5e849SAlexander Usyskin  * @idev: the device structure
788bf5e849SAlexander Usyskin  *
798bf5e849SAlexander Usyskin  * Return: H_CSR register value (u32)
808bf5e849SAlexander Usyskin  */
818bf5e849SAlexander Usyskin static inline u32 heci_hcsr_read(const struct issei_device *idev)
828bf5e849SAlexander Usyskin {
838bf5e849SAlexander Usyskin 	return heci_reg_read(to_heci_hw(idev), H_CSR);
848bf5e849SAlexander Usyskin }
858bf5e849SAlexander Usyskin 
868bf5e849SAlexander Usyskin /**
878bf5e849SAlexander Usyskin  * heci_hcsr_write - writes H_CSR register to device
888bf5e849SAlexander Usyskin  * @idev: the device structure
898bf5e849SAlexander Usyskin  * @reg: new register value
908bf5e849SAlexander Usyskin  */
918bf5e849SAlexander Usyskin static inline void heci_hcsr_write(struct issei_device *idev, u32 reg)
928bf5e849SAlexander Usyskin {
938bf5e849SAlexander Usyskin 	heci_reg_write(to_heci_hw(idev), H_CSR, reg);
948bf5e849SAlexander Usyskin }
958bf5e849SAlexander Usyskin 
968bf5e849SAlexander Usyskin /**
978bf5e849SAlexander Usyskin  * heci_hcsr_set - writes H_CSR register to the heci device
988bf5e849SAlexander Usyskin  * @idev: the device structure
998bf5e849SAlexander Usyskin  * @reg: new register value
1008bf5e849SAlexander Usyskin  *
1018bf5e849SAlexander Usyskin  * Writes H_CSR register to the heci device
1028bf5e849SAlexander Usyskin  * and ignores the H_IS bit for it is write-one-to-zero.
1038bf5e849SAlexander Usyskin  *
1048bf5e849SAlexander Usyskin  */
1058bf5e849SAlexander Usyskin static inline void heci_hcsr_set(struct issei_device *idev, u32 reg)
1068bf5e849SAlexander Usyskin {
1078bf5e849SAlexander Usyskin 	reg &= ~H_CSR_IS;
1088bf5e849SAlexander Usyskin 	heci_hcsr_write(idev, reg);
1098bf5e849SAlexander Usyskin }
1108bf5e849SAlexander Usyskin 
1118bf5e849SAlexander Usyskin /**
1128bf5e849SAlexander Usyskin  * heci_hcsr_set_hig - set host interrupt (set H_CSR_IG)
1138bf5e849SAlexander Usyskin  * @idev: the device structure
1148bf5e849SAlexander Usyskin  */
1158bf5e849SAlexander Usyskin static inline void heci_hcsr_set_hig(struct issei_device *idev)
1168bf5e849SAlexander Usyskin {
1178bf5e849SAlexander Usyskin 	u32 reg;
1188bf5e849SAlexander Usyskin 
1198bf5e849SAlexander Usyskin 	reg = heci_hcsr_read(idev) | H_CSR_IG;
1208bf5e849SAlexander Usyskin 	heci_hcsr_set(idev, reg);
1218bf5e849SAlexander Usyskin }
1228bf5e849SAlexander Usyskin 
1238bf5e849SAlexander Usyskin /**
1248bf5e849SAlexander Usyskin  * heci_fwcsr_read - Reads 32bit data from the FW CSR
1258bf5e849SAlexander Usyskin  * @idev: the device structure
1268bf5e849SAlexander Usyskin  *
1278bf5e849SAlexander Usyskin  * Return: FW_CSR_HA register value (u32)
1288bf5e849SAlexander Usyskin  */
1298bf5e849SAlexander Usyskin static inline u32 heci_fwcsr_read(const struct issei_device *idev)
1308bf5e849SAlexander Usyskin {
1318bf5e849SAlexander Usyskin 	return heci_reg_read(to_heci_hw(idev), FW_CSR_HA);
1328bf5e849SAlexander Usyskin }
1338bf5e849SAlexander Usyskin 
1348bf5e849SAlexander Usyskin /**
1358bf5e849SAlexander Usyskin  * heci_count_full_read_slots - counts read full slots.
1368bf5e849SAlexander Usyskin  * @idev: the device structure
1378bf5e849SAlexander Usyskin  *
1388bf5e849SAlexander Usyskin  * Return: -EOVERFLOW if overflow, otherwise filled slots count
1398bf5e849SAlexander Usyskin  */
1408bf5e849SAlexander Usyskin static int heci_count_full_read_slots(struct issei_device *idev)
1418bf5e849SAlexander Usyskin {
1428bf5e849SAlexander Usyskin 	u8 buffer_depth, filled_slots;
1438bf5e849SAlexander Usyskin 	u8 read_ptr, write_ptr;
1448bf5e849SAlexander Usyskin 	u32 reg;
1458bf5e849SAlexander Usyskin 
1468bf5e849SAlexander Usyskin 	reg = heci_fwcsr_read(idev);
1478bf5e849SAlexander Usyskin 	buffer_depth = (u8)FIELD_GET(FW_CSR_CBD, reg);
1488bf5e849SAlexander Usyskin 	read_ptr = (u8)FIELD_GET(FW_CSR_CBRP, reg);
1498bf5e849SAlexander Usyskin 	write_ptr = (u8)FIELD_GET(FW_CSR_CBWP, reg);
1508bf5e849SAlexander Usyskin 	filled_slots = write_ptr - read_ptr;
1518bf5e849SAlexander Usyskin 
1528bf5e849SAlexander Usyskin 	/* check for overflow */
1538bf5e849SAlexander Usyskin 	if (filled_slots > buffer_depth)
1548bf5e849SAlexander Usyskin 		return -EOVERFLOW;
1558bf5e849SAlexander Usyskin 
1568bf5e849SAlexander Usyskin 	dev_dbg(&idev->dev, "filled_slots = %08x\n", filled_slots);
1578bf5e849SAlexander Usyskin 	return filled_slots;
1588bf5e849SAlexander Usyskin }
1598bf5e849SAlexander Usyskin 
1608bf5e849SAlexander Usyskin /**
1618bf5e849SAlexander Usyskin  * heci_irq_disable - disables heci device interrupts
1628bf5e849SAlexander Usyskin  * @idev: the device structure
1638bf5e849SAlexander Usyskin  * @reg: supplied hcsr register value
1648bf5e849SAlexander Usyskin  *
1658bf5e849SAlexander Usyskin  * disables heci device interrupts using supplied hcsr register value.
1668bf5e849SAlexander Usyskin  */
1678bf5e849SAlexander Usyskin static inline void heci_irq_disable(struct issei_device *idev, u32 reg)
1688bf5e849SAlexander Usyskin {
1698bf5e849SAlexander Usyskin 	reg &= ~H_CSR_IE;
1708bf5e849SAlexander Usyskin 	heci_hcsr_set(idev, reg);
1718bf5e849SAlexander Usyskin }
1728bf5e849SAlexander Usyskin 
1738bf5e849SAlexander Usyskin /**
1748bf5e849SAlexander Usyskin  * heci_irq_clear - clear and stop interrupts
1758bf5e849SAlexander Usyskin  * @idev: the device structure
1768bf5e849SAlexander Usyskin  * @reg: supplied hcsr register value
1778bf5e849SAlexander Usyskin  */
1788bf5e849SAlexander Usyskin static inline void heci_irq_clear(struct issei_device *idev, u32 reg)
1798bf5e849SAlexander Usyskin {
1808bf5e849SAlexander Usyskin 	if (heci_irq_src(reg))
1818bf5e849SAlexander Usyskin 		heci_hcsr_write(idev, reg);
1828bf5e849SAlexander Usyskin }
1838bf5e849SAlexander Usyskin 
1848bf5e849SAlexander Usyskin /**
1858bf5e849SAlexander Usyskin  * issei_heci_irq_clear - clear and stop interrupts
1868bf5e849SAlexander Usyskin  * @idev: the device structure
1878bf5e849SAlexander Usyskin  */
1888bf5e849SAlexander Usyskin static void issei_heci_irq_clear(struct issei_device *idev)
1898bf5e849SAlexander Usyskin {
1908bf5e849SAlexander Usyskin 	u32 reg = heci_hcsr_read(idev);
1918bf5e849SAlexander Usyskin 
1928bf5e849SAlexander Usyskin 	heci_irq_clear(idev, reg);
1938bf5e849SAlexander Usyskin }
1948bf5e849SAlexander Usyskin 
1958bf5e849SAlexander Usyskin /**
1968bf5e849SAlexander Usyskin  * issei_heci_irq_enable - enables heci device interrupts
1978bf5e849SAlexander Usyskin  * @idev: the device structure
1988bf5e849SAlexander Usyskin  */
1998bf5e849SAlexander Usyskin static void issei_heci_irq_enable(struct issei_device *idev)
2008bf5e849SAlexander Usyskin {
2018bf5e849SAlexander Usyskin 	u32 reg;
2028bf5e849SAlexander Usyskin 
2038bf5e849SAlexander Usyskin 	reg = heci_hcsr_read(idev) | H_CSR_IE;
2048bf5e849SAlexander Usyskin 	heci_hcsr_set(idev, reg);
2058bf5e849SAlexander Usyskin }
2068bf5e849SAlexander Usyskin 
2078bf5e849SAlexander Usyskin /**
2088bf5e849SAlexander Usyskin  * issei_heci_irq_disable - disables heci device interrupts
2098bf5e849SAlexander Usyskin  * @idev: the device structure
2108bf5e849SAlexander Usyskin  */
2118bf5e849SAlexander Usyskin static void issei_heci_irq_disable(struct issei_device *idev)
2128bf5e849SAlexander Usyskin {
2138bf5e849SAlexander Usyskin 	u32 reg = heci_hcsr_read(idev);
2148bf5e849SAlexander Usyskin 
2158bf5e849SAlexander Usyskin 	heci_irq_disable(idev, reg);
2168bf5e849SAlexander Usyskin }
2178bf5e849SAlexander Usyskin 
2188bf5e849SAlexander Usyskin static void issei_heci_irq_sync(struct issei_device *idev)
2198bf5e849SAlexander Usyskin {
2208bf5e849SAlexander Usyskin 	synchronize_irq(to_heci_hw(idev)->irq);
2218bf5e849SAlexander Usyskin }
2228bf5e849SAlexander Usyskin 
2238bf5e849SAlexander Usyskin /**
2248bf5e849SAlexander Usyskin  * issei_heci_hw_reset_release - release device from the reset
2258bf5e849SAlexander Usyskin  * @idev: the device structure
2268bf5e849SAlexander Usyskin  */
2278bf5e849SAlexander Usyskin static void issei_heci_hw_reset_release(struct issei_device *idev)
2288bf5e849SAlexander Usyskin {
2298bf5e849SAlexander Usyskin 	u32 reg = heci_hcsr_read(idev);
2308bf5e849SAlexander Usyskin 
2318bf5e849SAlexander Usyskin 	reg |= H_CSR_IG;
2328bf5e849SAlexander Usyskin 	reg &= ~H_CSR_RST;
2338bf5e849SAlexander Usyskin 	heci_hcsr_set(idev, reg);
2348bf5e849SAlexander Usyskin }
2358bf5e849SAlexander Usyskin 
2368bf5e849SAlexander Usyskin /**
2378bf5e849SAlexander Usyskin  * heci_hw_is_ready - check whether the hw has turned ready
2388bf5e849SAlexander Usyskin  * @idev: the device structure
2398bf5e849SAlexander Usyskin  *
2408bf5e849SAlexander Usyskin  * Return: bool
2418bf5e849SAlexander Usyskin  */
2428bf5e849SAlexander Usyskin static bool heci_hw_is_ready(struct issei_device *idev)
2438bf5e849SAlexander Usyskin {
2448bf5e849SAlexander Usyskin 	u32 reg = heci_fwcsr_read(idev);
2458bf5e849SAlexander Usyskin 
2468bf5e849SAlexander Usyskin 	return reg & FW_CSR_RDY;
2478bf5e849SAlexander Usyskin }
2488bf5e849SAlexander Usyskin 
2498bf5e849SAlexander Usyskin /**
2508bf5e849SAlexander Usyskin  * heci_hw_is_resetting - check whether the hw is in reset
2518bf5e849SAlexander Usyskin  * @idev: the device structure
2528bf5e849SAlexander Usyskin  *
2538bf5e849SAlexander Usyskin  * Return: bool
2548bf5e849SAlexander Usyskin  */
2558bf5e849SAlexander Usyskin static bool heci_hw_is_resetting(struct issei_device *idev)
2568bf5e849SAlexander Usyskin {
2578bf5e849SAlexander Usyskin 	u32 reg = heci_fwcsr_read(idev);
2588bf5e849SAlexander Usyskin 
2598bf5e849SAlexander Usyskin 	return reg & FW_CSR_RST;
2608bf5e849SAlexander Usyskin }
2618bf5e849SAlexander Usyskin 
2628bf5e849SAlexander Usyskin /**
2638bf5e849SAlexander Usyskin  * issei_heci_host_set_ready - enable device
2648bf5e849SAlexander Usyskin  * @idev: the device structure
2658bf5e849SAlexander Usyskin  */
2668bf5e849SAlexander Usyskin static void issei_heci_host_set_ready(struct issei_device *idev)
2678bf5e849SAlexander Usyskin {
2688bf5e849SAlexander Usyskin 	u32 reg = heci_hcsr_read(idev);
2698bf5e849SAlexander Usyskin 
2708bf5e849SAlexander Usyskin 	reg |= H_CSR_IE | H_CSR_IG | H_CSR_RDY;
2718bf5e849SAlexander Usyskin 	heci_hcsr_set(idev, reg);
2728bf5e849SAlexander Usyskin }
2738bf5e849SAlexander Usyskin 
2748bf5e849SAlexander Usyskin /**
2758bf5e849SAlexander Usyskin  * issei_heci_hw_reset - resets fw via heci csr register.
2768bf5e849SAlexander Usyskin  * @idev: the device structure
2778bf5e849SAlexander Usyskin  * @enable: if interrupt should be enabled after reset.
2788bf5e849SAlexander Usyskin  *
2798bf5e849SAlexander Usyskin  * Return: 0 on success an error code otherwise
2808bf5e849SAlexander Usyskin  */
2818bf5e849SAlexander Usyskin static int issei_heci_hw_reset(struct issei_device *idev, bool enable)
2828bf5e849SAlexander Usyskin {
2838bf5e849SAlexander Usyskin 	u32 reg;
2848bf5e849SAlexander Usyskin 
2858bf5e849SAlexander Usyskin 	if (enable)
2868bf5e849SAlexander Usyskin 		issei_heci_irq_enable(idev);
2878bf5e849SAlexander Usyskin 
2888bf5e849SAlexander Usyskin 	reg = heci_hcsr_read(idev);
2898bf5e849SAlexander Usyskin 	/*
2908bf5e849SAlexander Usyskin 	 * H_CSR_RST may be found lit before reset is started,
2918bf5e849SAlexander Usyskin 	 * for example if preceding reset flow hasn't completed.
2928bf5e849SAlexander Usyskin 	 * In that case asserting H_CSR_RST will be ignored, therefore
2938bf5e849SAlexander Usyskin 	 * we need to clean H_CSR_RST bit to start a successful reset sequence.
2948bf5e849SAlexander Usyskin 	 */
2958bf5e849SAlexander Usyskin 	if (reg & H_CSR_RST) {
2968bf5e849SAlexander Usyskin 		dev_warn(&idev->dev, "H_CSR_RST is set = 0x%08X", reg);
2978bf5e849SAlexander Usyskin 		reg &= ~H_CSR_RST;
2988bf5e849SAlexander Usyskin 		heci_hcsr_set(idev, reg);
2998bf5e849SAlexander Usyskin 		reg = heci_hcsr_read(idev);
3008bf5e849SAlexander Usyskin 	}
3018bf5e849SAlexander Usyskin 
3028bf5e849SAlexander Usyskin 	reg |= H_CSR_RST | H_CSR_IG | H_CSR_IS;
3038bf5e849SAlexander Usyskin 
3048bf5e849SAlexander Usyskin 	if (!enable)
3058bf5e849SAlexander Usyskin 		reg &= ~H_CSR_IE;
3068bf5e849SAlexander Usyskin 
3078bf5e849SAlexander Usyskin 	heci_hcsr_write(idev, reg);
3088bf5e849SAlexander Usyskin 
3098bf5e849SAlexander Usyskin 	/*
3108bf5e849SAlexander Usyskin 	 * Host reads the H_CSR once to ensure that the
3118bf5e849SAlexander Usyskin 	 * posted write to H_CSR completes.
3128bf5e849SAlexander Usyskin 	 */
3138bf5e849SAlexander Usyskin 	reg = heci_hcsr_read(idev);
3148bf5e849SAlexander Usyskin 
3158bf5e849SAlexander Usyskin 	if (!(reg & H_CSR_RST))
3168bf5e849SAlexander Usyskin 		dev_warn(&idev->dev, "H_CSR_RST is not set = 0x%08X", reg);
3178bf5e849SAlexander Usyskin 
3188bf5e849SAlexander Usyskin 	if (reg & H_CSR_RDY)
3198bf5e849SAlexander Usyskin 		dev_warn(&idev->dev, "H_CSR_RDY is not cleared 0x%08X", reg);
3208bf5e849SAlexander Usyskin 
3218bf5e849SAlexander Usyskin 	if (!enable)
3228bf5e849SAlexander Usyskin 		issei_heci_hw_reset_release(idev);
3238bf5e849SAlexander Usyskin 	return 0;
3248bf5e849SAlexander Usyskin }
3258bf5e849SAlexander Usyskin 
3268bf5e849SAlexander Usyskin /**
3278bf5e849SAlexander Usyskin  * issei_heci_irq_write_generate - generate interrupt to signal write completion.
3288bf5e849SAlexander Usyskin  * @idev: the device structure
3298bf5e849SAlexander Usyskin  *
3308bf5e849SAlexander Usyskin  * Return: 0 on success, -EIO if hardware is not ready and requires reset
3318bf5e849SAlexander Usyskin  */
3328bf5e849SAlexander Usyskin static int issei_heci_irq_write_generate(struct issei_device *idev)
3338bf5e849SAlexander Usyskin {
3348bf5e849SAlexander Usyskin 	struct issei_heci_hw *hw = to_heci_hw(idev);
3358bf5e849SAlexander Usyskin 
3368bf5e849SAlexander Usyskin 	scoped_guard(spinlock_irqsave, &hw->access_lock)
3378bf5e849SAlexander Usyskin 		heci_hcsr_set_hig(idev);
3388bf5e849SAlexander Usyskin 	if (!heci_hw_is_ready(idev))
3398bf5e849SAlexander Usyskin 		return -EIO;
3408bf5e849SAlexander Usyskin 	return 0;
3418bf5e849SAlexander Usyskin }
3428bf5e849SAlexander Usyskin 
3438bf5e849SAlexander Usyskin /**
3448bf5e849SAlexander Usyskin  * issei_heci_hw_config - initial hardware configuration.
3458bf5e849SAlexander Usyskin  * @idev: the device structure
3468bf5e849SAlexander Usyskin  *
3478bf5e849SAlexander Usyskin  * Return: 0 always
3488bf5e849SAlexander Usyskin  */
3498bf5e849SAlexander Usyskin static int issei_heci_hw_config(struct issei_device *idev)
3508bf5e849SAlexander Usyskin {
3518bf5e849SAlexander Usyskin 	struct issei_heci_hw *hw = to_heci_hw(idev);
3528bf5e849SAlexander Usyskin 	u32 reg;
3538bf5e849SAlexander Usyskin 
3548bf5e849SAlexander Usyskin 	/* Doesn't change in runtime */
3558bf5e849SAlexander Usyskin 	reg = heci_hcsr_read(idev);
3568bf5e849SAlexander Usyskin 	hw->hbuf_depth = FIELD_GET(H_CSR_CBD, reg);
3578bf5e849SAlexander Usyskin 
3588bf5e849SAlexander Usyskin 	return 0;
3598bf5e849SAlexander Usyskin }
3608bf5e849SAlexander Usyskin 
3618bf5e849SAlexander Usyskin /**
3628bf5e849SAlexander Usyskin  * heci_write_hbuf - write to hardware buffer
3638bf5e849SAlexander Usyskin  * @idev: the device structure
3648bf5e849SAlexander Usyskin  * @data: data to write
3658bf5e849SAlexander Usyskin  * @data_len: data size
3668bf5e849SAlexander Usyskin  *
3678bf5e849SAlexander Usyskin  * Return: 0 on success, <0 on error
3688bf5e849SAlexander Usyskin  */
3698bf5e849SAlexander Usyskin static int heci_write_hbuf(struct issei_device *idev, const void *data, size_t data_len)
3708bf5e849SAlexander Usyskin {
3718bf5e849SAlexander Usyskin 	struct issei_heci_hw *hw = to_heci_hw(idev);
3728bf5e849SAlexander Usyskin 
3738bf5e849SAlexander Usyskin 	if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) {
374*28449741SNathan Chancellor 		dev_err(&idev->dev, "Data size %zu not aligned to slot size %zu\n",
3758bf5e849SAlexander Usyskin 			data_len, CB_SLOT_SIZE);
3768bf5e849SAlexander Usyskin 		return -EINVAL;
3778bf5e849SAlexander Usyskin 	}
3788bf5e849SAlexander Usyskin 
3798bf5e849SAlexander Usyskin 	scoped_guard(spinlock_irqsave, &hw->access_lock) {
3808bf5e849SAlexander Usyskin 		const u32 *reg_buf = data;
3818bf5e849SAlexander Usyskin 		size_t i;
3828bf5e849SAlexander Usyskin 
3838bf5e849SAlexander Usyskin 		for (i = 0; i < data_len / CB_SLOT_SIZE; i++)
3848bf5e849SAlexander Usyskin 			heci_hcbww_write(hw, reg_buf[i]);
3858bf5e849SAlexander Usyskin 	}
3868bf5e849SAlexander Usyskin 
3878bf5e849SAlexander Usyskin 	return issei_heci_irq_write_generate(idev);
3888bf5e849SAlexander Usyskin }
3898bf5e849SAlexander Usyskin 
3908bf5e849SAlexander Usyskin /**
3918bf5e849SAlexander Usyskin  * heci_read_hbuf - read data from hardware buffer
3928bf5e849SAlexander Usyskin  * @idev: the device structure
3938bf5e849SAlexander Usyskin  * @data: buffer to store read data
3948bf5e849SAlexander Usyskin  * @data_len: buffer size
3958bf5e849SAlexander Usyskin  *
3968bf5e849SAlexander Usyskin  * Return: 0 on success, <0 on error
3978bf5e849SAlexander Usyskin  */
3988bf5e849SAlexander Usyskin static int heci_read_hbuf(struct issei_device *idev, void *data, size_t data_len)
3998bf5e849SAlexander Usyskin {
4008bf5e849SAlexander Usyskin 	struct issei_heci_hw *hw = to_heci_hw(idev);
4018bf5e849SAlexander Usyskin 	u32 *reg_buf = data;
4028bf5e849SAlexander Usyskin 
4038bf5e849SAlexander Usyskin 	if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) {
404*28449741SNathan Chancellor 		dev_err(&idev->dev, "Data size %zu not aligned to slot size %zu\n",
4058bf5e849SAlexander Usyskin 			data_len, CB_SLOT_SIZE);
4068bf5e849SAlexander Usyskin 		return -EINVAL;
4078bf5e849SAlexander Usyskin 	}
4088bf5e849SAlexander Usyskin 
4098bf5e849SAlexander Usyskin 	scoped_guard(spinlock_irqsave, &hw->access_lock) {
4108bf5e849SAlexander Usyskin 		for (; data_len >= CB_SLOT_SIZE; data_len -= CB_SLOT_SIZE)
4118bf5e849SAlexander Usyskin 			*reg_buf++ = heci_fwcbrw_read(hw);
4128bf5e849SAlexander Usyskin 	}
4138bf5e849SAlexander Usyskin 	return 0;
4148bf5e849SAlexander Usyskin }
4158bf5e849SAlexander Usyskin 
4168bf5e849SAlexander Usyskin /**
4178bf5e849SAlexander Usyskin  * issei_heci_setup_message_send - send setup message to firmware
4188bf5e849SAlexander Usyskin  * @idev: the device structure
4198bf5e849SAlexander Usyskin  *
4208bf5e849SAlexander Usyskin  * Return: 0 on success, <0 on error
4218bf5e849SAlexander Usyskin  */
4228bf5e849SAlexander Usyskin static int issei_heci_setup_message_send(struct issei_device *idev)
4238bf5e849SAlexander Usyskin {
4248bf5e849SAlexander Usyskin 	struct ham_setup_shared_memory_req req = {
4258bf5e849SAlexander Usyskin 		.msg_id = HAM_CB_MESSAGE_ID_REQ,
4268bf5e849SAlexander Usyskin 		.ver = HAM_CB_MESSAGE_VER,
4278bf5e849SAlexander Usyskin 		.reserved = 0,
4288bf5e849SAlexander Usyskin 		.buffer_physical_address = idev->dma.daddr,
4298bf5e849SAlexander Usyskin 		.host_to_fw_section_length = idev->dma.length.h2f,
4308bf5e849SAlexander Usyskin 		.fw_to_host_section_length = idev->dma.length.f2h,
4318bf5e849SAlexander Usyskin 		.control_length = idev->dma.length.ctl
4328bf5e849SAlexander Usyskin 	};
4338bf5e849SAlexander Usyskin 	int ret;
4348bf5e849SAlexander Usyskin 
4358bf5e849SAlexander Usyskin 	ret = heci_write_hbuf(idev, &req, sizeof(req));
4368bf5e849SAlexander Usyskin 	if (ret)
4378bf5e849SAlexander Usyskin 		dev_err(&idev->dev, "Shared memory req write failed ret = %d\n", ret);
4388bf5e849SAlexander Usyskin 
4398bf5e849SAlexander Usyskin 	return ret;
4408bf5e849SAlexander Usyskin }
4418bf5e849SAlexander Usyskin 
4428bf5e849SAlexander Usyskin /**
4438bf5e849SAlexander Usyskin  * issei_heci_setup_message_recv - receive setup message response from firmware
4448bf5e849SAlexander Usyskin  * @idev: the device structure
4458bf5e849SAlexander Usyskin  *
4468bf5e849SAlexander Usyskin  * Return: 0 on success, <0 on error
4478bf5e849SAlexander Usyskin  */
4488bf5e849SAlexander Usyskin static int issei_heci_setup_message_recv(struct issei_device *idev)
4498bf5e849SAlexander Usyskin {
4508bf5e849SAlexander Usyskin 	struct ham_setup_shared_memory_res res;
4518bf5e849SAlexander Usyskin 	int ret;
4528bf5e849SAlexander Usyskin 
4538bf5e849SAlexander Usyskin 	if (heci_count_full_read_slots(idev) != sizeof(res) / CB_SLOT_SIZE) {
4548bf5e849SAlexander Usyskin 		dev_dbg(&idev->dev, "Setup response is not fully received\n");
4558bf5e849SAlexander Usyskin 		return -ENODATA;
4568bf5e849SAlexander Usyskin 	}
4578bf5e849SAlexander Usyskin 
4588bf5e849SAlexander Usyskin 	ret = heci_read_hbuf(idev, &res, sizeof(res));
4598bf5e849SAlexander Usyskin 	if (ret) {
4608bf5e849SAlexander Usyskin 		dev_err(&idev->dev, "Shared memory res read failed ret = %d\n", ret);
4618bf5e849SAlexander Usyskin 		return ret;
4628bf5e849SAlexander Usyskin 	}
4638bf5e849SAlexander Usyskin 
4648bf5e849SAlexander Usyskin 	if (res.msg_id != HAM_CB_MESSAGE_ID_RES) {
4658bf5e849SAlexander Usyskin 		dev_err(&idev->dev, "Shared memory res header 0x%x != 0x%x\n",
4668bf5e849SAlexander Usyskin 			res.msg_id, HAM_CB_MESSAGE_ID_RES);
4678bf5e849SAlexander Usyskin 		return -EPROTO;
4688bf5e849SAlexander Usyskin 	}
4698bf5e849SAlexander Usyskin 	if (res.status != 0) {
4708bf5e849SAlexander Usyskin 		dev_err(&idev->dev, "Shared memory res status %d != 0\n", res.status);
4718bf5e849SAlexander Usyskin 		return -EPROTO;
4728bf5e849SAlexander Usyskin 	}
4738bf5e849SAlexander Usyskin 
4748bf5e849SAlexander Usyskin 	return 0;
4758bf5e849SAlexander Usyskin }
4768bf5e849SAlexander Usyskin 
4778bf5e849SAlexander Usyskin static const struct issei_hw_ops hw_heci_ops = {
4788bf5e849SAlexander Usyskin 	.irq_clear = issei_heci_irq_clear,
4798bf5e849SAlexander Usyskin 	.irq_enable = issei_heci_irq_enable,
4808bf5e849SAlexander Usyskin 	.irq_disable = issei_heci_irq_disable,
4818bf5e849SAlexander Usyskin 	.irq_sync = issei_heci_irq_sync,
4828bf5e849SAlexander Usyskin 
4838bf5e849SAlexander Usyskin 	.hw_reset = issei_heci_hw_reset,
4848bf5e849SAlexander Usyskin 	.hw_config = issei_heci_hw_config,
4858bf5e849SAlexander Usyskin 	.hw_reset_release = issei_heci_hw_reset_release,
4868bf5e849SAlexander Usyskin 	.host_set_ready = issei_heci_host_set_ready,
4878bf5e849SAlexander Usyskin 
4888bf5e849SAlexander Usyskin 	.hw_is_ready = heci_hw_is_ready,
4898bf5e849SAlexander Usyskin 
4908bf5e849SAlexander Usyskin 	.setup_message_send = issei_heci_setup_message_send,
4918bf5e849SAlexander Usyskin 	.setup_message_recv = issei_heci_setup_message_recv,
4928bf5e849SAlexander Usyskin 	.irq_write_generate = issei_heci_irq_write_generate,
4938bf5e849SAlexander Usyskin };
4948bf5e849SAlexander Usyskin 
4958bf5e849SAlexander Usyskin const struct issei_hw_ops *issei_heci_get_ops(void)
4968bf5e849SAlexander Usyskin {
4978bf5e849SAlexander Usyskin 	return &hw_heci_ops;
4988bf5e849SAlexander Usyskin }
4998bf5e849SAlexander Usyskin 
5008bf5e849SAlexander Usyskin irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id)
5018bf5e849SAlexander Usyskin {
5028bf5e849SAlexander Usyskin 	struct issei_device *idev = dev_id;
5038bf5e849SAlexander Usyskin 	struct issei_heci_hw *hw = to_heci_hw(idev);
5048bf5e849SAlexander Usyskin 	u32 reg;
5058bf5e849SAlexander Usyskin 
5068bf5e849SAlexander Usyskin 	reg = heci_hcsr_read(idev);
5078bf5e849SAlexander Usyskin 	if (!heci_irq_src(reg))
5088bf5e849SAlexander Usyskin 		return IRQ_NONE;
5098bf5e849SAlexander Usyskin 
5108bf5e849SAlexander Usyskin 	scoped_guard(spinlock_irqsave, &hw->access_lock) {
5118bf5e849SAlexander Usyskin 		reg = heci_hcsr_read(idev);
5128bf5e849SAlexander Usyskin 		heci_irq_clear(idev, reg);
5138bf5e849SAlexander Usyskin 
5148bf5e849SAlexander Usyskin 		if (heci_hw_is_resetting(idev))
5158bf5e849SAlexander Usyskin 			heci_hcsr_set_hig(idev);
5168bf5e849SAlexander Usyskin 	}
5178bf5e849SAlexander Usyskin 
5188bf5e849SAlexander Usyskin 	dev_dbg(&idev->dev, "interrupt source 0x%08X\n", heci_irq_src(reg));
5198bf5e849SAlexander Usyskin 
5208bf5e849SAlexander Usyskin 	issei_poke_process_thread(idev);
5218bf5e849SAlexander Usyskin 
5228bf5e849SAlexander Usyskin 	return IRQ_HANDLED;
5238bf5e849SAlexander Usyskin }
5248bf5e849SAlexander Usyskin 
5258bf5e849SAlexander Usyskin static const struct hw_heci_cfg hw_heci_pch_cfg = {
5268bf5e849SAlexander Usyskin 	.dma_length.h2f = SZ_64K,
5278bf5e849SAlexander Usyskin 	.dma_length.f2h = SZ_64K,
5288bf5e849SAlexander Usyskin 	.dma_length.ctl = SZ_4K,
5298bf5e849SAlexander Usyskin };
5308bf5e849SAlexander Usyskin 
5318bf5e849SAlexander Usyskin const struct hw_heci_cfg *issei_heci_get_cfg(kernel_ulong_t idx)
5328bf5e849SAlexander Usyskin {
5338bf5e849SAlexander Usyskin 	return &hw_heci_pch_cfg;
5348bf5e849SAlexander Usyskin }
5358bf5e849SAlexander Usyskin 
5368bf5e849SAlexander Usyskin /**
5378bf5e849SAlexander Usyskin  * issei_heci_dev_init - initializes the issei device structure with hw_heci
5388bf5e849SAlexander Usyskin  * @idev: device structure
5398bf5e849SAlexander Usyskin  * @mem_addr: memory address on bar
5408bf5e849SAlexander Usyskin  * @cfg: per device generation config
5418bf5e849SAlexander Usyskin  */
5428bf5e849SAlexander Usyskin void issei_heci_dev_init(struct issei_device *idev,
5438bf5e849SAlexander Usyskin 			 void __iomem *mem_addr, const struct hw_heci_cfg *cfg)
5448bf5e849SAlexander Usyskin {
5458bf5e849SAlexander Usyskin 	struct issei_heci_hw *hw = to_heci_hw(idev);
5468bf5e849SAlexander Usyskin 
5478bf5e849SAlexander Usyskin 	spin_lock_init(&hw->access_lock);
5488bf5e849SAlexander Usyskin 	hw->mem_addr = mem_addr;
5498bf5e849SAlexander Usyskin 	hw->cfg = cfg;
5508bf5e849SAlexander Usyskin }
551