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