linux/drivers/misc/mei/interrupt.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
  18#include <linux/export.h>
  19#include <linux/kthread.h>
  20#include <linux/interrupt.h>
  21#include <linux/fs.h>
  22#include <linux/jiffies.h>
  23#include <linux/slab.h>
  24#include <linux/pm_runtime.h>
  25
  26#include <linux/mei.h>
  27
  28#include "mei_dev.h"
  29#include "hbm.h"
  30#include "client.h"
  31
  32
  33/**
  34 * mei_irq_compl_handler - dispatch complete handlers
  35 *      for the completed callbacks
  36 *
  37 * @dev: mei device
  38 * @cmpl_list: list of completed cbs
  39 */
  40void mei_irq_compl_handler(struct mei_device *dev, struct list_head *cmpl_list)
  41{
  42        struct mei_cl_cb *cb, *next;
  43        struct mei_cl *cl;
  44
  45        list_for_each_entry_safe(cb, next, cmpl_list, list) {
  46                cl = cb->cl;
  47                list_del_init(&cb->list);
  48
  49                dev_dbg(dev->dev, "completing call back.\n");
  50                mei_cl_complete(cl, cb);
  51        }
  52}
  53EXPORT_SYMBOL_GPL(mei_irq_compl_handler);
  54
  55/**
  56 * mei_cl_hbm_equal - check if hbm is addressed to the client
  57 *
  58 * @cl: host client
  59 * @mei_hdr: header of mei client message
  60 *
  61 * Return: true if matches, false otherwise
  62 */
  63static inline int mei_cl_hbm_equal(struct mei_cl *cl,
  64                        struct mei_msg_hdr *mei_hdr)
  65{
  66        return  mei_cl_host_addr(cl) == mei_hdr->host_addr &&
  67                mei_cl_me_id(cl) == mei_hdr->me_addr;
  68}
  69
  70/**
  71 * mei_irq_discard_msg  - discard received message
  72 *
  73 * @dev: mei device
  74 * @hdr: message header
  75 */
  76static void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr)
  77{
  78        if (hdr->dma_ring)
  79                mei_dma_ring_read(dev, NULL, hdr->extension[0]);
  80        /*
  81         * no need to check for size as it is guarantied
  82         * that length fits into rd_msg_buf
  83         */
  84        mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
  85        dev_dbg(dev->dev, "discarding message " MEI_HDR_FMT "\n",
  86                MEI_HDR_PRM(hdr));
  87}
  88
  89/**
  90 * mei_cl_irq_read_msg - process client message
  91 *
  92 * @cl: reading client
  93 * @mei_hdr: header of mei client message
  94 * @cmpl_list: completion list
  95 *
  96 * Return: always 0
  97 */
  98static int mei_cl_irq_read_msg(struct mei_cl *cl,
  99                               struct mei_msg_hdr *mei_hdr,
 100                               struct list_head *cmpl_list)
 101{
 102        struct mei_device *dev = cl->dev;
 103        struct mei_cl_cb *cb;
 104        size_t buf_sz;
 105        u32 length;
 106
 107        cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list);
 108        if (!cb) {
 109                if (!mei_cl_is_fixed_address(cl)) {
 110                        cl_err(dev, cl, "pending read cb not found\n");
 111                        goto discard;
 112                }
 113                cb = mei_cl_alloc_cb(cl, mei_cl_mtu(cl), MEI_FOP_READ, cl->fp);
 114                if (!cb)
 115                        goto discard;
 116                list_add_tail(&cb->list, &cl->rd_pending);
 117        }
 118
 119        if (!mei_cl_is_connected(cl)) {
 120                cl_dbg(dev, cl, "not connected\n");
 121                cb->status = -ENODEV;
 122                goto discard;
 123        }
 124
 125        length = mei_hdr->dma_ring ? mei_hdr->extension[0] : mei_hdr->length;
 126
 127        buf_sz = length + cb->buf_idx;
 128        /* catch for integer overflow */
 129        if (buf_sz < cb->buf_idx) {
 130                cl_err(dev, cl, "message is too big len %d idx %zu\n",
 131                       length, cb->buf_idx);
 132                cb->status = -EMSGSIZE;
 133                goto discard;
 134        }
 135
 136        if (cb->buf.size < buf_sz) {
 137                cl_dbg(dev, cl, "message overflow. size %zu len %d idx %zu\n",
 138                        cb->buf.size, length, cb->buf_idx);
 139                cb->status = -EMSGSIZE;
 140                goto discard;
 141        }
 142
 143        if (mei_hdr->dma_ring)
 144                mei_dma_ring_read(dev, cb->buf.data + cb->buf_idx, length);
 145
 146        /*  for DMA read 0 length to generate an interrupt to the device */
 147        mei_read_slots(dev, cb->buf.data + cb->buf_idx, mei_hdr->length);
 148
 149        cb->buf_idx += length;
 150
 151        if (mei_hdr->msg_complete) {
 152                cl_dbg(dev, cl, "completed read length = %zu\n", cb->buf_idx);
 153                list_move_tail(&cb->list, cmpl_list);
 154        } else {
 155                pm_runtime_mark_last_busy(dev->dev);
 156                pm_request_autosuspend(dev->dev);
 157        }
 158
 159        return 0;
 160
 161discard:
 162        if (cb)
 163                list_move_tail(&cb->list, cmpl_list);
 164        mei_irq_discard_msg(dev, mei_hdr);
 165        return 0;
 166}
 167
 168/**
 169 * mei_cl_irq_disconnect_rsp - send disconnection response message
 170 *
 171 * @cl: client
 172 * @cb: callback block.
 173 * @cmpl_list: complete list.
 174 *
 175 * Return: 0, OK; otherwise, error.
 176 */
 177static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb,
 178                                     struct list_head *cmpl_list)
 179{
 180        struct mei_device *dev = cl->dev;
 181        u32 msg_slots;
 182        int slots;
 183        int ret;
 184
 185        msg_slots = mei_hbm2slots(sizeof(struct hbm_client_connect_response));
 186        slots = mei_hbuf_empty_slots(dev);
 187        if (slots < 0)
 188                return -EOVERFLOW;
 189
 190        if ((u32)slots < msg_slots)
 191                return -EMSGSIZE;
 192
 193        ret = mei_hbm_cl_disconnect_rsp(dev, cl);
 194        list_move_tail(&cb->list, cmpl_list);
 195
 196        return ret;
 197}
 198
 199/**
 200 * mei_cl_irq_read - processes client read related operation from the
 201 *      interrupt thread context - request for flow control credits
 202 *
 203 * @cl: client
 204 * @cb: callback block.
 205 * @cmpl_list: complete list.
 206 *
 207 * Return: 0, OK; otherwise, error.
 208 */
 209static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
 210                           struct list_head *cmpl_list)
 211{
 212        struct mei_device *dev = cl->dev;
 213        u32 msg_slots;
 214        int slots;
 215        int ret;
 216
 217        if (!list_empty(&cl->rd_pending))
 218                return 0;
 219
 220        msg_slots = mei_hbm2slots(sizeof(struct hbm_flow_control));
 221        slots = mei_hbuf_empty_slots(dev);
 222        if (slots < 0)
 223                return -EOVERFLOW;
 224
 225        if ((u32)slots < msg_slots)
 226                return -EMSGSIZE;
 227
 228        ret = mei_hbm_cl_flow_control_req(dev, cl);
 229        if (ret) {
 230                cl->status = ret;
 231                cb->buf_idx = 0;
 232                list_move_tail(&cb->list, cmpl_list);
 233                return ret;
 234        }
 235
 236        list_move_tail(&cb->list, &cl->rd_pending);
 237
 238        return 0;
 239}
 240
 241static inline bool hdr_is_hbm(struct mei_msg_hdr *mei_hdr)
 242{
 243        return mei_hdr->host_addr == 0 && mei_hdr->me_addr == 0;
 244}
 245
 246static inline bool hdr_is_fixed(struct mei_msg_hdr *mei_hdr)
 247{
 248        return mei_hdr->host_addr == 0 && mei_hdr->me_addr != 0;
 249}
 250
 251static inline int hdr_is_valid(u32 msg_hdr)
 252{
 253        struct mei_msg_hdr *mei_hdr;
 254
 255        mei_hdr = (struct mei_msg_hdr *)&msg_hdr;
 256        if (!msg_hdr || mei_hdr->reserved)
 257                return -EBADMSG;
 258
 259        if (mei_hdr->dma_ring && mei_hdr->length != MEI_SLOT_SIZE)
 260                return -EBADMSG;
 261
 262        return 0;
 263}
 264
 265/**
 266 * mei_irq_read_handler - bottom half read routine after ISR to
 267 * handle the read processing.
 268 *
 269 * @dev: the device structure
 270 * @cmpl_list: An instance of our list structure
 271 * @slots: slots to read.
 272 *
 273 * Return: 0 on success, <0 on failure.
 274 */
 275int mei_irq_read_handler(struct mei_device *dev,
 276                         struct list_head *cmpl_list, s32 *slots)
 277{
 278        struct mei_msg_hdr *mei_hdr;
 279        struct mei_cl *cl;
 280        int ret;
 281
 282        if (!dev->rd_msg_hdr[0]) {
 283                dev->rd_msg_hdr[0] = mei_read_hdr(dev);
 284                (*slots)--;
 285                dev_dbg(dev->dev, "slots =%08x.\n", *slots);
 286
 287                ret = hdr_is_valid(dev->rd_msg_hdr[0]);
 288                if (ret) {
 289                        dev_err(dev->dev, "corrupted message header 0x%08X\n",
 290                                dev->rd_msg_hdr[0]);
 291                        goto end;
 292                }
 293        }
 294
 295        mei_hdr = (struct mei_msg_hdr *)dev->rd_msg_hdr;
 296        dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
 297
 298        if (mei_slots2data(*slots) < mei_hdr->length) {
 299                dev_err(dev->dev, "less data available than length=%08x.\n",
 300                                *slots);
 301                /* we can't read the message */
 302                ret = -ENODATA;
 303                goto end;
 304        }
 305
 306        if (mei_hdr->dma_ring) {
 307                dev->rd_msg_hdr[1] = mei_read_hdr(dev);
 308                (*slots)--;
 309                mei_hdr->length = 0;
 310        }
 311
 312        /*  HBM message */
 313        if (hdr_is_hbm(mei_hdr)) {
 314                ret = mei_hbm_dispatch(dev, mei_hdr);
 315                if (ret) {
 316                        dev_dbg(dev->dev, "mei_hbm_dispatch failed ret = %d\n",
 317                                        ret);
 318                        goto end;
 319                }
 320                goto reset_slots;
 321        }
 322
 323        /* find recipient cl */
 324        list_for_each_entry(cl, &dev->file_list, link) {
 325                if (mei_cl_hbm_equal(cl, mei_hdr)) {
 326                        cl_dbg(dev, cl, "got a message\n");
 327                        break;
 328                }
 329        }
 330
 331        /* if no recipient cl was found we assume corrupted header */
 332        if (&cl->link == &dev->file_list) {
 333                /* A message for not connected fixed address clients
 334                 * should be silently discarded
 335                 * On power down client may be force cleaned,
 336                 * silently discard such messages
 337                 */
 338                if (hdr_is_fixed(mei_hdr) ||
 339                    dev->dev_state == MEI_DEV_POWER_DOWN) {
 340                        mei_irq_discard_msg(dev, mei_hdr);
 341                        ret = 0;
 342                        goto reset_slots;
 343                }
 344                dev_err(dev->dev, "no destination client found 0x%08X\n",
 345                                dev->rd_msg_hdr[0]);
 346                ret = -EBADMSG;
 347                goto end;
 348        }
 349
 350        ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list);
 351
 352
 353reset_slots:
 354        /* reset the number of slots and header */
 355        memset(dev->rd_msg_hdr, 0, sizeof(dev->rd_msg_hdr));
 356        *slots = mei_count_full_read_slots(dev);
 357        if (*slots == -EOVERFLOW) {
 358                /* overflow - reset */
 359                dev_err(dev->dev, "resetting due to slots overflow.\n");
 360                /* set the event since message has been read */
 361                ret = -ERANGE;
 362                goto end;
 363        }
 364end:
 365        return ret;
 366}
 367EXPORT_SYMBOL_GPL(mei_irq_read_handler);
 368
 369
 370/**
 371 * mei_irq_write_handler -  dispatch write requests
 372 *  after irq received
 373 *
 374 * @dev: the device structure
 375 * @cmpl_list: An instance of our list structure
 376 *
 377 * Return: 0 on success, <0 on failure.
 378 */
 379int mei_irq_write_handler(struct mei_device *dev, struct list_head *cmpl_list)
 380{
 381
 382        struct mei_cl *cl;
 383        struct mei_cl_cb *cb, *next;
 384        s32 slots;
 385        int ret;
 386
 387
 388        if (!mei_hbuf_acquire(dev))
 389                return 0;
 390
 391        slots = mei_hbuf_empty_slots(dev);
 392        if (slots < 0)
 393                return -EOVERFLOW;
 394
 395        if (slots == 0)
 396                return -EMSGSIZE;
 397
 398        /* complete all waiting for write CB */
 399        dev_dbg(dev->dev, "complete all waiting for write cb.\n");
 400
 401        list_for_each_entry_safe(cb, next, &dev->write_waiting_list, list) {
 402                cl = cb->cl;
 403
 404                cl->status = 0;
 405                cl_dbg(dev, cl, "MEI WRITE COMPLETE\n");
 406                cl->writing_state = MEI_WRITE_COMPLETE;
 407                list_move_tail(&cb->list, cmpl_list);
 408        }
 409
 410        /* complete control write list CB */
 411        dev_dbg(dev->dev, "complete control write list cb.\n");
 412        list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list, list) {
 413                cl = cb->cl;
 414                switch (cb->fop_type) {
 415                case MEI_FOP_DISCONNECT:
 416                        /* send disconnect message */
 417                        ret = mei_cl_irq_disconnect(cl, cb, cmpl_list);
 418                        if (ret)
 419                                return ret;
 420
 421                        break;
 422                case MEI_FOP_READ:
 423                        /* send flow control message */
 424                        ret = mei_cl_irq_read(cl, cb, cmpl_list);
 425                        if (ret)
 426                                return ret;
 427
 428                        break;
 429                case MEI_FOP_CONNECT:
 430                        /* connect message */
 431                        ret = mei_cl_irq_connect(cl, cb, cmpl_list);
 432                        if (ret)
 433                                return ret;
 434
 435                        break;
 436                case MEI_FOP_DISCONNECT_RSP:
 437                        /* send disconnect resp */
 438                        ret = mei_cl_irq_disconnect_rsp(cl, cb, cmpl_list);
 439                        if (ret)
 440                                return ret;
 441                        break;
 442
 443                case MEI_FOP_NOTIFY_START:
 444                case MEI_FOP_NOTIFY_STOP:
 445                        ret = mei_cl_irq_notify(cl, cb, cmpl_list);
 446                        if (ret)
 447                                return ret;
 448                        break;
 449                default:
 450                        BUG();
 451                }
 452
 453        }
 454        /* complete  write list CB */
 455        dev_dbg(dev->dev, "complete write list cb.\n");
 456        list_for_each_entry_safe(cb, next, &dev->write_list, list) {
 457                cl = cb->cl;
 458                ret = mei_cl_irq_write(cl, cb, cmpl_list);
 459                if (ret)
 460                        return ret;
 461        }
 462        return 0;
 463}
 464EXPORT_SYMBOL_GPL(mei_irq_write_handler);
 465
 466
 467/**
 468 * mei_connect_timeout  - connect/disconnect timeouts
 469 *
 470 * @cl: host client
 471 */
 472static void mei_connect_timeout(struct mei_cl *cl)
 473{
 474        struct mei_device *dev = cl->dev;
 475
 476        if (cl->state == MEI_FILE_CONNECTING) {
 477                if (dev->hbm_f_dot_supported) {
 478                        cl->state = MEI_FILE_DISCONNECT_REQUIRED;
 479                        wake_up(&cl->wait);
 480                        return;
 481                }
 482        }
 483        mei_reset(dev);
 484}
 485
 486#define MEI_STALL_TIMER_FREQ (2 * HZ)
 487/**
 488 * mei_schedule_stall_timer - re-arm stall_timer work
 489 *
 490 * Schedule stall timer
 491 *
 492 * @dev: the device structure
 493 */
 494void mei_schedule_stall_timer(struct mei_device *dev)
 495{
 496        schedule_delayed_work(&dev->timer_work, MEI_STALL_TIMER_FREQ);
 497}
 498
 499/**
 500 * mei_timer - timer function.
 501 *
 502 * @work: pointer to the work_struct structure
 503 *
 504 */
 505void mei_timer(struct work_struct *work)
 506{
 507        struct mei_cl *cl;
 508        struct mei_device *dev = container_of(work,
 509                                        struct mei_device, timer_work.work);
 510        bool reschedule_timer = false;
 511
 512        mutex_lock(&dev->device_lock);
 513
 514        /* Catch interrupt stalls during HBM init handshake */
 515        if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
 516            dev->hbm_state != MEI_HBM_IDLE) {
 517
 518                if (dev->init_clients_timer) {
 519                        if (--dev->init_clients_timer == 0) {
 520                                dev_err(dev->dev, "timer: init clients timeout hbm_state = %d.\n",
 521                                        dev->hbm_state);
 522                                mei_reset(dev);
 523                                goto out;
 524                        }
 525                        reschedule_timer = true;
 526                }
 527        }
 528
 529        if (dev->dev_state != MEI_DEV_ENABLED)
 530                goto out;
 531
 532        /*** connect/disconnect timeouts ***/
 533        list_for_each_entry(cl, &dev->file_list, link) {
 534                if (cl->timer_count) {
 535                        if (--cl->timer_count == 0) {
 536                                dev_err(dev->dev, "timer: connect/disconnect timeout.\n");
 537                                mei_connect_timeout(cl);
 538                                goto out;
 539                        }
 540                        reschedule_timer = true;
 541                }
 542        }
 543
 544out:
 545        if (dev->dev_state != MEI_DEV_DISABLED && reschedule_timer)
 546                mei_schedule_stall_timer(dev);
 547
 548        mutex_unlock(&dev->device_lock);
 549}
 550