162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2012-2022, Intel Corporation. All rights reserved. 462306a36Sopenharmony_ci * Intel Management Engine Interface (Intel MEI) Linux driver 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/export.h> 862306a36Sopenharmony_ci#include <linux/sched.h> 962306a36Sopenharmony_ci#include <linux/wait.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/mei.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "mei_dev.h" 1562306a36Sopenharmony_ci#include "hbm.h" 1662306a36Sopenharmony_ci#include "client.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ciconst char *mei_dev_state_str(int state) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci#define MEI_DEV_STATE(state) case MEI_DEV_##state: return #state 2162306a36Sopenharmony_ci switch (state) { 2262306a36Sopenharmony_ci MEI_DEV_STATE(INITIALIZING); 2362306a36Sopenharmony_ci MEI_DEV_STATE(INIT_CLIENTS); 2462306a36Sopenharmony_ci MEI_DEV_STATE(ENABLED); 2562306a36Sopenharmony_ci MEI_DEV_STATE(RESETTING); 2662306a36Sopenharmony_ci MEI_DEV_STATE(DISABLED); 2762306a36Sopenharmony_ci MEI_DEV_STATE(POWERING_DOWN); 2862306a36Sopenharmony_ci MEI_DEV_STATE(POWER_DOWN); 2962306a36Sopenharmony_ci MEI_DEV_STATE(POWER_UP); 3062306a36Sopenharmony_ci default: 3162306a36Sopenharmony_ci return "unknown"; 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci#undef MEI_DEV_STATE 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ciconst char *mei_pg_state_str(enum mei_pg_state state) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci#define MEI_PG_STATE(state) case MEI_PG_##state: return #state 3962306a36Sopenharmony_ci switch (state) { 4062306a36Sopenharmony_ci MEI_PG_STATE(OFF); 4162306a36Sopenharmony_ci MEI_PG_STATE(ON); 4262306a36Sopenharmony_ci default: 4362306a36Sopenharmony_ci return "unknown"; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci#undef MEI_PG_STATE 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/** 4962306a36Sopenharmony_ci * mei_fw_status2str - convert fw status registers to printable string 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * @fw_status: firmware status 5262306a36Sopenharmony_ci * @buf: string buffer at minimal size MEI_FW_STATUS_STR_SZ 5362306a36Sopenharmony_ci * @len: buffer len must be >= MEI_FW_STATUS_STR_SZ 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * Return: number of bytes written or -EINVAL if buffer is to small 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_cissize_t mei_fw_status2str(struct mei_fw_status *fw_status, 5862306a36Sopenharmony_ci char *buf, size_t len) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci ssize_t cnt = 0; 6162306a36Sopenharmony_ci int i; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci buf[0] = '\0'; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (len < MEI_FW_STATUS_STR_SZ) 6662306a36Sopenharmony_ci return -EINVAL; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci for (i = 0; i < fw_status->count; i++) 6962306a36Sopenharmony_ci cnt += scnprintf(buf + cnt, len - cnt, "%08X ", 7062306a36Sopenharmony_ci fw_status->status[i]); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* drop last space */ 7362306a36Sopenharmony_ci buf[cnt] = '\0'; 7462306a36Sopenharmony_ci return cnt; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_fw_status2str); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/** 7962306a36Sopenharmony_ci * mei_cancel_work - Cancel mei background jobs 8062306a36Sopenharmony_ci * 8162306a36Sopenharmony_ci * @dev: the device structure 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_civoid mei_cancel_work(struct mei_device *dev) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci cancel_work_sync(&dev->reset_work); 8662306a36Sopenharmony_ci cancel_work_sync(&dev->bus_rescan_work); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci cancel_delayed_work_sync(&dev->timer_work); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_cancel_work); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/** 9362306a36Sopenharmony_ci * mei_reset - resets host and fw. 9462306a36Sopenharmony_ci * 9562306a36Sopenharmony_ci * @dev: the device structure 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * Return: 0 on success or < 0 if the reset hasn't succeeded 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ciint mei_reset(struct mei_device *dev) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci enum mei_dev_state state = dev->dev_state; 10262306a36Sopenharmony_ci bool interrupts_enabled; 10362306a36Sopenharmony_ci int ret; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (state != MEI_DEV_INITIALIZING && 10662306a36Sopenharmony_ci state != MEI_DEV_DISABLED && 10762306a36Sopenharmony_ci state != MEI_DEV_POWER_DOWN && 10862306a36Sopenharmony_ci state != MEI_DEV_POWER_UP) { 10962306a36Sopenharmony_ci char fw_sts_str[MEI_FW_STATUS_STR_SZ]; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci mei_fw_status_str(dev, fw_sts_str, MEI_FW_STATUS_STR_SZ); 11262306a36Sopenharmony_ci dev_warn(dev->dev, "unexpected reset: dev_state = %s fw status = %s\n", 11362306a36Sopenharmony_ci mei_dev_state_str(state), fw_sts_str); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci mei_clear_interrupts(dev); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* we're already in reset, cancel the init timer 11962306a36Sopenharmony_ci * if the reset was called due the hbm protocol error 12062306a36Sopenharmony_ci * we need to call it before hw start 12162306a36Sopenharmony_ci * so the hbm watchdog won't kick in 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci mei_hbm_idle(dev); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* enter reset flow */ 12662306a36Sopenharmony_ci interrupts_enabled = state != MEI_DEV_POWER_DOWN; 12762306a36Sopenharmony_ci mei_set_devstate(dev, MEI_DEV_RESETTING); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci dev->reset_count++; 13062306a36Sopenharmony_ci if (dev->reset_count > MEI_MAX_CONSEC_RESET) { 13162306a36Sopenharmony_ci dev_err(dev->dev, "reset: reached maximal consecutive resets: disabling the device\n"); 13262306a36Sopenharmony_ci mei_set_devstate(dev, MEI_DEV_DISABLED); 13362306a36Sopenharmony_ci return -ENODEV; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci ret = mei_hw_reset(dev, interrupts_enabled); 13762306a36Sopenharmony_ci /* fall through and remove the sw state even if hw reset has failed */ 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* no need to clean up software state in case of power up */ 14062306a36Sopenharmony_ci if (state != MEI_DEV_INITIALIZING && state != MEI_DEV_POWER_UP) 14162306a36Sopenharmony_ci mei_cl_all_disconnect(dev); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci mei_hbm_reset(dev); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* clean stale FW version */ 14662306a36Sopenharmony_ci dev->fw_ver_received = 0; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci memset(dev->rd_msg_hdr, 0, sizeof(dev->rd_msg_hdr)); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (ret) { 15162306a36Sopenharmony_ci dev_err(dev->dev, "hw_reset failed ret = %d\n", ret); 15262306a36Sopenharmony_ci return ret; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (state == MEI_DEV_POWER_DOWN) { 15662306a36Sopenharmony_ci dev_dbg(dev->dev, "powering down: end of reset\n"); 15762306a36Sopenharmony_ci mei_set_devstate(dev, MEI_DEV_DISABLED); 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci ret = mei_hw_start(dev); 16262306a36Sopenharmony_ci if (ret) { 16362306a36Sopenharmony_ci char fw_sts_str[MEI_FW_STATUS_STR_SZ]; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci mei_fw_status_str(dev, fw_sts_str, MEI_FW_STATUS_STR_SZ); 16662306a36Sopenharmony_ci dev_err(dev->dev, "hw_start failed ret = %d fw status = %s\n", ret, fw_sts_str); 16762306a36Sopenharmony_ci return ret; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (dev->dev_state != MEI_DEV_RESETTING) { 17162306a36Sopenharmony_ci dev_dbg(dev->dev, "wrong state = %d on link start\n", dev->dev_state); 17262306a36Sopenharmony_ci return 0; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci dev_dbg(dev->dev, "link is established start sending messages.\n"); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci mei_set_devstate(dev, MEI_DEV_INIT_CLIENTS); 17862306a36Sopenharmony_ci ret = mei_hbm_start_req(dev); 17962306a36Sopenharmony_ci if (ret) { 18062306a36Sopenharmony_ci dev_err(dev->dev, "hbm_start failed ret = %d\n", ret); 18162306a36Sopenharmony_ci mei_set_devstate(dev, MEI_DEV_RESETTING); 18262306a36Sopenharmony_ci return ret; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_reset); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/** 19062306a36Sopenharmony_ci * mei_start - initializes host and fw to start work. 19162306a36Sopenharmony_ci * 19262306a36Sopenharmony_ci * @dev: the device structure 19362306a36Sopenharmony_ci * 19462306a36Sopenharmony_ci * Return: 0 on success, <0 on failure. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ciint mei_start(struct mei_device *dev) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci int ret; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci mutex_lock(&dev->device_lock); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* acknowledge interrupt and stop interrupts */ 20362306a36Sopenharmony_ci mei_clear_interrupts(dev); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci ret = mei_hw_config(dev); 20662306a36Sopenharmony_ci if (ret) 20762306a36Sopenharmony_ci goto err; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci dev_dbg(dev->dev, "reset in start the mei device.\n"); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci dev->reset_count = 0; 21262306a36Sopenharmony_ci do { 21362306a36Sopenharmony_ci mei_set_devstate(dev, MEI_DEV_INITIALIZING); 21462306a36Sopenharmony_ci ret = mei_reset(dev); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (ret == -ENODEV || dev->dev_state == MEI_DEV_DISABLED) { 21762306a36Sopenharmony_ci dev_err(dev->dev, "reset failed ret = %d", ret); 21862306a36Sopenharmony_ci goto err; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci } while (ret); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (mei_hbm_start_wait(dev)) { 22362306a36Sopenharmony_ci dev_err(dev->dev, "HBM haven't started"); 22462306a36Sopenharmony_ci goto err; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (!mei_hbm_version_is_supported(dev)) { 22862306a36Sopenharmony_ci dev_dbg(dev->dev, "MEI start failed.\n"); 22962306a36Sopenharmony_ci goto err; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci dev_dbg(dev->dev, "link layer has been established.\n"); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci mutex_unlock(&dev->device_lock); 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_cierr: 23762306a36Sopenharmony_ci dev_err(dev->dev, "link layer initialization failed.\n"); 23862306a36Sopenharmony_ci mei_set_devstate(dev, MEI_DEV_DISABLED); 23962306a36Sopenharmony_ci mutex_unlock(&dev->device_lock); 24062306a36Sopenharmony_ci return -ENODEV; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_start); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci/** 24562306a36Sopenharmony_ci * mei_restart - restart device after suspend 24662306a36Sopenharmony_ci * 24762306a36Sopenharmony_ci * @dev: the device structure 24862306a36Sopenharmony_ci * 24962306a36Sopenharmony_ci * Return: 0 on success or -ENODEV if the restart hasn't succeeded 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ciint mei_restart(struct mei_device *dev) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci int err; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci mutex_lock(&dev->device_lock); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci mei_set_devstate(dev, MEI_DEV_POWER_UP); 25862306a36Sopenharmony_ci dev->reset_count = 0; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci err = mei_reset(dev); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci mutex_unlock(&dev->device_lock); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (err == -ENODEV || dev->dev_state == MEI_DEV_DISABLED) { 26562306a36Sopenharmony_ci dev_err(dev->dev, "device disabled = %d\n", err); 26662306a36Sopenharmony_ci return -ENODEV; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* try to start again */ 27062306a36Sopenharmony_ci if (err) 27162306a36Sopenharmony_ci schedule_work(&dev->reset_work); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return 0; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_restart); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic void mei_reset_work(struct work_struct *work) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct mei_device *dev = 28162306a36Sopenharmony_ci container_of(work, struct mei_device, reset_work); 28262306a36Sopenharmony_ci int ret; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci mei_clear_interrupts(dev); 28562306a36Sopenharmony_ci mei_synchronize_irq(dev); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci mutex_lock(&dev->device_lock); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci ret = mei_reset(dev); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci mutex_unlock(&dev->device_lock); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (dev->dev_state == MEI_DEV_DISABLED) { 29462306a36Sopenharmony_ci dev_err(dev->dev, "device disabled = %d\n", ret); 29562306a36Sopenharmony_ci return; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* retry reset in case of failure */ 29962306a36Sopenharmony_ci if (ret) 30062306a36Sopenharmony_ci schedule_work(&dev->reset_work); 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_civoid mei_stop(struct mei_device *dev) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci dev_dbg(dev->dev, "stopping the device.\n"); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci mutex_lock(&dev->device_lock); 30862306a36Sopenharmony_ci mei_set_devstate(dev, MEI_DEV_POWERING_DOWN); 30962306a36Sopenharmony_ci mutex_unlock(&dev->device_lock); 31062306a36Sopenharmony_ci mei_cl_bus_remove_devices(dev); 31162306a36Sopenharmony_ci mutex_lock(&dev->device_lock); 31262306a36Sopenharmony_ci mei_set_devstate(dev, MEI_DEV_POWER_DOWN); 31362306a36Sopenharmony_ci mutex_unlock(&dev->device_lock); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci mei_cancel_work(dev); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci mei_clear_interrupts(dev); 31862306a36Sopenharmony_ci mei_synchronize_irq(dev); 31962306a36Sopenharmony_ci /* to catch HW-initiated reset */ 32062306a36Sopenharmony_ci mei_cancel_work(dev); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci mutex_lock(&dev->device_lock); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci mei_reset(dev); 32562306a36Sopenharmony_ci /* move device to disabled state unconditionally */ 32662306a36Sopenharmony_ci mei_set_devstate(dev, MEI_DEV_DISABLED); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci mutex_unlock(&dev->device_lock); 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_stop); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci/** 33362306a36Sopenharmony_ci * mei_write_is_idle - check if the write queues are idle 33462306a36Sopenharmony_ci * 33562306a36Sopenharmony_ci * @dev: the device structure 33662306a36Sopenharmony_ci * 33762306a36Sopenharmony_ci * Return: true of there is no pending write 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_cibool mei_write_is_idle(struct mei_device *dev) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci bool idle = (dev->dev_state == MEI_DEV_ENABLED && 34262306a36Sopenharmony_ci list_empty(&dev->ctrl_wr_list) && 34362306a36Sopenharmony_ci list_empty(&dev->write_list) && 34462306a36Sopenharmony_ci list_empty(&dev->write_waiting_list)); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci dev_dbg(dev->dev, "write pg: is idle[%d] state=%s ctrl=%01d write=%01d wwait=%01d\n", 34762306a36Sopenharmony_ci idle, 34862306a36Sopenharmony_ci mei_dev_state_str(dev->dev_state), 34962306a36Sopenharmony_ci list_empty(&dev->ctrl_wr_list), 35062306a36Sopenharmony_ci list_empty(&dev->write_list), 35162306a36Sopenharmony_ci list_empty(&dev->write_waiting_list)); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return idle; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_write_is_idle); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci/** 35862306a36Sopenharmony_ci * mei_device_init - initialize mei_device structure 35962306a36Sopenharmony_ci * 36062306a36Sopenharmony_ci * @dev: the mei device 36162306a36Sopenharmony_ci * @device: the device structure 36262306a36Sopenharmony_ci * @slow_fw: configure longer timeouts as FW is slow 36362306a36Sopenharmony_ci * @hw_ops: hw operations 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_civoid mei_device_init(struct mei_device *dev, 36662306a36Sopenharmony_ci struct device *device, 36762306a36Sopenharmony_ci bool slow_fw, 36862306a36Sopenharmony_ci const struct mei_hw_ops *hw_ops) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci /* setup our list array */ 37162306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->file_list); 37262306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->device_list); 37362306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->me_clients); 37462306a36Sopenharmony_ci mutex_init(&dev->device_lock); 37562306a36Sopenharmony_ci init_rwsem(&dev->me_clients_rwsem); 37662306a36Sopenharmony_ci mutex_init(&dev->cl_bus_lock); 37762306a36Sopenharmony_ci init_waitqueue_head(&dev->wait_hw_ready); 37862306a36Sopenharmony_ci init_waitqueue_head(&dev->wait_pg); 37962306a36Sopenharmony_ci init_waitqueue_head(&dev->wait_hbm_start); 38062306a36Sopenharmony_ci dev->dev_state = MEI_DEV_INITIALIZING; 38162306a36Sopenharmony_ci dev->reset_count = 0; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->write_list); 38462306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->write_waiting_list); 38562306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->ctrl_wr_list); 38662306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->ctrl_rd_list); 38762306a36Sopenharmony_ci dev->tx_queue_limit = MEI_TX_QUEUE_LIMIT_DEFAULT; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci INIT_DELAYED_WORK(&dev->timer_work, mei_timer); 39062306a36Sopenharmony_ci INIT_WORK(&dev->reset_work, mei_reset_work); 39162306a36Sopenharmony_ci INIT_WORK(&dev->bus_rescan_work, mei_cl_bus_rescan_work); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX); 39462306a36Sopenharmony_ci dev->open_handle_count = 0; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci dev->pxp_mode = MEI_DEV_PXP_DEFAULT; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* 39962306a36Sopenharmony_ci * Reserving the first client ID 40062306a36Sopenharmony_ci * 0: Reserved for MEI Bus Message communications 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ci bitmap_set(dev->host_clients_map, 0, 1); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci dev->pg_event = MEI_PG_EVENT_IDLE; 40562306a36Sopenharmony_ci dev->ops = hw_ops; 40662306a36Sopenharmony_ci dev->dev = device; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci dev->timeouts.hw_ready = mei_secs_to_jiffies(MEI_HW_READY_TIMEOUT); 40962306a36Sopenharmony_ci dev->timeouts.connect = MEI_CONNECT_TIMEOUT; 41062306a36Sopenharmony_ci dev->timeouts.client_init = MEI_CLIENTS_INIT_TIMEOUT; 41162306a36Sopenharmony_ci dev->timeouts.pgi = mei_secs_to_jiffies(MEI_PGI_TIMEOUT); 41262306a36Sopenharmony_ci dev->timeouts.d0i3 = mei_secs_to_jiffies(MEI_D0I3_TIMEOUT); 41362306a36Sopenharmony_ci if (slow_fw) { 41462306a36Sopenharmony_ci dev->timeouts.cl_connect = mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT_SLOW); 41562306a36Sopenharmony_ci dev->timeouts.hbm = mei_secs_to_jiffies(MEI_HBM_TIMEOUT_SLOW); 41662306a36Sopenharmony_ci dev->timeouts.mkhi_recv = msecs_to_jiffies(MKHI_RCV_TIMEOUT_SLOW); 41762306a36Sopenharmony_ci } else { 41862306a36Sopenharmony_ci dev->timeouts.cl_connect = mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT); 41962306a36Sopenharmony_ci dev->timeouts.hbm = mei_secs_to_jiffies(MEI_HBM_TIMEOUT); 42062306a36Sopenharmony_ci dev->timeouts.mkhi_recv = msecs_to_jiffies(MKHI_RCV_TIMEOUT); 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_device_init); 42462306a36Sopenharmony_ci 425