18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * H/W layer of ISHTP provider device (ISH) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2014-2016, Intel Corporation. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/sched.h> 98c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 128c2ecf20Sopenharmony_ci#include "client.h" 138c2ecf20Sopenharmony_ci#include "hw-ish.h" 148c2ecf20Sopenharmony_ci#include "hbm.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* For FW reset flow */ 178c2ecf20Sopenharmony_cistatic struct work_struct fw_reset_work; 188c2ecf20Sopenharmony_cistatic struct ishtp_device *ishtp_dev; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/** 218c2ecf20Sopenharmony_ci * ish_reg_read() - Read register 228c2ecf20Sopenharmony_ci * @dev: ISHTP device pointer 238c2ecf20Sopenharmony_ci * @offset: Register offset 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Read 32 bit register at a given offset 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * Return: Read register value 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_cistatic inline uint32_t ish_reg_read(const struct ishtp_device *dev, 308c2ecf20Sopenharmony_ci unsigned long offset) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct ish_hw *hw = to_ish_hw(dev); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci return readl(hw->mem_addr + offset); 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/** 388c2ecf20Sopenharmony_ci * ish_reg_write() - Write register 398c2ecf20Sopenharmony_ci * @dev: ISHTP device pointer 408c2ecf20Sopenharmony_ci * @offset: Register offset 418c2ecf20Sopenharmony_ci * @value: Value to write 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * Writes 32 bit register at a give offset 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_cistatic inline void ish_reg_write(struct ishtp_device *dev, 468c2ecf20Sopenharmony_ci unsigned long offset, 478c2ecf20Sopenharmony_ci uint32_t value) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct ish_hw *hw = to_ish_hw(dev); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci writel(value, hw->mem_addr + offset); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/** 558c2ecf20Sopenharmony_ci * _ish_read_fw_sts_reg() - Read FW status register 568c2ecf20Sopenharmony_ci * @dev: ISHTP device pointer 578c2ecf20Sopenharmony_ci * 588c2ecf20Sopenharmony_ci * Read FW status register 598c2ecf20Sopenharmony_ci * 608c2ecf20Sopenharmony_ci * Return: Read register value 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_cistatic inline uint32_t _ish_read_fw_sts_reg(struct ishtp_device *dev) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci return ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/** 688c2ecf20Sopenharmony_ci * check_generated_interrupt() - Check if ISH interrupt 698c2ecf20Sopenharmony_ci * @dev: ISHTP device pointer 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * Check if an interrupt was generated for ISH 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * Return: Read true or false 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_cistatic bool check_generated_interrupt(struct ishtp_device *dev) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci bool interrupt_generated = true; 788c2ecf20Sopenharmony_ci uint32_t pisr_val = 0; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (dev->pdev->device == CHV_DEVICE_ID) { 818c2ecf20Sopenharmony_ci pisr_val = ish_reg_read(dev, IPC_REG_PISR_CHV_AB); 828c2ecf20Sopenharmony_ci interrupt_generated = 838c2ecf20Sopenharmony_ci IPC_INT_FROM_ISH_TO_HOST_CHV_AB(pisr_val); 848c2ecf20Sopenharmony_ci } else { 858c2ecf20Sopenharmony_ci pisr_val = ish_reg_read(dev, IPC_REG_PISR_BXT); 868c2ecf20Sopenharmony_ci interrupt_generated = !!pisr_val; 878c2ecf20Sopenharmony_ci /* only busy-clear bit is RW, others are RO */ 888c2ecf20Sopenharmony_ci if (pisr_val) 898c2ecf20Sopenharmony_ci ish_reg_write(dev, IPC_REG_PISR_BXT, pisr_val); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return interrupt_generated; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/** 968c2ecf20Sopenharmony_ci * ish_is_input_ready() - Check if FW ready for RX 978c2ecf20Sopenharmony_ci * @dev: ISHTP device pointer 988c2ecf20Sopenharmony_ci * 998c2ecf20Sopenharmony_ci * Check if ISH FW is ready for receiving data 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * Return: Read true or false 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_cistatic bool ish_is_input_ready(struct ishtp_device *dev) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci uint32_t doorbell_val; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci doorbell_val = ish_reg_read(dev, IPC_REG_HOST2ISH_DRBL); 1088c2ecf20Sopenharmony_ci return !IPC_IS_BUSY(doorbell_val); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/** 1128c2ecf20Sopenharmony_ci * set_host_ready() - Indicate host ready 1138c2ecf20Sopenharmony_ci * @dev: ISHTP device pointer 1148c2ecf20Sopenharmony_ci * 1158c2ecf20Sopenharmony_ci * Set host ready indication to FW 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_cistatic void set_host_ready(struct ishtp_device *dev) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci if (dev->pdev->device == CHV_DEVICE_ID) { 1208c2ecf20Sopenharmony_ci if (dev->pdev->revision == REVISION_ID_CHT_A0 || 1218c2ecf20Sopenharmony_ci (dev->pdev->revision & REVISION_ID_SI_MASK) == 1228c2ecf20Sopenharmony_ci REVISION_ID_CHT_Ax_SI) 1238c2ecf20Sopenharmony_ci ish_reg_write(dev, IPC_REG_HOST_COMM, 0x81); 1248c2ecf20Sopenharmony_ci else if (dev->pdev->revision == REVISION_ID_CHT_B0 || 1258c2ecf20Sopenharmony_ci (dev->pdev->revision & REVISION_ID_SI_MASK) == 1268c2ecf20Sopenharmony_ci REVISION_ID_CHT_Bx_SI || 1278c2ecf20Sopenharmony_ci (dev->pdev->revision & REVISION_ID_SI_MASK) == 1288c2ecf20Sopenharmony_ci REVISION_ID_CHT_Kx_SI || 1298c2ecf20Sopenharmony_ci (dev->pdev->revision & REVISION_ID_SI_MASK) == 1308c2ecf20Sopenharmony_ci REVISION_ID_CHT_Dx_SI) { 1318c2ecf20Sopenharmony_ci uint32_t host_comm_val; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci host_comm_val = ish_reg_read(dev, IPC_REG_HOST_COMM); 1348c2ecf20Sopenharmony_ci host_comm_val |= IPC_HOSTCOMM_INT_EN_BIT_CHV_AB | 0x81; 1358c2ecf20Sopenharmony_ci ish_reg_write(dev, IPC_REG_HOST_COMM, host_comm_val); 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci } else { 1388c2ecf20Sopenharmony_ci uint32_t host_pimr_val; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci host_pimr_val = ish_reg_read(dev, IPC_REG_PIMR_BXT); 1418c2ecf20Sopenharmony_ci host_pimr_val |= IPC_PIMR_INT_EN_BIT_BXT; 1428c2ecf20Sopenharmony_ci /* 1438c2ecf20Sopenharmony_ci * disable interrupt generated instead of 1448c2ecf20Sopenharmony_ci * RX_complete_msg 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_ci host_pimr_val &= ~IPC_HOST2ISH_BUSYCLEAR_MASK_BIT; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci ish_reg_write(dev, IPC_REG_PIMR_BXT, host_pimr_val); 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/** 1538c2ecf20Sopenharmony_ci * ishtp_fw_is_ready() - Check if FW ready 1548c2ecf20Sopenharmony_ci * @dev: ISHTP device pointer 1558c2ecf20Sopenharmony_ci * 1568c2ecf20Sopenharmony_ci * Check if ISH FW is ready 1578c2ecf20Sopenharmony_ci * 1588c2ecf20Sopenharmony_ci * Return: Read true or false 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_cistatic bool ishtp_fw_is_ready(struct ishtp_device *dev) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci uint32_t ish_status = _ish_read_fw_sts_reg(dev); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return IPC_IS_ISH_ILUP(ish_status) && 1658c2ecf20Sopenharmony_ci IPC_IS_ISH_ISHTP_READY(ish_status); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/** 1698c2ecf20Sopenharmony_ci * ish_set_host_rdy() - Indicate host ready 1708c2ecf20Sopenharmony_ci * @dev: ISHTP device pointer 1718c2ecf20Sopenharmony_ci * 1728c2ecf20Sopenharmony_ci * Set host ready indication to FW 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_cistatic void ish_set_host_rdy(struct ishtp_device *dev) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci uint32_t host_status = ish_reg_read(dev, IPC_REG_HOST_COMM); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci IPC_SET_HOST_READY(host_status); 1798c2ecf20Sopenharmony_ci ish_reg_write(dev, IPC_REG_HOST_COMM, host_status); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/** 1838c2ecf20Sopenharmony_ci * ish_clr_host_rdy() - Indicate host not ready 1848c2ecf20Sopenharmony_ci * @dev: ISHTP device pointer 1858c2ecf20Sopenharmony_ci * 1868c2ecf20Sopenharmony_ci * Send host not ready indication to FW 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_cistatic void ish_clr_host_rdy(struct ishtp_device *dev) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci uint32_t host_status = ish_reg_read(dev, IPC_REG_HOST_COMM); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci IPC_CLEAR_HOST_READY(host_status); 1938c2ecf20Sopenharmony_ci ish_reg_write(dev, IPC_REG_HOST_COMM, host_status); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/** 1978c2ecf20Sopenharmony_ci * _ishtp_read_hdr() - Read message header 1988c2ecf20Sopenharmony_ci * @dev: ISHTP device pointer 1998c2ecf20Sopenharmony_ci * 2008c2ecf20Sopenharmony_ci * Read header of 32bit length 2018c2ecf20Sopenharmony_ci * 2028c2ecf20Sopenharmony_ci * Return: Read register value 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_cistatic uint32_t _ishtp_read_hdr(const struct ishtp_device *dev) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci return ish_reg_read(dev, IPC_REG_ISH2HOST_MSG); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/** 2108c2ecf20Sopenharmony_ci * _ishtp_read - Read message 2118c2ecf20Sopenharmony_ci * @dev: ISHTP device pointer 2128c2ecf20Sopenharmony_ci * @buffer: message buffer 2138c2ecf20Sopenharmony_ci * @buffer_length: length of message buffer 2148c2ecf20Sopenharmony_ci * 2158c2ecf20Sopenharmony_ci * Read message from FW 2168c2ecf20Sopenharmony_ci * 2178c2ecf20Sopenharmony_ci * Return: Always 0 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_cistatic int _ishtp_read(struct ishtp_device *dev, unsigned char *buffer, 2208c2ecf20Sopenharmony_ci unsigned long buffer_length) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci uint32_t i; 2238c2ecf20Sopenharmony_ci uint32_t *r_buf = (uint32_t *)buffer; 2248c2ecf20Sopenharmony_ci uint32_t msg_offs; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci msg_offs = IPC_REG_ISH2HOST_MSG + sizeof(struct ishtp_msg_hdr); 2278c2ecf20Sopenharmony_ci for (i = 0; i < buffer_length; i += sizeof(uint32_t)) 2288c2ecf20Sopenharmony_ci *r_buf++ = ish_reg_read(dev, msg_offs + i); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/** 2348c2ecf20Sopenharmony_ci * write_ipc_from_queue() - try to write ipc msg from Tx queue to device 2358c2ecf20Sopenharmony_ci * @dev: ishtp device pointer 2368c2ecf20Sopenharmony_ci * 2378c2ecf20Sopenharmony_ci * Check if DRBL is cleared. if it is - write the first IPC msg, then call 2388c2ecf20Sopenharmony_ci * the callback function (unless it's NULL) 2398c2ecf20Sopenharmony_ci * 2408c2ecf20Sopenharmony_ci * Return: 0 for success else failure code 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_cistatic int write_ipc_from_queue(struct ishtp_device *dev) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct wr_msg_ctl_info *ipc_link; 2458c2ecf20Sopenharmony_ci unsigned long length; 2468c2ecf20Sopenharmony_ci unsigned long rem; 2478c2ecf20Sopenharmony_ci unsigned long flags; 2488c2ecf20Sopenharmony_ci uint32_t doorbell_val; 2498c2ecf20Sopenharmony_ci uint32_t *r_buf; 2508c2ecf20Sopenharmony_ci uint32_t reg_addr; 2518c2ecf20Sopenharmony_ci int i; 2528c2ecf20Sopenharmony_ci void (*ipc_send_compl)(void *); 2538c2ecf20Sopenharmony_ci void *ipc_send_compl_prm; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (dev->dev_state == ISHTP_DEV_DISABLED) 2568c2ecf20Sopenharmony_ci return -EINVAL; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->wr_processing_spinlock, flags); 2598c2ecf20Sopenharmony_ci if (!ish_is_input_ready(dev)) { 2608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); 2618c2ecf20Sopenharmony_ci return -EBUSY; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* 2658c2ecf20Sopenharmony_ci * if tx send list is empty - return 0; 2668c2ecf20Sopenharmony_ci * may happen, as RX_COMPLETE handler doesn't check list emptiness. 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_ci if (list_empty(&dev->wr_processing_list)) { 2698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci ipc_link = list_first_entry(&dev->wr_processing_list, 2748c2ecf20Sopenharmony_ci struct wr_msg_ctl_info, link); 2758c2ecf20Sopenharmony_ci /* first 4 bytes of the data is the doorbell value (IPC header) */ 2768c2ecf20Sopenharmony_ci length = ipc_link->length - sizeof(uint32_t); 2778c2ecf20Sopenharmony_ci doorbell_val = *(uint32_t *)ipc_link->inline_data; 2788c2ecf20Sopenharmony_ci r_buf = (uint32_t *)(ipc_link->inline_data + sizeof(uint32_t)); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* If sending MNG_SYNC_FW_CLOCK, update clock again */ 2818c2ecf20Sopenharmony_ci if (IPC_HEADER_GET_PROTOCOL(doorbell_val) == IPC_PROTOCOL_MNG && 2828c2ecf20Sopenharmony_ci IPC_HEADER_GET_MNG_CMD(doorbell_val) == MNG_SYNC_FW_CLOCK) { 2838c2ecf20Sopenharmony_ci uint64_t usec_system, usec_utc; 2848c2ecf20Sopenharmony_ci struct ipc_time_update_msg time_update; 2858c2ecf20Sopenharmony_ci struct time_sync_format ts_format; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci usec_system = ktime_to_us(ktime_get_boottime()); 2888c2ecf20Sopenharmony_ci usec_utc = ktime_to_us(ktime_get_real()); 2898c2ecf20Sopenharmony_ci ts_format.ts1_source = HOST_SYSTEM_TIME_USEC; 2908c2ecf20Sopenharmony_ci ts_format.ts2_source = HOST_UTC_TIME_USEC; 2918c2ecf20Sopenharmony_ci ts_format.reserved = 0; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci time_update.primary_host_time = usec_system; 2948c2ecf20Sopenharmony_ci time_update.secondary_host_time = usec_utc; 2958c2ecf20Sopenharmony_ci time_update.sync_info = ts_format; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci memcpy(r_buf, &time_update, 2988c2ecf20Sopenharmony_ci sizeof(struct ipc_time_update_msg)); 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci for (i = 0, reg_addr = IPC_REG_HOST2ISH_MSG; i < length >> 2; i++, 3028c2ecf20Sopenharmony_ci reg_addr += 4) 3038c2ecf20Sopenharmony_ci ish_reg_write(dev, reg_addr, r_buf[i]); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci rem = length & 0x3; 3068c2ecf20Sopenharmony_ci if (rem > 0) { 3078c2ecf20Sopenharmony_ci uint32_t reg = 0; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci memcpy(®, &r_buf[length >> 2], rem); 3108c2ecf20Sopenharmony_ci ish_reg_write(dev, reg_addr, reg); 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, doorbell_val); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* Flush writes to msg registers and doorbell */ 3158c2ecf20Sopenharmony_ci ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* Update IPC counters */ 3188c2ecf20Sopenharmony_ci ++dev->ipc_tx_cnt; 3198c2ecf20Sopenharmony_ci dev->ipc_tx_bytes_cnt += IPC_HEADER_GET_LENGTH(doorbell_val); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci ipc_send_compl = ipc_link->ipc_send_compl; 3228c2ecf20Sopenharmony_ci ipc_send_compl_prm = ipc_link->ipc_send_compl_prm; 3238c2ecf20Sopenharmony_ci list_del_init(&ipc_link->link); 3248c2ecf20Sopenharmony_ci list_add(&ipc_link->link, &dev->wr_free_list); 3258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* 3288c2ecf20Sopenharmony_ci * callback will be called out of spinlock, 3298c2ecf20Sopenharmony_ci * after ipc_link returned to free list 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_ci if (ipc_send_compl) 3328c2ecf20Sopenharmony_ci ipc_send_compl(ipc_send_compl_prm); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci return 0; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci/** 3388c2ecf20Sopenharmony_ci * write_ipc_to_queue() - write ipc msg to Tx queue 3398c2ecf20Sopenharmony_ci * @dev: ishtp device instance 3408c2ecf20Sopenharmony_ci * @ipc_send_compl: Send complete callback 3418c2ecf20Sopenharmony_ci * @ipc_send_compl_prm: Parameter to send in complete callback 3428c2ecf20Sopenharmony_ci * @msg: Pointer to message 3438c2ecf20Sopenharmony_ci * @length: Length of message 3448c2ecf20Sopenharmony_ci * 3458c2ecf20Sopenharmony_ci * Recived msg with IPC (and upper protocol) header and add it to the device 3468c2ecf20Sopenharmony_ci * Tx-to-write list then try to send the first IPC waiting msg 3478c2ecf20Sopenharmony_ci * (if DRBL is cleared) 3488c2ecf20Sopenharmony_ci * This function returns negative value for failure (means free list 3498c2ecf20Sopenharmony_ci * is empty, or msg too long) and 0 for success. 3508c2ecf20Sopenharmony_ci * 3518c2ecf20Sopenharmony_ci * Return: 0 for success else failure code 3528c2ecf20Sopenharmony_ci */ 3538c2ecf20Sopenharmony_cistatic int write_ipc_to_queue(struct ishtp_device *dev, 3548c2ecf20Sopenharmony_ci void (*ipc_send_compl)(void *), void *ipc_send_compl_prm, 3558c2ecf20Sopenharmony_ci unsigned char *msg, int length) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct wr_msg_ctl_info *ipc_link; 3588c2ecf20Sopenharmony_ci unsigned long flags; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (length > IPC_FULL_MSG_SIZE) 3618c2ecf20Sopenharmony_ci return -EMSGSIZE; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->wr_processing_spinlock, flags); 3648c2ecf20Sopenharmony_ci if (list_empty(&dev->wr_free_list)) { 3658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); 3668c2ecf20Sopenharmony_ci return -ENOMEM; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci ipc_link = list_first_entry(&dev->wr_free_list, 3698c2ecf20Sopenharmony_ci struct wr_msg_ctl_info, link); 3708c2ecf20Sopenharmony_ci list_del_init(&ipc_link->link); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci ipc_link->ipc_send_compl = ipc_send_compl; 3738c2ecf20Sopenharmony_ci ipc_link->ipc_send_compl_prm = ipc_send_compl_prm; 3748c2ecf20Sopenharmony_ci ipc_link->length = length; 3758c2ecf20Sopenharmony_ci memcpy(ipc_link->inline_data, msg, length); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci list_add_tail(&ipc_link->link, &dev->wr_processing_list); 3788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci write_ipc_from_queue(dev); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci return 0; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci/** 3868c2ecf20Sopenharmony_ci * ipc_send_mng_msg() - Send management message 3878c2ecf20Sopenharmony_ci * @dev: ishtp device instance 3888c2ecf20Sopenharmony_ci * @msg_code: Message code 3898c2ecf20Sopenharmony_ci * @msg: Pointer to message 3908c2ecf20Sopenharmony_ci * @size: Length of message 3918c2ecf20Sopenharmony_ci * 3928c2ecf20Sopenharmony_ci * Send management message to FW 3938c2ecf20Sopenharmony_ci * 3948c2ecf20Sopenharmony_ci * Return: 0 for success else failure code 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_cistatic int ipc_send_mng_msg(struct ishtp_device *dev, uint32_t msg_code, 3978c2ecf20Sopenharmony_ci void *msg, size_t size) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci unsigned char ipc_msg[IPC_FULL_MSG_SIZE]; 4008c2ecf20Sopenharmony_ci uint32_t drbl_val = IPC_BUILD_MNG_MSG(msg_code, size); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci memcpy(ipc_msg, &drbl_val, sizeof(uint32_t)); 4038c2ecf20Sopenharmony_ci memcpy(ipc_msg + sizeof(uint32_t), msg, size); 4048c2ecf20Sopenharmony_ci return write_ipc_to_queue(dev, NULL, NULL, ipc_msg, 4058c2ecf20Sopenharmony_ci sizeof(uint32_t) + size); 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci#define WAIT_FOR_FW_RDY 0x1 4098c2ecf20Sopenharmony_ci#define WAIT_FOR_INPUT_RDY 0x2 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci/** 4128c2ecf20Sopenharmony_ci * timed_wait_for_timeout() - wait special event with timeout 4138c2ecf20Sopenharmony_ci * @dev: ISHTP device pointer 4148c2ecf20Sopenharmony_ci * @condition: indicate the condition for waiting 4158c2ecf20Sopenharmony_ci * @timeinc: time slice for every wait cycle, in ms 4168c2ecf20Sopenharmony_ci * @timeout: time in ms for timeout 4178c2ecf20Sopenharmony_ci * 4188c2ecf20Sopenharmony_ci * This function will check special event to be ready in a loop, the loop 4198c2ecf20Sopenharmony_ci * period is specificd in timeinc. Wait timeout will causes failure. 4208c2ecf20Sopenharmony_ci * 4218c2ecf20Sopenharmony_ci * Return: 0 for success else failure code 4228c2ecf20Sopenharmony_ci */ 4238c2ecf20Sopenharmony_cistatic int timed_wait_for_timeout(struct ishtp_device *dev, int condition, 4248c2ecf20Sopenharmony_ci unsigned int timeinc, unsigned int timeout) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci bool complete = false; 4278c2ecf20Sopenharmony_ci int ret; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci do { 4308c2ecf20Sopenharmony_ci if (condition == WAIT_FOR_FW_RDY) { 4318c2ecf20Sopenharmony_ci complete = ishtp_fw_is_ready(dev); 4328c2ecf20Sopenharmony_ci } else if (condition == WAIT_FOR_INPUT_RDY) { 4338c2ecf20Sopenharmony_ci complete = ish_is_input_ready(dev); 4348c2ecf20Sopenharmony_ci } else { 4358c2ecf20Sopenharmony_ci ret = -EINVAL; 4368c2ecf20Sopenharmony_ci goto out; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (!complete) { 4408c2ecf20Sopenharmony_ci unsigned long left_time; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci left_time = msleep_interruptible(timeinc); 4438c2ecf20Sopenharmony_ci timeout -= (timeinc - left_time); 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci } while (!complete && timeout > 0); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (complete) 4488c2ecf20Sopenharmony_ci ret = 0; 4498c2ecf20Sopenharmony_ci else 4508c2ecf20Sopenharmony_ci ret = -EBUSY; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ciout: 4538c2ecf20Sopenharmony_ci return ret; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci#define TIME_SLICE_FOR_FW_RDY_MS 100 4578c2ecf20Sopenharmony_ci#define TIME_SLICE_FOR_INPUT_RDY_MS 100 4588c2ecf20Sopenharmony_ci#define TIMEOUT_FOR_FW_RDY_MS 2000 4598c2ecf20Sopenharmony_ci#define TIMEOUT_FOR_INPUT_RDY_MS 2000 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci/** 4628c2ecf20Sopenharmony_ci * ish_fw_reset_handler() - FW reset handler 4638c2ecf20Sopenharmony_ci * @dev: ishtp device pointer 4648c2ecf20Sopenharmony_ci * 4658c2ecf20Sopenharmony_ci * Handle FW reset 4668c2ecf20Sopenharmony_ci * 4678c2ecf20Sopenharmony_ci * Return: 0 for success else failure code 4688c2ecf20Sopenharmony_ci */ 4698c2ecf20Sopenharmony_cistatic int ish_fw_reset_handler(struct ishtp_device *dev) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci uint32_t reset_id; 4728c2ecf20Sopenharmony_ci unsigned long flags; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* Read reset ID */ 4758c2ecf20Sopenharmony_ci reset_id = ish_reg_read(dev, IPC_REG_ISH2HOST_MSG) & 0xFFFF; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* Clear IPC output queue */ 4788c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->wr_processing_spinlock, flags); 4798c2ecf20Sopenharmony_ci list_splice_init(&dev->wr_processing_list, &dev->wr_free_list); 4808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* ISHTP notification in IPC_RESET */ 4838c2ecf20Sopenharmony_ci ishtp_reset_handler(dev); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (!ish_is_input_ready(dev)) 4868c2ecf20Sopenharmony_ci timed_wait_for_timeout(dev, WAIT_FOR_INPUT_RDY, 4878c2ecf20Sopenharmony_ci TIME_SLICE_FOR_INPUT_RDY_MS, TIMEOUT_FOR_INPUT_RDY_MS); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* ISH FW is dead */ 4908c2ecf20Sopenharmony_ci if (!ish_is_input_ready(dev)) 4918c2ecf20Sopenharmony_ci return -EPIPE; 4928c2ecf20Sopenharmony_ci /* 4938c2ecf20Sopenharmony_ci * Set HOST2ISH.ILUP. Apparently we need this BEFORE sending 4948c2ecf20Sopenharmony_ci * RESET_NOTIFY_ACK - FW will be checking for it 4958c2ecf20Sopenharmony_ci */ 4968c2ecf20Sopenharmony_ci ish_set_host_rdy(dev); 4978c2ecf20Sopenharmony_ci /* Send RESET_NOTIFY_ACK (with reset_id) */ 4988c2ecf20Sopenharmony_ci ipc_send_mng_msg(dev, MNG_RESET_NOTIFY_ACK, &reset_id, 4998c2ecf20Sopenharmony_ci sizeof(uint32_t)); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* Wait for ISH FW'es ILUP and ISHTP_READY */ 5028c2ecf20Sopenharmony_ci timed_wait_for_timeout(dev, WAIT_FOR_FW_RDY, 5038c2ecf20Sopenharmony_ci TIME_SLICE_FOR_FW_RDY_MS, TIMEOUT_FOR_FW_RDY_MS); 5048c2ecf20Sopenharmony_ci if (!ishtp_fw_is_ready(dev)) { 5058c2ecf20Sopenharmony_ci /* ISH FW is dead */ 5068c2ecf20Sopenharmony_ci uint32_t ish_status; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci ish_status = _ish_read_fw_sts_reg(dev); 5098c2ecf20Sopenharmony_ci dev_err(dev->devc, 5108c2ecf20Sopenharmony_ci "[ishtp-ish]: completed reset, ISH is dead (FWSTS = %08X)\n", 5118c2ecf20Sopenharmony_ci ish_status); 5128c2ecf20Sopenharmony_ci return -ENODEV; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci return 0; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci#define TIMEOUT_FOR_HW_RDY_MS 300 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci/** 5208c2ecf20Sopenharmony_ci * ish_fw_reset_work_fn() - FW reset worker function 5218c2ecf20Sopenharmony_ci * @unused: not used 5228c2ecf20Sopenharmony_ci * 5238c2ecf20Sopenharmony_ci * Call ish_fw_reset_handler to complete FW reset 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_cistatic void fw_reset_work_fn(struct work_struct *unused) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci int rv; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci rv = ish_fw_reset_handler(ishtp_dev); 5308c2ecf20Sopenharmony_ci if (!rv) { 5318c2ecf20Sopenharmony_ci /* ISH is ILUP & ISHTP-ready. Restart ISHTP */ 5328c2ecf20Sopenharmony_ci msleep_interruptible(TIMEOUT_FOR_HW_RDY_MS); 5338c2ecf20Sopenharmony_ci ishtp_dev->recvd_hw_ready = 1; 5348c2ecf20Sopenharmony_ci wake_up_interruptible(&ishtp_dev->wait_hw_ready); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* ISHTP notification in IPC_RESET sequence completion */ 5378c2ecf20Sopenharmony_ci ishtp_reset_compl_handler(ishtp_dev); 5388c2ecf20Sopenharmony_ci } else 5398c2ecf20Sopenharmony_ci dev_err(ishtp_dev->devc, "[ishtp-ish]: FW reset failed (%d)\n", 5408c2ecf20Sopenharmony_ci rv); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci/** 5448c2ecf20Sopenharmony_ci * _ish_sync_fw_clock() -Sync FW clock with the OS clock 5458c2ecf20Sopenharmony_ci * @dev: ishtp device pointer 5468c2ecf20Sopenharmony_ci * 5478c2ecf20Sopenharmony_ci * Sync FW and OS time 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_cistatic void _ish_sync_fw_clock(struct ishtp_device *dev) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci static unsigned long prev_sync; 5528c2ecf20Sopenharmony_ci uint64_t usec; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (prev_sync && jiffies - prev_sync < 20 * HZ) 5558c2ecf20Sopenharmony_ci return; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci prev_sync = jiffies; 5588c2ecf20Sopenharmony_ci usec = ktime_to_us(ktime_get_boottime()); 5598c2ecf20Sopenharmony_ci ipc_send_mng_msg(dev, MNG_SYNC_FW_CLOCK, &usec, sizeof(uint64_t)); 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci/** 5638c2ecf20Sopenharmony_ci * recv_ipc() - Receive and process IPC management messages 5648c2ecf20Sopenharmony_ci * @dev: ishtp device instance 5658c2ecf20Sopenharmony_ci * @doorbell_val: doorbell value 5668c2ecf20Sopenharmony_ci * 5678c2ecf20Sopenharmony_ci * This function runs in ISR context. 5688c2ecf20Sopenharmony_ci * NOTE: Any other mng command than reset_notify and reset_notify_ack 5698c2ecf20Sopenharmony_ci * won't wake BH handler 5708c2ecf20Sopenharmony_ci */ 5718c2ecf20Sopenharmony_cistatic void recv_ipc(struct ishtp_device *dev, uint32_t doorbell_val) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci uint32_t mng_cmd; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci mng_cmd = IPC_HEADER_GET_MNG_CMD(doorbell_val); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci switch (mng_cmd) { 5788c2ecf20Sopenharmony_ci default: 5798c2ecf20Sopenharmony_ci break; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci case MNG_RX_CMPL_INDICATION: 5828c2ecf20Sopenharmony_ci if (dev->suspend_flag) { 5838c2ecf20Sopenharmony_ci dev->suspend_flag = 0; 5848c2ecf20Sopenharmony_ci wake_up_interruptible(&dev->suspend_wait); 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci if (dev->resume_flag) { 5878c2ecf20Sopenharmony_ci dev->resume_flag = 0; 5888c2ecf20Sopenharmony_ci wake_up_interruptible(&dev->resume_wait); 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci write_ipc_from_queue(dev); 5928c2ecf20Sopenharmony_ci break; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci case MNG_RESET_NOTIFY: 5958c2ecf20Sopenharmony_ci if (!ishtp_dev) { 5968c2ecf20Sopenharmony_ci ishtp_dev = dev; 5978c2ecf20Sopenharmony_ci INIT_WORK(&fw_reset_work, fw_reset_work_fn); 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci schedule_work(&fw_reset_work); 6008c2ecf20Sopenharmony_ci break; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci case MNG_RESET_NOTIFY_ACK: 6038c2ecf20Sopenharmony_ci dev->recvd_hw_ready = 1; 6048c2ecf20Sopenharmony_ci wake_up_interruptible(&dev->wait_hw_ready); 6058c2ecf20Sopenharmony_ci break; 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci/** 6108c2ecf20Sopenharmony_ci * ish_irq_handler() - ISH IRQ handler 6118c2ecf20Sopenharmony_ci * @irq: irq number 6128c2ecf20Sopenharmony_ci * @dev_id: ishtp device pointer 6138c2ecf20Sopenharmony_ci * 6148c2ecf20Sopenharmony_ci * ISH IRQ handler. If interrupt is generated and is for ISH it will process 6158c2ecf20Sopenharmony_ci * the interrupt. 6168c2ecf20Sopenharmony_ci */ 6178c2ecf20Sopenharmony_ciirqreturn_t ish_irq_handler(int irq, void *dev_id) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct ishtp_device *dev = dev_id; 6208c2ecf20Sopenharmony_ci uint32_t doorbell_val; 6218c2ecf20Sopenharmony_ci bool interrupt_generated; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci /* Check that it's interrupt from ISH (may be shared) */ 6248c2ecf20Sopenharmony_ci interrupt_generated = check_generated_interrupt(dev); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (!interrupt_generated) 6278c2ecf20Sopenharmony_ci return IRQ_NONE; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci doorbell_val = ish_reg_read(dev, IPC_REG_ISH2HOST_DRBL); 6308c2ecf20Sopenharmony_ci if (!IPC_IS_BUSY(doorbell_val)) 6318c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (dev->dev_state == ISHTP_DEV_DISABLED) 6348c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* Sanity check: IPC dgram length in header */ 6378c2ecf20Sopenharmony_ci if (IPC_HEADER_GET_LENGTH(doorbell_val) > IPC_PAYLOAD_SIZE) { 6388c2ecf20Sopenharmony_ci dev_err(dev->devc, 6398c2ecf20Sopenharmony_ci "IPC hdr - bad length: %u; dropped\n", 6408c2ecf20Sopenharmony_ci (unsigned int)IPC_HEADER_GET_LENGTH(doorbell_val)); 6418c2ecf20Sopenharmony_ci goto eoi; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci switch (IPC_HEADER_GET_PROTOCOL(doorbell_val)) { 6458c2ecf20Sopenharmony_ci default: 6468c2ecf20Sopenharmony_ci break; 6478c2ecf20Sopenharmony_ci case IPC_PROTOCOL_MNG: 6488c2ecf20Sopenharmony_ci recv_ipc(dev, doorbell_val); 6498c2ecf20Sopenharmony_ci break; 6508c2ecf20Sopenharmony_ci case IPC_PROTOCOL_ISHTP: 6518c2ecf20Sopenharmony_ci ishtp_recv(dev); 6528c2ecf20Sopenharmony_ci break; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cieoi: 6568c2ecf20Sopenharmony_ci /* Update IPC counters */ 6578c2ecf20Sopenharmony_ci ++dev->ipc_rx_cnt; 6588c2ecf20Sopenharmony_ci dev->ipc_rx_bytes_cnt += IPC_HEADER_GET_LENGTH(doorbell_val); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci ish_reg_write(dev, IPC_REG_ISH2HOST_DRBL, 0); 6618c2ecf20Sopenharmony_ci /* Flush write to doorbell */ 6628c2ecf20Sopenharmony_ci ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci/** 6688c2ecf20Sopenharmony_ci * ish_disable_dma() - disable dma communication between host and ISHFW 6698c2ecf20Sopenharmony_ci * @dev: ishtp device pointer 6708c2ecf20Sopenharmony_ci * 6718c2ecf20Sopenharmony_ci * Clear the dma enable bit and wait for dma inactive. 6728c2ecf20Sopenharmony_ci * 6738c2ecf20Sopenharmony_ci * Return: 0 for success else error code. 6748c2ecf20Sopenharmony_ci */ 6758c2ecf20Sopenharmony_ciint ish_disable_dma(struct ishtp_device *dev) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci unsigned int dma_delay; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci /* Clear the dma enable bit */ 6808c2ecf20Sopenharmony_ci ish_reg_write(dev, IPC_REG_ISH_RMP2, 0); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* wait for dma inactive */ 6838c2ecf20Sopenharmony_ci for (dma_delay = 0; dma_delay < MAX_DMA_DELAY && 6848c2ecf20Sopenharmony_ci _ish_read_fw_sts_reg(dev) & (IPC_ISH_IN_DMA); 6858c2ecf20Sopenharmony_ci dma_delay += 5) 6868c2ecf20Sopenharmony_ci mdelay(5); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci if (dma_delay >= MAX_DMA_DELAY) { 6898c2ecf20Sopenharmony_ci dev_err(dev->devc, 6908c2ecf20Sopenharmony_ci "Wait for DMA inactive timeout\n"); 6918c2ecf20Sopenharmony_ci return -EBUSY; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci return 0; 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci/** 6988c2ecf20Sopenharmony_ci * ish_wakeup() - wakeup ishfw from waiting-for-host state 6998c2ecf20Sopenharmony_ci * @dev: ishtp device pointer 7008c2ecf20Sopenharmony_ci * 7018c2ecf20Sopenharmony_ci * Set the dma enable bit and send a void message to FW, 7028c2ecf20Sopenharmony_ci * it wil wakeup FW from waiting-for-host state. 7038c2ecf20Sopenharmony_ci */ 7048c2ecf20Sopenharmony_cistatic void ish_wakeup(struct ishtp_device *dev) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci /* Set dma enable bit */ 7078c2ecf20Sopenharmony_ci ish_reg_write(dev, IPC_REG_ISH_RMP2, IPC_RMP2_DMA_ENABLED); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci /* 7108c2ecf20Sopenharmony_ci * Send 0 IPC message so that ISH FW wakes up if it was already 7118c2ecf20Sopenharmony_ci * asleep. 7128c2ecf20Sopenharmony_ci */ 7138c2ecf20Sopenharmony_ci ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, IPC_DRBL_BUSY_BIT); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci /* Flush writes to doorbell and REMAP2 */ 7168c2ecf20Sopenharmony_ci ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci/** 7208c2ecf20Sopenharmony_ci * _ish_hw_reset() - HW reset 7218c2ecf20Sopenharmony_ci * @dev: ishtp device pointer 7228c2ecf20Sopenharmony_ci * 7238c2ecf20Sopenharmony_ci * Reset ISH HW to recover if any error 7248c2ecf20Sopenharmony_ci * 7258c2ecf20Sopenharmony_ci * Return: 0 for success else error fault code 7268c2ecf20Sopenharmony_ci */ 7278c2ecf20Sopenharmony_cistatic int _ish_hw_reset(struct ishtp_device *dev) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci struct pci_dev *pdev = dev->pdev; 7308c2ecf20Sopenharmony_ci int rv; 7318c2ecf20Sopenharmony_ci uint16_t csr; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (!pdev) 7348c2ecf20Sopenharmony_ci return -ENODEV; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci rv = pci_reset_function(pdev); 7378c2ecf20Sopenharmony_ci if (!rv) 7388c2ecf20Sopenharmony_ci dev->dev_state = ISHTP_DEV_RESETTING; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (!pdev->pm_cap) { 7418c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't reset - no PM caps\n"); 7428c2ecf20Sopenharmony_ci return -EINVAL; 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci /* Disable dma communication between FW and host */ 7468c2ecf20Sopenharmony_ci if (ish_disable_dma(dev)) { 7478c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 7488c2ecf20Sopenharmony_ci "Can't reset - stuck with DMA in-progress\n"); 7498c2ecf20Sopenharmony_ci return -EBUSY; 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &csr); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci csr &= ~PCI_PM_CTRL_STATE_MASK; 7558c2ecf20Sopenharmony_ci csr |= PCI_D3hot; 7568c2ecf20Sopenharmony_ci pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, csr); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci mdelay(pdev->d3hot_delay); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci csr &= ~PCI_PM_CTRL_STATE_MASK; 7618c2ecf20Sopenharmony_ci csr |= PCI_D0; 7628c2ecf20Sopenharmony_ci pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, csr); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* Now we can enable ISH DMA operation and wakeup ISHFW */ 7658c2ecf20Sopenharmony_ci ish_wakeup(dev); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci return 0; 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci/** 7718c2ecf20Sopenharmony_ci * _ish_ipc_reset() - IPC reset 7728c2ecf20Sopenharmony_ci * @dev: ishtp device pointer 7738c2ecf20Sopenharmony_ci * 7748c2ecf20Sopenharmony_ci * Resets host and fw IPC and upper layers 7758c2ecf20Sopenharmony_ci * 7768c2ecf20Sopenharmony_ci * Return: 0 for success else error fault code 7778c2ecf20Sopenharmony_ci */ 7788c2ecf20Sopenharmony_cistatic int _ish_ipc_reset(struct ishtp_device *dev) 7798c2ecf20Sopenharmony_ci{ 7808c2ecf20Sopenharmony_ci struct ipc_rst_payload_type ipc_mng_msg; 7818c2ecf20Sopenharmony_ci int rv = 0; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci ipc_mng_msg.reset_id = 1; 7848c2ecf20Sopenharmony_ci ipc_mng_msg.reserved = 0; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci set_host_ready(dev); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci /* Clear the incoming doorbell */ 7898c2ecf20Sopenharmony_ci ish_reg_write(dev, IPC_REG_ISH2HOST_DRBL, 0); 7908c2ecf20Sopenharmony_ci /* Flush write to doorbell */ 7918c2ecf20Sopenharmony_ci ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci dev->recvd_hw_ready = 0; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci /* send message */ 7968c2ecf20Sopenharmony_ci rv = ipc_send_mng_msg(dev, MNG_RESET_NOTIFY, &ipc_mng_msg, 7978c2ecf20Sopenharmony_ci sizeof(struct ipc_rst_payload_type)); 7988c2ecf20Sopenharmony_ci if (rv) { 7998c2ecf20Sopenharmony_ci dev_err(dev->devc, "Failed to send IPC MNG_RESET_NOTIFY\n"); 8008c2ecf20Sopenharmony_ci return rv; 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci wait_event_interruptible_timeout(dev->wait_hw_ready, 8048c2ecf20Sopenharmony_ci dev->recvd_hw_ready, 2 * HZ); 8058c2ecf20Sopenharmony_ci if (!dev->recvd_hw_ready) { 8068c2ecf20Sopenharmony_ci dev_err(dev->devc, "Timed out waiting for HW ready\n"); 8078c2ecf20Sopenharmony_ci rv = -ENODEV; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci return rv; 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci/** 8148c2ecf20Sopenharmony_ci * ish_hw_start() -Start ISH HW 8158c2ecf20Sopenharmony_ci * @dev: ishtp device pointer 8168c2ecf20Sopenharmony_ci * 8178c2ecf20Sopenharmony_ci * Set host to ready state and wait for FW reset 8188c2ecf20Sopenharmony_ci * 8198c2ecf20Sopenharmony_ci * Return: 0 for success else error fault code 8208c2ecf20Sopenharmony_ci */ 8218c2ecf20Sopenharmony_ciint ish_hw_start(struct ishtp_device *dev) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci ish_set_host_rdy(dev); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci set_host_ready(dev); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci /* After that we can enable ISH DMA operation and wakeup ISHFW */ 8288c2ecf20Sopenharmony_ci ish_wakeup(dev); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci /* wait for FW-initiated reset flow */ 8318c2ecf20Sopenharmony_ci if (!dev->recvd_hw_ready) 8328c2ecf20Sopenharmony_ci wait_event_interruptible_timeout(dev->wait_hw_ready, 8338c2ecf20Sopenharmony_ci dev->recvd_hw_ready, 8348c2ecf20Sopenharmony_ci 10 * HZ); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci if (!dev->recvd_hw_ready) { 8378c2ecf20Sopenharmony_ci dev_err(dev->devc, 8388c2ecf20Sopenharmony_ci "[ishtp-ish]: Timed out waiting for FW-initiated reset\n"); 8398c2ecf20Sopenharmony_ci return -ENODEV; 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci return 0; 8438c2ecf20Sopenharmony_ci} 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci/** 8468c2ecf20Sopenharmony_ci * ish_ipc_get_header() -Get doorbell value 8478c2ecf20Sopenharmony_ci * @dev: ishtp device pointer 8488c2ecf20Sopenharmony_ci * @length: length of message 8498c2ecf20Sopenharmony_ci * @busy: busy status 8508c2ecf20Sopenharmony_ci * 8518c2ecf20Sopenharmony_ci * Get door bell value from message header 8528c2ecf20Sopenharmony_ci * 8538c2ecf20Sopenharmony_ci * Return: door bell value 8548c2ecf20Sopenharmony_ci */ 8558c2ecf20Sopenharmony_cistatic uint32_t ish_ipc_get_header(struct ishtp_device *dev, int length, 8568c2ecf20Sopenharmony_ci int busy) 8578c2ecf20Sopenharmony_ci{ 8588c2ecf20Sopenharmony_ci uint32_t drbl_val; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci drbl_val = IPC_BUILD_HEADER(length, IPC_PROTOCOL_ISHTP, busy); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci return drbl_val; 8638c2ecf20Sopenharmony_ci} 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cistatic const struct ishtp_hw_ops ish_hw_ops = { 8668c2ecf20Sopenharmony_ci .hw_reset = _ish_hw_reset, 8678c2ecf20Sopenharmony_ci .ipc_reset = _ish_ipc_reset, 8688c2ecf20Sopenharmony_ci .ipc_get_header = ish_ipc_get_header, 8698c2ecf20Sopenharmony_ci .ishtp_read = _ishtp_read, 8708c2ecf20Sopenharmony_ci .write = write_ipc_to_queue, 8718c2ecf20Sopenharmony_ci .get_fw_status = _ish_read_fw_sts_reg, 8728c2ecf20Sopenharmony_ci .sync_fw_clock = _ish_sync_fw_clock, 8738c2ecf20Sopenharmony_ci .ishtp_read_hdr = _ishtp_read_hdr 8748c2ecf20Sopenharmony_ci}; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci/** 8778c2ecf20Sopenharmony_ci * ish_dev_init() -Initialize ISH devoce 8788c2ecf20Sopenharmony_ci * @pdev: PCI device 8798c2ecf20Sopenharmony_ci * 8808c2ecf20Sopenharmony_ci * Allocate ISHTP device and initialize IPC processing 8818c2ecf20Sopenharmony_ci * 8828c2ecf20Sopenharmony_ci * Return: ISHTP device instance on success else NULL 8838c2ecf20Sopenharmony_ci */ 8848c2ecf20Sopenharmony_cistruct ishtp_device *ish_dev_init(struct pci_dev *pdev) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci struct ishtp_device *dev; 8878c2ecf20Sopenharmony_ci int i; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci dev = devm_kzalloc(&pdev->dev, 8908c2ecf20Sopenharmony_ci sizeof(struct ishtp_device) + sizeof(struct ish_hw), 8918c2ecf20Sopenharmony_ci GFP_KERNEL); 8928c2ecf20Sopenharmony_ci if (!dev) 8938c2ecf20Sopenharmony_ci return NULL; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci ishtp_device_init(dev); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci init_waitqueue_head(&dev->wait_hw_ready); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci spin_lock_init(&dev->wr_processing_spinlock); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci /* Init IPC processing and free lists */ 9028c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->wr_processing_list); 9038c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->wr_free_list); 9048c2ecf20Sopenharmony_ci for (i = 0; i < IPC_TX_FIFO_SIZE; i++) { 9058c2ecf20Sopenharmony_ci struct wr_msg_ctl_info *tx_buf; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci tx_buf = devm_kzalloc(&pdev->dev, 9088c2ecf20Sopenharmony_ci sizeof(struct wr_msg_ctl_info), 9098c2ecf20Sopenharmony_ci GFP_KERNEL); 9108c2ecf20Sopenharmony_ci if (!tx_buf) { 9118c2ecf20Sopenharmony_ci /* 9128c2ecf20Sopenharmony_ci * IPC buffers may be limited or not available 9138c2ecf20Sopenharmony_ci * at all - although this shouldn't happen 9148c2ecf20Sopenharmony_ci */ 9158c2ecf20Sopenharmony_ci dev_err(dev->devc, 9168c2ecf20Sopenharmony_ci "[ishtp-ish]: failure in Tx FIFO allocations (%d)\n", 9178c2ecf20Sopenharmony_ci i); 9188c2ecf20Sopenharmony_ci break; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci list_add_tail(&tx_buf->link, &dev->wr_free_list); 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci dev->ops = &ish_hw_ops; 9248c2ecf20Sopenharmony_ci dev->devc = &pdev->dev; 9258c2ecf20Sopenharmony_ci dev->mtu = IPC_PAYLOAD_SIZE - sizeof(struct ishtp_msg_hdr); 9268c2ecf20Sopenharmony_ci return dev; 9278c2ecf20Sopenharmony_ci} 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci/** 9308c2ecf20Sopenharmony_ci * ish_device_disable() - Disable ISH device 9318c2ecf20Sopenharmony_ci * @dev: ISHTP device pointer 9328c2ecf20Sopenharmony_ci * 9338c2ecf20Sopenharmony_ci * Disable ISH by clearing host ready to inform firmware. 9348c2ecf20Sopenharmony_ci */ 9358c2ecf20Sopenharmony_civoid ish_device_disable(struct ishtp_device *dev) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci struct pci_dev *pdev = dev->pdev; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if (!pdev) 9408c2ecf20Sopenharmony_ci return; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci /* Disable dma communication between FW and host */ 9438c2ecf20Sopenharmony_ci if (ish_disable_dma(dev)) { 9448c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 9458c2ecf20Sopenharmony_ci "Can't reset - stuck with DMA in-progress\n"); 9468c2ecf20Sopenharmony_ci return; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci /* Put ISH to D3hot state for power saving */ 9508c2ecf20Sopenharmony_ci pci_set_power_state(pdev, PCI_D3hot); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci dev->dev_state = ISHTP_DEV_DISABLED; 9538c2ecf20Sopenharmony_ci ish_clr_host_rdy(dev); 9548c2ecf20Sopenharmony_ci} 955