Blame view

drivers/gpio/gpio-cs5535.c 9.22 KB
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
1
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * AMD CS5535/CS5536 GPIO driver
   * Copyright (C) 2006  Advanced Micro Devices, Inc.
   * Copyright (C) 2007-2009  Andres Salomon <dilinger@collabora.co.uk>
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of version 2 of the GNU General Public License
   * as published by the Free Software Foundation.
   */
  
  #include <linux/kernel.h>
  #include <linux/spinlock.h>
  #include <linux/module.h>
df9666940   Andres Salomon   gpio: Convert cs5...
14
  #include <linux/platform_device.h>
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
15
16
17
  #include <linux/gpio.h>
  #include <linux/io.h>
  #include <linux/cs5535.h>
1b912c1bc   Andres Salomon   drivers/gpio/cs55...
18
  #include <asm/msr.h>
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
19
20
  
  #define DRV_NAME "cs5535-gpio"
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
21

1ea3fa7bb   Tobias Mueller   cs5535-gpio: requ...
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  /*
   * Some GPIO pins
   *  31-29,23 : reserved (always mask out)
   *  28       : Power Button
   *  26       : PME#
   *  22-16    : LPC
   *  14,15    : SMBus
   *  9,8      : UART1
   *  7        : PCI INTB
   *  3,4      : UART2/DDC
   *  2        : IDE_IRQ0
   *  1        : AC_BEEP
   *  0        : PCI INTA
   *
   * If a mask was not specified, allow all except
   * reserved and Power Button
   */
  #define GPIO_DEFAULT_MASK 0x0F7FFFFF
  
  static ulong mask = GPIO_DEFAULT_MASK;
  module_param_named(mask, mask, ulong, 0444);
  MODULE_PARM_DESC(mask, "GPIO channel mask.");
c634fc19f   Linus Walleij   gpio: cs5535: use...
44
45
46
47
  /*
   * FIXME: convert this singleton driver to use the state container
   * design pattern, see Documentation/driver-model/design-patterns.txt
   */
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
48
49
50
  static struct cs5535_gpio_chip {
  	struct gpio_chip chip;
  	resource_size_t base;
df9666940   Andres Salomon   gpio: Convert cs5...
51
  	struct platform_device *pdev;
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
52
53
54
55
56
57
58
59
  	spinlock_t lock;
  } cs5535_gpio_chip;
  
  /*
   * The CS5535/CS5536 GPIOs support a number of extra features not defined
   * by the gpio_chip API, so these are exported.  For a full list of the
   * registers, see include/linux/cs5535.h.
   */
001851659   Andres Salomon   cs5535-gpio: don'...
60
61
  static void errata_outl(struct cs5535_gpio_chip *chip, u32 val,
  		unsigned int reg)
853ff8832   Andres Salomon   cs5535-gpio: appl...
62
  {
001851659   Andres Salomon   cs5535-gpio: don'...
63
  	unsigned long addr = chip->base + 0x80 + reg;
853ff8832   Andres Salomon   cs5535-gpio: appl...
64
65
66
67
68
  	/*
  	 * According to the CS5536 errata (#36), after suspend
  	 * a write to the high bank GPIO register will clear all
  	 * non-selected bits; the recommended workaround is a
  	 * read-modify-write operation.
001851659   Andres Salomon   cs5535-gpio: don'...
69
70
71
  	 *
  	 * Don't apply this errata to the edge status GPIOs, as writing
  	 * to their lower bits will clear them.
853ff8832   Andres Salomon   cs5535-gpio: appl...
72
  	 */
44658a11f   Andres Salomon   cs5535-gpio: hand...
73
74
75
76
77
78
  	if (reg != GPIO_POSITIVE_EDGE_STS && reg != GPIO_NEGATIVE_EDGE_STS) {
  		if (val & 0xffff)
  			val |= (inl(addr) & 0xffff); /* ignore the high bits */
  		else
  			val |= (inl(addr) ^ (val >> 16));
  	}
853ff8832   Andres Salomon   cs5535-gpio: appl...
79
80
  	outl(val, addr);
  }
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
81
82
83
84
85
86
87
88
  static void __cs5535_gpio_set(struct cs5535_gpio_chip *chip, unsigned offset,
  		unsigned int reg)
  {
  	if (offset < 16)
  		/* low bank register */
  		outl(1 << offset, chip->base + reg);
  	else
  		/* high bank register */
001851659   Andres Salomon   cs5535-gpio: don'...
89
  		errata_outl(chip, 1 << (offset - 16), reg);
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
  }
  
  void cs5535_gpio_set(unsigned offset, unsigned int reg)
  {
  	struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
  	unsigned long flags;
  
  	spin_lock_irqsave(&chip->lock, flags);
  	__cs5535_gpio_set(chip, offset, reg);
  	spin_unlock_irqrestore(&chip->lock, flags);
  }
  EXPORT_SYMBOL_GPL(cs5535_gpio_set);
  
  static void __cs5535_gpio_clear(struct cs5535_gpio_chip *chip, unsigned offset,
  		unsigned int reg)
  {
  	if (offset < 16)
  		/* low bank register */
  		outl(1 << (offset + 16), chip->base + reg);
  	else
  		/* high bank register */
001851659   Andres Salomon   cs5535-gpio: don'...
111
  		errata_outl(chip, 1 << offset, reg);
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
  }
  
  void cs5535_gpio_clear(unsigned offset, unsigned int reg)
  {
  	struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
  	unsigned long flags;
  
  	spin_lock_irqsave(&chip->lock, flags);
  	__cs5535_gpio_clear(chip, offset, reg);
  	spin_unlock_irqrestore(&chip->lock, flags);
  }
  EXPORT_SYMBOL_GPL(cs5535_gpio_clear);
  
  int cs5535_gpio_isset(unsigned offset, unsigned int reg)
  {
  	struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
  	unsigned long flags;
  	long val;
  
  	spin_lock_irqsave(&chip->lock, flags);
  	if (offset < 16)
  		/* low bank register */
  		val = inl(chip->base + reg);
  	else {
  		/* high bank register */
  		val = inl(chip->base + 0x80 + reg);
  		offset -= 16;
  	}
  	spin_unlock_irqrestore(&chip->lock, flags);
  
  	return (val & (1 << offset)) ? 1 : 0;
  }
  EXPORT_SYMBOL_GPL(cs5535_gpio_isset);
1b912c1bc   Andres Salomon   drivers/gpio/cs55...
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
  int cs5535_gpio_set_irq(unsigned group, unsigned irq)
  {
  	uint32_t lo, hi;
  
  	if (group > 7 || irq > 15)
  		return -EINVAL;
  
  	rdmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
  
  	lo &= ~(0xF << (group * 4));
  	lo |= (irq & 0xF) << (group * 4);
  
  	wrmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
  	return 0;
  }
  EXPORT_SYMBOL_GPL(cs5535_gpio_set_irq);
  
  void cs5535_gpio_setup_event(unsigned offset, int pair, int pme)
  {
  	struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
  	uint32_t shift = (offset % 8) * 4;
  	unsigned long flags;
  	uint32_t val;
  
  	if (offset >= 24)
  		offset = GPIO_MAP_W;
  	else if (offset >= 16)
  		offset = GPIO_MAP_Z;
  	else if (offset >= 8)
  		offset = GPIO_MAP_Y;
  	else
  		offset = GPIO_MAP_X;
  
  	spin_lock_irqsave(&chip->lock, flags);
  	val = inl(chip->base + offset);
  
  	/* Clear whatever was there before */
  	val &= ~(0xF << shift);
  
  	/* Set the new value */
  	val |= ((pair & 7) << shift);
  
  	/* Set the PME bit if this is a PME event */
  	if (pme)
  		val |= (1 << (shift + 3));
  
  	outl(val, chip->base + offset);
  	spin_unlock_irqrestore(&chip->lock, flags);
  }
  EXPORT_SYMBOL_GPL(cs5535_gpio_setup_event);
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
195
196
197
  /*
   * Generic gpio_chip API support.
   */
1ea3fa7bb   Tobias Mueller   cs5535-gpio: requ...
198
199
  static int chip_gpio_request(struct gpio_chip *c, unsigned offset)
  {
c634fc19f   Linus Walleij   gpio: cs5535: use...
200
  	struct cs5535_gpio_chip *chip = gpiochip_get_data(c);
1ea3fa7bb   Tobias Mueller   cs5535-gpio: requ...
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
  	unsigned long flags;
  
  	spin_lock_irqsave(&chip->lock, flags);
  
  	/* check if this pin is available */
  	if ((mask & (1 << offset)) == 0) {
  		dev_info(&chip->pdev->dev,
  			"pin %u is not available (check mask)
  ", offset);
  		spin_unlock_irqrestore(&chip->lock, flags);
  		return -EINVAL;
  	}
  
  	/* disable output aux 1 & 2 on this pin */
  	__cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX1);
  	__cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX2);
  
  	/* disable input aux 1 on this pin */
  	__cs5535_gpio_clear(chip, offset, GPIO_INPUT_AUX1);
  
  	spin_unlock_irqrestore(&chip->lock, flags);
  
  	return 0;
  }
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
225
226
  static int chip_gpio_get(struct gpio_chip *chip, unsigned offset)
  {
a8a5164c2   Ben Gardner   gpio: cs5535-gpio...
227
  	return cs5535_gpio_isset(offset, GPIO_READ_BACK);
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
228
229
230
231
232
233
234
235
236
237
238
239
  }
  
  static void chip_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
  {
  	if (val)
  		cs5535_gpio_set(offset, GPIO_OUTPUT_VAL);
  	else
  		cs5535_gpio_clear(offset, GPIO_OUTPUT_VAL);
  }
  
  static int chip_direction_input(struct gpio_chip *c, unsigned offset)
  {
c634fc19f   Linus Walleij   gpio: cs5535: use...
240
  	struct cs5535_gpio_chip *chip = gpiochip_get_data(c);
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
241
242
243
244
  	unsigned long flags;
  
  	spin_lock_irqsave(&chip->lock, flags);
  	__cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
a8a5164c2   Ben Gardner   gpio: cs5535-gpio...
245
  	__cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_ENABLE);
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
246
247
248
249
250
251
252
  	spin_unlock_irqrestore(&chip->lock, flags);
  
  	return 0;
  }
  
  static int chip_direction_output(struct gpio_chip *c, unsigned offset, int val)
  {
c634fc19f   Linus Walleij   gpio: cs5535: use...
253
  	struct cs5535_gpio_chip *chip = gpiochip_get_data(c);
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
254
255
256
  	unsigned long flags;
  
  	spin_lock_irqsave(&chip->lock, flags);
a8a5164c2   Ben Gardner   gpio: cs5535-gpio...
257
  	__cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
258
259
260
261
262
263
264
265
266
267
  	__cs5535_gpio_set(chip, offset, GPIO_OUTPUT_ENABLE);
  	if (val)
  		__cs5535_gpio_set(chip, offset, GPIO_OUTPUT_VAL);
  	else
  		__cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_VAL);
  
  	spin_unlock_irqrestore(&chip->lock, flags);
  
  	return 0;
  }
62154991a   Uwe Kleine-König   gpiolib: make nam...
268
  static const char * const cs5535_gpio_names[] = {
1ea3fa7bb   Tobias Mueller   cs5535-gpio: requ...
269
270
271
272
273
274
275
276
277
  	"GPIO0", "GPIO1", "GPIO2", "GPIO3",
  	"GPIO4", "GPIO5", "GPIO6", "GPIO7",
  	"GPIO8", "GPIO9", "GPIO10", "GPIO11",
  	"GPIO12", "GPIO13", "GPIO14", "GPIO15",
  	"GPIO16", "GPIO17", "GPIO18", "GPIO19",
  	"GPIO20", "GPIO21", "GPIO22", NULL,
  	"GPIO24", "GPIO25", "GPIO26", "GPIO27",
  	"GPIO28", NULL, NULL, NULL,
  };
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
278
279
280
281
282
283
  static struct cs5535_gpio_chip cs5535_gpio_chip = {
  	.chip = {
  		.owner = THIS_MODULE,
  		.label = DRV_NAME,
  
  		.base = 0,
1ea3fa7bb   Tobias Mueller   cs5535-gpio: requ...
284
285
286
  		.ngpio = 32,
  		.names = cs5535_gpio_names,
  		.request = chip_gpio_request,
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
287
288
289
290
291
292
293
294
  
  		.get = chip_gpio_get,
  		.set = chip_gpio_set,
  
  		.direction_input = chip_direction_input,
  		.direction_output = chip_direction_output,
  	},
  };
3836309d9   Bill Pemberton   gpio: remove use ...
295
  static int cs5535_gpio_probe(struct platform_device *pdev)
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
296
  {
df9666940   Andres Salomon   gpio: Convert cs5...
297
298
  	struct resource *res;
  	int err = -EIO;
1ea3fa7bb   Tobias Mueller   cs5535-gpio: requ...
299
  	ulong mask_orig = mask;
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
300
301
302
303
304
305
306
  
  	/* There are two ways to get the GPIO base address; one is by
  	 * fetching it from MSR_LBAR_GPIO, the other is by reading the
  	 * PCI BAR info.  The latter method is easier (especially across
  	 * different architectures), so we'll stick with that for now.  If
  	 * it turns out to be unreliable in the face of crappy BIOSes, we
  	 * can always go back to using MSRs.. */
df9666940   Andres Salomon   gpio: Convert cs5...
307
308
309
310
  	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
  	if (!res) {
  		dev_err(&pdev->dev, "can't fetch device resource info
  ");
85bd84f53   Laxman Dewangan   gpio: cs5535: Use...
311
  		return err;
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
312
  	}
3eebd6132   Pramod Gurav   gpio: cs5535: Swi...
313
314
  	if (!devm_request_region(&pdev->dev, res->start, resource_size(res),
  				 pdev->name)) {
df9666940   Andres Salomon   gpio: Convert cs5...
315
316
  		dev_err(&pdev->dev, "can't request region
  ");
85bd84f53   Laxman Dewangan   gpio: cs5535: Use...
317
  		return err;
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
318
319
320
  	}
  
  	/* set up the driver-specific struct */
df9666940   Andres Salomon   gpio: Convert cs5...
321
  	cs5535_gpio_chip.base = res->start;
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
322
323
  	cs5535_gpio_chip.pdev = pdev;
  	spin_lock_init(&cs5535_gpio_chip.lock);
1db0b427e   Joe Perches   gpio: Fix cs5535 ...
324
325
  	dev_info(&pdev->dev, "reserved resource region %pR
  ", res);
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
326

1ea3fa7bb   Tobias Mueller   cs5535-gpio: requ...
327
328
329
330
331
332
333
334
335
336
337
  	/* mask out reserved pins */
  	mask &= 0x1F7FFFFF;
  
  	/* do not allow pin 28, Power Button, as there's special handling
  	 * in the PMC needed. (note 12, p. 48) */
  	mask &= ~(1 << 28);
  
  	if (mask_orig != mask)
  		dev_info(&pdev->dev, "mask changed from 0x%08lX to 0x%08lX
  ",
  				mask_orig, mask);
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
338
  	/* finally, register with the generic GPIO API */
85bd84f53   Laxman Dewangan   gpio: cs5535: Use...
339
340
  	err = devm_gpiochip_add_data(&pdev->dev, &cs5535_gpio_chip.chip,
  				     &cs5535_gpio_chip);
1ea3fa7bb   Tobias Mueller   cs5535-gpio: requ...
341
  	if (err)
85bd84f53   Laxman Dewangan   gpio: cs5535: Use...
342
  		return err;
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
343

df9666940   Andres Salomon   gpio: Convert cs5...
344
  	return 0;
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
345
  }
36885ff0e   Nikanth Karthikesan   gpio/cs5535-gpio:...
346
  static struct platform_driver cs5535_gpio_driver = {
df9666940   Andres Salomon   gpio: Convert cs5...
347
348
  	.driver = {
  		.name = DRV_NAME,
df9666940   Andres Salomon   gpio: Convert cs5...
349
350
  	},
  	.probe = cs5535_gpio_probe,
df9666940   Andres Salomon   gpio: Convert cs5...
351
  };
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
352

6f61415e9   Mark Brown   gpio: Convert GPI...
353
  module_platform_driver(cs5535_gpio_driver);
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
354

d45840d9f   Andres Salomon   Andres has moved
355
  MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>");
5f0a96b04   Andres Salomon   cs5535-gpio: add ...
356
357
  MODULE_DESCRIPTION("AMD CS5535/CS5536 GPIO driver");
  MODULE_LICENSE("GPL");
ec9d0cf57   Andres Salomon   gpio/misc: Add MO...
358
  MODULE_ALIAS("platform:" DRV_NAME);