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