root/arch/sh/boards/mach-x3proto/ilsel.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ilsel_offset
  2. mk_ilsel_addr
  3. mk_ilsel_shift
  4. __ilsel_enable
  5. ilsel_enable
  6. ilsel_enable_fixed
  7. ilsel_disable

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * arch/sh/boards/mach-x3proto/ilsel.c
   4  *
   5  * Helper routines for SH-X3 proto board ILSEL.
   6  *
   7  * Copyright (C) 2007 - 2010  Paul Mundt
   8  */
   9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  10 
  11 #include <linux/init.h>
  12 #include <linux/kernel.h>
  13 #include <linux/module.h>
  14 #include <linux/bitmap.h>
  15 #include <linux/io.h>
  16 #include <mach/ilsel.h>
  17 
  18 /*
  19  * ILSEL is split across:
  20  *
  21  *      ILSEL0 - 0xb8100004 [ Levels  1 -  4 ]
  22  *      ILSEL1 - 0xb8100006 [ Levels  5 -  8 ]
  23  *      ILSEL2 - 0xb8100008 [ Levels  9 - 12 ]
  24  *      ILSEL3 - 0xb810000a [ Levels 13 - 15 ]
  25  *
  26  * With each level being relative to an ilsel_source_t.
  27  */
  28 #define ILSEL_BASE      0xb8100004
  29 #define ILSEL_LEVELS    15
  30 
  31 /*
  32  * ILSEL level map, in descending order from the highest level down.
  33  *
  34  * Supported levels are 1 - 15 spread across ILSEL0 - ILSEL4, mapping
  35  * directly to IRLs. As the IRQs are numbered in reverse order relative
  36  * to the interrupt level, the level map is carefully managed to ensure a
  37  * 1:1 mapping between the bit position and the IRQ number.
  38  *
  39  * This careful constructions allows ilsel_enable*() to be referenced
  40  * directly for hooking up an ILSEL set and getting back an IRQ which can
  41  * subsequently be used for internal accounting in the (optional) disable
  42  * path.
  43  */
  44 static unsigned long ilsel_level_map;
  45 
  46 static inline unsigned int ilsel_offset(unsigned int bit)
  47 {
  48         return ILSEL_LEVELS - bit - 1;
  49 }
  50 
  51 static inline unsigned long mk_ilsel_addr(unsigned int bit)
  52 {
  53         return ILSEL_BASE + ((ilsel_offset(bit) >> 1) & ~0x1);
  54 }
  55 
  56 static inline unsigned int mk_ilsel_shift(unsigned int bit)
  57 {
  58         return (ilsel_offset(bit) & 0x3) << 2;
  59 }
  60 
  61 static void __ilsel_enable(ilsel_source_t set, unsigned int bit)
  62 {
  63         unsigned int tmp, shift;
  64         unsigned long addr;
  65 
  66         pr_notice("enabling ILSEL set %d\n", set);
  67 
  68         addr = mk_ilsel_addr(bit);
  69         shift = mk_ilsel_shift(bit);
  70 
  71         pr_debug("%s: bit#%d: addr - 0x%08lx (shift %d, set %d)\n",
  72                  __func__, bit, addr, shift, set);
  73 
  74         tmp = __raw_readw(addr);
  75         tmp &= ~(0xf << shift);
  76         tmp |= set << shift;
  77         __raw_writew(tmp, addr);
  78 }
  79 
  80 /**
  81  * ilsel_enable - Enable an ILSEL set.
  82  * @set: ILSEL source (see ilsel_source_t enum in include/asm-sh/ilsel.h).
  83  *
  84  * Enables a given non-aliased ILSEL source (<= ILSEL_KEY) at the highest
  85  * available interrupt level. Callers should take care to order callsites
  86  * noting descending interrupt levels. Aliasing FPGA and external board
  87  * IRQs need to use ilsel_enable_fixed().
  88  *
  89  * The return value is an IRQ number that can later be taken down with
  90  * ilsel_disable().
  91  */
  92 int ilsel_enable(ilsel_source_t set)
  93 {
  94         unsigned int bit;
  95 
  96         if (unlikely(set > ILSEL_KEY)) {
  97                 pr_err("Aliased sources must use ilsel_enable_fixed()\n");
  98                 return -EINVAL;
  99         }
 100 
 101         do {
 102                 bit = find_first_zero_bit(&ilsel_level_map, ILSEL_LEVELS);
 103         } while (test_and_set_bit(bit, &ilsel_level_map));
 104 
 105         __ilsel_enable(set, bit);
 106 
 107         return bit;
 108 }
 109 EXPORT_SYMBOL_GPL(ilsel_enable);
 110 
 111 /**
 112  * ilsel_enable_fixed - Enable an ILSEL set at a fixed interrupt level
 113  * @set: ILSEL source (see ilsel_source_t enum in include/asm-sh/ilsel.h).
 114  * @level: Interrupt level (1 - 15)
 115  *
 116  * Enables a given ILSEL source at a fixed interrupt level. Necessary
 117  * both for level reservation as well as for aliased sources that only
 118  * exist on special ILSEL#s.
 119  *
 120  * Returns an IRQ number (as ilsel_enable()).
 121  */
 122 int ilsel_enable_fixed(ilsel_source_t set, unsigned int level)
 123 {
 124         unsigned int bit = ilsel_offset(level - 1);
 125 
 126         if (test_and_set_bit(bit, &ilsel_level_map))
 127                 return -EBUSY;
 128 
 129         __ilsel_enable(set, bit);
 130 
 131         return bit;
 132 }
 133 EXPORT_SYMBOL_GPL(ilsel_enable_fixed);
 134 
 135 /**
 136  * ilsel_disable - Disable an ILSEL set
 137  * @irq: Bit position for ILSEL set value (retval from enable routines)
 138  *
 139  * Disable a previously enabled ILSEL set.
 140  */
 141 void ilsel_disable(unsigned int irq)
 142 {
 143         unsigned long addr;
 144         unsigned int tmp;
 145 
 146         pr_notice("disabling ILSEL set %d\n", irq);
 147 
 148         addr = mk_ilsel_addr(irq);
 149 
 150         tmp = __raw_readw(addr);
 151         tmp &= ~(0xf << mk_ilsel_shift(irq));
 152         __raw_writew(tmp, addr);
 153 
 154         clear_bit(irq, &ilsel_level_map);
 155 }
 156 EXPORT_SYMBOL_GPL(ilsel_disable);

/* [<][>][^][v][top][bottom][index][help] */