linux/drivers/misc/mei/init.c
<<
>>
Prefs
   1/*
   2 *
   3 * Intel Management Engine Interface (Intel MEI) Linux driver
   4 * Copyright (c) 2003-2012, Intel Corporation.
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms and conditions of the GNU General Public License,
   8 * version 2, as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope it will be useful, but WITHOUT
  11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  13 * more details.
  14 *
  15 */
  16
  17#include <linux/export.h>
  18#include <linux/pci.h>
  19#include <linux/sched.h>
  20#include <linux/wait.h>
  21#include <linux/delay.h>
  22
  23#include <linux/mei.h>
  24
  25#include "mei_dev.h"
  26#include "hbm.h"
  27#include "client.h"
  28
  29const char *mei_dev_state_str(int state)
  30{
  31#define MEI_DEV_STATE(state) case MEI_DEV_##state: return #state
  32        switch (state) {
  33        MEI_DEV_STATE(INITIALIZING);
  34        MEI_DEV_STATE(INIT_CLIENTS);
  35        MEI_DEV_STATE(ENABLED);
  36        MEI_DEV_STATE(RESETTING);
  37        MEI_DEV_STATE(DISABLED);
  38        MEI_DEV_STATE(POWER_DOWN);
  39        MEI_DEV_STATE(POWER_UP);
  40        default:
  41                return "unknown";
  42        }
  43#undef MEI_DEV_STATE
  44}
  45
  46void mei_device_init(struct mei_device *dev)
  47{
  48        /* setup our list array */
  49        INIT_LIST_HEAD(&dev->file_list);
  50        INIT_LIST_HEAD(&dev->device_list);
  51        mutex_init(&dev->device_lock);
  52        init_waitqueue_head(&dev->wait_hw_ready);
  53        init_waitqueue_head(&dev->wait_recvd_msg);
  54        init_waitqueue_head(&dev->wait_stop_wd);
  55        dev->dev_state = MEI_DEV_INITIALIZING;
  56
  57        mei_io_list_init(&dev->read_list);
  58        mei_io_list_init(&dev->write_list);
  59        mei_io_list_init(&dev->write_waiting_list);
  60        mei_io_list_init(&dev->ctrl_wr_list);
  61        mei_io_list_init(&dev->ctrl_rd_list);
  62
  63        INIT_DELAYED_WORK(&dev->timer_work, mei_timer);
  64        INIT_WORK(&dev->init_work, mei_host_client_init);
  65
  66        INIT_LIST_HEAD(&dev->wd_cl.link);
  67        INIT_LIST_HEAD(&dev->iamthif_cl.link);
  68        mei_io_list_init(&dev->amthif_cmd_list);
  69        mei_io_list_init(&dev->amthif_rd_complete_list);
  70
  71}
  72EXPORT_SYMBOL_GPL(mei_device_init);
  73
  74/**
  75 * mei_start - initializes host and fw to start work.
  76 *
  77 * @dev: the device structure
  78 *
  79 * returns 0 on success, <0 on failure.
  80 */
  81int mei_start(struct mei_device *dev)
  82{
  83        mutex_lock(&dev->device_lock);
  84
  85        /* acknowledge interrupt and stop interupts */
  86        mei_clear_interrupts(dev);
  87
  88        mei_hw_config(dev);
  89
  90        dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n");
  91
  92        mei_reset(dev, 1);
  93
  94        if (mei_hbm_start_wait(dev)) {
  95                dev_err(&dev->pdev->dev, "HBM haven't started");
  96                goto err;
  97        }
  98
  99        if (!mei_host_is_ready(dev)) {
 100                dev_err(&dev->pdev->dev, "host is not ready.\n");
 101                goto err;
 102        }
 103
 104        if (!mei_hw_is_ready(dev)) {
 105                dev_err(&dev->pdev->dev, "ME is not ready.\n");
 106                goto err;
 107        }
 108
 109        if (!mei_hbm_version_is_supported(dev)) {
 110                dev_dbg(&dev->pdev->dev, "MEI start failed.\n");
 111                goto err;
 112        }
 113
 114        dev_dbg(&dev->pdev->dev, "link layer has been established.\n");
 115
 116        mutex_unlock(&dev->device_lock);
 117        return 0;
 118err:
 119        dev_err(&dev->pdev->dev, "link layer initialization failed.\n");
 120        dev->dev_state = MEI_DEV_DISABLED;
 121        mutex_unlock(&dev->device_lock);
 122        return -ENODEV;
 123}
 124EXPORT_SYMBOL_GPL(mei_start);
 125
 126/**
 127 * mei_reset - resets host and fw.
 128 *
 129 * @dev: the device structure
 130 * @interrupts_enabled: if interrupt should be enabled after reset.
 131 */
 132void mei_reset(struct mei_device *dev, int interrupts_enabled)
 133{
 134        bool unexpected;
 135        int ret;
 136
 137        unexpected = (dev->dev_state != MEI_DEV_INITIALIZING &&
 138                        dev->dev_state != MEI_DEV_DISABLED &&
 139                        dev->dev_state != MEI_DEV_POWER_DOWN &&
 140                        dev->dev_state != MEI_DEV_POWER_UP);
 141
 142        ret = mei_hw_reset(dev, interrupts_enabled);
 143        if (ret) {
 144                dev_err(&dev->pdev->dev, "hw reset failed disabling the device\n");
 145                interrupts_enabled = false;
 146                dev->dev_state = MEI_DEV_DISABLED;
 147        }
 148
 149        dev->hbm_state = MEI_HBM_IDLE;
 150
 151        if (dev->dev_state != MEI_DEV_INITIALIZING &&
 152            dev->dev_state != MEI_DEV_POWER_UP) {
 153                if (dev->dev_state != MEI_DEV_DISABLED &&
 154                    dev->dev_state != MEI_DEV_POWER_DOWN)
 155                        dev->dev_state = MEI_DEV_RESETTING;
 156
 157                mei_cl_all_disconnect(dev);
 158
 159                /* remove entry if already in list */
 160                dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n");
 161                mei_cl_unlink(&dev->wd_cl);
 162                if (dev->open_handle_count > 0)
 163                        dev->open_handle_count--;
 164                mei_cl_unlink(&dev->iamthif_cl);
 165                if (dev->open_handle_count > 0)
 166                        dev->open_handle_count--;
 167
 168                mei_amthif_reset_params(dev);
 169                memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg));
 170        }
 171
 172        dev->me_clients_num = 0;
 173        dev->rd_msg_hdr = 0;
 174        dev->wd_pending = false;
 175
 176        if (unexpected)
 177                dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
 178                         mei_dev_state_str(dev->dev_state));
 179
 180        if (!interrupts_enabled) {
 181                dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n");
 182                return;
 183        }
 184
 185        ret = mei_hw_start(dev);
 186        if (ret) {
 187                dev_err(&dev->pdev->dev, "hw_start failed disabling the device\n");
 188                dev->dev_state = MEI_DEV_DISABLED;
 189                return;
 190        }
 191
 192        dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
 193        /* link is established * start sending messages.  */
 194
 195        dev->dev_state = MEI_DEV_INIT_CLIENTS;
 196
 197        mei_hbm_start_req(dev);
 198
 199        /* wake up all readings so they can be interrupted */
 200        mei_cl_all_read_wakeup(dev);
 201
 202        /* remove all waiting requests */
 203        mei_cl_all_write_clear(dev);
 204}
 205EXPORT_SYMBOL_GPL(mei_reset);
 206
 207void mei_stop(struct mei_device *dev)
 208{
 209        dev_dbg(&dev->pdev->dev, "stopping the device.\n");
 210
 211        flush_scheduled_work();
 212
 213        mutex_lock(&dev->device_lock);
 214
 215        cancel_delayed_work(&dev->timer_work);
 216
 217        mei_wd_stop(dev);
 218
 219        mei_nfc_host_exit();
 220
 221        dev->dev_state = MEI_DEV_POWER_DOWN;
 222        mei_reset(dev, 0);
 223
 224        mutex_unlock(&dev->device_lock);
 225
 226        mei_watchdog_unregister(dev);
 227}
 228EXPORT_SYMBOL_GPL(mei_stop);
 229
 230
 231
 232