/** * mei_wd_ops_start - wd start command from the watchdog core. * * @wd_dev: watchdog device struct * * Return: 0 if success, negative errno code for failure */ static int mei_wd_ops_start(struct watchdog_device *wd_dev) { struct mei_device *dev; struct mei_cl *cl; int err = -ENODEV; dev = watchdog_get_drvdata(wd_dev); if (!dev) return -ENODEV; cl = &dev->wd_cl; mutex_lock(&dev->device_lock); if (dev->dev_state != MEI_DEV_ENABLED) { dev_dbg(dev->dev, "wd: dev_state != MEI_DEV_ENABLED dev_state = %s\n", mei_dev_state_str(dev->dev_state)); goto end_unlock; } if (!mei_cl_is_connected(cl)) { cl_dbg(dev, cl, "MEI Driver is not connected to Watchdog Client\n"); goto end_unlock; } mei_wd_set_start_timeout(dev, dev->wd_timeout); err = 0; end_unlock: mutex_unlock(&dev->device_lock); return err; }
/** * mei_open - the open function * * @inode: pointer to inode structure * @file: pointer to file structure e * returns 0 on success, <0 on error */ static int mei_open(struct inode *inode, struct file *file) { struct miscdevice *misc = file->private_data; struct pci_dev *pdev; struct mei_cl *cl; struct mei_device *dev; int err; err = -ENODEV; if (!misc->parent) goto out; pdev = container_of(misc->parent, struct pci_dev, dev); dev = pci_get_drvdata(pdev); if (!dev) goto out; mutex_lock(&dev->device_lock); err = -ENOMEM; cl = mei_cl_allocate(dev); if (!cl) goto out_unlock; err = -ENODEV; if (dev->dev_state != MEI_DEV_ENABLED) { dev_dbg(&dev->pdev->dev, "dev_state != MEI_ENABLED dev_state = %s\n", mei_dev_state_str(dev->dev_state)); goto out_unlock; } err = -EMFILE; if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { dev_err(&dev->pdev->dev, "open_handle_count exceded %d", MEI_MAX_OPEN_HANDLE_COUNT); goto out_unlock; } err = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY); if (err) goto out_unlock; file->private_data = cl; mutex_unlock(&dev->device_lock); return nonseekable_open(inode, file); out_unlock: mutex_unlock(&dev->device_lock); kfree(cl); out: return err; }
/** * mei_open - the open function * * @inode: pointer to inode structure * @file: pointer to file structure * * returns 0 on success, <0 on error */ static int mei_open(struct inode *inode, struct file *file) { struct miscdevice *misc = file->private_data; struct pci_dev *pdev; struct mei_cl *cl; struct mei_device *dev; int err; if (!misc->parent) return -ENODEV; pdev = container_of(misc->parent, struct pci_dev, dev); dev = pci_get_drvdata(pdev); if (!dev) return -ENODEV; mutex_lock(&dev->device_lock); cl = NULL; err = -ENODEV; if (dev->dev_state != MEI_DEV_ENABLED) { dev_dbg(&dev->pdev->dev, "dev_state != MEI_ENABLED dev_state = %s\n", mei_dev_state_str(dev->dev_state)); goto err_unlock; } err = -ENOMEM; cl = mei_cl_allocate(dev); if (!cl) goto err_unlock; /* open_handle_count check is handled in the mei_cl_link */ err = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY); if (err) goto err_unlock; file->private_data = cl; mutex_unlock(&dev->device_lock); return nonseekable_open(inode, file); err_unlock: mutex_unlock(&dev->device_lock); kfree(cl); return err; }
/** * mei_reset - resets host and fw. * * @dev: the device structure * @interrupts_enabled: if interrupt should be enabled after reset. */ void mei_reset(struct mei_device *dev, int interrupts_enabled) { bool unexpected; if (dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) return; unexpected = (dev->dev_state != MEI_DEV_INITIALIZING && dev->dev_state != MEI_DEV_DISABLED && dev->dev_state != MEI_DEV_POWER_DOWN && dev->dev_state != MEI_DEV_POWER_UP); mei_hw_reset(dev, interrupts_enabled); if (dev->dev_state != MEI_DEV_INITIALIZING) { if (dev->dev_state != MEI_DEV_DISABLED && dev->dev_state != MEI_DEV_POWER_DOWN) dev->dev_state = MEI_DEV_RESETING; mei_cl_all_disconnect(dev); /* remove entry if already in list */ dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n"); mei_cl_unlink(&dev->wd_cl); if (dev->open_handle_count > 0) dev->open_handle_count--; mei_cl_unlink(&dev->iamthif_cl); if (dev->open_handle_count > 0) dev->open_handle_count--; mei_amthif_reset_params(dev); memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg)); } dev->me_clients_num = 0; dev->rd_msg_hdr = 0; dev->wd_pending = false; if (unexpected) dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n", mei_dev_state_str(dev->dev_state)); /* wake up all readings so they can be interrupted */ mei_cl_all_read_wakeup(dev); /* remove all waiting requests */ mei_cl_all_write_clear(dev); }
/** * mei_write_is_idle - check if there is pending write transaction * * @dev: the device structure * returns true if the writ queues are empty */ bool mei_write_is_idle(struct mei_device *dev) { bool idle = (dev->dev_state == MEI_DEV_ENABLED && dev->wr_ext_msg.hdr.length == 0 && list_empty(&dev->ctrl_wr_list.list) && list_empty(&dev->write_list.list)); dev_dbg(&dev->pdev->dev, "pm: is idle[%d] state=%s extra=%d, ctrl=%d write=%d\n", idle, mei_dev_state_str(dev->dev_state), dev->wr_ext_msg.hdr.length == 0, list_empty(&dev->ctrl_wr_list.list), list_empty(&dev->write_list.list)); return idle; }
/** * mei_write_is_idle - check if the write queues are idle * * @dev: the device structure * * Return: true of there is no pending write */ bool mei_write_is_idle(struct mei_device *dev) { bool idle = (dev->dev_state == MEI_DEV_ENABLED && list_empty(&dev->ctrl_wr_list) && list_empty(&dev->write_list) && list_empty(&dev->write_waiting_list)); dev_dbg(dev->dev, "write pg: is idle[%d] state=%s ctrl=%01d write=%01d wwait=%01d\n", idle, mei_dev_state_str(dev->dev_state), list_empty(&dev->ctrl_wr_list), list_empty(&dev->write_list), list_empty(&dev->write_waiting_list)); return idle; }
static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, size_t cnt, loff_t *ppos) { struct mei_device *dev = fp->private_data; const size_t bufsz = 1024; char *buf = kzalloc(bufsz, GFP_KERNEL); int pos = 0; int ret; if (!buf) return -ENOMEM; pos += scnprintf(buf + pos, bufsz - pos, "%s\n", mei_dev_state_str(dev->dev_state)); ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); kfree(buf); return ret; }
/** * mei_open - the open function * * @inode: pointer to inode structure * @file: pointer to file structure * * Return: 0 on success, <0 on error */ static int mei_open(struct inode *inode, struct file *file) { struct mei_device *dev; struct mei_cl *cl; int err; dev = container_of(inode->i_cdev, struct mei_device, cdev); if (!dev) return -ENODEV; mutex_lock(&dev->device_lock); if (dev->dev_state != MEI_DEV_ENABLED) { dev_dbg(dev->dev, "dev_state != MEI_ENABLED dev_state = %s\n", mei_dev_state_str(dev->dev_state)); err = -ENODEV; goto err_unlock; } cl = mei_cl_alloc_linked(dev); if (IS_ERR(cl)) { err = PTR_ERR(cl); goto err_unlock; } cl->fp = file; file->private_data = cl; mutex_unlock(&dev->device_lock); return nonseekable_open(inode, file); err_unlock: mutex_unlock(&dev->device_lock); return err; }
/** * mei_reset - resets host and fw. * * @dev: the device structure */ int mei_reset(struct mei_device *dev) { enum mei_dev_state state = dev->dev_state; bool interrupts_enabled; int ret; if (state != MEI_DEV_INITIALIZING && state != MEI_DEV_DISABLED && state != MEI_DEV_POWER_DOWN && state != MEI_DEV_POWER_UP) dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n", mei_dev_state_str(state)); /* we're already in reset, cancel the init timer * if the reset was called due the hbm protocol error * we need to call it before hw start * so the hbm watchdog won't kick in */ mei_hbm_idle(dev); /* enter reset flow */ interrupts_enabled = state != MEI_DEV_POWER_DOWN; dev->dev_state = MEI_DEV_RESETTING; dev->reset_count++; if (dev->reset_count > MEI_MAX_CONSEC_RESET) { dev_err(&dev->pdev->dev, "reset: reached maximal consecutive resets: disabling the device\n"); dev->dev_state = MEI_DEV_DISABLED; return -ENODEV; } ret = mei_hw_reset(dev, interrupts_enabled); /* fall through and remove the sw state even if hw reset has failed */ /* no need to clean up software state in case of power up */ if (state != MEI_DEV_INITIALIZING && state != MEI_DEV_POWER_UP) { /* remove all waiting requests */ mei_cl_all_write_clear(dev); mei_cl_all_disconnect(dev); /* wake up all readers and writers so they can be interrupted */ mei_cl_all_wakeup(dev); /* remove entry if already in list */ dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n"); mei_cl_unlink(&dev->wd_cl); mei_cl_unlink(&dev->iamthif_cl); mei_amthif_reset_params(dev); } dev->me_clients_num = 0; dev->rd_msg_hdr = 0; dev->wd_pending = false; if (ret) { dev_err(&dev->pdev->dev, "hw_reset failed ret = %d\n", ret); return ret; } if (state == MEI_DEV_POWER_DOWN) { dev_dbg(&dev->pdev->dev, "powering down: end of reset\n"); dev->dev_state = MEI_DEV_DISABLED; return 0; } ret = mei_hw_start(dev); if (ret) { dev_err(&dev->pdev->dev, "hw_start failed ret = %d\n", ret); return ret; } dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); dev->dev_state = MEI_DEV_INIT_CLIENTS; ret = mei_hbm_start_req(dev); if (ret) { dev_err(&dev->pdev->dev, "hbm_start failed ret = %d\n", ret); dev->dev_state = MEI_DEV_RESETTING; return ret; } return 0; }
/** * mei_reset - resets host and fw. * * @dev: the device structure * @interrupts_enabled: if interrupt should be enabled after reset. */ void mei_reset(struct mei_device *dev, int interrupts_enabled) { bool unexpected; unexpected = (dev->dev_state != MEI_DEV_INITIALIZING && dev->dev_state != MEI_DEV_DISABLED && dev->dev_state != MEI_DEV_POWER_DOWN && dev->dev_state != MEI_DEV_POWER_UP); mei_hw_reset(dev, interrupts_enabled); dev->hbm_state = MEI_HBM_IDLE; if (dev->dev_state != MEI_DEV_INITIALIZING && dev->dev_state != MEI_DEV_POWER_UP) { if (dev->dev_state != MEI_DEV_DISABLED && dev->dev_state != MEI_DEV_POWER_DOWN) dev->dev_state = MEI_DEV_RESETTING; mei_cl_all_disconnect(dev); /* remove entry if already in list */ dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n"); mei_cl_unlink(&dev->wd_cl); if (dev->open_handle_count > 0) dev->open_handle_count--; mei_cl_unlink(&dev->iamthif_cl); if (dev->open_handle_count > 0) dev->open_handle_count--; mei_amthif_reset_params(dev); memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg)); } dev->me_clients_num = 0; dev->rd_msg_hdr = 0; dev->wd_pending = false; if (unexpected) dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n", mei_dev_state_str(dev->dev_state)); if (!interrupts_enabled) { dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n"); return; } mei_hw_start(dev); dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); /* link is established * start sending messages. */ dev->dev_state = MEI_DEV_INIT_CLIENTS; mei_hbm_start_req(dev); /* wake up all readings so they can be interrupted */ mei_cl_all_read_wakeup(dev); /* remove all waiting requests */ mei_cl_all_write_clear(dev); }
/** * mei_reset - resets host and fw. * * @dev: the device structure * * Return: 0 on success or < 0 if the reset hasn't succeeded */ int mei_reset(struct mei_device *dev) { enum mei_dev_state state = dev->dev_state; bool interrupts_enabled; int ret; if (state != MEI_DEV_INITIALIZING && state != MEI_DEV_DISABLED && state != MEI_DEV_POWER_DOWN && state != MEI_DEV_POWER_UP) { char fw_sts_str[MEI_FW_STATUS_STR_SZ]; mei_fw_status_str(dev, fw_sts_str, MEI_FW_STATUS_STR_SZ); dev_warn(dev->dev, "unexpected reset: dev_state = %s fw status = %s\n", mei_dev_state_str(state), fw_sts_str); } mei_clear_interrupts(dev); /* we're already in reset, cancel the init timer * if the reset was called due the hbm protocol error * we need to call it before hw start * so the hbm watchdog won't kick in */ mei_hbm_idle(dev); /* enter reset flow */ interrupts_enabled = state != MEI_DEV_POWER_DOWN; dev->dev_state = MEI_DEV_RESETTING; dev->reset_count++; if (dev->reset_count > MEI_MAX_CONSEC_RESET) { dev_err(dev->dev, "reset: reached maximal consecutive resets: disabling the device\n"); dev->dev_state = MEI_DEV_DISABLED; return -ENODEV; } ret = mei_hw_reset(dev, interrupts_enabled); /* fall through and remove the sw state even if hw reset has failed */ /* no need to clean up software state in case of power up */ if (state != MEI_DEV_INITIALIZING && state != MEI_DEV_POWER_UP) mei_cl_all_disconnect(dev); mei_hbm_reset(dev); memset(dev->rd_msg_hdr, 0, sizeof(dev->rd_msg_hdr)); if (ret) { dev_err(dev->dev, "hw_reset failed ret = %d\n", ret); return ret; } if (state == MEI_DEV_POWER_DOWN) { dev_dbg(dev->dev, "powering down: end of reset\n"); dev->dev_state = MEI_DEV_DISABLED; return 0; } ret = mei_hw_start(dev); if (ret) { dev_err(dev->dev, "hw_start failed ret = %d\n", ret); return ret; } dev_dbg(dev->dev, "link is established start sending messages.\n"); dev->dev_state = MEI_DEV_INIT_CLIENTS; ret = mei_hbm_start_req(dev); if (ret) { dev_err(dev->dev, "hbm_start failed ret = %d\n", ret); dev->dev_state = MEI_DEV_RESETTING; return ret; } return 0; }