linux/drivers/misc/mei/wd.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#include <linux/kernel.h>
  17#include <linux/module.h>
  18#include <linux/moduleparam.h>
  19#include <linux/device.h>
  20#include <linux/sched.h>
  21#include <linux/watchdog.h>
  22
  23#include <linux/mei.h>
  24
  25#include "mei_dev.h"
  26#include "hbm.h"
  27#include "client.h"
  28
  29static const u8 mei_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 };
  30static const u8 mei_stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 };
  31
  32/*
  33 * AMT Watchdog Device
  34 */
  35#define INTEL_AMT_WATCHDOG_ID "INTCAMT"
  36
  37/* UUIDs for AMT F/W clients */
  38const uuid_le mei_wd_guid = UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, 0x89,
  39                                                0x9D, 0xA9, 0x15, 0x14, 0xCB,
  40                                                0x32, 0xAB);
  41
  42static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout)
  43{
  44        dev_dbg(dev->dev, "wd: set timeout=%d.\n", timeout);
  45        memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE);
  46        memcpy(dev->wd_data + MEI_WD_HDR_SIZE, &timeout, sizeof(u16));
  47}
  48
  49/**
  50 * mei_wd_host_init - connect to the watchdog client
  51 *
  52 * @dev: the device structure
  53 *
  54 * Return: -ENOTTY if wd client cannot be found
  55 *         -EIO if write has failed
  56 *         0 on success
  57 */
  58int mei_wd_host_init(struct mei_device *dev)
  59{
  60        struct mei_cl *cl = &dev->wd_cl;
  61        struct mei_me_client *me_cl;
  62        int ret;
  63
  64        mei_cl_init(cl, dev);
  65
  66        dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT;
  67        dev->wd_state = MEI_WD_IDLE;
  68
  69
  70        /* check for valid client id */
  71        me_cl = mei_me_cl_by_uuid(dev, &mei_wd_guid);
  72        if (!me_cl) {
  73                dev_info(dev->dev, "wd: failed to find the client\n");
  74                return -ENOTTY;
  75        }
  76
  77        cl->me_client_id = me_cl->client_id;
  78        cl->cl_uuid = me_cl->props.protocol_name;
  79        mei_me_cl_put(me_cl);
  80
  81        ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID);
  82
  83        if (ret < 0) {
  84                dev_info(dev->dev, "wd: failed link client\n");
  85                return ret;
  86        }
  87
  88        ret = mei_cl_connect(cl, NULL);
  89
  90        if (ret) {
  91                dev_err(dev->dev, "wd: failed to connect = %d\n", ret);
  92                mei_cl_unlink(cl);
  93                return ret;
  94        }
  95
  96        ret = mei_watchdog_register(dev);
  97        if (ret) {
  98                mei_cl_disconnect(cl);
  99                mei_cl_unlink(cl);
 100        }
 101        return ret;
 102}
 103
 104/**
 105 * mei_wd_send - sends watch dog message to fw.
 106 *
 107 * @dev: the device structure
 108 *
 109 * Return: 0 if success,
 110 *      -EIO when message send fails
 111 *      -EINVAL when invalid message is to be sent
 112 *      -ENODEV on flow control failure
 113 */
 114int mei_wd_send(struct mei_device *dev)
 115{
 116        struct mei_cl *cl = &dev->wd_cl;
 117        struct mei_msg_hdr hdr;
 118        int ret;
 119
 120        hdr.host_addr = cl->host_client_id;
 121        hdr.me_addr = cl->me_client_id;
 122        hdr.msg_complete = 1;
 123        hdr.reserved = 0;
 124        hdr.internal = 0;
 125
 126        if (!memcmp(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE))
 127                hdr.length = MEI_WD_START_MSG_SIZE;
 128        else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_HDR_SIZE))
 129                hdr.length = MEI_WD_STOP_MSG_SIZE;
 130        else {
 131                dev_err(dev->dev, "wd: invalid message is to be sent, aborting\n");
 132                return -EINVAL;
 133        }
 134
 135        ret = mei_write_message(dev, &hdr, dev->wd_data);
 136        if (ret) {
 137                dev_err(dev->dev, "wd: write message failed\n");
 138                return ret;
 139        }
 140
 141        ret = mei_cl_flow_ctrl_reduce(cl);
 142        if (ret) {
 143                dev_err(dev->dev, "wd: flow_ctrl_reduce failed.\n");
 144                return ret;
 145        }
 146
 147        return 0;
 148}
 149
 150/**
 151 * mei_wd_stop - sends watchdog stop message to fw.
 152 *
 153 * @dev: the device structure
 154 *
 155 * Return: 0 if success
 156 * on error:
 157 *      -EIO    when message send fails
 158 *      -EINVAL when invalid message is to be sent
 159 *      -ETIME  on message timeout
 160 */
 161int mei_wd_stop(struct mei_device *dev)
 162{
 163        struct mei_cl *cl = &dev->wd_cl;
 164        int ret;
 165
 166        if (!mei_cl_is_connected(cl) ||
 167            dev->wd_state != MEI_WD_RUNNING)
 168                return 0;
 169
 170        memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_STOP_MSG_SIZE);
 171
 172        dev->wd_state = MEI_WD_STOPPING;
 173
 174        ret = mei_cl_flow_ctrl_creds(cl);
 175        if (ret < 0)
 176                goto err;
 177
 178        if (ret && mei_hbuf_acquire(dev)) {
 179                ret = mei_wd_send(dev);
 180                if (ret)
 181                        goto err;
 182                dev->wd_pending = false;
 183        } else {
 184                dev->wd_pending = true;
 185        }
 186
 187        mutex_unlock(&dev->device_lock);
 188
 189        ret = wait_event_timeout(dev->wait_stop_wd,
 190                                dev->wd_state == MEI_WD_IDLE,
 191                                msecs_to_jiffies(MEI_WD_STOP_TIMEOUT));
 192        mutex_lock(&dev->device_lock);
 193        if (dev->wd_state != MEI_WD_IDLE) {
 194                /* timeout */
 195                ret = -ETIME;
 196                dev_warn(dev->dev, "wd: stop failed to complete ret=%d\n", ret);
 197                goto err;
 198        }
 199        dev_dbg(dev->dev, "wd: stop completed after %u msec\n",
 200                        MEI_WD_STOP_TIMEOUT - jiffies_to_msecs(ret));
 201        return 0;
 202err:
 203        return ret;
 204}
 205
 206/**
 207 * mei_wd_ops_start - wd start command from the watchdog core.
 208 *
 209 * @wd_dev: watchdog device struct
 210 *
 211 * Return: 0 if success, negative errno code for failure
 212 */
 213static int mei_wd_ops_start(struct watchdog_device *wd_dev)
 214{
 215        struct mei_device *dev;
 216        struct mei_cl *cl;
 217        int err = -ENODEV;
 218
 219        dev = watchdog_get_drvdata(wd_dev);
 220        if (!dev)
 221                return -ENODEV;
 222
 223        cl = &dev->wd_cl;
 224
 225        mutex_lock(&dev->device_lock);
 226
 227        if (dev->dev_state != MEI_DEV_ENABLED) {
 228                dev_dbg(dev->dev, "wd: dev_state != MEI_DEV_ENABLED  dev_state = %s\n",
 229                        mei_dev_state_str(dev->dev_state));
 230                goto end_unlock;
 231        }
 232
 233        if (!mei_cl_is_connected(cl)) {
 234                cl_dbg(dev, cl, "MEI Driver is not connected to Watchdog Client\n");
 235                goto end_unlock;
 236        }
 237
 238        mei_wd_set_start_timeout(dev, dev->wd_timeout);
 239
 240        err = 0;
 241end_unlock:
 242        mutex_unlock(&dev->device_lock);
 243        return err;
 244}
 245
 246/**
 247 * mei_wd_ops_stop -  wd stop command from the watchdog core.
 248 *
 249 * @wd_dev: watchdog device struct
 250 *
 251 * Return: 0 if success, negative errno code for failure
 252 */
 253static int mei_wd_ops_stop(struct watchdog_device *wd_dev)
 254{
 255        struct mei_device *dev;
 256
 257        dev = watchdog_get_drvdata(wd_dev);
 258        if (!dev)
 259                return -ENODEV;
 260
 261        mutex_lock(&dev->device_lock);
 262        mei_wd_stop(dev);
 263        mutex_unlock(&dev->device_lock);
 264
 265        return 0;
 266}
 267
 268/**
 269 * mei_wd_ops_ping - wd ping command from the watchdog core.
 270 *
 271 * @wd_dev: watchdog device struct
 272 *
 273 * Return: 0 if success, negative errno code for failure
 274 */
 275static int mei_wd_ops_ping(struct watchdog_device *wd_dev)
 276{
 277        struct mei_device *dev;
 278        struct mei_cl *cl;
 279        int ret;
 280
 281        dev = watchdog_get_drvdata(wd_dev);
 282        if (!dev)
 283                return -ENODEV;
 284
 285        cl = &dev->wd_cl;
 286
 287        mutex_lock(&dev->device_lock);
 288
 289        if (!mei_cl_is_connected(cl)) {
 290                cl_err(dev, cl, "wd: not connected.\n");
 291                ret = -ENODEV;
 292                goto end;
 293        }
 294
 295        dev->wd_state = MEI_WD_RUNNING;
 296
 297        ret = mei_cl_flow_ctrl_creds(cl);
 298        if (ret < 0)
 299                goto end;
 300
 301        /* Check if we can send the ping to HW*/
 302        if (ret && mei_hbuf_acquire(dev)) {
 303                dev_dbg(dev->dev, "wd: sending ping\n");
 304
 305                ret = mei_wd_send(dev);
 306                if (ret)
 307                        goto end;
 308                dev->wd_pending = false;
 309        } else {
 310                dev->wd_pending = true;
 311        }
 312
 313end:
 314        mutex_unlock(&dev->device_lock);
 315        return ret;
 316}
 317
 318/**
 319 * mei_wd_ops_set_timeout - wd set timeout command from the watchdog core.
 320 *
 321 * @wd_dev: watchdog device struct
 322 * @timeout: timeout value to set
 323 *
 324 * Return: 0 if success, negative errno code for failure
 325 */
 326static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev,
 327                unsigned int timeout)
 328{
 329        struct mei_device *dev;
 330
 331        dev = watchdog_get_drvdata(wd_dev);
 332        if (!dev)
 333                return -ENODEV;
 334
 335        /* Check Timeout value */
 336        if (timeout < MEI_WD_MIN_TIMEOUT || timeout > MEI_WD_MAX_TIMEOUT)
 337                return -EINVAL;
 338
 339        mutex_lock(&dev->device_lock);
 340
 341        dev->wd_timeout = timeout;
 342        wd_dev->timeout = timeout;
 343        mei_wd_set_start_timeout(dev, dev->wd_timeout);
 344
 345        mutex_unlock(&dev->device_lock);
 346
 347        return 0;
 348}
 349
 350/*
 351 * Watchdog Device structs
 352 */
 353static const struct watchdog_ops wd_ops = {
 354                .owner = THIS_MODULE,
 355                .start = mei_wd_ops_start,
 356                .stop = mei_wd_ops_stop,
 357                .ping = mei_wd_ops_ping,
 358                .set_timeout = mei_wd_ops_set_timeout,
 359};
 360static const struct watchdog_info wd_info = {
 361                .identity = INTEL_AMT_WATCHDOG_ID,
 362                .options = WDIOF_KEEPALIVEPING |
 363                           WDIOF_SETTIMEOUT |
 364                           WDIOF_ALARMONLY,
 365};
 366
 367static struct watchdog_device amt_wd_dev = {
 368                .info = &wd_info,
 369                .ops = &wd_ops,
 370                .timeout = MEI_WD_DEFAULT_TIMEOUT,
 371                .min_timeout = MEI_WD_MIN_TIMEOUT,
 372                .max_timeout = MEI_WD_MAX_TIMEOUT,
 373};
 374
 375
 376int mei_watchdog_register(struct mei_device *dev)
 377{
 378
 379        int ret;
 380
 381        /* unlock to perserve correct locking order */
 382        mutex_unlock(&dev->device_lock);
 383        ret = watchdog_register_device(&amt_wd_dev);
 384        mutex_lock(&dev->device_lock);
 385        if (ret) {
 386                dev_err(dev->dev, "wd: unable to register watchdog device = %d.\n",
 387                        ret);
 388                return ret;
 389        }
 390
 391        dev_dbg(dev->dev, "wd: successfully register watchdog interface.\n");
 392        watchdog_set_drvdata(&amt_wd_dev, dev);
 393        return 0;
 394}
 395
 396void mei_watchdog_unregister(struct mei_device *dev)
 397{
 398        if (watchdog_get_drvdata(&amt_wd_dev) == NULL)
 399                return;
 400
 401        watchdog_set_drvdata(&amt_wd_dev, NULL);
 402        watchdog_unregister_device(&amt_wd_dev);
 403}
 404
 405