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