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