Logo Search packages:      
Sourcecode: linux version File versions  Download package

modem-irq-db5500.c

/*
 * Copyright (C) ST-Ericsson SA 2010
 * Author: Stefan Nilsson <stefan.xk.nilsson@stericsson.com> for ST-Ericsson.
 * Author: Martin Persson <martin.persson@stericsson.com> for ST-Ericsson.
 * License terms: GNU General Public License (GPL), version 2.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/slab.h>

#include <mach/id.h>

#define MODEM_INTCON_BASE_ADDR 0xBFFD3000
#define MODEM_INTCON_SIZE 0xFFF

#define DEST_IRQ41_OFFSET 0x2A4
#define DEST_IRQ43_OFFSET 0x2AC
#define DEST_IRQ45_OFFSET 0x2B4

#define PRIO_IRQ41_OFFSET 0x6A4
#define PRIO_IRQ43_OFFSET 0x6AC
#define PRIO_IRQ45_OFFSET 0x6B4

#define ALLOW_IRQ_OFFSET 0x104

#define MODEM_INTCON_CPU_NBR 0x1
#define MODEM_INTCON_PRIO_HIGH 0x0

#define MODEM_INTCON_ALLOW_IRQ41 0x0200
#define MODEM_INTCON_ALLOW_IRQ43 0x0800
#define MODEM_INTCON_ALLOW_IRQ45 0x2000

#define MODEM_IRQ_REG_OFFSET 0x4

00039 struct modem_irq {
      void __iomem *modem_intcon_base;
};


static void setup_modem_intcon(void __iomem *modem_intcon_base)
{
      /* IC_DESTINATION_BASE_ARRAY - Which CPU to receive the IRQ */
      writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ41_OFFSET);
      writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ43_OFFSET);
      writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ45_OFFSET);

      /* IC_PRIORITY_BASE_ARRAY - IRQ priority in modem IRQ controller */
      writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ41_OFFSET);
      writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ43_OFFSET);
      writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ45_OFFSET);

      /* IC_ALLOW_ARRAY - IRQ enable */
      writel(MODEM_INTCON_ALLOW_IRQ41 |
               MODEM_INTCON_ALLOW_IRQ43 |
               MODEM_INTCON_ALLOW_IRQ45,
               modem_intcon_base + ALLOW_IRQ_OFFSET);
}

static irqreturn_t modem_cpu_irq_handler(int irq, void *data)
{
      int real_irq;
      int virt_irq;
      struct modem_irq *mi = (struct modem_irq *)data;

      /* Read modem side IRQ number from modem IRQ controller */
      real_irq = readl(mi->modem_intcon_base + MODEM_IRQ_REG_OFFSET) & 0xFF;
      virt_irq = IRQ_MODEM_EVENTS_BASE + real_irq;

      pr_debug("modem_irq: Worker read addr 0x%X and got value 0x%X "
             "which will be 0x%X (%d) which translates to "
             "virtual IRQ 0x%X (%d)!\n",
               (u32)mi->modem_intcon_base + MODEM_IRQ_REG_OFFSET,
               real_irq,
               real_irq & 0xFF,
               real_irq & 0xFF,
               virt_irq,
               virt_irq);

      if (virt_irq != 0)
            generic_handle_irq(virt_irq);

      pr_debug("modem_irq: Done handling virtual IRQ %d!\n", virt_irq);

      return IRQ_HANDLED;
}

static void create_virtual_irq(int irq, struct irq_chip *modem_irq_chip)
{
      set_irq_chip(irq, modem_irq_chip);
      set_irq_handler(irq, handle_simple_irq);
      set_irq_flags(irq, IRQF_VALID);

      pr_debug("modem_irq: Created virtual IRQ %d\n", irq);
}

static int modem_irq_init(void)
{
      int err;
      static struct irq_chip  modem_irq_chip;
      struct modem_irq *mi;

      if (!cpu_is_u5500())
            return -ENODEV;

      pr_info("modem_irq: Set up IRQ handler for incoming modem IRQ %d\n",
               IRQ_DB5500_MODEM);

      mi = kmalloc(sizeof(struct modem_irq), GFP_KERNEL);
      if (!mi) {
            pr_err("modem_irq: Could not allocate device\n");
            return -ENOMEM;
      }

      mi->modem_intcon_base =
            ioremap(MODEM_INTCON_BASE_ADDR, MODEM_INTCON_SIZE);
      pr_debug("modem_irq: ioremapped modem_intcon_base from "
             "phy 0x%x to virt 0x%x\n", MODEM_INTCON_BASE_ADDR,
             (u32)mi->modem_intcon_base);

      setup_modem_intcon(mi->modem_intcon_base);

      modem_irq_chip = dummy_irq_chip;
      modem_irq_chip.name = "modem_irq";

      /* Create the virtual IRQ:s needed */
      create_virtual_irq(MBOX_PAIR0_VIRT_IRQ, &modem_irq_chip);
      create_virtual_irq(MBOX_PAIR1_VIRT_IRQ, &modem_irq_chip);
      create_virtual_irq(MBOX_PAIR2_VIRT_IRQ, &modem_irq_chip);

      err = request_threaded_irq(IRQ_DB5500_MODEM, NULL,
                           modem_cpu_irq_handler, IRQF_ONESHOT,
                           "modem_irq", mi);
      if (err)
            pr_err("modem_irq: Could not register IRQ %d\n",
                   IRQ_DB5500_MODEM);

      return 0;
}

arch_initcall(modem_irq_init);

Generated by  Doxygen 1.6.0   Back to index