Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/gpio/cs5535-gpio.c
15109 views
1
/*
2
* AMD CS5535/CS5536 GPIO driver
3
* Copyright (C) 2006 Advanced Micro Devices, Inc.
4
* Copyright (C) 2007-2009 Andres Salomon <[email protected]>
5
*
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of version 2 of the GNU General Public License
8
* as published by the Free Software Foundation.
9
*/
10
11
#include <linux/kernel.h>
12
#include <linux/spinlock.h>
13
#include <linux/module.h>
14
#include <linux/platform_device.h>
15
#include <linux/gpio.h>
16
#include <linux/io.h>
17
#include <linux/cs5535.h>
18
#include <asm/msr.h>
19
20
#define DRV_NAME "cs5535-gpio"
21
22
/*
23
* Some GPIO pins
24
* 31-29,23 : reserved (always mask out)
25
* 28 : Power Button
26
* 26 : PME#
27
* 22-16 : LPC
28
* 14,15 : SMBus
29
* 9,8 : UART1
30
* 7 : PCI INTB
31
* 3,4 : UART2/DDC
32
* 2 : IDE_IRQ0
33
* 1 : AC_BEEP
34
* 0 : PCI INTA
35
*
36
* If a mask was not specified, allow all except
37
* reserved and Power Button
38
*/
39
#define GPIO_DEFAULT_MASK 0x0F7FFFFF
40
41
static ulong mask = GPIO_DEFAULT_MASK;
42
module_param_named(mask, mask, ulong, 0444);
43
MODULE_PARM_DESC(mask, "GPIO channel mask.");
44
45
static struct cs5535_gpio_chip {
46
struct gpio_chip chip;
47
resource_size_t base;
48
49
struct platform_device *pdev;
50
spinlock_t lock;
51
} cs5535_gpio_chip;
52
53
/*
54
* The CS5535/CS5536 GPIOs support a number of extra features not defined
55
* by the gpio_chip API, so these are exported. For a full list of the
56
* registers, see include/linux/cs5535.h.
57
*/
58
59
static void errata_outl(struct cs5535_gpio_chip *chip, u32 val,
60
unsigned int reg)
61
{
62
unsigned long addr = chip->base + 0x80 + reg;
63
64
/*
65
* According to the CS5536 errata (#36), after suspend
66
* a write to the high bank GPIO register will clear all
67
* non-selected bits; the recommended workaround is a
68
* read-modify-write operation.
69
*
70
* Don't apply this errata to the edge status GPIOs, as writing
71
* to their lower bits will clear them.
72
*/
73
if (reg != GPIO_POSITIVE_EDGE_STS && reg != GPIO_NEGATIVE_EDGE_STS) {
74
if (val & 0xffff)
75
val |= (inl(addr) & 0xffff); /* ignore the high bits */
76
else
77
val |= (inl(addr) ^ (val >> 16));
78
}
79
outl(val, addr);
80
}
81
82
static void __cs5535_gpio_set(struct cs5535_gpio_chip *chip, unsigned offset,
83
unsigned int reg)
84
{
85
if (offset < 16)
86
/* low bank register */
87
outl(1 << offset, chip->base + reg);
88
else
89
/* high bank register */
90
errata_outl(chip, 1 << (offset - 16), reg);
91
}
92
93
void cs5535_gpio_set(unsigned offset, unsigned int reg)
94
{
95
struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
96
unsigned long flags;
97
98
spin_lock_irqsave(&chip->lock, flags);
99
__cs5535_gpio_set(chip, offset, reg);
100
spin_unlock_irqrestore(&chip->lock, flags);
101
}
102
EXPORT_SYMBOL_GPL(cs5535_gpio_set);
103
104
static void __cs5535_gpio_clear(struct cs5535_gpio_chip *chip, unsigned offset,
105
unsigned int reg)
106
{
107
if (offset < 16)
108
/* low bank register */
109
outl(1 << (offset + 16), chip->base + reg);
110
else
111
/* high bank register */
112
errata_outl(chip, 1 << offset, reg);
113
}
114
115
void cs5535_gpio_clear(unsigned offset, unsigned int reg)
116
{
117
struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
118
unsigned long flags;
119
120
spin_lock_irqsave(&chip->lock, flags);
121
__cs5535_gpio_clear(chip, offset, reg);
122
spin_unlock_irqrestore(&chip->lock, flags);
123
}
124
EXPORT_SYMBOL_GPL(cs5535_gpio_clear);
125
126
int cs5535_gpio_isset(unsigned offset, unsigned int reg)
127
{
128
struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
129
unsigned long flags;
130
long val;
131
132
spin_lock_irqsave(&chip->lock, flags);
133
if (offset < 16)
134
/* low bank register */
135
val = inl(chip->base + reg);
136
else {
137
/* high bank register */
138
val = inl(chip->base + 0x80 + reg);
139
offset -= 16;
140
}
141
spin_unlock_irqrestore(&chip->lock, flags);
142
143
return (val & (1 << offset)) ? 1 : 0;
144
}
145
EXPORT_SYMBOL_GPL(cs5535_gpio_isset);
146
147
int cs5535_gpio_set_irq(unsigned group, unsigned irq)
148
{
149
uint32_t lo, hi;
150
151
if (group > 7 || irq > 15)
152
return -EINVAL;
153
154
rdmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
155
156
lo &= ~(0xF << (group * 4));
157
lo |= (irq & 0xF) << (group * 4);
158
159
wrmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
160
return 0;
161
}
162
EXPORT_SYMBOL_GPL(cs5535_gpio_set_irq);
163
164
void cs5535_gpio_setup_event(unsigned offset, int pair, int pme)
165
{
166
struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
167
uint32_t shift = (offset % 8) * 4;
168
unsigned long flags;
169
uint32_t val;
170
171
if (offset >= 24)
172
offset = GPIO_MAP_W;
173
else if (offset >= 16)
174
offset = GPIO_MAP_Z;
175
else if (offset >= 8)
176
offset = GPIO_MAP_Y;
177
else
178
offset = GPIO_MAP_X;
179
180
spin_lock_irqsave(&chip->lock, flags);
181
val = inl(chip->base + offset);
182
183
/* Clear whatever was there before */
184
val &= ~(0xF << shift);
185
186
/* Set the new value */
187
val |= ((pair & 7) << shift);
188
189
/* Set the PME bit if this is a PME event */
190
if (pme)
191
val |= (1 << (shift + 3));
192
193
outl(val, chip->base + offset);
194
spin_unlock_irqrestore(&chip->lock, flags);
195
}
196
EXPORT_SYMBOL_GPL(cs5535_gpio_setup_event);
197
198
/*
199
* Generic gpio_chip API support.
200
*/
201
202
static int chip_gpio_request(struct gpio_chip *c, unsigned offset)
203
{
204
struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
205
unsigned long flags;
206
207
spin_lock_irqsave(&chip->lock, flags);
208
209
/* check if this pin is available */
210
if ((mask & (1 << offset)) == 0) {
211
dev_info(&chip->pdev->dev,
212
"pin %u is not available (check mask)\n", offset);
213
spin_unlock_irqrestore(&chip->lock, flags);
214
return -EINVAL;
215
}
216
217
/* disable output aux 1 & 2 on this pin */
218
__cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX1);
219
__cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX2);
220
221
/* disable input aux 1 on this pin */
222
__cs5535_gpio_clear(chip, offset, GPIO_INPUT_AUX1);
223
224
spin_unlock_irqrestore(&chip->lock, flags);
225
226
return 0;
227
}
228
229
static int chip_gpio_get(struct gpio_chip *chip, unsigned offset)
230
{
231
return cs5535_gpio_isset(offset, GPIO_READ_BACK);
232
}
233
234
static void chip_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
235
{
236
if (val)
237
cs5535_gpio_set(offset, GPIO_OUTPUT_VAL);
238
else
239
cs5535_gpio_clear(offset, GPIO_OUTPUT_VAL);
240
}
241
242
static int chip_direction_input(struct gpio_chip *c, unsigned offset)
243
{
244
struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
245
unsigned long flags;
246
247
spin_lock_irqsave(&chip->lock, flags);
248
__cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
249
__cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_ENABLE);
250
spin_unlock_irqrestore(&chip->lock, flags);
251
252
return 0;
253
}
254
255
static int chip_direction_output(struct gpio_chip *c, unsigned offset, int val)
256
{
257
struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
258
unsigned long flags;
259
260
spin_lock_irqsave(&chip->lock, flags);
261
262
__cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
263
__cs5535_gpio_set(chip, offset, GPIO_OUTPUT_ENABLE);
264
if (val)
265
__cs5535_gpio_set(chip, offset, GPIO_OUTPUT_VAL);
266
else
267
__cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_VAL);
268
269
spin_unlock_irqrestore(&chip->lock, flags);
270
271
return 0;
272
}
273
274
static const char * const cs5535_gpio_names[] = {
275
"GPIO0", "GPIO1", "GPIO2", "GPIO3",
276
"GPIO4", "GPIO5", "GPIO6", "GPIO7",
277
"GPIO8", "GPIO9", "GPIO10", "GPIO11",
278
"GPIO12", "GPIO13", "GPIO14", "GPIO15",
279
"GPIO16", "GPIO17", "GPIO18", "GPIO19",
280
"GPIO20", "GPIO21", "GPIO22", NULL,
281
"GPIO24", "GPIO25", "GPIO26", "GPIO27",
282
"GPIO28", NULL, NULL, NULL,
283
};
284
285
static struct cs5535_gpio_chip cs5535_gpio_chip = {
286
.chip = {
287
.owner = THIS_MODULE,
288
.label = DRV_NAME,
289
290
.base = 0,
291
.ngpio = 32,
292
.names = cs5535_gpio_names,
293
.request = chip_gpio_request,
294
295
.get = chip_gpio_get,
296
.set = chip_gpio_set,
297
298
.direction_input = chip_direction_input,
299
.direction_output = chip_direction_output,
300
},
301
};
302
303
static int __devinit cs5535_gpio_probe(struct platform_device *pdev)
304
{
305
struct resource *res;
306
int err = -EIO;
307
ulong mask_orig = mask;
308
309
/* There are two ways to get the GPIO base address; one is by
310
* fetching it from MSR_LBAR_GPIO, the other is by reading the
311
* PCI BAR info. The latter method is easier (especially across
312
* different architectures), so we'll stick with that for now. If
313
* it turns out to be unreliable in the face of crappy BIOSes, we
314
* can always go back to using MSRs.. */
315
316
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
317
if (!res) {
318
dev_err(&pdev->dev, "can't fetch device resource info\n");
319
goto done;
320
}
321
322
if (!request_region(res->start, resource_size(res), pdev->name)) {
323
dev_err(&pdev->dev, "can't request region\n");
324
goto done;
325
}
326
327
/* set up the driver-specific struct */
328
cs5535_gpio_chip.base = res->start;
329
cs5535_gpio_chip.pdev = pdev;
330
spin_lock_init(&cs5535_gpio_chip.lock);
331
332
dev_info(&pdev->dev, "reserved resource region %pR\n", res);
333
334
/* mask out reserved pins */
335
mask &= 0x1F7FFFFF;
336
337
/* do not allow pin 28, Power Button, as there's special handling
338
* in the PMC needed. (note 12, p. 48) */
339
mask &= ~(1 << 28);
340
341
if (mask_orig != mask)
342
dev_info(&pdev->dev, "mask changed from 0x%08lX to 0x%08lX\n",
343
mask_orig, mask);
344
345
/* finally, register with the generic GPIO API */
346
err = gpiochip_add(&cs5535_gpio_chip.chip);
347
if (err)
348
goto release_region;
349
350
dev_info(&pdev->dev, "GPIO support successfully loaded.\n");
351
return 0;
352
353
release_region:
354
release_region(res->start, resource_size(res));
355
done:
356
return err;
357
}
358
359
static int __devexit cs5535_gpio_remove(struct platform_device *pdev)
360
{
361
struct resource *r;
362
int err;
363
364
err = gpiochip_remove(&cs5535_gpio_chip.chip);
365
if (err) {
366
/* uhh? */
367
dev_err(&pdev->dev, "unable to remove gpio_chip?\n");
368
return err;
369
}
370
371
r = platform_get_resource(pdev, IORESOURCE_IO, 0);
372
release_region(r->start, resource_size(r));
373
return 0;
374
}
375
376
static struct platform_driver cs5535_gpio_driver = {
377
.driver = {
378
.name = DRV_NAME,
379
.owner = THIS_MODULE,
380
},
381
.probe = cs5535_gpio_probe,
382
.remove = __devexit_p(cs5535_gpio_remove),
383
};
384
385
static int __init cs5535_gpio_init(void)
386
{
387
return platform_driver_register(&cs5535_gpio_driver);
388
}
389
390
static void __exit cs5535_gpio_exit(void)
391
{
392
platform_driver_unregister(&cs5535_gpio_driver);
393
}
394
395
module_init(cs5535_gpio_init);
396
module_exit(cs5535_gpio_exit);
397
398
MODULE_AUTHOR("Andres Salomon <[email protected]>");
399
MODULE_DESCRIPTION("AMD CS5535/CS5536 GPIO driver");
400
MODULE_LICENSE("GPL");
401
MODULE_ALIAS("platform:" DRV_NAME);
402
403