Blame view

drivers/gpio/gpio-htc-egpio.c 10.5 KB
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  /*
   * Support for the GPIO/IRQ expander chips present on several HTC phones.
   * These are implemented in CPLD chips present on the board.
   *
   * Copyright (c) 2007 Kevin O'Connor <kevin@koconnor.net>
   * Copyright (c) 2007 Philipp Zabel <philipp.zabel@gmail.com>
   *
   * This file may be distributed under the terms of the GNU GPL license.
   */
  
  #include <linux/kernel.h>
  #include <linux/errno.h>
  #include <linux/interrupt.h>
  #include <linux/irq.h>
  #include <linux/io.h>
  #include <linux/spinlock.h>
3c6e8d05d   Linus Walleij   mfd/gpio: Move HT...
17
  #include <linux/platform_data/gpio-htc-egpio.h>
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
18
  #include <linux/platform_device.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
19
  #include <linux/slab.h>
43bbf94c3   Paul Gortmaker   gpio: htc-egpio: ...
20
  #include <linux/init.h>
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
  
  struct egpio_chip {
  	int              reg_start;
  	int              cached_values;
  	unsigned long    is_out;
  	struct device    *dev;
  	struct gpio_chip chip;
  };
  
  struct egpio_info {
  	spinlock_t        lock;
  
  	/* iomem info */
  	void __iomem      *base_addr;
  	int               bus_shift;	/* byte shift */
  	int               reg_shift;	/* bit shift */
  	int               reg_mask;
  
  	/* irq info */
  	int               ack_register;
  	int               ack_write;
  	u16               irqs_enabled;
  	uint              irq_start;
  	int               nirqs;
  	uint              chained_irq;
  
  	/* egpio info */
  	struct egpio_chip *chip;
  	int               nchips;
  };
  
  static inline void egpio_writew(u16 value, struct egpio_info *ei, int reg)
  {
  	writew(value, ei->base_addr + (reg << ei->bus_shift));
  }
  
  static inline u16 egpio_readw(struct egpio_info *ei, int reg)
  {
  	return readw(ei->base_addr + (reg << ei->bus_shift));
  }
  
  /*
   * IRQs
   */
  
  static inline void ack_irqs(struct egpio_info *ei)
  {
  	egpio_writew(ei->ack_write, ei, ei->ack_register);
  	pr_debug("EGPIO ack - write %x to base+%x
  ",
  			ei->ack_write, ei->ack_register << ei->bus_shift);
  }
949b9deca   Mark Brown   mfd: Convert HTC ...
73
  static void egpio_ack(struct irq_data *data)
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
74
75
76
77
78
79
  {
  }
  
  /* There does not appear to be a way to proactively mask interrupts
   * on the egpio chip itself.  So, we simply ignore interrupts that
   * aren't desired. */
949b9deca   Mark Brown   mfd: Convert HTC ...
80
  static void egpio_mask(struct irq_data *data)
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
81
  {
949b9deca   Mark Brown   mfd: Convert HTC ...
82
83
84
85
  	struct egpio_info *ei = irq_data_get_irq_chip_data(data);
  	ei->irqs_enabled &= ~(1 << (data->irq - ei->irq_start));
  	pr_debug("EGPIO mask %d %04x
  ", data->irq, ei->irqs_enabled);
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
86
  }
949b9deca   Mark Brown   mfd: Convert HTC ...
87
88
  
  static void egpio_unmask(struct irq_data *data)
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
89
  {
949b9deca   Mark Brown   mfd: Convert HTC ...
90
91
92
93
  	struct egpio_info *ei = irq_data_get_irq_chip_data(data);
  	ei->irqs_enabled |= 1 << (data->irq - ei->irq_start);
  	pr_debug("EGPIO unmask %d %04x
  ", data->irq, ei->irqs_enabled);
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
94
95
96
  }
  
  static struct irq_chip egpio_muxed_chip = {
949b9deca   Mark Brown   mfd: Convert HTC ...
97
98
99
100
  	.name		= "htc-egpio",
  	.irq_ack	= egpio_ack,
  	.irq_mask	= egpio_mask,
  	.irq_unmask	= egpio_unmask,
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
101
  };
bd0b9ac40   Thomas Gleixner   genirq: Remove ir...
102
  static void egpio_handler(struct irq_desc *desc)
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
103
  {
77eda9669   Thomas Gleixner   mfd: htc-egpio: C...
104
  	struct egpio_info *ei = irq_desc_get_handler_data(desc);
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
105
106
107
108
109
110
111
112
113
114
  	int irqpin;
  
  	/* Read current pins. */
  	unsigned long readval = egpio_readw(ei, ei->ack_register);
  	pr_debug("IRQ reg: %x
  ", (unsigned int)readval);
  	/* Ack/unmask interrupts. */
  	ack_irqs(ei);
  	/* Process all set pins. */
  	readval &= ei->irqs_enabled;
984b3f574   Akinobu Mita   bitops: rename fo...
115
  	for_each_set_bit(irqpin, &readval, ei->nirqs) {
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
116
117
118
  		/* Run irq handler */
  		pr_debug("got IRQ %d
  ", irqpin);
77eda9669   Thomas Gleixner   mfd: htc-egpio: C...
119
  		generic_handle_irq(ei->irq_start + irqpin);
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
  	}
  }
  
  int htc_egpio_get_wakeup_irq(struct device *dev)
  {
  	struct egpio_info *ei = dev_get_drvdata(dev);
  
  	/* Read current pins. */
  	u16 readval = egpio_readw(ei, ei->ack_register);
  	/* Ack/unmask interrupts. */
  	ack_irqs(ei);
  	/* Return first set pin. */
  	readval &= ei->irqs_enabled;
  	return ei->irq_start + ffs(readval) - 1;
  }
  EXPORT_SYMBOL(htc_egpio_get_wakeup_irq);
  
  static inline int egpio_pos(struct egpio_info *ei, int bit)
  {
  	return bit >> ei->reg_shift;
  }
  
  static inline int egpio_bit(struct egpio_info *ei, int bit)
  {
  	return 1 << (bit & ((1 << ei->reg_shift)-1));
  }
  
  /*
   * Input pins
   */
  
  static int egpio_get(struct gpio_chip *chip, unsigned offset)
  {
  	struct egpio_chip *egpio;
  	struct egpio_info *ei;
  	unsigned           bit;
  	int                reg;
  	int                value;
  
  	pr_debug("egpio_get_value(%d)
  ", chip->base + offset);
8d5f095fc   Linus Walleij   mfd: htc-egpio: U...
161
  	egpio = gpiochip_get_data(chip);
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
162
163
164
  	ei    = dev_get_drvdata(egpio->dev);
  	bit   = egpio_bit(ei, offset);
  	reg   = egpio->reg_start + egpio_pos(ei, offset);
24b35ed9a   Linus Walleij   gpio: htc-egpio: ...
165
166
167
168
169
170
171
172
173
  	if (test_bit(offset, &egpio->is_out)) {
  		return !!(egpio->cached_values & (1 << offset));
  	} else {
  		value = egpio_readw(ei, reg);
  		pr_debug("readw(%p + %x) = %x
  ",
  			 ei->base_addr, reg << ei->bus_shift, value);
  		return !!(value & bit);
  	}
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
174
175
176
177
178
  }
  
  static int egpio_direction_input(struct gpio_chip *chip, unsigned offset)
  {
  	struct egpio_chip *egpio;
8d5f095fc   Linus Walleij   mfd: htc-egpio: U...
179
  	egpio = gpiochip_get_data(chip);
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
  	return test_bit(offset, &egpio->is_out) ? -EINVAL : 0;
  }
  
  
  /*
   * Output pins
   */
  
  static void egpio_set(struct gpio_chip *chip, unsigned offset, int value)
  {
  	unsigned long     flag;
  	struct egpio_chip *egpio;
  	struct egpio_info *ei;
  	unsigned          bit;
  	int               pos;
  	int               reg;
  	int               shift;
  
  	pr_debug("egpio_set(%s, %d(%d), %d)
  ",
  			chip->label, offset, offset+chip->base, value);
8d5f095fc   Linus Walleij   mfd: htc-egpio: U...
201
  	egpio = gpiochip_get_data(chip);
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
  	ei    = dev_get_drvdata(egpio->dev);
  	bit   = egpio_bit(ei, offset);
  	pos   = egpio_pos(ei, offset);
  	reg   = egpio->reg_start + pos;
  	shift = pos << ei->reg_shift;
  
  	pr_debug("egpio %s: reg %d = 0x%04x
  ", value ? "set" : "clear",
  			reg, (egpio->cached_values >> shift) & ei->reg_mask);
  
  	spin_lock_irqsave(&ei->lock, flag);
  	if (value)
  		egpio->cached_values |= (1 << offset);
  	else
  		egpio->cached_values &= ~(1 << offset);
  	egpio_writew((egpio->cached_values >> shift) & ei->reg_mask, ei, reg);
  	spin_unlock_irqrestore(&ei->lock, flag);
  }
  
  static int egpio_direction_output(struct gpio_chip *chip,
  					unsigned offset, int value)
  {
  	struct egpio_chip *egpio;
8d5f095fc   Linus Walleij   mfd: htc-egpio: U...
225
  	egpio = gpiochip_get_data(chip);
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
226
227
228
229
230
231
232
  	if (test_bit(offset, &egpio->is_out)) {
  		egpio_set(chip, offset, value);
  		return 0;
  	} else {
  		return -EINVAL;
  	}
  }
9298539c0   Linus Walleij   gpio: htc-egpio: ...
233
234
235
236
237
238
239
240
  static int egpio_get_direction(struct gpio_chip *chip, unsigned offset)
  {
  	struct egpio_chip *egpio;
  
  	egpio = gpiochip_get_data(chip);
  
  	return !test_bit(offset, &egpio->is_out);
  }
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
  static void egpio_write_cache(struct egpio_info *ei)
  {
  	int               i;
  	struct egpio_chip *egpio;
  	int               shift;
  
  	for (i = 0; i < ei->nchips; i++) {
  		egpio = &(ei->chip[i]);
  		if (!egpio->is_out)
  			continue;
  
  		for (shift = 0; shift < egpio->chip.ngpio;
  				shift += (1<<ei->reg_shift)) {
  
  			int reg = egpio->reg_start + egpio_pos(ei, shift);
  
  			if (!((egpio->is_out >> shift) & ei->reg_mask))
  				continue;
  
  			pr_debug("EGPIO: setting %x to %x, was %x
  ", reg,
  				(egpio->cached_values >> shift) & ei->reg_mask,
  				egpio_readw(ei, reg));
  
  			egpio_writew((egpio->cached_values >> shift)
  					& ei->reg_mask, ei, reg);
  		}
  	}
  }
  
  
  /*
   * Setup
   */
  
  static int __init egpio_probe(struct platform_device *pdev)
  {
334a41ce9   Jingoo Han   mfd: Use dev_get_...
278
  	struct htc_egpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
279
280
281
282
283
284
285
286
  	struct resource   *res;
  	struct egpio_info *ei;
  	struct gpio_chip  *chip;
  	unsigned int      irq, irq_end;
  	int               i;
  	int               ret;
  
  	/* Initialize ei data structure. */
58645b36b   Lee Jones   mfd: htc-egpio: C...
287
  	ei = devm_kzalloc(&pdev->dev, sizeof(*ei), GFP_KERNEL);
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
  	if (!ei)
  		return -ENOMEM;
  
  	spin_lock_init(&ei->lock);
  
  	/* Find chained irq */
  	ret = -EINVAL;
  	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
  	if (res)
  		ei->chained_irq = res->start;
  
  	/* Map egpio chip into virtual address space. */
  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	if (!res)
  		goto fail;
3f9850f26   Wei Yongjun   mfd: htc-egpio: U...
303
304
  	ei->base_addr = devm_ioremap_nocache(&pdev->dev, res->start,
  					     resource_size(res));
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
305
306
  	if (!ei->base_addr)
  		goto fail;
504f97f88   Samuel Ortiz   mfd: Fix htc-egpi...
307
308
  	pr_debug("EGPIO phys=%08x virt=%p
  ", (u32)res->start, ei->base_addr);
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
  
  	if ((pdata->bus_width != 16) && (pdata->bus_width != 32))
  		goto fail;
  	ei->bus_shift = fls(pdata->bus_width - 1) - 3;
  	pr_debug("bus_shift = %d
  ", ei->bus_shift);
  
  	if ((pdata->reg_width != 8) && (pdata->reg_width != 16))
  		goto fail;
  	ei->reg_shift = fls(pdata->reg_width - 1);
  	pr_debug("reg_shift = %d
  ", ei->reg_shift);
  
  	ei->reg_mask = (1 << pdata->reg_width) - 1;
  
  	platform_set_drvdata(pdev, ei);
  
  	ei->nchips = pdata->num_chips;
58645b36b   Lee Jones   mfd: htc-egpio: C...
327
328
329
  	ei->chip = devm_kzalloc(&pdev->dev,
  				sizeof(struct egpio_chip) * ei->nchips,
  				GFP_KERNEL);
720fd66df   Julia Lawall   mfd: Fix egpio kz...
330
  	if (!ei->chip) {
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
331
332
333
334
335
336
337
338
339
340
  		ret = -ENOMEM;
  		goto fail;
  	}
  	for (i = 0; i < ei->nchips; i++) {
  		ei->chip[i].reg_start = pdata->chip[i].reg_start;
  		ei->chip[i].cached_values = pdata->chip[i].initial_values;
  		ei->chip[i].is_out = pdata->chip[i].direction;
  		ei->chip[i].dev = &(pdev->dev);
  		chip = &(ei->chip[i].chip);
  		chip->label           = "htc-egpio";
58383c784   Linus Walleij   gpio: change memb...
341
  		chip->parent          = &pdev->dev;
d8f388d8d   David Brownell   gpio: sysfs inter...
342
  		chip->owner           = THIS_MODULE;
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
343
344
345
346
  		chip->get             = egpio_get;
  		chip->set             = egpio_set;
  		chip->direction_input = egpio_direction_input;
  		chip->direction_output = egpio_direction_output;
9298539c0   Linus Walleij   gpio: htc-egpio: ...
347
  		chip->get_direction   = egpio_get_direction;
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
348
349
  		chip->base            = pdata->chip[i].gpio_base;
  		chip->ngpio           = pdata->chip[i].num_gpios;
8d5f095fc   Linus Walleij   mfd: htc-egpio: U...
350
  		gpiochip_add_data(chip, &ei->chip[i]);
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
  	}
  
  	/* Set initial pin values */
  	egpio_write_cache(ei);
  
  	ei->irq_start = pdata->irq_base;
  	ei->nirqs = pdata->num_irqs;
  	ei->ack_register = pdata->ack_register;
  
  	if (ei->chained_irq) {
  		/* Setup irq handlers */
  		ei->ack_write = 0xFFFF;
  		if (pdata->invert_acks)
  			ei->ack_write = 0;
  		irq_end = ei->irq_start + ei->nirqs;
  		for (irq = ei->irq_start; irq < irq_end; irq++) {
d6f7ce9f7   Thomas Gleixner   mfd: Fold irq_set...
367
368
  			irq_set_chip_and_handler(irq, &egpio_muxed_chip,
  						 handle_simple_irq);
d5bb12216   Thomas Gleixner   mfd: Cleanup irq ...
369
  			irq_set_chip_data(irq, ei);
9bd09f345   Rob Herring   mfd: Kill off set...
370
  			irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE);
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
371
  		}
d5bb12216   Thomas Gleixner   mfd: Cleanup irq ...
372
  		irq_set_irq_type(ei->chained_irq, IRQ_TYPE_EDGE_RISING);
073f7f99a   Thomas Gleixner   mfd: htc-egpio: C...
373
374
  		irq_set_chained_handler_and_data(ei->chained_irq,
  						 egpio_handler, ei);
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
375
376
377
378
379
380
381
382
383
384
  		ack_irqs(ei);
  
  		device_init_wakeup(&pdev->dev, 1);
  	}
  
  	return 0;
  
  fail:
  	printk(KERN_ERR "EGPIO failed to setup
  ");
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
385
386
  	return ret;
  }
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
  #ifdef CONFIG_PM
  static int egpio_suspend(struct platform_device *pdev, pm_message_t state)
  {
  	struct egpio_info *ei = platform_get_drvdata(pdev);
  
  	if (ei->chained_irq && device_may_wakeup(&pdev->dev))
  		enable_irq_wake(ei->chained_irq);
  	return 0;
  }
  
  static int egpio_resume(struct platform_device *pdev)
  {
  	struct egpio_info *ei = platform_get_drvdata(pdev);
  
  	if (ei->chained_irq && device_may_wakeup(&pdev->dev))
  		disable_irq_wake(ei->chained_irq);
  
  	/* Update registers from the cache, in case
  	   the CPLD was powered off during suspend */
  	egpio_write_cache(ei);
  	return 0;
  }
  #else
  #define egpio_suspend NULL
  #define egpio_resume NULL
  #endif
  
  
  static struct platform_driver egpio_driver = {
  	.driver = {
  		.name = "htc-egpio",
43bbf94c3   Paul Gortmaker   gpio: htc-egpio: ...
418
  		.suppress_bind_attrs = true,
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
419
  	},
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
420
421
422
423
424
425
426
427
  	.suspend      = egpio_suspend,
  	.resume       = egpio_resume,
  };
  
  static int __init egpio_init(void)
  {
  	return platform_driver_probe(&egpio_driver, egpio_probe);
  }
a1635b8fe   Philipp Zabel   [ARM] 4947/1: htc...
428
429
  /* start early for dependencies */
  subsys_initcall(egpio_init);