Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/xtensa/variants/s6000/gpio.c
10819 views
1
/*
2
* s6000 gpio driver
3
*
4
* Copyright (c) 2009 emlix GmbH
5
* Authors: Oskar Schirmer <[email protected]>
6
* Johannes Weiner <[email protected]>
7
* Daniel Gloeckner <[email protected]>
8
*/
9
#include <linux/bitops.h>
10
#include <linux/kernel.h>
11
#include <linux/module.h>
12
#include <linux/init.h>
13
#include <linux/io.h>
14
#include <linux/irq.h>
15
#include <linux/gpio.h>
16
17
#include <variant/hardware.h>
18
19
#define IRQ_BASE XTENSA_NR_IRQS
20
21
#define S6_GPIO_DATA 0x000
22
#define S6_GPIO_IS 0x404
23
#define S6_GPIO_IBE 0x408
24
#define S6_GPIO_IEV 0x40C
25
#define S6_GPIO_IE 0x410
26
#define S6_GPIO_RIS 0x414
27
#define S6_GPIO_MIS 0x418
28
#define S6_GPIO_IC 0x41C
29
#define S6_GPIO_AFSEL 0x420
30
#define S6_GPIO_DIR 0x800
31
#define S6_GPIO_BANK(nr) ((nr) * 0x1000)
32
#define S6_GPIO_MASK(nr) (4 << (nr))
33
#define S6_GPIO_OFFSET(nr) \
34
(S6_GPIO_BANK((nr) >> 3) + S6_GPIO_MASK((nr) & 7))
35
36
static int direction_input(struct gpio_chip *chip, unsigned int off)
37
{
38
writeb(0, S6_REG_GPIO + S6_GPIO_DIR + S6_GPIO_OFFSET(off));
39
return 0;
40
}
41
42
static int get(struct gpio_chip *chip, unsigned int off)
43
{
44
return readb(S6_REG_GPIO + S6_GPIO_DATA + S6_GPIO_OFFSET(off));
45
}
46
47
static int direction_output(struct gpio_chip *chip, unsigned int off, int val)
48
{
49
unsigned rel = S6_GPIO_OFFSET(off);
50
writeb(~0, S6_REG_GPIO + S6_GPIO_DIR + rel);
51
writeb(val ? ~0 : 0, S6_REG_GPIO + S6_GPIO_DATA + rel);
52
return 0;
53
}
54
55
static void set(struct gpio_chip *chip, unsigned int off, int val)
56
{
57
writeb(val ? ~0 : 0, S6_REG_GPIO + S6_GPIO_DATA + S6_GPIO_OFFSET(off));
58
}
59
60
static int to_irq(struct gpio_chip *chip, unsigned offset)
61
{
62
if (offset < 8)
63
return offset + IRQ_BASE;
64
return -EINVAL;
65
}
66
67
static struct gpio_chip gpiochip = {
68
.owner = THIS_MODULE,
69
.direction_input = direction_input,
70
.get = get,
71
.direction_output = direction_output,
72
.set = set,
73
.to_irq = to_irq,
74
.base = 0,
75
.ngpio = 24,
76
.can_sleep = 0, /* no blocking io needed */
77
.exported = 0, /* no exporting to userspace */
78
};
79
80
int s6_gpio_init(u32 afsel)
81
{
82
writeb(afsel, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_AFSEL);
83
writeb(afsel >> 8, S6_REG_GPIO + S6_GPIO_BANK(1) + S6_GPIO_AFSEL);
84
writeb(afsel >> 16, S6_REG_GPIO + S6_GPIO_BANK(2) + S6_GPIO_AFSEL);
85
return gpiochip_add(&gpiochip);
86
}
87
88
static void ack(struct irq_data *d)
89
{
90
writeb(1 << (d->irq - IRQ_BASE), S6_REG_GPIO + S6_GPIO_IC);
91
}
92
93
static void mask(struct irq_data *d)
94
{
95
u8 r = readb(S6_REG_GPIO + S6_GPIO_IE);
96
r &= ~(1 << (d->irq - IRQ_BASE));
97
writeb(r, S6_REG_GPIO + S6_GPIO_IE);
98
}
99
100
static void unmask(struct irq_data *d)
101
{
102
u8 m = readb(S6_REG_GPIO + S6_GPIO_IE);
103
m |= 1 << (d->irq - IRQ_BASE);
104
writeb(m, S6_REG_GPIO + S6_GPIO_IE);
105
}
106
107
static int set_type(struct irq_data *d, unsigned int type)
108
{
109
const u8 m = 1 << (d->irq - IRQ_BASE);
110
irq_flow_handler_t handler;
111
u8 reg;
112
113
if (type == IRQ_TYPE_PROBE) {
114
if ((readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_AFSEL) & m)
115
|| (readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IE) & m)
116
|| readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_DIR
117
+ S6_GPIO_MASK(irq - IRQ_BASE)))
118
return 0;
119
type = IRQ_TYPE_EDGE_BOTH;
120
}
121
122
reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IS);
123
if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) {
124
reg |= m;
125
handler = handle_level_irq;
126
} else {
127
reg &= ~m;
128
handler = handle_edge_irq;
129
}
130
writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IS);
131
__irq_set_handler_locked(irq, handler);
132
133
reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IEV);
134
if (type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_EDGE_RISING))
135
reg |= m;
136
else
137
reg &= ~m;
138
writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IEV);
139
140
reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IBE);
141
if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
142
reg |= m;
143
else
144
reg &= ~m;
145
writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IBE);
146
return 0;
147
}
148
149
static struct irq_chip gpioirqs = {
150
.name = "GPIO",
151
.irq_ack = ack,
152
.irq_mask = mask,
153
.irq_unmask = unmask,
154
.irq_set_type = set_type,
155
};
156
157
static u8 demux_masks[4];
158
159
static void demux_irqs(unsigned int irq, struct irq_desc *desc)
160
{
161
struct irq_chip *chip = irq_desc_get_chip(desc);
162
u8 *mask = irq_desc_get_handler_data(desc);
163
u8 pending;
164
int cirq;
165
166
chip->irq_mask(&desc->irq_data);
167
chip->irq_ack(&desc->irq_data));
168
pending = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_MIS) & *mask;
169
cirq = IRQ_BASE - 1;
170
while (pending) {
171
int n = ffs(pending);
172
cirq += n;
173
pending >>= n;
174
generic_handle_irq(cirq);
175
}
176
chip->irq_unmask(&desc->irq_data));
177
}
178
179
extern const signed char *platform_irq_mappings[XTENSA_NR_IRQS];
180
181
void __init variant_init_irq(void)
182
{
183
int irq, n;
184
writeb(0, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IE);
185
for (irq = n = 0; irq < XTENSA_NR_IRQS; irq++) {
186
const signed char *mapping = platform_irq_mappings[irq];
187
int alone = 1;
188
u8 mask;
189
if (!mapping)
190
continue;
191
for(mask = 0; *mapping != -1; mapping++)
192
switch (*mapping) {
193
case S6_INTC_GPIO(0):
194
mask |= 1 << 0;
195
break;
196
case S6_INTC_GPIO(1):
197
mask |= 1 << 1;
198
break;
199
case S6_INTC_GPIO(2):
200
mask |= 1 << 2;
201
break;
202
case S6_INTC_GPIO(3):
203
mask |= 0x1f << 3;
204
break;
205
default:
206
alone = 0;
207
}
208
if (mask) {
209
int cirq, i;
210
if (!alone) {
211
printk(KERN_ERR "chained irq chips can't share"
212
" parent irq %i\n", irq);
213
continue;
214
}
215
demux_masks[n] = mask;
216
cirq = IRQ_BASE - 1;
217
do {
218
i = ffs(mask);
219
cirq += i;
220
mask >>= i;
221
irq_set_chip(cirq, &gpioirqs);
222
irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
223
} while (mask);
224
irq_set_handler_data(irq, demux_masks + n);
225
irq_set_chained_handler(irq, demux_irqs);
226
if (++n == ARRAY_SIZE(demux_masks))
227
break;
228
}
229
}
230
}
231
232