linux/drivers/staging/meilhaus/me0600_ext_irq.c
<<
>>
Prefs
   1/**
   2 * @file me0600_ext_irq.c
   3 *
   4 * @brief ME-630 external interrupt subdevice instance.
   5 * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
   6 * @author Guenter Gebhardt
   7 * @author Krzysztof Gantzke    (k.gantzke@meilhaus.de)
   8 */
   9
  10/*
  11 * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
  12 *
  13 * This file is free software; you can redistribute it and/or modify
  14 * it under the terms of the GNU General Public License as published by
  15 * the Free Software Foundation; either version 2 of the License, or
  16 * (at your option) any later version.
  17 *
  18 * This program is distributed in the hope that it will be useful,
  19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21 * GNU General Public License for more details.
  22 *
  23 * You should have received a copy of the GNU General Public License
  24 * along with this program; if not, write to the Free Software
  25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  26 */
  27
  28#ifndef __KERNEL__
  29#  define __KERNEL__
  30#endif
  31
  32/*
  33 * Includes
  34 */
  35#include <linux/version.h>
  36#include <linux/module.h>
  37
  38#include <linux/slab.h>
  39#include <linux/spinlock.h>
  40#include <asm/io.h>
  41#include <linux/types.h>
  42#include <linux/interrupt.h>
  43
  44#include "medefines.h"
  45#include "meinternal.h"
  46#include "meerror.h"
  47#include "meids.h"
  48#include "medebug.h"
  49
  50#include "meplx_reg.h"
  51#include "me0600_ext_irq_reg.h"
  52#include "me0600_ext_irq.h"
  53
  54/*
  55 * Functions
  56 */
  57
  58static int me0600_ext_irq_io_irq_start(struct me_subdevice *subdevice,
  59                                       struct file *filep,
  60                                       int channel,
  61                                       int irq_source,
  62                                       int irq_edge, int irq_arg, int flags)
  63{
  64        me0600_ext_irq_subdevice_t *instance;
  65        uint32_t tmp;
  66        unsigned long cpu_flags;
  67
  68        PDEBUG("executed.\n");
  69
  70        instance = (me0600_ext_irq_subdevice_t *) subdevice;
  71
  72        if (flags & ~ME_IO_IRQ_START_DIO_BIT) {
  73                PERROR("Invalid flag specified.\n");
  74                return ME_ERRNO_INVALID_FLAGS;
  75        }
  76
  77        if (instance->lintno > 1) {
  78                PERROR("Wrong idx=%d.\n", instance->lintno);
  79                return ME_ERRNO_INVALID_SUBDEVICE;
  80        }
  81
  82        if (channel) {
  83                PERROR("Invalid channel specified.\n");
  84                return ME_ERRNO_INVALID_CHANNEL;
  85        }
  86
  87        if (irq_source != ME_IRQ_SOURCE_DIO_LINE) {
  88                PERROR("Invalid irq source specified.\n");
  89                return ME_ERRNO_INVALID_IRQ_SOURCE;
  90        }
  91
  92        if (irq_edge != ME_IRQ_EDGE_RISING) {
  93                PERROR("Invalid irq edge specified.\n");
  94                return ME_ERRNO_INVALID_IRQ_EDGE;
  95        }
  96
  97        ME_SUBDEVICE_ENTER;
  98
  99        spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
 100        spin_lock(instance->intcsr_lock);
 101        tmp = inl(instance->intcsr);
 102        switch (instance->lintno) {
 103        case 0:
 104                tmp |=
 105                    PLX_INTCSR_LOCAL_INT1_EN | PLX_INTCSR_LOCAL_INT1_POL |
 106                    PLX_INTCSR_PCI_INT_EN;
 107                break;
 108        case 1:
 109                tmp |=
 110                    PLX_INTCSR_LOCAL_INT2_EN | PLX_INTCSR_LOCAL_INT2_POL |
 111                    PLX_INTCSR_PCI_INT_EN;
 112                break;
 113        }
 114        outl(tmp, instance->intcsr);
 115        PDEBUG_REG("intcsr outl(plx:0x%X)=0x%x\n", instance->intcsr, tmp);
 116        spin_unlock(instance->intcsr_lock);
 117        instance->rised = 0;
 118        spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
 119
 120        ME_SUBDEVICE_EXIT;
 121
 122        return ME_ERRNO_SUCCESS;
 123}
 124
 125static int me0600_ext_irq_io_irq_wait(struct me_subdevice *subdevice,
 126                                      struct file *filep,
 127                                      int channel,
 128                                      int *irq_count,
 129                                      int *value, int time_out, int flags)
 130{
 131        me0600_ext_irq_subdevice_t *instance;
 132        int err = ME_ERRNO_SUCCESS;
 133        long t = 0;
 134        unsigned long cpu_flags;
 135
 136        PDEBUG("executed.\n");
 137
 138        instance = (me0600_ext_irq_subdevice_t *) subdevice;
 139
 140        if (flags) {
 141                PERROR("Invalid flag specified.\n");
 142                return ME_ERRNO_INVALID_FLAGS;
 143        }
 144
 145        if (channel) {
 146                PERROR("Invalid channel specified.\n");
 147                return ME_ERRNO_INVALID_CHANNEL;
 148        }
 149
 150        if (time_out < 0) {
 151                PERROR("Invalid time_out specified.\n");
 152                return ME_ERRNO_INVALID_TIMEOUT;
 153        }
 154
 155        if (time_out) {
 156                t = (time_out * HZ) / 1000;
 157
 158                if (t == 0)
 159                        t = 1;
 160        }
 161
 162        ME_SUBDEVICE_ENTER;
 163
 164        if (instance->rised <= 0) {
 165                instance->rised = 0;
 166
 167                if (time_out) {
 168                        t = wait_event_interruptible_timeout(instance->
 169                                                             wait_queue,
 170                                                             (instance->rised !=
 171                                                              0), t);
 172
 173                        if (t == 0) {
 174                                PERROR("Wait on interrupt timed out.\n");
 175                                err = ME_ERRNO_TIMEOUT;
 176                        }
 177                } else {
 178                        wait_event_interruptible(instance->wait_queue,
 179                                                 (instance->rised != 0));
 180                }
 181
 182                if (instance->rised < 0) {
 183                        PERROR("Wait on interrupt aborted by user.\n");
 184                        err = ME_ERRNO_CANCELLED;
 185                }
 186        }
 187
 188        if (signal_pending(current)) {
 189                PERROR("Wait on interrupt aborted by signal.\n");
 190                err = ME_ERRNO_SIGNAL;
 191        }
 192
 193        spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
 194        instance->rised = 0;
 195        *irq_count = instance->n;
 196        *value = 1;
 197        spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
 198
 199        ME_SUBDEVICE_EXIT;
 200
 201        return err;
 202}
 203
 204static int me0600_ext_irq_io_irq_stop(struct me_subdevice *subdevice,
 205                                      struct file *filep,
 206                                      int channel, int flags)
 207{
 208        me0600_ext_irq_subdevice_t *instance;
 209        int err = ME_ERRNO_SUCCESS;
 210        uint32_t tmp;
 211        unsigned long cpu_flags;
 212
 213        PDEBUG("executed.\n");
 214
 215        instance = (me0600_ext_irq_subdevice_t *) subdevice;
 216
 217        if (flags) {
 218                PERROR("Invalid flag specified.\n");
 219                return ME_ERRNO_INVALID_FLAGS;
 220        }
 221
 222        if (instance->lintno > 1) {
 223                PERROR("Wrong idx=%d.\n", instance->lintno);
 224                return ME_ERRNO_INVALID_SUBDEVICE;
 225        }
 226
 227        if (channel) {
 228                PERROR("Invalid channel specified.\n");
 229                return ME_ERRNO_INVALID_CHANNEL;
 230        }
 231
 232        ME_SUBDEVICE_ENTER;
 233
 234        spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
 235        spin_lock(instance->intcsr_lock);
 236        tmp = inl(instance->intcsr);
 237        switch (instance->lintno) {
 238        case 0:
 239                tmp &= ~PLX_INTCSR_LOCAL_INT1_EN;
 240                break;
 241        case 1:
 242                tmp &= ~PLX_INTCSR_LOCAL_INT2_EN;
 243                break;
 244        }
 245        outl(tmp, instance->intcsr);
 246        PDEBUG_REG("intcsr outl(plx:0x%X)=0x%x\n", instance->intcsr, tmp);
 247        spin_unlock(instance->intcsr_lock);
 248        instance->rised = -1;
 249        spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
 250        wake_up_interruptible_all(&instance->wait_queue);
 251
 252        ME_SUBDEVICE_EXIT;
 253
 254        return err;
 255}
 256
 257static int me0600_ext_irq_io_reset_subdevice(struct me_subdevice *subdevice,
 258                                             struct file *filep, int flags)
 259{
 260        me0600_ext_irq_subdevice_t *instance;
 261        uint32_t tmp;
 262        unsigned long cpu_flags;
 263
 264        PDEBUG("executed.\n");
 265
 266        instance = (me0600_ext_irq_subdevice_t *) subdevice;
 267
 268        if (flags) {
 269                PERROR("Invalid flag specified.\n");
 270                return ME_ERRNO_INVALID_FLAGS;
 271        }
 272
 273        ME_SUBDEVICE_ENTER;
 274
 275        spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
 276        spin_lock(instance->intcsr_lock);
 277        tmp = inl(instance->intcsr);
 278        switch (instance->lintno) {
 279        case 0:
 280                tmp |= PLX_INTCSR_LOCAL_INT1_POL | PLX_INTCSR_PCI_INT_EN;
 281                tmp &= ~PLX_INTCSR_LOCAL_INT1_EN;
 282                break;
 283        case 1:
 284                tmp |= PLX_INTCSR_LOCAL_INT2_POL | PLX_INTCSR_PCI_INT_EN;
 285                tmp &= ~PLX_INTCSR_LOCAL_INT2_EN;
 286                break;
 287        }
 288        outl(tmp, instance->intcsr);
 289        PDEBUG_REG("intcsr outl(plx:0x%X)=0x%x\n", instance->intcsr, tmp);
 290        spin_unlock(instance->intcsr_lock);
 291
 292        instance->rised = -1;
 293        instance->n = 0;
 294        spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
 295        wake_up_interruptible_all(&instance->wait_queue);
 296
 297        ME_SUBDEVICE_EXIT;
 298
 299        return ME_ERRNO_SUCCESS;
 300}
 301
 302static int me0600_ext_irq_query_number_channels(struct me_subdevice *subdevice,
 303                                                int *number)
 304{
 305        PDEBUG("executed.\n");
 306        *number = 1;
 307        return ME_ERRNO_SUCCESS;
 308}
 309
 310static int me0600_ext_irq_query_subdevice_type(struct me_subdevice *subdevice,
 311                                               int *type, int *subtype)
 312{
 313        PDEBUG("executed.\n");
 314        *type = ME_TYPE_EXT_IRQ;
 315        *subtype = ME_SUBTYPE_SINGLE;
 316        return ME_ERRNO_SUCCESS;
 317}
 318
 319static int me0600_ext_irq_query_subdevice_caps(struct me_subdevice *subdevice,
 320                                               int *caps)
 321{
 322        PDEBUG("executed.\n");
 323        *caps = ME_CAPS_EXT_IRQ_EDGE_RISING;
 324        return ME_ERRNO_SUCCESS;
 325}
 326
 327static void me0600_ext_irq_destructor(struct me_subdevice *subdevice)
 328{
 329        me0600_ext_irq_subdevice_t *instance;
 330
 331        PDEBUG("executed.\n");
 332
 333        instance = (me0600_ext_irq_subdevice_t *) subdevice;
 334
 335        free_irq(instance->irq, (void *)instance);
 336        me_subdevice_deinit(&instance->base);
 337        kfree(instance);
 338}
 339
 340#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
 341static irqreturn_t me0600_isr(int irq, void *dev_id)
 342#else
 343static irqreturn_t me0600_isr(int irq, void *dev_id, struct pt_regs *regs)
 344#endif
 345{
 346        me0600_ext_irq_subdevice_t *instance;
 347        uint32_t status;
 348        uint32_t mask = PLX_INTCSR_PCI_INT_EN;
 349        irqreturn_t ret = IRQ_HANDLED;
 350
 351        instance = (me0600_ext_irq_subdevice_t *) dev_id;
 352
 353        if (irq != instance->irq) {
 354                PERROR("Incorrect interrupt num: %d.\n", irq);
 355                return IRQ_NONE;
 356        }
 357
 358        PDEBUG("executed.\n");
 359
 360        if (instance->lintno > 1) {
 361                PERROR_CRITICAL
 362                    ("%s():Wrong subdevice index=%d plx:irq_status_reg=0x%04X.\n",
 363                     __func__, instance->lintno, inl(instance->intcsr));
 364                return IRQ_NONE;
 365        }
 366
 367        spin_lock(&instance->subdevice_lock);
 368        spin_lock(instance->intcsr_lock);
 369        status = inl(instance->intcsr);
 370        switch (instance->lintno) {
 371        case 0:
 372                mask |= PLX_INTCSR_LOCAL_INT1_STATE | PLX_INTCSR_LOCAL_INT1_EN;
 373                break;
 374        case 1:
 375                mask |= PLX_INTCSR_LOCAL_INT2_STATE | PLX_INTCSR_LOCAL_INT2_EN;
 376                break;
 377        }
 378
 379        if ((status & mask) == mask) {
 380                instance->rised = 1;
 381                instance->n++;
 382                inb(instance->reset_reg);
 383                PDEBUG("Interrupt detected.\n");
 384        } else {
 385                PINFO
 386                    ("%ld Shared interrupt. %s(): idx=0 plx:irq_status_reg=0x%04X\n",
 387                     jiffies, __func__, status);
 388                ret = IRQ_NONE;
 389        }
 390        spin_unlock(instance->intcsr_lock);
 391        spin_unlock(&instance->subdevice_lock);
 392
 393        wake_up_interruptible_all(&instance->wait_queue);
 394
 395        return ret;
 396}
 397
 398me0600_ext_irq_subdevice_t *me0600_ext_irq_constructor(uint32_t plx_reg_base,
 399                                                       uint32_t me0600_reg_base,
 400                                                       spinlock_t * intcsr_lock,
 401                                                       unsigned ext_irq_idx,
 402                                                       int irq)
 403{
 404        me0600_ext_irq_subdevice_t *subdevice;
 405        int err;
 406
 407        PDEBUG("executed.\n");
 408
 409        /* Allocate memory for subdevice instance */
 410        subdevice = kmalloc(sizeof(me0600_ext_irq_subdevice_t), GFP_KERNEL);
 411
 412        if (!subdevice) {
 413                PERROR("Cannot get memory for 630_ext_irq instance.\n");
 414                return NULL;
 415        }
 416
 417        memset(subdevice, 0, sizeof(me0600_ext_irq_subdevice_t));
 418
 419        /* Initialize subdevice base class */
 420        err = me_subdevice_init(&subdevice->base);
 421
 422        if (err) {
 423                PERROR("Cannot initialize subdevice base class instance.\n");
 424                kfree(subdevice);
 425                return NULL;
 426        }
 427        // Initialize spin locks.
 428        spin_lock_init(&subdevice->subdevice_lock);
 429
 430        subdevice->intcsr_lock = intcsr_lock;
 431
 432        /* Initialize wait queue */
 433        init_waitqueue_head(&subdevice->wait_queue);
 434
 435        subdevice->lintno = ext_irq_idx;
 436
 437        /* Request interrupt line */
 438        subdevice->irq = irq;
 439
 440        err = request_irq(subdevice->irq, me0600_isr,
 441#ifdef IRQF_DISABLED
 442                          IRQF_DISABLED | IRQF_SHARED,
 443#else
 444                          SA_INTERRUPT | SA_SHIRQ,
 445#endif
 446                          ME0600_NAME, (void *)subdevice);
 447
 448        if (err) {
 449                PERROR("Cannot get interrupt line.\n");
 450                kfree(subdevice);
 451                return NULL;
 452        }
 453        PINFO("Registered irq=%d.\n", subdevice->irq);
 454
 455        /* Initialize registers */
 456        subdevice->intcsr = plx_reg_base + PLX_INTCSR;
 457        subdevice->reset_reg =
 458            me0600_reg_base + ME0600_INT_0_RESET_REG + ext_irq_idx;
 459
 460        /* Initialize the subdevice methods */
 461        subdevice->base.me_subdevice_io_irq_start = me0600_ext_irq_io_irq_start;
 462        subdevice->base.me_subdevice_io_irq_wait = me0600_ext_irq_io_irq_wait;
 463        subdevice->base.me_subdevice_io_irq_stop = me0600_ext_irq_io_irq_stop;
 464        subdevice->base.me_subdevice_io_reset_subdevice =
 465            me0600_ext_irq_io_reset_subdevice;
 466        subdevice->base.me_subdevice_query_number_channels =
 467            me0600_ext_irq_query_number_channels;
 468        subdevice->base.me_subdevice_query_subdevice_type =
 469            me0600_ext_irq_query_subdevice_type;
 470        subdevice->base.me_subdevice_query_subdevice_caps =
 471            me0600_ext_irq_query_subdevice_caps;
 472        subdevice->base.me_subdevice_destructor = me0600_ext_irq_destructor;
 473
 474        subdevice->rised = 0;
 475        subdevice->n = 0;
 476
 477        return subdevice;
 478}
 479
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.